Home | History | Annotate | Download | only in search
      1 /*
      2  * Copyright (C) 2016 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.search;
     18 
     19 import static com.google.common.truth.Truth.assertThat;
     20 
     21 import static org.mockito.Matchers.any;
     22 import static org.mockito.Matchers.anyBoolean;
     23 import static org.mockito.Matchers.anyInt;
     24 import static org.mockito.Matchers.anyString;
     25 import static org.mockito.Matchers.eq;
     26 import static org.mockito.Mockito.mock;
     27 import static org.mockito.Mockito.never;
     28 import static org.mockito.Mockito.only;
     29 import static org.mockito.Mockito.reset;
     30 import static org.mockito.Mockito.times;
     31 import static org.mockito.Mockito.verify;
     32 
     33 import android.accessibilityservice.AccessibilityServiceInfo;
     34 import android.app.Activity;
     35 import android.app.Application;
     36 import android.app.LoaderManager;
     37 import android.content.BroadcastReceiver;
     38 import android.content.Context;
     39 import android.content.Intent;
     40 import android.content.Loader;
     41 import android.content.pm.ActivityInfo;
     42 import android.content.pm.PackageManager;
     43 import android.content.pm.ResolveInfo;
     44 import android.content.pm.ServiceInfo;
     45 import android.database.ContentObserver;
     46 import android.hardware.input.InputManager;
     47 import android.net.Uri;
     48 import android.os.Bundle;
     49 import android.print.PrintManager;
     50 import android.print.PrintServicesLoader;
     51 import android.printservice.PrintServiceInfo;
     52 import android.provider.Settings;
     53 import android.provider.UserDictionary;
     54 import android.view.inputmethod.InputMethodInfo;
     55 
     56 import com.android.internal.content.PackageMonitor;
     57 import com.android.settings.SettingsRobolectricTestRunner;
     58 import com.android.settings.TestConfig;
     59 import com.android.settings.accessibility.AccessibilitySettings;
     60 import com.android.settings.inputmethod.AvailableVirtualKeyboardFragment;
     61 import com.android.settings.inputmethod.PhysicalKeyboardFragment;
     62 import com.android.settings.inputmethod.VirtualKeyboardFragment;
     63 import com.android.settings.language.LanguageAndInputSettings;
     64 import com.android.settings.print.PrintSettingsFragment;
     65 import com.android.settings.search2.DatabaseIndexingManager;
     66 import com.android.settings.testutils.DatabaseTestUtils;
     67 import com.android.settings.testutils.shadow.ShadowActivityWithLoadManager;
     68 import com.android.settings.testutils.shadow.ShadowContextImplWithRegisterReceiver;
     69 import com.android.settings.testutils.shadow.ShadowInputManager;
     70 import com.android.settings.testutils.shadow.ShadowInputMethodManagerWithMethodList;
     71 import com.android.settings.testutils.shadow.ShadowPackageMonitor;
     72 
     73 import org.junit.After;
     74 import org.junit.Before;
     75 import org.junit.Test;
     76 import org.junit.runner.RunWith;
     77 import org.mockito.Mock;
     78 import org.mockito.MockitoAnnotations;
     79 import org.robolectric.Robolectric;
     80 import org.robolectric.RuntimeEnvironment;
     81 import org.robolectric.annotation.Config;
     82 import org.robolectric.internal.ShadowExtractor;
     83 import org.robolectric.res.builder.RobolectricPackageManager;
     84 import org.robolectric.shadows.ShadowAccessibilityManager;
     85 import org.robolectric.shadows.ShadowApplication;
     86 import org.robolectric.shadows.ShadowContentResolver;
     87 import org.xmlpull.v1.XmlPullParserException;
     88 
     89 import java.io.IOException;
     90 import java.util.ArrayList;
     91 import java.util.Collection;
     92 import java.util.Collections;
     93 import java.util.List;
     94 
     95 @RunWith(SettingsRobolectricTestRunner.class)
     96 @Config(
     97         manifest = TestConfig.MANIFEST_PATH,
     98         sdk = TestConfig.SDK_VERSION,
     99         shadows = {
    100                 ShadowActivityWithLoadManager.class,
    101                 ShadowContextImplWithRegisterReceiver.class,
    102                 ShadowInputManager.class,
    103                 ShadowInputMethodManagerWithMethodList.class,
    104                 ShadowPackageMonitor.class,
    105         }
    106 )
    107 public class DynamicIndexableContentMonitorTest {
    108 
    109     private static final int LOADER_ID = 1234;
    110     private static final String A11Y_PACKAGE_1 = "a11y-1";
    111     private static final String A11Y_PACKAGE_2 = "a11y-2";
    112     private static final String IME_PACKAGE_1 = "ime-1";
    113     private static final String IME_PACKAGE_2 = "ime-2";
    114 
    115     @Mock
    116     private LoaderManager mLoaderManager;
    117     @Mock
    118     private DatabaseIndexingManager mIndexManager;
    119 
    120     private Activity mActivity;
    121     private InputManager mInputManager;
    122 
    123     private ShadowContextImplWithRegisterReceiver mShadowContextImpl;
    124     private ShadowActivityWithLoadManager mShadowActivity;
    125     private ShadowAccessibilityManager mShadowAccessibilityManager;
    126     private ShadowInputMethodManagerWithMethodList mShadowInputMethodManager;
    127     private RobolectricPackageManager mRobolectricPackageManager;
    128 
    129     private final DynamicIndexableContentMonitor mMonitor = new DynamicIndexableContentMonitor();
    130 
    131     @Before
    132     public void setUp() {
    133         MockitoAnnotations.initMocks(this);
    134         mActivity = Robolectric.buildActivity(Activity.class).get();
    135         mInputManager = InputManager.getInstance();
    136 
    137         // Robolectric shadows.
    138         mShadowContextImpl = (ShadowContextImplWithRegisterReceiver) ShadowExtractor.extract(
    139                 ((Application) ShadowApplication.getInstance().getApplicationContext())
    140                 .getBaseContext());
    141         mShadowActivity = (ShadowActivityWithLoadManager) ShadowExtractor.extract(mActivity);
    142         mShadowAccessibilityManager = (ShadowAccessibilityManager) ShadowExtractor.extract(
    143                 mActivity.getSystemService(Context.ACCESSIBILITY_SERVICE));
    144         mShadowInputMethodManager = (ShadowInputMethodManagerWithMethodList) ShadowExtractor
    145                 .extract(mActivity.getSystemService(Context.INPUT_METHOD_SERVICE));
    146         mRobolectricPackageManager = RuntimeEnvironment.getRobolectricPackageManager();
    147 
    148         // Setup shadows.
    149         mShadowContextImpl.setSystemService(Context.PRINT_SERVICE, mock(PrintManager.class));
    150         mShadowContextImpl.setSystemService(Context.INPUT_SERVICE, mInputManager);
    151         mShadowActivity.setLoaderManager(mLoaderManager);
    152         mShadowAccessibilityManager.setInstalledAccessibilityServiceList(Collections.emptyList());
    153         mShadowInputMethodManager.setInputMethodList(Collections.emptyList());
    154         mRobolectricPackageManager.setSystemFeature(PackageManager.FEATURE_PRINTING, true);
    155         mRobolectricPackageManager.setSystemFeature(PackageManager.FEATURE_INPUT_METHODS, true);
    156     }
    157 
    158     @After
    159     public void shutDown() {
    160         mMonitor.unregister(mActivity, LOADER_ID);
    161         // BroadcastReceiver must be unregistered.
    162         assertThat(extractPackageMonitor()).isNull();
    163 
    164         DynamicIndexableContentMonitor.resetForTesting();
    165         mRobolectricPackageManager.reset();
    166 
    167         DatabaseTestUtils.clearDb();
    168     }
    169 
    170     @Test
    171     public void testLockedUser() {
    172         mMonitor.register(mActivity, LOADER_ID, mIndexManager, false /* isUserUnlocked */);
    173 
    174         // No loader procedure happens.
    175         verify(mLoaderManager, never()).initLoader(
    176                 anyInt(), any(Bundle.class), any(LoaderManager.LoaderCallbacks.class));
    177         // No indexing happens.
    178         verify(mIndexManager, never()).updateFromClassNameResource(
    179                 anyString(), anyBoolean());
    180 
    181         mMonitor.unregister(mActivity, LOADER_ID);
    182 
    183         // No destroy loader should happen.
    184         verify(mLoaderManager, never()).destroyLoader(anyInt());
    185     }
    186 
    187     @Test
    188     public void testWithNoPrintingFeature() {
    189         mRobolectricPackageManager.setSystemFeature(PackageManager.FEATURE_PRINTING, false);
    190 
    191         mMonitor.register(mActivity, LOADER_ID, mIndexManager, true /* isUserUnlocked */);
    192 
    193         // No loader procedure happens.
    194         verify(mLoaderManager, never()).initLoader(
    195                 anyInt(), any(Bundle.class), any(LoaderManager.LoaderCallbacks.class));
    196         verifyNoIndexing(PrintSettingsFragment.class);
    197 
    198         mMonitor.unregister(mActivity, LOADER_ID);
    199 
    200         // No destroy loader should happen.
    201         verify(mLoaderManager, never()).destroyLoader(anyInt());
    202         // BroadcastReceiver must be unregistered.
    203         assertThat(extractPackageMonitor()).isNull();
    204 
    205         // To suppress spurious test fail in {@link #shutDown()}.
    206         mMonitor.register(mActivity, LOADER_ID, mIndexManager, true /* isUserUnlocked */);
    207     }
    208 
    209     @Test
    210     public void testPrinterServiceIndex() {
    211         mMonitor.register(mActivity, LOADER_ID, mIndexManager, true /* isUserUnlocked */);
    212 
    213         // Loader procedure happens.
    214         verify(mLoaderManager, only()).initLoader(LOADER_ID, null, mMonitor);
    215 
    216         // Loading print services happens.
    217         final Loader<List<PrintServiceInfo>> loader =
    218                 mMonitor.onCreateLoader(LOADER_ID, null /* args */);
    219         assertThat(loader).isInstanceOf(PrintServicesLoader.class);
    220         verifyNoIndexing(PrintSettingsFragment.class);
    221 
    222         mMonitor.onLoadFinished(loader, Collections.emptyList());
    223 
    224         verifyIncrementalIndexing(PrintSettingsFragment.class);
    225     }
    226 
    227     @Test
    228     public void testInputDevicesMonitor() {
    229         mMonitor.register(mActivity, LOADER_ID, mIndexManager, true /* isUserUnlocked */);
    230 
    231         // Rebuild indexing should happen.
    232         verifyIncrementalIndexing(PhysicalKeyboardFragment.class);
    233         // Input monitor should be registered to InputManager.
    234         final InputManager.InputDeviceListener listener = extactInputDeviceListener();
    235         assertThat(listener).isNotNull();
    236 
    237         /*
    238          * Nothing happens on successive register calls.
    239          */
    240         mMonitor.unregister(mActivity, LOADER_ID);
    241         reset(mIndexManager);
    242 
    243         mMonitor.register(mActivity, LOADER_ID, mIndexManager, true /* isUserUnlocked */);
    244 
    245         verifyNoIndexing(PhysicalKeyboardFragment.class);
    246         assertThat(extactInputDeviceListener()).isEqualTo(listener);
    247 
    248         /*
    249          * A device is added.
    250          */
    251         reset(mIndexManager);
    252 
    253         listener.onInputDeviceAdded(1 /* deviceId */);
    254 
    255         verifyIncrementalIndexing(PhysicalKeyboardFragment.class);
    256 
    257         /*
    258          * A device is removed.
    259          */
    260         reset(mIndexManager);
    261 
    262         listener.onInputDeviceRemoved(2 /* deviceId */);
    263 
    264         verifyIncrementalIndexing(PhysicalKeyboardFragment.class);
    265 
    266         /*
    267          * A device is changed.
    268          */
    269         reset(mIndexManager);
    270 
    271         listener.onInputDeviceChanged(3 /* deviceId */);
    272 
    273         verifyIncrementalIndexing(PhysicalKeyboardFragment.class);
    274     }
    275 
    276     @Test
    277     public void testAccessibilityServicesMonitor() throws Exception {
    278         mMonitor.register(mActivity, LOADER_ID, mIndexManager, true /* isUserUnlocked */);
    279 
    280         verifyIncrementalIndexing(AccessibilitySettings.class);
    281 
    282         /*
    283          * When an accessibility service package is installed, incremental indexing happen.
    284          */
    285         reset(mIndexManager);
    286 
    287         installAccessibilityService(A11Y_PACKAGE_1);
    288 
    289         verifyIncrementalIndexing(AccessibilitySettings.class);
    290 
    291         /*
    292          * When another accessibility service package is installed, incremental indexing happens.
    293          */
    294         reset(mIndexManager);
    295 
    296         installAccessibilityService(A11Y_PACKAGE_2);
    297 
    298         verifyIncrementalIndexing(AccessibilitySettings.class);
    299 
    300         /*
    301          * When an accessibility service is disabled, rebuild indexing happens.
    302          */
    303         reset(mIndexManager);
    304 
    305         disableInstalledPackage(A11Y_PACKAGE_1);
    306 
    307         verifyIncrementalIndexing(AccessibilitySettings.class);
    308 
    309         /*
    310          * When an accessibility service is enabled, incremental indexing happens.
    311          */
    312         reset(mIndexManager);
    313 
    314         enableInstalledPackage(A11Y_PACKAGE_1);
    315 
    316         verifyIncrementalIndexing(AccessibilitySettings.class);
    317 
    318         /*
    319          * When an accessibility service package is uninstalled, rebuild indexing happens.
    320          */
    321         reset(mIndexManager);
    322 
    323         uninstallAccessibilityService(A11Y_PACKAGE_1);
    324 
    325         verifyIncrementalIndexing(AccessibilitySettings.class);
    326 
    327         /*
    328          * When an input method service package is installed, nothing happens.
    329          */
    330         reset(mIndexManager);
    331 
    332         installInputMethodService(IME_PACKAGE_1);
    333 
    334         verifyNoIndexing(AccessibilitySettings.class);
    335     }
    336 
    337     @Test
    338     public void testInputMethodServicesMonitor() throws Exception {
    339         mMonitor.register(mActivity, LOADER_ID, mIndexManager, true /* isUserUnlocked */);
    340 
    341         verifyIncrementalIndexing(VirtualKeyboardFragment.class);
    342         verifyIncrementalIndexing(AvailableVirtualKeyboardFragment.class);
    343 
    344         final Uri enabledInputMethodsContentUri = Settings.Secure.getUriFor(
    345                 Settings.Secure.ENABLED_INPUT_METHODS);
    346         // Content observer should be registered.
    347         final ContentObserver observer = extractContentObserver(enabledInputMethodsContentUri);
    348         assertThat(observer).isNotNull();
    349 
    350         /*
    351          * When an input method service package is installed, incremental indexing happen.
    352          */
    353         reset(mIndexManager);
    354 
    355         installInputMethodService(IME_PACKAGE_1);
    356 
    357         verifyIncrementalIndexing(VirtualKeyboardFragment.class);
    358         verifyIncrementalIndexing(AvailableVirtualKeyboardFragment.class);
    359 
    360         /*
    361          * When another input method service package is installed, incremental indexing happens.
    362          */
    363         reset(mIndexManager);
    364 
    365         installInputMethodService(IME_PACKAGE_2);
    366 
    367         verifyIncrementalIndexing(VirtualKeyboardFragment.class);
    368         verifyIncrementalIndexing(AvailableVirtualKeyboardFragment.class);
    369 
    370         /*
    371          * When an input method service is disabled, rebuild indexing happens.
    372          */
    373         reset(mIndexManager);
    374 
    375         disableInstalledPackage(IME_PACKAGE_1);
    376 
    377         verifyIncrementalIndexing(VirtualKeyboardFragment.class);
    378         verifyIncrementalIndexing(AvailableVirtualKeyboardFragment.class);
    379 
    380         /*
    381          * When an input method service is enabled, incremental indexing happens.
    382          */
    383         reset(mIndexManager);
    384 
    385         enableInstalledPackage(IME_PACKAGE_1);
    386 
    387         verifyIncrementalIndexing(VirtualKeyboardFragment.class);
    388         verifyIncrementalIndexing(AvailableVirtualKeyboardFragment.class);
    389 
    390         /*
    391          * When an input method service package is uninstalled, rebuild indexing happens.
    392          */
    393         reset(mIndexManager);
    394 
    395         uninstallInputMethodService(IME_PACKAGE_1);
    396 
    397         verifyIncrementalIndexing(VirtualKeyboardFragment.class);
    398         verifyIncrementalIndexing(AvailableVirtualKeyboardFragment.class);
    399 
    400         /*
    401          * When an accessibility service package is installed, nothing happens.
    402          */
    403         reset(mIndexManager);
    404 
    405         installAccessibilityService(A11Y_PACKAGE_1);
    406 
    407         verifyNoIndexing(VirtualKeyboardFragment.class);
    408         verifyNoIndexing(AvailableVirtualKeyboardFragment.class);
    409 
    410         /*
    411          * When enabled IMEs list is changed, rebuild indexing happens.
    412          */
    413         reset(mIndexManager);
    414 
    415         observer.onChange(false /* selfChange */, enabledInputMethodsContentUri);
    416 
    417         verifyIncrementalIndexing(VirtualKeyboardFragment.class);
    418         verifyIncrementalIndexing(AvailableVirtualKeyboardFragment.class);
    419     }
    420 
    421     @Test
    422     public void testUserDictionaryChangeMonitor() throws Exception {
    423         mMonitor.register(mActivity, LOADER_ID, mIndexManager, true /* isUserUnlocked */);
    424 
    425         // Content observer should be registered.
    426         final ContentObserver observer = extractContentObserver(UserDictionary.Words.CONTENT_URI);
    427         assertThat(observer).isNotNull();
    428 
    429         verifyIncrementalIndexing(LanguageAndInputSettings.class);
    430 
    431         /*
    432          * When user dictionary content is changed, rebuild indexing happens.
    433          */
    434         reset(mIndexManager);
    435 
    436         observer.onChange(false /* selfChange */, UserDictionary.Words.CONTENT_URI);
    437 
    438         verifyIncrementalIndexing(LanguageAndInputSettings.class);
    439     }
    440 
    441     /*
    442      * Verification helpers.
    443      */
    444 
    445     private void verifyNoIndexing(Class<?> indexingClass) {
    446         verify(mIndexManager, never()).updateFromClassNameResource(eq(indexingClass.getName()),
    447                 anyBoolean());
    448     }
    449 
    450     private void verifyIncrementalIndexing(Class<?> indexingClass) {
    451         verify(mIndexManager, times(1)).updateFromClassNameResource(indexingClass.getName(),
    452                 true /* includeInSearchResults */);
    453         verify(mIndexManager, never()).updateFromClassNameResource(indexingClass.getName(),
    454                 false /* includeInSearchResults */);
    455     }
    456 
    457     /*
    458      * Testing helper methods.
    459      */
    460 
    461     private InputManager.InputDeviceListener extactInputDeviceListener() {
    462         List<InputManager.InputDeviceListener> listeners = ((ShadowInputManager) ShadowExtractor
    463                 .extract(mInputManager))
    464                 .getRegisteredInputDeviceListeners();
    465         InputManager.InputDeviceListener inputDeviceListener = null;
    466         for (InputManager.InputDeviceListener listener : listeners) {
    467             if (isUnderTest(listener)) {
    468                 if (inputDeviceListener != null) {
    469                     assertThat(listener).isEqualTo(inputDeviceListener);
    470                 } else {
    471                     inputDeviceListener = listener;
    472                 }
    473             }
    474         }
    475         return inputDeviceListener;
    476     }
    477 
    478     private PackageMonitor extractPackageMonitor() {
    479         List<ShadowApplication.Wrapper> receivers = ShadowApplication.getInstance()
    480                 .getRegisteredReceivers();
    481         PackageMonitor packageMonitor = null;
    482         for (ShadowApplication.Wrapper wrapper : receivers) {
    483             BroadcastReceiver receiver = wrapper.getBroadcastReceiver();
    484             if (isUnderTest(receiver) && receiver instanceof PackageMonitor) {
    485                 if (packageMonitor != null) {
    486                     assertThat(receiver).isEqualTo(packageMonitor);
    487                 } else {
    488                     packageMonitor = (PackageMonitor) receiver;
    489                 }
    490             }
    491         }
    492         return packageMonitor;
    493     }
    494 
    495     private ContentObserver extractContentObserver(Uri uri) {
    496         ShadowContentResolver contentResolver = (ShadowContentResolver) ShadowExtractor
    497                 .extract(mActivity.getContentResolver());
    498         Collection<ContentObserver> observers = contentResolver.getContentObservers(uri);
    499         ContentObserver contentObserver = null;
    500         for (ContentObserver observer : observers) {
    501             if (isUnderTest(observer)) {
    502                 if (contentObserver != null) {
    503                     assertThat(observer).isEqualTo(contentObserver);
    504                 } else {
    505                     contentObserver = observer;
    506                 }
    507             }
    508         }
    509         return contentObserver;
    510     }
    511 
    512     private void enableInstalledPackage(String packageName) {
    513         ((PackageManager) mRobolectricPackageManager).setApplicationEnabledSetting(
    514                 packageName, PackageManager.COMPONENT_ENABLED_STATE_DEFAULT, 0 /* flags */);
    515         extractPackageMonitor().onPackageModified(packageName);
    516         Robolectric.flushBackgroundThreadScheduler();
    517     }
    518 
    519     private void disableInstalledPackage(String packageName) {
    520         ((PackageManager) mRobolectricPackageManager).setApplicationEnabledSetting(
    521                 packageName, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, 0 /* flags */);
    522         extractPackageMonitor().onPackageModified(packageName);
    523         Robolectric.flushBackgroundThreadScheduler();
    524     }
    525 
    526     private void installAccessibilityService(String packageName) throws Exception {
    527         final AccessibilityServiceInfo serviceToAdd = buildAccessibilityServiceInfo(packageName);
    528 
    529         final List<AccessibilityServiceInfo> services = new ArrayList<>();
    530         services.addAll(mShadowAccessibilityManager.getInstalledAccessibilityServiceList());
    531         services.add(serviceToAdd);
    532         mShadowAccessibilityManager.setInstalledAccessibilityServiceList(services);
    533 
    534         final Intent intent = DynamicIndexableContentMonitor
    535                 .getAccessibilityServiceIntent(packageName);
    536         mRobolectricPackageManager.addResolveInfoForIntent(intent, serviceToAdd.getResolveInfo());
    537         mRobolectricPackageManager.addPackage(packageName);
    538 
    539         extractPackageMonitor()
    540                 .onPackageAppeared(packageName, PackageMonitor.PACKAGE_PERMANENT_CHANGE);
    541         Robolectric.flushBackgroundThreadScheduler();
    542     }
    543 
    544     private void uninstallAccessibilityService(String packageName) throws Exception {
    545         final AccessibilityServiceInfo serviceToRemove = buildAccessibilityServiceInfo(packageName);
    546 
    547         final List<AccessibilityServiceInfo> services = new ArrayList<>();
    548         services.addAll(mShadowAccessibilityManager.getInstalledAccessibilityServiceList());
    549         services.remove(serviceToRemove);
    550         mShadowAccessibilityManager.setInstalledAccessibilityServiceList(services);
    551 
    552         final Intent intent = DynamicIndexableContentMonitor
    553                 .getAccessibilityServiceIntent(packageName);
    554         mRobolectricPackageManager.removeResolveInfosForIntent(intent, packageName);
    555         mRobolectricPackageManager.removePackage(packageName);
    556 
    557         extractPackageMonitor()
    558                 .onPackageDisappeared(packageName, PackageMonitor.PACKAGE_PERMANENT_CHANGE);
    559         Robolectric.flushBackgroundThreadScheduler();
    560     }
    561 
    562     private void installInputMethodService(String packageName) throws Exception {
    563         final ResolveInfo resolveInfoToAdd = buildResolveInfo(packageName, "imeService");
    564         final InputMethodInfo serviceToAdd = buildInputMethodInfo(resolveInfoToAdd);
    565 
    566         final List<InputMethodInfo> services = new ArrayList<>();
    567         services.addAll(mShadowInputMethodManager.getInputMethodList());
    568         services.add(serviceToAdd);
    569         mShadowInputMethodManager.setInputMethodList(services);
    570 
    571         final Intent intent = DynamicIndexableContentMonitor.getIMEServiceIntent(packageName);
    572         mRobolectricPackageManager.addResolveInfoForIntent(intent, resolveInfoToAdd);
    573         mRobolectricPackageManager.addPackage(packageName);
    574 
    575         extractPackageMonitor()
    576                 .onPackageAppeared(packageName, PackageMonitor.PACKAGE_PERMANENT_CHANGE);
    577         Robolectric.flushBackgroundThreadScheduler();
    578     }
    579 
    580     private void uninstallInputMethodService(String packageName) throws Exception {
    581         final ResolveInfo resolveInfoToRemove = buildResolveInfo(packageName, "imeService");
    582         final InputMethodInfo serviceToRemove = buildInputMethodInfo(resolveInfoToRemove);
    583 
    584         final List<InputMethodInfo> services = new ArrayList<>();
    585         services.addAll(mShadowInputMethodManager.getInputMethodList());
    586         services.remove(serviceToRemove);
    587         mShadowInputMethodManager.setInputMethodList(services);
    588 
    589         final Intent intent = DynamicIndexableContentMonitor.getIMEServiceIntent(packageName);
    590         mRobolectricPackageManager.removeResolveInfosForIntent(intent, packageName);
    591         mRobolectricPackageManager.removePackage(packageName);
    592 
    593         extractPackageMonitor()
    594                 .onPackageDisappeared(packageName, PackageMonitor.PACKAGE_PERMANENT_CHANGE);
    595         Robolectric.flushBackgroundThreadScheduler();
    596     }
    597 
    598     private AccessibilityServiceInfo buildAccessibilityServiceInfo(String packageName)
    599             throws IOException, XmlPullParserException {
    600         return new AccessibilityServiceInfo(
    601                 buildResolveInfo(packageName, "A11yService"), mActivity);
    602     }
    603 
    604     private static InputMethodInfo buildInputMethodInfo(ResolveInfo resolveInfo) {
    605         return new InputMethodInfo(resolveInfo, false /* isAuxIme */, "SettingsActivity",
    606                 null /* subtypes */,  0 /* defaultResId */, false /* forceDefault */);
    607     }
    608 
    609     private static ResolveInfo buildResolveInfo(String packageName, String className) {
    610         final ResolveInfo resolveInfo = new ResolveInfo();
    611         resolveInfo.serviceInfo = new ServiceInfo();
    612         resolveInfo.serviceInfo.packageName = packageName;
    613         resolveInfo.serviceInfo.name = className;
    614         // To workaround that RobolectricPackageManager.removeResolveInfosForIntent() only works
    615         // for activity/broadcast resolver.
    616         resolveInfo.activityInfo = new ActivityInfo();
    617         resolveInfo.activityInfo.packageName = packageName;
    618         resolveInfo.activityInfo.name = className;
    619 
    620         return resolveInfo;
    621     }
    622 
    623     private static boolean isUnderTest(Object object) {
    624         return object.getClass().getName().startsWith(
    625                 DynamicIndexableContentMonitor.class.getName());
    626     }
    627 }
    628