• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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 com.android.settings.accessibility;
18 
19 import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.SOFTWARE;
20 
21 import static com.google.common.truth.Truth.assertThat;
22 
23 import static org.mockito.ArgumentMatchers.any;
24 import static org.mockito.Mockito.doReturn;
25 import static org.mockito.Mockito.when;
26 import static org.robolectric.Shadows.shadowOf;
27 
28 import static java.util.Collections.singletonList;
29 
30 import android.accessibilityservice.AccessibilityServiceInfo;
31 import android.accessibilityservice.AccessibilityShortcutInfo;
32 import android.content.ComponentName;
33 import android.content.Context;
34 import android.content.Intent;
35 import android.content.pm.ActivityInfo;
36 import android.content.pm.ApplicationInfo;
37 import android.content.pm.PackageManager;
38 import android.content.pm.ResolveInfo;
39 import android.content.pm.ServiceInfo;
40 import android.database.ContentObserver;
41 import android.os.Build;
42 import android.platform.test.annotations.DisableFlags;
43 import android.platform.test.annotations.EnableFlags;
44 import android.platform.test.flag.junit.SetFlagsRule;
45 import android.provider.Settings;
46 import android.view.accessibility.AccessibilityManager;
47 
48 import androidx.fragment.app.Fragment;
49 import androidx.test.core.app.ApplicationProvider;
50 
51 import com.android.internal.accessibility.util.AccessibilityUtils;
52 import com.android.settings.R;
53 import com.android.settings.SettingsActivity;
54 import com.android.settings.testutils.FakeFeatureFactory;
55 import com.android.settings.testutils.XmlTestUtils;
56 import com.android.settings.testutils.shadow.ShadowAccessibilityManager;
57 import com.android.settings.testutils.shadow.ShadowApplicationPackageManager;
58 import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
59 import com.android.settings.testutils.shadow.ShadowBluetoothUtils;
60 import com.android.settings.testutils.shadow.ShadowRestrictedLockUtilsInternal;
61 import com.android.settings.testutils.shadow.ShadowUserManager;
62 import com.android.settingslib.RestrictedPreference;
63 import com.android.settingslib.bluetooth.LocalBluetoothManager;
64 import com.android.settingslib.search.SearchIndexableRaw;
65 import com.android.settingslib.testutils.shadow.ShadowColorDisplayManager;
66 
67 import com.google.common.truth.BooleanSubject;
68 
69 import org.junit.Before;
70 import org.junit.Rule;
71 import org.junit.Test;
72 import org.junit.runner.RunWith;
73 import org.mockito.Mock;
74 import org.mockito.Mockito;
75 import org.mockito.Spy;
76 import org.mockito.junit.MockitoJUnit;
77 import org.mockito.junit.MockitoRule;
78 import org.robolectric.RobolectricTestRunner;
79 import org.robolectric.android.controller.ActivityController;
80 import org.robolectric.annotation.Config;
81 import org.robolectric.shadow.api.Shadow;
82 import org.robolectric.shadows.ShadowContentResolver;
83 import org.robolectric.shadows.ShadowLooper;
84 import org.xmlpull.v1.XmlPullParserException;
85 
86 import java.io.IOException;
87 import java.util.ArrayList;
88 import java.util.Collection;
89 import java.util.List;
90 
91 /** Test for {@link AccessibilitySettings}. */
92 @RunWith(RobolectricTestRunner.class)
93 @Config(shadows = {
94         ShadowAccessibilityManager.class,
95         ShadowBluetoothAdapter.class,
96         ShadowUserManager.class,
97         ShadowColorDisplayManager.class,
98         ShadowApplicationPackageManager.class,
99         ShadowRestrictedLockUtilsInternal.class,
100 })
101 public class AccessibilitySettingsTest {
102     private static final String PACKAGE_NAME = "com.android.test";
103     private static final ComponentName SERVICE_COMPONENT_NAME =
104             new ComponentName(PACKAGE_NAME, PACKAGE_NAME + ".test_a11y_service");
105     private static final ComponentName ACTIVITY_COMPONENT_NAME =
106             new ComponentName(PACKAGE_NAME, PACKAGE_NAME + ".test_a11y_activity");
107     private static final String EMPTY_STRING = "";
108     private static final String DEFAULT_SUMMARY = "default summary";
109     private static final String DEFAULT_DESCRIPTION = "default description";
110     private static final String DEFAULT_LABEL = "default label";
111     private static final Boolean SERVICE_ENABLED = true;
112     private static final Boolean SERVICE_DISABLED = false;
113 
114     @Rule
115     public final MockitoRule mocks = MockitoJUnit.rule();
116     @Rule
117     public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
118     private final Context mContext = ApplicationProvider.getApplicationContext();
119     @Spy
120     private final AccessibilityServiceInfo mServiceInfo = getMockAccessibilityServiceInfo(
121             SERVICE_COMPONENT_NAME);
122     private ShadowAccessibilityManager mShadowAccessibilityManager;
123     @Mock
124     private LocalBluetoothManager mLocalBluetoothManager;
125 
126     private ActivityController<SettingsActivity> mActivityController;
127 
128     private AccessibilitySettings mFragment;
129 
130     @Before
setup()131     public void setup() {
132         mShadowAccessibilityManager = Shadow.extract(
133                 mContext.getSystemService(AccessibilityManager.class));
134         mShadowAccessibilityManager.setInstalledAccessibilityServiceList(new ArrayList<>());
135         mContext.setTheme(androidx.appcompat.R.style.Theme_AppCompat);
136         ShadowBluetoothUtils.sLocalBluetoothManager = mLocalBluetoothManager;
137 
138         Intent intent = new Intent();
139         intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT,
140                 AccessibilitySettings.class.getName());
141 
142         mActivityController = ActivityController.of(new SettingsActivity(), intent);
143     }
144 
145     @Test
getNonIndexableKeys_existInXmlLayout()146     public void getNonIndexableKeys_existInXmlLayout() {
147         final List<String> niks = AccessibilitySettings.SEARCH_INDEX_DATA_PROVIDER
148                 .getNonIndexableKeys(mContext);
149         final List<String> keys =
150                 XmlTestUtils.getKeysFromPreferenceXml(mContext, R.xml.accessibility_settings);
151 
152         assertThat(keys).containsAtLeastElementsIn(niks);
153     }
154 
155     @Test
getRawDataToIndex_isNull()156     public void getRawDataToIndex_isNull() {
157         final List<SearchIndexableRaw> indexableRawList =
158                 AccessibilitySettings.SEARCH_INDEX_DATA_PROVIDER
159                         .getRawDataToIndex(mContext, true);
160 
161         assertThat(indexableRawList).isNull();
162     }
163 
164     @DisableFlags(Flags.FLAG_FIX_A11Y_SETTINGS_SEARCH)
165     @Test
getDynamicRawDataToIndex_hasInstalledA11yFeatures_flagOff_returnEmpty()166     public void getDynamicRawDataToIndex_hasInstalledA11yFeatures_flagOff_returnEmpty() {
167         mShadowAccessibilityManager.setInstalledAccessibilityServiceList(
168                 List.of(mServiceInfo));
169         mShadowAccessibilityManager.setInstalledAccessibilityShortcutListAsUser(
170                 List.of(getMockAccessibilityShortcutInfo()));
171 
172         assertThat(AccessibilitySettings.SEARCH_INDEX_DATA_PROVIDER.getDynamicRawDataToIndex(
173                 mContext, /* enabled= */ true))
174                 .isEmpty();
175     }
176 
177     @EnableFlags(Flags.FLAG_FIX_A11Y_SETTINGS_SEARCH)
178     @Test
getDynamicRawDataToIndex_hasInstalledA11yFeatures_flagOn_returnRawDataForInstalledA11yFeatures()179     public void getDynamicRawDataToIndex_hasInstalledA11yFeatures_flagOn_returnRawDataForInstalledA11yFeatures() {
180         mShadowAccessibilityManager.setInstalledAccessibilityServiceList(
181                 List.of(mServiceInfo));
182         mShadowAccessibilityManager.setInstalledAccessibilityShortcutListAsUser(
183                 List.of(getMockAccessibilityShortcutInfo()));
184         final AccessibilitySearchFeatureProvider featureProvider =
185                 FakeFeatureFactory.setupForTest().getAccessibilitySearchFeatureProvider();
186         final String synonyms = "fake keyword1, fake keyword2";
187         when(featureProvider.getSynonymsForComponent(mContext, ACTIVITY_COMPONENT_NAME))
188                 .thenReturn("");
189         when(featureProvider.getSynonymsForComponent(mContext, SERVICE_COMPONENT_NAME))
190                 .thenReturn(synonyms);
191 
192         final List<SearchIndexableRaw> indexableRawDataList =
193                 AccessibilitySettings.SEARCH_INDEX_DATA_PROVIDER.getDynamicRawDataToIndex(
194                         mContext, /* enabled= */ true);
195         ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
196 
197         assertThat(indexableRawDataList).hasSize(2);
198         SearchIndexableRaw a11yActivityIndexableData = indexableRawDataList.get(0);
199         assertThat(a11yActivityIndexableData.key).isEqualTo(
200                 ACTIVITY_COMPONENT_NAME.flattenToString());
201         assertThat(a11yActivityIndexableData.title).isEqualTo(DEFAULT_LABEL);
202         assertThat(a11yActivityIndexableData.keywords).isEmpty();
203 
204         SearchIndexableRaw a11yServiceIndexableData = indexableRawDataList.get(1);
205         assertThat(a11yServiceIndexableData.key).isEqualTo(
206                 SERVICE_COMPONENT_NAME.flattenToString());
207         assertThat(a11yServiceIndexableData.title).isEqualTo(DEFAULT_LABEL);
208         assertThat(a11yServiceIndexableData.keywords).isEqualTo(synonyms);
209     }
210 
211     @Test
getServiceSummary_serviceCrash_showsStopped()212     public void getServiceSummary_serviceCrash_showsStopped() {
213         mServiceInfo.crashed = true;
214 
215         final CharSequence summary = AccessibilitySettings.getServiceSummary(mContext,
216                 mServiceInfo, SERVICE_ENABLED);
217 
218         assertThat(summary).isEqualTo(
219                 mContext.getString(R.string.accessibility_summary_state_stopped));
220     }
221 
222     @Test
getServiceSummary_invisibleToggle_shortcutEnabled_showsOnSummary()223     public void getServiceSummary_invisibleToggle_shortcutEnabled_showsOnSummary() {
224         setInvisibleToggleFragmentType(mServiceInfo);
225         doReturn(DEFAULT_SUMMARY).when(mServiceInfo).loadSummary(any());
226         setShortcutEnabled(mServiceInfo.getComponentName(), true);
227 
228         final CharSequence summary = AccessibilitySettings.getServiceSummary(mContext,
229                 mServiceInfo, SERVICE_ENABLED);
230 
231         assertThat(summary).isEqualTo(
232                 mContext.getString(
233                         com.android.settingslib.R.string.preference_summary_default_combination,
234                         mContext.getString(R.string.accessibility_summary_shortcut_enabled),
235                         DEFAULT_SUMMARY));
236     }
237 
238     @Test
getServiceSummary_invisibleToggle_shortcutDisabled_showsOffSummary()239     public void getServiceSummary_invisibleToggle_shortcutDisabled_showsOffSummary() {
240         setInvisibleToggleFragmentType(mServiceInfo);
241         setShortcutEnabled(mServiceInfo.getComponentName(), false);
242         doReturn(DEFAULT_SUMMARY).when(mServiceInfo).loadSummary(any());
243 
244         final CharSequence summary = AccessibilitySettings.getServiceSummary(mContext,
245                 mServiceInfo, SERVICE_ENABLED);
246 
247         assertThat(summary).isEqualTo(
248                 mContext.getString(
249                         com.android.settingslib.R.string.preference_summary_default_combination,
250                         mContext.getString(R.string.generic_accessibility_feature_shortcut_off),
251                         DEFAULT_SUMMARY));
252     }
253 
254     @Test
getServiceSummary_enableServiceShortcutOn_showsServiceEnabledShortcutOn()255     public void getServiceSummary_enableServiceShortcutOn_showsServiceEnabledShortcutOn() {
256         doReturn(EMPTY_STRING).when(mServiceInfo).loadSummary(any());
257         setShortcutEnabled(mServiceInfo.getComponentName(), true);
258 
259         String summary = AccessibilitySettings.getServiceSummary(mContext,
260                 mServiceInfo, SERVICE_ENABLED).toString();
261 
262         assertThat(summary).isEqualTo(
263                 mContext.getString(R.string.generic_accessibility_service_on));
264     }
265 
266     @Test
getServiceSummary_enableServiceShortcutOff_showsServiceEnabledShortcutOff()267     public void getServiceSummary_enableServiceShortcutOff_showsServiceEnabledShortcutOff() {
268         doReturn(EMPTY_STRING).when(mServiceInfo).loadSummary(any());
269         setShortcutEnabled(mServiceInfo.getComponentName(), false);
270 
271         String summary = AccessibilitySettings.getServiceSummary(
272                 mContext, mServiceInfo, SERVICE_ENABLED).toString();
273 
274         assertThat(summary).isEqualTo(
275                 mContext.getString(R.string.generic_accessibility_service_on));
276     }
277 
278     @Test
getServiceSummary_disableServiceShortcutOff_showsDisabledShortcutOff()279     public void getServiceSummary_disableServiceShortcutOff_showsDisabledShortcutOff() {
280         doReturn(EMPTY_STRING).when(mServiceInfo).loadSummary(any());
281         setShortcutEnabled(mServiceInfo.getComponentName(), false);
282 
283         String summary = AccessibilitySettings.getServiceSummary(mContext,
284                 mServiceInfo, SERVICE_DISABLED).toString();
285 
286         assertThat(summary).isEqualTo(
287                 mContext.getString(R.string.generic_accessibility_service_off));
288     }
289 
290     @Test
getServiceSummary_disableServiceShortcutOn_showsDisabledShortcutOn()291     public void getServiceSummary_disableServiceShortcutOn_showsDisabledShortcutOn() {
292         doReturn(EMPTY_STRING).when(mServiceInfo).loadSummary(any());
293         setShortcutEnabled(mServiceInfo.getComponentName(), true);
294 
295         String summary = AccessibilitySettings.getServiceSummary(mContext,
296                 mServiceInfo, SERVICE_DISABLED).toString();
297 
298         assertThat(summary).isEqualTo(
299                 mContext.getString(R.string.generic_accessibility_service_off));
300     }
301 
302     @Test
getServiceSummary_enableServiceShortcutOffAndHasSummary_showsEnabledSummary()303     public void getServiceSummary_enableServiceShortcutOffAndHasSummary_showsEnabledSummary() {
304         setShortcutEnabled(mServiceInfo.getComponentName(), false);
305         doReturn(DEFAULT_SUMMARY).when(mServiceInfo).loadSummary(any());
306 
307         String summary = AccessibilitySettings.getServiceSummary(mContext,
308                 mServiceInfo, SERVICE_ENABLED).toString();
309 
310         assertThat(summary).isEqualTo(
311                 mContext.getString(
312                         com.android.settingslib.R.string.preference_summary_default_combination,
313                         mContext.getString(R.string.generic_accessibility_service_on),
314                         DEFAULT_SUMMARY));
315     }
316 
317     @Test
getServiceSummary_enableServiceShortcutOnAndHasSummary_showsEnabledSummary()318     public void getServiceSummary_enableServiceShortcutOnAndHasSummary_showsEnabledSummary() {
319         doReturn(DEFAULT_SUMMARY).when(mServiceInfo).loadSummary(any());
320         setShortcutEnabled(mServiceInfo.getComponentName(), true);
321 
322         String summary = AccessibilitySettings.getServiceSummary(mContext,
323                 mServiceInfo, SERVICE_ENABLED).toString();
324 
325         assertThat(summary).isEqualTo(
326                 mContext.getString(
327                         com.android.settingslib.R.string.preference_summary_default_combination,
328                         mContext.getString(R.string.generic_accessibility_service_on),
329                         DEFAULT_SUMMARY));
330     }
331 
332     @Test
getServiceSummary_disableServiceShortcutOnAndHasSummary_showsDisabledSummary()333     public void getServiceSummary_disableServiceShortcutOnAndHasSummary_showsDisabledSummary() {
334         doReturn(DEFAULT_SUMMARY).when(mServiceInfo).loadSummary(any());
335         setShortcutEnabled(mServiceInfo.getComponentName(), true);
336 
337         String summary = AccessibilitySettings.getServiceSummary(mContext,
338                 mServiceInfo, SERVICE_DISABLED).toString();
339 
340         assertThat(summary).isEqualTo(
341                 mContext.getString(
342                         com.android.settingslib.R.string.preference_summary_default_combination,
343                         mContext.getString(R.string.generic_accessibility_service_off),
344                         DEFAULT_SUMMARY));
345     }
346 
347     @Test
getServiceSummary_disableServiceShortcutOffAndHasSummary_showsDisabledSummary()348     public void getServiceSummary_disableServiceShortcutOffAndHasSummary_showsDisabledSummary() {
349         setShortcutEnabled(mServiceInfo.getComponentName(), false);
350         doReturn(DEFAULT_SUMMARY).when(mServiceInfo).loadSummary(any());
351 
352         String summary = AccessibilitySettings.getServiceSummary(mContext,
353                 mServiceInfo, SERVICE_DISABLED).toString();
354 
355         assertThat(summary).isEqualTo(
356                 mContext.getString(
357                         com.android.settingslib.R.string.preference_summary_default_combination,
358                         mContext.getString(R.string.generic_accessibility_service_off),
359                         DEFAULT_SUMMARY));
360     }
361 
362     @Test
getServiceDescription_serviceCrash_showsStopped()363     public void getServiceDescription_serviceCrash_showsStopped() {
364         mServiceInfo.crashed = true;
365 
366         String description = AccessibilitySettings.getServiceDescription(mContext,
367                 mServiceInfo, SERVICE_ENABLED).toString();
368 
369         assertThat(description).isEqualTo(
370                 mContext.getString(R.string.accessibility_description_state_stopped));
371     }
372 
373     @Test
getServiceDescription_haveDescription_showsDescription()374     public void getServiceDescription_haveDescription_showsDescription() {
375         doReturn(DEFAULT_DESCRIPTION).when(mServiceInfo).loadDescription(any());
376 
377         String description = AccessibilitySettings.getServiceDescription(mContext,
378                 mServiceInfo, SERVICE_ENABLED).toString();
379 
380         assertThat(description).isEqualTo(DEFAULT_DESCRIPTION);
381     }
382 
383     @Test
onCreate_haveRegisterToSpecificUrisAndActions()384     public void onCreate_haveRegisterToSpecificUrisAndActions() {
385         setupFragment();
386 
387         assertUriObserversContainsClazz(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS,
388                 AccessibilitySettingsContentObserver.class).isTrue();
389         assertUriObserversContainsClazz(Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE,
390                 AccessibilitySettingsContentObserver.class).isTrue();
391         assertUriObserversContainsClazz(Settings.Secure.ACCESSIBILITY_QS_TARGETS,
392                 AccessibilitySettingsContentObserver.class).isTrue();
393     }
394 
395     @Test
onDestroy_unregisterObserverAndReceiver()396     public void onDestroy_unregisterObserverAndReceiver() {
397         setupFragment();
398 
399         mActivityController.pause().stop().destroy();
400 
401         assertUriObserversContainsClazz(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS,
402                 AccessibilitySettingsContentObserver.class).isFalse();
403         assertUriObserversContainsClazz(Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE,
404                 AccessibilitySettingsContentObserver.class).isFalse();
405         assertUriObserversContainsClazz(Settings.Secure.ACCESSIBILITY_QS_TARGETS,
406                 AccessibilitySettingsContentObserver.class).isFalse();
407     }
408 
409     @Test
onContentChanged_updatePreferenceInForeground_preferenceUpdated()410     public void onContentChanged_updatePreferenceInForeground_preferenceUpdated() {
411         setupFragment();
412         mShadowAccessibilityManager.setInstalledAccessibilityServiceList(
413                 singletonList(mServiceInfo));
414 
415         mFragment.onContentChanged();
416 
417         RestrictedPreference preference = mFragment.getPreferenceScreen().findPreference(
418                 SERVICE_COMPONENT_NAME.flattenToString());
419 
420         assertThat(preference).isNotNull();
421 
422     }
423 
424     @Test
onContentChanged_updatePreferenceInBackground_preferenceUpdated()425     public void onContentChanged_updatePreferenceInBackground_preferenceUpdated() {
426         setupFragment();
427         mFragment.onPause();
428         mFragment.onStop();
429 
430         mShadowAccessibilityManager.setInstalledAccessibilityServiceList(
431                 singletonList(mServiceInfo));
432 
433         mFragment.onContentChanged();
434         mFragment.onStart();
435         mFragment.onResume();
436 
437         RestrictedPreference preference = mFragment.getPreferenceScreen().findPreference(
438                 SERVICE_COMPONENT_NAME.flattenToString());
439 
440         assertThat(preference).isNotNull();
441 
442     }
443 
444     @Test
testAccessibilityMenuInSystem_IncludedInInteractionControl()445     public void testAccessibilityMenuInSystem_IncludedInInteractionControl() {
446         mShadowAccessibilityManager.setInstalledAccessibilityServiceList(
447                 List.of(getMockAccessibilityServiceInfo(
448                         AccessibilityUtils.ACCESSIBILITY_MENU_IN_SYSTEM,
449                         /*isSystemApp=*/true)));
450         setupFragment();
451 
452         assertThat(getPreferenceCategory(AccessibilityUtils.ACCESSIBILITY_MENU_IN_SYSTEM))
453                 .isEqualTo(AccessibilitySettings.CATEGORY_INTERACTION_CONTROL);
454     }
455 
456     @Test
testNonPreinstalledApp_IncludedInDownloadedCategory()457     public void testNonPreinstalledApp_IncludedInDownloadedCategory() {
458         mShadowAccessibilityManager.setInstalledAccessibilityServiceList(
459                 List.of(getMockAccessibilityServiceInfo(
460                         AccessibilityUtils.ACCESSIBILITY_MENU_IN_SYSTEM,
461                         /*isSystemApp=*/false)));
462         setupFragment();
463 
464         assertThat(getPreferenceCategory(AccessibilityUtils.ACCESSIBILITY_MENU_IN_SYSTEM))
465                 .isEqualTo(AccessibilitySettings.CATEGORY_DOWNLOADED_SERVICES);
466     }
467 
468     @Test
testAccessibilityMenuInSystem_NoPrefWhenNotInstalled()469     public void testAccessibilityMenuInSystem_NoPrefWhenNotInstalled() {
470         mShadowAccessibilityManager.setInstalledAccessibilityServiceList(List.of());
471         setupFragment();
472 
473         final RestrictedPreference pref = mFragment.getPreferenceScreen().findPreference(
474                 AccessibilityUtils.ACCESSIBILITY_MENU_IN_SYSTEM.flattenToString());
475         assertThat(pref).isNull();
476     }
477 
478     @Test
testSameNamedServiceAndActivity_bothPreferencesExist()479     public void testSameNamedServiceAndActivity_bothPreferencesExist() {
480         final PackageManager pm = mContext.getPackageManager();
481         AccessibilityServiceInfo a11yServiceInfo = mServiceInfo;
482         AccessibilityShortcutInfo a11yShortcutInfo = getMockAccessibilityShortcutInfo();
483         // Ensure the test service and activity have the same package name and label.
484         // Before this change, any service and activity with the same package name and
485         // label would cause the service to be hidden.
486         assertThat(a11yServiceInfo.getComponentName())
487                 .isNotEqualTo(a11yShortcutInfo.getComponentName());
488         assertThat(a11yServiceInfo.getComponentName().getPackageName())
489                 .isEqualTo(a11yShortcutInfo.getComponentName().getPackageName());
490         assertThat(a11yServiceInfo.getResolveInfo().serviceInfo.loadLabel(pm))
491                 .isEqualTo(a11yShortcutInfo.getActivityInfo().loadLabel(pm));
492         // Prepare A11yManager with the test service and activity.
493         mShadowAccessibilityManager.setInstalledAccessibilityServiceList(
494                 List.of(mServiceInfo));
495         mShadowAccessibilityManager.setInstalledAccessibilityShortcutListAsUser(
496                 List.of(getMockAccessibilityShortcutInfo()));
497         setupFragment();
498 
499         // Both service and activity preferences should exist on the page.
500         RestrictedPreference servicePref = mFragment.getPreferenceScreen().findPreference(
501                 a11yServiceInfo.getComponentName().flattenToString());
502         RestrictedPreference activityPref = mFragment.getPreferenceScreen().findPreference(
503                 a11yShortcutInfo.getComponentName().flattenToString());
504         assertThat(servicePref).isNotNull();
505         assertThat(activityPref).isNotNull();
506     }
507 
getPreferenceCategory(ComponentName componentName)508     private String getPreferenceCategory(ComponentName componentName) {
509         return mFragment.getPreferenceScreen().findPreference(
510                 componentName.flattenToString()).getParent().getKey();
511     }
512 
getMockAccessibilityServiceInfo(ComponentName componentName)513     private AccessibilityServiceInfo getMockAccessibilityServiceInfo(ComponentName componentName) {
514         return getMockAccessibilityServiceInfo(componentName, true);
515     }
516 
getMockAccessibilityServiceInfo(ComponentName componentName, boolean isSystemApp)517     private AccessibilityServiceInfo getMockAccessibilityServiceInfo(ComponentName componentName,
518             boolean isSystemApp) {
519         final ApplicationInfo applicationInfo = Mockito.mock(ApplicationInfo.class);
520         when(applicationInfo.isSystemApp()).thenReturn(isSystemApp);
521         final ServiceInfo serviceInfo = Mockito.spy(new ServiceInfo());
522         applicationInfo.packageName = componentName.getPackageName();
523         serviceInfo.packageName = componentName.getPackageName();
524         serviceInfo.name = componentName.getClassName();
525         serviceInfo.applicationInfo = applicationInfo;
526         when(serviceInfo.loadLabel(any())).thenReturn(DEFAULT_LABEL);
527 
528         final ResolveInfo resolveInfo = new ResolveInfo();
529         resolveInfo.serviceInfo = serviceInfo;
530         try {
531             final AccessibilityServiceInfo info = new AccessibilityServiceInfo(resolveInfo,
532                     mContext);
533             info.setComponentName(componentName);
534             return info;
535         } catch (XmlPullParserException | IOException e) {
536             // Do nothing
537         }
538 
539         return null;
540     }
541 
getMockAccessibilityShortcutInfo()542     private AccessibilityShortcutInfo getMockAccessibilityShortcutInfo() {
543         AccessibilityShortcutInfo mockInfo = Mockito.mock(AccessibilityShortcutInfo.class);
544         final ActivityInfo activityInfo = Mockito.mock(ActivityInfo.class);
545         activityInfo.applicationInfo = new ApplicationInfo();
546         when(mockInfo.getActivityInfo()).thenReturn(activityInfo);
547         when(activityInfo.loadLabel(any())).thenReturn(DEFAULT_LABEL);
548         when(mockInfo.loadSummary(any())).thenReturn(DEFAULT_SUMMARY);
549         when(mockInfo.loadDescription(any())).thenReturn(DEFAULT_DESCRIPTION);
550         when(mockInfo.getComponentName()).thenReturn(ACTIVITY_COMPONENT_NAME);
551         return mockInfo;
552     }
553 
setInvisibleToggleFragmentType(AccessibilityServiceInfo info)554     private void setInvisibleToggleFragmentType(AccessibilityServiceInfo info) {
555         info.getResolveInfo().serviceInfo.applicationInfo.targetSdkVersion = Build.VERSION_CODES.R;
556         info.flags |= AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON;
557     }
558 
setupFragment()559     private void setupFragment() {
560         mActivityController.create().start().resume();
561         Fragment fragment = mActivityController.get().getSupportFragmentManager().findFragmentById(
562                 R.id.main_content);
563 
564         assertThat(fragment).isNotNull();
565         assertThat(fragment).isInstanceOf(AccessibilitySettings.class);
566 
567         mFragment = (AccessibilitySettings) fragment;
568     }
569 
setShortcutEnabled(ComponentName componentName, boolean enabled)570     private void setShortcutEnabled(ComponentName componentName, boolean enabled) {
571         mShadowAccessibilityManager.setAccessibilityShortcutTargets(
572                 SOFTWARE, (enabled) ? List.of(componentName.flattenToString()) : List.of());
573     }
574 
assertUriObserversContainsClazz( String settingUri, Class<?> clazz)575     private BooleanSubject assertUriObserversContainsClazz(
576             String settingUri, Class<?> clazz) {
577         ShadowContentResolver shadowContentResolver = shadowOf(mContext.getContentResolver());
578         Collection<ContentObserver> observers =
579                 shadowContentResolver.getContentObservers(
580                         Settings.Secure.getUriFor(settingUri));
581 
582         return assertThat(observers.stream().anyMatch(clazz::isInstance));
583     }
584 }
585