From 010a3b574534cb991d1d663c874181def2bb0d07 Mon Sep 17 00:00:00 2001 From: Asmita Poddar Date: Mon, 23 Sep 2024 12:50:49 +0000 Subject: Change mouse keys scroll rate Earlier, the mouse keys scroll rate was too fast. Changed mouse keys scroll rate from 1.0f -> 0.2f to improve scrolling experience for the user. Bug: 341799888 Test: Manual scrolling Test: atest FrameworksServicesTests:MouseKeysInterceptorTest Flag: EXEMPT bugfix Change-Id: I58bf46102e2f1f4ab965ccb540f1de4388aba4ea --- .../com/android/server/accessibility/MouseKeysInterceptor.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'services/accessibility') diff --git a/services/accessibility/java/com/android/server/accessibility/MouseKeysInterceptor.java b/services/accessibility/java/com/android/server/accessibility/MouseKeysInterceptor.java index 54368ca9c03e..4b97745b3b25 100644 --- a/services/accessibility/java/com/android/server/accessibility/MouseKeysInterceptor.java +++ b/services/accessibility/java/com/android/server/accessibility/MouseKeysInterceptor.java @@ -73,12 +73,16 @@ public class MouseKeysInterceptor extends BaseEventStreamTransformation private static final int MESSAGE_MOVE_MOUSE_POINTER = 1; private static final int MESSAGE_SCROLL_MOUSE_POINTER = 2; - private static final float MOUSE_POINTER_MOVEMENT_STEP = 1.8f; private static final int KEY_NOT_SET = -1; /** Time interval after which mouse action will be repeated */ private static final int INTERVAL_MILLIS = 10; + @VisibleForTesting + public static final float MOUSE_POINTER_MOVEMENT_STEP = 1.8f; + @VisibleForTesting + public static final float MOUSE_SCROLL_STEP = 0.2f; + private final AccessibilityManagerService mAms; private final Handler mHandler; private final InputManager mInputManager; @@ -281,8 +285,8 @@ public class MouseKeysInterceptor extends BaseEventStreamTransformation MouseKeyEvent mouseKeyEvent = MouseKeyEvent.from( keyCode, mActiveInputDeviceId, mDeviceKeyCodeMap); float y = switch (mouseKeyEvent) { - case UP_MOVE_OR_SCROLL -> 1.0f; - case DOWN_MOVE_OR_SCROLL -> -1.0f; + case UP_MOVE_OR_SCROLL -> MOUSE_SCROLL_STEP; + case DOWN_MOVE_OR_SCROLL -> -MOUSE_SCROLL_STEP; default -> 0.0f; }; waitForVirtualMouseCreation(); -- cgit v1.2.3-59-g8ed1b From 3bf69d6d2a6a0852c149d37304472d82a2f9f961 Mon Sep 17 00:00:00 2001 From: "seokgyun.hong" Date: Fri, 19 Jul 2024 10:26:21 +0900 Subject: Prevent calls to StatusBarManagerInternal from visible background users Visible background users have access to UI on assigned displays on devices that have config_multiuserVisibleBackgroundUsers enabled. The main use case is Automotive's multi-display Whole Cabin experience where passengers (modeled as visible background users) can interact with the display in front of them concurrently with the driver (modeled as the the current user) interacting with driver's display. - Calls to StatusBarManagerInternal trigger callbacks to the registered IStatusBar for the current user. - However, StatusBarManagerInternal APIs can be called from not only the current user but also visible background users. - We should prevent this to ensure that visible background users do not interfere with the current user's experience. Bug: 332222893 Flag: EXEMPT bugfix Test: atest MagnificationConnectionManagerTest atest WmTests:PhoneWindowManagerTests (cherry picked from https://partner-android-review.googlesource.com/q/commit:62538ec79f939853bd4ea75e81f4f46204fce72d) Change-Id: Icdc6515e69e8044cf972a9e14e2fdd7f7a4b6958 --- .../accessibility/AccessibilityManagerService.java | 6 ++++ .../MagnificationConnectionManager.java | 10 ++++++ .../android/server/policy/PhoneWindowManager.java | 36 ++++++++++++++++++++-- .../server/search/SearchManagerService.java | 12 ++++++++ .../MagnificationConnectionManagerTest.java | 7 +++++ 5 files changed, 68 insertions(+), 3 deletions(-) (limited to 'services/accessibility') diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index 7580b697b516..49f15e46894d 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -3653,6 +3653,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub return; } + // Magnification connection should not be requested for visible background users. + // (b/332222893) + if (mUmi.isVisibleBackgroundFullUser(userState.mUserId)) { + return; + } + final boolean shortcutEnabled = (userState.isShortcutMagnificationEnabledLocked() || userState.isMagnificationSingleFingerTripleTapEnabledLocked() || (Flags.enableMagnificationMultipleFingerMultipleTapGesture() diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionManager.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionManager.java index 19e3e690924e..fe06406e580a 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionManager.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionManager.java @@ -19,6 +19,7 @@ package com.android.server.accessibility.magnification; import static android.accessibilityservice.AccessibilityTrace.FLAGS_MAGNIFICATION_CONNECTION; import static android.accessibilityservice.AccessibilityTrace.FLAGS_MAGNIFICATION_CONNECTION_CALLBACK; import static android.os.Build.HW_TIMEOUT_MULTIPLIER; +import static android.os.UserHandle.getCallingUserId; import static android.view.accessibility.MagnificationAnimationCallback.STUB_ANIMATION_CALLBACK; import static com.android.server.accessibility.AccessibilityManagerService.INVALID_SERVICE_ID; @@ -54,6 +55,7 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.server.LocalServices; import com.android.server.accessibility.AccessibilityTraceManager; +import com.android.server.pm.UserManagerInternal; import com.android.server.statusbar.StatusBarManagerInternal; import com.android.server.wm.WindowManagerInternal; @@ -209,6 +211,7 @@ public class MagnificationConnectionManager implements private final Callback mCallback; private final AccessibilityTraceManager mTrace; private final MagnificationScaleProvider mScaleProvider; + private final UserManagerInternal mUserManagerInternal; public MagnificationConnectionManager(Context context, Object lock, @NonNull Callback callback, AccessibilityTraceManager trace, MagnificationScaleProvider scaleProvider) { @@ -217,6 +220,7 @@ public class MagnificationConnectionManager implements mCallback = callback; mTrace = trace; mScaleProvider = scaleProvider; + mUserManagerInternal = LocalServices.getService(UserManagerInternal.class); } /** @@ -280,12 +284,18 @@ public class MagnificationConnectionManager implements * Requests {@link IMagnificationConnection} through * {@link StatusBarManagerInternal#requestMagnificationConnection(boolean)} and * destroys all window magnifications if necessary. + * NOTE: Currently, this is not allowed to call from visible background users.(b/332222893) * * @param connect {@code true} if needs connection, otherwise set the connection to null and * destroy all window magnifications. * @return {@code true} if {@link IMagnificationConnection} state is going to change. */ public boolean requestConnection(boolean connect) { + final int callingUserId = getCallingUserId(); + if (mUserManagerInternal.isVisibleBackgroundFullUser(callingUserId)) { + throw new SecurityException("Visible background user(u" + callingUserId + + " is not permitted to request magnification connection."); + } if (DBG) { Slog.d(TAG, "requestConnection :" + connect); } diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index ca6051874d78..e0c4ebeb9f89 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -734,7 +734,10 @@ public class PhoneWindowManager implements WindowManagerPolicy { KeyEvent.KEYCODE_ASSIST, KeyEvent.KEYCODE_VOICE_ASSIST, KeyEvent.KEYCODE_MUTE, - KeyEvent.KEYCODE_VOLUME_MUTE + KeyEvent.KEYCODE_VOLUME_MUTE, + KeyEvent.KEYCODE_RECENT_APPS, + KeyEvent.KEYCODE_APP_SWITCH, + KeyEvent.KEYCODE_NOTIFICATION )); private static final int MSG_DISPATCH_MEDIA_KEY_WITH_WAKE_LOCK = 3; @@ -2077,12 +2080,21 @@ public class PhoneWindowManager implements WindowManagerPolicy { } switch (mDoubleTapOnHomeBehavior) { case DOUBLE_TAP_HOME_RECENT_SYSTEM_UI: + if (!isKeyEventForCurrentUser( + event.getDisplayId(), event.getKeyCode(), "toggleRecentApps")) { + break; + } notifyKeyGestureCompleted(event, KeyGestureEvent.KEY_GESTURE_TYPE_APP_SWITCH); mHomeConsumed = true; toggleRecentApps(); break; case DOUBLE_TAP_HOME_PIP_MENU: + if (!isKeyEventForCurrentUser( + event.getDisplayId(), event.getKeyCode(), + "showPictureInPictureMenu")) { + break; + } mHomeConsumed = true; showPictureInPictureMenuInternal(); break; @@ -2111,12 +2123,20 @@ public class PhoneWindowManager implements WindowManagerPolicy { } break; case LONG_PRESS_HOME_ASSIST: + if (!isKeyEventForCurrentUser( + event.getDisplayId(), event.getKeyCode(), "launchAssistAction")) { + break; + } notifyKeyGestureCompleted(event, KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_ASSISTANT); launchAssistAction(null, event.getDeviceId(), event.getEventTime(), AssistUtils.INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS); break; case LONG_PRESS_HOME_NOTIFICATION_PANEL: + if (!isKeyEventForCurrentUser( + event.getDisplayId(), event.getKeyCode(), "toggleNotificationPanel")) { + break; + } notifyKeyGestureCompleted(event, KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL); toggleNotificationPanel(); @@ -3477,7 +3497,11 @@ public class PhoneWindowManager implements WindowManagerPolicy { if (isUserSetupComplete() && !keyguardOn) { if (mModifierShortcutManager.interceptKey(event)) { - dismissKeyboardShortcutsMenu(); + if (isKeyEventForCurrentUser( + event.getDisplayId(), event.getKeyCode(), + "dismissKeyboardShortcutsMenu")) { + dismissKeyboardShortcutsMenu(); + } mPendingMetaAction = false; mPendingCapsLockToggle = false; return true; @@ -4733,7 +4757,10 @@ public class PhoneWindowManager implements WindowManagerPolicy { } // no keyguard stuff to worry about, just launch home! - if (mRecentsVisible) { + // If Recents is visible and the action is not from visible background users, + // hide Recents and notify it to launch Home. + if (mRecentsVisible + && (!mVisibleBackgroundUsersEnabled || displayId == DEFAULT_DISPLAY)) { try { ActivityManager.getService().stopAppSwitches(); } catch (RemoteException e) {} @@ -5477,6 +5504,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { * Notify the StatusBar that a system key was pressed. */ private void sendSystemKeyToStatusBar(KeyEvent key) { + if (!isKeyEventForCurrentUser(key.getDisplayId(), key.getKeyCode(), "handleSystemKey")) { + return; + } IStatusBarService statusBar = getStatusBarService(); if (statusBar != null) { try { diff --git a/services/core/java/com/android/server/search/SearchManagerService.java b/services/core/java/com/android/server/search/SearchManagerService.java index 9b39fa1e177c..a49a9fdf4cca 100644 --- a/services/core/java/com/android/server/search/SearchManagerService.java +++ b/services/core/java/com/android/server/search/SearchManagerService.java @@ -46,6 +46,7 @@ import com.android.internal.util.IndentingPrintWriter; import com.android.server.LocalServices; import com.android.server.SystemService; import com.android.server.SystemService.TargetUser; +import com.android.server.pm.UserManagerInternal; import com.android.server.statusbar.StatusBarManagerInternal; import java.io.FileDescriptor; @@ -89,6 +90,8 @@ public class SearchManagerService extends ISearchManager.Stub { @GuardedBy("mSearchables") private final SparseArray mSearchables = new SparseArray<>(); + private final UserManagerInternal mUserManagerInternal; + /** * Initializes the Search Manager service in the provided system context. * Only one instance of this object should be created! @@ -101,6 +104,7 @@ public class SearchManagerService extends ISearchManager.Stub { mMyPackageMonitor.register(context, null, UserHandle.ALL, true); new GlobalSearchProviderObserver(context.getContentResolver()); mHandler = BackgroundThread.getHandler(); + mUserManagerInternal = LocalServices.getService(UserManagerInternal.class); } private Searchables getSearchables(int userId) { @@ -336,6 +340,14 @@ public class SearchManagerService extends ISearchManager.Stub { @Override public void launchAssist(int userHandle, Bundle args) { + // Currently, visible background users are not allowed to launch assist.(b/332222893) + // TODO(b/368715893): Consider indirect calls from system service when checking the + // calling user. + final int callingUserId = UserHandle.getCallingUserId(); + if (mUserManagerInternal.isVisibleBackgroundFullUser(callingUserId)) { + throw new SecurityException("Visible background user(u" + callingUserId + + ") is not permitted to launch assist."); + } StatusBarManagerInternal statusBarManager = LocalServices.getService(StatusBarManagerInternal.class); if (statusBarManager != null) { diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationConnectionManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationConnectionManagerTest.java index 87fe6cf8f283..6d27dddfc357 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationConnectionManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationConnectionManagerTest.java @@ -62,6 +62,7 @@ import androidx.test.filters.FlakyTest; import com.android.internal.util.test.FakeSettingsProvider; import com.android.server.LocalServices; import com.android.server.accessibility.AccessibilityTraceManager; +import com.android.server.pm.UserManagerInternal; import com.android.server.statusbar.StatusBarManagerInternal; import org.junit.Before; @@ -92,12 +93,16 @@ public class MagnificationConnectionManagerTest { private MagnificationConnectionManager.Callback mMockCallback; private MockContentResolver mResolver; private MagnificationConnectionManager mMagnificationConnectionManager; + @Mock + private UserManagerInternal mMockUserManagerInternal; @Before public void setUp() throws RemoteException { MockitoAnnotations.initMocks(this); LocalServices.removeServiceForTest(StatusBarManagerInternal.class); + LocalServices.removeServiceForTest(UserManagerInternal.class); LocalServices.addService(StatusBarManagerInternal.class, mMockStatusBarManagerInternal); + LocalServices.addService(UserManagerInternal.class, mMockUserManagerInternal); mResolver = new MockContentResolver(); mMockConnection = new MockMagnificationConnection(); mMagnificationConnectionManager = new MagnificationConnectionManager(mContext, new Object(), @@ -110,6 +115,8 @@ public class MagnificationConnectionManagerTest { Settings.Secure.putFloatForUser(mResolver, Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, 2.5f, CURRENT_USER_ID); + + when(mMockUserManagerInternal.isVisibleBackgroundFullUser(anyInt())).thenReturn(false); } private void stubSetConnection(boolean needDelay) { -- cgit v1.2.3-59-g8ed1b From 8da0bd0af6afc3a6f4b3a3b8fdc15680290084cb Mon Sep 17 00:00:00 2001 From: Chun-Ku Lin Date: Wed, 7 Aug 2024 05:14:31 +0000 Subject: Use correct user context to get the EnhancedConfirmationManager Bug: 357768060 Bug: 363074042 Test: Download Accessibility Scanner from play store, verified I can turn on the a11y service as a second user Test: atest AccessibilityManagerServiceTest Flag: EXEMPT low risk bugfix Change-Id: I6e01179ae4af791070a359ebf3a4d9e7b80f6575 --- .../accessibility/AccessibilityManagerService.java | 11 +++++-- .../AccessibilityManagerServiceTest.java | 37 ++++++++++++++++++++++ 2 files changed, 45 insertions(+), 3 deletions(-) (limited to 'services/accessibility') diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index 7580b697b516..114541222995 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -4962,9 +4962,14 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub if (android.permission.flags.Flags.enhancedConfirmationModeApisEnabled() && android.security.Flags.extendEcmToAllSettings()) { try { - return !mContext.getSystemService(EnhancedConfirmationManager.class) - .isRestricted(packageName, - AppOpsManager.OPSTR_BIND_ACCESSIBILITY_SERVICE); + final EnhancedConfirmationManager userContextEcm = + mContext.createContextAsUser(UserHandle.of(userId), /* flags = */ 0) + .getSystemService(EnhancedConfirmationManager.class); + if (userContextEcm != null) { + return !userContextEcm.isRestricted(packageName, + AppOpsManager.OPSTR_BIND_ACCESSIBILITY_SERVICE); + } + return false; } catch (PackageManager.NameNotFoundException e) { Log.e(LOG_TAG, "Exception when retrieving package:" + packageName, e); return false; diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java index 566feb7e3d80..7481fc8ec46d 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java @@ -63,8 +63,11 @@ import static org.mockito.Mockito.when; import android.Manifest; import android.accessibilityservice.AccessibilityServiceInfo; import android.accessibilityservice.IAccessibilityServiceClient; +import android.annotation.NonNull; import android.app.PendingIntent; import android.app.RemoteAction; +import android.app.admin.DevicePolicyManager; +import android.app.ecm.EnhancedConfirmationManager; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; @@ -212,6 +215,7 @@ public class AccessibilityManagerServiceTest { @Mock private FullScreenMagnificationController mMockFullScreenMagnificationController; @Mock private ProxyManager mProxyManager; @Mock private StatusBarManagerInternal mStatusBarManagerInternal; + @Mock private DevicePolicyManager mDevicePolicyManager; @Spy private IUserInitializationCompleteCallback mUserInitializationCompleteCallback; @Captor private ArgumentCaptor mIntentArgumentCaptor; private IAccessibilityManager mA11yManagerServiceOnDevice; @@ -241,6 +245,7 @@ public class AccessibilityManagerServiceTest { UserManagerInternal.class, mMockUserManagerInternal); LocalServices.addService(StatusBarManagerInternal.class, mStatusBarManagerInternal); mInputFilter = mock(FakeInputFilter.class); + mTestableContext.addMockSystemService(DevicePolicyManager.class, mDevicePolicyManager); when(mMockMagnificationController.getMagnificationConnectionManager()).thenReturn( mMockMagnificationConnectionManager); @@ -2160,6 +2165,24 @@ public class AccessibilityManagerServiceTest { .isEqualTo(SOFTWARE); } + @Test + @EnableFlags({android.permission.flags.Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED, + android.security.Flags.FLAG_EXTEND_ECM_TO_ALL_SETTINGS}) + public void isAccessibilityTargetAllowed_nonSystemUserId_useEcmWithNonSystemUserId() { + String fakePackageName = "FAKE_PACKAGE_NAME"; + int uid = 0; // uid is not used in the actual implementation when flags are on + int userId = mTestableContext.getUserId() + 1234; + when(mDevicePolicyManager.getPermittedAccessibilityServices(userId)).thenReturn( + List.of(fakePackageName)); + Context mockUserContext = mock(Context.class); + mTestableContext.addMockUserContext(userId, mockUserContext); + + mA11yms.isAccessibilityTargetAllowed(fakePackageName, uid, userId); + + verify(mockUserContext).getSystemService(EnhancedConfirmationManager.class); + } + + private Set readStringsFromSetting(String setting) { final Set result = new ArraySet<>(); mA11yms.readColonDelimitedSettingToSet( @@ -2280,6 +2303,7 @@ public class AccessibilityManagerServiceTest { private final Context mMockContext; private final Map> mBroadcastReceivers = new ArrayMap<>(); + private ArrayMap mMockUserContexts = new ArrayMap<>(); A11yTestableContext(Context base) { super(base); @@ -2317,6 +2341,19 @@ public class AccessibilityManagerServiceTest { return mMockContext; } + public void addMockUserContext(int userId, Context context) { + mMockUserContexts.put(userId, context); + } + + @Override + @NonNull + public Context createContextAsUser(UserHandle user, int flags) { + if (mMockUserContexts.containsKey(user.getIdentifier())) { + return mMockUserContexts.get(user.getIdentifier()); + } + return super.createContextAsUser(user, flags); + } + Map> getBroadcastReceivers() { return mBroadcastReceivers; } -- cgit v1.2.3-59-g8ed1b From 7ce79d8a00c30236b43597c4d1123d309837b430 Mon Sep 17 00:00:00 2001 From: Linyu He Date: Wed, 25 Sep 2024 14:30:56 -0700 Subject: Rename two "service" fields in AbstractA11yServiceConnection (1) mService -> mClientBinder (2) mServiceInterface -> mClient The two "service" fields were introduced a long time ago, probably when app-side AccessibilityServices were the only use case of the a11y subsystem, since the word "service" refers to an AccessibilityService in an app process. Today the a11y subsystem supports more than just AccessibilityServices. UiAutomation (a test API) is one example, and we will add app automation APIs soon. For all the above use cases, the "service" fields represent a client of system_server, and the client can be an AccessibilityService, a UiAutomation, etc. For more clarity, this CL renames "service" as "client". Bug: 369648004 Flag: EXEMPT refactor Test: Presubmit Test: atest CtsAccessibilityTestCases Test: atest CtsAccessibilityServiceTestCases Change-Id: I2118eb3f7185714bc7405c28c63c9a95a380046b --- .../AbstractAccessibilityServiceConnection.java | 237 +++++++++++---------- .../accessibility/AccessibilityManagerService.java | 4 +- .../AccessibilityServiceConnection.java | 86 ++++---- .../ProxyAccessibilityServiceConnection.java | 13 +- .../android/server/accessibility/ProxyManager.java | 6 +- .../server/accessibility/UiAutomationManager.java | 93 ++++---- ...AbstractAccessibilityServiceConnectionTest.java | 12 +- 7 files changed, 231 insertions(+), 220 deletions(-) (limited to 'services/accessibility') diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java index 3224b27d5803..73b7b35ba9a7 100644 --- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java +++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java @@ -165,16 +165,27 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ protected final AccessibilitySecurityPolicy mSecurityPolicy; protected final AccessibilityTrace mTrace; - // The attribution tag set by the service that is bound to this instance + /** The attribution tag set by the client that is bound to this instance */ protected String mAttributionTag; protected int mDisplayTypes = DISPLAY_TYPE_DEFAULT; - // The service that's bound to this instance. Whenever this value is non-null, this - // object is registered as a death recipient - IBinder mService; + /** + * Binder of the {@link #mClient}. + * + *

Whenever this value is non-null, it should be registered as a {@link + * IBinder.DeathRecipient} + */ + @Nullable IBinder mClientBinder; - IAccessibilityServiceClient mServiceInterface; + /** + * The accessibility client this class represents. + * + *

The client is in the application process, i.e., it's a client of system_server. Depending + * on the use case, the client can be an {@link AccessibilityService}, a {@code UiAutomation}, + * etc. + */ + @Nullable IAccessibilityServiceClient mClient; int mEventTypes; @@ -218,10 +229,10 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ int mGenericMotionEventSources; int mObservedMotionEventSources; - // the events pending events to be dispatched to this service + /** Pending events to be dispatched to the client */ final SparseArray mPendingEvents = new SparseArray<>(); - /** Whether this service relies on its {@link AccessibilityCache} being up to date */ + /** Whether the client relies on its {@link AccessibilityCache} being up to date */ boolean mUsesAccessibilityCache = false; // Handler only for dispatching accessibility events since we use event @@ -230,7 +241,7 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ final SparseArray mOverlayWindowTokens = new SparseArray(); - // All the embedded accessibility overlays that have been added by this service. + /** All the embedded accessibility overlays that have been added by the client. */ private List mOverlays = new ArrayList<>(); /** The timestamp of requesting to take screenshot in milliseconds */ @@ -274,7 +285,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ /** * Called back to notify system that the client has changed - * @param serviceInfoChanged True if the service's AccessibilityServiceInfo changed. + * + * @param serviceInfoChanged True if the client's AccessibilityServiceInfo changed. */ void onClientChangeLocked(boolean serviceInfoChanged); @@ -360,21 +372,22 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); mIPlatformCompat = IPlatformCompat.Stub.asInterface( ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE)); - mEventDispatchHandler = new Handler(mainHandler.getLooper()) { - @Override - public void handleMessage(Message message) { - final int eventType = message.what; - AccessibilityEvent event = (AccessibilityEvent) message.obj; - boolean serviceWantsEvent = message.arg1 != 0; - notifyAccessibilityEventInternal(eventType, event, serviceWantsEvent); - } - }; + mEventDispatchHandler = + new Handler(mainHandler.getLooper()) { + @Override + public void handleMessage(Message message) { + final int eventType = message.what; + AccessibilityEvent event = (AccessibilityEvent) message.obj; + boolean clientWantsEvent = message.arg1 != 0; + notifyAccessibilityEventInternal(eventType, event, clientWantsEvent); + } + }; setDynamicallyConfigurableProperties(accessibilityServiceInfo); } @Override public boolean onKeyEvent(KeyEvent keyEvent, int sequenceNumber) { - if (!mRequestFilterKeyEvents || (mServiceInterface == null)) { + if (!mRequestFilterKeyEvents || (mClient == null)) { return false; } if((mAccessibilityServiceInfo.getCapabilities() @@ -388,7 +401,7 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ if (svcClientTracingEnabled()) { logTraceSvcClient("onKeyEvent", keyEvent + ", " + sequenceNumber); } - mServiceInterface.onKeyEvent(keyEvent, sequenceNumber); + mClient.onKeyEvent(keyEvent, sequenceNumber); } catch (RemoteException e) { return false; } @@ -470,7 +483,7 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ } public boolean canReceiveEventsLocked() { - return (mEventTypes != 0 && mService != null); + return (mEventTypes != 0 && mClientBinder != null); } @RequiresNoPermission @@ -520,7 +533,7 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ final long identity = Binder.clearCallingIdentity(); try { synchronized (mLock) { - // If the XML manifest had data to configure the service its info + // If the XML manifest had data to configure the AccessibilityService, its info // should be already set. In such a case update only the dynamically // configurable properties. boolean oldRequestIme = mRequestImeApis; @@ -1733,40 +1746,40 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ try { // Clear the proxy in the other process so this // IAccessibilityServiceConnection can be garbage collected. - if (mServiceInterface != null) { + if (mClient != null) { if (svcClientTracingEnabled()) { logTraceSvcClient("init", "null, " + mId + ", null"); } - mServiceInterface.init(null, mId, null); + mClient.init(null, mId, null); } } catch (RemoteException re) { /* ignore */ } - if (mService != null) { + if (mClientBinder != null) { try { - mService.unlinkToDeath(this, 0); + mClientBinder.unlinkToDeath(this, 0); } catch (NoSuchElementException e) { Slog.e(LOG_TAG, "Failed unregistering death link"); } - mService = null; + mClientBinder = null; } - mServiceInterface = null; + mClient = null; mReceivedAccessibilityButtonCallbackSinceBind = false; } public boolean isConnectedLocked() { - return (mService != null); + return (mClientBinder != null); } public void notifyAccessibilityEvent(AccessibilityEvent event) { synchronized (mLock) { final int eventType = event.getEventType(); - final boolean serviceWantsEvent = wantsEventLocked(event); + final boolean clientWantsEvent = clientWantsEventLocked(event); final boolean requiredForCacheConsistency = mUsesAccessibilityCache && ((AccessibilityCache.CACHE_CRITICAL_EVENTS_MASK & eventType) != 0); - if (!serviceWantsEvent && !requiredForCacheConsistency) { + if (!clientWantsEvent && !requiredForCacheConsistency) { return; } @@ -1774,7 +1787,7 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ return; } // Make a copy since during dispatch it is possible the event to - // be modified to remove its source if the receiving service does + // be modified to remove its source if the receiving client does // not have permission to access the window content. AccessibilityEvent newEvent = AccessibilityEvent.obtain(event); Message message; @@ -1792,22 +1805,20 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ // Send all messages, bypassing mPendingEvents message = mEventDispatchHandler.obtainMessage(eventType, newEvent); } - message.arg1 = serviceWantsEvent ? 1 : 0; + message.arg1 = clientWantsEvent ? 1 : 0; mEventDispatchHandler.sendMessageDelayed(message, mNotificationTimeout); } } /** - * Determines if given event can be dispatched to a service based on the package of the - * event source. Specifically, a service is notified if it is interested in events from the - * package. + * Determines if given event can be dispatched to a client based on the package of the event + * source. Specifically, a client is notified if it is interested in events from the package. * * @param event The event. - * @return True if the listener should be notified, false otherwise. + * @return True if the client should be notified, false otherwise. */ - private boolean wantsEventLocked(AccessibilityEvent event) { - + private boolean clientWantsEventLocked(AccessibilityEvent event) { if (!canReceiveEventsLocked()) { return false; } @@ -1838,22 +1849,20 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ } /** - * Notifies an accessibility service client for a scheduled event given the event type. + * Notifies a client for a scheduled event given the event type. * * @param eventType The type of the event to dispatch. */ private void notifyAccessibilityEventInternal( - int eventType, - AccessibilityEvent event, - boolean serviceWantsEvent) { - IAccessibilityServiceClient listener; + int eventType, AccessibilityEvent event, boolean clientWantsEvent) { + IAccessibilityServiceClient client; synchronized (mLock) { - listener = mServiceInterface; + client = mClient; - // If the service died/was disabled while the message for dispatching - // the accessibility event was propagating the listener may be null. - if (listener == null) { + // If the client (in the application process) died/was disabled while the message for + // dispatching the accessibility event was propagating, "client" may be null. + if (client == null) { return; } @@ -1868,7 +1877,7 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ // 1) A binder thread calls notifyAccessibilityServiceDelayedLocked // which posts a message for dispatching an event and stores the event // in mPendingEvents. - // 2) The message is pulled from the queue by the handler on the service + // 2) The message is pulled from the queue by the handler on the client // thread and this method is just about to acquire the lock. // 3) Another binder thread acquires the lock in notifyAccessibilityEvent // 4) notifyAccessibilityEvent recycles the event that this method was about @@ -1876,7 +1885,7 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ // 5) This method grabs the new event, processes it, and removes it from // mPendingEvents // 6) The second message dispatched in (4) arrives, but the event has been - // remvoved in (5). + // removed in (5). event = mPendingEvents.get(eventType); if (event == null) { return; @@ -1893,14 +1902,14 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ try { if (svcClientTracingEnabled()) { - logTraceSvcClient("onAccessibilityEvent", event + ";" + serviceWantsEvent); + logTraceSvcClient("onAccessibilityEvent", event + ";" + clientWantsEvent); } - listener.onAccessibilityEvent(event, serviceWantsEvent); + client.onAccessibilityEvent(event, clientWantsEvent); if (DEBUG) { - Slog.i(LOG_TAG, "Event " + event + " sent to " + listener); + Slog.i(LOG_TAG, "Event " + event + " sent to " + client); } } catch (RemoteException re) { - Slog.e(LOG_TAG, "Error during sending " + event + " to " + listener, re); + Slog.e(LOG_TAG, "Error during sending " + event + " to " + client, re); } finally { event.recycle(); } @@ -1978,122 +1987,126 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ return (mGenericMotionEventSources & eventSourceWithoutClass) != 0; } - /** - * Called by the invocation handler to notify the service that the - * state of magnification has changed. + * Called by the invocation handler to notify the client that the state of magnification has + * changed. */ - private void notifyMagnificationChangedInternal(int displayId, @NonNull Region region, - @NonNull MagnificationConfig config) { - final IAccessibilityServiceClient listener = getServiceInterfaceSafely(); - if (listener != null) { + private void notifyMagnificationChangedInternal( + int displayId, @NonNull Region region, @NonNull MagnificationConfig config) { + final IAccessibilityServiceClient client = getClientSafely(); + if (client != null) { try { if (svcClientTracingEnabled()) { logTraceSvcClient("onMagnificationChanged", displayId + ", " + region + ", " + config.toString()); } - listener.onMagnificationChanged(displayId, region, config); + client.onMagnificationChanged(displayId, region, config); } catch (RemoteException re) { - Slog.e(LOG_TAG, "Error sending magnification changes to " + mService, re); + Slog.e(LOG_TAG, "Error sending magnification changes to " + mClientBinder, re); } } } /** - * Called by the invocation handler to notify the service that the state of the soft - * keyboard show mode has changed. + * Called by the invocation handler to notify the client that the state of the soft keyboard + * show mode has changed. */ private void notifySoftKeyboardShowModeChangedInternal(int showState) { - final IAccessibilityServiceClient listener = getServiceInterfaceSafely(); - if (listener != null) { + final IAccessibilityServiceClient client = getClientSafely(); + if (client != null) { try { if (svcClientTracingEnabled()) { logTraceSvcClient("onSoftKeyboardShowModeChanged", String.valueOf(showState)); } - listener.onSoftKeyboardShowModeChanged(showState); + client.onSoftKeyboardShowModeChanged(showState); } catch (RemoteException re) { - Slog.e(LOG_TAG, "Error sending soft keyboard show mode changes to " + mService, + Slog.e( + LOG_TAG, + "Error sending soft keyboard show mode changes to " + mClientBinder, re); } } } private void notifyAccessibilityButtonClickedInternal(int displayId) { - final IAccessibilityServiceClient listener = getServiceInterfaceSafely(); - if (listener != null) { + final IAccessibilityServiceClient client = getClientSafely(); + if (client != null) { try { if (svcClientTracingEnabled()) { logTraceSvcClient("onAccessibilityButtonClicked", String.valueOf(displayId)); } - listener.onAccessibilityButtonClicked(displayId); + client.onAccessibilityButtonClicked(displayId); } catch (RemoteException re) { - Slog.e(LOG_TAG, "Error sending accessibility button click to " + mService, re); + Slog.e(LOG_TAG, "Error sending accessibility button click to " + mClientBinder, re); } } } private void notifyAccessibilityButtonAvailabilityChangedInternal(boolean available) { - // Only notify the service if it's not been notified or the state has changed + // Only notify the client if it's not been notified or the state has changed if (mReceivedAccessibilityButtonCallbackSinceBind && (mLastAccessibilityButtonCallbackState == available)) { return; } mReceivedAccessibilityButtonCallbackSinceBind = true; mLastAccessibilityButtonCallbackState = available; - final IAccessibilityServiceClient listener = getServiceInterfaceSafely(); - if (listener != null) { + final IAccessibilityServiceClient client = getClientSafely(); + if (client != null) { try { if (svcClientTracingEnabled()) { logTraceSvcClient("onAccessibilityButtonAvailabilityChanged", String.valueOf(available)); } - listener.onAccessibilityButtonAvailabilityChanged(available); + client.onAccessibilityButtonAvailabilityChanged(available); } catch (RemoteException re) { - Slog.e(LOG_TAG, - "Error sending accessibility button availability change to " + mService, + Slog.e( + LOG_TAG, + "Error sending accessibility button availability change to " + + mClientBinder, re); } } } private void notifyGestureInternal(AccessibilityGestureEvent gestureInfo) { - final IAccessibilityServiceClient listener = getServiceInterfaceSafely(); - if (listener != null) { + final IAccessibilityServiceClient client = getClientSafely(); + if (client != null) { try { if (svcClientTracingEnabled()) { logTraceSvcClient("onGesture", gestureInfo.toString()); } - listener.onGesture(gestureInfo); + client.onGesture(gestureInfo); } catch (RemoteException re) { - Slog.e(LOG_TAG, "Error during sending gesture " + gestureInfo - + " to " + mService, re); + Slog.e( + LOG_TAG, + "Error during sending gesture " + gestureInfo + " to " + mClientBinder, + re); } } } private void notifySystemActionsChangedInternal() { - final IAccessibilityServiceClient listener = getServiceInterfaceSafely(); - if (listener != null) { + final IAccessibilityServiceClient client = getClientSafely(); + if (client != null) { try { if (svcClientTracingEnabled()) { logTraceSvcClient("onSystemActionsChanged", ""); } - listener.onSystemActionsChanged(); + client.onSystemActionsChanged(); } catch (RemoteException re) { - Slog.e(LOG_TAG, "Error sending system actions change to " + mService, - re); + Slog.e(LOG_TAG, "Error sending system actions change to " + mClientBinder, re); } } } private void notifyClearAccessibilityCacheInternal() { - final IAccessibilityServiceClient listener = getServiceInterfaceSafely(); - if (listener != null) { + final IAccessibilityServiceClient client = getClientSafely(); + if (client != null) { try { if (svcClientTracingEnabled()) { logTraceSvcClient("clearAccessibilityCache", ""); } - listener.clearAccessibilityCache(); + client.clearAccessibilityCache(); } catch (RemoteException re) { Slog.e(LOG_TAG, "Error during requesting accessibility info cache" + " to be cleared.", re); @@ -2106,70 +2119,66 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ private void setImeSessionEnabledInternal(IAccessibilityInputMethodSession session, boolean enabled) { - final IAccessibilityServiceClient listener = getServiceInterfaceSafely(); - if (listener != null && session != null) { + final IAccessibilityServiceClient client = getClientSafely(); + if (client != null && session != null) { try { if (svcClientTracingEnabled()) { logTraceSvcClient("createImeSession", ""); } - listener.setImeSessionEnabled(session, enabled); + client.setImeSessionEnabled(session, enabled); } catch (RemoteException re) { - Slog.e(LOG_TAG, - "Error requesting IME session from " + mService, re); + Slog.e(LOG_TAG, "Error requesting IME session from " + mClientBinder, re); } } } private void bindInputInternal() { - final IAccessibilityServiceClient listener = getServiceInterfaceSafely(); - if (listener != null) { + final IAccessibilityServiceClient client = getClientSafely(); + if (client != null) { try { if (svcClientTracingEnabled()) { logTraceSvcClient("bindInput", ""); } - listener.bindInput(); + client.bindInput(); } catch (RemoteException re) { - Slog.e(LOG_TAG, - "Error binding input to " + mService, re); + Slog.e(LOG_TAG, "Error binding input to " + mClientBinder, re); } } } private void unbindInputInternal() { - final IAccessibilityServiceClient listener = getServiceInterfaceSafely(); - if (listener != null) { + final IAccessibilityServiceClient client = getClientSafely(); + if (client != null) { try { if (svcClientTracingEnabled()) { logTraceSvcClient("unbindInput", ""); } - listener.unbindInput(); + client.unbindInput(); } catch (RemoteException re) { - Slog.e(LOG_TAG, - "Error unbinding input to " + mService, re); + Slog.e(LOG_TAG, "Error unbinding input to " + mClientBinder, re); } } } private void startInputInternal(IRemoteAccessibilityInputConnection connection, EditorInfo editorInfo, boolean restarting) { - final IAccessibilityServiceClient listener = getServiceInterfaceSafely(); - if (listener != null) { + final IAccessibilityServiceClient client = getClientSafely(); + if (client != null) { try { if (svcClientTracingEnabled()) { logTraceSvcClient("startInput", "editorInfo=" + editorInfo + " restarting=" + restarting); } - listener.startInput(connection, editorInfo, restarting); + client.startInput(connection, editorInfo, restarting); } catch (RemoteException re) { - Slog.e(LOG_TAG, - "Error starting input to " + mService, re); + Slog.e(LOG_TAG, "Error starting input to " + mClientBinder, re); } } } - protected IAccessibilityServiceClient getServiceInterfaceSafely() { + protected IAccessibilityServiceClient getClientSafely() { synchronized (mLock) { - return mServiceInterface; + return mClient; } } diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index 7580b697b516..fa5b78d2d946 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -1435,8 +1435,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub interfacesToInterrupt = new ArrayList<>(services.size()); for (int i = 0; i < services.size(); i++) { AccessibilityServiceConnection service = services.get(i); - IBinder a11yServiceBinder = service.mService; - IAccessibilityServiceClient a11yServiceInterface = service.mServiceInterface; + IBinder a11yServiceBinder = service.mClientBinder; + IAccessibilityServiceClient a11yServiceInterface = service.mClient; if ((a11yServiceBinder != null) && (a11yServiceInterface != null)) { interfacesToInterrupt.add(a11yServiceInterface); } diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java index 786d167af5de..15999d19ebc0 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java @@ -166,8 +166,9 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect if (userState.getBindInstantServiceAllowedLocked()) { flags |= Context.BIND_ALLOW_INSTANT; } - if (mService == null && mContext.bindServiceAsUser( - mIntent, this, flags, new UserHandle(userState.mUserId))) { + if (mClientBinder == null + && mContext.bindServiceAsUser( + mIntent, this, flags, new UserHandle(userState.mUserId))) { userState.getBindingServicesLocked().add(mComponentName); } } finally { @@ -227,20 +228,20 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect addWindowTokensForAllDisplays(); } synchronized (mLock) { - if (mService != service) { - if (mService != null) { - mService.unlinkToDeath(this, 0); + if (mClientBinder != service) { + if (mClientBinder != null) { + mClientBinder.unlinkToDeath(this, 0); } - mService = service; + mClientBinder = service; try { - mService.linkToDeath(this, 0); + mClientBinder.linkToDeath(this, 0); } catch (RemoteException re) { Slog.e(LOG_TAG, "Failed registering death link"); binderDied(); return; } } - mServiceInterface = IAccessibilityServiceClient.Stub.asInterface(service); + mClient = IAccessibilityServiceClient.Stub.asInterface(service); if (userState == null) return; userState.addServiceLocked(this); mSystemSupport.onClientChangeLocked(false); @@ -261,7 +262,7 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect } private void initializeService() { - IAccessibilityServiceClient serviceInterface = null; + IAccessibilityServiceClient client = null; synchronized (mLock) { AccessibilityUserState userState = mUserStateWeakReference.get(); if (userState == null) return; @@ -272,18 +273,17 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect bindingServices.remove(mComponentName); crashedServices.remove(mComponentName); mAccessibilityServiceInfo.crashed = false; - serviceInterface = mServiceInterface; + client = mClient; } // There's a chance that service is removed from enabled_accessibility_services setting // key, but skip unbinding because of it's in binding state. Unbinds it if it's // not in enabled service list. - if (serviceInterface != null - && !userState.getEnabledServicesLocked().contains(mComponentName)) { + if (client != null && !userState.getEnabledServicesLocked().contains(mComponentName)) { mSystemSupport.onClientChangeLocked(false); return; } } - if (serviceInterface == null) { + if (client == null) { binderDied(); return; } @@ -292,10 +292,9 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect logTraceSvcClient("init", this + "," + mId + "," + mOverlayWindowTokens.get(Display.DEFAULT_DISPLAY)); } - serviceInterface.init(this, mId, mOverlayWindowTokens.get(Display.DEFAULT_DISPLAY)); + client.init(this, mId, mOverlayWindowTokens.get(Display.DEFAULT_DISPLAY)); } catch (RemoteException re) { - Slog.w(LOG_TAG, "Error while setting connection for service: " - + serviceInterface, re); + Slog.w(LOG_TAG, "Error while setting connection for service: " + client, re); binderDied(); } } @@ -496,7 +495,7 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect @Override public boolean isCapturingFingerprintGestures() { - return (mServiceInterface != null) + return (mClient != null) && mSecurityPolicy.canCaptureFingerprintGestures(this) && mCaptureFingerprintGestures; } @@ -506,17 +505,17 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect if (!isCapturingFingerprintGestures()) { return; } - IAccessibilityServiceClient serviceInterface; + IAccessibilityServiceClient client; synchronized (mLock) { - serviceInterface = mServiceInterface; + client = mClient; } - if (serviceInterface != null) { + if (client != null) { try { if (svcClientTracingEnabled()) { logTraceSvcClient( "onFingerprintCapturingGesturesChanged", String.valueOf(active)); } - mServiceInterface.onFingerprintCapturingGesturesChanged(active); + mClient.onFingerprintCapturingGesturesChanged(active); } catch (RemoteException e) { } } @@ -527,16 +526,16 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect if (!isCapturingFingerprintGestures()) { return; } - IAccessibilityServiceClient serviceInterface; + IAccessibilityServiceClient client; synchronized (mLock) { - serviceInterface = mServiceInterface; + client = mClient; } - if (serviceInterface != null) { + if (client != null) { try { if (svcClientTracingEnabled()) { logTraceSvcClient("onFingerprintGesture", String.valueOf(gesture)); } - mServiceInterface.onFingerprintGesture(gesture); + mClient.onFingerprintGesture(gesture); } catch (RemoteException e) { } } @@ -546,7 +545,7 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect @Override public void dispatchGesture(int sequence, ParceledListSlice gestureSteps, int displayId) { synchronized (mLock) { - if (mServiceInterface != null && mSecurityPolicy.canPerformGestures(this)) { + if (mClient != null && mSecurityPolicy.canPerformGestures(this)) { final long identity = Binder.clearCallingIdentity(); try { MotionEventInjector motionEventInjector = @@ -557,16 +556,18 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect if (motionEventInjector != null && mWindowManagerService.isTouchOrFaketouchDevice()) { motionEventInjector.injectEvents( - gestureSteps.getList(), mServiceInterface, sequence, displayId); + gestureSteps.getList(), mClient, sequence, displayId); } else { try { if (svcClientTracingEnabled()) { logTraceSvcClient("onPerformGestureResult", sequence + ", false"); } - mServiceInterface.onPerformGestureResult(sequence, false); + mClient.onPerformGestureResult(sequence, false); } catch (RemoteException re) { - Slog.e(LOG_TAG, "Error sending motion event injection failure to " - + mServiceInterface, re); + Slog.e( + LOG_TAG, + "Error sending motion event injection failure to " + mClient, + re); } } } finally { @@ -631,48 +632,47 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect @Override protected void createImeSessionInternal() { - final IAccessibilityServiceClient listener = getServiceInterfaceSafely(); - if (listener != null) { + final IAccessibilityServiceClient client = getClientSafely(); + if (client != null) { try { if (svcClientTracingEnabled()) { logTraceSvcClient("createImeSession", ""); } AccessibilityInputMethodSessionCallback callback = new AccessibilityInputMethodSessionCallback(mUserId); - listener.createImeSession(callback); + client.createImeSession(callback); } catch (RemoteException re) { - Slog.e(LOG_TAG, - "Error requesting IME session from " + mService, re); + Slog.e(LOG_TAG, "Error requesting IME session from " + mClientBinder, re); } } } private void notifyMotionEventInternal(MotionEvent event) { - final IAccessibilityServiceClient listener = getServiceInterfaceSafely(); - if (listener != null) { + final IAccessibilityServiceClient client = getClientSafely(); + if (client != null) { try { if (mTrace.isA11yTracingEnabled()) { logTraceSvcClient(".onMotionEvent ", event.toString()); } - listener.onMotionEvent(event); + client.onMotionEvent(event); } catch (RemoteException re) { - Slog.e(LOG_TAG, "Error sending motion event to" + mService, re); + Slog.e(LOG_TAG, "Error sending motion event to" + mClientBinder, re); } } } private void notifyTouchStateInternal(int displayId, int state) { - final IAccessibilityServiceClient listener = getServiceInterfaceSafely(); - if (listener != null) { + final IAccessibilityServiceClient client = getClientSafely(); + if (client != null) { try { if (mTrace.isA11yTracingEnabled()) { logTraceSvcClient(".onTouchStateChanged ", TouchInteractionController.stateToString(state)); } - listener.onTouchStateChanged(displayId, state); + client.onTouchStateChanged(displayId, state); } catch (RemoteException re) { - Slog.e(LOG_TAG, "Error sending motion event to" + mService, re); + Slog.e(LOG_TAG, "Error sending motion event to" + mClientBinder, re); } } } diff --git a/services/accessibility/java/com/android/server/accessibility/ProxyAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/ProxyAccessibilityServiceConnection.java index 4cb3d247edb0..cd97d838e3a0 100644 --- a/services/accessibility/java/com/android/server/accessibility/ProxyAccessibilityServiceConnection.java +++ b/services/accessibility/java/com/android/server/accessibility/ProxyAccessibilityServiceConnection.java @@ -109,14 +109,11 @@ public class ProxyAccessibilityServiceConnection extends AccessibilityServiceCon return mDeviceId; } - /** - * Called when the proxy is registered. - */ - void initializeServiceInterface(IAccessibilityServiceClient serviceInterface) - throws RemoteException { - mServiceInterface = serviceInterface; - mService = serviceInterface.asBinder(); - mServiceInterface.init(this, mId, this.mOverlayWindowTokens.get(mDisplayId)); + /** Called when the proxy is registered. */ + void initializeClient(IAccessibilityServiceClient client) throws RemoteException { + mClient = client; + mClientBinder = client.asBinder(); + mClient.init(this, mId, this.mOverlayWindowTokens.get(mDisplayId)); } /** diff --git a/services/accessibility/java/com/android/server/accessibility/ProxyManager.java b/services/accessibility/java/com/android/server/accessibility/ProxyManager.java index b4deeb0a6872..da11a76d5282 100644 --- a/services/accessibility/java/com/android/server/accessibility/ProxyManager.java +++ b/services/accessibility/java/com/android/server/accessibility/ProxyManager.java @@ -214,7 +214,7 @@ public class ProxyManager { mA11yInputFilter.disableFeaturesForDisplayIfInstalled(displayId); } }); - connection.initializeServiceInterface(client); + connection.initializeClient(client); } private void registerVirtualDeviceListener() { @@ -561,8 +561,8 @@ public class ProxyManager { final ProxyAccessibilityServiceConnection proxy = mProxyA11yServiceConnections.valueAt(i); if (proxy != null && proxy.getDeviceId() == deviceId) { - final IBinder proxyBinder = proxy.mService; - final IAccessibilityServiceClient proxyInterface = proxy.mServiceInterface; + final IBinder proxyBinder = proxy.mClientBinder; + final IAccessibilityServiceClient proxyInterface = proxy.mClient; if ((proxyBinder != null) && (proxyInterface != null)) { interfaces.add(proxyInterface); } diff --git a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java index f85d786f89c5..ed4eeb534412 100644 --- a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java +++ b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java @@ -107,8 +107,7 @@ class UiAutomationManager { Binder.getCallingUserHandle().getIdentifier()); if (mUiAutomationService != null) { throw new IllegalStateException( - "UiAutomationService " + mUiAutomationService.mServiceInterface - + "already registered!"); + "UiAutomationService " + mUiAutomationService.mClient + "already registered!"); } try { @@ -130,10 +129,9 @@ class UiAutomationManager { mainHandler, mLock, securityPolicy, systemSupport, trace, windowManagerInternal, systemActionPerformer, awm); mUiAutomationServiceOwner = owner; - mUiAutomationService.mServiceInterface = serviceClient; + mUiAutomationService.mClient = serviceClient; try { - mUiAutomationService.mServiceInterface.asBinder().linkToDeath(mUiAutomationService, - 0); + mUiAutomationService.mClient.asBinder().linkToDeath(mUiAutomationService, 0); } catch (RemoteException re) { Slog.e(LOG_TAG, "Failed registering death link: " + re); destroyUiAutomationService(); @@ -149,10 +147,10 @@ class UiAutomationManager { synchronized (mLock) { if (useAccessibility() && ((mUiAutomationService == null) - || (serviceClient == null) - || (mUiAutomationService.mServiceInterface == null) - || (serviceClient.asBinder() - != mUiAutomationService.mServiceInterface.asBinder()))) { + || (serviceClient == null) + || (mUiAutomationService.mClient == null) + || (serviceClient.asBinder() + != mUiAutomationService.mClient.asBinder()))) { throw new IllegalStateException("UiAutomationService " + serviceClient + " not registered!"); } @@ -230,8 +228,7 @@ class UiAutomationManager { private void destroyUiAutomationService() { synchronized (mLock) { if (mUiAutomationService != null) { - mUiAutomationService.mServiceInterface.asBinder().unlinkToDeath( - mUiAutomationService, 0); + mUiAutomationService.mClient.asBinder().unlinkToDeath(mUiAutomationService, 0); mUiAutomationService.onRemoved(); mUiAutomationService.resetLocked(); mUiAutomationService = null; @@ -271,40 +268,48 @@ class UiAutomationManager { void connectServiceUnknownThread() { // This needs to be done on the main thread - mMainHandler.post(() -> { - try { - final IAccessibilityServiceClient serviceInterface; - final UiAutomationService uiAutomationService; - synchronized (mLock) { - serviceInterface = mServiceInterface; - uiAutomationService = mUiAutomationService; - if (serviceInterface == null) { - mService = null; - } else { - mService = mServiceInterface.asBinder(); - mService.linkToDeath(this, 0); + mMainHandler.post( + () -> { + try { + final IAccessibilityServiceClient client; + final UiAutomationService uiAutomationService; + synchronized (mLock) { + client = mClient; + uiAutomationService = mUiAutomationService; + if (client == null) { + mClientBinder = null; + } else { + mClientBinder = mClient.asBinder(); + mClientBinder.linkToDeath(this, 0); + } + } + // If the client is null, the UiAutomation has been shut down on + // another thread. + if (client != null && uiAutomationService != null) { + uiAutomationService.addWindowTokensForAllDisplays(); + if (mTrace.isA11yTracingEnabledForTypes( + AccessibilityTrace.FLAGS_ACCESSIBILITY_SERVICE_CLIENT)) { + mTrace.logTrace( + "UiAutomationService.connectServiceUnknownThread", + AccessibilityTrace.FLAGS_ACCESSIBILITY_SERVICE_CLIENT, + "serviceConnection=" + + this + + ";connectionId=" + + mId + + "windowToken=" + + mOverlayWindowTokens.get( + Display.DEFAULT_DISPLAY)); + } + client.init( + this, + mId, + mOverlayWindowTokens.get(Display.DEFAULT_DISPLAY)); + } + } catch (RemoteException re) { + Slog.w(LOG_TAG, "Error initializing connection", re); + destroyUiAutomationService(); } - } - // If the serviceInterface is null, the UiAutomation has been shut down on - // another thread. - if (serviceInterface != null && uiAutomationService != null) { - uiAutomationService.addWindowTokensForAllDisplays(); - if (mTrace.isA11yTracingEnabledForTypes( - AccessibilityTrace.FLAGS_ACCESSIBILITY_SERVICE_CLIENT)) { - mTrace.logTrace("UiAutomationService.connectServiceUnknownThread", - AccessibilityTrace.FLAGS_ACCESSIBILITY_SERVICE_CLIENT, - "serviceConnection=" + this + ";connectionId=" + mId - + "windowToken=" - + mOverlayWindowTokens.get(Display.DEFAULT_DISPLAY)); - } - serviceInterface.init(this, mId, - mOverlayWindowTokens.get(Display.DEFAULT_DISPLAY)); - } - } catch (RemoteException re) { - Slog.w(LOG_TAG, "Error initializing connection", re); - destroyUiAutomationService(); - } - }); + }); } @Override diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java index 6e6d5a870031..8dfd54fe38bc 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java @@ -174,8 +174,8 @@ public class AbstractAccessibilityServiceConnectionTest { @Mock private AccessibilityTrace mMockA11yTrace; @Mock private WindowManagerInternal mMockWindowManagerInternal; @Mock private SystemActionPerformer mMockSystemActionPerformer; - @Mock private IBinder mMockService; - @Mock private IAccessibilityServiceClient mMockServiceInterface; + @Mock private IBinder mMockClientBinder; + @Mock private IAccessibilityServiceClient mMockClient; @Mock private KeyEventDispatcher mMockKeyEventDispatcher; @Mock private IAccessibilityInteractionConnection mMockIA11yInteractionConnection; @Mock private IAccessibilityInteractionConnectionCallback mMockCallback; @@ -247,9 +247,9 @@ public class AbstractAccessibilityServiceConnectionTest { mSpyServiceInfo, SERVICE_ID, mHandler, new Object(), mMockSecurityPolicy, mMockSystemSupport, mMockA11yTrace, mMockWindowManagerInternal, mMockSystemActionPerformer, mMockA11yWindowManager); - // Assume that the service is connected - mServiceConnection.mService = mMockService; - mServiceConnection.mServiceInterface = mMockServiceInterface; + // Assume that the client is connected + mServiceConnection.mClientBinder = mMockClientBinder; + mServiceConnection.mClient = mMockClient; // Update security policy for this service when(mMockSecurityPolicy.checkAccessibilityAccess(mServiceConnection)).thenReturn(true); @@ -273,7 +273,7 @@ public class AbstractAccessibilityServiceConnectionTest { final KeyEvent mockKeyEvent = mock(KeyEvent.class); mServiceConnection.onKeyEvent(mockKeyEvent, sequenceNumber); - verify(mMockServiceInterface).onKeyEvent(mockKeyEvent, sequenceNumber); + verify(mMockClient).onKeyEvent(mockKeyEvent, sequenceNumber); } @Test -- cgit v1.2.3-59-g8ed1b From 6bea0d919a5679b3a83a0b6d61c7338d8949b205 Mon Sep 17 00:00:00 2001 From: Hiroki Sato Date: Wed, 2 Oct 2024 10:43:10 +0900 Subject: Fix FullScreenMagnificationController#persistScale for multi-display This also fixes the related test to correctly set up precondition. Bug: 370851963 Test: FullScreenMagnificationControllerTest Flag: EXEMPT bugfix Change-Id: Ieb18d4a42fda5d0fa631356e0bc5ec79132530c4 --- .../FullScreenMagnificationController.java | 4 +-- .../FullScreenMagnificationControllerTest.java | 37 +++++++++++++++++++++- 2 files changed, 38 insertions(+), 3 deletions(-) (limited to 'services/accessibility') diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java index 6b6b39df24d7..a77ba624a68f 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java @@ -47,7 +47,6 @@ import android.util.MathUtils; import android.util.Slog; import android.util.SparseArray; import android.util.TypedValue; -import android.view.Display; import android.view.DisplayInfo; import android.view.MagnificationSpec; import android.view.View; @@ -1637,9 +1636,10 @@ public class FullScreenMagnificationController implements * if scale is >= {@link MagnificationConstants.PERSISTED_SCALE_MIN_VALUE}. * We assume if the scale is < {@link MagnificationConstants.PERSISTED_SCALE_MIN_VALUE}, there * will be no obvious magnification effect. + * Only the value of the default display is persisted in user's settings. */ public void persistScale(int displayId) { - final float scale = getScale(Display.DEFAULT_DISPLAY); + final float scale = getScale(displayId); if (scale < MagnificationConstants.PERSISTED_SCALE_MIN_VALUE) { return; } diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java index 1426d5d20419..c4b4afd13a60 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java @@ -69,6 +69,7 @@ import androidx.test.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; import com.android.internal.R; +import com.android.internal.os.BackgroundThread; import com.android.internal.util.ConcurrentUtils; import com.android.internal.util.test.FakeSettingsProvider; import com.android.server.LocalServices; @@ -92,6 +93,8 @@ import org.mockito.stubbing.Answer; import org.testng.Assert; import java.util.Locale; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; @RunWith(AndroidJUnit4.class) public class FullScreenMagnificationControllerTest { @@ -1440,18 +1443,41 @@ public class FullScreenMagnificationControllerTest { @Test public void persistScale_setValueWhenScaleIsOne_nothingChanged() { + register(TEST_DISPLAY); final float persistedScale = mFullScreenMagnificationController.getPersistedScale(TEST_DISPLAY); PointF pivotPoint = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER; - mFullScreenMagnificationController.setScale(DISPLAY_0, 1.0f, pivotPoint.x, pivotPoint.y, + mFullScreenMagnificationController.setScale(TEST_DISPLAY, 1.0f, pivotPoint.x, pivotPoint.y, false, SERVICE_ID_1); mFullScreenMagnificationController.persistScale(TEST_DISPLAY); + // persistScale may post a task to a background thread. Let's wait for it completes. + waitForBackgroundThread(); Assert.assertEquals(mFullScreenMagnificationController.getPersistedScale(TEST_DISPLAY), persistedScale); } + @Test + public void persistScale_setValuesOnMultipleDisplays() { + register(DISPLAY_0); + register(DISPLAY_1); + final PointF pivotPoint = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER; + mFullScreenMagnificationController.setScale(DISPLAY_0, 3.0f, pivotPoint.x, pivotPoint.y, + false, SERVICE_ID_1); + mFullScreenMagnificationController.persistScale(DISPLAY_0); + mFullScreenMagnificationController.setScale(DISPLAY_1, 4.0f, pivotPoint.x, pivotPoint.y, + false, SERVICE_ID_1); + mFullScreenMagnificationController.persistScale(DISPLAY_1); + + // persistScale may post a task to a background thread. Let's wait for it completes. + waitForBackgroundThread(); + Assert.assertEquals(mFullScreenMagnificationController.getPersistedScale(DISPLAY_0), + 3.0f); + Assert.assertEquals(mFullScreenMagnificationController.getPersistedScale(DISPLAY_1), + 4.0f); + } + @Test public void testOnContextChanged_alwaysOnFeatureDisabled_resetMagnification() { setScaleToMagnifying(); @@ -1494,6 +1520,15 @@ public class FullScreenMagnificationControllerTest { ); } + private static void waitForBackgroundThread() { + final CompletableFuture future = new CompletableFuture<>(); + BackgroundThread.getHandler().post(() -> future.complete(null)); + try { + future.get(); + } catch (InterruptedException | ExecutionException ignore) { + } + } + private void setScaleToMagnifying() { register(DISPLAY_0); float scale = 2.0f; -- cgit v1.2.3-59-g8ed1b From 69f781014ea220802a60abf8c865c7f8bf0a7025 Mon Sep 17 00:00:00 2001 From: Hiroki Sato Date: Tue, 1 Oct 2024 13:24:52 +0900 Subject: Add aconfig flag for enlaring pointer icon with magnification Bug: 355734856 Test: build Flag: com.android.server.accessibility.magnification_enlarge_pointer Change-Id: Ia041e5651a434feb12884f92e19029eda411b994 --- services/accessibility/accessibility.aconfig | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'services/accessibility') diff --git a/services/accessibility/accessibility.aconfig b/services/accessibility/accessibility.aconfig index ee3bbcaf711d..034127c0420e 100644 --- a/services/accessibility/accessibility.aconfig +++ b/services/accessibility/accessibility.aconfig @@ -161,6 +161,13 @@ flag { } } +flag { + name: "magnification_enlarge_pointer" + namespace: "accessibility" + description: "When fullscreen magnification is enabled, pointer icon is enlarged" + bug: "355734856" +} + flag { name: "manager_avoid_receiver_timeout" namespace: "accessibility" -- cgit v1.2.3-59-g8ed1b From ad97b5e9f8f3146d5d10623525b74860aa1acf93 Mon Sep 17 00:00:00 2001 From: Asmita Poddar Date: Wed, 9 Oct 2024 15:33:36 +0000 Subject: Add left/ right scrolling for mouse keys Earlier we could only scroll up and down if the scroll toggle was on for mouse keys. This CL adds the ability to scroll left and right when the scroll toggle is on. Bug: 341799888 Test: atest FrameworksServicesTests:MouseKeysInterceptorTest Flag: EXEMPT bugfix Change-Id: I75025a39bc9a67015664475e0f2abaeb9e026d8f --- .../server/accessibility/MouseKeysInterceptor.java | 64 +++++++++++++++------- .../accessibility/MouseKeysInterceptorTest.kt | 21 +++++++ 2 files changed, 65 insertions(+), 20 deletions(-) (limited to 'services/accessibility') diff --git a/services/accessibility/java/com/android/server/accessibility/MouseKeysInterceptor.java b/services/accessibility/java/com/android/server/accessibility/MouseKeysInterceptor.java index 4b97745b3b25..1df5d1a1d54f 100644 --- a/services/accessibility/java/com/android/server/accessibility/MouseKeysInterceptor.java +++ b/services/accessibility/java/com/android/server/accessibility/MouseKeysInterceptor.java @@ -138,8 +138,8 @@ public class MouseKeysInterceptor extends BaseEventStreamTransformation DIAGONAL_UP_LEFT_MOVE(KeyEvent.KEYCODE_7), UP_MOVE_OR_SCROLL(KeyEvent.KEYCODE_8), DIAGONAL_UP_RIGHT_MOVE(KeyEvent.KEYCODE_9), - LEFT_MOVE(KeyEvent.KEYCODE_U), - RIGHT_MOVE(KeyEvent.KEYCODE_O), + LEFT_MOVE_OR_SCROLL(KeyEvent.KEYCODE_U), + RIGHT_MOVE_OR_SCROLL(KeyEvent.KEYCODE_O), DIAGONAL_DOWN_LEFT_MOVE(KeyEvent.KEYCODE_J), DOWN_MOVE_OR_SCROLL(KeyEvent.KEYCODE_K), DIAGONAL_DOWN_RIGHT_MOVE(KeyEvent.KEYCODE_L), @@ -267,6 +267,16 @@ public class MouseKeysInterceptor extends BaseEventStreamTransformation ); } + @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) + private void sendVirtualMouseScrollEvent(float x, float y) { + waitForVirtualMouseCreation(); + mVirtualMouse.sendScrollEvent(new VirtualMouseScrollEvent.Builder() + .setXAxisMovement(x) + .setYAxisMovement(y) + .build() + ); + } + /** * Performs a mouse scroll action based on the provided key code. * The scroll action will only be performed if the scroll toggle is on. @@ -284,19 +294,31 @@ public class MouseKeysInterceptor extends BaseEventStreamTransformation private void performMouseScrollAction(int keyCode) { MouseKeyEvent mouseKeyEvent = MouseKeyEvent.from( keyCode, mActiveInputDeviceId, mDeviceKeyCodeMap); - float y = switch (mouseKeyEvent) { - case UP_MOVE_OR_SCROLL -> MOUSE_SCROLL_STEP; - case DOWN_MOVE_OR_SCROLL -> -MOUSE_SCROLL_STEP; - default -> 0.0f; - }; - waitForVirtualMouseCreation(); - mVirtualMouse.sendScrollEvent(new VirtualMouseScrollEvent.Builder() - .setYAxisMovement(y) - .build() - ); + float x = 0f; + float y = 0f; + + switch (mouseKeyEvent) { + case UP_MOVE_OR_SCROLL -> { + y = MOUSE_SCROLL_STEP; + } + case DOWN_MOVE_OR_SCROLL -> { + y = -MOUSE_SCROLL_STEP; + } + case LEFT_MOVE_OR_SCROLL -> { + x = MOUSE_SCROLL_STEP; + } + case RIGHT_MOVE_OR_SCROLL -> { + x = -MOUSE_SCROLL_STEP; + } + default -> { + x = 0.0f; + y = 0.0f; + } + } + sendVirtualMouseScrollEvent(x, y); if (DEBUG) { Slog.d(LOG_TAG, "Performed mouse key event: " + mouseKeyEvent.name() - + " for scroll action with axis movement (y=" + y + ")"); + + " for scroll action with axis movement (x=" + x + ", y=" + y + ")"); } } @@ -344,8 +366,8 @@ public class MouseKeysInterceptor extends BaseEventStreamTransformation * The method calculates the relative movement of the mouse pointer * and sends the corresponding event to the virtual mouse. * - * The UP and DOWN pointer actions will only take place for their respective keys - * if the scroll toggle is off. + * The UP, DOWN, LEFT, RIGHT pointer actions will only take place for their + * respective keys if the scroll toggle is off. * * @param keyCode The key code representing the direction or button press. * Supported keys are: @@ -353,8 +375,8 @@ public class MouseKeysInterceptor extends BaseEventStreamTransformation *

  • {@link MouseKeysInterceptor.MouseKeyEvent#DIAGONAL_DOWN_LEFT_MOVE} *
  • {@link MouseKeysInterceptor.MouseKeyEvent#DOWN_MOVE_OR_SCROLL} *
  • {@link MouseKeysInterceptor.MouseKeyEvent#DIAGONAL_DOWN_RIGHT_MOVE} - *
  • {@link MouseKeysInterceptor.MouseKeyEvent#LEFT_MOVE} - *
  • {@link MouseKeysInterceptor.MouseKeyEvent#RIGHT_MOVE} + *
  • {@link MouseKeysInterceptor.MouseKeyEvent#LEFT_MOVE_OR_SCROLL} + *
  • {@link MouseKeysInterceptor.MouseKeyEvent#RIGHT_MOVE_OR_SCROLL} *
  • {@link MouseKeysInterceptor.MouseKeyEvent#DIAGONAL_UP_LEFT_MOVE} *
  • {@link MouseKeysInterceptor.MouseKeyEvent#UP_MOVE_OR_SCROLL} *
  • {@link MouseKeysInterceptor.MouseKeyEvent#DIAGONAL_UP_RIGHT_MOVE} @@ -381,10 +403,10 @@ public class MouseKeysInterceptor extends BaseEventStreamTransformation x = MOUSE_POINTER_MOVEMENT_STEP / sqrt(2); y = MOUSE_POINTER_MOVEMENT_STEP / sqrt(2); } - case LEFT_MOVE -> { + case LEFT_MOVE_OR_SCROLL -> { x = -MOUSE_POINTER_MOVEMENT_STEP; } - case RIGHT_MOVE -> { + case RIGHT_MOVE_OR_SCROLL -> { x = MOUSE_POINTER_MOVEMENT_STEP; } case DIAGONAL_UP_LEFT_MOVE -> { @@ -424,7 +446,9 @@ public class MouseKeysInterceptor extends BaseEventStreamTransformation private boolean isMouseScrollKey(int keyCode, InputDevice inputDevice) { return keyCode == MouseKeyEvent.UP_MOVE_OR_SCROLL.getKeyCode(inputDevice) - || keyCode == MouseKeyEvent.DOWN_MOVE_OR_SCROLL.getKeyCode(inputDevice); + || keyCode == MouseKeyEvent.DOWN_MOVE_OR_SCROLL.getKeyCode(inputDevice) + || keyCode == MouseKeyEvent.LEFT_MOVE_OR_SCROLL.getKeyCode(inputDevice) + || keyCode == MouseKeyEvent.RIGHT_MOVE_OR_SCROLL.getKeyCode(inputDevice); } /** diff --git a/services/tests/servicestests/src/com/android/server/accessibility/MouseKeysInterceptorTest.kt b/services/tests/servicestests/src/com/android/server/accessibility/MouseKeysInterceptorTest.kt index c76392b30276..5134737ae660 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/MouseKeysInterceptorTest.kt +++ b/services/tests/servicestests/src/com/android/server/accessibility/MouseKeysInterceptorTest.kt @@ -235,6 +235,27 @@ class MouseKeysInterceptorTest { arrayOf(MouseKeysInterceptor.MOUSE_SCROLL_STEP)) } + @Test + fun whenScrollToggleOn_ScrollRightKeyIsPressed_scrollEventIsSent() { + // There should be some delay between the downTime of the key event and calling onKeyEvent + val downTime = clock.now() - KEYBOARD_POST_EVENT_DELAY_MILLIS + val keyCodeScrollToggle = MouseKeysInterceptor.MouseKeyEvent.SCROLL_TOGGLE.keyCodeValue + val keyCodeScroll = MouseKeysInterceptor.MouseKeyEvent.RIGHT_MOVE_OR_SCROLL.keyCodeValue + + val scrollToggleDownEvent = KeyEvent(downTime, downTime, KeyEvent.ACTION_DOWN, + keyCodeScrollToggle, 0, 0, DEVICE_ID, 0) + val scrollDownEvent = KeyEvent(downTime, downTime, KeyEvent.ACTION_DOWN, + keyCodeScroll, 0, 0, DEVICE_ID, 0) + + mouseKeysInterceptor.onKeyEvent(scrollToggleDownEvent, 0) + mouseKeysInterceptor.onKeyEvent(scrollDownEvent, 0) + testLooper.dispatchAll() + + // Verify the sendScrollEvent method is called once and capture the arguments + verifyScrollEvents(arrayOf(-MouseKeysInterceptor.MOUSE_SCROLL_STEP), + arrayOf(0f)) + } + @Test fun whenScrollToggleOff_DirectionalUpKeyIsPressed_RelativeEventIsSent() { // There should be some delay between the downTime of the key event and calling onKeyEvent -- cgit v1.2.3-59-g8ed1b From 5a2c7673eb6a40eebc6122e33f6c22ea82c19e2b Mon Sep 17 00:00:00 2001 From: Hiroki Sato Date: Tue, 1 Oct 2024 16:07:40 +0900 Subject: Enlarge pointer icon for magnification When full screen magnification is enabled, pointer icons should be also enlarged by the zoom factor. This change adds an API in InputManagerInternal for accessibility to update the scale factor when full screen magnification is enabled. Currently there's only one caller, but it will be called from more places in future changes. Bug: 355734856 Test: Enable flag, and changing scale with slider or gesture will change pointer icons. Test: PointerIconCacheTest Test: atest com.android.server.accessibility.magnification Flag: com.android.server.accessibility.magnification_enlarge_pointer Change-Id: I985183b12d6a1c4a6aa4c53e4f462778aeb2e9ae --- .../FullScreenMagnificationController.java | 29 +++++ .../android/server/input/InputManagerInternal.java | 8 ++ .../android/server/input/InputManagerService.java | 9 ++ .../com/android/server/input/PointerIconCache.java | 37 +++++- .../FullScreenMagnificationControllerTest.java | 21 ++++ .../FullScreenMagnificationGestureHandlerTest.java | 3 + .../magnification/MagnificationControllerTest.java | 4 + tests/Input/Android.bp | 1 + .../android/server/input/PointerIconCacheTest.kt | 135 +++++++++++++++++++++ 9 files changed, 245 insertions(+), 2 deletions(-) create mode 100644 tests/Input/src/com/android/server/input/PointerIconCacheTest.kt (limited to 'services/accessibility') diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java index a77ba624a68f..12c64c52b4a0 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java @@ -64,6 +64,7 @@ import com.android.server.LocalServices; import com.android.server.accessibility.AccessibilityManagerService; import com.android.server.accessibility.AccessibilityTraceManager; import com.android.server.accessibility.Flags; +import com.android.server.input.InputManagerInternal; import com.android.server.wm.WindowManagerInternal; import java.util.ArrayList; @@ -955,6 +956,7 @@ public class FullScreenMagnificationController implements context, traceManager, LocalServices.getService(WindowManagerInternal.class), + LocalServices.getService(InputManagerInternal.class), new Handler(context.getMainLooper()), context.getResources().getInteger(R.integer.config_longAnimTime)), lock, @@ -1640,6 +1642,8 @@ public class FullScreenMagnificationController implements */ public void persistScale(int displayId) { final float scale = getScale(displayId); + notifyScaleForInput(displayId, scale); + if (scale < MagnificationConstants.PERSISTED_SCALE_MIN_VALUE) { return; } @@ -1690,6 +1694,20 @@ public class FullScreenMagnificationController implements } } + /** + * Notifies input manager that magnification scale changed non-transiently + * so that pointer cursor is scaled as well. + * + * @param displayId The logical display id. + * @param scale The new scale factor. + */ + public void notifyScaleForInput(int displayId, float scale) { + if (Flags.magnificationEnlargePointer()) { + mControllerCtx.getInputManager() + .setAccessibilityPointerIconScaleFactor(displayId, scale); + } + } + /** * Resets all displays' magnification if last magnifying service is disabled. * @@ -2166,6 +2184,7 @@ public class FullScreenMagnificationController implements private final Context mContext; private final AccessibilityTraceManager mTrace; private final WindowManagerInternal mWindowManager; + private final InputManagerInternal mInputManager; private final Handler mHandler; private final Long mAnimationDuration; @@ -2175,11 +2194,13 @@ public class FullScreenMagnificationController implements public ControllerContext(@NonNull Context context, @NonNull AccessibilityTraceManager traceManager, @NonNull WindowManagerInternal windowManager, + @NonNull InputManagerInternal inputManager, @NonNull Handler handler, long animationDuration) { mContext = context; mTrace = traceManager; mWindowManager = windowManager; + mInputManager = inputManager; mHandler = handler; mAnimationDuration = animationDuration; } @@ -2208,6 +2229,14 @@ public class FullScreenMagnificationController implements return mWindowManager; } + /** + * @return InputManagerInternal + */ + @NonNull + public InputManagerInternal getInputManager() { + return mInputManager; + } + /** * @return Handler for main looper */ diff --git a/services/core/java/com/android/server/input/InputManagerInternal.java b/services/core/java/com/android/server/input/InputManagerInternal.java index 99f7f12567b4..c888eef7f5df 100644 --- a/services/core/java/com/android/server/input/InputManagerInternal.java +++ b/services/core/java/com/android/server/input/InputManagerInternal.java @@ -262,4 +262,12 @@ public abstract class InputManagerInternal { */ public abstract void handleKeyGestureInKeyGestureController(int deviceId, int[] keycodes, int modifierState, @KeyGestureEvent.KeyGestureType int event); + + /** + * Sets the magnification scale factor for pointer icons. + * + * @param displayId the ID of the display where the new scale factor is applied. + * @param scaleFactor the new scale factor to be applied for pointer icons. + */ + public abstract void setAccessibilityPointerIconScaleFactor(int displayId, float scaleFactor); } diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java index 8acf583e0765..98e5319cde30 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -3506,6 +3506,11 @@ public class InputManagerService extends IInputManager.Stub int modifierState, @KeyGestureEvent.KeyGestureType int gestureType) { mKeyGestureController.handleKeyGesture(deviceId, keycodes, modifierState, gestureType); } + + @Override + public void setAccessibilityPointerIconScaleFactor(int displayId, float scaleFactor) { + InputManagerService.this.setAccessibilityPointerIconScaleFactor(displayId, scaleFactor); + } } @Override @@ -3688,6 +3693,10 @@ public class InputManagerService extends IInputManager.Stub mPointerIconCache.setPointerScale(scale); } + void setAccessibilityPointerIconScaleFactor(int displayId, float scaleFactor) { + mPointerIconCache.setAccessibilityScaleFactor(displayId, scaleFactor); + } + interface KeyboardBacklightControllerInterface { default void incrementKeyboardBacklight(int deviceId) {} default void decrementKeyboardBacklight(int deviceId) {} diff --git a/services/core/java/com/android/server/input/PointerIconCache.java b/services/core/java/com/android/server/input/PointerIconCache.java index 297cd68d5d3d..e16031cb664a 100644 --- a/services/core/java/com/android/server/input/PointerIconCache.java +++ b/services/core/java/com/android/server/input/PointerIconCache.java @@ -27,6 +27,7 @@ import android.hardware.display.DisplayManager; import android.os.Handler; import android.util.Slog; import android.util.SparseArray; +import android.util.SparseDoubleArray; import android.util.SparseIntArray; import android.view.ContextThemeWrapper; import android.view.Display; @@ -34,6 +35,7 @@ import android.view.DisplayInfo; import android.view.PointerIcon; import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; import com.android.server.UiThread; import java.util.Objects; @@ -51,7 +53,7 @@ final class PointerIconCache { private final NativeInputManagerService mNative; // We use the UI thread for loading pointer icons. - private final Handler mUiThreadHandler = UiThread.getHandler(); + private final Handler mUiThreadHandler; @GuardedBy("mLoadedPointerIconsByDisplayAndType") private final SparseArray> mLoadedPointerIconsByDisplayAndType = @@ -70,6 +72,9 @@ final class PointerIconCache { POINTER_ICON_VECTOR_STYLE_STROKE_WHITE; @GuardedBy("mLoadedPointerIconsByDisplayAndType") private float mPointerIconScale = DEFAULT_POINTER_SCALE; + // Note that android doesn't have SparseFloatArray, so this falls back to use double instead. + @GuardedBy("mLoadedPointerIconsByDisplayAndType") + private final SparseDoubleArray mAccessibilityScaleFactorPerDisplay = new SparseDoubleArray(); private final DisplayManager.DisplayListener mDisplayListener = new DisplayManager.DisplayListener() { @@ -86,6 +91,7 @@ final class PointerIconCache { mLoadedPointerIconsByDisplayAndType.remove(displayId); mDisplayContexts.remove(displayId); mDisplayDensities.delete(displayId); + mAccessibilityScaleFactorPerDisplay.delete(displayId); } } @@ -96,8 +102,15 @@ final class PointerIconCache { }; /* package */ PointerIconCache(Context context, NativeInputManagerService nativeService) { + this(context, nativeService, UiThread.getHandler()); + } + + @VisibleForTesting + /* package */ PointerIconCache(Context context, NativeInputManagerService nativeService, + Handler handler) { mContext = context; mNative = nativeService; + mUiThreadHandler = handler; } public void systemRunning() { @@ -134,6 +147,11 @@ final class PointerIconCache { mUiThreadHandler.post(() -> handleSetPointerScale(scale)); } + /** Set the scale for accessibility (magnification) for vector pointer icons. */ + public void setAccessibilityScaleFactor(int displayId, float scaleFactor) { + mUiThreadHandler.post(() -> handleAccessibilityScaleFactor(displayId, scaleFactor)); + } + /** * Get a loaded system pointer icon. This will fetch the icon from the cache, or load it if * it isn't already cached. @@ -155,8 +173,10 @@ final class PointerIconCache { /* force= */ true); theme.applyStyle(PointerIcon.vectorStrokeStyleToResource(mPointerIconStrokeStyle), /* force= */ true); + final float scale = mPointerIconScale + * (float) mAccessibilityScaleFactorPerDisplay.get(displayId, 1f); icon = PointerIcon.getLoadedSystemIcon(new ContextThemeWrapper(context, theme), - type, mUseLargePointerIcons, mPointerIconScale); + type, mUseLargePointerIcons, scale); iconsByType.put(type, icon); } return Objects.requireNonNull(icon); @@ -261,6 +281,19 @@ final class PointerIconCache { mNative.reloadPointerIcons(); } + @android.annotation.UiThread + private void handleAccessibilityScaleFactor(int displayId, float scale) { + synchronized (mLoadedPointerIconsByDisplayAndType) { + if (mAccessibilityScaleFactorPerDisplay.get(displayId, 1f) == scale) { + return; + } + mAccessibilityScaleFactorPerDisplay.put(displayId, scale); + // Clear cached icons on the display. + mLoadedPointerIconsByDisplayAndType.remove(displayId); + } + mNative.reloadPointerIcons(); + } + // Updates the cached display density for the given displayId, and returns true if // the cached density changed. @GuardedBy("mLoadedPointerIconsByDisplayAndType") diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java index c4b4afd13a60..76553ba0120f 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java @@ -18,6 +18,7 @@ package com.android.server.accessibility.magnification; import static android.accessibilityservice.MagnificationConfig.MAGNIFICATION_MODE_FULLSCREEN; +import static com.android.server.accessibility.Flags.FLAG_MAGNIFICATION_ENLARGE_POINTER; import static com.android.server.accessibility.magnification.FullScreenMagnificationController.MagnificationInfoChangedCallback; import static com.android.server.accessibility.magnification.MockMagnificationConnection.TEST_DISPLAY; import static com.android.window.flags.Flags.FLAG_ALWAYS_DRAW_MAGNIFICATION_FULLSCREEN_BORDER; @@ -76,6 +77,7 @@ import com.android.server.LocalServices; import com.android.server.accessibility.AccessibilityTraceManager; import com.android.server.accessibility.Flags; import com.android.server.accessibility.test.MessageCapturingHandler; +import com.android.server.input.InputManagerInternal; import com.android.server.wm.WindowManagerInternal; import com.android.server.wm.WindowManagerInternal.MagnificationCallbacks; @@ -126,6 +128,7 @@ public class FullScreenMagnificationControllerTest { final Resources mMockResources = mock(Resources.class); final AccessibilityTraceManager mMockTraceManager = mock(AccessibilityTraceManager.class); final WindowManagerInternal mMockWindowManager = mock(WindowManagerInternal.class); + final InputManagerInternal mMockInputManager = mock(InputManagerInternal.class); private final MagnificationAnimationCallback mAnimationCallback = mock( MagnificationAnimationCallback.class); private final MagnificationInfoChangedCallback mRequestObserver = mock( @@ -163,6 +166,7 @@ public class FullScreenMagnificationControllerTest { when(mMockControllerCtx.getContext()).thenReturn(mMockContext); when(mMockControllerCtx.getTraceManager()).thenReturn(mMockTraceManager); when(mMockControllerCtx.getWindowManager()).thenReturn(mMockWindowManager); + when(mMockControllerCtx.getInputManager()).thenReturn(mMockInputManager); when(mMockControllerCtx.getHandler()).thenReturn(mMessageCapturingHandler); when(mMockControllerCtx.getAnimationDuration()).thenReturn(1000L); mResolver = new MockContentResolver(); @@ -1478,6 +1482,23 @@ public class FullScreenMagnificationControllerTest { 4.0f); } + @Test + @RequiresFlagsEnabled(FLAG_MAGNIFICATION_ENLARGE_POINTER) + public void persistScale_setValue_notifyInput() { + register(TEST_DISPLAY); + + PointF pivotPoint = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER; + mFullScreenMagnificationController.setScale(TEST_DISPLAY, 4.0f, pivotPoint.x, pivotPoint.y, + true, SERVICE_ID_1); + mFullScreenMagnificationController.persistScale(TEST_DISPLAY); + + // persistScale may post a task to a background thread. Let's wait for it completes. + waitForBackgroundThread(); + Assert.assertEquals(mFullScreenMagnificationController.getPersistedScale(TEST_DISPLAY), + 4.0f); + verify(mMockInputManager).setAccessibilityPointerIconScaleFactor(TEST_DISPLAY, 4.0f); + } + @Test public void testOnContextChanged_alwaysOnFeatureDisabled_resetMagnification() { setScaleToMagnifying(); diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java index b745e6a7d4a5..00b7de818c12 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java @@ -90,6 +90,7 @@ import com.android.server.accessibility.AccessibilityTraceManager; import com.android.server.accessibility.EventStreamTransformation; import com.android.server.accessibility.Flags; import com.android.server.accessibility.magnification.FullScreenMagnificationController.MagnificationInfoChangedCallback; +import com.android.server.input.InputManagerInternal; import com.android.server.testutils.OffsettableClock; import com.android.server.testutils.TestHandler; import com.android.server.wm.WindowManagerInternal; @@ -227,9 +228,11 @@ public class FullScreenMagnificationGestureHandlerTest { final FullScreenMagnificationController.ControllerContext mockController = mock(FullScreenMagnificationController.ControllerContext.class); final WindowManagerInternal mockWindowManager = mock(WindowManagerInternal.class); + final InputManagerInternal mockInputManager = mock(InputManagerInternal.class); when(mockController.getContext()).thenReturn(mContext); when(mockController.getTraceManager()).thenReturn(mMockTraceManager); when(mockController.getWindowManager()).thenReturn(mockWindowManager); + when(mockController.getInputManager()).thenReturn(mockInputManager); when(mockController.getHandler()).thenReturn(new Handler(mContext.getMainLooper())); when(mockController.newValueAnimator()).thenReturn(new ValueAnimator()); when(mockController.getAnimationDuration()).thenReturn(1000L); diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java index 25281774cd95..d70e1fe1d9d1 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java @@ -76,6 +76,7 @@ import com.android.server.LocalServices; import com.android.server.accessibility.AccessibilityManagerService; import com.android.server.accessibility.AccessibilityTraceManager; import com.android.server.accessibility.test.MessageCapturingHandler; +import com.android.server.input.InputManagerInternal; import com.android.server.wm.WindowManagerInternal; import com.android.window.flags.Flags; @@ -154,6 +155,8 @@ public class MagnificationControllerTest { private WindowManagerInternal mWindowManagerInternal; @Mock private WindowManagerInternal.AccessibilityControllerInternal mA11yController; + @Mock + private InputManagerInternal mInputManagerInternal; @Mock private DisplayManagerInternal mDisplayManagerInternal; @@ -200,6 +203,7 @@ public class MagnificationControllerTest { when(mControllerCtx.getContext()).thenReturn(mContext); when(mControllerCtx.getTraceManager()).thenReturn(mTraceManager); when(mControllerCtx.getWindowManager()).thenReturn(mWindowManagerInternal); + when(mControllerCtx.getInputManager()).thenReturn(mInputManagerInternal); when(mControllerCtx.getHandler()).thenReturn(mMessageCapturingHandler); when(mControllerCtx.getAnimationDuration()).thenReturn(1000L); when(mControllerCtx.newValueAnimator()).thenReturn(mValueAnimator); diff --git a/tests/Input/Android.bp b/tests/Input/Android.bp index 6742cbe1f19a..168141bf6e7d 100644 --- a/tests/Input/Android.bp +++ b/tests/Input/Android.bp @@ -41,6 +41,7 @@ android_test { "hamcrest-library", "junit-params", "kotlin-test", + "mockito-kotlin-nodeps", "mockito-target-extended-minus-junit4", "platform-test-annotations", "platform-screenshot-diff-core", diff --git a/tests/Input/src/com/android/server/input/PointerIconCacheTest.kt b/tests/Input/src/com/android/server/input/PointerIconCacheTest.kt new file mode 100644 index 000000000000..47e7ac720a08 --- /dev/null +++ b/tests/Input/src/com/android/server/input/PointerIconCacheTest.kt @@ -0,0 +1,135 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.input + +import android.content.Context +import android.content.ContextWrapper +import android.os.Handler +import android.os.test.TestLooper +import android.platform.test.annotations.Presubmit +import android.view.Display +import android.view.PointerIcon +import androidx.test.platform.app.InstrumentationRegistry +import junit.framework.Assert.assertEquals +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.mockito.Mock +import org.mockito.junit.MockitoJUnit +import org.mockito.kotlin.times +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever + +/** + * Tests for {@link PointerIconCache}. + */ +@Presubmit +class PointerIconCacheTest { + + @get:Rule + val rule = MockitoJUnit.rule()!! + + @Mock + private lateinit var native: NativeInputManagerService + @Mock + private lateinit var defaultDisplay: Display + + private lateinit var context: Context + private lateinit var testLooper: TestLooper + private lateinit var cache: PointerIconCache + + @Before + fun setup() { + whenever(defaultDisplay.displayId).thenReturn(Display.DEFAULT_DISPLAY) + + context = object : ContextWrapper(InstrumentationRegistry.getInstrumentation().context) { + override fun getDisplay() = defaultDisplay + } + + testLooper = TestLooper() + cache = PointerIconCache(context, native, Handler(testLooper.looper)) + } + + @Test + fun testSetPointerScale() { + val defaultBitmap = getDefaultIcon().bitmap + cache.setPointerScale(2f) + + testLooper.dispatchAll() + verify(native).reloadPointerIcons() + + val bitmap = + cache.getLoadedPointerIcon(Display.DEFAULT_DISPLAY, PointerIcon.TYPE_ARROW).bitmap + + assertEquals(defaultBitmap.height * 2, bitmap.height) + assertEquals(defaultBitmap.width * 2, bitmap.width) + } + + @Test + fun testSetAccessibilityScaleFactor() { + val defaultBitmap = getDefaultIcon().bitmap + cache.setAccessibilityScaleFactor(Display.DEFAULT_DISPLAY, 4f) + + testLooper.dispatchAll() + verify(native).reloadPointerIcons() + + val bitmap = + cache.getLoadedPointerIcon(Display.DEFAULT_DISPLAY, PointerIcon.TYPE_ARROW).bitmap + + assertEquals(defaultBitmap.height * 4, bitmap.height) + assertEquals(defaultBitmap.width * 4, bitmap.width) + } + + @Test + fun testSetAccessibilityScaleFactorOnSecondaryDisplay() { + val defaultBitmap = getDefaultIcon().bitmap + val secondaryDisplayId = Display.DEFAULT_DISPLAY + 1 + cache.setAccessibilityScaleFactor(secondaryDisplayId, 4f) + + testLooper.dispatchAll() + verify(native).reloadPointerIcons() + + val bitmap = + cache.getLoadedPointerIcon(Display.DEFAULT_DISPLAY, PointerIcon.TYPE_ARROW).bitmap + assertEquals(defaultBitmap.height, bitmap.height) + assertEquals(defaultBitmap.width, bitmap.width) + + val bitmapSecondary = + cache.getLoadedPointerIcon(secondaryDisplayId, PointerIcon.TYPE_ARROW).bitmap + assertEquals(defaultBitmap.height * 4, bitmapSecondary.height) + assertEquals(defaultBitmap.width * 4, bitmapSecondary.width) + } + + @Test + fun testSetPointerScaleAndAccessibilityScaleFactor() { + val defaultBitmap = getDefaultIcon().bitmap + cache.setPointerScale(2f) + cache.setAccessibilityScaleFactor(Display.DEFAULT_DISPLAY, 3f) + + testLooper.dispatchAll() + verify(native, times(2)).reloadPointerIcons() + + val bitmap = + cache.getLoadedPointerIcon(Display.DEFAULT_DISPLAY, PointerIcon.TYPE_ARROW).bitmap + + assertEquals(defaultBitmap.height * 6, bitmap.height) + assertEquals(defaultBitmap.width * 6, bitmap.width) + } + + private fun getDefaultIcon() = + PointerIcon.getLoadedSystemIcon(context, PointerIcon.TYPE_ARROW, false, 1f) +} -- cgit v1.2.3-59-g8ed1b From 3ce8ae768d4dc35edbafc111a6b3179753679856 Mon Sep 17 00:00:00 2001 From: Chun-Ku Lin Date: Wed, 16 Oct 2024 01:38:34 +0000 Subject: Clear shortcuts associated to an A11yActivity when the A11yActivity becomes A11yService with the same componentName Bug: 358092445 Test: atest CtsAccessibilityServiceTestCases (with the flag on) Flag: com.android.server.accessibility.clear_shortcuts_when_activity_updates_to_service Change-Id: I562f532cc97f1a7a85b6f13cd3d7ce4685cc1c90 --- services/accessibility/accessibility.aconfig | 10 ++++++++++ .../server/accessibility/AccessibilityManagerService.java | 13 +++++++++++++ 2 files changed, 23 insertions(+) (limited to 'services/accessibility') diff --git a/services/accessibility/accessibility.aconfig b/services/accessibility/accessibility.aconfig index 034127c0420e..3bfab8583ed6 100644 --- a/services/accessibility/accessibility.aconfig +++ b/services/accessibility/accessibility.aconfig @@ -44,6 +44,16 @@ flag { } } +flag { + name: "clear_shortcuts_when_activity_updates_to_service" + namespace: "accessibility" + description: "When an a11y activity is updated to an a11y service, clears the associated shortcuts so that we don't skip the AccessibilityServiceWarning." + bug: "358092445" + metadata { + purpose: PURPOSE_BUGFIX + } +} + flag { name: "compute_window_changes_on_a11y_v2" namespace: "accessibility" diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index 1451dfaa7964..ec8908bc7c91 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -2513,6 +2513,19 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub private boolean readInstalledAccessibilityShortcutLocked(AccessibilityUserState userState, List parsedAccessibilityShortcutInfos) { if (!parsedAccessibilityShortcutInfos.equals(userState.mInstalledShortcuts)) { + if (Flags.clearShortcutsWhenActivityUpdatesToService()) { + List componentNames = userState.mInstalledShortcuts.stream() + .filter(a11yActivity -> + !parsedAccessibilityShortcutInfos.contains(a11yActivity)) + .map(a11yActivity -> a11yActivity.getComponentName().flattenToString()) + .toList(); + if (!componentNames.isEmpty()) { + enableShortcutsForTargets( + /* enable= */ false, UserShortcutType.ALL, + componentNames, userState.mUserId); + } + } + userState.mInstalledShortcuts.clear(); userState.mInstalledShortcuts.addAll(parsedAccessibilityShortcutInfos); userState.updateTileServiceMapForAccessibilityActivityLocked(); -- cgit v1.2.3-59-g8ed1b From 5e996f90e50f115931845ff2e7619bf9b53f334e Mon Sep 17 00:00:00 2001 From: Katie Date: Wed, 16 Oct 2024 18:52:34 +0000 Subject: Rename Gantry flag for fullscreen magnifier + mouse bugfix Fullscreen magnifier moves with mouse is now a bugfix instead of a feature, so we must rename the flag. Bug: 354696546 Test: Existing Flag: com.server.accessibility.enable_magnification_follows_mouse Change-Id: I3d8289396fa4e951238cf64d503ce37e5a0f9e1f --- services/accessibility/accessibility.aconfig | 5 ++++- .../FullScreenMagnificationGestureHandler.java | 4 ++-- .../magnification/MagnificationGestureHandler.java | 3 ++- .../FullScreenMagnificationGestureHandlerTest.java | 20 ++++++++++---------- .../MagnificationGestureHandlerTest.java | 8 ++++---- 5 files changed, 22 insertions(+), 18 deletions(-) (limited to 'services/accessibility') diff --git a/services/accessibility/accessibility.aconfig b/services/accessibility/accessibility.aconfig index 034127c0420e..95c07533d37f 100644 --- a/services/accessibility/accessibility.aconfig +++ b/services/accessibility/accessibility.aconfig @@ -114,10 +114,13 @@ flag { } flag { - name: "enable_magnification_follows_mouse" + name: "enable_magnification_follows_mouse_bugfix" namespace: "accessibility" description: "Whether to enable mouse following for fullscreen magnification" bug: "354696546" + metadata { + purpose: PURPOSE_BUGFIX + } } flag { diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java index a19fdddea49c..963334b07ea6 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java @@ -345,7 +345,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH @Override void handleMouseOrStylusEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) { - if (Flags.enableMagnificationFollowsMouse()) { + if (Flags.enableMagnificationFollowsMouseBugfix()) { if (mFullScreenMagnificationController.isActivated(mDisplayId)) { // TODO(b/354696546): Allow mouse/stylus to activate whichever display they are // over, rather than only interacting with the current display. @@ -1206,7 +1206,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH protected void cacheDelayedMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) { - if (Flags.enableMagnificationFollowsMouse() + if (Flags.enableMagnificationFollowsMouseBugfix() && !event.isFromSource(SOURCE_TOUCHSCREEN)) { // Only touch events need to be cached and sent later. return; diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureHandler.java index 446123f07f64..fa86ba39bb1a 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureHandler.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureHandler.java @@ -146,7 +146,8 @@ public abstract class MagnificationGestureHandler extends BaseEventStreamTransfo } break; case SOURCE_MOUSE: case SOURCE_STYLUS: { - if (magnificationShortcutExists() && Flags.enableMagnificationFollowsMouse()) { + if (magnificationShortcutExists() + && Flags.enableMagnificationFollowsMouseBugfix()) { handleMouseOrStylusEvent(event, rawEvent, policyFlags); } } diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java index b745e6a7d4a5..e5831b326de5 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java @@ -1417,7 +1417,7 @@ public class FullScreenMagnificationGestureHandlerTest { } @Test - @RequiresFlagsDisabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE) + @RequiresFlagsDisabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE_BUGFIX) public void testMouseMoveEventsDoNotMoveMagnifierViewport() { runMoveEventsDoNotMoveMagnifierViewport(InputDevice.SOURCE_MOUSE); } @@ -1471,55 +1471,55 @@ public class FullScreenMagnificationGestureHandlerTest { } @Test - @RequiresFlagsDisabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE) + @RequiresFlagsDisabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE_BUGFIX) public void testMouseHoverMoveEventsDoNotMoveMagnifierViewport() { runHoverMoveEventsDoNotMoveMagnifierViewport(InputDevice.SOURCE_MOUSE); } @Test - @RequiresFlagsDisabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE) + @RequiresFlagsDisabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE_BUGFIX) public void testStylusHoverMoveEventsDoNotMoveMagnifierViewport() { runHoverMoveEventsDoNotMoveMagnifierViewport(InputDevice.SOURCE_STYLUS); } @Test - @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE) + @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE_BUGFIX) public void testMouseHoverMoveEventsMoveMagnifierViewport() { runHoverMovesViewportTest(InputDevice.SOURCE_MOUSE); } @Test - @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE) + @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE_BUGFIX) public void testStylusHoverMoveEventsMoveMagnifierViewport() { runHoverMovesViewportTest(InputDevice.SOURCE_STYLUS); } @Test - @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE) + @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE_BUGFIX) public void testMouseDownEventsDoNotMoveMagnifierViewport() { runDownDoesNotMoveViewportTest(InputDevice.SOURCE_MOUSE); } @Test - @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE) + @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE_BUGFIX) public void testStylusDownEventsDoNotMoveMagnifierViewport() { runDownDoesNotMoveViewportTest(InputDevice.SOURCE_STYLUS); } @Test - @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE) + @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE_BUGFIX) public void testMouseUpEventsDoNotMoveMagnifierViewport() { runUpDoesNotMoveViewportTest(InputDevice.SOURCE_MOUSE); } @Test - @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE) + @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE_BUGFIX) public void testStylusUpEventsDoNotMoveMagnifierViewport() { runUpDoesNotMoveViewportTest(InputDevice.SOURCE_STYLUS); } @Test - @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE) + @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE_BUGFIX) public void testMouseMoveEventsMoveMagnifierViewport() { final EventCaptor eventCaptor = new EventCaptor(); mMgh.setNext(eventCaptor); diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationGestureHandlerTest.java index d80a1f056e94..45c157d1c1a0 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationGestureHandlerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationGestureHandlerTest.java @@ -93,7 +93,7 @@ public class MagnificationGestureHandlerTest { } @Test - @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE) + @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE_BUGFIX) public void onMotionEvent_isFromMouse_handleMouseOrStylusEvent() { final MotionEvent mouseEvent = MotionEvent.obtain(0, 0, ACTION_HOVER_MOVE, 0, 0, 0); mouseEvent.setSource(InputDevice.SOURCE_MOUSE); @@ -108,7 +108,7 @@ public class MagnificationGestureHandlerTest { } @Test - @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE) + @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE_BUGFIX) public void onMotionEvent_isFromStylus_handleMouseOrStylusEvent() { final MotionEvent stylusEvent = MotionEvent.obtain(0, 0, ACTION_HOVER_MOVE, 0, 0, 0); stylusEvent.setSource(InputDevice.SOURCE_STYLUS); @@ -123,7 +123,7 @@ public class MagnificationGestureHandlerTest { } @Test - @RequiresFlagsDisabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE) + @RequiresFlagsDisabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE_BUGFIX) public void onMotionEvent_isFromMouse_handleMouseOrStylusEventNotCalled() { final MotionEvent mouseEvent = MotionEvent.obtain(0, 0, ACTION_HOVER_MOVE, 0, 0, 0); mouseEvent.setSource(InputDevice.SOURCE_MOUSE); @@ -138,7 +138,7 @@ public class MagnificationGestureHandlerTest { } @Test - @RequiresFlagsDisabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE) + @RequiresFlagsDisabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE_BUGFIX) public void onMotionEvent_isFromStylus_handleMouseOrStylusEventNotCalled() { final MotionEvent stylusEvent = MotionEvent.obtain(0, 0, ACTION_HOVER_MOVE, 0, 0, 0); stylusEvent.setSource(InputDevice.SOURCE_STYLUS); -- cgit v1.2.3-59-g8ed1b From 43722df2e05b0694e16291d6b3d0e59010edbcd1 Mon Sep 17 00:00:00 2001 From: Melody Hsu Date: Thu, 17 Oct 2024 18:04:35 +0000 Subject: Remove deleteCaptureDisplay flags Flag has been out for several releases without known issues and can be removed now. Bug: 351894825 Test: presubmit Flag: EXEMPT flag removal Change-Id: I93cb173f4d25f88dca23a65b9a78f7e6aeafa6d2 --- .../AbstractAccessibilityServiceConnection.java | 78 ++++++---------------- .../android/server/wm/ScreenRotationAnimation.java | 32 +-------- 2 files changed, 20 insertions(+), 90 deletions(-) (limited to 'services/accessibility') diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java index 73b7b35ba9a7..3441d94facda 100644 --- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java +++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java @@ -35,7 +35,6 @@ import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK; import static android.view.accessibility.AccessibilityNodeInfo.ACTION_LONG_CLICK; import static com.android.server.pm.UserManagerService.enforceCurrentUserIfVisibleBackgroundEnabled; -import static com.android.window.flags.Flags.deleteCaptureDisplay; import android.accessibilityservice.AccessibilityGestureEvent; import android.accessibilityservice.AccessibilityService; @@ -62,7 +61,6 @@ import android.graphics.ParcelableColorSpace; import android.graphics.Region; import android.hardware.HardwareBuffer; import android.hardware.display.DisplayManager; -import android.hardware.display.DisplayManagerInternal; import android.hardware.usb.UsbDevice; import android.os.Binder; import android.os.Build; @@ -104,7 +102,6 @@ import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection; import com.android.internal.os.SomeArgs; import com.android.internal.util.DumpUtils; import com.android.internal.util.function.pooled.PooledLambda; -import com.android.server.LocalServices; import com.android.server.accessibility.AccessibilityWindowManager.RemoteAccessibilityConnection; import com.android.server.accessibility.magnification.MagnificationProcessor; import com.android.server.wm.WindowManagerInternal; @@ -1513,68 +1510,31 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ return; } final long identity = Binder.clearCallingIdentity(); - if (deleteCaptureDisplay()) { - try { - ScreenCapture.ScreenCaptureListener screenCaptureListener = new - ScreenCapture.ScreenCaptureListener( - (screenshotBuffer, result) -> { - if (screenshotBuffer != null && result == 0) { - sendScreenshotSuccess(screenshotBuffer, callback); - } else { - sendScreenshotFailure( - AccessibilityService.ERROR_TAKE_SCREENSHOT_INVALID_DISPLAY, - callback); - } + try { + ScreenCapture.ScreenCaptureListener screenCaptureListener = new + ScreenCapture.ScreenCaptureListener( + (screenshotBuffer, result) -> { + if (screenshotBuffer != null && result == 0) { + sendScreenshotSuccess(screenshotBuffer, callback); + } else { + sendScreenshotFailure( + AccessibilityService.ERROR_TAKE_SCREENSHOT_INVALID_DISPLAY, + callback); } - ); - mWindowManagerService.captureDisplay(displayId, null, screenCaptureListener); - } catch (Exception e) { - sendScreenshotFailure(AccessibilityService.ERROR_TAKE_SCREENSHOT_INVALID_DISPLAY, - callback); - } finally { - Binder.restoreCallingIdentity(identity); - } - } else { - try { - mMainHandler.post(PooledLambda.obtainRunnable((nonArg) -> { - final ScreenshotHardwareBuffer screenshotBuffer = LocalServices - .getService(DisplayManagerInternal.class).userScreenshot(displayId); - if (screenshotBuffer != null) { - sendScreenshotSuccess(screenshotBuffer, callback); - } else { - sendScreenshotFailure( - AccessibilityService.ERROR_TAKE_SCREENSHOT_INVALID_DISPLAY, - callback); } - }, null).recycleOnUse()); - } finally { - Binder.restoreCallingIdentity(identity); - } + ); + mWindowManagerService.captureDisplay(displayId, null, screenCaptureListener); + } catch (Exception e) { + sendScreenshotFailure(AccessibilityService.ERROR_TAKE_SCREENSHOT_INVALID_DISPLAY, + callback); + } finally { + Binder.restoreCallingIdentity(identity); } } private void sendScreenshotSuccess(ScreenshotHardwareBuffer screenshotBuffer, RemoteCallback callback) { - if (deleteCaptureDisplay()) { - mMainHandler.post(PooledLambda.obtainRunnable((nonArg) -> { - final HardwareBuffer hardwareBuffer = screenshotBuffer.getHardwareBuffer(); - final ParcelableColorSpace colorSpace = - new ParcelableColorSpace(screenshotBuffer.getColorSpace()); - - final Bundle payload = new Bundle(); - payload.putInt(KEY_ACCESSIBILITY_SCREENSHOT_STATUS, - AccessibilityService.TAKE_SCREENSHOT_SUCCESS); - payload.putParcelable(KEY_ACCESSIBILITY_SCREENSHOT_HARDWAREBUFFER, - hardwareBuffer); - payload.putParcelable(KEY_ACCESSIBILITY_SCREENSHOT_COLORSPACE, colorSpace); - payload.putLong(KEY_ACCESSIBILITY_SCREENSHOT_TIMESTAMP, - SystemClock.uptimeMillis()); - - // Send back the result. - callback.sendResult(payload); - hardwareBuffer.close(); - }, null).recycleOnUse()); - } else { + mMainHandler.post(PooledLambda.obtainRunnable((nonArg) -> { final HardwareBuffer hardwareBuffer = screenshotBuffer.getHardwareBuffer(); final ParcelableColorSpace colorSpace = new ParcelableColorSpace(screenshotBuffer.getColorSpace()); @@ -1591,7 +1551,7 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ // Send back the result. callback.sendResult(payload); hardwareBuffer.close(); - } + }, null).recycleOnUse()); } private void sendScreenshotFailure(@AccessibilityService.ScreenshotErrorCode int errorCode, diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java index 0d3fa1b48b4c..4bd294be22b6 100644 --- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java +++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java @@ -32,7 +32,6 @@ import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_SCREEN_ROTATI import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import static com.android.server.wm.utils.CoordinateTransforms.computeRotationMatrix; -import static com.android.window.flags.Flags.deleteCaptureDisplay; import android.animation.ArgbEvaluator; import android.content.Context; @@ -41,11 +40,9 @@ import android.graphics.Matrix; import android.graphics.Point; import android.graphics.Rect; import android.hardware.HardwareBuffer; -import android.os.IBinder; import android.os.Trace; import android.util.Slog; import android.util.proto.ProtoOutputStream; -import android.view.DisplayAddress; import android.view.DisplayInfo; import android.view.Surface; import android.view.Surface.OutOfResourcesException; @@ -58,7 +55,6 @@ import android.window.ScreenCapture; import com.android.internal.R; import com.android.internal.policy.TransitionAnimation; import com.android.internal.protolog.ProtoLog; -import com.android.server.display.DisplayControl; import com.android.server.wm.SurfaceAnimator.AnimationType; import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback; @@ -171,33 +167,7 @@ class ScreenRotationAnimation { try { final ScreenCapture.ScreenshotHardwareBuffer screenshotBuffer; - if (isSizeChanged && !deleteCaptureDisplay()) { - final DisplayAddress address = displayInfo.address; - if (!(address instanceof DisplayAddress.Physical)) { - Slog.e(TAG, "Display does not have a physical address: " + displayId); - return; - } - final DisplayAddress.Physical physicalAddress = - (DisplayAddress.Physical) address; - final IBinder displayToken = DisplayControl.getPhysicalDisplayToken( - physicalAddress.getPhysicalDisplayId()); - if (displayToken == null) { - Slog.e(TAG, "Display token is null."); - return; - } - // Temporarily not skip screenshot for the rounded corner overlays and screenshot - // the whole display to include the rounded corner overlays. - setSkipScreenshotForRoundedCornerOverlays(false, t); - mRoundedCornerOverlay = displayContent.findRoundedCornerOverlays(); - final ScreenCapture.DisplayCaptureArgs captureArgs = - new ScreenCapture.DisplayCaptureArgs.Builder(displayToken) - .setSourceCrop(new Rect(0, 0, width, height)) - .setAllowProtected(true) - .setCaptureSecureLayers(true) - .setHintForSeamlessTransition(true) - .build(); - screenshotBuffer = ScreenCapture.captureDisplay(captureArgs); - } else if (isSizeChanged) { + if (isSizeChanged) { // Temporarily not skip screenshot for the rounded corner overlays and screenshot // the whole display to include the rounded corner overlays. setSkipScreenshotForRoundedCornerOverlays(false, t); -- cgit v1.2.3-59-g8ed1b From dca77daf2c4466d8d143f6b80c86737a244ecde0 Mon Sep 17 00:00:00 2001 From: Daniel Norman Date: Fri, 18 Oct 2024 18:23:19 +0000 Subject: Runs the A11yManagerService package monitor on a dedicated thread. The default BackgroundThread used by PackageMonitor is shared by other components and can get busy, causing a delay and eventual ANR when responding to broadcasts sent to this PackageMonitor. Fix: 348138695 Test: existing tests in `atest AccessibilityManagerService` Test: install and uninstall packages, observe no change in behavior Flag: com.android.server.accessibility.package_monitor_dedicated_thread Change-Id: Idd112cfeb7e3e55d18d4596c28a90450aa72f682 --- services/accessibility/accessibility.aconfig | 10 ++++++++++ .../server/accessibility/AccessibilityManagerService.java | 15 ++++++++++++++- 2 files changed, 24 insertions(+), 1 deletion(-) (limited to 'services/accessibility') diff --git a/services/accessibility/accessibility.aconfig b/services/accessibility/accessibility.aconfig index ee3bbcaf711d..1280a048ed32 100644 --- a/services/accessibility/accessibility.aconfig +++ b/services/accessibility/accessibility.aconfig @@ -171,6 +171,16 @@ flag { } } +flag { + name: "package_monitor_dedicated_thread" + namespace: "accessibility" + description: "Runs the A11yManagerService PackageMonitor on a dedicated thread" + bug: "348138695" + metadata { + purpose: PURPOSE_BUGFIX + } +} + flag { name: "manager_package_monitor_logic_fix" namespace: "accessibility" diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index d595d02016e0..a23eb6748481 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -116,6 +116,7 @@ import android.os.Binder; import android.os.Build; import android.os.Bundle; import android.os.Handler; +import android.os.HandlerThread; import android.os.IBinder; import android.os.Looper; import android.os.Message; @@ -901,7 +902,19 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub private void registerBroadcastReceivers() { // package changes mPackageMonitor = new ManagerPackageMonitor(this); - mPackageMonitor.register(mContext, null, UserHandle.ALL, true); + final Looper packageMonitorLooper; + if (Flags.packageMonitorDedicatedThread()) { + // Use a dedicated thread because the default BackgroundThread used by PackageMonitor + // is shared by other components and can get busy, causing a delay and eventual ANR when + // responding to broadcasts sent to this PackageMonitor. + HandlerThread packageMonitorThread = new HandlerThread(LOG_TAG + " PackageMonitor", + Process.THREAD_PRIORITY_BACKGROUND); + packageMonitorThread.start(); + packageMonitorLooper = packageMonitorThread.getLooper(); + } else { + packageMonitorLooper = null; + } + mPackageMonitor.register(mContext, packageMonitorLooper, UserHandle.ALL, true); // user change and unlock IntentFilter intentFilter = new IntentFilter(); -- cgit v1.2.3-59-g8ed1b From 2b5c3c6cddf1ba710f5e51ab51ea9166f79327da Mon Sep 17 00:00:00 2001 From: Hiroki Sato Date: Wed, 2 Oct 2024 15:57:14 +0900 Subject: Fullscreen Magnification updates pointer icon on non-transient change Changing pointer icon scale involves resource loading, which is expensive. Thus, we want to change the size of pointer icon only when the change is not transient, i.e., not during animation, user's gesture, moving a slider bar in settings panel, etc. In order to update it, this change adds `isScaleTransient` parameter to all the entrance point of changing fullscreen magnification scale so that when the change is actually applied, we can tell whether we should update a pointer icon size. The change itself is big, but most of them are mechanical change of method signatures and the behavior is controlled behind the aconfig flag. Bug: 355734856 Test: Enable flag, and changing scale with slider or gesture will change pointer icons. Test: atest com.android.server.accessibility.magnification Flag: com.android.server.accessibility.magnification_enlarge_pointer Change-Id: Ibd8c0aa05c047c3977f8d2e7fc5d50f2193705fa --- .../FullScreenMagnificationController.java | 93 ++++++++++--- .../FullScreenMagnificationGestureHandler.java | 4 +- .../magnification/MagnificationController.java | 5 +- .../accessibility/MagnificationProcessorTest.java | 5 +- .../FullScreenMagnificationControllerTest.java | 147 ++++++++++++++++----- .../FullScreenMagnificationGestureHandlerTest.java | 20 +-- .../magnification/MagnificationControllerTest.java | 19 +-- 7 files changed, 218 insertions(+), 75 deletions(-) (limited to 'services/accessibility') diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java index 12c64c52b4a0..ce1a292fb069 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java @@ -397,7 +397,7 @@ public class FullScreenMagnificationController implements mCurrentMagnificationSpec.offsetX, mCurrentMagnificationSpec.offsetY)) { sendSpecToAnimation(mCurrentMagnificationSpec, null); } - onMagnificationChangedLocked(); + onMagnificationChangedLocked(/* isScaleTransient= */ false); } magnified.recycle(); } @@ -475,8 +475,16 @@ public class FullScreenMagnificationController implements return mIdOfLastServiceToMagnify; } + /** + * This is invoked whenever magnification change happens. + * + * @param isScaleTransient represents that if the scale is being changed and the changed + * value may be short lived and be updated again soon. + * Calling the method usually notifies input manager to update the + * cursor scale, but setting this value {@code true} prevents it. + */ @GuardedBy("mLock") - void onMagnificationChangedLocked() { + void onMagnificationChangedLocked(boolean isScaleTransient) { final float scale = getScale(); final float centerX = getCenterX(); final float centerY = getCenterY(); @@ -499,6 +507,10 @@ public class FullScreenMagnificationController implements } else { hideThumbnail(); } + + if (!isScaleTransient) { + notifyScaleForInput(mDisplayId, scale); + } } @GuardedBy("mLock") @@ -612,8 +624,9 @@ public class FullScreenMagnificationController implements * Directly Zooms out the scale to 1f with animating the transition. This method is * triggered only by service automatically, such as when user context changed. */ + @GuardedBy("mLock") void zoomOutFromService() { - setScaleAndCenter(1.0f, Float.NaN, Float.NaN, + setScaleAndCenter(1.0f, Float.NaN, Float.NaN, /* isScaleTransient= */ false, transformToStubCallback(true), AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID); mZoomedOutFromService = true; @@ -641,7 +654,7 @@ public class FullScreenMagnificationController implements setActivated(false); if (changed) { spec.clear(); - onMagnificationChangedLocked(); + onMagnificationChangedLocked(/* isScaleTransient= */ false); } mIdOfLastServiceToMagnify = INVALID_SERVICE_ID; sendSpecToAnimation(spec, animationCallback); @@ -652,7 +665,7 @@ public class FullScreenMagnificationController implements } @GuardedBy("mLock") - boolean setScale(float scale, float pivotX, float pivotY, + boolean setScale(float scale, float pivotX, float pivotY, boolean isScaleTransient, boolean animate, int id) { if (!mRegistered) { return false; @@ -675,12 +688,14 @@ public class FullScreenMagnificationController implements final float centerX = normPivotX + offsetX; final float centerY = normPivotY + offsetY; mIdOfLastServiceToMagnify = id; - return setScaleAndCenter(scale, centerX, centerY, transformToStubCallback(animate), id); + return setScaleAndCenter(scale, centerX, centerY, isScaleTransient, + transformToStubCallback(animate), id); } @GuardedBy("mLock") boolean setScaleAndCenter(float scale, float centerX, float centerY, - MagnificationAnimationCallback animationCallback, int id) { + boolean isScaleTransient, MagnificationAnimationCallback animationCallback, + int id) { if (!mRegistered) { return false; } @@ -697,7 +712,7 @@ public class FullScreenMagnificationController implements + animationCallback + ", id = " + id + ")"); } boolean changed = setActivated(true); - changed |= updateMagnificationSpecLocked(scale, centerX, centerY); + changed |= updateMagnificationSpecLocked(scale, centerX, centerY, isScaleTransient); sendSpecToAnimation(mCurrentMagnificationSpec, animationCallback); if (isActivated() && (id != INVALID_SERVICE_ID)) { mIdOfLastServiceToMagnify = id; @@ -774,7 +789,9 @@ public class FullScreenMagnificationController implements * @return {@code true} if the magnification spec changed or {@code false} * otherwise */ - boolean updateMagnificationSpecLocked(float scale, float centerX, float centerY) { + @GuardedBy("mLock") + boolean updateMagnificationSpecLocked(float scale, float centerX, float centerY, + boolean isScaleTransient) { // Handle defaults. if (Float.isNaN(centerX)) { centerX = getCenterX(); @@ -802,7 +819,7 @@ public class FullScreenMagnificationController implements changed |= updateCurrentSpecWithOffsetsLocked(nonNormOffsetX, nonNormOffsetY); if (changed) { - onMagnificationChangedLocked(); + onMagnificationChangedLocked(isScaleTransient); } return changed; @@ -817,7 +834,7 @@ public class FullScreenMagnificationController implements final float nonNormOffsetX = mCurrentMagnificationSpec.offsetX - offsetX; final float nonNormOffsetY = mCurrentMagnificationSpec.offsetY - offsetY; if (updateCurrentSpecWithOffsetsLocked(nonNormOffsetX, nonNormOffsetY)) { - onMagnificationChangedLocked(); + onMagnificationChangedLocked(/* isScaleTransient= */ false); } if (id != INVALID_SERVICE_ID) { mIdOfLastServiceToMagnify = id; @@ -862,7 +879,7 @@ public class FullScreenMagnificationController implements } synchronized (mLock) { mCurrentMagnificationSpec.setTo(lastSpecSent); - onMagnificationChangedLocked(); + onMagnificationChangedLocked(/* isScaleTransient= */ false); } } }); @@ -1466,20 +1483,24 @@ public class FullScreenMagnificationController implements * @param scale the target scale, must be >= 1 * @param pivotX the screen-relative X coordinate around which to scale * @param pivotY the screen-relative Y coordinate around which to scale + * @param isScaleTransient {@code true} if the scale is for a short time and potentially changed + * soon. {@code false} otherwise. * @param animate {@code true} to animate the transition, {@code false} * to transition immediately * @param id the ID of the service requesting the change * @return {@code true} if the magnification spec changed, {@code false} if * the spec did not change */ + @SuppressWarnings("GuardedBy") + // errorprone cannot recognize an inner class guarded by an outer class member. public boolean setScale(int displayId, float scale, float pivotX, float pivotY, - boolean animate, int id) { + boolean isScaleTransient, boolean animate, int id) { synchronized (mLock) { final DisplayMagnification display = mDisplays.get(displayId); if (display == null) { return false; } - return display.setScale(scale, pivotX, pivotY, animate, id); + return display.setScale(scale, pivotX, pivotY, isScaleTransient, animate, id); } } @@ -1498,6 +1519,8 @@ public class FullScreenMagnificationController implements * @return {@code true} if the magnification spec changed, {@code false} if * the spec did not change */ + @SuppressWarnings("GuardedBy") + // errorprone cannot recognize an inner class guarded by an outer class member. public boolean setCenter(int displayId, float centerX, float centerY, boolean animate, int id) { synchronized (mLock) { final DisplayMagnification display = mDisplays.get(displayId); @@ -1505,7 +1528,7 @@ public class FullScreenMagnificationController implements return false; } return display.setScaleAndCenter(Float.NaN, centerX, centerY, - animate ? STUB_ANIMATION_CALLBACK : null, id); + /* isScaleTransient= */ false, animate ? STUB_ANIMATION_CALLBACK : null, id); } } @@ -1528,7 +1551,32 @@ public class FullScreenMagnificationController implements */ public boolean setScaleAndCenter(int displayId, float scale, float centerX, float centerY, boolean animate, int id) { - return setScaleAndCenter(displayId, scale, centerX, centerY, + return setScaleAndCenter(displayId, scale, centerX, centerY, /* isScaleTransient= */ false, + transformToStubCallback(animate), id); + } + + /** + * Sets the scale and center of the magnified region, optionally + * animating the transition. If animation is disabled, the transition + * is immediate. + * + * @param displayId The logical display id. + * @param scale the target scale, or {@link Float#NaN} to leave unchanged + * @param centerX the screen-relative X coordinate around which to + * center and scale, or {@link Float#NaN} to leave unchanged + * @param centerY the screen-relative Y coordinate around which to + * center and scale, or {@link Float#NaN} to leave unchanged + * @param isScaleTransient {@code true} if the scale is for a short time and potentially changed + * soon. {@code false} otherwise. + * @param animate {@code true} to animate the transition, {@code false} + * to transition immediately + * @param id the ID of the service requesting the change + * @return {@code true} if the magnification spec changed, {@code false} if + * the spec did not change + */ + public boolean setScaleAndCenter(int displayId, float scale, float centerX, float centerY, + boolean isScaleTransient, boolean animate, int id) { + return setScaleAndCenter(displayId, scale, centerX, centerY, isScaleTransient, transformToStubCallback(animate), id); } @@ -1543,20 +1591,25 @@ public class FullScreenMagnificationController implements * center and scale, or {@link Float#NaN} to leave unchanged * @param centerY the screen-relative Y coordinate around which to * center and scale, or {@link Float#NaN} to leave unchanged + * @param isScaleTransient {@code true} if the scale is for a short time and potentially changed + * soon. {@code false} otherwise. * @param animationCallback Called when the animation result is valid. * {@code null} to transition immediately * @param id the ID of the service requesting the change * @return {@code true} if the magnification spec changed, {@code false} if * the spec did not change */ + @SuppressWarnings("GuardedBy") + // errorprone cannot recognize an inner class guarded by an outer class member. public boolean setScaleAndCenter(int displayId, float scale, float centerX, float centerY, - MagnificationAnimationCallback animationCallback, int id) { + boolean isScaleTransient, MagnificationAnimationCallback animationCallback, int id) { synchronized (mLock) { final DisplayMagnification display = mDisplays.get(displayId); if (display == null) { return false; } - return display.setScaleAndCenter(scale, centerX, centerY, animationCallback, id); + return display.setScaleAndCenter(scale, centerX, centerY, isScaleTransient, + animationCallback, id); } } @@ -1571,6 +1624,8 @@ public class FullScreenMagnificationController implements * screen pixels. * @param id the ID of the service requesting the change */ + @SuppressWarnings("GuardedBy") + // errorprone cannot recognize an inner class guarded by an outer class member. public void offsetMagnifiedRegion(int displayId, float offsetX, float offsetY, int id) { synchronized (mLock) { final DisplayMagnification display = mDisplays.get(displayId); @@ -1669,6 +1724,8 @@ public class FullScreenMagnificationController implements * * @param displayId The logical display id. */ + @SuppressWarnings("GuardedBy") + // errorprone cannot recognize an inner class guarded by an outer class member. private void zoomOutFromService(int displayId) { synchronized (mLock) { final DisplayMagnification display = mDisplays.get(displayId); diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java index a19fdddea49c..39cecb0384b6 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java @@ -617,7 +617,8 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH } if (DEBUG_PANNING_SCALING) Slog.i(mLogTag, "Scaled content to: " + scale + "x"); - mFullScreenMagnificationController.setScale(mDisplayId, scale, pivotX, pivotY, false, + mFullScreenMagnificationController.setScale(mDisplayId, scale, pivotX, pivotY, + /* isScaleTransient= */ true, /* animate= */ false, AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID); checkShouldDetectPassPersistedScale(); @@ -1974,6 +1975,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH /* scale= */ scale, /* centerX= */ mPivotEdge.x, /* centerY= */ mPivotEdge.y, + /* isScaleTransient= */ true, /* animate= */ true, /* id= */ AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID); if (scale == 1.0f) { diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java index 1489d16c3764..d40e7476f7ec 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java @@ -176,7 +176,8 @@ public class MagnificationController implements MagnificationConnectionManager.C public void onPerformScaleAction(int displayId, float scale, boolean updatePersistence) { if (getFullScreenMagnificationController().isActivated(displayId)) { getFullScreenMagnificationController().setScaleAndCenter(displayId, scale, - Float.NaN, Float.NaN, false, MAGNIFICATION_GESTURE_HANDLER_ID); + Float.NaN, Float.NaN, /* isScaleTransient= */ !updatePersistence, false, + MAGNIFICATION_GESTURE_HANDLER_ID); if (updatePersistence) { getFullScreenMagnificationController().persistScale(displayId); } @@ -371,7 +372,7 @@ public class MagnificationController implements MagnificationConnectionManager.C } screenMagnificationController.setScaleAndCenter(displayId, targetScale, magnificationCenter.x, magnificationCenter.y, - magnificationAnimationCallback, id); + /* isScaleTransient= */ false, magnificationAnimationCallback, id); } else { if (screenMagnificationController.isRegistered(displayId)) { screenMagnificationController.reset(displayId, false); diff --git a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationProcessorTest.java b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationProcessorTest.java index 7829fcc4b44d..8df18a8c178b 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationProcessorTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationProcessorTest.java @@ -480,7 +480,7 @@ public class MagnificationProcessorTest { if (config.getMode() == MAGNIFICATION_MODE_FULLSCREEN) { mFullScreenMagnificationControllerStub.resetAndStubMethods(); mMockFullScreenMagnificationController.setScaleAndCenter(displayId, config.getScale(), - config.getCenterX(), config.getCenterY(), false, SERVICE_ID); + config.getCenterX(), config.getCenterY(), true, false, SERVICE_ID); mMagnificationManagerStub.deactivateIfNeed(); } else if (config.getMode() == MAGNIFICATION_MODE_WINDOW) { mMagnificationManagerStub.resetAndStubMethods(); @@ -529,6 +529,9 @@ public class MagnificationProcessorTest { mCenterY = invocation.getArgument(3); return true; }; + doAnswer(enableMagnificationStubAnswer).when( + mScreenMagnificationController).setScaleAndCenter(eq(TEST_DISPLAY), anyFloat(), + anyFloat(), anyFloat(), anyBoolean(), anyBoolean(), eq(SERVICE_ID)); doAnswer(enableMagnificationStubAnswer).when( mScreenMagnificationController).setScaleAndCenter(eq(TEST_DISPLAY), anyFloat(), anyFloat(), anyFloat(), anyBoolean(), eq(SERVICE_ID)); diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java index 76553ba0120f..5985abc344a2 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java @@ -289,10 +289,11 @@ public class FullScreenMagnificationControllerTest { mFullScreenMagnificationController.magnificationRegionContains(displayId, 100, 100)); assertFalse(mFullScreenMagnificationController.reset(displayId, true)); - assertFalse(mFullScreenMagnificationController.setScale(displayId, 2, 100, 100, true, 0)); + assertFalse( + mFullScreenMagnificationController.setScale(displayId, 2, 100, 100, true, true, 0)); assertFalse(mFullScreenMagnificationController.setCenter(displayId, 100, 100, false, 1)); assertFalse(mFullScreenMagnificationController.setScaleAndCenter(displayId, - 1.5f, 100, 100, false, 2)); + 1.5f, 100, 100, true, false, 2)); assertTrue(mFullScreenMagnificationController.getIdOfLastServiceToMagnify(displayId) < 0); mFullScreenMagnificationController.getMagnificationRegion(displayId, new Region()); @@ -317,7 +318,7 @@ public class FullScreenMagnificationControllerTest { final float scale = 2.0f; final PointF center = INITIAL_MAGNIFICATION_BOUNDS_CENTER; assertFalse(mFullScreenMagnificationController - .setScale(TEST_DISPLAY, scale, center.x, center.y, false, SERVICE_ID_1)); + .setScale(TEST_DISPLAY, scale, center.x, center.y, true, false, SERVICE_ID_1)); assertFalse(mFullScreenMagnificationController.isActivated(TEST_DISPLAY)); } @@ -335,7 +336,7 @@ public class FullScreenMagnificationControllerTest { final PointF center = INITIAL_MAGNIFICATION_BOUNDS_CENTER; final PointF offsets = computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, center, scale); assertTrue(mFullScreenMagnificationController - .setScale(displayId, scale, center.x, center.y, false, SERVICE_ID_1)); + .setScale(displayId, scale, center.x, center.y, true, false, SERVICE_ID_1)); mMessageCapturingHandler.sendAllMessages(); final MagnificationSpec expectedSpec = getMagnificationSpec(scale, offsets); @@ -361,7 +362,7 @@ public class FullScreenMagnificationControllerTest { float scale = 2.0f; PointF pivotPoint = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER; assertTrue(mFullScreenMagnificationController - .setScale(displayId, scale, pivotPoint.x, pivotPoint.y, true, SERVICE_ID_1)); + .setScale(displayId, scale, pivotPoint.x, pivotPoint.y, true, true, SERVICE_ID_1)); mMessageCapturingHandler.sendAllMessages(); // New center should be halfway between original center and pivot @@ -409,7 +410,7 @@ public class FullScreenMagnificationControllerTest { float scale = 2.0f; assertTrue(mFullScreenMagnificationController.setScale(displayId, scale, INITIAL_MAGNIFICATION_BOUNDS.centerX(), INITIAL_MAGNIFICATION_BOUNDS.centerY(), - false, SERVICE_ID_1)); + true, false, SERVICE_ID_1)); Mockito.reset(mMockWindowManager); PointF newCenter = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER; @@ -444,7 +445,7 @@ public class FullScreenMagnificationControllerTest { MagnificationSpec endSpec = getMagnificationSpec(scale, offsets); assertTrue(mFullScreenMagnificationController.setScaleAndCenter(displayId, scale, - newCenter.x, newCenter.y, mAnimationCallback, SERVICE_ID_1)); + newCenter.x, newCenter.y, true, mAnimationCallback, SERVICE_ID_1)); mMessageCapturingHandler.sendAllMessages(); assertEquals(newCenter.x, mFullScreenMagnificationController.getCenterX(displayId), 0.5); @@ -490,11 +491,11 @@ public class FullScreenMagnificationControllerTest { final PointF center = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER; final float targetScale = 2.0f; assertTrue(mFullScreenMagnificationController.setScaleAndCenter(displayId, - targetScale, center.x, center.y, false, SERVICE_ID_1)); + targetScale, center.x, center.y, true, false, SERVICE_ID_1)); mMessageCapturingHandler.sendAllMessages(); assertFalse(mFullScreenMagnificationController.setScaleAndCenter(displayId, - targetScale, center.x, center.y, mAnimationCallback, SERVICE_ID_1)); + targetScale, center.x, center.y, true, mAnimationCallback, SERVICE_ID_1)); mMessageCapturingHandler.sendAllMessages(); verify(mMockValueAnimator, never()).start(); @@ -520,7 +521,7 @@ public class FullScreenMagnificationControllerTest { assertTrue(mFullScreenMagnificationController.setScaleAndCenter(displayId, MagnificationScaleProvider.MAX_SCALE + 1.0f, - newCenter.x, newCenter.y, false, SERVICE_ID_1)); + newCenter.x, newCenter.y, true, false, SERVICE_ID_1)); mMessageCapturingHandler.sendAllMessages(); assertEquals(newCenter.x, mFullScreenMagnificationController.getCenterX(displayId), 0.5); @@ -531,7 +532,7 @@ public class FullScreenMagnificationControllerTest { // Verify that we can't zoom below 1x assertTrue(mFullScreenMagnificationController.setScaleAndCenter(displayId, 0.5f, INITIAL_MAGNIFICATION_BOUNDS_CENTER.x, INITIAL_MAGNIFICATION_BOUNDS_CENTER.y, - false, SERVICE_ID_1)); + true, false, SERVICE_ID_1)); mMessageCapturingHandler.sendAllMessages(); assertEquals(INITIAL_MAGNIFICATION_BOUNDS_CENTER.x, @@ -555,7 +556,7 @@ public class FullScreenMagnificationControllerTest { // Off the edge to the top and left assertTrue(mFullScreenMagnificationController.setScaleAndCenter(displayId, - scale, -100f, -200f, false, SERVICE_ID_1)); + scale, -100f, -200f, true, false, SERVICE_ID_1)); mMessageCapturingHandler.sendAllMessages(); PointF newCenter = INITIAL_BOUNDS_UPPER_LEFT_2X_CENTER; @@ -569,7 +570,7 @@ public class FullScreenMagnificationControllerTest { // Off the edge to the bottom and right assertTrue(mFullScreenMagnificationController.setScaleAndCenter(displayId, scale, INITIAL_MAGNIFICATION_BOUNDS.right + 1, INITIAL_MAGNIFICATION_BOUNDS.bottom + 1, - false, SERVICE_ID_1)); + true, false, SERVICE_ID_1)); mMessageCapturingHandler.sendAllMessages(); newCenter = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER; newOffsets = computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, newCenter, scale); @@ -623,7 +624,7 @@ public class FullScreenMagnificationControllerTest { PointF startOffsets = computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, startCenter, scale); // First zoom in assertTrue(mFullScreenMagnificationController - .setScaleAndCenter(displayId, scale, startCenter.x, startCenter.y, false, + .setScaleAndCenter(displayId, scale, startCenter.x, startCenter.y, true, false, SERVICE_ID_1)); mMessageCapturingHandler.sendAllMessages(); Mockito.reset(mMockWindowManager); @@ -677,7 +678,7 @@ public class FullScreenMagnificationControllerTest { // Upper left edges PointF ulCenter = INITIAL_BOUNDS_UPPER_LEFT_2X_CENTER; assertTrue(mFullScreenMagnificationController - .setScaleAndCenter(displayId, scale, ulCenter.x, ulCenter.y, false, + .setScaleAndCenter(displayId, scale, ulCenter.x, ulCenter.y, true, false, SERVICE_ID_1)); Mockito.reset(mMockWindowManager); MagnificationSpec ulSpec = getCurrentMagnificationSpec(displayId); @@ -689,7 +690,7 @@ public class FullScreenMagnificationControllerTest { // Lower right edges PointF lrCenter = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER; assertTrue(mFullScreenMagnificationController - .setScaleAndCenter(displayId, scale, lrCenter.x, lrCenter.y, false, + .setScaleAndCenter(displayId, scale, lrCenter.x, lrCenter.y, true, false, SERVICE_ID_1)); Mockito.reset(mMockWindowManager); MagnificationSpec lrSpec = getCurrentMagnificationSpec(displayId); @@ -714,7 +715,7 @@ public class FullScreenMagnificationControllerTest { float scale = 2.0f; // First zoom in assertTrue(mFullScreenMagnificationController - .setScaleAndCenter(displayId, scale, startCenter.x, startCenter.y, false, + .setScaleAndCenter(displayId, scale, startCenter.x, startCenter.y, true, false, SERVICE_ID_1)); mMessageCapturingHandler.sendAllMessages(); @@ -757,7 +758,7 @@ public class FullScreenMagnificationControllerTest { PointF startOffsets = computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, startCenter, scale); // First zoom in assertTrue(mFullScreenMagnificationController - .setScaleAndCenter(displayId, scale, startCenter.x, startCenter.y, false, + .setScaleAndCenter(displayId, scale, startCenter.x, startCenter.y, true, false, SERVICE_ID_1)); mMessageCapturingHandler.sendAllMessages(); @@ -790,12 +791,12 @@ public class FullScreenMagnificationControllerTest { register(displayId); PointF startCenter = INITIAL_MAGNIFICATION_BOUNDS_CENTER; assertTrue(mFullScreenMagnificationController - .setScale(displayId, 2.0f, startCenter.x, startCenter.y, false, + .setScale(displayId, 2.0f, startCenter.x, startCenter.y, true, false, SERVICE_ID_1)); assertEquals(SERVICE_ID_1, mFullScreenMagnificationController.getIdOfLastServiceToMagnify(displayId)); assertTrue(mFullScreenMagnificationController - .setScale(displayId, 1.5f, startCenter.x, startCenter.y, false, + .setScale(displayId, 1.5f, startCenter.x, startCenter.y, true, false, SERVICE_ID_2)); assertEquals(SERVICE_ID_2, mFullScreenMagnificationController.getIdOfLastServiceToMagnify(displayId)); @@ -813,10 +814,10 @@ public class FullScreenMagnificationControllerTest { register(displayId); PointF startCenter = INITIAL_MAGNIFICATION_BOUNDS_CENTER; mFullScreenMagnificationController - .setScale(displayId, 2.0f, startCenter.x, startCenter.y, false, + .setScale(displayId, 2.0f, startCenter.x, startCenter.y, true, false, SERVICE_ID_1); mFullScreenMagnificationController - .setScale(displayId, 1.5f, startCenter.x, startCenter.y, false, + .setScale(displayId, 1.5f, startCenter.x, startCenter.y, true, false, SERVICE_ID_2); assertFalse(mFullScreenMagnificationController.resetIfNeeded(displayId, SERVICE_ID_1)); checkActivatedAndMagnifying(/* activated= */ true, /* magnifying= */ true, displayId); @@ -877,7 +878,7 @@ public class FullScreenMagnificationControllerTest { float scale = 2.5f; PointF firstCenter = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER; assertTrue(mFullScreenMagnificationController.setScaleAndCenter(displayId, - scale, firstCenter.x, firstCenter.y, mAnimationCallback, SERVICE_ID_1)); + scale, firstCenter.x, firstCenter.y, true, mAnimationCallback, SERVICE_ID_1)); mMessageCapturingHandler.sendAllMessages(); Mockito.reset(mMockValueAnimator); // Stubs the logic after the animation is started. @@ -1080,7 +1081,7 @@ public class FullScreenMagnificationControllerTest { float scale = 2.0f; // setting animate parameter to true is differ from zoomIn2xToMiddle() mFullScreenMagnificationController.setScale(displayId, scale, startCenter.x, startCenter.y, - true, SERVICE_ID_1); + true, true, SERVICE_ID_1); MagnificationSpec startSpec = getCurrentMagnificationSpec(displayId); MagnificationCallbacks callbacks = getMagnificationCallbacks(displayId); Mockito.reset(mMockWindowManager); @@ -1111,7 +1112,7 @@ public class FullScreenMagnificationControllerTest { PointF startCenter = OTHER_BOUNDS_LOWER_RIGHT_2X_CENTER; float scale = 2.0f; mFullScreenMagnificationController.setScale(displayId, scale, startCenter.x, startCenter.y, - false, SERVICE_ID_1); + true, false, SERVICE_ID_1); mMessageCapturingHandler.sendAllMessages(); MagnificationSpec startSpec = getCurrentMagnificationSpec(displayId); verify(mMockWindowManager).setMagnificationSpec(eq(displayId), argThat(closeTo(startSpec))); @@ -1152,7 +1153,7 @@ public class FullScreenMagnificationControllerTest { PointF startCenter = OTHER_BOUNDS_LOWER_RIGHT_2X_CENTER; float scale = 2.0f; mFullScreenMagnificationController.setScale(displayId, scale, startCenter.x, startCenter.y, - true, SERVICE_ID_1); + true, true, SERVICE_ID_1); mMessageCapturingHandler.sendAllMessages(); MagnificationSpec startSpec = getCurrentMagnificationSpec(displayId); when(mMockValueAnimator.isRunning()).thenReturn(true); @@ -1339,7 +1340,7 @@ public class FullScreenMagnificationControllerTest { scale, computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, firstCenter, scale)); assertTrue(mFullScreenMagnificationController.setScaleAndCenter(displayId, - scale, firstCenter.x, firstCenter.y, true, SERVICE_ID_1)); + scale, firstCenter.x, firstCenter.y, true, true, SERVICE_ID_1)); mMessageCapturingHandler.sendAllMessages(); assertEquals(firstCenter.x, mFullScreenMagnificationController.getCenterX(displayId), 0.5); @@ -1408,7 +1409,7 @@ public class FullScreenMagnificationControllerTest { register(DISPLAY_0); final float scale = 1.0f; mFullScreenMagnificationController.setScaleAndCenter( - DISPLAY_0, scale, Float.NaN, Float.NaN, true, SERVICE_ID_1); + DISPLAY_0, scale, Float.NaN, Float.NaN, true, true, SERVICE_ID_1); checkActivatedAndMagnifying(/* activated= */ true, /* magnifying= */ false, DISPLAY_0); verify(mMockWindowManager).setFullscreenMagnificationActivated(DISPLAY_0, true); @@ -1453,7 +1454,7 @@ public class FullScreenMagnificationControllerTest { PointF pivotPoint = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER; mFullScreenMagnificationController.setScale(TEST_DISPLAY, 1.0f, pivotPoint.x, pivotPoint.y, - false, SERVICE_ID_1); + true, false, SERVICE_ID_1); mFullScreenMagnificationController.persistScale(TEST_DISPLAY); // persistScale may post a task to a background thread. Let's wait for it completes. @@ -1468,10 +1469,10 @@ public class FullScreenMagnificationControllerTest { register(DISPLAY_1); final PointF pivotPoint = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER; mFullScreenMagnificationController.setScale(DISPLAY_0, 3.0f, pivotPoint.x, pivotPoint.y, - false, SERVICE_ID_1); + true, false, SERVICE_ID_1); mFullScreenMagnificationController.persistScale(DISPLAY_0); mFullScreenMagnificationController.setScale(DISPLAY_1, 4.0f, pivotPoint.x, pivotPoint.y, - false, SERVICE_ID_1); + true, false, SERVICE_ID_1); mFullScreenMagnificationController.persistScale(DISPLAY_1); // persistScale may post a task to a background thread. Let's wait for it completes. @@ -1489,7 +1490,10 @@ public class FullScreenMagnificationControllerTest { PointF pivotPoint = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER; mFullScreenMagnificationController.setScale(TEST_DISPLAY, 4.0f, pivotPoint.x, pivotPoint.y, - true, SERVICE_ID_1); + /* isScaleTransient= */ true, /* animate= */ false, SERVICE_ID_1); + verify(mMockInputManager, never()).setAccessibilityPointerIconScaleFactor(anyInt(), + anyFloat()); + mFullScreenMagnificationController.persistScale(TEST_DISPLAY); // persistScale may post a task to a background thread. Let's wait for it completes. @@ -1499,6 +1503,81 @@ public class FullScreenMagnificationControllerTest { verify(mMockInputManager).setAccessibilityPointerIconScaleFactor(TEST_DISPLAY, 4.0f); } + @Test + @RequiresFlagsEnabled(FLAG_MAGNIFICATION_ENLARGE_POINTER) + public void setScale_setNonTransientScale_notifyInput() { + register(TEST_DISPLAY); + + PointF pivotPoint = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER; + mFullScreenMagnificationController.setScale(TEST_DISPLAY, 4.0f, pivotPoint.x, pivotPoint.y, + /* isScaleTransient= */ false, /* animate= */ false, SERVICE_ID_1); + + verify(mMockInputManager).setAccessibilityPointerIconScaleFactor(TEST_DISPLAY, 4.0f); + } + + @Test + @RequiresFlagsEnabled(FLAG_MAGNIFICATION_ENLARGE_POINTER) + public void setScaleAndCenter_setTransientScale_notNotifyInput() { + register(TEST_DISPLAY); + + PointF point = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER; + mFullScreenMagnificationController.setScaleAndCenter(TEST_DISPLAY, 3.0f, point.x, + point.y, /* isScaleTransient= */ true, /* animate= */ false, SERVICE_ID_1); + + verify(mRequestObserver).onFullScreenMagnificationChanged(anyInt(), any(Region.class), + any(MagnificationConfig.class)); + verify(mMockInputManager, never()).setAccessibilityPointerIconScaleFactor(anyInt(), + anyFloat()); + } + + @Test + @RequiresFlagsEnabled(FLAG_MAGNIFICATION_ENLARGE_POINTER) + public void setScaleAndCenter_setNonTransientScale_notifyInput() { + register(TEST_DISPLAY); + + PointF point = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER; + mFullScreenMagnificationController.setScaleAndCenter(TEST_DISPLAY, 3.0f, point.x, + point.y, /* isScaleTransient= */ false, /* animate= */ false, SERVICE_ID_1); + + verify(mMockInputManager).setAccessibilityPointerIconScaleFactor(TEST_DISPLAY, 3.0f); + } + + @Test + @RequiresFlagsEnabled(FLAG_MAGNIFICATION_ENLARGE_POINTER) + public void setCenter_notNotifyInput() { + register(TEST_DISPLAY); + + PointF point = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER; + mFullScreenMagnificationController.setScale(TEST_DISPLAY, 2.0f, point.x, point.y, + /* isScaleTransient= */ true, /* animate= */ false, SERVICE_ID_1); + mFullScreenMagnificationController.setCenter(TEST_DISPLAY, point.x, point.y, false, + SERVICE_ID_1); + + // Note that setCenter doesn't change scale, so it's not necessary to notify the input + // manager, but we currently do. The input manager skips redundant computation if the + // notified scale is the same as the previous call. + verify(mMockInputManager).setAccessibilityPointerIconScaleFactor(TEST_DISPLAY, + 2.0f); + } + + @Test + @RequiresFlagsEnabled(FLAG_MAGNIFICATION_ENLARGE_POINTER) + public void offsetMagnifiedRegion_notNotifyInput() { + register(TEST_DISPLAY); + + PointF point = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER; + mFullScreenMagnificationController.setScale(TEST_DISPLAY, 2.0f, point.x, point.y, + /* isScaleTransient= */ true, /* animate= */ false, SERVICE_ID_1); + mFullScreenMagnificationController.offsetMagnifiedRegion(TEST_DISPLAY, 100, 50, + SERVICE_ID_1); + + // Note that setCenter doesn't change scale, so it's not necessary to notify the input + // manager, but we currently do. The input manager skips redundant computation if the + // notified scale is the same as the previous call. + verify(mMockInputManager).setAccessibilityPointerIconScaleFactor(TEST_DISPLAY, + 2.0f); + } + @Test public void testOnContextChanged_alwaysOnFeatureDisabled_resetMagnification() { setScaleToMagnifying(); @@ -1556,7 +1635,7 @@ public class FullScreenMagnificationControllerTest { PointF pivotPoint = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER; mFullScreenMagnificationController.setScale(DISPLAY_0, scale, pivotPoint.x, pivotPoint.y, - false, SERVICE_ID_1); + true, false, SERVICE_ID_1); } private void initMockWindowManager() { @@ -1599,7 +1678,7 @@ public class FullScreenMagnificationControllerTest { PointF startCenter = INITIAL_MAGNIFICATION_BOUNDS_CENTER; float scale = 2.0f; mFullScreenMagnificationController.setScale(displayId, scale, startCenter.x, startCenter.y, - false, SERVICE_ID_1); + true, false, SERVICE_ID_1); checkActivatedAndMagnifying(/* activated= */ true, /* magnifying= */ true, displayId); } diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java index 00b7de818c12..217bb2e582d5 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java @@ -1346,7 +1346,7 @@ public class FullScreenMagnificationGestureHandlerTest { Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, persistedScale, UserHandle.USER_SYSTEM); mFullScreenMagnificationController.setScale(DISPLAY_0, scale, DEFAULT_X, - DEFAULT_Y, /* animate= */ false, + DEFAULT_Y, true, /* animate= */ false, AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID); mMgh.transitionTo(mMgh.mPanningScalingState); @@ -1367,7 +1367,7 @@ public class FullScreenMagnificationGestureHandlerTest { Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, persistedScale, UserHandle.USER_SYSTEM); mFullScreenMagnificationController.setScale(DISPLAY_0, scale, DEFAULT_X, - DEFAULT_Y, /* animate= */ false, + DEFAULT_Y, true, /* animate= */ false, AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID); mMgh.transitionTo(mMgh.mPanningScalingState); @@ -1404,7 +1404,7 @@ public class FullScreenMagnificationGestureHandlerTest { mFullScreenMagnificationController.getPersistedScale(DISPLAY_0); mFullScreenMagnificationController.setScale(DISPLAY_0, persistedScale, DEFAULT_X, - DEFAULT_Y, /* animate= */ false, + DEFAULT_Y, true, /* animate= */ false, AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID); mMgh.transitionTo(mMgh.mPanningScalingState); @@ -1441,7 +1441,7 @@ public class FullScreenMagnificationGestureHandlerTest { (INITIAL_MAGNIFICATION_BOUNDS.top + INITIAL_MAGNIFICATION_BOUNDS.height()) / 2.0f; float scale = 5.6f; // value is unimportant but unique among tests to increase coverage. mFullScreenMagnificationController.setScaleAndCenter( - DISPLAY_0, centerX, centerY, scale, /* animate= */ false, 1); + DISPLAY_0, centerX, centerY, scale, true, /* animate= */ false, 1); centerX = mFullScreenMagnificationController.getCenterX(DISPLAY_0); centerY = mFullScreenMagnificationController.getCenterY(DISPLAY_0); @@ -1533,7 +1533,7 @@ public class FullScreenMagnificationGestureHandlerTest { (INITIAL_MAGNIFICATION_BOUNDS.top + INITIAL_MAGNIFICATION_BOUNDS.height()) / 2.0f; float scale = 6.2f; // value is unimportant but unique among tests to increase coverage. mFullScreenMagnificationController.setScaleAndCenter( - DISPLAY_0, centerX, centerY, scale, /* animate= */ false, 1); + DISPLAY_0, centerX, centerY, scale, true, /* animate= */ false, 1); MotionEvent event = mouseEvent(centerX, centerY, ACTION_HOVER_MOVE); send(event, InputDevice.SOURCE_MOUSE); fastForward(20); @@ -1574,7 +1574,7 @@ public class FullScreenMagnificationGestureHandlerTest { (INITIAL_MAGNIFICATION_BOUNDS.top + INITIAL_MAGNIFICATION_BOUNDS.height()) / 2.0f; float scale = 4.0f; // value is unimportant but unique among tests to increase coverage. mFullScreenMagnificationController.setScaleAndCenter( - DISPLAY_0, centerX, centerY, scale, /* animate= */ false, 1); + DISPLAY_0, centerX, centerY, scale, true, /* animate= */ false, 1); // HOVER_MOVE should change magnifier viewport. MotionEvent event = motionEvent(centerX + 20, centerY, ACTION_HOVER_MOVE); @@ -1618,7 +1618,7 @@ public class FullScreenMagnificationGestureHandlerTest { (INITIAL_MAGNIFICATION_BOUNDS.top + INITIAL_MAGNIFICATION_BOUNDS.height()) / 2.0f; float scale = 5.3f; // value is unimportant but unique among tests to increase coverage. mFullScreenMagnificationController.setScaleAndCenter( - DISPLAY_0, centerX, centerY, scale, /* animate= */ false, 1); + DISPLAY_0, centerX, centerY, scale, true, /* animate= */ false, 1); MotionEvent event = motionEvent(centerX, centerY, ACTION_HOVER_MOVE); send(event, source); fastForward(20); @@ -1652,7 +1652,7 @@ public class FullScreenMagnificationGestureHandlerTest { (INITIAL_MAGNIFICATION_BOUNDS.top + INITIAL_MAGNIFICATION_BOUNDS.height()) / 2.0f; float scale = 2.7f; // value is unimportant but unique among tests to increase coverage. mFullScreenMagnificationController.setScaleAndCenter( - DISPLAY_0, centerX, centerY, scale, /* animate= */ false, 1); + DISPLAY_0, centerX, centerY, scale, true, /* animate= */ false, 1); MotionEvent event = motionEvent(centerX, centerY, ACTION_HOVER_MOVE); send(event, source); fastForward(20); @@ -1688,7 +1688,7 @@ public class FullScreenMagnificationGestureHandlerTest { (INITIAL_MAGNIFICATION_BOUNDS.top + INITIAL_MAGNIFICATION_BOUNDS.height()) / 2.0f; float scale = 3.8f; // value is unimportant but unique among tests to increase coverage. mFullScreenMagnificationController.setScaleAndCenter( - DISPLAY_0, centerX, centerY, scale, /* animate= */ false, 1); + DISPLAY_0, centerX, centerY, scale, true, /* animate= */ false, 1); centerX = mFullScreenMagnificationController.getCenterX(DISPLAY_0); centerY = mFullScreenMagnificationController.getCenterY(DISPLAY_0); @@ -1725,7 +1725,7 @@ public class FullScreenMagnificationGestureHandlerTest { (INITIAL_MAGNIFICATION_BOUNDS.top + INITIAL_MAGNIFICATION_BOUNDS.height()) / 2.0f; float scale = 4.0f; // value is unimportant but unique among tests to increase coverage. mFullScreenMagnificationController.setScaleAndCenter( - DISPLAY_0, centerX, centerY, scale, /* animate= */ false, 1); + DISPLAY_0, centerX, centerY, scale, true, /* animate= */ false, 1); centerX = mFullScreenMagnificationController.getCenterX(DISPLAY_0); centerY = mFullScreenMagnificationController.getCenterY(DISPLAY_0); diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java index d70e1fe1d9d1..8164ef9314d9 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java @@ -421,7 +421,7 @@ public class MagnificationControllerTest { assertTrue(mMagnificationConnectionManager.isWindowMagnifierEnabled(TEST_DISPLAY)); verify(mScreenMagnificationController, never()).setScaleAndCenter(TEST_DISPLAY, DEFAULT_SCALE, MAGNIFIED_CENTER_X, MAGNIFIED_CENTER_Y, - true, MAGNIFICATION_GESTURE_HANDLER_ID); + true, true, MAGNIFICATION_GESTURE_HANDLER_ID); verify(mTransitionCallBack).onResult(TEST_DISPLAY, false); } @@ -471,7 +471,7 @@ public class MagnificationControllerTest { assertFalse(mMagnificationConnectionManager.isWindowMagnifierEnabled(TEST_DISPLAY)); verify(mScreenMagnificationController).setScaleAndCenter(eq(TEST_DISPLAY), eq(DEFAULT_SCALE), eq(MAGNIFIED_CENTER_X), eq(MAGNIFIED_CENTER_Y), - any(MagnificationAnimationCallback.class), eq(TEST_SERVICE_ID)); + eq(false), any(MagnificationAnimationCallback.class), eq(TEST_SERVICE_ID)); } @Test @@ -488,7 +488,7 @@ public class MagnificationControllerTest { verify(mScreenMagnificationController, never()).setScaleAndCenter(anyInt(), anyFloat(), anyFloat(), anyFloat(), - anyBoolean(), anyInt()); + anyBoolean(), anyBoolean(), anyInt()); } @Test @@ -550,7 +550,7 @@ public class MagnificationControllerTest { config, animate, TEST_SERVICE_ID); verify(mScreenMagnificationController).setScaleAndCenter(eq(TEST_DISPLAY), /* scale= */ anyFloat(), /* centerX= */ anyFloat(), /* centerY= */ anyFloat(), - mCallbackArgumentCaptor.capture(), /* id= */ anyInt()); + anyBoolean(), mCallbackArgumentCaptor.capture(), /* id= */ anyInt()); mCallbackArgumentCaptor.getValue().onResult(true); mMockConnection.invokeCallbacks(); @@ -620,7 +620,7 @@ public class MagnificationControllerTest { @Test public void magnifyThroughExternalRequest_showMagnificationButton() { mScreenMagnificationController.setScaleAndCenter(TEST_DISPLAY, DEFAULT_SCALE, - MAGNIFIED_CENTER_X, MAGNIFIED_CENTER_Y, false, TEST_SERVICE_ID); + MAGNIFIED_CENTER_X, MAGNIFIED_CENTER_Y, true, false, TEST_SERVICE_ID); // The first time is trigger when fullscreen mode is activated. // The second time is triggered when magnification spec is changed. @@ -642,7 +642,7 @@ public class MagnificationControllerTest { mMagnificationController.onPerformScaleAction(TEST_DISPLAY, newScale, updatePersistence); verify(mScreenMagnificationController).setScaleAndCenter(eq(TEST_DISPLAY), eq(newScale), - anyFloat(), anyFloat(), anyBoolean(), anyInt()); + anyFloat(), anyFloat(), anyBoolean(), anyBoolean(), anyInt()); verify(mScreenMagnificationController).persistScale(eq(TEST_DISPLAY)); } @@ -685,7 +685,7 @@ public class MagnificationControllerTest { final MagnificationConfig config = obtainMagnificationConfig(MODE_FULLSCREEN); mScreenMagnificationController.setScaleAndCenter(TEST_DISPLAY, config.getScale(), config.getCenterX(), config.getCenterY(), - true, TEST_SERVICE_ID); + true, true, TEST_SERVICE_ID); // The notify method is triggered when setting magnification enabled. // The setScaleAndCenter call should not trigger notify method due to same scale and center. @@ -934,7 +934,7 @@ public class MagnificationControllerTest { public void onWindowModeActivated_fullScreenIsActivatedByExternal_fullScreenIsDisabled() { mScreenMagnificationController.setScaleAndCenter(TEST_DISPLAY, DEFAULT_SCALE, MAGNIFIED_CENTER_X, MAGNIFIED_CENTER_Y, - true, TEST_SERVICE_ID); + true, true, TEST_SERVICE_ID); mMagnificationController.onWindowMagnificationActivationState(TEST_DISPLAY, true); @@ -1321,7 +1321,8 @@ public class MagnificationControllerTest { } if (mode == MODE_FULLSCREEN) { mScreenMagnificationController.setScaleAndCenter(displayId, DEFAULT_SCALE, centerX, - centerY, true, AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID); + centerY, true, true, + AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID); } else { mMagnificationConnectionManager.enableWindowMagnification(displayId, DEFAULT_SCALE, centerX, centerY, null, TEST_SERVICE_ID); -- cgit v1.2.3-59-g8ed1b From d8db3579b40357e9987f38c0f7a825dd658d33ac Mon Sep 17 00:00:00 2001 From: Asmita Poddar Date: Tue, 22 Oct 2024 10:56:52 +0000 Subject: Fix NPE in MouseKeysInterceptor Check virtual device state before closing it. Bug: 374550219 Change-Id: I34e676e594b89c340424e4892b1aba8af386f76b Test: atest FrameworksServicesTests:MouseKeysInterceptorTest Flag: EXEMPT bugfix --- .../java/com/android/server/accessibility/MouseKeysInterceptor.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'services/accessibility') diff --git a/services/accessibility/java/com/android/server/accessibility/MouseKeysInterceptor.java b/services/accessibility/java/com/android/server/accessibility/MouseKeysInterceptor.java index 1df5d1a1d54f..1212c757f1c2 100644 --- a/services/accessibility/java/com/android/server/accessibility/MouseKeysInterceptor.java +++ b/services/accessibility/java/com/android/server/accessibility/MouseKeysInterceptor.java @@ -625,7 +625,9 @@ public class MouseKeysInterceptor extends BaseEventStreamTransformation }); mHandler.removeCallbacksAndMessages(null); - mVirtualDevice.close(); + if (mVirtualDevice != null) { + mVirtualDevice.close(); + } } @Override -- cgit v1.2.3-59-g8ed1b From 7490b43a898fc99048980cd84802f176d044c014 Mon Sep 17 00:00:00 2001 From: Hiroki Sato Date: Wed, 23 Oct 2024 14:37:28 +0900 Subject: Move magnification_enlarge_pointer aconfig to bugfix flag This is now rolled out as a bugfix. Bug: 355734856 Test: atest com.android.server.accessibility.magnification (with/without flag enabled) Flag: com.android.server.accessibility.magnification_enlarge_pointer_bugfix Change-Id: Ia08e3192577e991acda9a1293fe40a39b2f32f70 --- services/accessibility/accessibility.aconfig | 5 ++++- .../magnification/FullScreenMagnificationController.java | 2 +- .../FullScreenMagnificationControllerTest.java | 14 +++++++------- 3 files changed, 12 insertions(+), 9 deletions(-) (limited to 'services/accessibility') diff --git a/services/accessibility/accessibility.aconfig b/services/accessibility/accessibility.aconfig index cb4e9949a8ff..2808056f72c9 100644 --- a/services/accessibility/accessibility.aconfig +++ b/services/accessibility/accessibility.aconfig @@ -175,10 +175,13 @@ flag { } flag { - name: "magnification_enlarge_pointer" + name: "magnification_enlarge_pointer_bugfix" namespace: "accessibility" description: "When fullscreen magnification is enabled, pointer icon is enlarged" bug: "355734856" + metadata { + purpose: PURPOSE_BUGFIX + } } flag { diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java index ce1a292fb069..d3d80e12313f 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java @@ -1759,7 +1759,7 @@ public class FullScreenMagnificationController implements * @param scale The new scale factor. */ public void notifyScaleForInput(int displayId, float scale) { - if (Flags.magnificationEnlargePointer()) { + if (Flags.magnificationEnlargePointerBugfix()) { mControllerCtx.getInputManager() .setAccessibilityPointerIconScaleFactor(displayId, scale); } diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java index 5985abc344a2..7e0c12a5a545 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java @@ -18,7 +18,7 @@ package com.android.server.accessibility.magnification; import static android.accessibilityservice.MagnificationConfig.MAGNIFICATION_MODE_FULLSCREEN; -import static com.android.server.accessibility.Flags.FLAG_MAGNIFICATION_ENLARGE_POINTER; +import static com.android.server.accessibility.Flags.FLAG_MAGNIFICATION_ENLARGE_POINTER_BUGFIX; import static com.android.server.accessibility.magnification.FullScreenMagnificationController.MagnificationInfoChangedCallback; import static com.android.server.accessibility.magnification.MockMagnificationConnection.TEST_DISPLAY; import static com.android.window.flags.Flags.FLAG_ALWAYS_DRAW_MAGNIFICATION_FULLSCREEN_BORDER; @@ -1484,7 +1484,7 @@ public class FullScreenMagnificationControllerTest { } @Test - @RequiresFlagsEnabled(FLAG_MAGNIFICATION_ENLARGE_POINTER) + @RequiresFlagsEnabled(FLAG_MAGNIFICATION_ENLARGE_POINTER_BUGFIX) public void persistScale_setValue_notifyInput() { register(TEST_DISPLAY); @@ -1504,7 +1504,7 @@ public class FullScreenMagnificationControllerTest { } @Test - @RequiresFlagsEnabled(FLAG_MAGNIFICATION_ENLARGE_POINTER) + @RequiresFlagsEnabled(FLAG_MAGNIFICATION_ENLARGE_POINTER_BUGFIX) public void setScale_setNonTransientScale_notifyInput() { register(TEST_DISPLAY); @@ -1516,7 +1516,7 @@ public class FullScreenMagnificationControllerTest { } @Test - @RequiresFlagsEnabled(FLAG_MAGNIFICATION_ENLARGE_POINTER) + @RequiresFlagsEnabled(FLAG_MAGNIFICATION_ENLARGE_POINTER_BUGFIX) public void setScaleAndCenter_setTransientScale_notNotifyInput() { register(TEST_DISPLAY); @@ -1531,7 +1531,7 @@ public class FullScreenMagnificationControllerTest { } @Test - @RequiresFlagsEnabled(FLAG_MAGNIFICATION_ENLARGE_POINTER) + @RequiresFlagsEnabled(FLAG_MAGNIFICATION_ENLARGE_POINTER_BUGFIX) public void setScaleAndCenter_setNonTransientScale_notifyInput() { register(TEST_DISPLAY); @@ -1543,7 +1543,7 @@ public class FullScreenMagnificationControllerTest { } @Test - @RequiresFlagsEnabled(FLAG_MAGNIFICATION_ENLARGE_POINTER) + @RequiresFlagsEnabled(FLAG_MAGNIFICATION_ENLARGE_POINTER_BUGFIX) public void setCenter_notNotifyInput() { register(TEST_DISPLAY); @@ -1561,7 +1561,7 @@ public class FullScreenMagnificationControllerTest { } @Test - @RequiresFlagsEnabled(FLAG_MAGNIFICATION_ENLARGE_POINTER) + @RequiresFlagsEnabled(FLAG_MAGNIFICATION_ENLARGE_POINTER_BUGFIX) public void offsetMagnifiedRegion_notNotifyInput() { register(TEST_DISPLAY); -- cgit v1.2.3-59-g8ed1b From af7095ea5b006af8e72e086a0528e383fb68e323 Mon Sep 17 00:00:00 2001 From: Riley Jones Date: Fri, 18 Oct 2024 22:11:25 +0000 Subject: adjust migrateAccessibilityButtonSettings to check every shortcut type some shortcuts were being unnecessarily migrated because we weren't checking the settings for every type. I took the opportunity to also refactor A11yUserState to enable fetching multiple shortcut types at once. Bug: 374373185 Test: atest AccessibilityManagerServiceTest AccessibilityUserStateTest, and manually verify the conditions in the bug no longer occur Flag: android.provider.a11y_standalone_gesture_enabled Change-Id: Ib9f62a20a88d09e3e2c1ecc3fec49d052d98b448 --- .../accessibility/AccessibilityManagerService.java | 8 +- .../accessibility/AccessibilityUserState.java | 91 +++++++++------------- .../AccessibilityManagerServiceTest.java | 9 ++- .../accessibility/AccessibilityUserStateTest.java | 39 +++++++--- 4 files changed, 75 insertions(+), 72 deletions(-) (limited to 'services/accessibility') diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index c6fe4971b9e5..974cba2450f4 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -52,6 +52,7 @@ import static com.android.internal.accessibility.AccessibilityShortcutController import static com.android.internal.accessibility.common.ShortcutConstants.CHOOSER_PACKAGE_NAME; import static com.android.internal.accessibility.common.ShortcutConstants.USER_SHORTCUT_TYPES; import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType; +import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.ALL; import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.GESTURE; import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.HARDWARE; import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.QUICK_SETTINGS; @@ -3897,6 +3898,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub userState.getShortcutTargetsLocked(HARDWARE); final Set qsShortcutTargets = userState.getShortcutTargetsLocked(QUICK_SETTINGS); + final Set shortcutTargets = userState.getShortcutTargetsLocked(ALL); userState.mEnabledServices.forEach(componentName -> { if (packageName != null && componentName != null && !packageName.equals(componentName.getPackageName())) { @@ -3917,7 +3919,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub if (TextUtils.isEmpty(serviceName)) { return; } - if (doesShortcutTargetsStringContain(buttonTargets, serviceName) + if (android.provider.Flags.a11yStandaloneGestureEnabled()) { + if (doesShortcutTargetsStringContain(shortcutTargets, serviceName)) { + return; + } + } else if (doesShortcutTargetsStringContain(buttonTargets, serviceName) || doesShortcutTargetsStringContain(shortcutKeyTargets, serviceName) || doesShortcutTargetsStringContain(qsShortcutTargets, serviceName)) { return; diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java index 0bf7ec001d4d..67b40632dde8 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java @@ -106,21 +106,17 @@ class AccessibilityUserState { final Set mTouchExplorationGrantedServices = new HashSet<>(); - private final ArraySet mAccessibilityShortcutKeyTargets = new ArraySet<>(); - - private final ArraySet mAccessibilityButtonTargets = new ArraySet<>(); - private final ArraySet mAccessibilityGestureTargets = new ArraySet<>(); - private final ArraySet mAccessibilityQsTargets = new ArraySet<>(); + private final HashMap> mShortcutTargets = new HashMap<>(); /** - * The QuickSettings tiles in the QS Panel. This can be different from - * {@link #mAccessibilityQsTargets} in that {@link #mA11yTilesInQsPanel} stores the + * The QuickSettings tiles in the QS Panel. This can be different from the QS targets in + * {@link #mShortcutTargets} in that {@link #mA11yTilesInQsPanel} stores the * TileService's or the a11y framework tile component names (e.g. * {@link AccessibilityShortcutController#COLOR_INVERSION_TILE_COMPONENT_NAME}) instead of the * A11y Feature's component names. *

    * In addition, {@link #mA11yTilesInQsPanel} stores what's on the QS Panel, whereas - * {@link #mAccessibilityQsTargets} stores the targets that configured qs as their shortcut and + * {@link #mShortcutTargets} stores the targets that configured qs as their shortcut and * also grant full device control permission. */ private final ArraySet mA11yTilesInQsPanel = new ArraySet<>(); @@ -208,6 +204,11 @@ class AccessibilityUserState { mSupportWindowMagnification = mContext.getResources().getBoolean( R.bool.config_magnification_area) && mContext.getPackageManager().hasSystemFeature( PackageManager.FEATURE_WINDOW_MAGNIFICATION); + + mShortcutTargets.put(HARDWARE, new ArraySet<>()); + mShortcutTargets.put(SOFTWARE, new ArraySet<>()); + mShortcutTargets.put(GESTURE, new ArraySet<>()); + mShortcutTargets.put(QUICK_SETTINGS, new ArraySet<>()); } boolean isHandlingAccessibilityEventsLocked() { @@ -233,10 +234,7 @@ class AccessibilityUserState { // Clear state persisted in settings. mEnabledServices.clear(); mTouchExplorationGrantedServices.clear(); - mAccessibilityShortcutKeyTargets.clear(); - mAccessibilityButtonTargets.clear(); - mAccessibilityGestureTargets.clear(); - mAccessibilityQsTargets.clear(); + mShortcutTargets.forEach((type, targets) -> targets.clear()); mA11yTilesInQsPanel.clear(); mTargetAssignedToAccessibilityButton = null; mIsTouchExplorationEnabled = false; @@ -541,7 +539,7 @@ class AccessibilityUserState { private void dumpShortcutTargets( PrintWriter pw, @UserShortcutType int shortcutType, String name) { pw.append(" ").append(name).append(":{"); - ArraySet targets = getShortcutTargetsInternalLocked(shortcutType); + ArraySet targets = getShortcutTargetsLocked(shortcutType); int size = targets.size(); for (int i = 0; i < size; i++) { if (i > 0) { @@ -712,7 +710,7 @@ class AccessibilityUserState { */ public boolean isShortcutMagnificationEnabledLocked() { for (int shortcutType : ShortcutConstants.USER_SHORTCUT_TYPES) { - if (getShortcutTargetsInternalLocked(shortcutType) + if (getShortcutTargetsLocked(shortcutType) .contains(MAGNIFICATION_CONTROLLER_NAME)) { return true; } @@ -787,44 +785,30 @@ class AccessibilityUserState { mMagnificationModes.put(displayId, mode); } - /** - * Disable both shortcuts' magnification function. - */ - public void disableShortcutMagnificationLocked() { - mAccessibilityShortcutKeyTargets.remove(MAGNIFICATION_CONTROLLER_NAME); - mAccessibilityButtonTargets.remove(MAGNIFICATION_CONTROLLER_NAME); - } - /** * Returns a set which contains the flattened component names and the system class names - * assigned to the given shortcut. The set is a defensive copy. To apply any changes to the set, - * use {@link #updateShortcutTargetsLocked(Set, int)} + * assigned to the given shortcut. The set is a defensive copy. + * To apply any changes to the set, use {@link #updateShortcutTargetsLocked(Set, int)} * - * @param shortcutType The shortcut type. + * @param shortcutTypes The shortcut type or types (in bitmask format). * @return The array set of the strings */ - public ArraySet getShortcutTargetsLocked(@UserShortcutType int shortcutType) { - return new ArraySet<>(getShortcutTargetsInternalLocked(shortcutType)); - } - - private ArraySet getShortcutTargetsInternalLocked(@UserShortcutType int shortcutType) { - if (shortcutType == HARDWARE) { - return mAccessibilityShortcutKeyTargets; - } else if (shortcutType == SOFTWARE) { - return mAccessibilityButtonTargets; - } else if (shortcutType == GESTURE) { - return mAccessibilityGestureTargets; - } else if (shortcutType == QUICK_SETTINGS) { - return mAccessibilityQsTargets; - } else if ((shortcutType == TRIPLETAP - && isMagnificationSingleFingerTripleTapEnabledLocked()) || ( - shortcutType == TWOFINGER_DOUBLETAP - && isMagnificationTwoFingerTripleTapEnabledLocked())) { - ArraySet targets = new ArraySet<>(); - targets.add(MAGNIFICATION_CONTROLLER_NAME); - return targets; + public ArraySet getShortcutTargetsLocked(int shortcutTypes) { + ArraySet targets = new ArraySet<>(); + for (int shortcutType : ShortcutConstants.USER_SHORTCUT_TYPES) { + if ((shortcutTypes & shortcutType) != shortcutType) { + continue; + } + if ((shortcutType == TRIPLETAP + && isMagnificationSingleFingerTripleTapEnabledLocked()) || ( + shortcutType == TWOFINGER_DOUBLETAP + && isMagnificationTwoFingerTripleTapEnabledLocked())) { + targets.add(MAGNIFICATION_CONTROLLER_NAME); + } else if (mShortcutTargets.containsKey(shortcutType)) { + targets.addAll(mShortcutTargets.get(shortcutType)); + } } - return new ArraySet<>(); + return targets; } /** @@ -843,8 +827,10 @@ class AccessibilityUserState { if ((shortcutType & mask) != 0) { throw new IllegalArgumentException("Tap shortcuts cannot be updated with target sets."); } - - final Set currentTargets = getShortcutTargetsInternalLocked(shortcutType); + if (!mShortcutTargets.containsKey(shortcutType)) { + mShortcutTargets.put(shortcutType, new ArraySet<>()); + } + ArraySet currentTargets = mShortcutTargets.get(shortcutType); if (newTargets.equals(currentTargets)) { return false; } @@ -904,7 +890,7 @@ class AccessibilityUserState { } // getting internal set lets us directly modify targets, as it's not a copy. - Set targets = getShortcutTargetsInternalLocked(shortcutType); + Set targets = mShortcutTargets.get(shortcutType); return targets.removeIf(name -> { ComponentName componentName; if (name == null @@ -1169,13 +1155,6 @@ class AccessibilityUserState { ); } - /** - * Returns a copy of the targets which has qs shortcut turned on - */ - public ArraySet getA11yQsTargets() { - return new ArraySet<>(mAccessibilityQsTargets); - } - public void updateA11yTilesInQsPanelLocked(Set componentNames) { mA11yTilesInQsPanel.clear(); mA11yTilesInQsPanel.addAll(componentNames); diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java index 7481fc8ec46d..2edde9b74d0a 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java @@ -1615,7 +1615,8 @@ public class AccessibilityManagerServiceTest { List.of(tile) ); - assertThat(mA11yms.getCurrentUserState().getA11yQsTargets()).doesNotContain(tile); + assertThat(mA11yms.getCurrentUserState() + .getShortcutTargetsLocked(QUICK_SETTINGS)).doesNotContain(tile.flattenToString()); } @Test @@ -1636,7 +1637,7 @@ public class AccessibilityManagerServiceTest { List.of(tile) ); - assertThat(mA11yms.getCurrentUserState().getA11yQsTargets()) + assertThat(mA11yms.getCurrentUserState().getShortcutTargetsLocked(QUICK_SETTINGS)) .contains(TARGET_ALWAYS_ON_A11Y_SERVICE.flattenToString()); } @@ -1656,7 +1657,7 @@ public class AccessibilityManagerServiceTest { ); assertThat( - mA11yms.getCurrentUserState().getA11yQsTargets() + mA11yms.getCurrentUserState().getShortcutTargetsLocked(QUICK_SETTINGS) ).containsExactlyElementsIn(List.of( AccessibilityShortcutController.DALTONIZER_COMPONENT_NAME.flattenToString(), AccessibilityShortcutController.COLOR_INVERSION_COMPONENT_NAME.flattenToString()) @@ -1676,7 +1677,7 @@ public class AccessibilityManagerServiceTest { ); assertThat( - mA11yms.getCurrentUserState().getA11yQsTargets() + mA11yms.getCurrentUserState().getShortcutTargetsLocked(QUICK_SETTINGS) ).doesNotContain( AccessibilityShortcutController.DALTONIZER_COMPONENT_NAME.flattenToString()); } diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java index 627b5e39a20a..8c35925debff 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java @@ -29,6 +29,7 @@ import static android.view.accessibility.AccessibilityManager.STATE_FLAG_HIGH_TE import static android.view.accessibility.AccessibilityManager.STATE_FLAG_TOUCH_EXPLORATION_ENABLED; import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME; +import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.ALL; import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.GESTURE; import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.HARDWARE; import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.QUICK_SETTINGS; @@ -72,6 +73,7 @@ import com.android.internal.R; import com.android.internal.accessibility.AccessibilityShortcutController; import com.android.internal.accessibility.common.ShortcutConstants; import com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType; +import com.android.internal.accessibility.util.ShortcutUtils; import com.android.internal.util.test.FakeSettingsProvider; import org.junit.After; @@ -454,17 +456,7 @@ public class AccessibilityUserStateTest { mUserState.updateShortcutTargetsLocked(newTargets, QUICK_SETTINGS); - assertThat(mUserState.getA11yQsTargets()).isEqualTo(newTargets); - } - - @Test - public void getA11yQsTargets_returnsCopiedData() { - updateShortcutTargetsLocked_quickSettings_valueUpdated(); - - Set targets = mUserState.getA11yQsTargets(); - targets.clear(); - - assertThat(mUserState.getA11yQsTargets()).isNotEmpty(); + assertThat(mUserState.getShortcutTargetsLocked(QUICK_SETTINGS)).isEqualTo(newTargets); } @Test @@ -539,6 +531,31 @@ public class AccessibilityUserStateTest { assertThat(mUserState.isShortcutMagnificationEnabledLocked()).isFalse(); } + @Test + public void getShortcutTargetsLocked_returnsCorrectTargets() { + for (int shortcutType : ShortcutConstants.USER_SHORTCUT_TYPES) { + if (((TRIPLETAP | TWOFINGER_DOUBLETAP) & shortcutType) == shortcutType) { + continue; + } + Set expectedSet = Set.of(ShortcutUtils.convertToKey(shortcutType)); + mUserState.updateShortcutTargetsLocked(expectedSet, shortcutType); + + assertThat(mUserState.getShortcutTargetsLocked(shortcutType)) + .containsExactlyElementsIn(expectedSet); + } + } + + @Test + public void getShortcutTargetsLocked_returnsCopiedData() { + Set set = Set.of("FOO", "BAR"); + mUserState.updateShortcutTargetsLocked(set, SOFTWARE); + + Set targets = mUserState.getShortcutTargetsLocked(ALL); + targets.clear(); + + assertThat(mUserState.getShortcutTargetsLocked(ALL)).isNotEmpty(); + } + private int getSecureIntForUser(String key, int userId) { return Settings.Secure.getIntForUser(mMockResolver, key, -1, userId); } -- cgit v1.2.3-59-g8ed1b From 664c55c509e960708c65dd979eee0b814e900679 Mon Sep 17 00:00:00 2001 From: Daniel Norman Date: Mon, 4 Nov 2024 18:17:14 +0000 Subject: Add null check for A11yMS#isA11yServiceWarningRequired. Return true (warning required) as the "safer" default if a caller provides a null service info. Change-Id: I5fdb96aeb7fd22e3edf104f7b73f77399043cfb3 Fix: 377039485 Flag: EXEMPT bugfix --- .../android/server/accessibility/AccessibilityManagerService.java | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'services/accessibility') diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index 974cba2450f4..86d3ee6d1257 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -5071,6 +5071,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub @EnforcePermission(MANAGE_ACCESSIBILITY) public boolean isAccessibilityServiceWarningRequired(AccessibilityServiceInfo info) { isAccessibilityServiceWarningRequired_enforcePermission(); + if (info == null) { + Log.e(LOG_TAG, "Called isAccessibilityServiceWarningRequired with null service info"); + return true; + } + final ComponentName componentName = info.getComponentName(); // Warning is not required if the service is already enabled. -- cgit v1.2.3-59-g8ed1b From 248654d83f361c8e667fdfda770d9a8977b704d3 Mon Sep 17 00:00:00 2001 From: David Padlipsky Date: Wed, 16 Oct 2024 20:59:19 +0000 Subject: Implement magnification scale control shortcuts Adds keyboard shortcuts to zoom in and out of both the fullscreen and windowed magnifier depending on which magnifier is currently in use. Bug: 375277090 Test: atest + locally on device Flag: com.android.hardware.input.enable_talkback_and_magnifier_key_gestures Change-Id: Ia5a02168c03ac1f40fc4e326bdd10f393fecce14 --- .../android/hardware/input/KeyGestureEvent.java | 8 ++ .../accessibility/AccessibilityInputFilter.java | 60 +++++++++++++++ .../magnification/MagnificationController.java | 77 +++++++++++++++++++ .../android/server/input/InputGestureManager.java | 6 ++ services/tests/servicestests/AndroidManifest.xml | 1 + .../magnification/MagnificationControllerTest.java | 88 ++++++++++++++++++++++ .../server/input/KeyGestureControllerTests.kt | 28 ++++++- 7 files changed, 267 insertions(+), 1 deletion(-) (limited to 'services/accessibility') diff --git a/core/java/android/hardware/input/KeyGestureEvent.java b/core/java/android/hardware/input/KeyGestureEvent.java index 9d42b67bf5a8..506a19cce159 100644 --- a/core/java/android/hardware/input/KeyGestureEvent.java +++ b/core/java/android/hardware/input/KeyGestureEvent.java @@ -117,6 +117,8 @@ public final class KeyGestureEvent { public static final int KEY_GESTURE_TYPE_SNAP_RIGHT_FREEFORM_WINDOW = 69; public static final int KEY_GESTURE_TYPE_MAXIMIZE_FREEFORM_WINDOW = 70; public static final int KEY_GESTURE_TYPE_RESTORE_FREEFORM_WINDOW_SIZE = 71; + public static final int KEY_GESTURE_TYPE_MAGNIFIER_ZOOM_IN = 72; + public static final int KEY_GESTURE_TYPE_MAGNIFIER_ZOOM_OUT = 73; public static final int FLAG_CANCELLED = 1; @@ -203,6 +205,8 @@ public final class KeyGestureEvent { KEY_GESTURE_TYPE_SNAP_RIGHT_FREEFORM_WINDOW, KEY_GESTURE_TYPE_MAXIMIZE_FREEFORM_WINDOW, KEY_GESTURE_TYPE_RESTORE_FREEFORM_WINDOW_SIZE, + KEY_GESTURE_TYPE_MAGNIFIER_ZOOM_IN, + KEY_GESTURE_TYPE_MAGNIFIER_ZOOM_OUT, }) @Retention(RetentionPolicy.SOURCE) public @interface KeyGestureType { @@ -773,6 +777,10 @@ public final class KeyGestureEvent { return "KEY_GESTURE_TYPE_SNAP_RIGHT_FREEFORM_WINDOW"; case KEY_GESTURE_TYPE_RESTORE_FREEFORM_WINDOW_SIZE: return "KEY_GESTURE_TYPE_RESTORE_FREEFORM_WINDOW_SIZE"; + case KEY_GESTURE_TYPE_MAGNIFIER_ZOOM_IN: + return "KEY_GESTURE_TYPE_MAGNIFIER_ZOOM_IN"; + case KEY_GESTURE_TYPE_MAGNIFIER_ZOOM_OUT: + return "KEY_GESTURE_TYPE_MAGNIFIER_ZOOM_OUT"; default: return Integer.toHexString(value); } diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java index 617cca9d3075..d6fc6e461edc 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java @@ -27,6 +27,8 @@ import android.annotation.NonNull; import android.content.Context; import android.graphics.Region; import android.hardware.input.InputManager; +import android.hardware.input.KeyGestureEvent; +import android.os.IBinder; import android.os.Looper; import android.os.PowerManager; import android.os.SystemClock; @@ -44,11 +46,14 @@ import android.view.MotionEvent.PointerCoords; import android.view.MotionEvent.PointerProperties; import android.view.accessibility.AccessibilityEvent; +import androidx.annotation.Nullable; + import com.android.server.LocalServices; import com.android.server.accessibility.gestures.TouchExplorer; import com.android.server.accessibility.magnification.FullScreenMagnificationController; import com.android.server.accessibility.magnification.FullScreenMagnificationGestureHandler; import com.android.server.accessibility.magnification.FullScreenMagnificationVibrationHelper; +import com.android.server.accessibility.magnification.MagnificationController; import com.android.server.accessibility.magnification.MagnificationGestureHandler; import com.android.server.accessibility.magnification.MouseEventHandler; import com.android.server.accessibility.magnification.WindowMagnificationGestureHandler; @@ -187,6 +192,8 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo private final AccessibilityManagerService mAms; + private final InputManager mInputManager; + private final SparseArray mEventHandler; private final SparseArray mTouchExplorer = new SparseArray<>(0); @@ -228,6 +235,47 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo */ private MotionEvent mLastActiveDeviceMotionEvent = null; + private boolean mKeyGestureEventHandlerInstalled = false; + private InputManager.KeyGestureEventHandler mKeyGestureEventHandler = + new InputManager.KeyGestureEventHandler() { + @Override + public boolean handleKeyGestureEvent( + @NonNull KeyGestureEvent event, + @Nullable IBinder focusedToken) { + final boolean complete = + event.getAction() == KeyGestureEvent.ACTION_GESTURE_COMPLETE + && !event.isCancelled(); + final int gestureType = event.getKeyGestureType(); + final int displayId = isDisplayIdValid(event.getDisplayId()) + ? event.getDisplayId() : Display.DEFAULT_DISPLAY; + + switch (gestureType) { + case KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_ZOOM_IN: + if (complete) { + mAms.getMagnificationController().scaleMagnificationByStep( + displayId, MagnificationController.ZOOM_DIRECTION_IN); + } + return true; + case KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_ZOOM_OUT: + if (complete) { + mAms.getMagnificationController().scaleMagnificationByStep( + displayId, MagnificationController.ZOOM_DIRECTION_OUT); + } + return true; + } + return false; + } + + @Override + public boolean isKeyGestureSupported(int gestureType) { + return switch (gestureType) { + case KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_ZOOM_IN, + KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_ZOOM_OUT -> true; + default -> false; + }; + } + }; + private static MotionEvent cancelMotion(MotionEvent event) { if (event.getActionMasked() == MotionEvent.ACTION_CANCEL || event.getActionMasked() == MotionEvent.ACTION_HOVER_EXIT @@ -287,6 +335,7 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo mContext = context; mAms = service; mPm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); + mInputManager = context.getSystemService(InputManager.class); mEventHandler = eventHandler; } @@ -723,6 +772,12 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo createMagnificationGestureHandler(displayId, displayContext); addFirstEventHandler(displayId, magnificationGestureHandler); mMagnificationGestureHandler.put(displayId, magnificationGestureHandler); + + if (com.android.hardware.input.Flags.enableTalkbackAndMagnifierKeyGestures() + && !mKeyGestureEventHandlerInstalled) { + mInputManager.registerKeyGestureEventHandler(mKeyGestureEventHandler); + mKeyGestureEventHandlerInstalled = true; + } } if ((mEnabledFeatures & FLAG_FEATURE_INJECT_MOTION_EVENTS) != 0) { @@ -842,6 +897,11 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo mMouseKeysInterceptor.onDestroy(); mMouseKeysInterceptor = null; } + + if (mKeyGestureEventHandlerInstalled) { + mInputManager.unregisterKeyGestureEventHandler(mKeyGestureEventHandler); + mKeyGestureEventHandlerInstalled = false; + } } private MagnificationGestureHandler createMagnificationGestureHandler( diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java index d40e7476f7ec..51c4305061f8 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java @@ -27,6 +27,7 @@ import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ import static com.android.server.accessibility.AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID; import android.accessibilityservice.MagnificationConfig; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; @@ -101,6 +102,7 @@ public class MagnificationController implements MagnificationConnectionManager.C private int mMagnificationCapabilities = ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN; /** Whether the platform supports window magnification feature. */ private final boolean mSupportWindowMagnification; + private final MagnificationScaleStepProvider mScaleStepProvider; private final Executor mBackgroundExecutor; @@ -131,6 +133,14 @@ public class MagnificationController implements MagnificationConnectionManager.C .UiChangesForAccessibilityCallbacks> mAccessibilityCallbacksDelegateArray = new SparseArray<>(); + // Direction magnifier scale can be altered. + public static final int ZOOM_DIRECTION_IN = 0; + public static final int ZOOM_DIRECTION_OUT = 1; + + @IntDef({ZOOM_DIRECTION_IN, ZOOM_DIRECTION_OUT}) + public @interface ZoomDirection { + } + /** * A callback to inform the magnification transition result on the given display. */ @@ -144,6 +154,41 @@ public class MagnificationController implements MagnificationConnectionManager.C void onResult(int displayId, boolean success); } + + /** + * An interface to configure how much the magnification scale should be affected when moving in + * steps. + */ + public interface MagnificationScaleStepProvider { + /** + * Calculate the next value given which direction (in/out) to adjust the magnification + * scale. + * + * @param currentScale The current magnification scale value. + * @param direction Whether to zoom in or out. + * @return The next scale value. + */ + float nextScaleStep(float currentScale, @ZoomDirection int direction); + } + + public static class DefaultMagnificationScaleStepProvider implements + MagnificationScaleStepProvider { + // Factor of magnification scale. For example, when this value is 1.189, scale + // value will be changed x1.000, x1.189, x1.414, x1.681, x2.000, ... + // Note: this value is 2.0 ^ (1 / 4). + public static final float ZOOM_STEP_SCALE_FACTOR = 1.18920712f; + + @Override + public float nextScaleStep(float currentScale, @ZoomDirection int direction) { + final int stepDelta = direction == ZOOM_DIRECTION_IN ? 1 : -1; + final long scaleIndex = Math.round( + Math.log(currentScale) / Math.log(ZOOM_STEP_SCALE_FACTOR)); + final float nextScale = (float) Math.pow(ZOOM_STEP_SCALE_FACTOR, + scaleIndex + stepDelta); + return MagnificationScaleProvider.constrainScale(nextScale); + } + } + public MagnificationController(AccessibilityManagerService ams, Object lock, Context context, MagnificationScaleProvider scaleProvider, Executor backgroundExecutor) { @@ -156,6 +201,7 @@ public class MagnificationController implements MagnificationConnectionManager.C .getAccessibilityController().setUiChangesForAccessibilityCallbacks(this); mSupportWindowMagnification = context.getPackageManager().hasSystemFeature( FEATURE_WINDOW_MAGNIFICATION); + mScaleStepProvider = new DefaultMagnificationScaleStepProvider(); mAlwaysOnMagnificationFeatureFlag = new AlwaysOnMagnificationFeatureFlag(context); mAlwaysOnMagnificationFeatureFlag.addOnChangedListener( @@ -891,6 +937,37 @@ public class MagnificationController implements MagnificationConnectionManager.C return isActivated; } + /** + * Scales the magnifier on the given display one step in/out based on the zoomIn param. + * + * @param displayId The logical display id. + * @param direction Whether the scale should be zoomed in or out. + * @return {@code true} if the magnification scale was affected. + */ + public boolean scaleMagnificationByStep(int displayId, @ZoomDirection int direction) { + if (getFullScreenMagnificationController().isActivated(displayId)) { + final float magnificationScale = getFullScreenMagnificationController().getScale( + displayId); + final float nextMagnificationScale = mScaleStepProvider.nextScaleStep( + magnificationScale, direction); + getFullScreenMagnificationController().setScaleAndCenter(displayId, + nextMagnificationScale, + Float.NaN, Float.NaN, true, MAGNIFICATION_GESTURE_HANDLER_ID); + return nextMagnificationScale != magnificationScale; + } + + if (getMagnificationConnectionManager().isWindowMagnifierEnabled(displayId)) { + final float magnificationScale = getMagnificationConnectionManager().getScale( + displayId); + final float nextMagnificationScale = mScaleStepProvider.nextScaleStep( + magnificationScale, direction); + getMagnificationConnectionManager().setScale(displayId, nextMagnificationScale); + return nextMagnificationScale != magnificationScale; + } + + return false; + } + private final class DisableMagnificationCallback implements MagnificationAnimationCallback { private final TransitionCallBack mTransitionCallBack; diff --git a/services/core/java/com/android/server/input/InputGestureManager.java b/services/core/java/com/android/server/input/InputGestureManager.java index 8cb51ce35a89..b1aa694f3aa3 100644 --- a/services/core/java/com/android/server/input/InputGestureManager.java +++ b/services/core/java/com/android/server/input/InputGestureManager.java @@ -215,6 +215,12 @@ final class InputGestureManager { systemShortcuts.add(createKeyGesture(KeyEvent.KEYCODE_T, KeyEvent.META_META_ON | KeyEvent.META_ALT_ON, KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_TALKBACK)); + systemShortcuts.add(createKeyGesture(KeyEvent.KEYCODE_MINUS, + KeyEvent.META_META_ON | KeyEvent.META_ALT_ON, + KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_ZOOM_OUT)); + systemShortcuts.add(createKeyGesture(KeyEvent.KEYCODE_EQUALS, + KeyEvent.META_META_ON | KeyEvent.META_ALT_ON, + KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_ZOOM_IN)); } if (keyboardA11yShortcutControl()) { if (InputSettings.isAccessibilityBounceKeysFeatureEnabled()) { diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml index c645c0852f1b..9b7bbe04132c 100644 --- a/services/tests/servicestests/AndroidManifest.xml +++ b/services/tests/servicestests/AndroidManifest.xml @@ -114,6 +114,7 @@ + diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java index 8164ef9314d9..f0d3456a39de 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java @@ -19,9 +19,13 @@ package com.android.server.accessibility.magnification; import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN; import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW; +import static com.android.internal.accessibility.common.MagnificationConstants.SCALE_MAX_VALUE; +import static com.android.internal.accessibility.common.MagnificationConstants.SCALE_MIN_VALUE; import static com.android.server.accessibility.AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID; import static com.android.server.wm.WindowManagerInternal.AccessibilityControllerInternal.UiChangesForAccessibilityCallbacks; +import static com.google.common.truth.Truth.assertThat; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -659,6 +663,90 @@ public class MagnificationControllerTest { verify(mMagnificationConnectionManager, never()).persistScale(eq(TEST_DISPLAY)); } + @Test + public void scaleMagnificationByStep_fullscreenMode_stepInAndOut() throws RemoteException { + setMagnificationEnabled(MODE_FULLSCREEN); + mMagnificationController.onPerformScaleAction(TEST_DISPLAY, 1.0f, false); + reset(mScreenMagnificationController); + + // Verify the zoom scale factor increases by + // {@code MagnificationController.DefaultMagnificationScaleStepProvider + // .ZOOM_STEP_SCALE_FACTOR} and the center coordinates are + // unchanged (Float.NaN as values denotes unchanged center). + mMagnificationController.scaleMagnificationByStep(TEST_DISPLAY, + MagnificationController.ZOOM_DIRECTION_IN); + verify(mScreenMagnificationController).setScaleAndCenter(eq(TEST_DISPLAY), + eq(MagnificationController + .DefaultMagnificationScaleStepProvider.ZOOM_STEP_SCALE_FACTOR), + eq(Float.NaN), eq(Float.NaN), anyBoolean(), anyInt()); + + mMagnificationController.scaleMagnificationByStep(TEST_DISPLAY, + MagnificationController.ZOOM_DIRECTION_OUT); + verify(mScreenMagnificationController).setScaleAndCenter(eq(TEST_DISPLAY), + eq(SCALE_MIN_VALUE), eq(Float.NaN), eq(Float.NaN), anyBoolean(), anyInt()); + } + + @Test + public void scaleMagnificationByStep_testMaxScaling() throws RemoteException { + setMagnificationEnabled(MODE_FULLSCREEN); + mMagnificationController.onPerformScaleAction(TEST_DISPLAY, SCALE_MIN_VALUE, false); + reset(mScreenMagnificationController); + + float currentScale = mScreenMagnificationController.getScale(TEST_DISPLAY); + while (currentScale < SCALE_MAX_VALUE) { + assertThat(mMagnificationController.scaleMagnificationByStep(TEST_DISPLAY, + MagnificationController.ZOOM_DIRECTION_IN)).isTrue(); + final float nextScale = mScreenMagnificationController.getScale(TEST_DISPLAY); + assertThat(nextScale).isGreaterThan(currentScale); + currentScale = nextScale; + } + + assertThat(currentScale).isEqualTo(SCALE_MAX_VALUE); + assertThat(mMagnificationController.scaleMagnificationByStep(TEST_DISPLAY, + MagnificationController.ZOOM_DIRECTION_IN)).isFalse(); + } + + @Test + public void scaleMagnificationByStep_testMinScaling() throws RemoteException { + setMagnificationEnabled(MODE_FULLSCREEN); + mMagnificationController.onPerformScaleAction(TEST_DISPLAY, SCALE_MAX_VALUE, false); + reset(mScreenMagnificationController); + + float currentScale = mScreenMagnificationController.getScale(TEST_DISPLAY); + while (currentScale > SCALE_MIN_VALUE) { + assertThat(mMagnificationController.scaleMagnificationByStep(TEST_DISPLAY, + MagnificationController.ZOOM_DIRECTION_OUT)).isTrue(); + final float nextScale = mScreenMagnificationController.getScale(TEST_DISPLAY); + assertThat(nextScale).isLessThan(currentScale); + currentScale = nextScale; + } + + assertThat(currentScale).isEqualTo(SCALE_MIN_VALUE); + assertThat(mMagnificationController.scaleMagnificationByStep(TEST_DISPLAY, + MagnificationController.ZOOM_DIRECTION_OUT)).isFalse(); + } + + @Test + public void scaleMagnificationByStep_windowedMode_stepInAndOut() throws RemoteException { + setMagnificationEnabled(MODE_WINDOW); + mMagnificationController.onPerformScaleAction(TEST_DISPLAY, SCALE_MIN_VALUE, false); + reset(mMagnificationConnectionManager); + + // Verify the zoom scale factor increases by + // {@code MagnificationController.DefaultMagnificationScaleStepProvider + // .ZOOM_STEP_SCALE_FACTOR}. + mMagnificationController.scaleMagnificationByStep(TEST_DISPLAY, + MagnificationController.ZOOM_DIRECTION_IN); + verify(mMagnificationConnectionManager).setScale(eq(TEST_DISPLAY), + eq(MagnificationController + .DefaultMagnificationScaleStepProvider.ZOOM_STEP_SCALE_FACTOR)); + + mMagnificationController.scaleMagnificationByStep(TEST_DISPLAY, + MagnificationController.ZOOM_DIRECTION_OUT); + verify(mMagnificationConnectionManager).setScale(eq(TEST_DISPLAY), + eq(SCALE_MIN_VALUE)); + } + @Test public void enableWindowMode_notifyMagnificationChanged() throws RemoteException { setMagnificationEnabled(MODE_WINDOW); diff --git a/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt index 1574d1b7ce6f..265767088df9 100644 --- a/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt +++ b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt @@ -757,7 +757,31 @@ class KeyGestureControllerTests { intArrayOf(KeyEvent.KEYCODE_MINUS), KeyEvent.META_ALT_ON, intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE) - ) + ), + TestData( + "META + ALT + '-' -> Magnifier Zoom Out", + intArrayOf( + KeyEvent.KEYCODE_META_LEFT, + KeyEvent.KEYCODE_ALT_LEFT, + KeyEvent.KEYCODE_MINUS + ), + KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_ZOOM_OUT, + intArrayOf(KeyEvent.KEYCODE_MINUS), + KeyEvent.META_META_ON or KeyEvent.META_ALT_ON, + intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE) + ), + TestData( + "META + ALT + '=' -> Magnifier Zoom In", + intArrayOf( + KeyEvent.KEYCODE_META_LEFT, + KeyEvent.KEYCODE_ALT_LEFT, + KeyEvent.KEYCODE_EQUALS + ), + KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_ZOOM_IN, + intArrayOf(KeyEvent.KEYCODE_EQUALS), + KeyEvent.META_META_ON or KeyEvent.META_ALT_ON, + intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE) + ), ) } @@ -770,6 +794,7 @@ class KeyGestureControllerTests { com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_SLOW_KEYS_FLAG, com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_STICKY_KEYS_FLAG, com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_MOUSE_KEYS, + com.android.hardware.input.Flags.FLAG_ENABLE_TALKBACK_AND_MAGNIFIER_KEY_GESTURES, com.android.window.flags.Flags.FLAG_ENABLE_MOVE_TO_NEXT_DISPLAY_SHORTCUT, com.android.window.flags.Flags.FLAG_ENABLE_TASK_RESIZING_KEYBOARD_SHORTCUTS ) @@ -787,6 +812,7 @@ class KeyGestureControllerTests { com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_SLOW_KEYS_FLAG, com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_STICKY_KEYS_FLAG, com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_MOUSE_KEYS, + com.android.hardware.input.Flags.FLAG_ENABLE_TALKBACK_AND_MAGNIFIER_KEY_GESTURES, com.android.window.flags.Flags.FLAG_ENABLE_MOVE_TO_NEXT_DISPLAY_SHORTCUT, com.android.window.flags.Flags.FLAG_ENABLE_TASK_RESIZING_KEYBOARD_SHORTCUTS ) -- cgit v1.2.3-59-g8ed1b From f39fa244528b2f7265e21e51199236ef3e3fc705 Mon Sep 17 00:00:00 2001 From: David Padlipsky Date: Wed, 23 Oct 2024 00:45:35 +0000 Subject: Implement magnification toggle keyboard shortcut Adds new "KEY_GESTURE" shortcut type to the AccessibilityManagerService which tracks Accessibility Services which are able to be activated directly from a KeyGestureEvent key combo. Bug: 375277034 Test: atest AccessibilityManagerServiceTest Test: Manually on device Flag: com.android.hardware.input.enable_talkback_and_magnifier_key_gestures Change-Id: I4d65eb7834d1b48628fca46802781054901c0287 --- .../android/hardware/input/KeyGestureEvent.java | 4 + .../accessibility/common/ShortcutConstants.java | 10 +- .../internal/accessibility/util/ShortcutUtils.java | 3 + .../accessibility/util/ShortcutUtilsTest.java | 9 ++ .../accessibility/AccessibilityManagerService.java | 104 ++++++++++++++++++++- .../accessibility/AccessibilityUserState.java | 2 + .../android/server/input/InputGestureManager.java | 3 + .../AccessibilityManagerServiceTest.java | 28 ++++++ .../accessibility/AccessibilityUserStateTest.java | 3 + .../server/input/KeyGestureControllerTests.kt | 12 +++ 10 files changed, 172 insertions(+), 6 deletions(-) (limited to 'services/accessibility') diff --git a/core/java/android/hardware/input/KeyGestureEvent.java b/core/java/android/hardware/input/KeyGestureEvent.java index 506a19cce159..907f0f2c8fbb 100644 --- a/core/java/android/hardware/input/KeyGestureEvent.java +++ b/core/java/android/hardware/input/KeyGestureEvent.java @@ -119,6 +119,7 @@ public final class KeyGestureEvent { public static final int KEY_GESTURE_TYPE_RESTORE_FREEFORM_WINDOW_SIZE = 71; public static final int KEY_GESTURE_TYPE_MAGNIFIER_ZOOM_IN = 72; public static final int KEY_GESTURE_TYPE_MAGNIFIER_ZOOM_OUT = 73; + public static final int KEY_GESTURE_TYPE_TOGGLE_MAGNIFICATION = 74; public static final int FLAG_CANCELLED = 1; @@ -207,6 +208,7 @@ public final class KeyGestureEvent { KEY_GESTURE_TYPE_RESTORE_FREEFORM_WINDOW_SIZE, KEY_GESTURE_TYPE_MAGNIFIER_ZOOM_IN, KEY_GESTURE_TYPE_MAGNIFIER_ZOOM_OUT, + KEY_GESTURE_TYPE_TOGGLE_MAGNIFICATION, }) @Retention(RetentionPolicy.SOURCE) public @interface KeyGestureType { @@ -781,6 +783,8 @@ public final class KeyGestureEvent { return "KEY_GESTURE_TYPE_MAGNIFIER_ZOOM_IN"; case KEY_GESTURE_TYPE_MAGNIFIER_ZOOM_OUT: return "KEY_GESTURE_TYPE_MAGNIFIER_ZOOM_OUT"; + case KEY_GESTURE_TYPE_TOGGLE_MAGNIFICATION: + return "KEY_GESTURE_TYPE_TOGGLE_MAGNIFICATION"; default: return Integer.toHexString(value); } diff --git a/core/java/com/android/internal/accessibility/common/ShortcutConstants.java b/core/java/com/android/internal/accessibility/common/ShortcutConstants.java index 44dceb9b7edb..4a49bb6720ef 100644 --- a/core/java/com/android/internal/accessibility/common/ShortcutConstants.java +++ b/core/java/com/android/internal/accessibility/common/ShortcutConstants.java @@ -63,6 +63,8 @@ public final class ShortcutConstants { * quickly tapping screen 2 times with two fingers as preferred shortcut. * {@code QUICK_SETTINGS} for displaying specifying the accessibility services or features which * choose Quick Settings as preferred shortcut. + * {@code KEY_GESTURE} for shortcuts which are directly from key gestures and should be + * activated always. */ @Retention(RetentionPolicy.SOURCE) @IntDef({ @@ -73,6 +75,7 @@ public final class ShortcutConstants { UserShortcutType.TWOFINGER_DOUBLETAP, UserShortcutType.QUICK_SETTINGS, UserShortcutType.GESTURE, + UserShortcutType.KEY_GESTURE, UserShortcutType.ALL }) public @interface UserShortcutType { @@ -84,8 +87,10 @@ public final class ShortcutConstants { int TWOFINGER_DOUBLETAP = 1 << 3; int QUICK_SETTINGS = 1 << 4; int GESTURE = 1 << 5; + int KEY_GESTURE = 1 << 6; // LINT.ThenChange(:shortcut_type_array) - int ALL = SOFTWARE | HARDWARE | TRIPLETAP | TWOFINGER_DOUBLETAP | QUICK_SETTINGS | GESTURE; + int ALL = SOFTWARE | HARDWARE | TRIPLETAP | TWOFINGER_DOUBLETAP | QUICK_SETTINGS | GESTURE + | KEY_GESTURE; } /** @@ -99,7 +104,8 @@ public final class ShortcutConstants { UserShortcutType.TRIPLETAP, UserShortcutType.TWOFINGER_DOUBLETAP, UserShortcutType.QUICK_SETTINGS, - UserShortcutType.GESTURE + UserShortcutType.GESTURE, + UserShortcutType.KEY_GESTURE // LINT.ThenChange(:shortcut_type_intdef) }; diff --git a/core/java/com/android/internal/accessibility/util/ShortcutUtils.java b/core/java/com/android/internal/accessibility/util/ShortcutUtils.java index 2e0ff3db6c50..14ca0f8cae69 100644 --- a/core/java/com/android/internal/accessibility/util/ShortcutUtils.java +++ b/core/java/com/android/internal/accessibility/util/ShortcutUtils.java @@ -27,6 +27,7 @@ import static com.android.internal.accessibility.common.ShortcutConstants.SERVIC import static com.android.internal.accessibility.common.ShortcutConstants.USER_SHORTCUT_TYPES; import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.GESTURE; import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.HARDWARE; +import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.KEY_GESTURE; import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.QUICK_SETTINGS; import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.SOFTWARE; import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.TRIPLETAP; @@ -187,6 +188,7 @@ public final class ShortcutUtils { case TWOFINGER_DOUBLETAP -> Settings.Secure.ACCESSIBILITY_MAGNIFICATION_TWO_FINGER_TRIPLE_TAP_ENABLED; case QUICK_SETTINGS -> Settings.Secure.ACCESSIBILITY_QS_TARGETS; + case KEY_GESTURE -> Settings.Secure.ACCESSIBILITY_KEY_GESTURE_TARGETS; default -> throw new IllegalArgumentException( "Unsupported user shortcut type: " + type); }; @@ -209,6 +211,7 @@ public final class ShortcutUtils { TRIPLETAP; case Settings.Secure.ACCESSIBILITY_MAGNIFICATION_TWO_FINGER_TRIPLE_TAP_ENABLED -> TWOFINGER_DOUBLETAP; + case Settings.Secure.ACCESSIBILITY_KEY_GESTURE_TARGETS -> KEY_GESTURE; default -> throw new IllegalArgumentException( "Unsupported user shortcut key: " + key); }; diff --git a/core/tests/coretests/src/com/android/internal/accessibility/util/ShortcutUtilsTest.java b/core/tests/coretests/src/com/android/internal/accessibility/util/ShortcutUtilsTest.java index 8bebc62e93f2..1a9af6b55eed 100644 --- a/core/tests/coretests/src/com/android/internal/accessibility/util/ShortcutUtilsTest.java +++ b/core/tests/coretests/src/com/android/internal/accessibility/util/ShortcutUtilsTest.java @@ -21,6 +21,7 @@ import static android.provider.Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_MAG import static com.android.internal.accessibility.common.ShortcutConstants.SERVICES_SEPARATOR; import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.GESTURE; +import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.KEY_GESTURE; import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.SOFTWARE; import static com.google.common.truth.Truth.assertThat; @@ -122,6 +123,14 @@ public class ShortcutUtilsTest { ).isEmpty(); } + @Test + public void getShortcutTargets_keyGestureShortcutNoService_emptyResult() { + assertThat( + ShortcutUtils.getShortcutTargetsFromSettings( + mContext, KEY_GESTURE, mDefaultUserId) + ).isEmpty(); + } + @Test public void getShortcutTargets_softwareShortcut1Service_return1Service() { setupShortcutTargets(ONE_COMPONENT, Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS); diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index 86d3ee6d1257..22837acde1c5 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -42,9 +42,11 @@ import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATIN import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_GESTURE; import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR; import static android.provider.Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED; +import static android.view.Display.INVALID_DISPLAY; import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL; import static android.view.accessibility.AccessibilityManager.FlashNotificationReason; +import static com.android.hardware.input.Flags.enableTalkbackAndMagnifierKeyGestures; import static com.android.hardware.input.Flags.keyboardA11yMouseKeys; import static com.android.internal.accessibility.AccessibilityShortcutController.ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME; import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_COMPONENT_NAME; @@ -55,6 +57,7 @@ import static com.android.internal.accessibility.common.ShortcutConstants.UserSh import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.ALL; import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.GESTURE; import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.HARDWARE; +import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.KEY_GESTURE; import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.QUICK_SETTINGS; import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.SOFTWARE; import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.TRIPLETAP; @@ -111,6 +114,8 @@ import android.graphics.Rect; import android.graphics.Region; import android.hardware.display.DisplayManager; import android.hardware.fingerprint.IFingerprintService; +import android.hardware.input.InputManager; +import android.hardware.input.KeyGestureEvent; import android.media.AudioManagerInternal; import android.net.Uri; import android.os.Binder; @@ -338,6 +343,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub private AlertDialog mEnableTouchExplorationDialog; + private final InputManager mInputManager; + private AccessibilityInputFilter mInputFilter; private boolean mHasInputFilter; @@ -503,6 +510,24 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } } + private InputManager.KeyGestureEventHandler mKeyGestureEventHandler = + new InputManager.KeyGestureEventHandler() { + @Override + public boolean handleKeyGestureEvent( + @NonNull KeyGestureEvent event, + @Nullable IBinder focusedToken) { + return AccessibilityManagerService.this.handleKeyGestureEvent(event); + } + + @Override + public boolean isKeyGestureSupported(int gestureType) { + return switch (gestureType) { + case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MAGNIFICATION -> true; + default -> false; + }; + } + }; + @VisibleForTesting AccessibilityManagerService( Context context, @@ -542,6 +567,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub mUmi = LocalServices.getService(UserManagerInternal.class); // TODO(b/255426725): not used on tests mVisibleBgUserIds = null; + mInputManager = context.getSystemService(InputManager.class); init(); } @@ -583,6 +609,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub mUiAutomationManager, this); mFlashNotificationsController = new FlashNotificationsController(mContext); mUmi = LocalServices.getService(UserManagerInternal.class); + mInputManager = context.getSystemService(InputManager.class); if (UserManager.isVisibleBackgroundUsersEnabled()) { mVisibleBgUserIds = new SparseBooleanArray(); @@ -599,6 +626,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub registerBroadcastReceivers(); new AccessibilityContentObserver(mMainHandler).register( mContext.getContentResolver()); + if (enableTalkbackAndMagnifierKeyGestures()) { + mInputManager.registerKeyGestureEventHandler(mKeyGestureEventHandler); + } disableAccessibilityMenuToMigrateIfNeeded(); } @@ -640,6 +670,51 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub return mIsAccessibilityButtonShown; } + @VisibleForTesting + boolean handleKeyGestureEvent(KeyGestureEvent event) { + final boolean complete = + event.getAction() == KeyGestureEvent.ACTION_GESTURE_COMPLETE + && !event.isCancelled(); + final int gestureType = event.getKeyGestureType(); + if (!complete) { + return false; + } + + String targetName; + switch (gestureType) { + case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MAGNIFICATION: + targetName = MAGNIFICATION_CONTROLLER_NAME; + break; + default: + return false; + } + + List shortcutTargets = getAccessibilityShortcutTargets( + KEY_GESTURE); + if (!shortcutTargets.contains(targetName)) { + int userId; + synchronized (mLock) { + userId = mCurrentUserId; + } + // TODO(b/377752960): Add dialog to confirm enabling the service and to + // activate the first time. + enableShortcutForTargets(true, UserShortcutType.KEY_GESTURE, + List.of(targetName), userId); + + // Do not perform action on first press since it was just registered. Eventually, + // this will be a separate dialog that appears that requires the user to confirm + // which will resolve this race condition. For now, just require two presses the + // first time it is activated. + return true; + } + + final int displayId = event.getDisplayId() != INVALID_DISPLAY + ? event.getDisplayId() : getLastNonProxyTopFocusedDisplayId(); + performAccessibilityShortcutInternal(displayId, KEY_GESTURE, targetName); + + return true; + } + @Override public Pair getWindowTransformationMatrixAndMagnificationSpec( int windowId) { @@ -1224,14 +1299,14 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub int displayId = event.getDisplayId(); final int windowId = event.getWindowId(); if (windowId != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID - && displayId == Display.INVALID_DISPLAY) { + && displayId == INVALID_DISPLAY) { displayId = mA11yWindowManager.getDisplayIdByUserIdAndWindowId( resolvedUserId, windowId); event.setDisplayId(displayId); } synchronized (mLock) { if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED - && displayId != Display.INVALID_DISPLAY + && displayId != INVALID_DISPLAY && mA11yWindowManager.isTrackingWindowsLocked(displayId)) { shouldComputeWindows = true; } @@ -3257,6 +3332,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub updateAccessibilityShortcutTargetsLocked(userState, SOFTWARE); updateAccessibilityShortcutTargetsLocked(userState, GESTURE); updateAccessibilityShortcutTargetsLocked(userState, QUICK_SETTINGS); + updateAccessibilityShortcutTargetsLocked(userState, KEY_GESTURE); // Update the capabilities before the mode because we will check the current mode is // invalid or not.. updateMagnificationCapabilitiesSettingsChangeLocked(userState); @@ -3387,6 +3463,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub somethingChanged |= readAccessibilityShortcutTargetsLocked(userState, QUICK_SETTINGS); somethingChanged |= readAccessibilityShortcutTargetsLocked(userState, SOFTWARE); somethingChanged |= readAccessibilityShortcutTargetsLocked(userState, GESTURE); + somethingChanged |= readAccessibilityShortcutTargetsLocked(userState, KEY_GESTURE); somethingChanged |= readAccessibilityButtonTargetComponentLocked(userState); somethingChanged |= readUserRecommendedUiTimeoutSettingsLocked(userState); somethingChanged |= readMagnificationModeForDefaultDisplayLocked(userState); @@ -3968,6 +4045,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub if (android.provider.Flags.a11yStandaloneGestureEnabled()) { shortcutTypes.add(GESTURE); } + shortcutTypes.add(KEY_GESTURE); final ComponentName serviceName = service.getComponentName(); for (Integer shortcutType: shortcutTypes) { @@ -4078,13 +4156,15 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub */ private void performAccessibilityShortcutInternal(int displayId, @UserShortcutType int shortcutType, @Nullable String targetName) { - final List shortcutTargets = getAccessibilityShortcutTargetsInternal(shortcutType); + final List shortcutTargets = getAccessibilityShortcutTargetsInternal( + shortcutType); if (shortcutTargets.isEmpty()) { Slog.d(LOG_TAG, "No target to perform shortcut, shortcutType=" + shortcutType); return; } // In case the caller specified a target name - if (targetName != null && !doesShortcutTargetsStringContain(shortcutTargets, targetName)) { + if (targetName != null && !doesShortcutTargetsStringContain(shortcutTargets, + targetName)) { Slog.v(LOG_TAG, "Perform shortcut failed, invalid target name:" + targetName); targetName = null; } @@ -4306,6 +4386,13 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub return; } + if (shortcutType == UserShortcutType.KEY_GESTURE + && !enableTalkbackAndMagnifierKeyGestures()) { + Slog.w(LOG_TAG, + "KEY_GESTURE type shortcuts are disabled by feature flag"); + return; + } + final String shortcutTypeSettingKey = ShortcutUtils.convertToKey(shortcutType); if (shortcutType == UserShortcutType.TRIPLETAP || shortcutType == UserShortcutType.TWOFINGER_DOUBLETAP) { @@ -5683,6 +5770,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub private final Uri mAccessibilityGestureTargetsUri = Settings.Secure.getUriFor( Settings.Secure.ACCESSIBILITY_GESTURE_TARGETS); + private final Uri mAccessibilityKeyGestureTargetsUri = Settings.Secure.getUriFor( + Settings.Secure.ACCESSIBILITY_KEY_GESTURE_TARGETS); + private final Uri mUserNonInteractiveUiTimeoutUri = Settings.Secure.getUriFor( Settings.Secure.ACCESSIBILITY_NON_INTERACTIVE_UI_TIMEOUT_MS); @@ -5746,6 +5836,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub mAccessibilityButtonTargetsUri, false, this, UserHandle.USER_ALL); contentResolver.registerContentObserver( mAccessibilityGestureTargetsUri, false, this, UserHandle.USER_ALL); + contentResolver.registerContentObserver( + mAccessibilityKeyGestureTargetsUri, false, this, UserHandle.USER_ALL); contentResolver.registerContentObserver( mUserNonInteractiveUiTimeoutUri, false, this, UserHandle.USER_ALL); contentResolver.registerContentObserver( @@ -5828,6 +5920,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub if (readAccessibilityShortcutTargetsLocked(userState, GESTURE)) { onUserStateChangedLocked(userState); } + } else if (mAccessibilityKeyGestureTargetsUri.equals(uri)) { + if (readAccessibilityShortcutTargetsLocked(userState, KEY_GESTURE)) { + onUserStateChangedLocked(userState); + } } else if (mUserNonInteractiveUiTimeoutUri.equals(uri) || mUserInteractiveUiTimeoutUri.equals(uri)) { readUserRecommendedUiTimeoutSettingsLocked(userState); diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java index 67b40632dde8..8b3e63d0dc5e 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java @@ -29,6 +29,7 @@ import static com.android.internal.accessibility.AccessibilityShortcutController import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType; import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.GESTURE; import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.HARDWARE; +import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.KEY_GESTURE; import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.QUICK_SETTINGS; import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.SOFTWARE; import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.TRIPLETAP; @@ -209,6 +210,7 @@ class AccessibilityUserState { mShortcutTargets.put(SOFTWARE, new ArraySet<>()); mShortcutTargets.put(GESTURE, new ArraySet<>()); mShortcutTargets.put(QUICK_SETTINGS, new ArraySet<>()); + mShortcutTargets.put(KEY_GESTURE, new ArraySet<>()); } boolean isHandlingAccessibilityEventsLocked() { diff --git a/services/core/java/com/android/server/input/InputGestureManager.java b/services/core/java/com/android/server/input/InputGestureManager.java index 2e7f5c083ac3..bc5c2db3881f 100644 --- a/services/core/java/com/android/server/input/InputGestureManager.java +++ b/services/core/java/com/android/server/input/InputGestureManager.java @@ -221,6 +221,9 @@ final class InputGestureManager { systemShortcuts.add(createKeyGesture(KeyEvent.KEYCODE_EQUALS, KeyEvent.META_META_ON | KeyEvent.META_ALT_ON, KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_ZOOM_IN)); + systemShortcuts.add(createKeyGesture(KeyEvent.KEYCODE_M, + KeyEvent.META_META_ON | KeyEvent.META_ALT_ON, + KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MAGNIFICATION)); } if (keyboardA11yShortcutControl()) { if (InputSettings.isAccessibilityBounceKeysFeatureEnabled()) { diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java index 2edde9b74d0a..945e07913351 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java @@ -33,6 +33,7 @@ import static com.android.internal.accessibility.AccessibilityShortcutController import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME; import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.GESTURE; import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.HARDWARE; +import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.KEY_GESTURE; import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.QUICK_SETTINGS; import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.SOFTWARE; import static com.android.internal.accessibility.dialog.AccessibilityButtonChooserActivity.EXTRA_TYPE_TO_CHOOSE; @@ -80,6 +81,7 @@ import android.content.pm.ServiceInfo; import android.content.res.XmlResourceParser; import android.graphics.drawable.Icon; import android.hardware.display.DisplayManagerGlobal; +import android.hardware.input.KeyGestureEvent; import android.net.Uri; import android.os.Build; import android.os.Bundle; @@ -2183,6 +2185,28 @@ public class AccessibilityManagerServiceTest { verify(mockUserContext).getSystemService(EnhancedConfirmationManager.class); } + @Test + @EnableFlags(com.android.hardware.input.Flags.FLAG_ENABLE_TALKBACK_AND_MAGNIFIER_KEY_GESTURES) + public void handleKeyGestureEvent_toggleMagnifier() { + mFakePermissionEnforcer.grant(Manifest.permission.MANAGE_ACCESSIBILITY); + assertThat(ShortcutUtils.getShortcutTargetsFromSettings(mTestableContext, KEY_GESTURE, + mA11yms.getCurrentUserIdLocked())).isEmpty(); + + mA11yms.handleKeyGestureEvent(new KeyGestureEvent.Builder().setKeyGestureType( + KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MAGNIFICATION).setAction( + KeyGestureEvent.ACTION_GESTURE_COMPLETE).build()); + + assertThat(ShortcutUtils.getShortcutTargetsFromSettings(mTestableContext, KEY_GESTURE, + mA11yms.getCurrentUserIdLocked())).containsExactly(MAGNIFICATION_CONTROLLER_NAME); + + // The magnifier will only be toggled on the second event received since the first is + // used to toggle the feature on. + mA11yms.handleKeyGestureEvent(new KeyGestureEvent.Builder().setKeyGestureType( + KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MAGNIFICATION).setAction( + KeyGestureEvent.ACTION_GESTURE_COMPLETE).build()); + + verify(mInputFilter).notifyMagnificationShortcutTriggered(anyInt()); + } private Set readStringsFromSetting(String setting) { final Set result = new ArraySet<>(); @@ -2298,6 +2322,10 @@ public class AccessibilityManagerServiceTest { AccessibilityManagerService service) { super(context, service); } + + @Override + void notifyMagnificationShortcutTriggered(int displayId) { + } } private static class A11yTestableContext extends TestableContext { diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java index 8c35925debff..cb52eef6adfe 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java @@ -32,6 +32,7 @@ import static com.android.internal.accessibility.AccessibilityShortcutController import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.ALL; import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.GESTURE; import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.HARDWARE; +import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.KEY_GESTURE; import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.QUICK_SETTINGS; import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.SOFTWARE; import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.TRIPLETAP; @@ -174,6 +175,7 @@ public class AccessibilityUserStateTest { mUserState.updateShortcutTargetsLocked(Set.of(componentNameString), SOFTWARE); mUserState.updateShortcutTargetsLocked(Set.of(componentNameString), GESTURE); mUserState.updateShortcutTargetsLocked(Set.of(componentNameString), QUICK_SETTINGS); + mUserState.updateShortcutTargetsLocked(Set.of(componentNameString), KEY_GESTURE); mUserState.updateA11yTilesInQsPanelLocked( Set.of(AccessibilityShortcutController.COLOR_INVERSION_TILE_COMPONENT_NAME)); mUserState.setTargetAssignedToAccessibilityButton(componentNameString); @@ -201,6 +203,7 @@ public class AccessibilityUserStateTest { assertTrue(mUserState.getShortcutTargetsLocked(SOFTWARE).isEmpty()); assertTrue(mUserState.getShortcutTargetsLocked(GESTURE).isEmpty()); assertTrue(mUserState.getShortcutTargetsLocked(QUICK_SETTINGS).isEmpty()); + assertTrue(mUserState.getShortcutTargetsLocked(KEY_GESTURE).isEmpty()); assertTrue(mUserState.getA11yQsTilesInQsPanel().isEmpty()); assertNull(mUserState.getTargetAssignedToAccessibilityButton()); assertFalse(mUserState.isTouchExplorationEnabledLocked()); diff --git a/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt index 61400edba165..40ea9dc9cd89 100644 --- a/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt +++ b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt @@ -782,6 +782,18 @@ class KeyGestureControllerTests { KeyEvent.META_META_ON or KeyEvent.META_ALT_ON, intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE) ), + TestData( + "META + ALT + M -> Toggle Magnification", + intArrayOf( + KeyEvent.KEYCODE_META_LEFT, + KeyEvent.KEYCODE_ALT_LEFT, + KeyEvent.KEYCODE_M + ), + KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MAGNIFICATION, + intArrayOf(KeyEvent.KEYCODE_M), + KeyEvent.META_META_ON or KeyEvent.META_ALT_ON, + intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE) + ), ) } -- cgit v1.2.3-59-g8ed1b From 1a239822e1461d856fe2f4fd844f09ed65def40e Mon Sep 17 00:00:00 2001 From: David Padlipsky Date: Thu, 7 Nov 2024 05:05:22 +0000 Subject: Implement key gesture to activate Select to Speak Adds new shortcut (Meta + Alt + S) to first enable the select to speak service and then activate it once it is enabled. The select to speak service to use is controlled via the config_defaultSelectToSpeakService value and is empty by default. This select to speak service to use is checked to make sure it is pre-installed from the OEM. Bug: 375277034 Test: atest AccessibilityManagerServiceTest Test: Manually on device Flag: com.android.hardware.input.enable_talkback_and_magnifier_key_gestures Change-Id: Ic22e12fd66010890d6d495b6c52a52008dc0ca9e --- .../android/hardware/input/KeyGestureEvent.java | 4 + core/res/res/values/config.xml | 5 + core/res/res/values/symbols.xml | 1 + .../accessibility/AccessibilityManagerService.java | 31 ++++- .../android/server/input/InputGestureManager.java | 3 + .../AccessibilityManagerServiceTest.java | 140 +++++++++++++++++++++ .../server/input/KeyGestureControllerTests.kt | 12 ++ 7 files changed, 195 insertions(+), 1 deletion(-) (limited to 'services/accessibility') diff --git a/core/java/android/hardware/input/KeyGestureEvent.java b/core/java/android/hardware/input/KeyGestureEvent.java index 907f0f2c8fbb..24951c4d516e 100644 --- a/core/java/android/hardware/input/KeyGestureEvent.java +++ b/core/java/android/hardware/input/KeyGestureEvent.java @@ -120,6 +120,7 @@ public final class KeyGestureEvent { public static final int KEY_GESTURE_TYPE_MAGNIFIER_ZOOM_IN = 72; public static final int KEY_GESTURE_TYPE_MAGNIFIER_ZOOM_OUT = 73; public static final int KEY_GESTURE_TYPE_TOGGLE_MAGNIFICATION = 74; + public static final int KEY_GESTURE_TYPE_ACTIVATE_SELECT_TO_SPEAK = 75; public static final int FLAG_CANCELLED = 1; @@ -209,6 +210,7 @@ public final class KeyGestureEvent { KEY_GESTURE_TYPE_MAGNIFIER_ZOOM_IN, KEY_GESTURE_TYPE_MAGNIFIER_ZOOM_OUT, KEY_GESTURE_TYPE_TOGGLE_MAGNIFICATION, + KEY_GESTURE_TYPE_ACTIVATE_SELECT_TO_SPEAK, }) @Retention(RetentionPolicy.SOURCE) public @interface KeyGestureType { @@ -785,6 +787,8 @@ public final class KeyGestureEvent { return "KEY_GESTURE_TYPE_MAGNIFIER_ZOOM_OUT"; case KEY_GESTURE_TYPE_TOGGLE_MAGNIFICATION: return "KEY_GESTURE_TYPE_TOGGLE_MAGNIFICATION"; + case KEY_GESTURE_TYPE_ACTIVATE_SELECT_TO_SPEAK: + return "KEY_GESTURE_TYPE_ACTIVATE_SELECT_TO_SPEAK"; default: return Integer.toHexString(value); } diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 7402a2f24f97..219cefd273ac 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -4596,6 +4596,11 @@ exists on the device, the accessibility shortcut will be disabled by default. --> + + + diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index db81a3be440f..badb98686fb2 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -3718,6 +3718,7 @@ + diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index 22837acde1c5..d4af7b765254 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -522,7 +522,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub @Override public boolean isKeyGestureSupported(int gestureType) { return switch (gestureType) { - case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MAGNIFICATION -> true; + case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MAGNIFICATION, + KeyGestureEvent.KEY_GESTURE_TYPE_ACTIVATE_SELECT_TO_SPEAK -> true; default -> false; }; } @@ -685,6 +686,34 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MAGNIFICATION: targetName = MAGNIFICATION_CONTROLLER_NAME; break; + case KeyGestureEvent.KEY_GESTURE_TYPE_ACTIVATE_SELECT_TO_SPEAK: + targetName = mContext.getString(R.string.config_defaultSelectToSpeakService); + if (targetName.isEmpty()) { + return false; + } + + final ComponentName targetServiceComponent = TextUtils.isEmpty(targetName) + ? null : ComponentName.unflattenFromString(targetName); + AccessibilityServiceInfo accessibilityServiceInfo; + synchronized (mLock) { + AccessibilityUserState userState = getCurrentUserStateLocked(); + accessibilityServiceInfo = + userState.getInstalledServiceInfoLocked(targetServiceComponent); + } + if (accessibilityServiceInfo == null) { + return false; + } + + // Skip enabling if a warning dialog is required for the feature. + // TODO(b/377752960): Explore better options to instead show the warning dialog + // in this scenario. + if (isAccessibilityServiceWarningRequired(accessibilityServiceInfo)) { + Slog.w(LOG_TAG, + "Accessibility warning is required before this service can be " + + "activated automatically via KEY_GESTURE shortcut."); + return false; + } + break; default: return false; } diff --git a/services/core/java/com/android/server/input/InputGestureManager.java b/services/core/java/com/android/server/input/InputGestureManager.java index bc5c2db3881f..6f3540221b63 100644 --- a/services/core/java/com/android/server/input/InputGestureManager.java +++ b/services/core/java/com/android/server/input/InputGestureManager.java @@ -224,6 +224,9 @@ final class InputGestureManager { systemShortcuts.add(createKeyGesture(KeyEvent.KEYCODE_M, KeyEvent.META_META_ON | KeyEvent.META_ALT_ON, KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MAGNIFICATION)); + systemShortcuts.add(createKeyGesture(KeyEvent.KEYCODE_S, + KeyEvent.META_META_ON | KeyEvent.META_ALT_ON, + KeyGestureEvent.KEY_GESTURE_TYPE_ACTIVATE_SELECT_TO_SPEAK)); } if (keyboardA11yShortcutControl()) { if (InputSettings.isAccessibilityBounceKeysFeatureEnabled()) { diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java index 945e07913351..d5b930769e43 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java @@ -2208,6 +2208,146 @@ public class AccessibilityManagerServiceTest { verify(mInputFilter).notifyMagnificationShortcutTriggered(anyInt()); } + @Test + @EnableFlags(com.android.hardware.input.Flags.FLAG_ENABLE_TALKBACK_AND_MAGNIFIER_KEY_GESTURES) + public void handleKeyGestureEvent_activateSelectToSpeak_trustedService() { + setupAccessibilityServiceConnection(FLAG_REQUEST_ACCESSIBILITY_BUTTON); + mFakePermissionEnforcer.grant(Manifest.permission.MANAGE_ACCESSIBILITY); + + final AccessibilityServiceInfo trustedService = mockAccessibilityServiceInfo( + new ComponentName("package_a", "class_a"), + /* isSystemApp= */ true, /* isAlwaysOnService= */ true); + AccessibilityUserState userState = mA11yms.getCurrentUserState(); + userState.mInstalledServices.add(trustedService); + mTestableContext.getOrCreateTestableResources().addOverride( + R.string.config_defaultSelectToSpeakService, + trustedService.getComponentName().flattenToString()); + mTestableContext.getOrCreateTestableResources().addOverride( + R.array.config_trustedAccessibilityServices, + new String[]{trustedService.getComponentName().flattenToString()}); + + assertThat(ShortcutUtils.getShortcutTargetsFromSettings(mTestableContext, KEY_GESTURE, + mA11yms.getCurrentUserIdLocked())).isEmpty(); + + mA11yms.handleKeyGestureEvent(new KeyGestureEvent.Builder().setKeyGestureType( + KeyGestureEvent.KEY_GESTURE_TYPE_ACTIVATE_SELECT_TO_SPEAK).setAction( + KeyGestureEvent.ACTION_GESTURE_COMPLETE).build()); + + assertThat(ShortcutUtils.getShortcutTargetsFromSettings(mTestableContext, KEY_GESTURE, + mA11yms.getCurrentUserIdLocked())).containsExactly( + trustedService.getComponentName().flattenToString()); + } + + @Test + @EnableFlags(com.android.hardware.input.Flags.FLAG_ENABLE_TALKBACK_AND_MAGNIFIER_KEY_GESTURES) + public void handleKeyGestureEvent_activateSelectToSpeak_preinstalledService() { + setupAccessibilityServiceConnection(FLAG_REQUEST_ACCESSIBILITY_BUTTON); + mFakePermissionEnforcer.grant(Manifest.permission.MANAGE_ACCESSIBILITY); + + final AccessibilityServiceInfo untrustedService = mockAccessibilityServiceInfo( + new ComponentName("package_a", "class_a"), + /* isSystemApp= */ true, /* isAlwaysOnService= */ true); + AccessibilityUserState userState = mA11yms.getCurrentUserState(); + userState.mInstalledServices.add(untrustedService); + mTestableContext.getOrCreateTestableResources().addOverride( + R.string.config_defaultSelectToSpeakService, + untrustedService.getComponentName().flattenToString()); + + assertThat(ShortcutUtils.getShortcutTargetsFromSettings(mTestableContext, KEY_GESTURE, + mA11yms.getCurrentUserIdLocked())).isEmpty(); + + mA11yms.handleKeyGestureEvent(new KeyGestureEvent.Builder().setKeyGestureType( + KeyGestureEvent.KEY_GESTURE_TYPE_ACTIVATE_SELECT_TO_SPEAK).setAction( + KeyGestureEvent.ACTION_GESTURE_COMPLETE).build()); + + assertThat(ShortcutUtils.getShortcutTargetsFromSettings(mTestableContext, KEY_GESTURE, + mA11yms.getCurrentUserIdLocked())).isEmpty(); + } + + @Test + @EnableFlags(com.android.hardware.input.Flags.FLAG_ENABLE_TALKBACK_AND_MAGNIFIER_KEY_GESTURES) + public void handleKeyGestureEvent_activateSelectToSpeak_downloadedService() { + mFakePermissionEnforcer.grant(Manifest.permission.MANAGE_ACCESSIBILITY); + + final AccessibilityServiceInfo downloadedService = mockAccessibilityServiceInfo( + new ComponentName("package_a", "class_a"), + /* isSystemApp= */ false, /* isAlwaysOnService= */ true); + AccessibilityUserState userState = mA11yms.getCurrentUserState(); + userState.mInstalledServices.add(downloadedService); + mTestableContext.getOrCreateTestableResources().addOverride( + R.string.config_defaultSelectToSpeakService, + downloadedService.getComponentName().flattenToString()); + mTestableContext.getOrCreateTestableResources().addOverride( + R.array.config_trustedAccessibilityServices, + new String[]{downloadedService.getComponentName().flattenToString()}); + + assertThat(ShortcutUtils.getShortcutTargetsFromSettings(mTestableContext, KEY_GESTURE, + mA11yms.getCurrentUserIdLocked())).isEmpty(); + + mA11yms.handleKeyGestureEvent(new KeyGestureEvent.Builder().setKeyGestureType( + KeyGestureEvent.KEY_GESTURE_TYPE_ACTIVATE_SELECT_TO_SPEAK).setAction( + KeyGestureEvent.ACTION_GESTURE_COMPLETE).build()); + + assertThat(ShortcutUtils.getShortcutTargetsFromSettings(mTestableContext, KEY_GESTURE, + mA11yms.getCurrentUserIdLocked())).isEmpty(); + } + + @Test + @EnableFlags(com.android.hardware.input.Flags.FLAG_ENABLE_TALKBACK_AND_MAGNIFIER_KEY_GESTURES) + public void handleKeyGestureEvent_activateSelectToSpeak_defaultNotInstalled() { + mFakePermissionEnforcer.grant(Manifest.permission.MANAGE_ACCESSIBILITY); + + final AccessibilityServiceInfo installedService = mockAccessibilityServiceInfo( + new ComponentName("package_a", "class_a"), + /* isSystemApp= */ true, /* isAlwaysOnService= */ true); + final AccessibilityServiceInfo defaultService = mockAccessibilityServiceInfo( + new ComponentName("package_b", "class_b"), + /* isSystemApp= */ true, /* isAlwaysOnService= */ true); + AccessibilityUserState userState = mA11yms.getCurrentUserState(); + userState.mInstalledServices.add(installedService); + mTestableContext.getOrCreateTestableResources().addOverride( + R.string.config_defaultSelectToSpeakService, + defaultService.getComponentName().flattenToString()); + mTestableContext.getOrCreateTestableResources().addOverride( + R.array.config_trustedAccessibilityServices, + new String[]{defaultService.getComponentName().flattenToString()}); + + assertThat(ShortcutUtils.getShortcutTargetsFromSettings(mTestableContext, KEY_GESTURE, + mA11yms.getCurrentUserIdLocked())).isEmpty(); + + mA11yms.handleKeyGestureEvent(new KeyGestureEvent.Builder().setKeyGestureType( + KeyGestureEvent.KEY_GESTURE_TYPE_ACTIVATE_SELECT_TO_SPEAK).setAction( + KeyGestureEvent.ACTION_GESTURE_COMPLETE).build()); + + assertThat(ShortcutUtils.getShortcutTargetsFromSettings(mTestableContext, KEY_GESTURE, + mA11yms.getCurrentUserIdLocked())).isEmpty(); + } + + @Test + @EnableFlags(com.android.hardware.input.Flags.FLAG_ENABLE_TALKBACK_AND_MAGNIFIER_KEY_GESTURES) + public void handleKeyGestureEvent_activateSelectToSpeak_noDefault() { + mFakePermissionEnforcer.grant(Manifest.permission.MANAGE_ACCESSIBILITY); + + final AccessibilityServiceInfo installedService = mockAccessibilityServiceInfo( + new ComponentName("package_a", "class_a"), + /* isSystemApp= */ true, /* isAlwaysOnService= */ true); + AccessibilityUserState userState = mA11yms.getCurrentUserState(); + userState.mInstalledServices.add(installedService); + mTestableContext.getOrCreateTestableResources().addOverride( + R.array.config_trustedAccessibilityServices, + new String[]{installedService.getComponentName().flattenToString()}); + + assertThat(ShortcutUtils.getShortcutTargetsFromSettings(mTestableContext, KEY_GESTURE, + mA11yms.getCurrentUserIdLocked())).isEmpty(); + + mA11yms.handleKeyGestureEvent(new KeyGestureEvent.Builder().setKeyGestureType( + KeyGestureEvent.KEY_GESTURE_TYPE_ACTIVATE_SELECT_TO_SPEAK).setAction( + KeyGestureEvent.ACTION_GESTURE_COMPLETE).build()); + + assertThat(ShortcutUtils.getShortcutTargetsFromSettings(mTestableContext, KEY_GESTURE, + mA11yms.getCurrentUserIdLocked())).isEmpty(); + } + private Set readStringsFromSetting(String setting) { final Set result = new ArraySet<>(); mA11yms.readColonDelimitedSettingToSet( diff --git a/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt index 40ea9dc9cd89..09a686ca2c3f 100644 --- a/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt +++ b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt @@ -794,6 +794,18 @@ class KeyGestureControllerTests { KeyEvent.META_META_ON or KeyEvent.META_ALT_ON, intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE) ), + TestData( + "META + ALT + S -> Activate Select to Speak", + intArrayOf( + KeyEvent.KEYCODE_META_LEFT, + KeyEvent.KEYCODE_ALT_LEFT, + KeyEvent.KEYCODE_S + ), + KeyGestureEvent.KEY_GESTURE_TYPE_ACTIVATE_SELECT_TO_SPEAK, + intArrayOf(KeyEvent.KEYCODE_S), + KeyEvent.META_META_ON or KeyEvent.META_ALT_ON, + intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE) + ), ) } -- cgit v1.2.3-59-g8ed1b From adb9051bd47a6d5432b2944a0413de1a7e8acb7e Mon Sep 17 00:00:00 2001 From: Hiroki Sato Date: Tue, 19 Nov 2024 10:46:34 +0900 Subject: Remove unused obsolete code from FullScreenMagnificationGestureHandler Usages were deleted in following commits while ago: - Ib6a0ad7df56c7d040497d6f352abe3d74c38a49f - Ie2a782b162b837e9bdc4904a486ed292989f83d8 Bug: None Test: build, push and try fullscreen magnification with gesture Flag: EXEMPT rrefactor Change-Id: Iacdf25d59ef89a069a4b5fbbf6791a2f491ef7c7 --- .../FullScreenMagnificationGestureHandler.java | 37 ---------------------- 1 file changed, 37 deletions(-) (limited to 'services/accessibility') diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java index c6a966f47952..d11ae0a6ad97 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java @@ -58,8 +58,6 @@ import android.util.TypedValue; import android.view.GestureDetector; import android.view.GestureDetector.SimpleOnGestureListener; import android.view.MotionEvent; -import android.view.MotionEvent.PointerCoords; -import android.view.MotionEvent.PointerProperties; import android.view.ScaleGestureDetector; import android.view.ScaleGestureDetector.OnScaleGestureListener; import android.view.VelocityTracker; @@ -155,9 +153,6 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH @VisibleForTesting State mCurrentState; @VisibleForTesting State mPreviousState; - private PointerCoords[] mTempPointerCoords; - private PointerProperties[] mTempPointerProperties; - @VisibleForTesting static final int OVERSCROLL_NONE = 0; @VisibleForTesting static final int OVERSCROLL_LEFT_EDGE = 1; @VisibleForTesting static final int OVERSCROLL_RIGHT_EDGE = 2; @@ -430,38 +425,6 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH mPanningScalingState.clear(); } - private PointerCoords[] getTempPointerCoordsWithMinSize(int size) { - final int oldSize = (mTempPointerCoords != null) ? mTempPointerCoords.length : 0; - if (oldSize < size) { - PointerCoords[] oldTempPointerCoords = mTempPointerCoords; - mTempPointerCoords = new PointerCoords[size]; - if (oldTempPointerCoords != null) { - System.arraycopy(oldTempPointerCoords, 0, mTempPointerCoords, 0, oldSize); - } - } - for (int i = oldSize; i < size; i++) { - mTempPointerCoords[i] = new PointerCoords(); - } - return mTempPointerCoords; - } - - private PointerProperties[] getTempPointerPropertiesWithMinSize(int size) { - final int oldSize = (mTempPointerProperties != null) ? mTempPointerProperties.length - : 0; - if (oldSize < size) { - PointerProperties[] oldTempPointerProperties = mTempPointerProperties; - mTempPointerProperties = new PointerProperties[size]; - if (oldTempPointerProperties != null) { - System.arraycopy(oldTempPointerProperties, 0, mTempPointerProperties, 0, - oldSize); - } - } - for (int i = oldSize; i < size; i++) { - mTempPointerProperties[i] = new PointerProperties(); - } - return mTempPointerProperties; - } - @VisibleForTesting void transitionTo(State state) { if (DEBUG_STATE_TRANSITIONS) { -- cgit v1.2.3-59-g8ed1b From 21546066896b6a088705d553fe3f5e067d997016 Mon Sep 17 00:00:00 2001 From: Hiroki Sato Date: Tue, 19 Nov 2024 13:37:50 +0900 Subject: Remove enabled flag compute_window_changes_on_a11y_v2 It has been enabled 6 month. Note that AccessibilityWindowManagerTest is actually removed, and AccessibilityWindowManagerWithAccessibilityWindowTest is renamed to AccessibilityWindowManagerTest. Bug: 322444245 Test: build, atest AccessibilityWindowManagerTest Flag: EXEMPT removing com.android.server.accessibility.compute_window_changes_on_a11y_v2 Change-Id: Ifd8328fd21f5547726313c65c60d2a4b0d6095af --- services/accessibility/accessibility.aconfig | 10 - .../accessibility/AccessibilityWindowManager.java | 43 +- .../android/server/wm/AccessibilityController.java | 174 +-- .../server/wm/AccessibilityWindowsPopulator.java | 3 +- .../android/server/wm/WindowManagerInternal.java | 21 +- .../AccessibilityWindowManagerTest.java | 479 +++++-- ...tyWindowManagerWithAccessibilityWindowTest.java | 1444 -------------------- 7 files changed, 337 insertions(+), 1837 deletions(-) delete mode 100644 services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerWithAccessibilityWindowTest.java (limited to 'services/accessibility') diff --git a/services/accessibility/accessibility.aconfig b/services/accessibility/accessibility.aconfig index 2808056f72c9..a0b989b44f4f 100644 --- a/services/accessibility/accessibility.aconfig +++ b/services/accessibility/accessibility.aconfig @@ -54,16 +54,6 @@ flag { } } -flag { - name: "compute_window_changes_on_a11y_v2" - namespace: "accessibility" - description: "Computes accessibility window changes in accessibility instead of wm package." - bug: "322444245" - metadata { - purpose: PURPOSE_BUGFIX - } -} - flag { name: "deprecate_package_list_observer" namespace: "accessibility" diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java index 9a81aa6cc506..5cbe0c4fe4d2 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java @@ -433,22 +433,10 @@ public class AccessibilityWindowManager { return Collections.emptyList(); } - /** - * Callbacks from window manager when there's an accessibility change in windows. - * - * @param forceSend Send the windows for accessibility even if they haven't changed. - * @param topFocusedDisplayId The display Id which has the top focused window. - * @param topFocusedWindowToken The window token of top focused window. - * @param windows The windows for accessibility. - */ - @Override - public void onWindowsForAccessibilityChanged(boolean forceSend, int topFocusedDisplayId, + private void onWindowsForAccessibilityChanged(boolean forceSend, int topFocusedDisplayId, IBinder topFocusedWindowToken, @NonNull List windows) { + // TODO(b/322444245): no longer need to get a lock. synchronized (mLock) { - if (!Flags.computeWindowChangesOnA11yV2()) { - // If the flag is enabled, it's already done in #createWindowInfoListLocked. - updateWindowsByWindowAttributesLocked(windows); - } if (DEBUG) { Slogf.i(LOG_TAG, "mDisplayId=%d, topFocusedDisplayId=%d, currentUserId=%d, " + "visibleBgUsers=%s", mDisplayId, topFocusedDisplayId, @@ -490,9 +478,7 @@ public class AccessibilityWindowManager { } /** - * Called when the windows for accessibility changed. This is called if - * {@link com.android.server.accessibility.Flags.FLAG_COMPUTE_WINDOW_CHANGES_ON_A11Y_V2} is - * true. + * Called when the windows for accessibility changed. * * @param forceSend Send the windows for accessibility even if they haven't * changed. @@ -655,16 +641,6 @@ public class AccessibilityWindowManager { return true; } - private void updateWindowsByWindowAttributesLocked(List windows) { - for (int i = windows.size() - 1; i >= 0; i--) { - final WindowInfo windowInfo = windows.get(i); - final IBinder token = windowInfo.token; - final int windowId = findWindowIdLocked( - mAccessibilityUserManager.getCurrentUserIdLocked(), token); - updateWindowWithWindowAttributes(windowInfo, mWindowAttributes.get(windowId)); - } - } - private void updateWindowWithWindowAttributes(@NonNull WindowInfo windowInfo, @Nullable AccessibilityWindowAttributes attributes) { if (attributes == null) { @@ -990,19 +966,6 @@ public class AccessibilityWindowManager { private AccessibilityWindowInfo populateReportedWindowLocked(int userId, WindowInfo window, SparseArray oldWindowsById) { final int windowId = findWindowIdLocked(userId, window.token); - - // With the flag enabled, createWindowInfoListLocked() already removes invalid windows. - if (!Flags.computeWindowChangesOnA11yV2()) { - if (windowId < 0) { - return null; - } - - // Don't need to add the embedded hierarchy windows into the a11y windows list. - if (isEmbeddedHierarchyWindowsLocked(windowId)) { - return null; - } - } - final AccessibilityWindowInfo reportedWindow = AccessibilityWindowInfo.obtain(); reportedWindow.setId(windowId); diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java index 7cbacd6b0b82..4b7e74af474c 100644 --- a/services/core/java/com/android/server/wm/AccessibilityController.java +++ b/services/core/java/com/android/server/wm/AccessibilityController.java @@ -22,7 +22,6 @@ import static android.os.Build.IS_USER; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY; -import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; import static android.view.WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY; import static android.view.WindowManager.TRANSIT_FLAG_IS_RECENTS; @@ -1784,22 +1783,13 @@ final class AccessibilityController { mA11yWindowsPopulator.populateVisibleWindowsOnScreenLocked( mDisplayId, visibleWindows); - if (!com.android.server.accessibility.Flags.computeWindowChangesOnA11yV2()) { - windows = buildWindowInfoListLocked(visibleWindows, screenSize); - } - // Gets the top focused display Id and window token for supporting multi-display. topFocusedDisplayId = mService.mRoot.getTopFocusedDisplayContent().getDisplayId(); topFocusedWindowToken = topFocusedWindowState.mClient.asBinder(); } - if (com.android.server.accessibility.Flags.computeWindowChangesOnA11yV2()) { - mCallback.onAccessibilityWindowsChanged(forceSend, topFocusedDisplayId, - topFocusedWindowToken, screenSize, visibleWindows); - } else { - mCallback.onWindowsForAccessibilityChanged(forceSend, topFocusedDisplayId, - topFocusedWindowToken, windows); - } + mCallback.onAccessibilityWindowsChanged(forceSend, topFocusedDisplayId, + topFocusedWindowToken, screenSize, visibleWindows); // Recycle the windows as we do not need them. for (final AccessibilityWindowsPopulator.AccessibilityWindow window : visibleWindows) { @@ -1808,166 +1798,6 @@ final class AccessibilityController { mInitialized = true; } - // Here are old code paths, called when computeWindowChangesOnA11yV2 flag is disabled. - // LINT.IfChange - - /** - * From a list of windows, decides windows to be exposed to accessibility based on touchable - * region in the screen. - */ - private List buildWindowInfoListLocked(List visibleWindows, - Point screenSize) { - final List windows = new ArrayList<>(); - final Set addedWindows = mTempBinderSet; - addedWindows.clear(); - - boolean focusedWindowAdded = false; - - final int visibleWindowCount = visibleWindows.size(); - - Region unaccountedSpace = mTempRegion; - unaccountedSpace.set(0, 0, screenSize.x, screenSize.y); - - // Iterate until we figure out what is touchable for the entire screen. - for (int i = 0; i < visibleWindowCount; i++) { - final AccessibilityWindow a11yWindow = visibleWindows.get(i); - final Region regionInWindow = new Region(); - a11yWindow.getTouchableRegionInWindow(regionInWindow); - if (windowMattersToAccessibility(a11yWindow, regionInWindow, unaccountedSpace)) { - addPopulatedWindowInfo(a11yWindow, regionInWindow, windows, addedWindows); - if (windowMattersToUnaccountedSpaceComputation(a11yWindow)) { - updateUnaccountedSpace(a11yWindow, unaccountedSpace); - } - focusedWindowAdded |= a11yWindow.isFocused(); - } else if (a11yWindow.isUntouchableNavigationBar()) { - // If this widow is navigation bar without touchable region, accounting the - // region of navigation bar inset because all touch events from this region - // would be received by launcher, i.e. this region is a un-touchable one - // for the application. - unaccountedSpace.op( - getSystemBarInsetsFrame( - mService.mWindowMap.get(a11yWindow.getWindowInfo().token)), - unaccountedSpace, - Region.Op.REVERSE_DIFFERENCE); - } - - if (unaccountedSpace.isEmpty() && focusedWindowAdded) { - break; - } - } - - // Remove child/parent references to windows that were not added. - final int windowCount = windows.size(); - for (int i = 0; i < windowCount; i++) { - WindowInfo window = windows.get(i); - if (!addedWindows.contains(window.parentToken)) { - window.parentToken = null; - } - if (window.childTokens != null) { - final int childTokenCount = window.childTokens.size(); - for (int j = childTokenCount - 1; j >= 0; j--) { - if (!addedWindows.contains(window.childTokens.get(j))) { - window.childTokens.remove(j); - } - } - // Leave the child token list if empty. - } - } - - addedWindows.clear(); - - return windows; - } - - // Some windows should be excluded from unaccounted space computation, though they still - // should be reported - private boolean windowMattersToUnaccountedSpaceComputation(AccessibilityWindow a11yWindow) { - // Do not account space of trusted non-touchable windows, except the split-screen - // divider. - // If it's not trusted, touch events are not sent to the windows behind it. - if (!a11yWindow.isTouchable() - && (a11yWindow.getType() != TYPE_DOCK_DIVIDER) - && a11yWindow.isTrustedOverlay()) { - return false; - } - - if (a11yWindow.getType() == WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY) { - return false; - } - return true; - } - - private boolean windowMattersToAccessibility(AccessibilityWindow a11yWindow, - Region regionInScreen, Region unaccountedSpace) { - if (a11yWindow.isFocused()) { - return true; - } - - // Ignore non-touchable windows, except the split-screen divider, which is - // occasionally non-touchable but still useful for identifying split-screen - // mode and the PIP menu. - if (!a11yWindow.isTouchable() - && (a11yWindow.getType() != TYPE_DOCK_DIVIDER - && !a11yWindow.isPIPMenu())) { - return false; - } - - // If the window is completely covered by other windows - ignore. - if (unaccountedSpace.quickReject(regionInScreen)) { - return false; - } - - // Add windows of certain types not covered by modal windows. - if (isReportedWindowType(a11yWindow.getType())) { - return true; - } - - return false; - } - - private void updateUnaccountedSpace(AccessibilityWindow a11yWindow, - Region unaccountedSpace) { - if (a11yWindow.getType() - != WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY) { - // Account for the space this window takes if the window - // is not an accessibility overlay which does not change - // the reported windows. - final Region touchableRegion = mTempRegion2; - a11yWindow.getTouchableRegionInScreen(touchableRegion); - unaccountedSpace.op(touchableRegion, unaccountedSpace, - Region.Op.REVERSE_DIFFERENCE); - } - } - - private static void addPopulatedWindowInfo(AccessibilityWindow a11yWindow, - Region regionInScreen, List out, Set tokenOut) { - final WindowInfo window = a11yWindow.getWindowInfo(); - if (window.token == null) { - // The window was used in calculating visible windows but does not have an - // associated IWindow token, so exclude it from the list returned to accessibility. - return; - } - window.regionInScreen.set(regionInScreen); - window.layer = tokenOut.size(); - out.add(window); - tokenOut.add(window.token); - } - - private static boolean isReportedWindowType(int windowType) { - return (windowType != WindowManager.LayoutParams.TYPE_WALLPAPER - && windowType != WindowManager.LayoutParams.TYPE_BOOT_PROGRESS - && windowType != WindowManager.LayoutParams.TYPE_DISPLAY_OVERLAY - && windowType != WindowManager.LayoutParams.TYPE_DRAG - && windowType != WindowManager.LayoutParams.TYPE_INPUT_CONSUMER - && windowType != WindowManager.LayoutParams.TYPE_POINTER - && windowType != TYPE_MAGNIFICATION_OVERLAY - && windowType != WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY - && windowType != WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY - && windowType != WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION); - } - - // LINT.ThenChange(/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java) - private WindowState getTopFocusWindow() { return mService.mRoot.getTopFocusedDisplayContent().mCurrentFocus; } diff --git a/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java b/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java index fd2a909f8b05..7fc11e6c3ac9 100644 --- a/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java +++ b/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java @@ -724,8 +724,7 @@ public final class AccessibilityWindowsPopulator extends WindowInfosListener { } // Compute system bar insets frame if needed. - if (com.android.server.accessibility.Flags.computeWindowChangesOnA11yV2() - && windowState != null && instance.isUntouchableNavigationBar()) { + if (windowState != null && instance.isUntouchableNavigationBar()) { final InsetsSourceProvider provider = windowState.getControllableInsetProvider(); if (provider != null) { diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java index ce032b4f7f9a..c77b1d9a7bcf 100644 --- a/services/core/java/com/android/server/wm/WindowManagerInternal.java +++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java @@ -46,7 +46,6 @@ import android.view.RemoteAnimationTarget; import android.view.Surface; import android.view.SurfaceControl; import android.view.SurfaceControlViewHost; -import android.view.WindowInfo; import android.view.WindowManager.DisplayImePolicy; import android.view.inputmethod.ImeTracker; import android.window.ScreenCapture; @@ -158,26 +157,8 @@ public abstract class WindowManagerInternal { * accessibility changed. */ public interface WindowsForAccessibilityCallback { - - /** - * Called when the windows for accessibility changed. This is called if - * {@link com.android.server.accessibility.Flags.FLAG_COMPUTE_WINDOW_CHANGES_ON_A11Y_V2} is - * false. - * - * @param forceSend Send the windows for accessibility even if they haven't changed. - * @param topFocusedDisplayId The display Id which has the top focused window. - * @param topFocusedWindowToken The window token of top focused window. - * @param windows The windows for accessibility. - */ - void onWindowsForAccessibilityChanged(boolean forceSend, int topFocusedDisplayId, - IBinder topFocusedWindowToken, @NonNull List windows); - /** - * Called when the windows for accessibility changed. This is called if - * {@link com.android.server.accessibility.Flags.FLAG_COMPUTE_WINDOW_CHANGES_ON_A11Y_V2} is - * true. - * TODO(b/322444245): Remove screenSize parameter by getting it from - * DisplayManager#getDisplay(int).getRealSize() on the a11y side. + * Called when the windows for accessibility changed. * * @param forceSend Send the windows for accessibility even if they haven't changed. * @param topFocusedDisplayId The display Id which has the top focused window. diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java index 403930d96a12..2ae31ad618d6 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java @@ -18,20 +18,24 @@ package com.android.server.accessibility; import static com.android.server.accessibility.AbstractAccessibilityServiceConnection.DISPLAY_TYPE_DEFAULT; import static com.android.server.accessibility.AccessibilityWindowManagerTest.DisplayIdMatcher.displayId; +import static com.android.server.accessibility.AccessibilityWindowManagerTest.EventWindowIdMatcher.eventWindowId; import static com.android.server.accessibility.AccessibilityWindowManagerTest.WindowChangesMatcher.a11yWindowChanges; -import static com.android.server.accessibility.AccessibilityWindowManagerTest.WindowIdMatcher.a11yWindowId; +import static com.android.server.accessibility.AccessibilityWindowManagerTest.WindowIdMatcher.windowId; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertNotNull; import static junit.framework.Assert.assertNull; import static junit.framework.Assert.assertTrue; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.hasItem; +import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertThat; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; @@ -42,14 +46,13 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.annotation.Nullable; +import android.graphics.Point; +import android.graphics.Rect; import android.graphics.Region; import android.os.IBinder; import android.os.LocaleList; import android.os.RemoteException; import android.os.UserHandle; -import android.platform.test.annotations.RequiresFlagsDisabled; -import android.platform.test.flag.junit.CheckFlagsRule; -import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.util.SparseArray; import android.view.Display; import android.view.IWindow; @@ -63,6 +66,7 @@ import android.view.accessibility.IAccessibilityInteractionConnection; import com.android.server.accessibility.AccessibilityWindowManager.RemoteAccessibilityConnection; import com.android.server.accessibility.test.MessageCapturingHandler; +import com.android.server.wm.AccessibilityWindowsPopulator.AccessibilityWindow; import com.android.server.wm.WindowManagerInternal; import com.android.server.wm.WindowManagerInternal.WindowsForAccessibilityCallback; @@ -70,7 +74,6 @@ import org.hamcrest.Description; import org.hamcrest.TypeSafeMatcher; import org.junit.After; import org.junit.Before; -import org.junit.Rule; import org.junit.Test; import org.mockito.ArgumentCaptor; import org.mockito.Mock; @@ -81,17 +84,9 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; -// This test verifies deprecated codepath. Probably changing this file means -// AccessibilityWindowManagerWithAccessibilityWindowTest also needs to be updated. -// LINT.IfChange - /** - * Tests for the AccessibilityWindowManager with Flags.FLAG_COMPUTE_WINDOW_CHANGES_ON_A11Y_V2 - * enabled. - * TODO(b/322444245): Merge with AccessibilityWindowManagerWithAccessibilityWindowTest - * after completing the flag migration. + * Tests for the AccessibilityWindowManager. */ -@RequiresFlagsDisabled(Flags.FLAG_COMPUTE_WINDOW_CHANGES_ON_A11Y_V2) public class AccessibilityWindowManagerTest { private static final String PACKAGE_NAME = "com.android.server.accessibility"; private static final boolean FORCE_SEND = true; @@ -122,9 +117,8 @@ public class AccessibilityWindowManagerTest { // List of window token, mapping from windowId -> window token. private final SparseArray mA11yWindowTokens = new SparseArray<>(); - // List of window info lists, mapping from displayId -> window info lists. - private final SparseArray> mWindowInfos = - new SparseArray<>(); + // List of window info lists, mapping from displayId -> a11y window lists. + private final SparseArray> mWindows = new SparseArray<>(); // List of callback, mapping from displayId -> callback. private final SparseArray mCallbackOfWindows = new SparseArray<>(); @@ -134,6 +128,13 @@ public class AccessibilityWindowManagerTest { private final MessageCapturingHandler mHandler = new MessageCapturingHandler(null); + // This maps displayId -> next region offset. + // Touchable region must have un-occluded area so that it's exposed to a11y services. + // This offset can be used as left and top of new region so that top-left of each region are + // kept visible. + // It's expected to be incremented by some amount everytime the value is used. + private final SparseArray mNextRegionOffsets = new SparseArray<>(); + @Mock private WindowManagerInternal mMockWindowManagerInternal; @Mock private AccessibilityWindowManager.AccessibilityEventSender mMockA11yEventSender; @Mock private AccessibilitySecurityPolicy mMockA11ySecurityPolicy; @@ -144,9 +145,6 @@ public class AccessibilityWindowManagerTest { @Mock private IBinder mMockEmbeddedToken; @Mock private IBinder mMockInvalidToken; - @Rule - public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); - @Before public void setUp() throws RemoteException { MockitoAnnotations.initMocks(this); @@ -159,7 +157,7 @@ public class AccessibilityWindowManagerTest { anyString(), anyInt(), anyInt(), anyInt())).thenReturn(PACKAGE_NAME); doAnswer((invocation) -> { - onWindowsForAccessibilityChanged(invocation.getArgument(0), false); + onAccessibilityWindowsChanged(invocation.getArgument(0), false); return null; }).when(mMockWindowManagerInternal).computeWindowsForAccessibility(anyInt()); @@ -173,7 +171,7 @@ public class AccessibilityWindowManagerTest { // as top focused display before each testing starts. startTrackingPerDisplay(Display.DEFAULT_DISPLAY); - // AccessibilityEventSender is invoked during onWindowsForAccessibilityChanged. + // AccessibilityEventSender is invoked during onAccessibilityWindowsChanged. // Resets it for mockito verify of further test case. Mockito.reset(mMockA11yEventSender); @@ -237,19 +235,18 @@ public class AccessibilityWindowManagerTest { @Test public void onWindowsChanged_duringTouchInteractAndFocusChange_shouldChangeActiveWindow() { final int activeWindowId = mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID); - WindowInfo focusedWindowInfo = - mWindowInfos.get(Display.DEFAULT_DISPLAY).get(DEFAULT_FOCUSED_INDEX); + final WindowInfo focusedWindowInfo = + mWindows.get(Display.DEFAULT_DISPLAY).get(DEFAULT_FOCUSED_INDEX).getWindowInfo(); assertEquals(activeWindowId, mA11yWindowManager.findWindowIdLocked( USER_SYSTEM_ID, focusedWindowInfo.token)); focusedWindowInfo.focused = false; - focusedWindowInfo = - mWindowInfos.get(Display.DEFAULT_DISPLAY).get(DEFAULT_FOCUSED_INDEX + 1); - focusedWindowInfo.focused = true; + mWindows.get(Display.DEFAULT_DISPLAY).get( + DEFAULT_FOCUSED_INDEX + 1).getWindowInfo().focused = true; mA11yWindowManager.onTouchInteractionStart(); setTopFocusedWindowAndDisplay(Display.DEFAULT_DISPLAY, DEFAULT_FOCUSED_INDEX + 1); - onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); + onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); assertNotEquals(activeWindowId, mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID)); } @@ -273,7 +270,7 @@ public class AccessibilityWindowManagerTest { changeFocusedWindowOnDisplayPerDisplayFocusConfig(SECONDARY_DISPLAY_ID, DEFAULT_FOCUSED_INDEX + 1, Display.DEFAULT_DISPLAY, DEFAULT_FOCUSED_INDEX); - onWindowsForAccessibilityChanged(SECONDARY_DISPLAY_ID, SEND_ON_WINDOW_CHANGES); + onAccessibilityWindowsChanged(SECONDARY_DISPLAY_ID, SEND_ON_WINDOW_CHANGES); // The active window should not be changed. assertEquals(activeWindowId, mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID)); // The top focused window should not be changed. @@ -301,8 +298,8 @@ public class AccessibilityWindowManagerTest { changeFocusedWindowOnDisplayPerDisplayFocusConfig(SECONDARY_DISPLAY_ID, DEFAULT_FOCUSED_INDEX, Display.DEFAULT_DISPLAY, DEFAULT_FOCUSED_INDEX); - onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); - onWindowsForAccessibilityChanged(SECONDARY_DISPLAY_ID, SEND_ON_WINDOW_CHANGES); + onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); + onAccessibilityWindowsChanged(SECONDARY_DISPLAY_ID, SEND_ON_WINDOW_CHANGES); // The active window should be changed. assertNotEquals(activeWindowId, mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID)); // The top focused window should be changed. @@ -312,53 +309,181 @@ public class AccessibilityWindowManagerTest { @Test public void onWindowsChanged_shouldReportCorrectLayer() { - // AccessibilityWindowManager#onWindowsForAccessibilityChanged already invoked in setup. + // AccessibilityWindowManager#onAccessibilityWindowsChanged already invoked in setup. List a11yWindows = mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY); for (int i = 0; i < a11yWindows.size(); i++) { final AccessibilityWindowInfo a11yWindow = a11yWindows.get(i); - final WindowInfo windowInfo = mWindowInfos.get(Display.DEFAULT_DISPLAY).get(i); - assertThat(mWindowInfos.get(Display.DEFAULT_DISPLAY).size() - windowInfo.layer - 1, + assertThat(mWindows.get(Display.DEFAULT_DISPLAY).size() - i - 1, is(a11yWindow.getLayer())); } } @Test public void onWindowsChanged_shouldReportCorrectOrder() { - // AccessibilityWindowManager#onWindowsForAccessibilityChanged already invoked in setup. + // AccessibilityWindowManager#onAccessibilityWindowsChanged already invoked in setup. List a11yWindows = mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY); for (int i = 0; i < a11yWindows.size(); i++) { final AccessibilityWindowInfo a11yWindow = a11yWindows.get(i); final IBinder windowToken = mA11yWindowManager .getWindowTokenForUserAndWindowIdLocked(USER_SYSTEM_ID, a11yWindow.getId()); - final WindowInfo windowInfo = mWindowInfos.get(Display.DEFAULT_DISPLAY).get(i); + final WindowInfo windowInfo = mWindows.get(Display.DEFAULT_DISPLAY) + .get(i).getWindowInfo(); assertThat(windowToken, is(windowInfo.token)); } } @Test - public void onWindowsChangedAndForceSend_shouldUpdateWindows() { - final WindowInfo windowInfo = mWindowInfos.get(Display.DEFAULT_DISPLAY).get(0); - final int correctLayer = - mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY).get(0).getLayer(); - windowInfo.layer += 1; + public void onWindowsChanged_shouldNotReportNonTouchableWindow() { + final AccessibilityWindow window = mWindows.get(Display.DEFAULT_DISPLAY).get(0); + when(window.isTouchable()).thenReturn(false); + final int windowId = mA11yWindowManager.findWindowIdLocked( + USER_SYSTEM_ID, window.getWindowInfo().token); + + onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); + + final List a11yWindows = + mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY); + assertThat(a11yWindows, not(hasItem(windowId(windowId)))); + } + + @Test + public void onWindowsChanged_shouldReportFocusedNonTouchableWindow() { + final AccessibilityWindow window = mWindows.get(Display.DEFAULT_DISPLAY).get( + DEFAULT_FOCUSED_INDEX); + when(window.isTouchable()).thenReturn(false); + final int windowId = mA11yWindowManager.findWindowIdLocked( + USER_SYSTEM_ID, window.getWindowInfo().token); + + onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); + + final List a11yWindows = + mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY); + assertThat(a11yWindows, hasItem(windowId(windowId))); + } + + @Test + public void onWindowsChanged_trustedFocusedNonTouchableWindow_shouldNotHideWindowsBelow() { + // Make the focused trusted un-touchable window fullscreen. + final AccessibilityWindow window = mWindows.get(Display.DEFAULT_DISPLAY).get( + DEFAULT_FOCUSED_INDEX); + setRegionForMockAccessibilityWindow(window, new Region(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT)); + when(window.isTouchable()).thenReturn(false); + when(window.isTrustedOverlay()).thenReturn(true); + + onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); + + final List a11yWindows = + mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY); + assertThat(a11yWindows, hasSize(NUM_OF_WINDOWS)); + } + + @Test + public void onWindowsChanged_accessibilityOverlay_shouldNotHideWindowsBelow() { + // Make the a11y overlay window fullscreen. + final AccessibilityWindow window = mWindows.get(Display.DEFAULT_DISPLAY).get(0); + setRegionForMockAccessibilityWindow(window, new Region(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT)); + when(window.getType()).thenReturn(WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY); + + onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); + + final List a11yWindows = + mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY); + assertThat(a11yWindows, hasSize(NUM_OF_WINDOWS)); + } + + @Test + public void onWindowsChanged_shouldReportFocusedWindowEvenIfOccluded() { + // Make the front window fullscreen. + final AccessibilityWindow frontWindow = mWindows.get(Display.DEFAULT_DISPLAY).get(0); + setRegionForMockAccessibilityWindow(frontWindow, + new Region(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT)); + final int frontWindowId = mA11yWindowManager.findWindowIdLocked( + USER_SYSTEM_ID, frontWindow.getWindowInfo().token); + + final AccessibilityWindow focusedWindow = mWindows.get(Display.DEFAULT_DISPLAY).get( + DEFAULT_FOCUSED_INDEX); + final int focusedWindowId = mA11yWindowManager.findWindowIdLocked( + USER_SYSTEM_ID, focusedWindow.getWindowInfo().token); + + onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); + + final List a11yWindows = + mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY); + assertThat(a11yWindows, hasSize(2)); + assertThat(a11yWindows.get(0), windowId(frontWindowId)); + assertThat(a11yWindows.get(1), windowId(focusedWindowId)); + } + + @Test + public void onWindowsChanged_embeddedWindows_shouldOnlyReportHost() throws RemoteException { + final Rect embeddingBounds = new Rect(0, 0, 200, 100); + + // The embedded window comes front of the host window. + final IBinder embeddedWindowLeashToken = Mockito.mock(IBinder.class); + final int embeddedWindowId = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY, + false, embeddedWindowLeashToken, USER_SYSTEM_ID); + final AccessibilityWindow embeddedWindow = createMockAccessibilityWindow( + mA11yWindowTokens.get(embeddedWindowId), Display.DEFAULT_DISPLAY); + setRegionForMockAccessibilityWindow(embeddedWindow, new Region(embeddingBounds)); + mWindows.get(Display.DEFAULT_DISPLAY).set(0, embeddedWindow); + + final IBinder hostWindowLeashToken = Mockito.mock(IBinder.class); + final int hostWindowId = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY, + false, hostWindowLeashToken, USER_SYSTEM_ID); + final AccessibilityWindow hostWindow = createMockAccessibilityWindow( + mA11yWindowTokens.get(hostWindowId), Display.DEFAULT_DISPLAY); + setRegionForMockAccessibilityWindow(hostWindow, new Region(embeddingBounds)); + mWindows.get(Display.DEFAULT_DISPLAY).set(1, hostWindow); + + mA11yWindowManager.associateEmbeddedHierarchyLocked( + hostWindowLeashToken, embeddedWindowLeashToken); + + onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); + + final List a11yWindows = + mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY); + assertThat(a11yWindows, not(hasItem(windowId(embeddedWindowId)))); + assertThat(a11yWindows.get(0), windowId(hostWindowId)); + final Rect bounds = new Rect(); + a11yWindows.get(0).getBoundsInScreen(bounds); + assertEquals(bounds, embeddingBounds); + } - onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, FORCE_SEND); - assertNotEquals(correctLayer, - mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY).get(0).getLayer()); + @Test + public void onWindowsChanged_shouldNotReportfullyOccludedWindow() { + final AccessibilityWindow frontWindow = mWindows.get(Display.DEFAULT_DISPLAY).get(0); + setRegionForMockAccessibilityWindow(frontWindow, new Region(100, 100, 300, 300)); + final int frontWindowId = mA11yWindowManager.findWindowIdLocked( + USER_SYSTEM_ID, frontWindow.getWindowInfo().token); + + // index 1 is focused. Let's use the next one for this test. + final AccessibilityWindow occludedWindow = mWindows.get(Display.DEFAULT_DISPLAY).get(2); + setRegionForMockAccessibilityWindow(occludedWindow, new Region(150, 150, 250, 250)); + final int occludedWindowId = mA11yWindowManager.findWindowIdLocked( + USER_SYSTEM_ID, occludedWindow.getWindowInfo().token); + + onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); + + final List a11yWindows = + mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY); + assertThat(a11yWindows, hasItem(windowId(frontWindowId))); + assertThat(a11yWindows, not(hasItem(windowId(occludedWindowId)))); } @Test - public void onWindowsChangedNoForceSend_layerChanged_shouldNotUpdateWindows() { - final WindowInfo windowInfo = mWindowInfos.get(Display.DEFAULT_DISPLAY).get(0); - final int correctLayer = - mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY).get(0).getLayer(); - windowInfo.layer += 1; + public void onWindowsChangedAndForceSend_shouldUpdateWindows() { + assertNotEquals("new title", + toString(mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY) + .get(0).getTitle())); - onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); - assertEquals(correctLayer, - mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY).get(0).getLayer()); + mWindows.get(Display.DEFAULT_DISPLAY).get(0).getWindowInfo().title = "new title"; + + onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, FORCE_SEND); + assertEquals("new title", + toString(mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY) + .get(0).getTitle())); } @Test @@ -368,14 +493,10 @@ public class AccessibilityWindowManagerTest { mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY).get(0); final IWindow token = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY, true, USER_SYSTEM_ID); - final WindowInfo windowInfo = WindowInfo.obtain(); - windowInfo.type = AccessibilityWindowInfo.TYPE_APPLICATION; - windowInfo.token = token.asBinder(); - windowInfo.layer = 0; - windowInfo.regionInScreen.set(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); - mWindowInfos.get(Display.DEFAULT_DISPLAY).set(0, windowInfo); + mWindows.get(Display.DEFAULT_DISPLAY).set(0, + createMockAccessibilityWindow(token, Display.DEFAULT_DISPLAY)); - onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); + onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); assertNotEquals(oldWindow, mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY).get(0)); } @@ -383,12 +504,12 @@ public class AccessibilityWindowManagerTest { @Test public void onWindowsChangedNoForceSend_focusChanged_shouldUpdateWindows() { final WindowInfo focusedWindowInfo = - mWindowInfos.get(Display.DEFAULT_DISPLAY).get(DEFAULT_FOCUSED_INDEX); - final WindowInfo windowInfo = mWindowInfos.get(Display.DEFAULT_DISPLAY).get(0); + mWindows.get(Display.DEFAULT_DISPLAY).get(DEFAULT_FOCUSED_INDEX).getWindowInfo(); + final WindowInfo windowInfo = mWindows.get(Display.DEFAULT_DISPLAY).get(0).getWindowInfo(); focusedWindowInfo.focused = false; windowInfo.focused = true; - onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); + onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); assertTrue(mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY).get(0) .isFocused()); } @@ -497,15 +618,18 @@ public class AccessibilityWindowManagerTest { @Test public void computePartialInteractiveRegionForWindow_wholeVisible_returnWholeRegion() { // Updates top 2 z-order WindowInfo are whole visible. - WindowInfo windowInfo = mWindowInfos.get(Display.DEFAULT_DISPLAY).get(0); - windowInfo.regionInScreen.set(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT / 2); - windowInfo = mWindowInfos.get(Display.DEFAULT_DISPLAY).get(1); - windowInfo.regionInScreen.set(0, SCREEN_HEIGHT / 2, - SCREEN_WIDTH, SCREEN_HEIGHT); - onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); + final AccessibilityWindow firstWindow = mWindows.get(Display.DEFAULT_DISPLAY).get(0); + setRegionForMockAccessibilityWindow(firstWindow, + new Region(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT / 2)); + final AccessibilityWindow secondWindow = mWindows.get(Display.DEFAULT_DISPLAY).get(1); + setRegionForMockAccessibilityWindow(secondWindow, + new Region(0, SCREEN_HEIGHT / 2, SCREEN_WIDTH, SCREEN_HEIGHT)); + + onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); final List a11yWindows = mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY); + assertThat(a11yWindows, hasSize(2)); final Region outBounds = new Region(); int windowId = a11yWindows.get(0).getId(); @@ -523,12 +647,17 @@ public class AccessibilityWindowManagerTest { @Test public void computePartialInteractiveRegionForWindow_halfVisible_returnHalfRegion() { // Updates z-order #1 WindowInfo is half visible. - WindowInfo windowInfo = mWindowInfos.get(Display.DEFAULT_DISPLAY).get(0); - windowInfo.regionInScreen.set(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT / 2); - - onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); + final AccessibilityWindow firstWindow = mWindows.get(Display.DEFAULT_DISPLAY).get(0); + setRegionForMockAccessibilityWindow(firstWindow, + new Region(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT / 2)); + final AccessibilityWindow secondWindow = mWindows.get(Display.DEFAULT_DISPLAY).get(1); + setRegionForMockAccessibilityWindow(secondWindow, + new Region(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT)); + + onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); final List a11yWindows = mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY); + assertThat(a11yWindows, hasSize(2)); final Region outBounds = new Region(); int windowId = a11yWindows.get(1).getId(); @@ -539,9 +668,17 @@ public class AccessibilityWindowManagerTest { @Test public void computePartialInteractiveRegionForWindow_notVisible_returnEmptyRegion() { - // Since z-order #0 WindowInfo is full screen, z-order #1 WindowInfo should be invisible. + // z-order #0 WindowInfo is full screen, z-order #1 WindowInfo should be invisible. + final AccessibilityWindow firstWindow = mWindows.get(Display.DEFAULT_DISPLAY).get(0); + setRegionForMockAccessibilityWindow(firstWindow, + new Region(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT)); + + onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); + final List a11yWindows = mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY); + // Note that the second window is also exposed even if region is empty because it's focused. + assertThat(a11yWindows, hasSize(2)); final Region outBounds = new Region(); int windowId = a11yWindows.get(1).getId(); @@ -552,16 +689,21 @@ public class AccessibilityWindowManagerTest { @Test public void computePartialInteractiveRegionForWindow_partialVisible_returnVisibleRegion() { // Updates z-order #0 WindowInfo to have two interact-able areas. - Region region = new Region(0, 0, SCREEN_WIDTH, 200); + final Region region = new Region(0, 0, SCREEN_WIDTH, 200); region.op(0, SCREEN_HEIGHT - 200, SCREEN_WIDTH, SCREEN_HEIGHT, Region.Op.UNION); - WindowInfo windowInfo = mWindowInfos.get(Display.DEFAULT_DISPLAY).get(0); - windowInfo.regionInScreen.set(region); - onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); + final AccessibilityWindow firstWindow = mWindows.get(Display.DEFAULT_DISPLAY).get(0); + setRegionForMockAccessibilityWindow(firstWindow, region); + final AccessibilityWindow secondWindow = mWindows.get(Display.DEFAULT_DISPLAY).get(1); + setRegionForMockAccessibilityWindow(secondWindow, + new Region(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT)); + + onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); final List a11yWindows = mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY); + assertThat(a11yWindows, hasSize(2)); final Region outBounds = new Region(); - int windowId = a11yWindows.get(1).getId(); + final int windowId = a11yWindows.get(1).getId(); mA11yWindowManager.computePartialInteractiveRegionForWindowLocked(windowId, outBounds); assertFalse(outBounds.getBounds().isEmpty()); @@ -572,7 +714,8 @@ public class AccessibilityWindowManagerTest { @Test public void updateActiveAndA11yFocusedWindow_windowStateChangedEvent_noTracking_shouldUpdate() { final IBinder eventWindowToken = - mWindowInfos.get(Display.DEFAULT_DISPLAY).get(DEFAULT_FOCUSED_INDEX + 1).token; + mWindows.get(Display.DEFAULT_DISPLAY) + .get(DEFAULT_FOCUSED_INDEX + 1).getWindowInfo().token; final int eventWindowId = mA11yWindowManager.findWindowIdLocked( USER_SYSTEM_ID, eventWindowToken); when(mMockWindowManagerInternal.getFocusedWindowTokenFromWindowStates()) @@ -611,11 +754,11 @@ public class AccessibilityWindowManagerTest { .sendAccessibilityEventForCurrentUserLocked(captor.capture()); assertThat(captor.getAllValues().get(0), allOf(displayId(Display.DEFAULT_DISPLAY), - a11yWindowId(currentActiveWindowId), + eventWindowId(currentActiveWindowId), a11yWindowChanges(AccessibilityEvent.WINDOWS_CHANGE_ACTIVE))); assertThat(captor.getAllValues().get(1), allOf(displayId(Display.DEFAULT_DISPLAY), - a11yWindowId(eventWindowId), + eventWindowId(eventWindowId), a11yWindowChanges(AccessibilityEvent.WINDOWS_CHANGE_ACTIVE))); } @@ -641,7 +784,7 @@ public class AccessibilityWindowManagerTest { .sendAccessibilityEventForCurrentUserLocked(captor.capture()); assertThat(captor.getAllValues().get(0), allOf(displayId(Display.DEFAULT_DISPLAY), - a11yWindowId(eventWindowId), + eventWindowId(eventWindowId), a11yWindowChanges( AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED))); } @@ -690,12 +833,12 @@ public class AccessibilityWindowManagerTest { .sendAccessibilityEventForCurrentUserLocked(captor.capture()); assertThat(captor.getAllValues().get(0), allOf(displayId(initialDisplayId), - a11yWindowId(initialWindowId), + eventWindowId(initialWindowId), a11yWindowChanges( AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED))); assertThat(captor.getAllValues().get(1), allOf(displayId(eventDisplayId), - a11yWindowId(eventWindowId), + eventWindowId(eventWindowId), a11yWindowChanges( AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED))); } @@ -722,7 +865,7 @@ public class AccessibilityWindowManagerTest { AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED, noUse); assertThat(mA11yWindowManager.getFocusedWindowId( - AccessibilityNodeInfo.FOCUS_ACCESSIBILITY), + AccessibilityNodeInfo.FOCUS_ACCESSIBILITY), is(AccessibilityWindowInfo.UNDEFINED_WINDOW_ID)); } @@ -751,11 +894,11 @@ public class AccessibilityWindowManagerTest { .sendAccessibilityEventForCurrentUserLocked(captor.capture()); assertThat(captor.getAllValues().get(0), allOf(displayId(Display.DEFAULT_DISPLAY), - a11yWindowId(eventWindowId), + eventWindowId(eventWindowId), a11yWindowChanges(AccessibilityEvent.WINDOWS_CHANGE_ACTIVE))); assertThat(captor.getAllValues().get(1), allOf(displayId(Display.DEFAULT_DISPLAY), - a11yWindowId(currentActiveWindowId), + eventWindowId(currentActiveWindowId), a11yWindowChanges(AccessibilityEvent.WINDOWS_CHANGE_ACTIVE))); } @@ -763,7 +906,8 @@ public class AccessibilityWindowManagerTest { public void onTouchInteractionEnd_noServiceInteractiveWindow_shouldClearA11yFocus() throws RemoteException { final IBinder defaultFocusWinToken = - mWindowInfos.get(Display.DEFAULT_DISPLAY).get(DEFAULT_FOCUSED_INDEX).token; + mWindows.get(Display.DEFAULT_DISPLAY).get( + DEFAULT_FOCUSED_INDEX).getWindowInfo().token; final int defaultFocusWindowId = mA11yWindowManager.findWindowIdLocked( USER_SYSTEM_ID, defaultFocusWinToken); when(mMockWindowManagerInternal.getFocusedWindowTokenFromWindowStates()) @@ -808,8 +952,8 @@ public class AccessibilityWindowManagerTest { @Test public void getPictureInPictureWindow_shouldNotNull() { assertNull(mA11yWindowManager.getPictureInPictureWindowLocked()); - mWindowInfos.get(Display.DEFAULT_DISPLAY).get(1).inPictureInPicture = true; - onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); + mWindows.get(Display.DEFAULT_DISPLAY).get(1).getWindowInfo().inPictureInPicture = true; + onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); assertNotNull(mA11yWindowManager.getPictureInPictureWindowLocked()); } @@ -823,8 +967,9 @@ public class AccessibilityWindowManagerTest { final IAccessibilityInteractionConnection mockRemoteConnection = mA11yWindowManager.getConnectionLocked( USER_SYSTEM_ID, outsideWindowId).getRemote(); - mWindowInfos.get(Display.DEFAULT_DISPLAY).get(0).hasFlagWatchOutsideTouch = true; - onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); + mWindows.get(Display.DEFAULT_DISPLAY).get(0).getWindowInfo().hasFlagWatchOutsideTouch = + true; + onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); mA11yWindowManager.notifyOutsideTouch(USER_SYSTEM_ID, targetWindowId); verify(mockRemoteConnection).notifyOutsideTouch(); @@ -942,18 +1087,14 @@ public class AccessibilityWindowManagerTest { @Test public void sendAccessibilityEventOnWindowRemoval() { - final ArrayList infos = mWindowInfos.get(Display.DEFAULT_DISPLAY); + final ArrayList windows = mWindows.get(Display.DEFAULT_DISPLAY); // Removing index 0 because it's not focused, and avoids unnecessary layer change. final int windowId = getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY, 0); - infos.remove(0); - for (WindowInfo info : infos) { - // Adjust layer number because it should start from 0. - info.layer--; - } + windows.remove(0); - onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, FORCE_SEND); + onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, FORCE_SEND); final ArgumentCaptor captor = ArgumentCaptor.forClass(AccessibilityEvent.class); @@ -961,27 +1102,21 @@ public class AccessibilityWindowManagerTest { .sendAccessibilityEventForCurrentUserLocked(captor.capture()); assertThat(captor.getAllValues().get(0), allOf(displayId(Display.DEFAULT_DISPLAY), - a11yWindowId(windowId), + eventWindowId(windowId), a11yWindowChanges(AccessibilityEvent.WINDOWS_CHANGE_REMOVED))); } @Test public void sendAccessibilityEventOnWindowAddition() throws RemoteException { - final ArrayList infos = mWindowInfos.get(Display.DEFAULT_DISPLAY); - - for (WindowInfo info : infos) { - // Adjust layer number because new window will have 0 so that layer number in - // A11yWindowInfo in window won't be changed. - info.layer++; - } + final ArrayList windows = mWindows.get(Display.DEFAULT_DISPLAY); final IWindow token = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY, false, USER_SYSTEM_ID); - addWindowInfo(infos, token, 0); - final int windowId = - getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY, infos.size() - 1); + // Adding window to the front so that other windows' layer won't change. + windows.add(0, createMockAccessibilityWindow(token, Display.DEFAULT_DISPLAY)); + final int windowId = getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY, 0); - onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, FORCE_SEND); + onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, FORCE_SEND); final ArgumentCaptor captor = ArgumentCaptor.forClass(AccessibilityEvent.class); @@ -989,17 +1124,17 @@ public class AccessibilityWindowManagerTest { .sendAccessibilityEventForCurrentUserLocked(captor.capture()); assertThat(captor.getAllValues().get(0), allOf(displayId(Display.DEFAULT_DISPLAY), - a11yWindowId(windowId), + eventWindowId(windowId), a11yWindowChanges(AccessibilityEvent.WINDOWS_CHANGE_ADDED))); } @Test public void sendAccessibilityEventOnWindowChange() { - final ArrayList infos = mWindowInfos.get(Display.DEFAULT_DISPLAY); - infos.get(0).title = "new title"; + final ArrayList windows = mWindows.get(Display.DEFAULT_DISPLAY); + windows.get(0).getWindowInfo().title = "new title"; final int windowId = getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY, 0); - onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, FORCE_SEND); + onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, FORCE_SEND); final ArgumentCaptor captor = ArgumentCaptor.forClass(AccessibilityEvent.class); @@ -1007,7 +1142,7 @@ public class AccessibilityWindowManagerTest { .sendAccessibilityEventForCurrentUserLocked(captor.capture()); assertThat(captor.getAllValues().get(0), allOf(displayId(Display.DEFAULT_DISPLAY), - a11yWindowId(windowId), + eventWindowId(windowId), a11yWindowChanges(AccessibilityEvent.WINDOWS_CHANGE_TITLE))); } @@ -1017,48 +1152,47 @@ public class AccessibilityWindowManagerTest { } private void startTrackingPerDisplay(int displayId) throws RemoteException { - ArrayList windowInfosForDisplay = new ArrayList<>(); + ArrayList windowsForDisplay = new ArrayList<>(); // Adds RemoteAccessibilityConnection into AccessibilityWindowManager, and copy // mock window token into mA11yWindowTokens. Also, preparing WindowInfo mWindowInfos // for the test. - int layer = 0; for (int i = 0; i < NUM_GLOBAL_WINDOWS; i++) { final IWindow token = addAccessibilityInteractionConnection(displayId, true, USER_SYSTEM_ID); - addWindowInfo(windowInfosForDisplay, token, layer++); + windowsForDisplay.add(createMockAccessibilityWindow(token, displayId)); } for (int i = 0; i < NUM_APP_WINDOWS; i++) { final IWindow token = addAccessibilityInteractionConnection(displayId, false, USER_SYSTEM_ID); - addWindowInfo(windowInfosForDisplay, token, layer++); + windowsForDisplay.add(createMockAccessibilityWindow(token, displayId)); } // Sets up current focused window of display. // Each display has its own current focused window if config_perDisplayFocusEnabled is true. // Otherwise only default display needs to current focused window. if (mSupportPerDisplayFocus || displayId == Display.DEFAULT_DISPLAY) { - windowInfosForDisplay.get(DEFAULT_FOCUSED_INDEX).focused = true; + windowsForDisplay.get(DEFAULT_FOCUSED_INDEX).getWindowInfo().focused = true; } // Turns on windows tracking, and update window info. mA11yWindowManager.startTrackingWindows(displayId, false); // Puts window lists into array. - mWindowInfos.put(displayId, windowInfosForDisplay); + mWindows.put(displayId, windowsForDisplay); // Sets the default display is the top focused display and // its current focused window is the top focused window. if (displayId == Display.DEFAULT_DISPLAY) { setTopFocusedWindowAndDisplay(displayId, DEFAULT_FOCUSED_INDEX); } // Invokes callback for sending window lists to A11y framework. - onWindowsForAccessibilityChanged(displayId, FORCE_SEND); + onAccessibilityWindowsChanged(displayId, FORCE_SEND); assertEquals(mA11yWindowManager.getWindowListLocked(displayId).size(), - windowInfosForDisplay.size()); + windowsForDisplay.size()); } private WindowsForAccessibilityCallback getWindowsForAccessibilityCallbacks(int displayId) { ArgumentCaptor windowsForAccessibilityCallbacksCaptor = ArgumentCaptor.forClass( - WindowManagerInternal.WindowsForAccessibilityCallback.class); + WindowsForAccessibilityCallback.class); verify(mMockWindowManagerInternal) .setWindowsForAccessibilityCallback(eq(displayId), windowsForAccessibilityCallbacksCaptor.capture()); @@ -1106,36 +1240,28 @@ public class AccessibilityWindowManagerTest { return windowId; } - private void addWindowInfo(ArrayList windowInfos, IWindow windowToken, int layer) { - final WindowInfo windowInfo = WindowInfo.obtain(); - windowInfo.type = AccessibilityWindowInfo.TYPE_APPLICATION; - windowInfo.token = windowToken.asBinder(); - windowInfo.layer = layer; - windowInfo.regionInScreen.set(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); - windowInfos.add(windowInfo); - } - private int getWindowIdFromWindowInfosForDisplay(int displayId, int index) { - final IBinder windowToken = mWindowInfos.get(displayId).get(index).token; + final IBinder windowToken = mWindows.get(displayId).get(index).getWindowInfo().token; return mA11yWindowManager.findWindowIdLocked( USER_SYSTEM_ID, windowToken); } private void setTopFocusedWindowAndDisplay(int displayId, int index) { // Sets the top focus window. - mTopFocusedWindowToken = mWindowInfos.get(displayId).get(index).token; + mTopFocusedWindowToken = mWindows.get(displayId).get(index).getWindowInfo().token; // Sets the top focused display. mTopFocusedDisplayId = displayId; } - private void onWindowsForAccessibilityChanged(int displayId, boolean forceSend) { + private void onAccessibilityWindowsChanged(int displayId, boolean forceSend) { WindowsForAccessibilityCallback callbacks = mCallbackOfWindows.get(displayId); if (callbacks == null) { callbacks = getWindowsForAccessibilityCallbacks(displayId); mCallbackOfWindows.put(displayId, callbacks); } - callbacks.onWindowsForAccessibilityChanged(forceSend, mTopFocusedDisplayId, - mTopFocusedWindowToken, mWindowInfos.get(displayId)); + callbacks.onAccessibilityWindowsChanged(forceSend, mTopFocusedDisplayId, + mTopFocusedWindowToken, new Point(SCREEN_WIDTH, SCREEN_HEIGHT), + mWindows.get(displayId)); } private void changeFocusedWindowOnDisplayPerDisplayFocusConfig( @@ -1144,23 +1270,23 @@ public class AccessibilityWindowManagerTest { if (mSupportPerDisplayFocus) { // Gets the old focused window of display which wants to change focused window. WindowInfo focusedWindowInfo = - mWindowInfos.get(changeFocusedDisplayId).get(oldFocusedWindowIndex); + mWindows.get(changeFocusedDisplayId).get(oldFocusedWindowIndex).getWindowInfo(); // Resets the focus of old focused window. focusedWindowInfo.focused = false; // Gets the new window of display which wants to change focused window. focusedWindowInfo = - mWindowInfos.get(changeFocusedDisplayId).get(newFocusedWindowIndex); + mWindows.get(changeFocusedDisplayId).get(newFocusedWindowIndex).getWindowInfo(); // Sets the focus of new focused window. focusedWindowInfo.focused = true; } else { // Gets the window of display which wants to change focused window. WindowInfo focusedWindowInfo = - mWindowInfos.get(changeFocusedDisplayId).get(newFocusedWindowIndex); + mWindows.get(changeFocusedDisplayId).get(newFocusedWindowIndex).getWindowInfo(); // Sets the focus of new focused window. focusedWindowInfo.focused = true; // Gets the old focused window of old top focused display. focusedWindowInfo = - mWindowInfos.get(oldTopFocusedDisplayId).get(oldFocusedWindowIndex); + mWindows.get(oldTopFocusedDisplayId).get(oldFocusedWindowIndex).getWindowInfo(); // Resets the focus of old focused window. focusedWindowInfo.focused = false; // Changes the top focused display and window. @@ -1168,6 +1294,39 @@ public class AccessibilityWindowManagerTest { } } + private AccessibilityWindow createMockAccessibilityWindow(IWindow windowToken, int displayId) { + final WindowInfo windowInfo = WindowInfo.obtain(); + windowInfo.type = WindowManager.LayoutParams.TYPE_APPLICATION; + windowInfo.token = windowToken.asBinder(); + + final AccessibilityWindow window = Mockito.mock(AccessibilityWindow.class); + when(window.getWindowInfo()).thenReturn(windowInfo); + when(window.isFocused()).thenAnswer(invocation -> windowInfo.focused); + when(window.isTouchable()).thenReturn(true); + when(window.getType()).thenReturn(windowInfo.type); + + setRegionForMockAccessibilityWindow(window, nextToucableRegion(displayId)); + return window; + } + + private void setRegionForMockAccessibilityWindow(AccessibilityWindow window, Region region) { + doAnswer(invocation -> { + ((Region) invocation.getArgument(0)).set(region); + return null; + }).when(window).getTouchableRegionInScreen(any(Region.class)); + doAnswer(invocation -> { + ((Region) invocation.getArgument(0)).set(region); + return null; + }).when(window).getTouchableRegionInWindow(any(Region.class)); + } + + private Region nextToucableRegion(int displayId) { + final int topLeft = mNextRegionOffsets.get(displayId, 0); + final int bottomRight = topLeft + 100; + mNextRegionOffsets.put(displayId, topLeft + 10); + return new Region(topLeft, topLeft, bottomRight, bottomRight); + } + @Nullable private static String toString(@Nullable CharSequence cs) { return cs == null ? null : cs.toString(); @@ -1196,16 +1355,16 @@ public class AccessibilityWindowManagerTest { } } - static class WindowIdMatcher extends TypeSafeMatcher { + static class EventWindowIdMatcher extends TypeSafeMatcher { private int mWindowId; - WindowIdMatcher(int windowId) { + EventWindowIdMatcher(int windowId) { super(); mWindowId = windowId; } - static WindowIdMatcher a11yWindowId(int windowId) { - return new WindowIdMatcher(windowId); + static EventWindowIdMatcher eventWindowId(int windowId) { + return new EventWindowIdMatcher(windowId); } @Override @@ -1241,5 +1400,27 @@ public class AccessibilityWindowManagerTest { description.appendText("Matching to window changes " + mWindowChanges); } } + + static class WindowIdMatcher extends TypeSafeMatcher { + private final int mWindowId; + + WindowIdMatcher(int windowId) { + super(); + mWindowId = windowId; + } + + static WindowIdMatcher windowId(int windowId) { + return new WindowIdMatcher(windowId); + } + + @Override + protected boolean matchesSafely(AccessibilityWindowInfo window) { + return window.getId() == mWindowId; + } + + @Override + public void describeTo(Description description) { + description.appendText("Matching to windowId " + mWindowId); + } + } } -// LINT.ThenChange(/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerWithAccessibilityWindowTest.java) diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerWithAccessibilityWindowTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerWithAccessibilityWindowTest.java deleted file mode 100644 index 19041451c8eb..000000000000 --- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerWithAccessibilityWindowTest.java +++ /dev/null @@ -1,1444 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.accessibility; - -import static com.android.server.accessibility.AbstractAccessibilityServiceConnection.DISPLAY_TYPE_DEFAULT; -import static com.android.server.accessibility.AccessibilityWindowManagerWithAccessibilityWindowTest.DisplayIdMatcher.displayId; -import static com.android.server.accessibility.AccessibilityWindowManagerWithAccessibilityWindowTest.WindowIdMatcher.windowId; -import static com.android.server.accessibility.AccessibilityWindowManagerWithAccessibilityWindowTest.WindowChangesMatcher.a11yWindowChanges; -import static com.android.server.accessibility.AccessibilityWindowManagerWithAccessibilityWindowTest.EventWindowIdMatcher.eventWindowId; - -import static junit.framework.Assert.assertFalse; -import static junit.framework.Assert.assertNotNull; -import static junit.framework.Assert.assertNull; -import static junit.framework.Assert.assertTrue; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.allOf; -import static org.hamcrest.Matchers.hasItem; -import static org.hamcrest.Matchers.hasSize; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.not; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.ArgumentMatchers.isNull; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.annotation.Nullable; -import android.graphics.Point; -import android.graphics.Rect; -import android.graphics.Region; -import android.os.IBinder; -import android.os.LocaleList; -import android.os.RemoteException; -import android.os.UserHandle; -import android.platform.test.annotations.RequiresFlagsEnabled; -import android.platform.test.flag.junit.CheckFlagsRule; -import android.platform.test.flag.junit.DeviceFlagsValueProvider; -import android.util.SparseArray; -import android.view.Display; -import android.view.IWindow; -import android.view.WindowInfo; -import android.view.WindowManager; -import android.view.accessibility.AccessibilityEvent; -import android.view.accessibility.AccessibilityNodeInfo; -import android.view.accessibility.AccessibilityWindowAttributes; -import android.view.accessibility.AccessibilityWindowInfo; -import android.view.accessibility.IAccessibilityInteractionConnection; - -import com.android.server.accessibility.AccessibilityWindowManager.RemoteAccessibilityConnection; -import com.android.server.accessibility.test.MessageCapturingHandler; -import com.android.server.wm.AccessibilityWindowsPopulator.AccessibilityWindow; -import com.android.server.wm.WindowManagerInternal; -import com.android.server.wm.WindowManagerInternal.WindowsForAccessibilityCallback; - -import org.hamcrest.Description; -import org.hamcrest.TypeSafeMatcher; -import org.junit.After; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.Mockito; -import org.mockito.MockitoAnnotations; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -/** - * Tests for the AccessibilityWindowManager with Flags.FLAG_COMPUTE_WINDOW_CHANGES_ON_A11Y_V2 - * TODO(b/322444245): Merge with AccessibilityWindowManagerTest - * after completing the flag migration. - */ -@RequiresFlagsEnabled(Flags.FLAG_COMPUTE_WINDOW_CHANGES_ON_A11Y_V2) -public class AccessibilityWindowManagerWithAccessibilityWindowTest { - private static final String PACKAGE_NAME = "com.android.server.accessibility"; - private static final boolean FORCE_SEND = true; - private static final boolean SEND_ON_WINDOW_CHANGES = false; - private static final int USER_SYSTEM_ID = UserHandle.USER_SYSTEM; - private static final int USER_PROFILE = 11; - private static final int USER_PROFILE_PARENT = 1; - private static final int SECONDARY_DISPLAY_ID = Display.DEFAULT_DISPLAY + 1; - private static final int NUM_GLOBAL_WINDOWS = 4; - private static final int NUM_APP_WINDOWS = 4; - private static final int NUM_OF_WINDOWS = (NUM_GLOBAL_WINDOWS + NUM_APP_WINDOWS); - private static final int DEFAULT_FOCUSED_INDEX = 1; - private static final int SCREEN_WIDTH = 1080; - private static final int SCREEN_HEIGHT = 1920; - private static final int INVALID_ID = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID; - private static final int HOST_WINDOW_ID = 10; - private static final int EMBEDDED_WINDOW_ID = 11; - private static final int OTHER_WINDOW_ID = 12; - - private AccessibilityWindowManager mA11yWindowManager; - // Window manager will support multiple focused window if config_perDisplayFocusEnabled is true, - // i.e., each display would have its current focused window, and one of all focused windows - // would be top focused window. Otherwise, window manager only supports one focused window - // at all displays, and that focused window would be top focused window. - private boolean mSupportPerDisplayFocus = false; - private int mTopFocusedDisplayId = Display.INVALID_DISPLAY; - private IBinder mTopFocusedWindowToken = null; - - // List of window token, mapping from windowId -> window token. - private final SparseArray mA11yWindowTokens = new SparseArray<>(); - // List of window info lists, mapping from displayId -> a11y window lists. - private final SparseArray> mWindows = new SparseArray<>(); - // List of callback, mapping from displayId -> callback. - private final SparseArray mCallbackOfWindows = - new SparseArray<>(); - // List of display ID. - private final ArrayList mExpectedDisplayList = new ArrayList<>(Arrays.asList( - Display.DEFAULT_DISPLAY, SECONDARY_DISPLAY_ID)); - - private final MessageCapturingHandler mHandler = new MessageCapturingHandler(null); - - // This maps displayId -> next region offset. - // Touchable region must have un-occluded area so that it's exposed to a11y services. - // This offset can be used as left and top of new region so that top-left of each region are - // kept visible. - // It's expected to be incremented by some amount everytime the value is used. - private final SparseArray mNextRegionOffsets = new SparseArray<>(); - - @Mock - private WindowManagerInternal mMockWindowManagerInternal; - @Mock - private AccessibilityWindowManager.AccessibilityEventSender mMockA11yEventSender; - @Mock - private AccessibilitySecurityPolicy mMockA11ySecurityPolicy; - @Mock - private AccessibilitySecurityPolicy.AccessibilityUserManager mMockA11yUserManager; - @Mock - private AccessibilityTraceManager mMockA11yTraceManager; - - @Mock - private IBinder mMockHostToken; - @Mock - private IBinder mMockEmbeddedToken; - @Mock - private IBinder mMockInvalidToken; - - @Rule - public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); - - @Before - public void setUp() throws RemoteException { - MockitoAnnotations.initMocks(this); - when(mMockA11yUserManager.getCurrentUserIdLocked()).thenReturn(USER_SYSTEM_ID); - when(mMockA11ySecurityPolicy.resolveCallingUserIdEnforcingPermissionsLocked( - USER_PROFILE)).thenReturn(USER_PROFILE_PARENT); - when(mMockA11ySecurityPolicy.resolveCallingUserIdEnforcingPermissionsLocked( - USER_SYSTEM_ID)).thenReturn(USER_SYSTEM_ID); - when(mMockA11ySecurityPolicy.resolveValidReportedPackageLocked( - anyString(), anyInt(), anyInt(), anyInt())).thenReturn(PACKAGE_NAME); - - doAnswer((invocation) -> { - onAccessibilityWindowsChanged(invocation.getArgument(0), false); - return null; - }).when(mMockWindowManagerInternal).computeWindowsForAccessibility(anyInt()); - - mA11yWindowManager = new AccessibilityWindowManager(new Object(), mHandler, - mMockWindowManagerInternal, - mMockA11yEventSender, - mMockA11ySecurityPolicy, - mMockA11yUserManager, - mMockA11yTraceManager); - // Starts tracking window of default display and sets the default display - // as top focused display before each testing starts. - startTrackingPerDisplay(Display.DEFAULT_DISPLAY); - - // AccessibilityEventSender is invoked during onAccessibilityWindowsChanged. - // Resets it for mockito verify of further test case. - Mockito.reset(mMockA11yEventSender); - - registerLeashedTokenAndWindowId(); - } - - @After - public void tearDown() { - mHandler.removeAllMessages(); - } - - @Test - public void startTrackingWindows_shouldEnableWindowManagerCallback() { - // AccessibilityWindowManager#startTrackingWindows already invoked in setup. - assertTrue(mA11yWindowManager.isTrackingWindowsLocked(Display.DEFAULT_DISPLAY)); - final WindowsForAccessibilityCallback callbacks = - mCallbackOfWindows.get(Display.DEFAULT_DISPLAY); - verify(mMockWindowManagerInternal).setWindowsForAccessibilityCallback( - eq(Display.DEFAULT_DISPLAY), eq(callbacks)); - } - - @Test - public void stopTrackingWindows_shouldDisableWindowManagerCallback() { - assertTrue(mA11yWindowManager.isTrackingWindowsLocked(Display.DEFAULT_DISPLAY)); - Mockito.reset(mMockWindowManagerInternal); - - mA11yWindowManager.stopTrackingWindows(Display.DEFAULT_DISPLAY); - assertFalse(mA11yWindowManager.isTrackingWindowsLocked(Display.DEFAULT_DISPLAY)); - verify(mMockWindowManagerInternal).setWindowsForAccessibilityCallback( - eq(Display.DEFAULT_DISPLAY), isNull()); - - } - - @Test - public void stopTrackingWindows_shouldClearWindows() { - assertTrue(mA11yWindowManager.isTrackingWindowsLocked(Display.DEFAULT_DISPLAY)); - final int activeWindowId = mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID); - - mA11yWindowManager.stopTrackingWindows(Display.DEFAULT_DISPLAY); - assertNull(mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY)); - assertEquals(mA11yWindowManager.getFocusedWindowId(AccessibilityNodeInfo.FOCUS_INPUT), - AccessibilityWindowInfo.UNDEFINED_WINDOW_ID); - assertEquals(mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID), - activeWindowId); - } - - @Test - public void stopTrackingWindows_onNonTopFocusedDisplay_shouldNotResetTopFocusWindow() - throws RemoteException { - // At setup, the default display sets be the top focused display and - // its current focused window sets be the top focused window. - // Starts tracking window of second display. - startTrackingPerDisplay(SECONDARY_DISPLAY_ID); - assertTrue(mA11yWindowManager.isTrackingWindowsLocked(SECONDARY_DISPLAY_ID)); - // Stops tracking windows of second display. - mA11yWindowManager.stopTrackingWindows(SECONDARY_DISPLAY_ID); - assertNotEquals(mA11yWindowManager.getFocusedWindowId(AccessibilityNodeInfo.FOCUS_INPUT), - AccessibilityWindowInfo.UNDEFINED_WINDOW_ID); - } - - @Test - public void onWindowsChanged_duringTouchInteractAndFocusChange_shouldChangeActiveWindow() { - final int activeWindowId = mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID); - final WindowInfo focusedWindowInfo = - mWindows.get(Display.DEFAULT_DISPLAY).get(DEFAULT_FOCUSED_INDEX).getWindowInfo(); - assertEquals(activeWindowId, mA11yWindowManager.findWindowIdLocked( - USER_SYSTEM_ID, focusedWindowInfo.token)); - - focusedWindowInfo.focused = false; - mWindows.get(Display.DEFAULT_DISPLAY).get( - DEFAULT_FOCUSED_INDEX + 1).getWindowInfo().focused = true; - - mA11yWindowManager.onTouchInteractionStart(); - setTopFocusedWindowAndDisplay(Display.DEFAULT_DISPLAY, DEFAULT_FOCUSED_INDEX + 1); - onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); - - assertNotEquals(activeWindowId, mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID)); - } - - @Test - public void - onWindowsChanged_focusChangeOnNonTopFocusedDisplay_perDisplayFocusOn_notChangeWindow() - throws RemoteException { - // At setup, the default display sets be the top focused display and - // its current focused window sets be the top focused window. - // Sets supporting multiple focused window, i.e., config_perDisplayFocusEnabled is true. - mSupportPerDisplayFocus = true; - // Starts tracking window of second display. - startTrackingPerDisplay(SECONDARY_DISPLAY_ID); - // Gets the active window. - final int activeWindowId = mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID); - // Gets the top focused window. - final int topFocusedWindowId = - mA11yWindowManager.getFocusedWindowId(AccessibilityNodeInfo.FOCUS_INPUT); - // Changes the current focused window at second display. - changeFocusedWindowOnDisplayPerDisplayFocusConfig(SECONDARY_DISPLAY_ID, - DEFAULT_FOCUSED_INDEX + 1, Display.DEFAULT_DISPLAY, DEFAULT_FOCUSED_INDEX); - - onAccessibilityWindowsChanged(SECONDARY_DISPLAY_ID, SEND_ON_WINDOW_CHANGES); - // The active window should not be changed. - assertEquals(activeWindowId, mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID)); - // The top focused window should not be changed. - assertEquals(topFocusedWindowId, - mA11yWindowManager.getFocusedWindowId(AccessibilityNodeInfo.FOCUS_INPUT)); - } - - @Test - public void - onWindowChange_focusChangeToNonTopFocusedDisplay_perDisplayFocusOff_shouldChangeWindow() - throws RemoteException { - // At setup, the default display sets be the top focused display and - // its current focused window sets be the top focused window. - // Sets not supporting multiple focused window, i.e., config_perDisplayFocusEnabled is - // false. - mSupportPerDisplayFocus = false; - // Starts tracking window of second display. - startTrackingPerDisplay(SECONDARY_DISPLAY_ID); - // Gets the active window. - final int activeWindowId = mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID); - // Gets the top focused window. - final int topFocusedWindowId = - mA11yWindowManager.getFocusedWindowId(AccessibilityNodeInfo.FOCUS_INPUT); - // Changes the current focused window from default display to second display. - changeFocusedWindowOnDisplayPerDisplayFocusConfig(SECONDARY_DISPLAY_ID, - DEFAULT_FOCUSED_INDEX, Display.DEFAULT_DISPLAY, DEFAULT_FOCUSED_INDEX); - - onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); - onAccessibilityWindowsChanged(SECONDARY_DISPLAY_ID, SEND_ON_WINDOW_CHANGES); - // The active window should be changed. - assertNotEquals(activeWindowId, mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID)); - // The top focused window should be changed. - assertNotEquals(topFocusedWindowId, - mA11yWindowManager.getFocusedWindowId(AccessibilityNodeInfo.FOCUS_INPUT)); - } - - @Test - public void onWindowsChanged_shouldReportCorrectLayer() { - // AccessibilityWindowManager#onAccessibilityWindowsChanged already invoked in setup. - List a11yWindows = - mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY); - for (int i = 0; i < a11yWindows.size(); i++) { - final AccessibilityWindowInfo a11yWindow = a11yWindows.get(i); - assertThat(mWindows.get(Display.DEFAULT_DISPLAY).size() - i - 1, - is(a11yWindow.getLayer())); - } - } - - @Test - public void onWindowsChanged_shouldReportCorrectOrder() { - // AccessibilityWindowManager#onAccessibilityWindowsChanged already invoked in setup. - List a11yWindows = - mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY); - for (int i = 0; i < a11yWindows.size(); i++) { - final AccessibilityWindowInfo a11yWindow = a11yWindows.get(i); - final IBinder windowToken = mA11yWindowManager - .getWindowTokenForUserAndWindowIdLocked(USER_SYSTEM_ID, a11yWindow.getId()); - final WindowInfo windowInfo = mWindows.get(Display.DEFAULT_DISPLAY) - .get(i).getWindowInfo(); - assertThat(windowToken, is(windowInfo.token)); - } - } - - @Test - public void onWindowsChanged_shouldNotReportNonTouchableWindow() { - final AccessibilityWindow window = mWindows.get(Display.DEFAULT_DISPLAY).get(0); - when(window.isTouchable()).thenReturn(false); - final int windowId = mA11yWindowManager.findWindowIdLocked( - USER_SYSTEM_ID, window.getWindowInfo().token); - - onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); - - final List a11yWindows = - mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY); - assertThat(a11yWindows, not(hasItem(windowId(windowId)))); - } - - @Test - public void onWindowsChanged_shouldReportFocusedNonTouchableWindow() { - final AccessibilityWindow window = mWindows.get(Display.DEFAULT_DISPLAY).get( - DEFAULT_FOCUSED_INDEX); - when(window.isTouchable()).thenReturn(false); - final int windowId = mA11yWindowManager.findWindowIdLocked( - USER_SYSTEM_ID, window.getWindowInfo().token); - - onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); - - final List a11yWindows = - mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY); - assertThat(a11yWindows, hasItem(windowId(windowId))); - } - - @Test - public void onWindowsChanged_trustedFocusedNonTouchableWindow_shouldNotHideWindowsBelow() { - // Make the focused trusted un-touchable window fullscreen. - final AccessibilityWindow window = mWindows.get(Display.DEFAULT_DISPLAY).get( - DEFAULT_FOCUSED_INDEX); - setRegionForMockAccessibilityWindow(window, new Region(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT)); - when(window.isTouchable()).thenReturn(false); - when(window.isTrustedOverlay()).thenReturn(true); - - onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); - - final List a11yWindows = - mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY); - assertThat(a11yWindows, hasSize(NUM_OF_WINDOWS)); - } - - @Test - public void onWindowsChanged_accessibilityOverlay_shouldNotHideWindowsBelow() { - // Make the a11y overlay window fullscreen. - final AccessibilityWindow window = mWindows.get(Display.DEFAULT_DISPLAY).get(0); - setRegionForMockAccessibilityWindow(window, new Region(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT)); - when(window.getType()).thenReturn(WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY); - - onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); - - final List a11yWindows = - mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY); - assertThat(a11yWindows, hasSize(NUM_OF_WINDOWS)); - } - - @Test - public void onWindowsChanged_shouldReportFocusedWindowEvenIfOccluded() { - // Make the front window fullscreen. - final AccessibilityWindow frontWindow = mWindows.get(Display.DEFAULT_DISPLAY).get(0); - setRegionForMockAccessibilityWindow(frontWindow, - new Region(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT)); - final int frontWindowId = mA11yWindowManager.findWindowIdLocked( - USER_SYSTEM_ID, frontWindow.getWindowInfo().token); - - final AccessibilityWindow focusedWindow = mWindows.get(Display.DEFAULT_DISPLAY).get( - DEFAULT_FOCUSED_INDEX); - final int focusedWindowId = mA11yWindowManager.findWindowIdLocked( - USER_SYSTEM_ID, focusedWindow.getWindowInfo().token); - - onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); - - final List a11yWindows = - mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY); - assertThat(a11yWindows, hasSize(2)); - assertThat(a11yWindows.get(0), windowId(frontWindowId)); - assertThat(a11yWindows.get(1), windowId(focusedWindowId)); - } - - @Test - public void onWindowsChanged_embeddedWindows_shouldOnlyReportHost() throws RemoteException { - final Rect embeddingBounds = new Rect(0, 0, 200, 100); - - // The embedded window comes front of the host window. - final IBinder embeddedWindowLeashToken = Mockito.mock(IBinder.class); - final int embeddedWindowId = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY, - false, embeddedWindowLeashToken, USER_SYSTEM_ID); - final AccessibilityWindow embeddedWindow = createMockAccessibilityWindow( - mA11yWindowTokens.get(embeddedWindowId), Display.DEFAULT_DISPLAY); - setRegionForMockAccessibilityWindow(embeddedWindow, new Region(embeddingBounds)); - mWindows.get(Display.DEFAULT_DISPLAY).set(0, embeddedWindow); - - final IBinder hostWindowLeashToken = Mockito.mock(IBinder.class); - final int hostWindowId = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY, - false, hostWindowLeashToken, USER_SYSTEM_ID); - final AccessibilityWindow hostWindow = createMockAccessibilityWindow( - mA11yWindowTokens.get(hostWindowId), Display.DEFAULT_DISPLAY); - setRegionForMockAccessibilityWindow(hostWindow, new Region(embeddingBounds)); - mWindows.get(Display.DEFAULT_DISPLAY).set(1, hostWindow); - - mA11yWindowManager.associateEmbeddedHierarchyLocked( - hostWindowLeashToken, embeddedWindowLeashToken); - - onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); - - final List a11yWindows = - mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY); - assertThat(a11yWindows, not(hasItem(windowId(embeddedWindowId)))); - assertThat(a11yWindows.get(0), windowId(hostWindowId)); - final Rect bounds = new Rect(); - a11yWindows.get(0).getBoundsInScreen(bounds); - assertEquals(bounds, embeddingBounds); - } - - @Test - public void onWindowsChanged_shouldNotReportfullyOccludedWindow() { - final AccessibilityWindow frontWindow = mWindows.get(Display.DEFAULT_DISPLAY).get(0); - setRegionForMockAccessibilityWindow(frontWindow, new Region(100, 100, 300, 300)); - final int frontWindowId = mA11yWindowManager.findWindowIdLocked( - USER_SYSTEM_ID, frontWindow.getWindowInfo().token); - - // index 1 is focused. Let's use the next one for this test. - final AccessibilityWindow occludedWindow = mWindows.get(Display.DEFAULT_DISPLAY).get(2); - setRegionForMockAccessibilityWindow(occludedWindow, new Region(150, 150, 250, 250)); - final int occludedWindowId = mA11yWindowManager.findWindowIdLocked( - USER_SYSTEM_ID, occludedWindow.getWindowInfo().token); - - onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); - - final List a11yWindows = - mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY); - assertThat(a11yWindows, hasItem(windowId(frontWindowId))); - assertThat(a11yWindows, not(hasItem(windowId(occludedWindowId)))); - } - - @Test - public void onWindowsChangedAndForceSend_shouldUpdateWindows() { - assertNotEquals("new title", - toString(mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY) - .get(0).getTitle())); - - mWindows.get(Display.DEFAULT_DISPLAY).get(0).getWindowInfo().title = "new title"; - - onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, FORCE_SEND); - assertEquals("new title", - toString(mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY) - .get(0).getTitle())); - } - - @Test - public void onWindowsChangedNoForceSend_windowChanged_shouldUpdateWindows() - throws RemoteException { - final AccessibilityWindowInfo oldWindow = - mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY).get(0); - final IWindow token = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY, - true, USER_SYSTEM_ID); - mWindows.get(Display.DEFAULT_DISPLAY).set(0, - createMockAccessibilityWindow(token, Display.DEFAULT_DISPLAY)); - - onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); - assertNotEquals(oldWindow, - mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY).get(0)); - } - - @Test - public void onWindowsChangedNoForceSend_focusChanged_shouldUpdateWindows() { - final WindowInfo focusedWindowInfo = - mWindows.get(Display.DEFAULT_DISPLAY).get(DEFAULT_FOCUSED_INDEX).getWindowInfo(); - final WindowInfo windowInfo = mWindows.get(Display.DEFAULT_DISPLAY).get(0).getWindowInfo(); - focusedWindowInfo.focused = false; - windowInfo.focused = true; - - onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); - assertTrue(mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY).get(0) - .isFocused()); - } - - @Test - public void removeAccessibilityInteractionConnection_byWindowToken_shouldRemoved() { - for (int i = 0; i < NUM_OF_WINDOWS; i++) { - final int windowId = mA11yWindowTokens.keyAt(i); - final IWindow windowToken = mA11yWindowTokens.valueAt(i); - assertNotNull(mA11yWindowManager.getConnectionLocked(USER_SYSTEM_ID, windowId)); - - mA11yWindowManager.removeAccessibilityInteractionConnection(windowToken); - assertNull(mA11yWindowManager.getConnectionLocked(USER_SYSTEM_ID, windowId)); - } - } - - @Test - public void remoteAccessibilityConnection_binderDied_shouldRemoveConnection() { - for (int i = 0; i < NUM_OF_WINDOWS; i++) { - final int windowId = mA11yWindowTokens.keyAt(i); - final RemoteAccessibilityConnection remoteA11yConnection = - mA11yWindowManager.getConnectionLocked(USER_SYSTEM_ID, windowId); - assertNotNull(remoteA11yConnection); - - remoteA11yConnection.binderDied(); - assertNull(mA11yWindowManager.getConnectionLocked(USER_SYSTEM_ID, windowId)); - } - } - - @Test - public void getWindowTokenForUserAndWindowId_shouldNotNull() { - final List windows = - mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY); - for (int i = 0; i < windows.size(); i++) { - final int windowId = windows.get(i).getId(); - - assertNotNull(mA11yWindowManager.getWindowTokenForUserAndWindowIdLocked( - USER_SYSTEM_ID, windowId)); - } - } - - @Test - public void findWindowId() { - final List windows = - mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY); - for (int i = 0; i < windows.size(); i++) { - final int windowId = windows.get(i).getId(); - final IBinder windowToken = mA11yWindowManager.getWindowTokenForUserAndWindowIdLocked( - USER_SYSTEM_ID, windowId); - - assertEquals(mA11yWindowManager.findWindowIdLocked( - USER_SYSTEM_ID, windowToken), windowId); - } - } - - @Test - public void resolveParentWindowId_windowIsNotEmbedded_shouldReturnGivenId() - throws RemoteException { - final int windowId = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY, false, - Mockito.mock(IBinder.class), USER_SYSTEM_ID); - assertEquals(windowId, mA11yWindowManager.resolveParentWindowIdLocked(windowId)); - } - - @Test - public void resolveParentWindowId_windowIsNotRegistered_shouldReturnGivenId() { - final int windowId = -1; - assertEquals(windowId, mA11yWindowManager.resolveParentWindowIdLocked(windowId)); - } - - @Test - public void resolveParentWindowId_windowIsAssociated_shouldReturnParentWindowId() - throws RemoteException { - final IBinder mockHostToken = Mockito.mock(IBinder.class); - final IBinder mockEmbeddedToken = Mockito.mock(IBinder.class); - final int hostWindowId = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY, - false, mockHostToken, USER_SYSTEM_ID); - final int embeddedWindowId = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY, - false, mockEmbeddedToken, USER_SYSTEM_ID); - - mA11yWindowManager.associateEmbeddedHierarchyLocked(mockHostToken, mockEmbeddedToken); - - final int resolvedWindowId = mA11yWindowManager.resolveParentWindowIdLocked( - embeddedWindowId); - assertEquals(hostWindowId, resolvedWindowId); - } - - @Test - public void resolveParentWindowId_windowIsDisassociated_shouldReturnGivenId() - throws RemoteException { - final IBinder mockHostToken = Mockito.mock(IBinder.class); - final IBinder mockEmbeddedToken = Mockito.mock(IBinder.class); - final int hostWindowId = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY, - false, mockHostToken, USER_SYSTEM_ID); - final int embeddedWindowId = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY, - false, mockEmbeddedToken, USER_SYSTEM_ID); - - mA11yWindowManager.associateEmbeddedHierarchyLocked(mockHostToken, mockEmbeddedToken); - mA11yWindowManager.disassociateEmbeddedHierarchyLocked(mockEmbeddedToken); - - final int resolvedWindowId = mA11yWindowManager.resolveParentWindowIdLocked( - embeddedWindowId); - assertNotEquals(hostWindowId, resolvedWindowId); - assertEquals(embeddedWindowId, resolvedWindowId); - } - - @Test - public void computePartialInteractiveRegionForWindow_wholeVisible_returnWholeRegion() { - // Updates top 2 z-order WindowInfo are whole visible. - final AccessibilityWindow firstWindow = mWindows.get(Display.DEFAULT_DISPLAY).get(0); - setRegionForMockAccessibilityWindow(firstWindow, - new Region(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT / 2)); - final AccessibilityWindow secondWindow = mWindows.get(Display.DEFAULT_DISPLAY).get(1); - setRegionForMockAccessibilityWindow(secondWindow, - new Region(0, SCREEN_HEIGHT / 2, SCREEN_WIDTH, SCREEN_HEIGHT)); - - onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); - - final List a11yWindows = - mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY); - assertThat(a11yWindows, hasSize(2)); - final Region outBounds = new Region(); - int windowId = a11yWindows.get(0).getId(); - - mA11yWindowManager.computePartialInteractiveRegionForWindowLocked(windowId, outBounds); - assertThat(outBounds.getBounds().width(), is(SCREEN_WIDTH)); - assertThat(outBounds.getBounds().height(), is(SCREEN_HEIGHT / 2)); - - windowId = a11yWindows.get(1).getId(); - - mA11yWindowManager.computePartialInteractiveRegionForWindowLocked(windowId, outBounds); - assertThat(outBounds.getBounds().width(), is(SCREEN_WIDTH)); - assertThat(outBounds.getBounds().height(), is(SCREEN_HEIGHT / 2)); - } - - @Test - public void computePartialInteractiveRegionForWindow_halfVisible_returnHalfRegion() { - // Updates z-order #1 WindowInfo is half visible. - final AccessibilityWindow firstWindow = mWindows.get(Display.DEFAULT_DISPLAY).get(0); - setRegionForMockAccessibilityWindow(firstWindow, - new Region(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT / 2)); - final AccessibilityWindow secondWindow = mWindows.get(Display.DEFAULT_DISPLAY).get(1); - setRegionForMockAccessibilityWindow(secondWindow, - new Region(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT)); - - onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); - final List a11yWindows = - mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY); - assertThat(a11yWindows, hasSize(2)); - final Region outBounds = new Region(); - int windowId = a11yWindows.get(1).getId(); - - mA11yWindowManager.computePartialInteractiveRegionForWindowLocked(windowId, outBounds); - assertThat(outBounds.getBounds().width(), is(SCREEN_WIDTH)); - assertThat(outBounds.getBounds().height(), is(SCREEN_HEIGHT / 2)); - } - - @Test - public void computePartialInteractiveRegionForWindow_notVisible_returnEmptyRegion() { - // z-order #0 WindowInfo is full screen, z-order #1 WindowInfo should be invisible. - final AccessibilityWindow firstWindow = mWindows.get(Display.DEFAULT_DISPLAY).get(0); - setRegionForMockAccessibilityWindow(firstWindow, - new Region(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT)); - - onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); - - final List a11yWindows = - mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY); - // Note that the second window is also exposed even if region is empty because it's focused. - assertThat(a11yWindows, hasSize(2)); - final Region outBounds = new Region(); - int windowId = a11yWindows.get(1).getId(); - - mA11yWindowManager.computePartialInteractiveRegionForWindowLocked(windowId, outBounds); - assertTrue(outBounds.getBounds().isEmpty()); - } - - @Test - public void computePartialInteractiveRegionForWindow_partialVisible_returnVisibleRegion() { - // Updates z-order #0 WindowInfo to have two interact-able areas. - final Region region = new Region(0, 0, SCREEN_WIDTH, 200); - region.op(0, SCREEN_HEIGHT - 200, SCREEN_WIDTH, SCREEN_HEIGHT, Region.Op.UNION); - final AccessibilityWindow firstWindow = mWindows.get(Display.DEFAULT_DISPLAY).get(0); - setRegionForMockAccessibilityWindow(firstWindow, region); - final AccessibilityWindow secondWindow = mWindows.get(Display.DEFAULT_DISPLAY).get(1); - setRegionForMockAccessibilityWindow(secondWindow, - new Region(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT)); - - onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); - - final List a11yWindows = - mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY); - assertThat(a11yWindows, hasSize(2)); - final Region outBounds = new Region(); - final int windowId = a11yWindows.get(1).getId(); - - mA11yWindowManager.computePartialInteractiveRegionForWindowLocked(windowId, outBounds); - assertFalse(outBounds.getBounds().isEmpty()); - assertThat(outBounds.getBounds().width(), is(SCREEN_WIDTH)); - assertThat(outBounds.getBounds().height(), is(SCREEN_HEIGHT - 400)); - } - - @Test - public void updateActiveAndA11yFocusedWindow_windowStateChangedEvent_noTracking_shouldUpdate() { - final IBinder eventWindowToken = - mWindows.get(Display.DEFAULT_DISPLAY) - .get(DEFAULT_FOCUSED_INDEX + 1).getWindowInfo().token; - final int eventWindowId = mA11yWindowManager.findWindowIdLocked( - USER_SYSTEM_ID, eventWindowToken); - when(mMockWindowManagerInternal.getFocusedWindowTokenFromWindowStates()) - .thenReturn(eventWindowToken); - - final int noUse = 0; - mA11yWindowManager.stopTrackingWindows(Display.DEFAULT_DISPLAY); - mA11yWindowManager.updateActiveAndAccessibilityFocusedWindowLocked(USER_SYSTEM_ID, - eventWindowId, - noUse, - AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED, - noUse); - assertThat(mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID), is(eventWindowId)); - assertThat(mA11yWindowManager.getFocusedWindowId(AccessibilityNodeInfo.FOCUS_INPUT), - is(eventWindowId)); - } - - @Test - public void updateActiveAndA11yFocusedWindow_hoverEvent_touchInteract_shouldSetActiveWindow() { - final int eventWindowId = getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY, - DEFAULT_FOCUSED_INDEX + 1); - final int currentActiveWindowId = mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID); - assertThat(currentActiveWindowId, is(not(eventWindowId))); - - final int noUse = 0; - mA11yWindowManager.onTouchInteractionStart(); - mA11yWindowManager.updateActiveAndAccessibilityFocusedWindowLocked(USER_SYSTEM_ID, - eventWindowId, - noUse, - AccessibilityEvent.TYPE_VIEW_HOVER_ENTER, - noUse); - assertThat(mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID), is(eventWindowId)); - final ArgumentCaptor captor = - ArgumentCaptor.forClass(AccessibilityEvent.class); - verify(mMockA11yEventSender, times(2)) - .sendAccessibilityEventForCurrentUserLocked(captor.capture()); - assertThat(captor.getAllValues().get(0), - allOf(displayId(Display.DEFAULT_DISPLAY), - eventWindowId(currentActiveWindowId), - a11yWindowChanges(AccessibilityEvent.WINDOWS_CHANGE_ACTIVE))); - assertThat(captor.getAllValues().get(1), - allOf(displayId(Display.DEFAULT_DISPLAY), - eventWindowId(eventWindowId), - a11yWindowChanges(AccessibilityEvent.WINDOWS_CHANGE_ACTIVE))); - } - - @Test - public void updateActiveAndA11yFocusedWindow_a11yFocusEvent_shouldUpdateA11yFocus() { - final int eventWindowId = getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY, - DEFAULT_FOCUSED_INDEX); - final int currentA11yFocusedWindowId = mA11yWindowManager.getFocusedWindowId( - AccessibilityNodeInfo.FOCUS_ACCESSIBILITY); - assertThat(currentA11yFocusedWindowId, is(AccessibilityWindowInfo.UNDEFINED_WINDOW_ID)); - - final int noUse = 0; - mA11yWindowManager.updateActiveAndAccessibilityFocusedWindowLocked(USER_SYSTEM_ID, - eventWindowId, - AccessibilityNodeInfo.ROOT_NODE_ID, - AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED, - noUse); - assertThat(mA11yWindowManager.getFocusedWindowId( - AccessibilityNodeInfo.FOCUS_ACCESSIBILITY), is(eventWindowId)); - final ArgumentCaptor captor = - ArgumentCaptor.forClass(AccessibilityEvent.class); - verify(mMockA11yEventSender, times(1)) - .sendAccessibilityEventForCurrentUserLocked(captor.capture()); - assertThat(captor.getAllValues().get(0), - allOf(displayId(Display.DEFAULT_DISPLAY), - eventWindowId(eventWindowId), - a11yWindowChanges( - AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED))); - } - - @Test - public void updateActiveAndA11yFocusedWindow_a11yFocusEvent_multiDisplay_defaultToSecondary() - throws RemoteException { - runUpdateActiveAndA11yFocusedWindow_MultiDisplayTest( - Display.DEFAULT_DISPLAY, SECONDARY_DISPLAY_ID); - } - - @Test - public void updateActiveAndA11yFocusedWindow_a11yFocusEvent_multiDisplay_SecondaryToDefault() - throws RemoteException { - runUpdateActiveAndA11yFocusedWindow_MultiDisplayTest( - SECONDARY_DISPLAY_ID, Display.DEFAULT_DISPLAY); - } - - private void runUpdateActiveAndA11yFocusedWindow_MultiDisplayTest( - int initialDisplayId, int eventDisplayId) throws RemoteException { - startTrackingPerDisplay(SECONDARY_DISPLAY_ID); - final int initialWindowId = getWindowIdFromWindowInfosForDisplay( - initialDisplayId, DEFAULT_FOCUSED_INDEX); - final int noUse = 0; - mA11yWindowManager.updateActiveAndAccessibilityFocusedWindowLocked(USER_SYSTEM_ID, - initialWindowId, - AccessibilityNodeInfo.ROOT_NODE_ID, - AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED, - noUse); - assertThat(mA11yWindowManager.getFocusedWindowId( - AccessibilityNodeInfo.FOCUS_ACCESSIBILITY), is(initialWindowId)); - Mockito.reset(mMockA11yEventSender); - - final int eventWindowId = getWindowIdFromWindowInfosForDisplay( - eventDisplayId, DEFAULT_FOCUSED_INDEX); - mA11yWindowManager.updateActiveAndAccessibilityFocusedWindowLocked(USER_SYSTEM_ID, - eventWindowId, - AccessibilityNodeInfo.ROOT_NODE_ID, - AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED, - noUse); - assertThat(mA11yWindowManager.getFocusedWindowId( - AccessibilityNodeInfo.FOCUS_ACCESSIBILITY), is(eventWindowId)); - final ArgumentCaptor captor = - ArgumentCaptor.forClass(AccessibilityEvent.class); - verify(mMockA11yEventSender, times(2)) - .sendAccessibilityEventForCurrentUserLocked(captor.capture()); - assertThat(captor.getAllValues().get(0), - allOf(displayId(initialDisplayId), - eventWindowId(initialWindowId), - a11yWindowChanges( - AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED))); - assertThat(captor.getAllValues().get(1), - allOf(displayId(eventDisplayId), - eventWindowId(eventWindowId), - a11yWindowChanges( - AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED))); - } - - @Test - public void updateActiveAndA11yFocusedWindow_clearA11yFocusEvent_shouldClearA11yFocus() { - final int eventWindowId = getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY, - DEFAULT_FOCUSED_INDEX); - final int currentA11yFocusedWindowId = mA11yWindowManager.getFocusedWindowId( - AccessibilityNodeInfo.FOCUS_ACCESSIBILITY); - assertThat(currentA11yFocusedWindowId, is(not(eventWindowId))); - - final int noUse = 0; - mA11yWindowManager.updateActiveAndAccessibilityFocusedWindowLocked(USER_SYSTEM_ID, - eventWindowId, - AccessibilityNodeInfo.ROOT_NODE_ID, - AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED, - noUse); - assertThat(mA11yWindowManager.getFocusedWindowId( - AccessibilityNodeInfo.FOCUS_ACCESSIBILITY), is(eventWindowId)); - mA11yWindowManager.updateActiveAndAccessibilityFocusedWindowLocked(USER_SYSTEM_ID, - eventWindowId, - AccessibilityNodeInfo.ROOT_NODE_ID, - AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED, - noUse); - assertThat(mA11yWindowManager.getFocusedWindowId( - AccessibilityNodeInfo.FOCUS_ACCESSIBILITY), - is(AccessibilityWindowInfo.UNDEFINED_WINDOW_ID)); - } - - @Test - public void onTouchInteractionEnd_shouldRollbackActiveWindow() { - final int eventWindowId = getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY, - DEFAULT_FOCUSED_INDEX + 1); - final int currentActiveWindowId = mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID); - assertThat(currentActiveWindowId, is(not(eventWindowId))); - - final int noUse = 0; - mA11yWindowManager.onTouchInteractionStart(); - mA11yWindowManager.updateActiveAndAccessibilityFocusedWindowLocked(USER_SYSTEM_ID, - eventWindowId, - noUse, - AccessibilityEvent.TYPE_VIEW_HOVER_ENTER, - noUse); - assertThat(mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID), is(eventWindowId)); - // AccessibilityEventSender is invoked after active window changed. Reset it. - Mockito.reset(mMockA11yEventSender); - - mA11yWindowManager.onTouchInteractionEnd(); - final ArgumentCaptor captor = - ArgumentCaptor.forClass(AccessibilityEvent.class); - verify(mMockA11yEventSender, times(2)) - .sendAccessibilityEventForCurrentUserLocked(captor.capture()); - assertThat(captor.getAllValues().get(0), - allOf(displayId(Display.DEFAULT_DISPLAY), - eventWindowId(eventWindowId), - a11yWindowChanges(AccessibilityEvent.WINDOWS_CHANGE_ACTIVE))); - assertThat(captor.getAllValues().get(1), - allOf(displayId(Display.DEFAULT_DISPLAY), - eventWindowId(currentActiveWindowId), - a11yWindowChanges(AccessibilityEvent.WINDOWS_CHANGE_ACTIVE))); - } - - @Test - public void onTouchInteractionEnd_noServiceInteractiveWindow_shouldClearA11yFocus() - throws RemoteException { - final IBinder defaultFocusWinToken = - mWindows.get(Display.DEFAULT_DISPLAY).get( - DEFAULT_FOCUSED_INDEX).getWindowInfo().token; - final int defaultFocusWindowId = mA11yWindowManager.findWindowIdLocked( - USER_SYSTEM_ID, defaultFocusWinToken); - when(mMockWindowManagerInternal.getFocusedWindowTokenFromWindowStates()) - .thenReturn(defaultFocusWinToken); - final int newFocusWindowId = getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY, - DEFAULT_FOCUSED_INDEX + 1); - final IAccessibilityInteractionConnection mockNewFocusConnection = - mA11yWindowManager.getConnectionLocked( - USER_SYSTEM_ID, newFocusWindowId).getRemote(); - - mA11yWindowManager.stopTrackingWindows(Display.DEFAULT_DISPLAY); - final int noUse = 0; - mA11yWindowManager.updateActiveAndAccessibilityFocusedWindowLocked(USER_SYSTEM_ID, - defaultFocusWindowId, - noUse, - AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED, - noUse); - assertThat(mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID), is(defaultFocusWindowId)); - assertThat(mA11yWindowManager.getFocusedWindowId(AccessibilityNodeInfo.FOCUS_INPUT), - is(defaultFocusWindowId)); - - mA11yWindowManager.onTouchInteractionStart(); - mA11yWindowManager.updateActiveAndAccessibilityFocusedWindowLocked(USER_SYSTEM_ID, - newFocusWindowId, - noUse, - AccessibilityEvent.TYPE_VIEW_HOVER_ENTER, - noUse); - mA11yWindowManager.updateActiveAndAccessibilityFocusedWindowLocked(USER_SYSTEM_ID, - newFocusWindowId, - AccessibilityNodeInfo.ROOT_NODE_ID, - AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED, - noUse); - assertThat(mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID), is(newFocusWindowId)); - assertThat(mA11yWindowManager.getFocusedWindowId( - AccessibilityNodeInfo.FOCUS_ACCESSIBILITY), is(newFocusWindowId)); - - mA11yWindowManager.onTouchInteractionEnd(); - mHandler.sendLastMessage(); - verify(mockNewFocusConnection).clearAccessibilityFocus(); - } - - @Test - public void getPictureInPictureWindow_shouldNotNull() { - assertNull(mA11yWindowManager.getPictureInPictureWindowLocked()); - mWindows.get(Display.DEFAULT_DISPLAY).get(1).getWindowInfo().inPictureInPicture = true; - onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); - - assertNotNull(mA11yWindowManager.getPictureInPictureWindowLocked()); - } - - @Test - public void notifyOutsideTouch() throws RemoteException { - final int targetWindowId = - getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY, 1); - final int outsideWindowId = - getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY, 0); - final IAccessibilityInteractionConnection mockRemoteConnection = - mA11yWindowManager.getConnectionLocked( - USER_SYSTEM_ID, outsideWindowId).getRemote(); - mWindows.get(Display.DEFAULT_DISPLAY).get(0).getWindowInfo().hasFlagWatchOutsideTouch = - true; - onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); - - mA11yWindowManager.notifyOutsideTouch(USER_SYSTEM_ID, targetWindowId); - verify(mockRemoteConnection).notifyOutsideTouch(); - } - - @Test - public void addAccessibilityInteractionConnection_profileUser_findInParentUser() - throws RemoteException { - final IWindow token = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY, - false, USER_PROFILE); - final int windowId = mA11yWindowManager.findWindowIdLocked( - USER_PROFILE_PARENT, token.asBinder()); - assertTrue(windowId >= 0); - } - - @Test - public void getDisplayList() throws RemoteException { - // Starts tracking window of second display. - startTrackingPerDisplay(SECONDARY_DISPLAY_ID); - - final ArrayList displayList = mA11yWindowManager.getDisplayListLocked( - DISPLAY_TYPE_DEFAULT); - assertTrue(displayList.equals(mExpectedDisplayList)); - } - - @Test - public void setAccessibilityWindowIdToSurfaceMetadata() - throws RemoteException { - final IWindow token = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY, - true, USER_SYSTEM_ID); - int windowId = -1; - for (int i = 0; i < mA11yWindowTokens.size(); i++) { - if (mA11yWindowTokens.valueAt(i).equals(token)) { - windowId = mA11yWindowTokens.keyAt(i); - } - } - assertNotEquals("Returned token is not found in mA11yWindowTokens", -1, windowId); - verify(mMockWindowManagerInternal, times(1)).setAccessibilityIdToSurfaceMetadata( - token.asBinder(), windowId); - - mA11yWindowManager.removeAccessibilityInteractionConnection(token); - verify(mMockWindowManagerInternal, times(1)).setAccessibilityIdToSurfaceMetadata( - token.asBinder(), -1); - } - - @Test - public void getHostTokenLocked_hierarchiesAreAssociated_shouldReturnHostToken() { - mA11yWindowManager.associateLocked(mMockEmbeddedToken, mMockHostToken); - final IBinder hostToken = mA11yWindowManager.getHostTokenLocked(mMockEmbeddedToken); - assertEquals(hostToken, mMockHostToken); - } - - @Test - public void getHostTokenLocked_hierarchiesAreNotAssociated_shouldReturnNull() { - final IBinder hostToken = mA11yWindowManager.getHostTokenLocked(mMockEmbeddedToken); - assertNull(hostToken); - } - - @Test - public void getHostTokenLocked_embeddedHierarchiesAreDisassociated_shouldReturnNull() { - mA11yWindowManager.associateLocked(mMockEmbeddedToken, mMockHostToken); - mA11yWindowManager.disassociateLocked(mMockEmbeddedToken); - final IBinder hostToken = mA11yWindowManager.getHostTokenLocked(mMockEmbeddedToken); - assertNull(hostToken); - } - - @Test - public void getHostTokenLocked_hostHierarchiesAreDisassociated_shouldReturnNull() { - mA11yWindowManager.associateLocked(mMockEmbeddedToken, mMockHostToken); - mA11yWindowManager.disassociateLocked(mMockHostToken); - final IBinder hostToken = mA11yWindowManager.getHostTokenLocked(mMockHostToken); - assertNull(hostToken); - } - - @Test - public void getWindowIdLocked_windowIsRegistered_shouldReturnWindowId() { - final int windowId = mA11yWindowManager.getWindowIdLocked(mMockHostToken); - assertEquals(windowId, HOST_WINDOW_ID); - } - - @Test - public void getWindowIdLocked_windowIsNotRegistered_shouldReturnInvalidWindowId() { - final int windowId = mA11yWindowManager.getWindowIdLocked(mMockInvalidToken); - assertEquals(windowId, INVALID_ID); - } - - @Test - public void getTokenLocked_windowIsRegistered_shouldReturnToken() { - final IBinder token = mA11yWindowManager.getLeashTokenLocked(HOST_WINDOW_ID); - assertEquals(token, mMockHostToken); - } - - @Test - public void getTokenLocked_windowIsNotRegistered_shouldReturnNull() { - final IBinder token = mA11yWindowManager.getLeashTokenLocked(OTHER_WINDOW_ID); - assertNull(token); - } - - @Test - public void setAccessibilityWindowAttributes_windowIsNotRegistered_titleIsChanged() { - final int windowId = - getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY, 0); - final WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(); - layoutParams.accessibilityTitle = "accessibility window title"; - final AccessibilityWindowAttributes attributes = new AccessibilityWindowAttributes( - layoutParams, new LocaleList()); - - mA11yWindowManager.setAccessibilityWindowAttributes(Display.DEFAULT_DISPLAY, windowId, - USER_SYSTEM_ID, attributes); - - final AccessibilityWindowInfo a11yWindow = mA11yWindowManager.findA11yWindowInfoByIdLocked( - windowId); - assertEquals(toString(layoutParams.accessibilityTitle), toString(a11yWindow.getTitle())); - } - - @Test - public void sendAccessibilityEventOnWindowRemoval() { - final ArrayList windows = mWindows.get(Display.DEFAULT_DISPLAY); - - // Removing index 0 because it's not focused, and avoids unnecessary layer change. - final int windowId = - getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY, 0); - windows.remove(0); - - onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, FORCE_SEND); - - final ArgumentCaptor captor = - ArgumentCaptor.forClass(AccessibilityEvent.class); - verify(mMockA11yEventSender, times(1)) - .sendAccessibilityEventForCurrentUserLocked(captor.capture()); - assertThat(captor.getAllValues().get(0), - allOf(displayId(Display.DEFAULT_DISPLAY), - eventWindowId(windowId), - a11yWindowChanges(AccessibilityEvent.WINDOWS_CHANGE_REMOVED))); - } - - @Test - public void sendAccessibilityEventOnWindowAddition() throws RemoteException { - final ArrayList windows = mWindows.get(Display.DEFAULT_DISPLAY); - - final IWindow token = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY, - false, USER_SYSTEM_ID); - // Adding window to the front so that other windows' layer won't change. - windows.add(0, createMockAccessibilityWindow(token, Display.DEFAULT_DISPLAY)); - final int windowId = getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY, 0); - - onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, FORCE_SEND); - - final ArgumentCaptor captor = - ArgumentCaptor.forClass(AccessibilityEvent.class); - verify(mMockA11yEventSender, times(1)) - .sendAccessibilityEventForCurrentUserLocked(captor.capture()); - assertThat(captor.getAllValues().get(0), - allOf(displayId(Display.DEFAULT_DISPLAY), - eventWindowId(windowId), - a11yWindowChanges(AccessibilityEvent.WINDOWS_CHANGE_ADDED))); - } - - @Test - public void sendAccessibilityEventOnWindowChange() { - final ArrayList windows = mWindows.get(Display.DEFAULT_DISPLAY); - windows.get(0).getWindowInfo().title = "new title"; - final int windowId = getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY, 0); - - onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, FORCE_SEND); - - final ArgumentCaptor captor = - ArgumentCaptor.forClass(AccessibilityEvent.class); - verify(mMockA11yEventSender, times(1)) - .sendAccessibilityEventForCurrentUserLocked(captor.capture()); - assertThat(captor.getAllValues().get(0), - allOf(displayId(Display.DEFAULT_DISPLAY), - eventWindowId(windowId), - a11yWindowChanges(AccessibilityEvent.WINDOWS_CHANGE_TITLE))); - } - - private void registerLeashedTokenAndWindowId() { - mA11yWindowManager.registerIdLocked(mMockHostToken, HOST_WINDOW_ID); - mA11yWindowManager.registerIdLocked(mMockEmbeddedToken, EMBEDDED_WINDOW_ID); - } - - private void startTrackingPerDisplay(int displayId) throws RemoteException { - ArrayList windowsForDisplay = new ArrayList<>(); - // Adds RemoteAccessibilityConnection into AccessibilityWindowManager, and copy - // mock window token into mA11yWindowTokens. Also, preparing WindowInfo mWindowInfos - // for the test. - for (int i = 0; i < NUM_GLOBAL_WINDOWS; i++) { - final IWindow token = addAccessibilityInteractionConnection(displayId, - true, USER_SYSTEM_ID); - windowsForDisplay.add(createMockAccessibilityWindow(token, displayId)); - - } - for (int i = 0; i < NUM_APP_WINDOWS; i++) { - final IWindow token = addAccessibilityInteractionConnection(displayId, - false, USER_SYSTEM_ID); - windowsForDisplay.add(createMockAccessibilityWindow(token, displayId)); - } - // Sets up current focused window of display. - // Each display has its own current focused window if config_perDisplayFocusEnabled is true. - // Otherwise only default display needs to current focused window. - if (mSupportPerDisplayFocus || displayId == Display.DEFAULT_DISPLAY) { - windowsForDisplay.get(DEFAULT_FOCUSED_INDEX).getWindowInfo().focused = true; - } - // Turns on windows tracking, and update window info. - mA11yWindowManager.startTrackingWindows(displayId, false); - // Puts window lists into array. - mWindows.put(displayId, windowsForDisplay); - // Sets the default display is the top focused display and - // its current focused window is the top focused window. - if (displayId == Display.DEFAULT_DISPLAY) { - setTopFocusedWindowAndDisplay(displayId, DEFAULT_FOCUSED_INDEX); - } - // Invokes callback for sending window lists to A11y framework. - onAccessibilityWindowsChanged(displayId, FORCE_SEND); - - assertEquals(mA11yWindowManager.getWindowListLocked(displayId).size(), - windowsForDisplay.size()); - } - - private WindowsForAccessibilityCallback getWindowsForAccessibilityCallbacks(int displayId) { - ArgumentCaptor windowsForAccessibilityCallbacksCaptor = - ArgumentCaptor.forClass( - WindowsForAccessibilityCallback.class); - verify(mMockWindowManagerInternal) - .setWindowsForAccessibilityCallback(eq(displayId), - windowsForAccessibilityCallbacksCaptor.capture()); - return windowsForAccessibilityCallbacksCaptor.getValue(); - } - - private IWindow addAccessibilityInteractionConnection(int displayId, boolean bGlobal, - int userId) throws RemoteException { - final IWindow mockWindowToken = Mockito.mock(IWindow.class); - final IAccessibilityInteractionConnection mockA11yConnection = Mockito.mock( - IAccessibilityInteractionConnection.class); - final IBinder mockConnectionBinder = Mockito.mock(IBinder.class); - final IBinder mockWindowBinder = Mockito.mock(IBinder.class); - final IBinder mockLeashToken = Mockito.mock(IBinder.class); - when(mockA11yConnection.asBinder()).thenReturn(mockConnectionBinder); - when(mockWindowToken.asBinder()).thenReturn(mockWindowBinder); - when(mMockA11ySecurityPolicy.isCallerInteractingAcrossUsers(userId)) - .thenReturn(bGlobal); - when(mMockWindowManagerInternal.getDisplayIdForWindow(mockWindowBinder)) - .thenReturn(displayId); - - int windowId = mA11yWindowManager.addAccessibilityInteractionConnection( - mockWindowToken, mockLeashToken, mockA11yConnection, PACKAGE_NAME, userId); - mA11yWindowTokens.put(windowId, mockWindowToken); - return mockWindowToken; - } - - private int addAccessibilityInteractionConnection(int displayId, boolean bGlobal, - IBinder leashToken, int userId) throws RemoteException { - final IWindow mockWindowToken = Mockito.mock(IWindow.class); - final IAccessibilityInteractionConnection mockA11yConnection = Mockito.mock( - IAccessibilityInteractionConnection.class); - final IBinder mockConnectionBinder = Mockito.mock(IBinder.class); - final IBinder mockWindowBinder = Mockito.mock(IBinder.class); - when(mockA11yConnection.asBinder()).thenReturn(mockConnectionBinder); - when(mockWindowToken.asBinder()).thenReturn(mockWindowBinder); - when(mMockA11ySecurityPolicy.isCallerInteractingAcrossUsers(userId)) - .thenReturn(bGlobal); - when(mMockWindowManagerInternal.getDisplayIdForWindow(mockWindowBinder)) - .thenReturn(displayId); - - int windowId = mA11yWindowManager.addAccessibilityInteractionConnection( - mockWindowToken, leashToken, mockA11yConnection, PACKAGE_NAME, userId); - mA11yWindowTokens.put(windowId, mockWindowToken); - return windowId; - } - - private int getWindowIdFromWindowInfosForDisplay(int displayId, int index) { - final IBinder windowToken = mWindows.get(displayId).get(index).getWindowInfo().token; - return mA11yWindowManager.findWindowIdLocked( - USER_SYSTEM_ID, windowToken); - } - - private void setTopFocusedWindowAndDisplay(int displayId, int index) { - // Sets the top focus window. - mTopFocusedWindowToken = mWindows.get(displayId).get(index).getWindowInfo().token; - // Sets the top focused display. - mTopFocusedDisplayId = displayId; - } - - private void onAccessibilityWindowsChanged(int displayId, boolean forceSend) { - WindowsForAccessibilityCallback callbacks = mCallbackOfWindows.get(displayId); - if (callbacks == null) { - callbacks = getWindowsForAccessibilityCallbacks(displayId); - mCallbackOfWindows.put(displayId, callbacks); - } - callbacks.onAccessibilityWindowsChanged(forceSend, mTopFocusedDisplayId, - mTopFocusedWindowToken, new Point(SCREEN_WIDTH, SCREEN_HEIGHT), - mWindows.get(displayId)); - } - - private void changeFocusedWindowOnDisplayPerDisplayFocusConfig( - int changeFocusedDisplayId, int newFocusedWindowIndex, int oldTopFocusedDisplayId, - int oldFocusedWindowIndex) { - if (mSupportPerDisplayFocus) { - // Gets the old focused window of display which wants to change focused window. - WindowInfo focusedWindowInfo = - mWindows.get(changeFocusedDisplayId).get(oldFocusedWindowIndex).getWindowInfo(); - // Resets the focus of old focused window. - focusedWindowInfo.focused = false; - // Gets the new window of display which wants to change focused window. - focusedWindowInfo = - mWindows.get(changeFocusedDisplayId).get(newFocusedWindowIndex).getWindowInfo(); - // Sets the focus of new focused window. - focusedWindowInfo.focused = true; - } else { - // Gets the window of display which wants to change focused window. - WindowInfo focusedWindowInfo = - mWindows.get(changeFocusedDisplayId).get(newFocusedWindowIndex).getWindowInfo(); - // Sets the focus of new focused window. - focusedWindowInfo.focused = true; - // Gets the old focused window of old top focused display. - focusedWindowInfo = - mWindows.get(oldTopFocusedDisplayId).get(oldFocusedWindowIndex).getWindowInfo(); - // Resets the focus of old focused window. - focusedWindowInfo.focused = false; - // Changes the top focused display and window. - setTopFocusedWindowAndDisplay(changeFocusedDisplayId, newFocusedWindowIndex); - } - } - - private AccessibilityWindow createMockAccessibilityWindow(IWindow windowToken, int displayId) { - final WindowInfo windowInfo = WindowInfo.obtain(); - windowInfo.type = WindowManager.LayoutParams.TYPE_APPLICATION; - windowInfo.token = windowToken.asBinder(); - - final AccessibilityWindow window = Mockito.mock(AccessibilityWindow.class); - when(window.getWindowInfo()).thenReturn(windowInfo); - when(window.isFocused()).thenAnswer(invocation -> windowInfo.focused); - when(window.isTouchable()).thenReturn(true); - when(window.getType()).thenReturn(windowInfo.type); - - setRegionForMockAccessibilityWindow(window, nextToucableRegion(displayId)); - return window; - } - - private void setRegionForMockAccessibilityWindow(AccessibilityWindow window, Region region) { - doAnswer(invocation -> { - ((Region) invocation.getArgument(0)).set(region); - return null; - }).when(window).getTouchableRegionInScreen(any(Region.class)); - doAnswer(invocation -> { - ((Region) invocation.getArgument(0)).set(region); - return null; - }).when(window).getTouchableRegionInWindow(any(Region.class)); - } - - private Region nextToucableRegion(int displayId) { - final int topLeft = mNextRegionOffsets.get(displayId, 0); - final int bottomRight = topLeft + 100; - mNextRegionOffsets.put(displayId, topLeft + 10); - return new Region(topLeft, topLeft, bottomRight, bottomRight); - } - - @Nullable - private static String toString(@Nullable CharSequence cs) { - return cs == null ? null : cs.toString(); - } - - static class DisplayIdMatcher extends TypeSafeMatcher { - private final int mDisplayId; - - DisplayIdMatcher(int displayId) { - super(); - mDisplayId = displayId; - } - - static DisplayIdMatcher displayId(int displayId) { - return new DisplayIdMatcher(displayId); - } - - @Override - protected boolean matchesSafely(AccessibilityEvent event) { - return event.getDisplayId() == mDisplayId; - } - - @Override - public void describeTo(Description description) { - description.appendText("Matching to displayId " + mDisplayId); - } - } - - static class EventWindowIdMatcher extends TypeSafeMatcher { - private int mWindowId; - - EventWindowIdMatcher(int windowId) { - super(); - mWindowId = windowId; - } - - static EventWindowIdMatcher eventWindowId(int windowId) { - return new EventWindowIdMatcher(windowId); - } - - @Override - protected boolean matchesSafely(AccessibilityEvent event) { - return event.getWindowId() == mWindowId; - } - - @Override - public void describeTo(Description description) { - description.appendText("Matching to windowId " + mWindowId); - } - } - - static class WindowChangesMatcher extends TypeSafeMatcher { - private int mWindowChanges; - - WindowChangesMatcher(int windowChanges) { - super(); - mWindowChanges = windowChanges; - } - - static WindowChangesMatcher a11yWindowChanges(int windowChanges) { - return new WindowChangesMatcher(windowChanges); - } - - @Override - protected boolean matchesSafely(AccessibilityEvent event) { - return event.getWindowChanges() == mWindowChanges; - } - - @Override - public void describeTo(Description description) { - description.appendText("Matching to window changes " + mWindowChanges); - } - } - - static class WindowIdMatcher extends TypeSafeMatcher { - private final int mWindowId; - - WindowIdMatcher(int windowId) { - super(); - mWindowId = windowId; - } - - static WindowIdMatcher windowId(int windowId) { - return new WindowIdMatcher(windowId); - } - - @Override - protected boolean matchesSafely(AccessibilityWindowInfo window) { - return window.getId() == mWindowId; - } - - @Override - public void describeTo(Description description) { - description.appendText("Matching to windowId " + mWindowId); - } - } -} -- cgit v1.2.3-59-g8ed1b From f191ff1c13998310242eacbc5f8aa594f55ea1e4 Mon Sep 17 00:00:00 2001 From: Hiroki Sato Date: Tue, 19 Nov 2024 14:20:23 +0900 Subject: Cleanup AccessibilityWindowManager#onWindowsForAccessibilityChanged Due to a flag compute_window_changes_on_a11y_v2, AccessibilityWindowManager.DisplayWindowsObserver has two entry points, one is #onWindowsForAccessibilityChanged (for flag disabled) and another is #onAccessibilityWindowsChanged (for flag enabled). Now that the flag is removed [1], we can merge them to simplify the code. [1] Ifd8328fd21f5547726313c65c60d2a4b0d6095af Bug: 322444245 Test: atest AccessibilityEventAndCacheTest Flag: EXEMPT cleanup after removintg com.android.server.accessibility.fix_merged_content_change_event_v2 Change-Id: Ia72589b03c6af12abb8cc990325303f3afed23ea --- .../accessibility/AccessibilityWindowManager.java | 59 +++++++++------------- 1 file changed, 23 insertions(+), 36 deletions(-) (limited to 'services/accessibility') diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java index 5cbe0c4fe4d2..8b870dbaa100 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java @@ -433,10 +433,24 @@ public class AccessibilityWindowManager { return Collections.emptyList(); } - private void onWindowsForAccessibilityChanged(boolean forceSend, int topFocusedDisplayId, - IBinder topFocusedWindowToken, @NonNull List windows) { - // TODO(b/322444245): no longer need to get a lock. + /** + * Called when the windows for accessibility changed. + * + * @param forceSend Send the windows for accessibility even if they haven't + * changed. + * @param topFocusedDisplayId The display Id which has the top focused window. + * @param topFocusedWindowToken The window token of top focused window. + * @param screenSize The size of the display that the change happened. + * @param accessibilityWindows The windows for accessibility. + */ + @Override + public void onAccessibilityWindowsChanged(boolean forceSend, int topFocusedDisplayId, + @NonNull IBinder topFocusedWindowToken, @NonNull Point screenSize, + @NonNull List accessibilityWindows) { synchronized (mLock) { + final List windows = + createWindowInfoListLocked(screenSize, accessibilityWindows); + if (DEBUG) { Slogf.i(LOG_TAG, "mDisplayId=%d, topFocusedDisplayId=%d, currentUserId=%d, " + "visibleBgUsers=%s", mDisplayId, topFocusedDisplayId, @@ -451,14 +465,15 @@ public class AccessibilityWindowManager { Slogf.i(LOG_TAG, "%d windows changed: %s", windows.size(), windowsInfo); } } - if (shouldUpdateWindowsLocked(forceSend, windows)) { + + if (forceSend || shouldUpdateWindowsLocked(windows)) { mTopFocusedDisplayId = topFocusedDisplayId; if (!isProxyed(topFocusedDisplayId)) { mLastNonProxyTopFocusedDisplayId = topFocusedDisplayId; } mTopFocusedWindowToken = topFocusedWindowToken; if (DEBUG) { - Slogf.d(LOG_TAG, "onWindowsForAccessibilityChanged(): updating windows for " + Slogf.d(LOG_TAG, "onAccessibilityWindowsChanged(): updating windows for " + "display %d and token %s", topFocusedDisplayId, topFocusedWindowToken); } @@ -468,37 +483,14 @@ public class AccessibilityWindowManager { windows); // Someone may be waiting for the windows - advertise it. mLock.notifyAll(); - } - else if (DEBUG) { - Slogf.d(LOG_TAG, "onWindowsForAccessibilityChanged(): NOT updating windows for " + } else if (DEBUG) { + Slogf.d(LOG_TAG, "onAccessibilityWindowsChanged(): NOT updating windows for " + "display %d and token %s", topFocusedDisplayId, topFocusedWindowToken); } } } - /** - * Called when the windows for accessibility changed. - * - * @param forceSend Send the windows for accessibility even if they haven't - * changed. - * @param topFocusedDisplayId The display Id which has the top focused window. - * @param topFocusedWindowToken The window token of top focused window. - * @param screenSize The size of the display that the change happened. - * @param windows The windows for accessibility. - */ - @Override - public void onAccessibilityWindowsChanged(boolean forceSend, int topFocusedDisplayId, - @NonNull IBinder topFocusedWindowToken, @NonNull Point screenSize, - @NonNull List windows) { - synchronized (mLock) { - final List windowInfoList = - createWindowInfoListLocked(screenSize, windows); - onWindowsForAccessibilityChanged(forceSend, topFocusedDisplayId, - topFocusedWindowToken, windowInfoList); - } - } - private List createWindowInfoListLocked(@NonNull Point screenSize, @NonNull List visibleWindows) { final Set addedWindows = new ArraySet<>(); @@ -650,12 +642,7 @@ public class AccessibilityWindowManager { windowInfo.locales = attributes.getLocales(); } - private boolean shouldUpdateWindowsLocked(boolean forceSend, - @NonNull List windows) { - if (forceSend) { - return true; - } - + private boolean shouldUpdateWindowsLocked(@NonNull List windows) { final int windowCount = windows.size(); if (VERBOSE) { Slogf.v(LOG_TAG, -- cgit v1.2.3-59-g8ed1b From 3e31c23a32d14950e888d09b2b57146103675667 Mon Sep 17 00:00:00 2001 From: Daniel Norman Date: Tue, 19 Nov 2024 22:48:28 +0000 Subject: Roll out bugfix enable_hardware_shortcut_disables_warning This has been in trunkfood for 4 months. (This flagged fix was originally tagged as a feature flag, but under current org guidelines this should have been treated as a minor bugfix flag.) Bug: 287065325 Change-Id: Idfa29ae0d1e3c2b3b98a9357dd06b6746e499988 Flag: com.android.server.accessibility.enable_hardware_shortcut_disables_warning --- .../server/accessibility/AccessibilityManagerService.java | 12 +++++------- .../accessibility/AccessibilityManagerServiceTest.java | 1 - 2 files changed, 5 insertions(+), 8 deletions(-) (limited to 'services/accessibility') diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index d4af7b765254..c210e726fc12 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -4503,13 +4503,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } if (shortcutType == HARDWARE) { skipVolumeShortcutDialogTimeoutRestriction(userId); - if (com.android.server.accessibility.Flags.enableHardwareShortcutDisablesWarning()) { - persistIntToSetting( - userId, - Settings.Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, - AccessibilityShortcutController.DialogStatus.SHOWN - ); - } + persistIntToSetting( + userId, + Settings.Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, + AccessibilityShortcutController.DialogStatus.SHOWN + ); } else if (shortcutType == SOFTWARE) { // Update the A11y FAB size to large when the Magnification shortcut is // enabled and the user hasn't changed the floating button size diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java index d5b930769e43..492838e9b4fb 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java @@ -1136,7 +1136,6 @@ public class AccessibilityManagerServiceTest { } @Test - @EnableFlags(Flags.FLAG_ENABLE_HARDWARE_SHORTCUT_DISABLES_WARNING) public void enableHardwareShortcutsForTargets_shortcutDialogSetting_isShown() { // TODO(b/111889696): Remove the user 0 assumption once we support multi-user assumeTrue("The test is setup to run as a user 0", -- cgit v1.2.3-59-g8ed1b From e6d6411e18d061721494b7856452881e815c51b6 Mon Sep 17 00:00:00 2001 From: Daniel Norman Date: Fri, 15 Nov 2024 23:58:45 +0000 Subject: Fixes canEnableDisableInputMethod to use correct user handle. The old logic was grabbing the user handle while inside a block that cleared user identity, so the user handle always returned 0 instead of the correct user handle. Fix: 375119534 Test: follow steps in the bug on an HSUM device Test: atest --user-type secondary_user AccessibilityServiceConnectionTest Flag: EXEMPT minor bugfix Change-Id: Id86264284bdfec0b436c01335d979087a8fd099a --- .../server/accessibility/AccessibilitySecurityPolicy.java | 5 ++--- .../server/accessibility/AccessibilityServiceConnection.java | 4 +--- .../accessibility/AccessibilityServiceConnectionTest.java | 10 ++++++++++ 3 files changed, 13 insertions(+), 6 deletions(-) (limited to 'services/accessibility') diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java b/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java index f45fa921c4a2..5ae077363c88 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java @@ -405,10 +405,9 @@ public class AccessibilitySecurityPolicy { * @throws SecurityException if the input method is not in the same package as the service. */ @AccessibilityService.SoftKeyboardController.EnableImeResult - int canEnableDisableInputMethod(String imeId, AbstractAccessibilityServiceConnection service) - throws SecurityException { + int canEnableDisableInputMethod(String imeId, AbstractAccessibilityServiceConnection service, + int callingUserId) throws SecurityException { final String servicePackageName = service.getComponentName().getPackageName(); - final int callingUserId = UserHandle.getCallingUserId(); InputMethodInfo inputMethodInfo = null; List inputMethodInfoList = diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java index 15999d19ebc0..a3fe9ec5ea22 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java @@ -410,9 +410,7 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect final @AccessibilityService.SoftKeyboardController.EnableImeResult int checkResult; final long identity = Binder.clearCallingIdentity(); try { - synchronized (mLock) { - checkResult = mSecurityPolicy.canEnableDisableInputMethod(imeId, this); - } + checkResult = mSecurityPolicy.canEnableDisableInputMethod(imeId, this, callingUserId); if (checkResult != ENABLE_IME_SUCCESS) { return checkResult; } diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java index 8914696d55da..d4f2dcc24af6 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java @@ -448,4 +448,14 @@ public class AccessibilityServiceConnectionTest { mConnection.binderDied(); assertThat(mConnection.getServiceInfo().flags & flag).isEqualTo(0); } + + @Test + public void setInputMethodEnabled_checksAccessWithProvidedImeIdAndUserId() { + final String imeId = "test_ime_id"; + final int callingUserId = UserHandle.getCallingUserId(); + mConnection.setInputMethodEnabled(imeId, true); + + verify(mMockSecurityPolicy).canEnableDisableInputMethod( + eq(imeId), any(), eq(callingUserId)); + } } -- cgit v1.2.3-59-g8ed1b From 9f1122863ed2137761401004ea6be0e416d75b66 Mon Sep 17 00:00:00 2001 From: Cam Bickel Date: Fri, 22 Nov 2024 00:14:08 +0000 Subject: Cleanup flag "always_allow_observing_touch_events" Bug: b/344604959 Test: presubmit Flag: EXEMPT flag cleanup Change-Id: I5cd3797f2c7004eabc4222ce6743802c26542e1a --- .../accessibility/AccessibilityInputFilter.java | 29 +++------------------- 1 file changed, 3 insertions(+), 26 deletions(-) (limited to 'services/accessibility') diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java index d6fc6e461edc..7ef6aace1538 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java @@ -1180,18 +1180,8 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo } private boolean anyServiceWantsGenericMotionEvent(MotionEvent event) { - if (Flags.alwaysAllowObservingTouchEvents()) { - final boolean isTouchEvent = event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN); - if (isTouchEvent && !canShareGenericTouchEvent()) { - return false; - } - final int eventSourceWithoutClass = event.getSource() & ~InputDevice.SOURCE_CLASS_MASK; - return (mCombinedGenericMotionEventSources & eventSourceWithoutClass) != 0; - } - // Disable SOURCE_TOUCHSCREEN generic event interception if any service is performing - // touch exploration. - if (event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN) - && (mEnabledFeatures & FLAG_FEATURE_TOUCH_EXPLORATION) != 0) { + final boolean isTouchEvent = event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN); + if (isTouchEvent && !canShareGenericTouchEvent()) { return false; } final int eventSourceWithoutClass = event.getSource() & ~InputDevice.SOURCE_CLASS_MASK; @@ -1199,21 +1189,8 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo } private boolean anyServiceWantsToObserveMotionEvent(MotionEvent event) { - if (Flags.alwaysAllowObservingTouchEvents()) { - final int eventSourceWithoutClass = event.getSource() & ~InputDevice.SOURCE_CLASS_MASK; - return (mCombinedMotionEventObservedSources & eventSourceWithoutClass) != 0; - } - // Disable SOURCE_TOUCHSCREEN generic event interception if any service is performing - // touch exploration. - if (event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN) - && (mEnabledFeatures & FLAG_FEATURE_TOUCH_EXPLORATION) != 0) { - return false; - } final int eventSourceWithoutClass = event.getSource() & ~InputDevice.SOURCE_CLASS_MASK; - return (mCombinedGenericMotionEventSources - & mCombinedMotionEventObservedSources - & eventSourceWithoutClass) - != 0; + return (mCombinedMotionEventObservedSources & eventSourceWithoutClass) != 0; } private boolean canShareGenericTouchEvent() { -- cgit v1.2.3-59-g8ed1b From a5c067faf7fc8c43186bec039e95e5ebc1828cdf Mon Sep 17 00:00:00 2001 From: Candice Date: Tue, 19 Nov 2024 02:59:38 +0000 Subject: Clean up flag(1): com.android.window.flags.always_draw_magnification_fullscreen_border Clean up the flag usages in the services/accessibility directory. Bug: 377211252 Test: atest FullScreenMagnificationControllerTest Test: atest MagnificationControllerTest Test: atest AccessibilityManagerServiceTest Flag: com.android.window.flags.always_draw_magnification_fullscreen_border Change-Id: I78aeb95c7b21170f1536c9d3f85e96e993ee2ef4 --- .../accessibility/AccessibilityManagerService.java | 11 ++--------- .../FullScreenMagnificationController.java | 5 ++--- .../magnification/MagnificationController.java | 7 ++----- .../AccessibilityManagerServiceTest.java | 2 -- .../FullScreenMagnificationControllerTest.java | 2 -- .../magnification/MagnificationControllerTest.java | 22 +--------------------- 6 files changed, 7 insertions(+), 42 deletions(-) (limited to 'services/accessibility') diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index d4af7b765254..140be868703a 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -3797,13 +3797,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub || (Flags.enableMagnificationMultipleFingerMultipleTapGesture() && userState.isMagnificationTwoFingerTripleTapEnabledLocked())); - final boolean createConnectionForCurrentCapability = - com.android.window.flags.Flags.alwaysDrawMagnificationFullscreenBorder() - || (userState.getMagnificationCapabilitiesLocked() - != Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN); - - final boolean connect = (shortcutEnabled && createConnectionForCurrentCapability) - || userHasMagnificationServicesLocked(userState); + final boolean connect = shortcutEnabled || userHasMagnificationServicesLocked(userState); getMagnificationConnectionManager().requestConnection(connect); } @@ -4854,8 +4848,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub getMagnificationConnectionManager().setConnection(connection); - if (com.android.window.flags.Flags.alwaysDrawMagnificationFullscreenBorder() - && connection == null + if (connection == null && mMagnificationController.isFullScreenMagnificationControllerInitialized()) { // Since the connection does not exist, the system ui cannot provide the border // implementation for fullscreen magnification. So we call reset to deactivate the diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java index d3d80e12313f..11b8ccb70dfb 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java @@ -699,10 +699,9 @@ public class FullScreenMagnificationController implements if (!mRegistered) { return false; } - // If the border implementation is on system ui side but the connection is not + // The border implementation is on system ui side but the connection is not // established, the fullscreen magnification should not work. - if (com.android.window.flags.Flags.alwaysDrawMagnificationFullscreenBorder() - && !mMagnificationConnectionStateSupplier.get()) { + if (!mMagnificationConnectionStateSupplier.get()) { return false; } if (DEBUG) { diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java index 51c4305061f8..058b2be5f4b3 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java @@ -51,7 +51,6 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.server.LocalServices; import com.android.server.accessibility.AccessibilityManagerService; import com.android.server.wm.WindowManagerInternal; -import com.android.window.flags.Flags; import java.util.concurrent.Executor; @@ -634,10 +633,8 @@ public class MagnificationController implements MagnificationConnectionManager.C @Override public void onFullScreenMagnificationActivationState(int displayId, boolean activated) { - if (Flags.alwaysDrawMagnificationFullscreenBorder()) { - getMagnificationConnectionManager() - .onFullscreenMagnificationActivationChanged(displayId, activated); - } + getMagnificationConnectionManager() + .onFullscreenMagnificationActivationChanged(displayId, activated); if (activated) { synchronized (mLock) { diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java index d5b930769e43..505be4b7e0dd 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java @@ -38,7 +38,6 @@ import static com.android.internal.accessibility.common.ShortcutConstants.UserSh import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.SOFTWARE; import static com.android.internal.accessibility.dialog.AccessibilityButtonChooserActivity.EXTRA_TYPE_TO_CHOOSE; import static com.android.server.accessibility.AccessibilityManagerService.ACTION_LAUNCH_HEARING_DEVICES_DIALOG; -import static com.android.window.flags.Flags.FLAG_ALWAYS_DRAW_MAGNIFICATION_FULLSCREEN_BORDER; import static com.google.common.truth.Truth.assertThat; @@ -601,7 +600,6 @@ public class AccessibilityManagerServiceTest { } @Test - @EnableFlags(FLAG_ALWAYS_DRAW_MAGNIFICATION_FULLSCREEN_BORDER) public void testSetConnectionNull_borderFlagEnabled_unregisterFullScreenMagnification() throws RemoteException { mFakePermissionEnforcer.grant(Manifest.permission.STATUS_BAR_SERVICE); diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java index 7e0c12a5a545..ac27a971102a 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java @@ -21,7 +21,6 @@ import static android.accessibilityservice.MagnificationConfig.MAGNIFICATION_MOD import static com.android.server.accessibility.Flags.FLAG_MAGNIFICATION_ENLARGE_POINTER_BUGFIX; import static com.android.server.accessibility.magnification.FullScreenMagnificationController.MagnificationInfoChangedCallback; import static com.android.server.accessibility.magnification.MockMagnificationConnection.TEST_DISPLAY; -import static com.android.window.flags.Flags.FLAG_ALWAYS_DRAW_MAGNIFICATION_FULLSCREEN_BORDER; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -308,7 +307,6 @@ public class FullScreenMagnificationControllerTest { } @Test - @RequiresFlagsEnabled(FLAG_ALWAYS_DRAW_MAGNIFICATION_FULLSCREEN_BORDER) public void testSetScale_noConnection_doNothing() { register(TEST_DISPLAY); diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java index f0d3456a39de..c878799109dc 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java @@ -57,10 +57,6 @@ import android.hardware.display.DisplayManagerInternal; import android.os.Looper; import android.os.RemoteException; import android.os.UserHandle; -import android.platform.test.annotations.RequiresFlagsDisabled; -import android.platform.test.annotations.RequiresFlagsEnabled; -import android.platform.test.flag.junit.CheckFlagsRule; -import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.provider.Settings; import android.test.mock.MockContentResolver; import android.testing.DexmakerShareClassLoaderRule; @@ -82,7 +78,6 @@ import com.android.server.accessibility.AccessibilityTraceManager; import com.android.server.accessibility.test.MessageCapturingHandler; import com.android.server.input.InputManagerInternal; import com.android.server.wm.WindowManagerInternal; -import com.android.window.flags.Flags; import org.junit.After; import org.junit.Before; @@ -101,9 +96,6 @@ import org.mockito.stubbing.Answer; @RunWith(AndroidJUnit4.class) public class MagnificationControllerTest { - @Rule - public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); - private static final int TEST_DISPLAY = Display.DEFAULT_DISPLAY; private static final int TEST_SERVICE_ID = 1; private static final Region INITIAL_SCREEN_MAGNIFICATION_REGION = @@ -1365,8 +1357,7 @@ public class MagnificationControllerTest { } @Test - @RequiresFlagsEnabled(Flags.FLAG_ALWAYS_DRAW_MAGNIFICATION_FULLSCREEN_BORDER) - public void onFullscreenMagnificationActivationState_systemUiBorderFlagOn_notifyConnection() { + public void onFullscreenMagnificationActivationState_notifyConnection() { mMagnificationController.onFullScreenMagnificationActivationState( TEST_DISPLAY, /* activated= */ true); @@ -1374,17 +1365,6 @@ public class MagnificationControllerTest { .onFullscreenMagnificationActivationChanged(TEST_DISPLAY, /* activated= */ true); } - @Test - @RequiresFlagsDisabled(Flags.FLAG_ALWAYS_DRAW_MAGNIFICATION_FULLSCREEN_BORDER) - public void - onFullscreenMagnificationActivationState_systemUiBorderFlagOff_neverNotifyConnection() { - mMagnificationController.onFullScreenMagnificationActivationState( - TEST_DISPLAY, /* activated= */ true); - - verify(mMagnificationConnectionManager, never()) - .onFullscreenMagnificationActivationChanged(TEST_DISPLAY, /* activated= */ true); - } - private void setMagnificationEnabled(int mode) throws RemoteException { setMagnificationEnabled(mode, MAGNIFIED_CENTER_X, MAGNIFIED_CENTER_Y); } -- cgit v1.2.3-59-g8ed1b From 3aa2664a7f266b771d1ec0de8fdba2c410354efe Mon Sep 17 00:00:00 2001 From: Cam Bickel Date: Fri, 22 Nov 2024 17:49:53 +0000 Subject: Cleanup flag "skip_package_change_before_user_..." Full flag name: "skip_package_change_before_user_switch" Bug: b/340927041 Test: presubmit Flag: EXEMPT flag cleanup Change-Id: I86cb9acdbac252768c5725b89e17847e9980831f --- .../com/android/server/accessibility/AccessibilityManagerService.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'services/accessibility') diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index d4af7b765254..36706266b153 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -6550,8 +6550,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub // Only continue setting up the packages if the service has been initialized. // See: b/340927041 - if (Flags.skipPackageChangeBeforeUserSwitch() - && !mManagerService.isServiceInitializedLocked()) { + if (!mManagerService.isServiceInitializedLocked()) { Slog.w(LOG_TAG, "onSomePackagesChanged: service not initialized, skip the callback."); return; -- cgit v1.2.3-59-g8ed1b From c73526a908ca050320b80f8b1b8510bd2aacfbd4 Mon Sep 17 00:00:00 2001 From: Cam Bickel Date: Fri, 22 Nov 2024 18:18:06 +0000 Subject: Cleanup flag "send_hover_events_based_on_event..." Full flag name: "send_hover_events_based_on_event_stream" Bug: b/314251047 Test: atest FrameworksServicesTests:TouchExplorerTest Flag: EXEMPT flag cleanup Change-Id: I6c6c3be62a7fdfe3cebf05be0fc8d61e9b8e5c05 --- .../server/accessibility/gestures/TouchExplorer.java | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) (limited to 'services/accessibility') diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java index 0ed239e442e7..0cbbf6da022b 100644 --- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java +++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java @@ -240,10 +240,7 @@ public class TouchExplorer extends BaseEventStreamTransformation } private void clear(MotionEvent event, int policyFlags) { - if (mState.isTouchExploring() || Flags.sendHoverEventsBasedOnEventStream()) { - // If a touch exploration gesture is in progress send events for its end. - sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags); - } + sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags); mDraggingPointerId = INVALID_POINTER_ID; // Send exit to any pointers that we have delivered as part of delegating or dragging. mDispatcher.sendUpForInjectedDownPointers(event, policyFlags); @@ -562,10 +559,7 @@ public class TouchExplorer extends BaseEventStreamTransformation // clear any hover events that might have been queued and never sent. mSendHoverEnterAndMoveDelayed.clear(); mSendHoverExitDelayed.cancel(); - // If a touch exploration gesture is in progress send events for its end. - if (mState.isTouchExploring() || Flags.sendHoverEventsBasedOnEventStream()) { - sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags); - } + sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags); if (mState.isClear()) { if (!mSendHoverEnterAndMoveDelayed.isPending()) { // Queue a delayed transition to STATE_TOUCH_EXPLORING. @@ -1599,9 +1593,7 @@ public class TouchExplorer extends BaseEventStreamTransformation if (mEvents.size() == 0) { return; } - if (Flags.sendHoverEventsBasedOnEventStream()) { - sendHoverExitAndTouchExplorationGestureEndIfNeeded(mPolicyFlags); - } + sendHoverExitAndTouchExplorationGestureEndIfNeeded(mPolicyFlags); // Send an accessibility event to announce the touch exploration start. mDispatcher.sendAccessibilityEvent(TYPE_TOUCH_EXPLORATION_GESTURE_START); if (isSendMotionEventsEnabled()) { -- cgit v1.2.3-59-g8ed1b From ed6070c6f9eea7b372c6372c1b13ddabb8f75aa5 Mon Sep 17 00:00:00 2001 From: Cam Bickel Date: Fri, 22 Nov 2024 21:18:39 +0000 Subject: Cleanup flag "clear_default_from_a11y_shortcut..." Full flag name: "clear_default_from_a11y_shortcut_target_service_restore" Bug: b/341374402 Test: atest AccessibilityManagerServiceTest Flag: EXEMPT flag cleanup Change-Id: I2e47cd13305e121d8002eae17410c21b27462d29 --- .../server/accessibility/AccessibilityManagerService.java | 3 +-- .../accessibility/AccessibilityManagerServiceTest.java | 12 +++--------- 2 files changed, 4 insertions(+), 11 deletions(-) (limited to 'services/accessibility') diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index d4af7b765254..f1e375a858ff 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -2269,8 +2269,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub mContext, shortcutType, userState.mUserId)) : userState.getShortcutTargetsLocked(shortcutType); - if (Flags.clearDefaultFromA11yShortcutTargetServiceRestore() - && shortcutType == HARDWARE) { + if (shortcutType == HARDWARE) { final String defaultService = mContext.getString(R.string.config_defaultAccessibilityService); final ComponentName defaultServiceComponent = TextUtils.isEmpty(defaultService) diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java index d5b930769e43..429c4bddd1f1 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java @@ -1818,9 +1818,7 @@ public class AccessibilityManagerServiceTest { } @Test - @EnableFlags({ - android.view.accessibility.Flags.FLAG_RESTORE_A11Y_SHORTCUT_TARGET_SERVICE, - Flags.FLAG_CLEAR_DEFAULT_FROM_A11Y_SHORTCUT_TARGET_SERVICE_RESTORE}) + @EnableFlags(android.view.accessibility.Flags.FLAG_RESTORE_A11Y_SHORTCUT_TARGET_SERVICE) public void restoreShortcutTargets_hardware_alreadyHadDefaultService_doesNotClear() { final String serviceDefault = TARGET_STANDARD_A11Y_SERVICE_NAME; mTestableContext.getOrCreateTestableResources().addOverride( @@ -1846,9 +1844,7 @@ public class AccessibilityManagerServiceTest { } @Test - @EnableFlags({ - android.view.accessibility.Flags.FLAG_RESTORE_A11Y_SHORTCUT_TARGET_SERVICE, - Flags.FLAG_CLEAR_DEFAULT_FROM_A11Y_SHORTCUT_TARGET_SERVICE_RESTORE}) + @EnableFlags(android.view.accessibility.Flags.FLAG_RESTORE_A11Y_SHORTCUT_TARGET_SERVICE) public void restoreShortcutTargets_hardware_didNotHaveDefaultService_clearsDefaultService() { final String serviceDefault = TARGET_STANDARD_A11Y_SERVICE_NAME; final String serviceRestored = TARGET_ALWAYS_ON_A11Y_SERVICE.flattenToString(); @@ -1873,9 +1869,7 @@ public class AccessibilityManagerServiceTest { } @Test - @EnableFlags({ - android.view.accessibility.Flags.FLAG_RESTORE_A11Y_SHORTCUT_TARGET_SERVICE, - Flags.FLAG_CLEAR_DEFAULT_FROM_A11Y_SHORTCUT_TARGET_SERVICE_RESTORE}) + @EnableFlags(Flags.FLAG_CLEAR_DEFAULT_FROM_A11Y_SHORTCUT_TARGET_SERVICE_RESTORE) public void restoreShortcutTargets_hardware_nullSetting_clearsDefaultService() { final String serviceDefault = TARGET_STANDARD_A11Y_SERVICE_NAME; final String serviceRestored = TARGET_ALWAYS_ON_A11Y_SERVICE.flattenToString(); -- cgit v1.2.3-59-g8ed1b From 320cc6ffa6a4389a541dccbc57da4d378a692b18 Mon Sep 17 00:00:00 2001 From: Cam Bickel Date: Fri, 22 Nov 2024 22:26:06 +0000 Subject: Cleanup flag "do_not_reset_key_event_state" Bug: b/331900630 Test: presubmit Flag: EXEMPT flag cleanup Change-Id: I97b3f3509af84c0e8360e10e254bbd6c639aa489 --- .../com/android/server/accessibility/AccessibilityInputFilter.java | 4 ---- 1 file changed, 4 deletions(-) (limited to 'services/accessibility') diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java index 7ef6aace1538..e1b6c9c5aa42 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java @@ -409,10 +409,6 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo final int eventSource = event.getSource(); final int displayId = event.getDisplayId(); if ((policyFlags & WindowManagerPolicy.FLAG_PASS_TO_USER) == 0) { - if (!Flags.doNotResetKeyEventState()) { - state.reset(); - clearEventStreamHandler(displayId, eventSource); - } if (DEBUG) { Slog.d(TAG, "Not processing event " + event); } -- cgit v1.2.3-59-g8ed1b From 57a5341d64370fbd821650a609ca72e9e36288df Mon Sep 17 00:00:00 2001 From: Cam Bickel Date: Tue, 26 Nov 2024 00:36:19 +0000 Subject: Cleanup flag "proxy_use_apps_on_virtual_device..." Full flag name: "proxy_use_apps_on_virtual_device_listener" Bug: b/286587811 Test: atest ProxyManagerTest Flag: EXEMPT flag cleanup Change-Id: If75acc809d66e589040ba5c96bd2df384d515c7e --- .../android/server/accessibility/ProxyManager.java | 72 +++++++++------------- .../server/accessibility/ProxyManagerTest.java | 7 --- 2 files changed, 28 insertions(+), 51 deletions(-) (limited to 'services/accessibility') diff --git a/services/accessibility/java/com/android/server/accessibility/ProxyManager.java b/services/accessibility/java/com/android/server/accessibility/ProxyManager.java index da11a76d5282..f8551457d04d 100644 --- a/services/accessibility/java/com/android/server/accessibility/ProxyManager.java +++ b/services/accessibility/java/com/android/server/accessibility/ProxyManager.java @@ -183,14 +183,12 @@ public class ProxyManager { synchronized (mLock) { mProxyA11yServiceConnections.put(displayId, connection); - if (Flags.proxyUseAppsOnVirtualDeviceListener()) { - if (mAppsOnVirtualDeviceListener == null) { - mAppsOnVirtualDeviceListener = allRunningUids -> - notifyProxyOfRunningAppsChange(allRunningUids); - final VirtualDeviceManagerInternal localVdm = getLocalVdm(); - if (localVdm != null) { - localVdm.registerAppsOnVirtualDeviceListener(mAppsOnVirtualDeviceListener); - } + if (mAppsOnVirtualDeviceListener == null) { + mAppsOnVirtualDeviceListener = allRunningUids -> + notifyProxyOfRunningAppsChange(allRunningUids); + final VirtualDeviceManagerInternal localVdm = getLocalVdm(); + if (localVdm != null) { + localVdm.registerAppsOnVirtualDeviceListener(mAppsOnVirtualDeviceListener); } } if (mProxyA11yServiceConnections.size() == 1) { @@ -331,14 +329,12 @@ public class ProxyManager { // device. if (!isProxyedDeviceId(deviceId)) { synchronized (mLock) { - if (Flags.proxyUseAppsOnVirtualDeviceListener()) { - if (mProxyA11yServiceConnections.size() == 0) { - final VirtualDeviceManagerInternal localVdm = getLocalVdm(); - if (localVdm != null && mAppsOnVirtualDeviceListener != null) { - localVdm.unregisterAppsOnVirtualDeviceListener( - mAppsOnVirtualDeviceListener); - mAppsOnVirtualDeviceListener = null; - } + if (mProxyA11yServiceConnections.size() == 0) { + final VirtualDeviceManagerInternal localVdm = getLocalVdm(); + if (localVdm != null && mAppsOnVirtualDeviceListener != null) { + localVdm.unregisterAppsOnVirtualDeviceListener( + mAppsOnVirtualDeviceListener); + mAppsOnVirtualDeviceListener = null; } } mSystemSupport.removeDeviceIdLocked(deviceId); @@ -671,8 +667,7 @@ public class ProxyManager { + getLastSentStateLocked(deviceId)); Slog.v(LOG_TAG, "force update: " + forceUpdate); } - if ((getLastSentStateLocked(deviceId)) != proxyState - || (Flags.proxyUseAppsOnVirtualDeviceListener() && forceUpdate)) { + if ((getLastSentStateLocked(deviceId)) != proxyState || forceUpdate) { setLastStateLocked(deviceId, proxyState); mMainHandler.post(() -> { synchronized (mLock) { @@ -873,33 +868,22 @@ public class ProxyManager { for (int i = 0; i < clients.getRegisteredCallbackCount(); i++) { final AccessibilityManagerService.Client client = ((AccessibilityManagerService.Client) clients.getRegisteredCallbackCookie(i)); - if (Flags.proxyUseAppsOnVirtualDeviceListener()) { - if (deviceId == DEVICE_ID_DEFAULT || deviceId == DEVICE_ID_INVALID) { - continue; - } - boolean uidBelongsToDevice = - localVdm.getDeviceIdsForUid(client.mUid).contains(deviceId); - if (client.mDeviceId != deviceId && uidBelongsToDevice) { - if (DEBUG) { - Slog.v(LOG_TAG, "Packages moved to device id " + deviceId + " are " - + Arrays.toString(client.mPackageNames)); - } - client.mDeviceId = deviceId; - } else if (client.mDeviceId == deviceId && !uidBelongsToDevice) { - client.mDeviceId = DEVICE_ID_DEFAULT; - if (DEBUG) { - Slog.v(LOG_TAG, "Packages moved to the default device from device id " - + deviceId + " are " + Arrays.toString(client.mPackageNames)); - } + if (deviceId == DEVICE_ID_DEFAULT || deviceId == DEVICE_ID_INVALID) { + continue; + } + boolean uidBelongsToDevice = + localVdm.getDeviceIdsForUid(client.mUid).contains(deviceId); + if (client.mDeviceId != deviceId && uidBelongsToDevice) { + if (DEBUG) { + Slog.v(LOG_TAG, "Packages moved to device id " + deviceId + " are " + + Arrays.toString(client.mPackageNames)); } - } else { - if (deviceId != DEVICE_ID_DEFAULT && deviceId != DEVICE_ID_INVALID - && localVdm.getDeviceIdsForUid(client.mUid).contains(deviceId)) { - if (DEBUG) { - Slog.v(LOG_TAG, "Packages moved to device id " + deviceId + " are " - + Arrays.toString(client.mPackageNames)); - } - client.mDeviceId = deviceId; + client.mDeviceId = deviceId; + } else if (client.mDeviceId == deviceId && !uidBelongsToDevice) { + client.mDeviceId = DEVICE_ID_DEFAULT; + if (DEBUG) { + Slog.v(LOG_TAG, "Packages moved to the default device from device id " + + deviceId + " are " + Arrays.toString(client.mPackageNames)); } } } diff --git a/services/tests/servicestests/src/com/android/server/accessibility/ProxyManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/ProxyManagerTest.java index 52b33db556e6..f371823473ef 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/ProxyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/ProxyManagerTest.java @@ -51,9 +51,6 @@ import android.os.IBinder; import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.test.FakePermissionEnforcer; -import android.platform.test.annotations.RequiresFlagsEnabled; -import android.platform.test.flag.junit.CheckFlagsRule; -import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.platform.test.flag.junit.SetFlagsRule; import android.util.ArraySet; import android.view.KeyEvent; @@ -97,9 +94,6 @@ public class ProxyManagerTest { private static final int DEVICE_ID = 10; private static final int STREAMED_CALLING_UID = 9876; - @Rule - public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); - @Rule public SetFlagsRule mSetFlagsRule = new SetFlagsRule(); @@ -243,7 +237,6 @@ public class ProxyManagerTest { * app changes to the proxy device. */ @Test - @RequiresFlagsEnabled(Flags.FLAG_PROXY_USE_APPS_ON_VIRTUAL_DEVICE_LISTENER) public void testUpdateProxyOfRunningAppsChange_changedUidIsStreamedApp_propagatesChange() { final VirtualDeviceManagerInternal localVdm = Mockito.mock(VirtualDeviceManagerInternal.class); -- cgit v1.2.3-59-g8ed1b From cf89b3d25cc39f5735741e570a369958af00b962 Mon Sep 17 00:00:00 2001 From: Menghan Li Date: Mon, 25 Nov 2024 04:04:59 +0000 Subject: Add aconfig flag to use HaTS for low vision feedback Bug: 380346799 Test: build Flag: com.android.server.accessibility.enable_low_vision_hats Change-Id: I8802aa25ccc00e888b07b673661ead6685355480 --- services/accessibility/accessibility.aconfig | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) (limited to 'services/accessibility') diff --git a/services/accessibility/accessibility.aconfig b/services/accessibility/accessibility.aconfig index a0b989b44f4f..ad87ceaf6f38 100644 --- a/services/accessibility/accessibility.aconfig +++ b/services/accessibility/accessibility.aconfig @@ -86,10 +86,17 @@ flag { } flag { - name: "enable_hardware_shortcut_disables_warning" - namespace: "accessibility" - description: "When the user purposely enables the hardware shortcut, preemptively disables the first-time warning message." - bug: "287065325" + name: "enable_hardware_shortcut_disables_warning" + namespace: "accessibility" + description: "When the user purposely enables the hardware shortcut, preemptively disables the first-time warning message." + bug: "287065325" +} + +flag { + name: "enable_low_vision_hats" + namespace: "accessibility" + description: "Use HaTS for low vision feedback." + bug: "380346799" } flag { -- cgit v1.2.3-59-g8ed1b From 0ec80cdb0547f7dafe79937c60f3aeae2fe90c83 Mon Sep 17 00:00:00 2001 From: Cam Bickel Date: Tue, 26 Nov 2024 22:48:41 +0000 Subject: Cleanup flag "pinch_zoom_zero_min_span" Bug: b/295327792 Test: presubmit Flag: EXEMPT flag cleanup Change-Id: I0872d78ed5cc77572841b7d1c8b30808a4148eba --- .../accessibility/magnification/PanningScalingHandler.java | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) (limited to 'services/accessibility') diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/PanningScalingHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/PanningScalingHandler.java index 6b48d2bacf9d..a4568aaa7a0d 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/PanningScalingHandler.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/PanningScalingHandler.java @@ -31,7 +31,6 @@ import android.view.ScaleGestureDetector; import android.view.ViewConfiguration; import com.android.internal.R; -import com.android.server.accessibility.Flags; /** * Handles the behavior while receiving scaling and panning gestures if it's enabled. @@ -73,13 +72,9 @@ class PanningScalingHandler extends mMaxScale = maxScale; mMinScale = minScale; mBlockScroll = blockScroll; - if (Flags.pinchZoomZeroMinSpan()) { - mScaleGestureDetector = new ScaleGestureDetector(context, - ViewConfiguration.get(context).getScaledTouchSlop() * 2, - /* minSpan= */ 0, Handler.getMain(), this); - } else { - mScaleGestureDetector = new ScaleGestureDetector(context, this, Handler.getMain()); - } + mScaleGestureDetector = new ScaleGestureDetector(context, + ViewConfiguration.get(context).getScaledTouchSlop() * 2, + /* minSpan= */ 0, Handler.getMain(), this); mScrollGestureDetector = new GestureDetector(context, this, Handler.getMain()); mScaleGestureDetector.setQuickScaleEnabled(false); mMagnificationDelegate = magnificationDelegate; -- cgit v1.2.3-59-g8ed1b From 559d1005c63a759376a28cc8cc283bca4ee851bc Mon Sep 17 00:00:00 2001 From: Cam Bickel Date: Tue, 26 Nov 2024 23:02:00 +0000 Subject: Cleanup flag "manager_avoid_receiver_timeout" Bug: b/333890389 Test: atest AccessibilityManagerServiceTest Flag: EXEMPT flag cleanup Change-Id: I242508512fa37ac9ead0e151a072765a884331f2 --- .../com/android/server/accessibility/AccessibilityManagerService.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'services/accessibility') diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index 71a0fc453a95..45f157d39b2b 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -1028,8 +1028,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub intentFilter.addAction(Intent.ACTION_USER_REMOVED); intentFilter.addAction(Intent.ACTION_SETTING_RESTORED); - Handler receiverHandler = - Flags.managerAvoidReceiverTimeout() ? BackgroundThread.getHandler() : null; + Handler receiverHandler = BackgroundThread.getHandler(); mContext.registerReceiverAsUser(new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { -- cgit v1.2.3-59-g8ed1b From b73b432a1a7172d23789dd00422d17b5d503732b Mon Sep 17 00:00:00 2001 From: Cam Bickel Date: Wed, 27 Nov 2024 17:58:33 +0000 Subject: Cleanup flag "manager_package_monitor_logic_fix" Bug: b/337392123 Test: atest AccessibilityManagerServiceTest Flag: EXEMPT flag cleanup Change-Id: I47f7efaf1af2c2ec47878e3a806184eeb2e0942f --- .../accessibility/AccessibilityManagerService.java | 31 +++++++--------------- .../AccessibilityManagerServiceTest.java | 3 --- 2 files changed, 10 insertions(+), 24 deletions(-) (limited to 'services/accessibility') diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index 71a0fc453a95..1b14f8c6ef03 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -6649,28 +6649,17 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } final AccessibilityUserState userState = mManagerService.getUserStateLocked(userId); - if (Flags.managerPackageMonitorLogicFix()) { - if (!doit) { - // if we're not handling the stop here, then we only need to know - // if any of the force-stopped packages are currently enabled. - return userState.mEnabledServices.stream().anyMatch( - (comp) -> Arrays.stream(packages).anyMatch( - (pkg) -> pkg.equals(comp.getPackageName())) - ); - } else if (mManagerService.onPackagesForceStoppedLocked(packages, userState)) { - mManagerService.onUserStateChangedLocked(userState); - } - return false; - } else { - // this old logic did not properly indicate when base packageMonitor's routine - // should handle stopping the package. - if (doit && mManagerService.onPackagesForceStoppedLocked(packages, userState)) { - mManagerService.onUserStateChangedLocked(userState); - return false; - } else { - return true; - } + if (!doit) { + // if we're not handling the stop here, then we only need to know + // if any of the force-stopped packages are currently enabled. + return userState.mEnabledServices.stream().anyMatch( + (comp) -> Arrays.stream(packages).anyMatch( + (pkg) -> pkg.equals(comp.getPackageName())) + ); + } else if (mManagerService.onPackagesForceStoppedLocked(packages, userState)) { + mManagerService.onUserStateChangedLocked(userState); } + return false; } } diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java index 27de7644f6b2..f7653ece0a2a 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java @@ -1729,7 +1729,6 @@ public class AccessibilityManagerServiceTest { } @Test - @EnableFlags(Flags.FLAG_MANAGER_PACKAGE_MONITOR_LOGIC_FIX) public void onHandleForceStop_dontDoIt_packageEnabled_returnsTrue() { setupShortcutTargetServices(); AccessibilityUserState userState = mA11yms.getCurrentUserState(); @@ -1752,7 +1751,6 @@ public class AccessibilityManagerServiceTest { } @Test - @EnableFlags(Flags.FLAG_MANAGER_PACKAGE_MONITOR_LOGIC_FIX) public void onHandleForceStop_doIt_packageEnabled_returnsFalse() { setupShortcutTargetServices(); AccessibilityUserState userState = mA11yms.getCurrentUserState(); @@ -1775,7 +1773,6 @@ public class AccessibilityManagerServiceTest { } @Test - @EnableFlags(Flags.FLAG_MANAGER_PACKAGE_MONITOR_LOGIC_FIX) public void onHandleForceStop_dontDoIt_packageNotEnabled_returnsFalse() { PackageMonitor monitor = spy(mA11yms.getPackageMonitor()); when(monitor.getChangingUserId()).thenReturn(UserHandle.USER_SYSTEM); -- cgit v1.2.3-59-g8ed1b From e5fbb24e3858df269973eaa20cd07095d080fb95 Mon Sep 17 00:00:00 2001 From: Cam Bickel Date: Wed, 27 Nov 2024 19:18:26 +0000 Subject: Cleanup flag "restore_a11y_shortcut_target_..." Full flag name: "restore_a11y_shortcut_target_service" Note that on line 1869 of AccessibilityManagerServiceTest.java, a different flag is removed. This flag should have been removed in http://ag/30554709, but I accidentally removed the flag from this CL ("restore_a11y_shortcut_target_service") instead. Bug: b/341374402 Test: atest AccessibilityShortcutControllerTest SettingsHelperRestoreTest AccessibilityManagerServiceTest Flag: EXEMPT flag cleanup Change-Id: I023fed04527e20f3c547155e70c895b9eebacad3 --- .../AccessibilityShortcutController.java | 5 +-- .../AccessibilityShortcutControllerTest.java | 7 ---- .../android/providers/settings/SettingsHelper.java | 3 +- .../settings/SettingsHelperRestoreTest.java | 7 ---- .../accessibility/AccessibilityManagerService.java | 44 ++++++++++------------ .../AccessibilityManagerServiceTest.java | 4 -- 6 files changed, 21 insertions(+), 49 deletions(-) (limited to 'services/accessibility') diff --git a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java index e2be1f57b1fb..a27eeb8fdd63 100644 --- a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java +++ b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java @@ -58,7 +58,6 @@ import android.util.Slog; import android.view.Window; import android.view.WindowManager; import android.view.accessibility.AccessibilityManager; -import android.view.accessibility.Flags; import android.widget.Toast; import com.android.internal.R; @@ -289,9 +288,7 @@ public class AccessibilityShortcutController { cr, Settings.Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, DialogStatus.SHOWN, userId); } else { - if (Flags.restoreA11yShortcutTargetService()) { - enableDefaultHardwareShortcut(userId); - } + enableDefaultHardwareShortcut(userId); playNotificationTone(); if (mAlertDialog != null) { mAlertDialog.dismiss(); diff --git a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java index a6466c58dfda..74b4de1833ea 100644 --- a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java +++ b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java @@ -63,8 +63,6 @@ import android.os.Build; import android.os.Handler; import android.os.Message; import android.os.Vibrator; -import android.platform.test.annotations.EnableFlags; -import android.platform.test.flag.junit.SetFlagsRule; import android.provider.Settings; import android.speech.tts.TextToSpeech; import android.speech.tts.Voice; @@ -73,7 +71,6 @@ import android.view.Display; import android.view.Window; import android.view.WindowManager; import android.view.accessibility.AccessibilityManager; -import android.view.accessibility.Flags; import android.view.accessibility.IAccessibilityManager; import android.widget.Toast; @@ -86,7 +83,6 @@ import com.android.internal.util.test.FakeSettingsProvider; import org.junit.AfterClass; import org.junit.Before; -import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; @@ -104,8 +100,6 @@ import java.util.Set; @RunWith(AndroidJUnit4.class) public class AccessibilityShortcutControllerTest { - @Rule - public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); private static final String SERVICE_NAME_STRING = "fake.package/fake.service.name"; private static final CharSequence PACKAGE_NAME_STRING = "Service name"; private static final String SERVICE_NAME_SUMMARY = "Summary"; @@ -535,7 +529,6 @@ public class AccessibilityShortcutControllerTest { } @Test - @EnableFlags(Flags.FLAG_RESTORE_A11Y_SHORTCUT_TARGET_SERVICE) public void testOnAccessibilityShortcut_settingNull_dialogShown_enablesDefaultShortcut() throws Exception { configureDefaultAccessibilityService(); diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java index ebeee8564d2f..ea8ae7b208cd 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java @@ -242,8 +242,7 @@ public class SettingsHelper { // Don't write it to setting. Let the broadcast receiver in // AccessibilityManagerService handle restore/merging logic. return; - } else if (android.view.accessibility.Flags.restoreA11yShortcutTargetService() - && Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE.equals(name)) { + } else if (Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE.equals(name)) { // Don't write it to setting. Let the broadcast receiver in // AccessibilityManagerService handle restore/merging logic. return; diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperRestoreTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperRestoreTest.java index f64f72a74609..048d93b09967 100644 --- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperRestoreTest.java +++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperRestoreTest.java @@ -26,8 +26,6 @@ import android.content.Context; import android.content.Intent; import android.net.Uri; import android.os.Build; -import android.platform.test.annotations.EnableFlags; -import android.platform.test.flag.junit.SetFlagsRule; import android.provider.Settings; import android.provider.SettingsStringUtil; @@ -37,7 +35,6 @@ import androidx.test.runner.AndroidJUnit4; import com.android.internal.util.test.BroadcastInterceptingContext; import org.junit.Before; -import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mockito; @@ -52,9 +49,6 @@ import java.util.concurrent.ExecutionException; @RunWith(AndroidJUnit4.class) public class SettingsHelperRestoreTest { - @Rule - public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); - private static final float FLOAT_TOLERANCE = 0.01f; private Context mContext; @@ -211,7 +205,6 @@ public class SettingsHelperRestoreTest { } @Test - @EnableFlags(android.view.accessibility.Flags.FLAG_RESTORE_A11Y_SHORTCUT_TARGET_SERVICE) public void restoreAccessibilityShortcutTargetService_broadcastSent() throws ExecutionException, InterruptedException { BroadcastInterceptingContext interceptingContext = new BroadcastInterceptingContext( diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index ab556b39f516..006225d9b045 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -2257,10 +2257,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub if (shortcutType == QUICK_SETTINGS && !android.view.accessibility.Flags.a11yQsShortcut()) { return; } - if (shortcutType == HARDWARE - && !android.view.accessibility.Flags.restoreA11yShortcutTargetService()) { - return; - } synchronized (mLock) { final AccessibilityUserState userState = getUserStateLocked(UserHandle.USER_SYSTEM); @@ -2929,27 +2925,25 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub final String builderValue = builder.toString(); final String settingValue = TextUtils.isEmpty(builderValue) ? defaultEmptyString : builderValue; - if (android.view.accessibility.Flags.restoreA11yShortcutTargetService()) { - final String currentValue = Settings.Secure.getStringForUser( - mContext.getContentResolver(), settingName, userId); - if (Objects.equals(settingValue, currentValue)) { - // This logic exists to fix a bug where AccessibilityManagerService was writing - // `null` to the ACCESSIBILITY_SHORTCUT_TARGET_SERVICE setting during early boot - // during setup, due to a race condition in package scanning making A11yMS think - // that the default service was not installed. - // - // Writing `null` was implicitly causing that Setting to have the default - // `DEFAULT_OVERRIDEABLE_BY_RESTORE` property, which was preventing B&R for that - // Setting altogether. - // - // The "quick fix" here is to not write `null` if the existing value is already - // `null`. The ideal fix would be use the Settings.Secure#putStringForUser overload - // that allows override-by-restore, but the full repercussions of using that here - // have not yet been evaluated. - // TODO: b/333457719 - Evaluate and fix AccessibilityManagerService's usage of - // "overridable by restore" when writing secure settings. - return; - } + final String currentValue = Settings.Secure.getStringForUser( + mContext.getContentResolver(), settingName, userId); + if (Objects.equals(settingValue, currentValue)) { + // This logic exists to fix a bug where AccessibilityManagerService was writing + // `null` to the ACCESSIBILITY_SHORTCUT_TARGET_SERVICE setting during early boot + // during setup, due to a race condition in package scanning making A11yMS think + // that the default service was not installed. + // + // Writing `null` was implicitly causing that Setting to have the default + // `DEFAULT_OVERRIDEABLE_BY_RESTORE` property, which was preventing B&R for that + // Setting altogether. + // + // The "quick fix" here is to not write `null` if the existing value is already + // `null`. The ideal fix would be use the Settings.Secure#putStringForUser overload + // that allows override-by-restore, but the full repercussions of using that here + // have not yet been evaluated. + // TODO: b/333457719 - Evaluate and fix AccessibilityManagerService's usage of + // "overridable by restore" when writing secure settings. + return; } final long identity = Binder.clearCallingIdentity(); try { diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java index 08fdaf44f913..83869ad2575d 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java @@ -1790,7 +1790,6 @@ public class AccessibilityManagerServiceTest { } @Test - @EnableFlags(android.view.accessibility.Flags.FLAG_RESTORE_A11Y_SHORTCUT_TARGET_SERVICE) public void restoreShortcutTargets_hardware_targetsMerged() { mFakePermissionEnforcer.grant(Manifest.permission.MANAGE_ACCESSIBILITY); final String servicePrevious = TARGET_ALWAYS_ON_A11Y_SERVICE.flattenToString(); @@ -1815,7 +1814,6 @@ public class AccessibilityManagerServiceTest { } @Test - @EnableFlags(android.view.accessibility.Flags.FLAG_RESTORE_A11Y_SHORTCUT_TARGET_SERVICE) public void restoreShortcutTargets_hardware_alreadyHadDefaultService_doesNotClear() { final String serviceDefault = TARGET_STANDARD_A11Y_SERVICE_NAME; mTestableContext.getOrCreateTestableResources().addOverride( @@ -1841,7 +1839,6 @@ public class AccessibilityManagerServiceTest { } @Test - @EnableFlags(android.view.accessibility.Flags.FLAG_RESTORE_A11Y_SHORTCUT_TARGET_SERVICE) public void restoreShortcutTargets_hardware_didNotHaveDefaultService_clearsDefaultService() { final String serviceDefault = TARGET_STANDARD_A11Y_SERVICE_NAME; final String serviceRestored = TARGET_ALWAYS_ON_A11Y_SERVICE.flattenToString(); @@ -1866,7 +1863,6 @@ public class AccessibilityManagerServiceTest { } @Test - @EnableFlags(Flags.FLAG_CLEAR_DEFAULT_FROM_A11Y_SHORTCUT_TARGET_SERVICE_RESTORE) public void restoreShortcutTargets_hardware_nullSetting_clearsDefaultService() { final String serviceDefault = TARGET_STANDARD_A11Y_SERVICE_NAME; final String serviceRestored = TARGET_ALWAYS_ON_A11Y_SERVICE.flattenToString(); -- cgit v1.2.3-59-g8ed1b From e0195319f6b51cdfe71f694f6b2c750f58256fc9 Mon Sep 17 00:00:00 2001 From: Hiroki Sato Date: Mon, 2 Dec 2024 15:42:38 +0900 Subject: Small cleanup AccessibilityWindowManager#updateWindowsLocked After a flag compute_window_changes_on_a11y_v2 is removed [1], a method #populateReportedWindowLocked returns non-null. Thus we can simplify some window computation in A11yWindowManager. [1] Ifd8328fd21f5547726313c65c60d2a4b0d6095af Bug: 322444245 Test: atest AccessibilityWindowManagerTest Flag: EXEMPT cleanup after removing com.android.server.accessibility.compute_window_changes_on_a11y_v2 Change-Id: Idbb99e276b06cd742d0e3bef96171852d41cbdd7 --- .../accessibility/AccessibilityWindowManager.java | 25 +++++----------------- 1 file changed, 5 insertions(+), 20 deletions(-) (limited to 'services/accessibility') diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java index 8b870dbaa100..b7fd09f7b594 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java @@ -832,20 +832,12 @@ public class AccessibilityWindowManager { != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID; } - boolean hasWindowIgnore = false; if (windowCount > 0) { - for (int i = 0; i < windowCount; i++) { - final WindowInfo windowInfo = windows.get(i); - final AccessibilityWindowInfo window; - if (mTrackingWindows) { - window = populateReportedWindowLocked(userId, windowInfo, oldWindowsById); - if (window == null) { - hasWindowIgnore = true; - } - } else { - window = null; - } - if (window != null) { + if (mTrackingWindows) { + for (int i = 0; i < windowCount; i++) { + final WindowInfo windowInfo = windows.get(i); + final AccessibilityWindowInfo window = + populateReportedWindowLocked(userId, windowInfo, oldWindowsById); // Flip layers in list to be consistent with AccessibilityService#getWindows window.setLayer(windowCount - 1 - window.getLayer()); @@ -870,13 +862,6 @@ public class AccessibilityWindowManager { } } final int accessibilityWindowCount = mWindows.size(); - // Re-order the window layer of all windows in the windows list because there's - // window not been added into the windows list. - if (hasWindowIgnore) { - for (int i = 0; i < accessibilityWindowCount; i++) { - mWindows.get(i).setLayer(accessibilityWindowCount - 1 - i); - } - } if (isTopFocusedDisplay) { if (mTouchInteractionInProgress && activeWindowGone) { mActiveWindowId = mTopFocusedWindowId; -- cgit v1.2.3-59-g8ed1b From 8fa8705e88d293acf5a5a2b9c897f763ef4014d1 Mon Sep 17 00:00:00 2001 From: Chun-Ku Lin Date: Tue, 3 Dec 2024 01:29:21 +0000 Subject: Remove gesture shortcut restoration/merge logic from AMS The gesture shortcut is restored in SettingsBackupAgent. In Setup Wizard, there is no way for the user to turn on a gesture shortcut because in Setup Wizard, it's in button navigation mode, hence no gesture navigation available. Bug: N/A Test: N/A Flag: EXEMPT comment only Change-Id: I5fcd606ea645ae57b71420656ce0c30916471e65 --- .../android/server/accessibility/AccessibilityManagerService.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'services/accessibility') diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index 71a0fc453a95..b5e69e19ba53 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -1071,8 +1071,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub newValue, restoredFromSdk); } } + // Currently in SUW, the user can't see gesture shortcut option as the + // navigation system is set to button navigation. We'll rely on the + // SettingsBackupAgent to restore the settings since we don't + // need to merge an empty gesture target. case Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, - Settings.Secure.ACCESSIBILITY_GESTURE_TARGETS, Settings.Secure.ACCESSIBILITY_QS_TARGETS, Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE -> restoreShortcutTargets(newValue, -- cgit v1.2.3-59-g8ed1b