diff --git a/core/java/android/os/RemoteCallbackList.java b/core/java/android/os/RemoteCallbackList.java index 9db58eec3e1a8..6656b00d457cd 100644 --- a/core/java/android/os/RemoteCallbackList.java +++ b/core/java/android/os/RemoteCallbackList.java @@ -18,6 +18,8 @@ package android.os; import android.util.ArrayMap; +import java.util.function.Consumer; + /** * Takes care of the grunt work of maintaining a list of remote interfaces, * typically for the use of performing callbacks from a @@ -307,6 +309,23 @@ public class RemoteCallbackList { } } + /** + * Performs {@code action} on each callback, calling + * {@link #beginBroadcast()}/{@link #finishBroadcast()} before/after looping + * + * @hide + */ + public void broadcast(Consumer action) { + int itemCount = beginBroadcast(); + try { + for (int i = 0; i < itemCount; i++) { + action.accept(getBroadcastItem(i)); + } + } finally { + finishBroadcast(); + } + } + /** * Returns the number of registered callbacks. Note that the number of registered * callbacks may differ from the value returned by {@link #beginBroadcast()} since diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java index fe888ec56c093..c9f9f310e0b8c 100644 --- a/core/java/android/view/accessibility/AccessibilityManager.java +++ b/core/java/android/view/accessibility/AccessibilityManager.java @@ -40,6 +40,8 @@ import android.util.Log; import android.view.IWindow; import android.view.View; +import com.android.internal.util.IntPair; + import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -109,6 +111,8 @@ public final class AccessibilityManager { boolean mIsEnabled; + int mRelevantEventTypes = AccessibilityEvent.TYPES_ALL_MASK; + boolean mIsTouchExplorationEnabled; boolean mIsHighTextContrastEnabled; @@ -203,6 +207,11 @@ public final class AccessibilityManager { public void notifyServicesStateChanged() { mHandler.obtainMessage(MyHandler.MSG_NOTIFY_SERVICES_STATE_CHANGED).sendToTarget(); } + + @Override + public void setRelevantEventTypes(int eventTypes) { + mRelevantEventTypes = eventTypes; + } }; /** @@ -362,6 +371,14 @@ public final class AccessibilityManager { return; } } + if ((event.getEventType() & mRelevantEventTypes) == 0) { + if (DEBUG) { + Log.i(LOG_TAG, "Not dispatching irrelevant event: " + event + + " that is not among " + + AccessibilityEvent.eventTypeToString(mRelevantEventTypes)); + } + return; + } userId = mUserId; } try { @@ -865,8 +882,9 @@ public final class AccessibilityManager { } try { - final int stateFlags = service.addClient(mClient, mUserId); - setStateLocked(stateFlags); + final long userStateAndRelevantEvents = service.addClient(mClient, mUserId); + setStateLocked(IntPair.first(userStateAndRelevantEvents)); + mRelevantEventTypes = IntPair.second(userStateAndRelevantEvents); mService = service; } catch (RemoteException re) { Log.e(LOG_TAG, "AccessibilityManagerService is dead", re); diff --git a/core/java/android/view/accessibility/IAccessibilityManager.aidl b/core/java/android/view/accessibility/IAccessibilityManager.aidl index 157980f6cb831..06cb5dca8afa9 100644 --- a/core/java/android/view/accessibility/IAccessibilityManager.aidl +++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl @@ -38,7 +38,7 @@ interface IAccessibilityManager { oneway void sendAccessibilityEvent(in AccessibilityEvent uiEvent, int userId); - int addClient(IAccessibilityManagerClient client, int userId); + long addClient(IAccessibilityManagerClient client, int userId); List getInstalledAccessibilityServiceList(int userId); diff --git a/core/java/android/view/accessibility/IAccessibilityManagerClient.aidl b/core/java/android/view/accessibility/IAccessibilityManagerClient.aidl index 045ac917e4b82..9cc0315280c73 100644 --- a/core/java/android/view/accessibility/IAccessibilityManagerClient.aidl +++ b/core/java/android/view/accessibility/IAccessibilityManagerClient.aidl @@ -25,5 +25,8 @@ package android.view.accessibility; oneway interface IAccessibilityManagerClient { void setState(int stateFlags); + void notifyServicesStateChanged(); + + void setRelevantEventTypes(int eventTypes); } diff --git a/core/java/com/android/internal/util/IntPair.java b/core/java/com/android/internal/util/IntPair.java new file mode 100644 index 0000000000000..79925077dd8a2 --- /dev/null +++ b/core/java/com/android/internal/util/IntPair.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2017 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.internal.util; + +/** + * Utilities for treating a {@code long} as a pair of {@code int}s + * + * @hide + */ +public class IntPair { + private IntPair() {} + + public static long of(int first, int second) { + return (((long)first) << 32) | ((long)second & 0xffffffffL); + } + + public static int first(long intPair) { + return (int)(intPair >> 32); + } + + public static int second(long intPair) { + return (int)intPair; + } +} diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index e0d78063ffb8b..b56035fb84caa 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -50,6 +50,7 @@ import android.graphics.Point; 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.net.Uri; import android.os.Binder; @@ -69,7 +70,6 @@ import android.os.UserHandle; import android.os.UserManager; import android.os.UserManagerInternal; import android.provider.Settings; -import android.hardware.fingerprint.IFingerprintService; import android.provider.SettingsStringUtil.ComponentNameSet; import android.provider.SettingsStringUtil.SettingStringHelper; import android.text.TextUtils; @@ -100,7 +100,9 @@ import android.view.accessibility.IAccessibilityManagerClient; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.content.PackageMonitor; +import com.android.internal.os.HandlerCaller; import com.android.internal.os.SomeArgs; +import com.android.internal.util.IntPair; import com.android.server.LocalServices; import com.android.server.policy.AccessibilityShortcutController; import com.android.server.statusbar.StatusBarManagerInternal; @@ -120,6 +122,7 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; +import java.util.function.Consumer; /** * This class is instantiated by the system as a system level service and can be @@ -434,7 +437,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { } @Override - public int addClient(IAccessibilityManagerClient client, int userId) { + public long addClient(IAccessibilityManagerClient client, int userId) { synchronized (mLock) { // We treat calls from a profile as if made by its parent as profiles // share the accessibility state of the parent. The call below @@ -450,7 +453,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { if (DEBUG) { Slog.i(LOG_TAG, "Added global client for pid:" + Binder.getCallingPid()); } - return userState.getClientState(); + return IntPair.of( + userState.getClientState(), userState.mLastSentRelevantEventTypes); } else { userState.mUserClients.register(client); // If this client is not for the current user we do not @@ -460,7 +464,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { Slog.i(LOG_TAG, "Added user client for pid:" + Binder.getCallingPid() + " and userId:" + mCurrentUserId); } - return (resolvedUserId == mCurrentUserId) ? userState.getClientState() : 0; + return IntPair.of( + (resolvedUserId == mCurrentUserId) ? userState.getClientState() : 0, + userState.mLastSentRelevantEventTypes); } } } @@ -1323,6 +1329,35 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { scheduleNotifyClientsOfServicesStateChange(userState); } + private void updateRelevantEventsLocked(UserState userState) { + int relevantEventTypes = AccessibilityCache.CACHE_CRITICAL_EVENTS_MASK; + for (Service service : userState.mBoundServices) { + relevantEventTypes |= service.mEventTypes; + } + int finalRelevantEventTypes = relevantEventTypes; + + if (userState.mLastSentRelevantEventTypes != finalRelevantEventTypes) { + userState.mLastSentRelevantEventTypes = finalRelevantEventTypes; + mMainHandler.obtainMessage(MainHandler.MSG_SEND_RELEVANT_EVENTS_CHANGED_TO_CLIENTS, + userState.mUserId, finalRelevantEventTypes); + mMainHandler.post(() -> { + broadcastToClients(userState, (client) -> { + try { + client.setRelevantEventTypes(finalRelevantEventTypes); + } catch (RemoteException re) { + /* ignore */ + } + }); + }); + } + } + + private void broadcastToClients( + UserState userState, Consumer clientAction) { + mGlobalClients.broadcast(clientAction); + userState.mUserClients.broadcast(clientAction); + } + /** * 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 @@ -1633,6 +1668,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { scheduleUpdateFingerprintGestureHandling(userState); scheduleUpdateInputFilter(userState); scheduleUpdateClientsIfNeededLocked(userState); + updateRelevantEventsLocked(userState); } private void updateAccessibilityFocusBehaviorLocked(UserState userState) { @@ -2281,6 +2317,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { public static final int MSG_CLEAR_ACCESSIBILITY_FOCUS = 9; public static final int MSG_SEND_SERVICES_STATE_CHANGED_TO_CLIENTS = 10; public static final int MSG_UPDATE_FINGERPRINT = 11; + public static final int MSG_SEND_RELEVANT_EVENTS_CHANGED_TO_CLIENTS = 12; public MainHandler(Looper looper) { super(looper); @@ -2351,6 +2388,22 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { case MSG_UPDATE_FINGERPRINT: { updateFingerprintGestureHandling((UserState) msg.obj); } break; + + case MSG_SEND_RELEVANT_EVENTS_CHANGED_TO_CLIENTS: { + final int userId = msg.arg1; + final int relevantEventTypes = msg.arg2; + final UserState userState; + synchronized (mLock) { + userState = getUserStateLocked(userId); + } + broadcastToClients(userState, (client) -> { + try { + client.setRelevantEventTypes(relevantEventTypes); + } catch (RemoteException re) { + /* ignore */ + } + }); + } break; } } @@ -2380,19 +2433,13 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { private void sendStateToClients(int clientState, RemoteCallbackList clients) { - try { - final int userClientCount = clients.beginBroadcast(); - for (int i = 0; i < userClientCount; i++) { - IAccessibilityManagerClient client = clients.getBroadcastItem(i); - try { - client.setState(clientState); - } catch (RemoteException re) { - /* ignore */ - } + clients.broadcast((client) -> { + try { + client.setState(clientState); + } catch (RemoteException re) { + /* ignore */ } - } finally { - clients.finishBroadcast(); - } + }); } private void notifyClientsOfServicesStateChange( @@ -4710,6 +4757,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { public final CopyOnWriteArrayList mBoundServices = new CopyOnWriteArrayList<>(); + public int mLastSentRelevantEventTypes = AccessibilityEvent.TYPES_ALL_MASK; + public final Map mComponentNameToServiceMap = new HashMap<>(); diff --git a/services/tests/servicestests/src/com/android/server/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/AccessibilityManagerServiceTest.java index 60842a6bf727c..340c62405814a 100644 --- a/services/tests/servicestests/src/com/android/server/AccessibilityManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/AccessibilityManagerServiceTest.java @@ -34,6 +34,8 @@ import android.view.accessibility.AccessibilityManager; import android.view.accessibility.IAccessibilityManager; import android.view.accessibility.IAccessibilityManagerClient; +import com.android.internal.util.IntPair; + /** * This test exercises the * {@link com.android.server.accessibility.AccessibilityManagerService} by mocking the @@ -109,7 +111,7 @@ public class AccessibilityManagerServiceTest extends AndroidTestCase { // invoke the method under test final int stateFlagsDisabled = - mManagerService.addClient(mockClient, UserHandle.USER_CURRENT); + IntPair.first(mManagerService.addClient(mockClient, UserHandle.USER_CURRENT)); boolean enabledAccessibilityDisabled = (stateFlagsDisabled & AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED) != 0; @@ -122,7 +124,7 @@ public class AccessibilityManagerServiceTest extends AndroidTestCase { // invoke the method under test final int stateFlagsEnabled = - mManagerService.addClient(mockClient, UserHandle.USER_CURRENT); + IntPair.first(mManagerService.addClient(mockClient, UserHandle.USER_CURRENT)); boolean enabledAccessibilityEnabled = (stateFlagsEnabled & AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED) != 0; @@ -144,7 +146,7 @@ public class AccessibilityManagerServiceTest extends AndroidTestCase { // invoke the method under test final int stateFlagsEnabled = - mManagerService.addClient(mockClient, UserHandle.USER_CURRENT); + IntPair.first(mManagerService.addClient(mockClient, UserHandle.USER_CURRENT)); boolean enabledAccessibilityEnabled = (stateFlagsEnabled & AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED) != 0; @@ -157,7 +159,7 @@ public class AccessibilityManagerServiceTest extends AndroidTestCase { // invoke the method under test final int stateFlagsDisabled = - mManagerService.addClient(mockClient, UserHandle.USER_CURRENT); + IntPair.first(mManagerService.addClient(mockClient, UserHandle.USER_CURRENT)); boolean enabledAccessibilityDisabled = (stateFlagsDisabled & AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED) != 0; @@ -572,8 +574,9 @@ public class AccessibilityManagerServiceTest extends AndroidTestCase { public void notifyServicesStateChanged() {} - public void setTouchExplorationEnabled(boolean enabled) { - } + public void setRelevantEventTypes(int eventTypes) {} + + public void setTouchExplorationEnabled(boolean enabled) {} } /** diff --git a/services/tests/servicestests/src/com/android/server/AccessibilityManagerTest.java b/services/tests/servicestests/src/com/android/server/AccessibilityManagerTest.java index 6e3e6c6278a9e..92617716e28d8 100644 --- a/services/tests/servicestests/src/com/android/server/AccessibilityManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/AccessibilityManagerTest.java @@ -18,7 +18,6 @@ package com.android.server; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.eq; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -32,6 +31,8 @@ import android.view.accessibility.AccessibilityManager; import android.view.accessibility.IAccessibilityManager; import android.view.accessibility.IAccessibilityManagerClient; +import com.android.internal.util.IntPair; + import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -60,10 +61,12 @@ public class AccessibilityManagerTest extends AndroidTestCase { private AccessibilityManager createManager(boolean enabled) throws Exception { if (enabled) { when(mMockService.addClient(any(IAccessibilityManagerClient.class), anyInt())) - .thenReturn(AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED); + .thenReturn( + IntPair.of(AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED, + AccessibilityEvent.TYPES_ALL_MASK)); } else { when(mMockService.addClient(any(IAccessibilityManagerClient.class), anyInt())) - .thenReturn(0); + .thenReturn(IntPair.of(0, AccessibilityEvent.TYPES_ALL_MASK)); } AccessibilityManager manager = diff --git a/tools/layoutlib/bridge/src/android/view/accessibility/AccessibilityManager.java b/tools/layoutlib/bridge/src/android/view/accessibility/AccessibilityManager.java index 3ce7cab192e09..672ff6d684c65 100644 --- a/tools/layoutlib/bridge/src/android/view/accessibility/AccessibilityManager.java +++ b/tools/layoutlib/bridge/src/android/view/accessibility/AccessibilityManager.java @@ -95,6 +95,9 @@ public final class AccessibilityManager { public void notifyServicesStateChanged() { } + + public void setRelevantEventTypes(int eventTypes) { + } }; /**