1 /* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.accessibilityservice.delegate; 18 19 import android.accessibilityservice.AccessibilityService; 20 import android.accessibilityservice.AccessibilityServiceInfo; 21 import android.accessibilityservice.IAccessibilityServiceDelegate; 22 import android.accessibilityservice.IAccessibilityServiceDelegateConnection; 23 import android.app.Service; 24 import android.content.Intent; 25 import android.os.Bundle; 26 import android.os.IBinder; 27 import android.os.RemoteException; 28 import android.util.Log; 29 import android.view.accessibility.AccessibilityEvent; 30 import android.view.accessibility.AccessibilityNodeInfo; 31 32 import java.util.List; 33 34 /** 35 * This class is an accessibility service mock to which the system is bound and 36 * exposes a mock interface to the CTS accessibility tests. 37 * </p> 38 * Note: The end-to-end test is composed of two APKs, one with a mock accessibility 39 * service, another with the instrumented activity and test cases. The 40 * motivation for two APKs design is that CTS tests cannot access the secure 41 * settings which is required for enabling accessibility and accessibility 42 * services. Therefore, manual installation of this package is required. Once 43 * the package has been installed accessibility must be enabled (Settings -> 44 * Accessibility), the mock service must be enabled (Settings -> Accessibility 45 * -> Mock Accessibility Service), and then the CTS tests in this package 46 * <strong>CtsAccessibilityServiceTestCases.apk</strong> located in 47 * <strong>cts/tests/tests/accessibility</strong> can be successfully run. 48 * Further, the mock and tests run in separate processes since the 49 * instrumentation restarts the process in which it is running and this breaks 50 * the binding between the mock accessibility service and the system. 51 */ 52 public class DelegatingAccessibilityService extends AccessibilityService { 53 54 /** 55 * Tag used for logging. 56 */ 57 private static final String LOG_TAG = "AccessibilityServiceDelegate"; 58 59 /** 60 * Handle to the instance used by the accessibility service connection. 61 */ 62 static DelegatingAccessibilityService sServiceDelegate; 63 64 /** 65 * Interface for delegating events and interrupt requests. 66 */ 67 private IAccessibilityServiceDelegate mDelegateInterface; 68 69 @Override 70 protected void onServiceConnected() { 71 // the service is ready to be used only 72 // after the system has bound to it 73 sServiceDelegate = this; 74 } 75 76 @Override 77 public void onAccessibilityEvent(AccessibilityEvent event) { 78 if (mDelegateInterface == null) { 79 return; 80 } 81 82 try { 83 mDelegateInterface.onAccessibilityEvent(event); 84 } catch (RemoteException re) { 85 Log.i(LOG_TAG, "Dead: " + mDelegateInterface.toString() + " cleaning up."); 86 mDelegateInterface = null; 87 } 88 } 89 90 @Override 91 public void onInterrupt() { 92 if (mDelegateInterface == null) { 93 return; 94 } 95 96 try { 97 mDelegateInterface.onInterrupt(); 98 } catch (RemoteException re) { 99 Log.i(LOG_TAG, "Dead: " + mDelegateInterface.toString() + " cleaning up."); 100 mDelegateInterface = null; 101 } 102 } 103 104 /** 105 * Sets the interface to which to delegate. 106 * 107 * @param delegateInterface The delegate interface. 108 */ 109 private void setDelegateInterface(IAccessibilityServiceDelegate delegateInterface) { 110 mDelegateInterface = delegateInterface; 111 } 112 113 /** 114 * This is a service to which the end-to-end CTS test connects to pass a 115 * delegate interface to which the {@link DelegatingAccessibilityService} 116 * to delegate. 117 */ 118 public static class DelegatingConnectionService extends Service { 119 120 @Override 121 public IBinder onBind(Intent intent) { 122 if (sServiceDelegate == null) { 123 return null; 124 } 125 return new AccessibilityServiceDelegateConnection(); 126 } 127 128 /** 129 * This class is the connection wrapper passed to the end-to-end CTS 130 * test, so the latter can pass a delegating interface. 131 */ 132 private class AccessibilityServiceDelegateConnection extends 133 IAccessibilityServiceDelegateConnection.Stub { 134 135 @Override 136 public void setAccessibilityServiceDelegate(IBinder binder) { 137 sServiceDelegate.setDelegateInterface(IAccessibilityServiceDelegate.Stub 138 .asInterface(binder)); 139 } 140 141 @Override 142 public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByText( 143 AccessibilityNodeInfo root, String text) { 144 return root.findAccessibilityNodeInfosByText(text); 145 } 146 147 @Override 148 public AccessibilityNodeInfo getChild(AccessibilityNodeInfo parent, int index) { 149 return parent.getChild(index); 150 } 151 152 @Override 153 public AccessibilityNodeInfo getParent(AccessibilityNodeInfo child) { 154 return child.getParent(); 155 } 156 157 @Override 158 public AccessibilityNodeInfo findFocus(AccessibilityNodeInfo root, int focusType) { 159 return root.findFocus(focusType); 160 } 161 162 @Override 163 public AccessibilityNodeInfo focusSearch(AccessibilityNodeInfo current, int direction) { 164 return current.focusSearch(direction); 165 } 166 167 @Override 168 public AccessibilityNodeInfo getSource(AccessibilityEvent event) { 169 return event.getSource(); 170 } 171 172 @Override 173 public boolean performAccessibilityAction(AccessibilityNodeInfo target, int action, 174 Bundle arguments) { 175 return target.performAction(action, arguments); 176 } 177 178 @Override 179 public void setFetchViewsNotExposedForAccessibility(boolean fetch) { 180 AccessibilityServiceInfo info = sServiceDelegate.getServiceInfo(); 181 if (fetch) { 182 info.flags |= AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS; 183 } else { 184 info.flags &= ~AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS; 185 } 186 sServiceDelegate.setServiceInfo(info); 187 } 188 189 @Override 190 public boolean performGlobalAction(int action) { 191 return sServiceDelegate.performGlobalAction(action); 192 } 193 194 @Override 195 public AccessibilityNodeInfo getRootInActiveWindow() { 196 return sServiceDelegate.getRootInActiveWindow(); 197 } 198 } 199 } 200 } 201