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.ext.junit.runners.AndroidJUnit4; 38 import androidx.test.filters.MediumTest; 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( 150 "FLAG_REQUEST_SHORTCUT_WARNING_DIALOG_SPOKEN_FEEDBACK", 151 AccessibilityServiceInfo.flagToString( 152 AccessibilityServiceInfo 153 .FLAG_REQUEST_SHORTCUT_WARNING_DIALOG_SPOKEN_FEEDBACK)); 154 } 155 156 @Test 157 @AsbSecurityTest(cveBugId = {277072324}) testSetServiceInfo_throwsForLargeServiceInfo_calledUsingReflection()158 public void testSetServiceInfo_throwsForLargeServiceInfo_calledUsingReflection() 159 throws Exception { 160 try { 161 final InstrumentedAccessibilityService service = 162 InstrumentedAccessibilityService.enableService( 163 InstrumentedAccessibilityService.class); 164 final AccessibilityServiceInfo info = service.getServiceInfo(); 165 setLargePackageNames(info); 166 167 // NOTE: Using reflection requires setting the HIDDEN_API_POLICY global setting. 168 // This is done in AndroidTest.xml for this test because it must be set before 169 // the test process is started. 170 final Field connectionIdField = AccessibilityService.class.getDeclaredField( 171 "mConnectionId"); 172 connectionIdField.setAccessible(true); // Allow the test to read this private field. 173 final Method getConnection = 174 Class.forName("android.view.accessibility.AccessibilityInteractionClient") 175 .getDeclaredMethod("getConnection", int.class); 176 final Object connection = getConnection.invoke(null, connectionIdField.get(service)); 177 final Method setServiceInfo = 178 Class.forName("android.accessibilityservice.IAccessibilityServiceConnection") 179 .getDeclaredMethod("setServiceInfo", AccessibilityServiceInfo.class); 180 181 InvocationTargetException exception = assertThrows( 182 InvocationTargetException.class, () -> setServiceInfo.invoke(connection, info)); 183 assertThat(exception).hasCauseThat().isInstanceOf(IllegalStateException.class); 184 } finally { 185 InstrumentedAccessibilityService.disableAllServices(); 186 } 187 } 188 189 @Test 190 @AsbSecurityTest(cveBugId = {261589597}) testSetServiceInfo_throwsForLargeServiceInfo()191 public void testSetServiceInfo_throwsForLargeServiceInfo() { 192 try { 193 final InstrumentedAccessibilityService service = 194 InstrumentedAccessibilityService.enableService( 195 InstrumentedAccessibilityService.class); 196 final AccessibilityServiceInfo info = service.getServiceInfo(); 197 setLargePackageNames(info); 198 199 assertThrows(IllegalStateException.class, () -> service.setServiceInfo(info)); 200 } finally { 201 InstrumentedAccessibilityService.disableAllServices(); 202 } 203 } 204 205 @Test testDefaultConstructor()206 public void testDefaultConstructor() throws Exception { 207 AccessibilityServiceInfo info = new AccessibilityServiceInfo(); 208 209 assertWithMessage("info.getId()").that(info.getId()).isNull(); 210 assertWithMessage("info.toString()").that(info.toString()).isNotNull(); 211 } 212 213 @Test testDynamicallyConfigurableProperties_doNotPersist()214 public void testDynamicallyConfigurableProperties_doNotPersist() { 215 try { 216 final int flag = AccessibilityServiceInfo.FLAG_REPORT_VIEW_IDS; 217 final InstrumentedAccessibilityService service = 218 InstrumentedAccessibilityService.enableService( 219 InstrumentedAccessibilityService.class); 220 final AccessibilityServiceInfo info = service.getServiceInfo(); 221 assertThat(info.flags & flag).isEqualTo(0); 222 info.flags |= flag; 223 service.setServiceInfo(info); 224 assertThat(service.getServiceInfo().flags & flag).isEqualTo(flag); 225 InstrumentedAccessibilityService.disableAllServices(); 226 227 final InstrumentedAccessibilityService reenabledService = 228 InstrumentedAccessibilityService.enableService( 229 InstrumentedAccessibilityService.class); 230 assertThat(reenabledService.getServiceInfo().flags & flag).isEqualTo(0); 231 } finally { 232 InstrumentedAccessibilityService.disableAllServices(); 233 } 234 } 235 236 /** 237 * Fully populates the {@link AccessibilityServiceInfo} to marshal. 238 * 239 * @param sentInfo The service info to populate. 240 */ fullyPopulateSentAccessibilityServiceInfo(AccessibilityServiceInfo sentInfo)241 private void fullyPopulateSentAccessibilityServiceInfo(AccessibilityServiceInfo sentInfo) { 242 sentInfo.eventTypes = AccessibilityEvent.TYPE_VIEW_CLICKED; 243 sentInfo.feedbackType = AccessibilityServiceInfo.FEEDBACK_SPOKEN; 244 sentInfo.flags = AccessibilityServiceInfo.DEFAULT; 245 sentInfo.notificationTimeout = 1000; 246 sentInfo.packageNames = new String[] { 247 "foo.bar.baz" 248 }; 249 sentInfo.setInteractiveUiTimeoutMillis(2000); 250 sentInfo.setNonInteractiveUiTimeoutMillis(4000); 251 sentInfo.setAccessibilityTool(true); 252 } 253 254 /** 255 * Compares all properties of the <code>sentInfo</code> and the 256 * <code>receviedInfo</code> to make sure marshaling is correctly 257 * implemented. 258 */ assertAllFieldsProperlyMarshalled(AccessibilityServiceInfo sentInfo, AccessibilityServiceInfo receivedInfo)259 private void assertAllFieldsProperlyMarshalled(AccessibilityServiceInfo sentInfo, 260 AccessibilityServiceInfo receivedInfo) { 261 assertEquals("eventTypes not marshalled properly", sentInfo.eventTypes, 262 receivedInfo.eventTypes); 263 assertEquals("feedbackType not marshalled properly", sentInfo.feedbackType, 264 receivedInfo.feedbackType); 265 assertEquals("flags not marshalled properly", sentInfo.flags, receivedInfo.flags); 266 assertEquals("notificationTimeout not marshalled properly", sentInfo.notificationTimeout, 267 receivedInfo.notificationTimeout); 268 assertEquals("packageNames not marshalled properly", sentInfo.packageNames.length, 269 receivedInfo.packageNames.length); 270 assertEquals("packageNames not marshalled properly", sentInfo.packageNames[0], 271 receivedInfo.packageNames[0]); 272 assertEquals("interactiveUiTimeout not marshalled properly", 273 sentInfo.getInteractiveUiTimeoutMillis(), 274 receivedInfo.getInteractiveUiTimeoutMillis()); 275 assertEquals("nonInteractiveUiTimeout not marshalled properly", 276 sentInfo.getNonInteractiveUiTimeoutMillis(), 277 receivedInfo.getNonInteractiveUiTimeoutMillis()); 278 assertEquals("isAccessibilityTool not marshalled properly", 279 sentInfo.isAccessibilityTool(), receivedInfo.isAccessibilityTool()); 280 } 281 setLargePackageNames(AccessibilityServiceInfo info)282 private static void setLargePackageNames(AccessibilityServiceInfo info) { 283 // android_util_Binder.cpp says that very large transactions (above 200*1024 bytes) 284 // will fail with TransactionTooLargeException, but the accessibility framework uses the 285 // more aggressive suggested individual parcel size limit of IBinder.java's 286 // MAX_IPC_SIZE (64*1024 bytes). 287 info.packageNames = new String[]{"A".repeat(1024 * 32)}; 288 } 289 } 290