| /* |
| ** Copyright 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.server.accessibility; |
| |
| import static junit.framework.Assert.assertFalse; |
| import static junit.framework.Assert.assertTrue; |
| |
| import static org.mockito.ArgumentMatchers.any; |
| import static org.mockito.ArgumentMatchers.anyInt; |
| import static org.mockito.ArgumentMatchers.eq; |
| import static org.mockito.Mockito.mock; |
| import static org.mockito.Mockito.never; |
| import static org.mockito.Mockito.times; |
| import static org.mockito.Mockito.verify; |
| import static org.mockito.Mockito.when; |
| |
| import android.accessibilityservice.AccessibilityServiceInfo; |
| import android.accessibilityservice.GestureDescription; |
| import android.accessibilityservice.IAccessibilityServiceClient; |
| import android.content.ComponentName; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.content.pm.ApplicationInfo; |
| import android.content.pm.ParceledListSlice; |
| import android.content.pm.ResolveInfo; |
| import android.content.pm.ServiceInfo; |
| import android.os.IBinder; |
| import android.os.RemoteException; |
| import android.os.UserHandle; |
| import android.view.Display; |
| |
| import com.android.server.wm.WindowManagerInternal; |
| |
| import org.junit.After; |
| import org.junit.Before; |
| import org.junit.Test; |
| import org.mockito.Mock; |
| import org.mockito.MockitoAnnotations; |
| |
| import java.util.Arrays; |
| import java.util.Collections; |
| import java.util.HashSet; |
| import java.util.List; |
| |
| |
| /** |
| * Tests for AccessibilityServiceConnection |
| */ |
| public class AccessibilityServiceConnectionTest { |
| static final ComponentName COMPONENT_NAME = new ComponentName( |
| "com.android.server.accessibility", "AccessibilityServiceConnectionTest"); |
| static final int SERVICE_ID = 42; |
| |
| AccessibilityServiceConnection mConnection; |
| |
| @Mock AccessibilityManagerService.UserState mMockUserState; |
| @Mock Context mMockContext; |
| @Mock AccessibilityServiceInfo mMockServiceInfo; |
| @Mock ResolveInfo mMockResolveInfo; |
| @Mock AccessibilitySecurityPolicy mMockSecurityPolicy; |
| @Mock AccessibilityWindowManager mMockA11yWindowManager; |
| @Mock AbstractAccessibilityServiceConnection.SystemSupport mMockSystemSupport; |
| @Mock WindowManagerInternal mMockWindowManagerInternal; |
| @Mock GlobalActionPerformer mMockGlobalActionPerformer; |
| @Mock KeyEventDispatcher mMockKeyEventDispatcher; |
| @Mock MagnificationController mMockMagnificationController; |
| @Mock IBinder mMockIBinder; |
| @Mock IAccessibilityServiceClient mMockServiceClient; |
| @Mock MotionEventInjector mMockMotionEventInjector; |
| |
| MessageCapturingHandler mHandler = new MessageCapturingHandler(null); |
| |
| @Before |
| public void setup() { |
| MockitoAnnotations.initMocks(this); |
| when(mMockSystemSupport.getKeyEventDispatcher()).thenReturn(mMockKeyEventDispatcher); |
| when(mMockSystemSupport.getMagnificationController()) |
| .thenReturn(mMockMagnificationController); |
| when(mMockSystemSupport.getMotionEventInjectorForDisplayLocked( |
| Display.DEFAULT_DISPLAY)).thenReturn(mMockMotionEventInjector); |
| |
| when(mMockServiceInfo.getResolveInfo()).thenReturn(mMockResolveInfo); |
| mMockResolveInfo.serviceInfo = mock(ServiceInfo.class); |
| mMockResolveInfo.serviceInfo.applicationInfo = mock(ApplicationInfo.class); |
| |
| when(mMockIBinder.queryLocalInterface(any())).thenReturn(mMockServiceClient); |
| when(mMockWindowManagerInternal.isTouchableDisplay(Display.DEFAULT_DISPLAY)).thenReturn( |
| true); |
| |
| mConnection = new AccessibilityServiceConnection(mMockUserState, mMockContext, |
| COMPONENT_NAME, mMockServiceInfo, SERVICE_ID, mHandler, new Object(), |
| mMockSecurityPolicy, mMockSystemSupport, mMockWindowManagerInternal, |
| mMockGlobalActionPerformer, mMockA11yWindowManager); |
| when(mMockSecurityPolicy.canPerformGestures(mConnection)).thenReturn(true); |
| } |
| |
| @After |
| public void tearDown() { |
| mHandler.removeAllMessages(); |
| } |
| |
| |
| @Test |
| public void bind_requestsContextToBindService() { |
| mConnection.bindLocked(); |
| verify(mMockContext).bindServiceAsUser(any(Intent.class), eq(mConnection), |
| eq(Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE |
| | Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS), any(UserHandle.class)); |
| } |
| |
| @Test |
| public void unbind_requestsContextToUnbindService() { |
| mConnection.unbindLocked(); |
| verify(mMockContext).unbindService(mConnection); |
| } |
| |
| @Test |
| public void bindConnectUnbind_linksAndUnlinksToServiceDeath() throws RemoteException { |
| setServiceBinding(COMPONENT_NAME); |
| mConnection.bindLocked(); |
| mConnection.onServiceConnected(COMPONENT_NAME, mMockIBinder); |
| verify(mMockIBinder).linkToDeath(eq(mConnection), anyInt()); |
| mConnection.unbindLocked(); |
| verify(mMockIBinder).unlinkToDeath(eq(mConnection), anyInt()); |
| } |
| |
| @Test |
| public void connectedServiceCrashedAndRestarted_crashReportedInServiceInfo() { |
| setServiceBinding(COMPONENT_NAME); |
| mConnection.bindLocked(); |
| mConnection.onServiceConnected(COMPONENT_NAME, mMockIBinder); |
| assertFalse(mConnection.getServiceInfo().crashed); |
| mConnection.binderDied(); |
| assertTrue(mConnection.getServiceInfo().crashed); |
| mConnection.onServiceConnected(COMPONENT_NAME, mMockIBinder); |
| mHandler.sendAllMessages(); |
| assertFalse(mConnection.getServiceInfo().crashed); |
| } |
| |
| private void setServiceBinding(ComponentName componentName) { |
| when(mMockUserState.getBindingServicesLocked()) |
| .thenReturn(new HashSet<>(Arrays.asList(componentName))); |
| } |
| |
| @Test |
| public void binderDied_keysGetFlushed() { |
| setServiceBinding(COMPONENT_NAME); |
| mConnection.bindLocked(); |
| mConnection.onServiceConnected(COMPONENT_NAME, mMockIBinder); |
| mConnection.binderDied(); |
| assertTrue(mConnection.getServiceInfo().crashed); |
| verify(mMockKeyEventDispatcher).flush(mConnection); |
| } |
| |
| @Test |
| public void connectedService_notInEnabledServiceList_doNotInitClient() |
| throws RemoteException { |
| when(mMockUserState.getEnabledServicesLocked()) |
| .thenReturn(Collections.emptySet()); |
| setServiceBinding(COMPONENT_NAME); |
| |
| mConnection.bindLocked(); |
| mConnection.onServiceConnected(COMPONENT_NAME, mMockIBinder); |
| mHandler.sendAllMessages(); |
| verify(mMockSystemSupport, times(2)).onClientChangeLocked(false); |
| verify(mMockServiceClient, times(0)).init(any(), anyInt(), any()); |
| } |
| |
| @Test |
| public void sendGesture_touchableDisplay_injectEvents() |
| throws RemoteException { |
| setServiceBinding(COMPONENT_NAME); |
| mConnection.bindLocked(); |
| mConnection.onServiceConnected(COMPONENT_NAME, mMockIBinder); |
| |
| ParceledListSlice parceledListSlice = mock(ParceledListSlice.class); |
| List<GestureDescription.GestureStep> gestureSteps = mock(List.class); |
| when(parceledListSlice.getList()).thenReturn(gestureSteps); |
| mConnection.dispatchGesture(0, parceledListSlice, Display.DEFAULT_DISPLAY); |
| |
| verify(mMockMotionEventInjector).injectEvents(gestureSteps, mMockServiceClient, 0); |
| } |
| |
| @Test |
| public void sendGesture_untouchableDisplay_performGestureResultFailed() |
| throws RemoteException { |
| when(mMockWindowManagerInternal.isTouchableDisplay(Display.DEFAULT_DISPLAY)).thenReturn( |
| false); |
| setServiceBinding(COMPONENT_NAME); |
| mConnection.bindLocked(); |
| mConnection.onServiceConnected(COMPONENT_NAME, mMockIBinder); |
| |
| ParceledListSlice parceledListSlice = mock(ParceledListSlice.class); |
| List<GestureDescription.GestureStep> gestureSteps = mock(List.class); |
| when(parceledListSlice.getList()).thenReturn(gestureSteps); |
| mConnection.dispatchGesture(0, parceledListSlice, Display.DEFAULT_DISPLAY); |
| |
| verify(mMockMotionEventInjector, never()).injectEvents(gestureSteps, mMockServiceClient, 0); |
| verify(mMockServiceClient).onPerformGestureResult(0, false); |
| } |
| |
| @Test |
| public void sendGesture_invalidDisplay_performGestureResultFailed() |
| throws RemoteException { |
| setServiceBinding(COMPONENT_NAME); |
| mConnection.bindLocked(); |
| mConnection.onServiceConnected(COMPONENT_NAME, mMockIBinder); |
| |
| ParceledListSlice parceledListSlice = mock(ParceledListSlice.class); |
| List<GestureDescription.GestureStep> gestureSteps = mock(List.class); |
| when(parceledListSlice.getList()).thenReturn(gestureSteps); |
| mConnection.dispatchGesture(0, parceledListSlice, Display.INVALID_DISPLAY); |
| |
| verify(mMockServiceClient).onPerformGestureResult(0, false); |
| } |
| |
| } |