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