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.cts; 18 19 import static com.google.common.truth.Truth.assertThat; 20 import static com.google.common.truth.Truth.assertWithMessage; 21 22 import static org.junit.Assert.assertEquals; 23 import static org.junit.Assert.assertSame; 24 import static org.junit.Assert.assertThrows; 25 26 import android.accessibility.cts.common.AccessibilityDumpOnFailureRule; 27 import android.accessibility.cts.common.InstrumentedAccessibilityService; 28 import android.accessibilityservice.AccessibilityService; 29 import android.accessibilityservice.AccessibilityServiceInfo; 30 import android.os.Parcel; 31 import android.platform.test.annotations.AsbSecurityTest; 32 import android.platform.test.annotations.Presubmit; 33 import android.platform.test.flag.junit.CheckFlagsRule; 34 import android.platform.test.flag.junit.DeviceFlagsValueProvider; 35 import android.view.accessibility.AccessibilityEvent; 36 37 import androidx.test.filters.MediumTest; 38 import androidx.test.runner.AndroidJUnit4; 39 40 import com.android.compatibility.common.util.CddTest; 41 import com.android.sts.common.util.StsExtraBusinessLogicTestCase; 42 43 import org.junit.Rule; 44 import org.junit.Test; 45 import org.junit.runner.RunWith; 46 47 import java.lang.reflect.Field; 48 import java.lang.reflect.InvocationTargetException; 49 import java.lang.reflect.Method; 50 51 /** 52 * Class for testing {@link AccessibilityServiceInfo}. 53 */ 54 @Presubmit 55 @RunWith(AndroidJUnit4.class) 56 @CddTest(requirements = {"3.10/C-1-1,C-1-2"}) 57 public class AccessibilityServiceInfoTest extends StsExtraBusinessLogicTestCase { 58 59 @Rule 60 public final AccessibilityDumpOnFailureRule mDumpOnFailureRule = 61 new AccessibilityDumpOnFailureRule(); 62 63 @Rule 64 public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); 65 66 @MediumTest 67 @Test testMarshalling()68 public void testMarshalling() throws Exception { 69 70 // fully populate the service info to marshal 71 AccessibilityServiceInfo sentInfo = new AccessibilityServiceInfo(); 72 fullyPopulateSentAccessibilityServiceInfo(sentInfo); 73 74 // marshal and unmarshal the service info 75 Parcel parcel = Parcel.obtain(); 76 sentInfo.writeToParcel(parcel, 0); 77 parcel.setDataPosition(0); 78 AccessibilityServiceInfo receivedInfo = AccessibilityServiceInfo.CREATOR 79 .createFromParcel(parcel); 80 81 // make sure all fields properly marshaled 82 assertAllFieldsProperlyMarshalled(sentInfo, receivedInfo); 83 } 84 85 /** 86 * Tests whether the service info describes its contents consistently. 87 */ 88 @MediumTest 89 @Test testDescribeContents()90 public void testDescribeContents() { 91 AccessibilityServiceInfo info = new AccessibilityServiceInfo(); 92 assertSame("Accessibility service info always return 0 for this method.", 0, 93 info.describeContents()); 94 fullyPopulateSentAccessibilityServiceInfo(info); 95 assertSame("Accessibility service infos always return 0 for this method.", 0, 96 info.describeContents()); 97 } 98 99 /** 100 * Tests whether a feedback type is correctly transformed to a string. 101 */ 102 @MediumTest 103 @Test testFeedbackTypeToString()104 public void testFeedbackTypeToString() { 105 assertEquals("[FEEDBACK_AUDIBLE]", AccessibilityServiceInfo.feedbackTypeToString( 106 AccessibilityServiceInfo.FEEDBACK_AUDIBLE)); 107 assertEquals("[FEEDBACK_GENERIC]", AccessibilityServiceInfo.feedbackTypeToString( 108 AccessibilityServiceInfo.FEEDBACK_GENERIC)); 109 assertEquals("[FEEDBACK_HAPTIC]", AccessibilityServiceInfo.feedbackTypeToString( 110 AccessibilityServiceInfo.FEEDBACK_HAPTIC)); 111 assertEquals("[FEEDBACK_SPOKEN]", AccessibilityServiceInfo.feedbackTypeToString( 112 AccessibilityServiceInfo.FEEDBACK_SPOKEN)); 113 assertEquals("[FEEDBACK_VISUAL]", AccessibilityServiceInfo.feedbackTypeToString( 114 AccessibilityServiceInfo.FEEDBACK_VISUAL)); 115 assertEquals("[FEEDBACK_BRAILLE]", AccessibilityServiceInfo.feedbackTypeToString( 116 AccessibilityServiceInfo.FEEDBACK_BRAILLE)); 117 assertEquals("[FEEDBACK_SPOKEN, FEEDBACK_HAPTIC, FEEDBACK_AUDIBLE, FEEDBACK_VISUAL," 118 + " FEEDBACK_GENERIC, FEEDBACK_BRAILLE]", 119 AccessibilityServiceInfo.feedbackTypeToString( 120 AccessibilityServiceInfo.FEEDBACK_ALL_MASK)); 121 } 122 123 /** 124 * Tests whether a flag is correctly transformed to a string. 125 */ 126 @MediumTest 127 @Test testFlagToString()128 public void testFlagToString() { 129 assertEquals("DEFAULT", AccessibilityServiceInfo.flagToString( 130 AccessibilityServiceInfo.DEFAULT)); 131 assertEquals("FLAG_INCLUDE_NOT_IMPORTANT_VIEWS", AccessibilityServiceInfo.flagToString( 132 AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS)); 133 assertEquals("FLAG_REPORT_VIEW_IDS", AccessibilityServiceInfo.flagToString( 134 AccessibilityServiceInfo.FLAG_REPORT_VIEW_IDS)); 135 assertEquals("FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY", AccessibilityServiceInfo 136 .flagToString(AccessibilityServiceInfo.FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY)); 137 assertEquals("FLAG_REQUEST_FILTER_KEY_EVENTS", AccessibilityServiceInfo.flagToString( 138 AccessibilityServiceInfo.FLAG_REQUEST_FILTER_KEY_EVENTS)); 139 assertEquals("FLAG_REQUEST_TOUCH_EXPLORATION_MODE", AccessibilityServiceInfo.flagToString( 140 AccessibilityServiceInfo.FLAG_REQUEST_TOUCH_EXPLORATION_MODE)); 141 assertEquals("FLAG_RETRIEVE_INTERACTIVE_WINDOWS", AccessibilityServiceInfo.flagToString( 142 AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS)); 143 assertEquals("FLAG_ENABLE_ACCESSIBILITY_VOLUME", AccessibilityServiceInfo.flagToString( 144 AccessibilityServiceInfo.FLAG_ENABLE_ACCESSIBILITY_VOLUME)); 145 assertEquals("FLAG_REQUEST_ACCESSIBILITY_BUTTON", AccessibilityServiceInfo.flagToString( 146 AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON)); 147 assertEquals("FLAG_REQUEST_FINGERPRINT_GESTURES", AccessibilityServiceInfo.flagToString( 148 AccessibilityServiceInfo.FLAG_REQUEST_FINGERPRINT_GESTURES)); 149 assertEquals("FLAG_REQUEST_SHORTCUT_WARNING_DIALOG_SPOKEN_FEEDBACK", AccessibilityServiceInfo.flagToString( 150 AccessibilityServiceInfo.FLAG_REQUEST_SHORTCUT_WARNING_DIALOG_SPOKEN_FEEDBACK)); 151 152 } 153 154 @Test 155 @AsbSecurityTest(cveBugId = {277072324}) testSetServiceInfo_throwsForLargeServiceInfo_calledUsingReflection()156 public void testSetServiceInfo_throwsForLargeServiceInfo_calledUsingReflection() 157 throws Exception { 158 try { 159 final InstrumentedAccessibilityService service = 160 InstrumentedAccessibilityService.enableService( 161 InstrumentedAccessibilityService.class); 162 final AccessibilityServiceInfo info = service.getServiceInfo(); 163 setLargePackageNames(info); 164 165 // NOTE: Using reflection requires setting the HIDDEN_API_POLICY global setting. 166 // This is done in AndroidTest.xml for this test because it must be set before 167 // the test process is started. 168 final Field connectionIdField = AccessibilityService.class.getDeclaredField( 169 "mConnectionId"); 170 connectionIdField.setAccessible(true); // Allow the test to read this private field. 171 final Method getConnection = 172 Class.forName("android.view.accessibility.AccessibilityInteractionClient") 173 .getDeclaredMethod("getConnection", int.class); 174 final Object connection = getConnection.invoke(null, connectionIdField.get(service)); 175 final Method setServiceInfo = 176 Class.forName("android.accessibilityservice.IAccessibilityServiceConnection") 177 .getDeclaredMethod("setServiceInfo", AccessibilityServiceInfo.class); 178 179 InvocationTargetException exception = assertThrows( 180 InvocationTargetException.class, () -> setServiceInfo.invoke(connection, info)); 181 assertThat(exception).hasCauseThat().isInstanceOf(IllegalStateException.class); 182 } finally { 183 InstrumentedAccessibilityService.disableAllServices(); 184 } 185 } 186 187 @Test 188 @AsbSecurityTest(cveBugId = {261589597}) testSetServiceInfo_throwsForLargeServiceInfo()189 public void testSetServiceInfo_throwsForLargeServiceInfo() { 190 try { 191 final InstrumentedAccessibilityService service = 192 InstrumentedAccessibilityService.enableService( 193 InstrumentedAccessibilityService.class); 194 final AccessibilityServiceInfo info = service.getServiceInfo(); 195 setLargePackageNames(info); 196 197 assertThrows(IllegalStateException.class, () -> service.setServiceInfo(info)); 198 } finally { 199 InstrumentedAccessibilityService.disableAllServices(); 200 } 201 } 202 203 @Test testDefaultConstructor()204 public void testDefaultConstructor() throws Exception { 205 AccessibilityServiceInfo info = new AccessibilityServiceInfo(); 206 207 assertWithMessage("info.getId()").that(info.getId()).isNull(); 208 assertWithMessage("info.toString()").that(info.toString()).isNotNull(); 209 } 210 211 @Test testDynamicallyConfigurableProperties_doNotPersist()212 public void testDynamicallyConfigurableProperties_doNotPersist() { 213 try { 214 final int flag = AccessibilityServiceInfo.FLAG_REPORT_VIEW_IDS; 215 final InstrumentedAccessibilityService service = 216 InstrumentedAccessibilityService.enableService( 217 InstrumentedAccessibilityService.class); 218 final AccessibilityServiceInfo info = service.getServiceInfo(); 219 assertThat(info.flags & flag).isEqualTo(0); 220 info.flags |= flag; 221 service.setServiceInfo(info); 222 assertThat(service.getServiceInfo().flags & flag).isEqualTo(flag); 223 InstrumentedAccessibilityService.disableAllServices(); 224 225 final InstrumentedAccessibilityService reenabledService = 226 InstrumentedAccessibilityService.enableService( 227 InstrumentedAccessibilityService.class); 228 assertThat(reenabledService.getServiceInfo().flags & flag).isEqualTo(0); 229 } finally { 230 InstrumentedAccessibilityService.disableAllServices(); 231 } 232 } 233 234 /** 235 * Fully populates the {@link AccessibilityServiceInfo} to marshal. 236 * 237 * @param sentInfo The service info to populate. 238 */ fullyPopulateSentAccessibilityServiceInfo(AccessibilityServiceInfo sentInfo)239 private void fullyPopulateSentAccessibilityServiceInfo(AccessibilityServiceInfo sentInfo) { 240 sentInfo.eventTypes = AccessibilityEvent.TYPE_VIEW_CLICKED; 241 sentInfo.feedbackType = AccessibilityServiceInfo.FEEDBACK_SPOKEN; 242 sentInfo.flags = AccessibilityServiceInfo.DEFAULT; 243 sentInfo.notificationTimeout = 1000; 244 sentInfo.packageNames = new String[] { 245 "foo.bar.baz" 246 }; 247 sentInfo.setInteractiveUiTimeoutMillis(2000); 248 sentInfo.setNonInteractiveUiTimeoutMillis(4000); 249 sentInfo.setAccessibilityTool(true); 250 } 251 252 /** 253 * Compares all properties of the <code>sentInfo</code> and the 254 * <code>receviedInfo</code> to make sure marshaling is correctly 255 * implemented. 256 */ assertAllFieldsProperlyMarshalled(AccessibilityServiceInfo sentInfo, AccessibilityServiceInfo receivedInfo)257 private void assertAllFieldsProperlyMarshalled(AccessibilityServiceInfo sentInfo, 258 AccessibilityServiceInfo receivedInfo) { 259 assertEquals("eventTypes not marshalled properly", sentInfo.eventTypes, 260 receivedInfo.eventTypes); 261 assertEquals("feedbackType not marshalled properly", sentInfo.feedbackType, 262 receivedInfo.feedbackType); 263 assertEquals("flags not marshalled properly", sentInfo.flags, receivedInfo.flags); 264 assertEquals("notificationTimeout not marshalled properly", sentInfo.notificationTimeout, 265 receivedInfo.notificationTimeout); 266 assertEquals("packageNames not marshalled properly", sentInfo.packageNames.length, 267 receivedInfo.packageNames.length); 268 assertEquals("packageNames not marshalled properly", sentInfo.packageNames[0], 269 receivedInfo.packageNames[0]); 270 assertEquals("interactiveUiTimeout not marshalled properly", 271 sentInfo.getInteractiveUiTimeoutMillis(), 272 receivedInfo.getInteractiveUiTimeoutMillis()); 273 assertEquals("nonInteractiveUiTimeout not marshalled properly", 274 sentInfo.getNonInteractiveUiTimeoutMillis(), 275 receivedInfo.getNonInteractiveUiTimeoutMillis()); 276 assertEquals("isAccessibilityTool not marshalled properly", 277 sentInfo.isAccessibilityTool(), receivedInfo.isAccessibilityTool()); 278 } 279 setLargePackageNames(AccessibilityServiceInfo info)280 private static void setLargePackageNames(AccessibilityServiceInfo info) { 281 // android_util_Binder.cpp says that very large transactions (above 200*1024 bytes) 282 // will fail with TransactionTooLargeException, but the accessibility framework uses the 283 // more aggressive suggested individual parcel size limit of IBinder.java's 284 // MAX_IPC_SIZE (64*1024 bytes). 285 info.packageNames = new String[]{"A".repeat(1024 * 32)}; 286 } 287 } 288