Merge "Update icon and string for new device intro suggestion" into oc-mr1-dev
diff --git a/res/xml/available_virtual_keyboard.xml b/res/xml/available_virtual_keyboard.xml
new file mode 100644
index 0000000..9a0b3c6
--- /dev/null
+++ b/res/xml/available_virtual_keyboard.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2017 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<PreferenceScreen
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:key="available_virtual_keyboard_page"
+    android:title="@string/add_virtual_keyboard">
+
+</PreferenceScreen>
\ No newline at end of file
diff --git a/src/com/android/settings/SettingsActivity.java b/src/com/android/settings/SettingsActivity.java
index f120b7f..609120d 100644
--- a/src/com/android/settings/SettingsActivity.java
+++ b/src/com/android/settings/SettingsActivity.java
@@ -59,7 +59,6 @@
 import com.android.settings.dashboard.DashboardSummary;
 import com.android.settings.development.DevelopmentSettings;
 import com.android.settings.overlay.FeatureFactory;
-import com.android.settings.search.DynamicIndexableContentMonitor;
 import com.android.settings.search.SearchActivity;
 import com.android.settings.wfd.WifiDisplaySettings;
 import com.android.settings.widget.SwitchBar;
@@ -76,8 +75,6 @@
 
     private static final String LOG_TAG = "Settings";
 
-    public static final int LOADER_ID_INDEXABLE_CONTENT_MONITOR = 1;
-
     // Constants for state save/restore
     private static final String SAVE_KEY_CATEGORIES = ":settings:categories";
     @VisibleForTesting
@@ -183,8 +180,6 @@
         }
     };
 
-    private DynamicIndexableContentMonitor mDynamicIndexableContentMonitor;
-
     private SwitchBar mSwitchBar;
 
     private Button mNextButton;
@@ -535,10 +530,6 @@
                 mDevelopmentPreferencesListener);
 
         registerReceiver(mBatteryInfoReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
-        if (mDynamicIndexableContentMonitor == null) {
-            mDynamicIndexableContentMonitor = new DynamicIndexableContentMonitor();
-        }
-        mDynamicIndexableContentMonitor.register(this, LOADER_ID_INDEXABLE_CONTENT_MONITOR);
 
         updateTilesList();
     }
@@ -550,9 +541,6 @@
                 mDevelopmentPreferencesListener);
         mDevelopmentPreferencesListener = null;
         unregisterReceiver(mBatteryInfoReceiver);
-        if (mDynamicIndexableContentMonitor != null) {
-            mDynamicIndexableContentMonitor.unregister(this, LOADER_ID_INDEXABLE_CONTENT_MONITOR);
-        }
     }
 
     @Override
diff --git a/src/com/android/settings/gestures/SwipeToNotificationPreferenceController.java b/src/com/android/settings/gestures/SwipeToNotificationPreferenceController.java
index 3b5dbba..6f334a5 100644
--- a/src/com/android/settings/gestures/SwipeToNotificationPreferenceController.java
+++ b/src/com/android/settings/gestures/SwipeToNotificationPreferenceController.java
@@ -17,29 +17,24 @@
 package com.android.settings.gestures;
 
 import android.content.Context;
-import android.content.Intent;
 import android.content.SharedPreferences;
 import android.provider.Settings;
 import android.support.v7.preference.Preference;
 
-import com.android.settings.R;
 import com.android.settings.Utils;
-import com.android.settings.search.DatabaseIndexingUtils;
-import com.android.settings.search.InlineSwitchPayload;
-import com.android.settings.search.ResultPayload;
 import com.android.settingslib.core.lifecycle.Lifecycle;
 
 import static android.provider.Settings.Secure.SYSTEM_NAVIGATION_KEYS_ENABLED;
 
 public class SwipeToNotificationPreferenceController extends GesturePreferenceController {
 
-    private final int ON = 1;
-    private final int OFF = 0;
+    private static final int ON = 1;
+    private static final int OFF = 0;
 
     private static final String PREF_KEY_VIDEO = "gesture_swipe_down_fingerprint_video";
     private final String mSwipeDownFingerPrefKey;
 
-    private final String SECURE_KEY = SYSTEM_NAVIGATION_KEYS_ENABLED;
+    private static final String SECURE_KEY = SYSTEM_NAVIGATION_KEYS_ENABLED;
 
     public SwipeToNotificationPreferenceController(Context context, Lifecycle lifecycle,
             String key) {
@@ -70,28 +65,30 @@
 
     @Override
     public boolean isAvailable() {
-        return isGestureAvailable(mContext);
+        return SwipeToNotificationPreferenceController.isAvailable(mContext);
     }
 
     @Override
     public boolean onPreferenceChange(Preference preference, Object newValue) {
-        Settings.Secure.putInt(mContext.getContentResolver(), SECURE_KEY,
-                (boolean) newValue ? ON : OFF);
+        setSwipeToNotification(mContext, (boolean) newValue);
         return true;
     }
 
     @Override
     protected boolean isSwitchPrefEnabled() {
-        return Settings.Secure.getInt(mContext.getContentResolver(), SECURE_KEY, OFF) == ON;
+        return isSwipeToNotificationOn(mContext);
     }
 
-    @Override
-    public ResultPayload getResultPayload() {
-        final Intent intent = DatabaseIndexingUtils.buildSubsettingIntent(mContext,
-                SwipeToNotificationSettings.class.getName(), mSwipeDownFingerPrefKey,
-                mContext.getString(R.string.display_settings));
+    public static boolean isSwipeToNotificationOn(Context context) {
+        return Settings.Secure.getInt(context.getContentResolver(), SECURE_KEY, OFF) == ON;
+    }
 
-        return new InlineSwitchPayload(SECURE_KEY, ResultPayload.SettingsSource.SECURE,
-                ON /* onValue */, intent, isAvailable(), OFF /* defaultValue */);
+    public static boolean setSwipeToNotification(Context context, boolean isEnabled) {
+        return Settings.Secure.putInt(
+                context.getContentResolver(), SECURE_KEY, isEnabled ? ON : OFF);
+    }
+
+    public static boolean isAvailable(Context context) {
+        return isGestureAvailable(context);
     }
 }
diff --git a/src/com/android/settings/inputmethod/AvailableVirtualKeyboardFragment.java b/src/com/android/settings/inputmethod/AvailableVirtualKeyboardFragment.java
index a7862ae..48b0b4a 100644
--- a/src/com/android/settings/inputmethod/AvailableVirtualKeyboardFragment.java
+++ b/src/com/android/settings/inputmethod/AvailableVirtualKeyboardFragment.java
@@ -21,7 +21,6 @@
 import android.annotation.Nullable;
 import android.app.Activity;
 import android.app.admin.DevicePolicyManager;
-import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
@@ -31,17 +30,15 @@
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
-import android.support.v7.preference.PreferenceScreen;
+import android.provider.SearchIndexableResource;
 import android.view.inputmethod.InputMethodInfo;
 import android.view.inputmethod.InputMethodManager;
-import android.view.inputmethod.InputMethodSubtype;
 
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.settings.R;
 import com.android.settings.SettingsPreferenceFragment;
 import com.android.settings.search.BaseSearchIndexProvider;
 import com.android.settings.search.Indexable;
-import com.android.settings.search.SearchIndexableRaw;
 import com.android.settingslib.inputmethod.InputMethodAndSubtypeUtil;
 import com.android.settingslib.inputmethod.InputMethodPreference;
 import com.android.settingslib.inputmethod.InputMethodSettingValuesWrapper;
@@ -60,10 +57,9 @@
 
     @Override
     public void onCreatePreferences(Bundle bundle, String s) {
+        addPreferencesFromResource(R.xml.available_virtual_keyboard);
         Activity activity = getActivity();
-        PreferenceScreen screen = getPreferenceManager().createPreferenceScreen(activity);
-        screen.setTitle(activity.getString(R.string.available_virtual_keyboard_category));
-        setPreferenceScreen(screen);
+
         mInputMethodSettingValues = InputMethodSettingValuesWrapper.getInstance(activity);
         mImm = activity.getSystemService(InputMethodManager.class);
         mDpm = activity.getSystemService(DevicePolicyManager.class);
@@ -105,7 +101,7 @@
         }
         try {
             return packageManager.getDrawable(packageName, resId, applicationInfo);
-        } catch (Exception e){
+        } catch (Exception e) {
             return null;
         }
     }
@@ -172,48 +168,16 @@
         }
     }
 
-    private static List<InputMethodSubtype> getAllSubtypesOf(final InputMethodInfo imi) {
-        final int subtypeCount = imi.getSubtypeCount();
-        final List<InputMethodSubtype> allSubtypes = new ArrayList<>(subtypeCount);
-        for (int index = 0; index < subtypeCount; index++) {
-            allSubtypes.add(imi.getSubtypeAt(index));
-        }
-        return allSubtypes;
-    }
-
-    static List<SearchIndexableRaw> buildSearchIndexOfInputMethods(final Context context,
-            final List<InputMethodInfo> inputMethods, final String screenTitle) {
-        final List<SearchIndexableRaw> indexes = new ArrayList<>();
-        for (int i = 0; i < inputMethods.size(); i++) {
-            final InputMethodInfo imi = inputMethods.get(i);
-            final ServiceInfo serviceInfo = imi.getServiceInfo();
-            final SearchIndexableRaw index = new SearchIndexableRaw(context);
-            index.key = new ComponentName(serviceInfo.packageName, serviceInfo.name)
-                    .flattenToString();
-            index.title = imi.loadLabel(context.getPackageManager()).toString();
-            index.summaryOn = index.summaryOff = InputMethodAndSubtypeUtil
-                    .getSubtypeLocaleNameListAsSentence(getAllSubtypesOf(imi), context, imi);
-            index.screenTitle = screenTitle;
-            indexes.add(index);
-        }
-        return indexes;
-    }
-
-    public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
+    public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
             new BaseSearchIndexProvider() {
-        @Override
-        public List<SearchIndexableRaw> getRawDataToIndex(Context context, boolean enabled) {
-            final InputMethodManager imm = context.getSystemService(InputMethodManager.class);
-            final List<InputMethodInfo> enabledInputMethods = imm.getEnabledInputMethodList();
-            final List<InputMethodInfo> disabledInputMethods = new ArrayList<>();
-            for (final InputMethodInfo imi : imm.getInputMethodList()) {
-                if (!enabledInputMethods.contains(imi)) {
-                    disabledInputMethods.add(imi);
+                @Override
+                public List<SearchIndexableResource> getXmlResourcesToIndex(Context context,
+                        boolean enabled) {
+                    List<SearchIndexableResource> res = new ArrayList<>();
+                    SearchIndexableResource index = new SearchIndexableResource(context);
+                    index.xmlResId = R.xml.available_virtual_keyboard;
+                    res.add(index);
+                    return res;
                 }
-            }
-            final String screenTitle = context.getString(
-                    R.string.available_virtual_keyboard_category);
-            return buildSearchIndexOfInputMethods(context, disabledInputMethods, screenTitle);
-        }
-    };
+            };
 }
diff --git a/src/com/android/settings/inputmethod/PhysicalKeyboardFragment.java b/src/com/android/settings/inputmethod/PhysicalKeyboardFragment.java
index 42eff15..83d501d 100644
--- a/src/com/android/settings/inputmethod/PhysicalKeyboardFragment.java
+++ b/src/com/android/settings/inputmethod/PhysicalKeyboardFragment.java
@@ -31,6 +31,7 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.UserHandle;
+import android.provider.SearchIndexableResource;
 import android.provider.Settings.Secure;
 import android.support.v14.preference.SwitchPreference;
 import android.support.v7.preference.Preference;
@@ -51,11 +52,11 @@
 import com.android.settings.SettingsPreferenceFragment;
 import com.android.settings.search.BaseSearchIndexProvider;
 import com.android.settings.search.Indexable;
-import com.android.settings.search.SearchIndexableRaw;
 import com.android.settingslib.inputmethod.InputMethodAndSubtypeUtil;
 
 import java.text.Collator;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -288,6 +289,7 @@
         final PhysicalKeyboardFragment mPhysicalKeyboardFragment;
         @NonNull
         final List<HardKeyboardDeviceInfo> mHardKeyboards;
+
         public Callbacks(
                 @NonNull Context context,
                 @NonNull PhysicalKeyboardFragment physicalKeyboardFragment,
@@ -532,43 +534,14 @@
         }
     }
 
-    public static List<InputDevice> getPhysicalFullKeyboards() {
-        List<InputDevice> keyboards = null;
-        for (final int deviceId : InputDevice.getDeviceIds()) {
-            final InputDevice device = InputDevice.getDevice(deviceId);
-            if (device != null && !device.isVirtual() && device.isFullKeyboard()) {
-                if (keyboards == null) keyboards = new ArrayList<>();
-                keyboards.add(device);
-            }
-        }
-        return (keyboards == null) ? Collections.emptyList() : keyboards;
-    }
-
     public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
             new BaseSearchIndexProvider() {
-        @Override
-        public List<SearchIndexableRaw> getRawDataToIndex(Context context, boolean enabled) {
-            final InputManager inputManager = (InputManager) context.getSystemService(
-                    Context.INPUT_SERVICE);
-            final String screenTitle = context.getString(R.string.physical_keyboard_title);
-            final List<SearchIndexableRaw> indexes = new ArrayList<>();
-            for (final InputDevice device : getPhysicalFullKeyboards()) {
-                final String keyboardLayoutDescriptor = inputManager
-                        .getCurrentKeyboardLayoutForInputDevice(device.getIdentifier());
-                final KeyboardLayout keyboardLayout = (keyboardLayoutDescriptor != null)
-                        ? inputManager.getKeyboardLayout(keyboardLayoutDescriptor) : null;
-                final String summary = (keyboardLayout != null)
-                        ? keyboardLayout.toString()
-                        : context.getString(R.string.keyboard_layout_default_label);
-                final SearchIndexableRaw index = new SearchIndexableRaw(context);
-                index.key = device.getName();
-                index.title = device.getName();
-                index.summaryOn = summary;
-                index.summaryOff = summary;
-                index.screenTitle = screenTitle;
-                indexes.add(index);
-            }
-            return indexes;
-        }
-    };
+                @Override
+                public List<SearchIndexableResource> getXmlResourcesToIndex(
+                        Context context, boolean enabled) {
+                    final SearchIndexableResource sir = new SearchIndexableResource(context);
+                    sir.xmlResId = R.xml.physical_keyboard_settings;
+                    return Arrays.asList(sir);
+                }
+            };
 }
diff --git a/src/com/android/settings/inputmethod/VirtualKeyboardFragment.java b/src/com/android/settings/inputmethod/VirtualKeyboardFragment.java
index 7b7c599..fbe9690 100644
--- a/src/com/android/settings/inputmethod/VirtualKeyboardFragment.java
+++ b/src/com/android/settings/inputmethod/VirtualKeyboardFragment.java
@@ -23,6 +23,7 @@
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
+import android.provider.SearchIndexableResource;
 import android.support.v7.preference.Preference;
 import android.view.inputmethod.InputMethodInfo;
 import android.view.inputmethod.InputMethodManager;
@@ -33,14 +34,12 @@
 import com.android.settings.SettingsPreferenceFragment;
 import com.android.settings.search.BaseSearchIndexProvider;
 import com.android.settings.search.Indexable;
-import com.android.settings.search.SearchIndexableRaw;
 import com.android.settingslib.inputmethod.InputMethodAndSubtypeUtil;
 import com.android.settingslib.inputmethod.InputMethodPreference;
 
 import java.text.Collator;
 import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
+import java.util.Arrays;
 import java.util.List;
 
 public final class VirtualKeyboardFragment extends SettingsPreferenceFragment implements Indexable {
@@ -121,13 +120,19 @@
 
     public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
             new BaseSearchIndexProvider() {
-        @Override
-        public List<SearchIndexableRaw> getRawDataToIndex(Context context, boolean enabled) {
-            final InputMethodManager imm = context.getSystemService(InputMethodManager.class);
-            final List<InputMethodInfo> enabledInputMethods = imm.getEnabledInputMethodList();
-            final String screenTitle = context.getString(R.string.virtual_keyboard_category);
-            return AvailableVirtualKeyboardFragment
-                    .buildSearchIndexOfInputMethods(context, enabledInputMethods, screenTitle);
-        }
-    };
+                @Override
+                public List<SearchIndexableResource> getXmlResourcesToIndex(
+                        Context context, boolean enabled) {
+                    final SearchIndexableResource sir = new SearchIndexableResource(context);
+                    sir.xmlResId = R.xml.virtual_keyboard_settings;
+                    return Arrays.asList(sir);
+                }
+
+                @Override
+                public List<String> getNonIndexableKeys(Context context) {
+                    final List<String> keys = super.getNonIndexableKeys(context);
+                    keys.add("add_virtual_keyboard_screen");
+                    return keys;
+                }
+            };
 }
diff --git a/src/com/android/settings/language/LanguageAndInputSettings.java b/src/com/android/settings/language/LanguageAndInputSettings.java
index fbcb02f..4affc6e 100644
--- a/src/com/android/settings/language/LanguageAndInputSettings.java
+++ b/src/com/android/settings/language/LanguageAndInputSettings.java
@@ -52,6 +52,7 @@
     private static final String TAG = "LangAndInputSettings";
 
     private static final String KEY_TEXT_TO_SPEECH = "tts_settings_summary";
+    private static final String KEY_PHYSICAL_KEYBOARD = "physical_keyboard_pref";
 
     @Override
     public int getMetricsCategory() {
@@ -174,6 +175,7 @@
                     List<String> keys = super.getNonIndexableKeys(context);
                     // Duplicates in summary and details pages.
                     keys.add(KEY_TEXT_TO_SPEECH);
+                    keys.add(KEY_PHYSICAL_KEYBOARD);
 
                     return keys;
                 }
diff --git a/src/com/android/settings/network/MobileNetworkPreferenceController.java b/src/com/android/settings/network/MobileNetworkPreferenceController.java
index d8b7745..6e2c2d5 100644
--- a/src/com/android/settings/network/MobileNetworkPreferenceController.java
+++ b/src/com/android/settings/network/MobileNetworkPreferenceController.java
@@ -33,7 +33,6 @@
 
 import static android.os.UserHandle.myUserId;
 import static android.os.UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS;
-import static com.android.settingslib.RestrictedLockUtils.hasBaseUserRestriction;
 
 public class MobileNetworkPreferenceController extends AbstractPreferenceController
         implements PreferenceControllerMixin, LifecycleObserver, OnResume, OnPause {
@@ -42,22 +41,30 @@
 
     private final boolean mIsSecondaryUser;
     private final TelephonyManager mTelephonyManager;
+    private final UserManager mUserManager;
     private Preference mPreference;
     @VisibleForTesting
     PhoneStateListener mPhoneStateListener;
 
     public MobileNetworkPreferenceController(Context context) {
         super(context);
-        UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
+        mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
         mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
-        mIsSecondaryUser = !userManager.isAdminUser();
+        mIsSecondaryUser = !mUserManager.isAdminUser();
     }
 
     @Override
     public boolean isAvailable() {
-        return !mIsSecondaryUser
-                && !Utils.isWifiOnly(mContext)
-                && !hasBaseUserRestriction(mContext, DISALLOW_CONFIG_MOBILE_NETWORKS, myUserId());
+        return !isUserRestricted() && !Utils.isWifiOnly(mContext);
+    }
+
+    public boolean isUserRestricted() {
+        final RestrictedLockUtilsWrapper wrapper = new RestrictedLockUtilsWrapper();
+        return mIsSecondaryUser ||
+                wrapper.hasBaseUserRestriction(
+                        mContext,
+                        DISALLOW_CONFIG_MOBILE_NETWORKS,
+                        myUserId());
     }
 
     @Override
diff --git a/src/com/android/settings/network/RestrictedLockUtilsWrapper.java b/src/com/android/settings/network/RestrictedLockUtilsWrapper.java
new file mode 100644
index 0000000..de5b808
--- /dev/null
+++ b/src/com/android/settings/network/RestrictedLockUtilsWrapper.java
@@ -0,0 +1,15 @@
+package com.android.settings.network;
+
+import android.content.Context;
+import com.android.settingslib.RestrictedLockUtils;
+
+/**
+ * Wrapper class needed to be able to test classes which use RestrictedLockUtils methods.
+ * Unfortunately there is no way to deal with this until robolectric is updated due to the fact
+ * that it is a static method and it uses new API's.
+ */
+public class RestrictedLockUtilsWrapper {
+    public boolean hasBaseUserRestriction(Context context, String userRestriction, int userId) {
+        return RestrictedLockUtils.hasBaseUserRestriction(context, userRestriction, userId);
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/settings/nfc/NfcEnabler.java b/src/com/android/settings/nfc/NfcEnabler.java
index 5bda1ad..14ee779 100644
--- a/src/com/android/settings/nfc/NfcEnabler.java
+++ b/src/com/android/settings/nfc/NfcEnabler.java
@@ -98,6 +98,7 @@
         // Turn NFC on/off
 
         final boolean desiredState = (Boolean) value;
+        mSwitch.setChecked(desiredState);
         mSwitch.setEnabled(false);
 
         if (desiredState) {
diff --git a/src/com/android/settings/notification/ChannelNotificationSettings.java b/src/com/android/settings/notification/ChannelNotificationSettings.java
index fa6c8ee..2f95dd2 100644
--- a/src/com/android/settings/notification/ChannelNotificationSettings.java
+++ b/src/com/android/settings/notification/ChannelNotificationSettings.java
@@ -351,7 +351,9 @@
         if (mRingtone != null) {
             mRingtone.onActivityResult(requestCode, resultCode, data);
         }
-        mImportance.setSummary(getImportanceSummary(mChannel.getImportance()));
+        if (mChannel != null) {
+            mImportance.setSummary(getImportanceSummary(mChannel.getImportance()));
+        }
     }
 
     boolean canPulseLight() {
diff --git a/src/com/android/settings/search/DynamicIndexableContentMonitor.java b/src/com/android/settings/search/DynamicIndexableContentMonitor.java
deleted file mode 100644
index a0e3c02..0000000
--- a/src/com/android/settings/search/DynamicIndexableContentMonitor.java
+++ /dev/null
@@ -1,415 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.search;
-
-import android.accessibilityservice.AccessibilityService;
-import android.app.Activity;
-import android.app.LoaderManager;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.Loader;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.content.pm.ServiceInfo;
-import android.database.ContentObserver;
-import android.hardware.input.InputManager;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.print.PrintManager;
-import android.print.PrintServicesLoader;
-import android.printservice.PrintServiceInfo;
-import android.provider.Settings;
-import android.provider.UserDictionary;
-import android.support.annotation.Nullable;
-import android.support.annotation.VisibleForTesting;
-import android.util.Log;
-import android.view.inputmethod.InputMethod;
-import android.view.inputmethod.InputMethodInfo;
-import android.view.inputmethod.InputMethodManager;
-
-import com.android.internal.content.PackageMonitor;
-import com.android.settings.inputmethod.AvailableVirtualKeyboardFragment;
-import com.android.settings.inputmethod.PhysicalKeyboardFragment;
-import com.android.settings.inputmethod.VirtualKeyboardFragment;
-import com.android.settings.language.LanguageAndInputSettings;
-import com.android.settings.overlay.FeatureFactory;
-import com.android.settings.print.PrintSettingsFragment;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public final class DynamicIndexableContentMonitor implements
-        LoaderManager.LoaderCallbacks<List<PrintServiceInfo>> {
-    // Shorten the class name because log TAG can be at most 23 chars.
-    private static final String TAG = "DynamicContentMonitor";
-
-    private static final long DELAY_PROCESS_PACKAGE_CHANGE = 2000;
-    // A PackageMonitor shared among Settings activities.
-    private static final PackageChangeMonitor PACKAGE_CHANGE_MONITOR = new PackageChangeMonitor();
-
-    // Null if not initialized.
-    @Nullable private DatabaseIndexingManager mIndexManager;
-    private Context mContext;
-    private boolean mHasFeaturePrinting;
-
-    @VisibleForTesting
-    static Intent getAccessibilityServiceIntent(String packageName) {
-        final Intent intent = new Intent(AccessibilityService.SERVICE_INTERFACE);
-        intent.setPackage(packageName);
-        return intent;
-    }
-
-    @VisibleForTesting
-    static Intent getIMEServiceIntent(String packageName) {
-        final Intent intent = new Intent(InputMethod.SERVICE_INTERFACE);
-        intent.setPackage(packageName);
-        return intent;
-    }
-
-    @VisibleForTesting
-    static void resetForTesting() {
-        InputDevicesMonitor.getInstance().resetForTesting();
-        InputMethodServicesMonitor.getInstance().resetForTesting();
-    }
-
-    /**
-     * This instance holds a set of content monitor singleton objects.
-     *
-     * This object is created every time a sub-settings that extends {@code SettingsActivity}
-     * is created.
-     */
-    public DynamicIndexableContentMonitor() {}
-
-    /**
-     * Creates and initializes a set of content monitor singleton objects if not yet exist.
-     * Also starts loading the list of print services.
-     * <code>mIndex</code> has non-null value after successfully initialized.
-     *
-     * @param activity used to get {@link LoaderManager}.
-     * @param loaderId id for loading print services.
-     */
-    public void register(Activity activity, int loaderId) {
-        final boolean isUserUnlocked = activity
-                .getSystemService(UserManager.class)
-                .isUserUnlocked();
-        register(activity, loaderId, FeatureFactory.getFactory(activity)
-                        .getSearchFeatureProvider().getIndexingManager(activity), isUserUnlocked);
-    }
-
-    /**
-     * For testing to inject {@link DatabaseIndexingManager} object.
-     * Also because currently Robolectric doesn't support API 24, we can not test code that calls
-     * {@link UserManager#isUserUnlocked()}.
-     */
-    @VisibleForTesting
-    void register(Activity activity, int loaderId, DatabaseIndexingManager indexManager,
-                  boolean isUserUnlocked) {
-        if (!isUserUnlocked) {
-            Log.w(TAG, "Skipping content monitoring because user is locked");
-            return;
-        }
-        final Context context = activity.getApplicationContext();
-        mContext = context;
-        mIndexManager = indexManager;
-
-        PACKAGE_CHANGE_MONITOR.registerMonitor(context);
-        mHasFeaturePrinting = context.getPackageManager()
-                .hasSystemFeature(PackageManager.FEATURE_PRINTING);
-        if (mHasFeaturePrinting) {
-            activity.getLoaderManager().initLoader(loaderId, null /* args */, this /* callbacks */);
-        }
-
-        // Watch for input device changes.
-        InputDevicesMonitor.getInstance().initialize(context, mIndexManager);
-
-        // Start tracking packages.
-        InputMethodServicesMonitor.getInstance().initialize(context, mIndexManager);
-    }
-
-    /**
-     * Aborts loading the list of print services.
-     * Note that a set of content monitor singletons keep alive while Settings app is running.
-     *
-     * @param activity user to get {@link LoaderManager}.
-     * @param loaderId id for loading print services.
-     */
-    public void unregister(Activity activity, int loaderId) {
-        if (mIndexManager == null) return;
-
-        PACKAGE_CHANGE_MONITOR.unregisterMonitor();
-        if (mHasFeaturePrinting) {
-            activity.getLoaderManager().destroyLoader(loaderId);
-        }
-    }
-
-    @Override
-    public Loader<List<PrintServiceInfo>> onCreateLoader(int id, Bundle args) {
-        return new PrintServicesLoader(
-                (PrintManager) mContext.getSystemService(Context.PRINT_SERVICE), mContext,
-                PrintManager.ALL_SERVICES);
-    }
-
-    @Override
-    public void onLoadFinished(Loader<List<PrintServiceInfo>> loader,
-            List<PrintServiceInfo> services) {
-        mIndexManager.updateFromClassNameResource(PrintSettingsFragment.class.getName(),
-                true /* includeInSearchResults */);
-    }
-
-    @Override
-    public void onLoaderReset(Loader<List<PrintServiceInfo>> loader) {
-        // nothing to do
-    }
-
-    // A singleton that monitors input devices changes and updates indexes of physical keyboards.
-    private static class InputDevicesMonitor implements InputManager.InputDeviceListener {
-
-        // Null if not initialized.
-        @Nullable private DatabaseIndexingManager mIndexManager;
-        private InputManager mInputManager;
-
-        private InputDevicesMonitor() {}
-
-        private static class SingletonHolder {
-            private static final InputDevicesMonitor INSTANCE = new InputDevicesMonitor();
-        }
-
-        static InputDevicesMonitor getInstance() {
-            return SingletonHolder.INSTANCE;
-        }
-
-        @VisibleForTesting
-        synchronized void resetForTesting() {
-            if (mIndexManager != null) {
-                mInputManager.unregisterInputDeviceListener(this /* listener */);
-            }
-            mIndexManager = null;
-        }
-
-        synchronized void initialize(Context context, DatabaseIndexingManager indexManager) {
-            if (mIndexManager != null) return;
-            mIndexManager = indexManager;
-            mInputManager = (InputManager) context.getSystemService(Context.INPUT_SERVICE);
-            buildIndex();
-
-            // Watch for input device changes.
-            mInputManager.registerInputDeviceListener(this /* listener */, null /* handler */);
-        }
-
-        private void buildIndex() {
-            mIndexManager.updateFromClassNameResource(PhysicalKeyboardFragment.class.getName(),
-                    true /* includeInSearchResults */);
-        }
-
-        @Override
-        public void onInputDeviceAdded(int deviceId) {
-            buildIndex();
-        }
-
-        @Override
-        public void onInputDeviceRemoved(int deviceId) {
-            buildIndex();
-        }
-
-        @Override
-        public void onInputDeviceChanged(int deviceId) {
-            buildIndex();
-        }
-    }
-
-    // A singleton that monitors package installing, uninstalling, enabling, and disabling.
-    // Then updates indexes of accessibility services and input methods.
-    private static class PackageChangeMonitor extends PackageMonitor {
-        private static final String TAG = PackageChangeMonitor.class.getSimpleName();
-
-        // Null if not initialized. Guarded by {@link #mLock}.
-        @Nullable private PackageManager mPackageManager;
-        private final Object mLock = new Object();
-
-        public void registerMonitor(Context context) {
-            synchronized (mLock) {
-                if (mPackageManager != null) {
-                    return;
-                }
-                mPackageManager = context.getPackageManager();
-
-                // Start tracking packages. Use background thread for monitoring. Note that no need
-                // to unregister this monitor. This should be alive while Settings app is running.
-                super.register(context, null /* thread */, UserHandle.CURRENT, false);
-            }
-        }
-
-        public void unregisterMonitor() {
-            synchronized (mLock) {
-                if (mPackageManager == null) {
-                    return;
-                }
-                super.unregister();
-                mPackageManager = null;
-            }
-        }
-
-        // Covers installed, appeared external storage with the package, upgraded.
-        @Override
-        public void onPackageAppeared(String packageName, int reason) {
-            postPackageAvailable(packageName);
-        }
-
-        // Covers uninstalled, removed external storage with the package.
-        @Override
-        public void onPackageDisappeared(String packageName, int reason) {
-            postPackageUnavailable(packageName);
-        }
-
-        // Covers enabled, disabled.
-        @Override
-        public void onPackageModified(String packageName) {
-            try {
-                final int state = mPackageManager.getApplicationEnabledSetting(packageName);
-                if (state == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
-                        || state == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
-                    postPackageAvailable(packageName);
-                } else {
-                    postPackageUnavailable(packageName);
-                }
-            } catch (IllegalArgumentException e) {
-                Log.e(TAG, "Package does not exist: " + packageName, e);
-            }
-        }
-
-        private void postPackageAvailable(final String packageName) {
-            getRegisteredHandler().postDelayed(() -> {
-                InputMethodServicesMonitor.getInstance().onPackageAvailable(packageName);
-            }, DELAY_PROCESS_PACKAGE_CHANGE);
-        }
-
-        private void postPackageUnavailable(final String packageName) {
-            getRegisteredHandler().postDelayed(() -> {
-                InputMethodServicesMonitor.getInstance().onPackageUnavailable(packageName);
-            }, DELAY_PROCESS_PACKAGE_CHANGE);
-        }
-    }
-
-    // A singleton that holds list of available input methods and updates search index.
-    // Also it monitors user dictionary changes and updates search index.
-    private static class InputMethodServicesMonitor extends ContentObserver {
-
-        private static final Uri ENABLED_INPUT_METHODS_CONTENT_URI =
-                Settings.Secure.getUriFor(Settings.Secure.ENABLED_INPUT_METHODS);
-
-        // Null if not initialized.
-        @Nullable private DatabaseIndexingManager mIndexManager;
-        private PackageManager mPackageManager;
-        private ContentResolver mContentResolver;
-        private final List<String> mInputMethodServices = new ArrayList<>();
-
-        private InputMethodServicesMonitor() {
-            // No need for handler because {@link #onChange(boolean,Uri)} is short and quick.
-            super(null /* handler */);
-        }
-
-        private static class SingletonHolder {
-            private static final InputMethodServicesMonitor INSTANCE =
-                    new InputMethodServicesMonitor();
-        }
-
-        static InputMethodServicesMonitor getInstance() {
-            return SingletonHolder.INSTANCE;
-        }
-
-        @VisibleForTesting
-        synchronized void resetForTesting() {
-            if (mIndexManager != null) {
-                mContentResolver.unregisterContentObserver(this /* observer */);
-            }
-            mIndexManager = null;
-        }
-
-        synchronized void initialize(Context context, DatabaseIndexingManager indexManager) {
-            final boolean hasFeatureIme = context.getPackageManager()
-                    .hasSystemFeature(PackageManager.FEATURE_INPUT_METHODS);
-            if (!hasFeatureIme) return;
-
-            if (mIndexManager != null) return;
-            mIndexManager = indexManager;
-            mPackageManager = context.getPackageManager();
-            mContentResolver = context.getContentResolver();
-            mInputMethodServices.clear();
-            // Build index of {@link UserDictionary}.
-            buildIndex(LanguageAndInputSettings.class);
-            // Build index of IMEs.
-            buildIndex(VirtualKeyboardFragment.class);
-            buildIndex(AvailableVirtualKeyboardFragment.class);
-
-            // Cache IME service packages to know when they go away.
-            final InputMethodManager inputMethodManager = (InputMethodManager) context
-                    .getSystemService(Context.INPUT_METHOD_SERVICE);
-            for (final InputMethodInfo inputMethod : inputMethodManager.getInputMethodList()) {
-                ServiceInfo serviceInfo = inputMethod.getServiceInfo();
-                if (serviceInfo != null) {
-                    mInputMethodServices.add(serviceInfo.packageName);
-                }
-            }
-
-            // TODO: Implements by JobScheduler with TriggerContentUri parameters.
-            // Watch for related content URIs.
-            mContentResolver.registerContentObserver(UserDictionary.Words.CONTENT_URI,
-                    true /* notifyForDescendants */, this /* observer */);
-            // Watch for changing enabled IMEs.
-            mContentResolver.registerContentObserver(ENABLED_INPUT_METHODS_CONTENT_URI,
-                    false /* notifyForDescendants */, this /* observer */);
-        }
-
-        private void buildIndex(Class<?> indexClass) {
-            mIndexManager.updateFromClassNameResource(indexClass.getName(),
-                    true /* includeInSearchResults */);
-        }
-
-        synchronized void onPackageAvailable(String packageName) {
-            if (mIndexManager == null) return;
-            if (mInputMethodServices.contains(packageName)) return;
-
-            final Intent intent = getIMEServiceIntent(packageName);
-            final List<ResolveInfo> services = mPackageManager
-                    .queryIntentServices(intent, 0 /* flags */);
-            if (services == null || services.isEmpty()) return;
-            mInputMethodServices.add(packageName);
-            buildIndex(VirtualKeyboardFragment.class);
-            buildIndex(AvailableVirtualKeyboardFragment.class);
-        }
-
-        synchronized void onPackageUnavailable(String packageName) {
-            if (mIndexManager == null) return;
-            if (!mInputMethodServices.remove(packageName)) return;
-            buildIndex(VirtualKeyboardFragment.class);
-            buildIndex(AvailableVirtualKeyboardFragment.class);
-        }
-
-        @Override
-        public void onChange(boolean selfChange, Uri uri) {
-            if (ENABLED_INPUT_METHODS_CONTENT_URI.equals(uri)) {
-                buildIndex(VirtualKeyboardFragment.class);
-                buildIndex(AvailableVirtualKeyboardFragment.class);
-            } else if (UserDictionary.Words.CONTENT_URI.equals(uri)) {
-                buildIndex(LanguageAndInputSettings.class);
-            }
-        }
-    }
-}
diff --git a/src/com/android/settings/search/InputDeviceResultLoader.java b/src/com/android/settings/search/InputDeviceResultLoader.java
new file mode 100644
index 0000000..61e1ad1
--- /dev/null
+++ b/src/com/android/settings/search/InputDeviceResultLoader.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.search;
+
+import static android.content.Context.INPUT_METHOD_SERVICE;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ServiceInfo;
+import android.hardware.input.InputManager;
+import android.hardware.input.KeyboardLayout;
+import android.support.annotation.VisibleForTesting;
+import android.view.InputDevice;
+import android.view.inputmethod.InputMethodInfo;
+import android.view.inputmethod.InputMethodManager;
+import android.view.inputmethod.InputMethodSubtype;
+
+import com.android.settings.R;
+import com.android.settings.dashboard.SiteMapManager;
+import com.android.settings.inputmethod.AvailableVirtualKeyboardFragment;
+import com.android.settings.inputmethod.PhysicalKeyboardFragment;
+import com.android.settings.utils.AsyncLoader;
+import com.android.settingslib.inputmethod.InputMethodAndSubtypeUtil;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Search result for input devices (physical/virtual keyboard, game controllers, etc)
+ */
+public class InputDeviceResultLoader extends AsyncLoader<Set<? extends SearchResult>> {
+    private static final int NAME_NO_MATCH = -1;
+
+    @VisibleForTesting
+    static final String PHYSICAL_KEYBOARD_FRAGMENT = PhysicalKeyboardFragment.class.getName();
+    @VisibleForTesting
+    static final String VIRTUAL_KEYBOARD_FRAGMENT =
+            AvailableVirtualKeyboardFragment.class.getName();
+
+    private final SiteMapManager mSiteMapManager;
+    private final InputManager mInputManager;
+    private final InputMethodManager mImm;
+    private final PackageManager mPackageManager;
+    @VisibleForTesting
+    final String mQuery;
+
+    private List<String> mPhysicalKeyboardBreadcrumb;
+    private List<String> mVirtualKeyboardBreadcrumb;
+
+    public InputDeviceResultLoader(Context context, String query, SiteMapManager mapManager) {
+        super(context);
+        mQuery = query;
+        mSiteMapManager = mapManager;
+        mInputManager = (InputManager) context.getSystemService(Context.INPUT_SERVICE);
+        mImm = (InputMethodManager) context.getSystemService(INPUT_METHOD_SERVICE);
+        mPackageManager = context.getPackageManager();
+    }
+
+    @Override
+    protected void onDiscardResult(Set<? extends SearchResult> result) {
+    }
+
+    @Override
+    public Set<? extends SearchResult> loadInBackground() {
+        final Set<SearchResult> results = new HashSet<>();
+        results.addAll(buildPhysicalKeyboardSearchResults());
+        results.addAll(buildVirtualKeyboardSearchResults());
+        return results;
+    }
+
+    private Set<SearchResult> buildPhysicalKeyboardSearchResults() {
+        final Set<SearchResult> results = new HashSet<>();
+        final Context context = getContext();
+        final String screenTitle = context.getString(R.string.physical_keyboard_title);
+
+        for (final InputDevice device : getPhysicalFullKeyboards()) {
+            final String deviceName = device.getName();
+            final int wordDiff = InstalledAppResultLoader.getWordDifference(deviceName, mQuery);
+            if (wordDiff == NAME_NO_MATCH) {
+                continue;
+            }
+            final String keyboardLayoutDescriptor = mInputManager
+                    .getCurrentKeyboardLayoutForInputDevice(device.getIdentifier());
+            final KeyboardLayout keyboardLayout = (keyboardLayoutDescriptor != null)
+                    ? mInputManager.getKeyboardLayout(keyboardLayoutDescriptor) : null;
+            final String summary = (keyboardLayout != null)
+                    ? keyboardLayout.toString()
+                    : context.getString(R.string.keyboard_layout_default_label);
+            final String key = deviceName;
+
+            final Intent intent = DatabaseIndexingUtils.buildSubsettingIntent(context,
+                    PHYSICAL_KEYBOARD_FRAGMENT, key, screenTitle);
+            results.add(new SearchResult.Builder()
+                    .setTitle(deviceName)
+                    .setPayload(new ResultPayload(intent))
+                    .setStableId(Objects.hash(PHYSICAL_KEYBOARD_FRAGMENT, key))
+                    .setSummary(summary)
+                    .setRank(wordDiff)
+                    .addBreadcrumbs(getPhysicalKeyboardBreadCrumb())
+                    .build());
+        }
+        return results;
+    }
+
+    private Set<SearchResult> buildVirtualKeyboardSearchResults() {
+        final Set<SearchResult> results = new HashSet<>();
+        final Context context = getContext();
+        final String screenTitle = context.getString(R.string.add_virtual_keyboard);
+        final List<InputMethodInfo> inputMethods = mImm.getInputMethodList();
+        for (InputMethodInfo info : inputMethods) {
+            final String title = info.loadLabel(mPackageManager).toString();
+            final String summary = InputMethodAndSubtypeUtil
+                    .getSubtypeLocaleNameListAsSentence(getAllSubtypesOf(info), context, info);
+            int wordDiff = InstalledAppResultLoader.getWordDifference(title, mQuery);
+            if (wordDiff == NAME_NO_MATCH) {
+                wordDiff = InstalledAppResultLoader.getWordDifference(summary, mQuery);
+            }
+            if (wordDiff == NAME_NO_MATCH) {
+                continue;
+            }
+            final ServiceInfo serviceInfo = info.getServiceInfo();
+            final String key = new ComponentName(serviceInfo.packageName, serviceInfo.name)
+                    .flattenToString();
+            final Intent intent = DatabaseIndexingUtils.buildSubsettingIntent(context,
+                    VIRTUAL_KEYBOARD_FRAGMENT, key, screenTitle);
+            results.add(new SearchResult.Builder()
+                    .setTitle(title)
+                    .setSummary(summary)
+                    .setRank(wordDiff)
+                    .setStableId(Objects.hash(VIRTUAL_KEYBOARD_FRAGMENT, key))
+                    .addBreadcrumbs(getVirtualKeyboardBreadCrumb())
+                    .setPayload(new ResultPayload(intent))
+                    .build());
+        }
+        return results;
+    }
+
+    private List<String> getPhysicalKeyboardBreadCrumb() {
+        if (mPhysicalKeyboardBreadcrumb == null || mPhysicalKeyboardBreadcrumb.isEmpty()) {
+            final Context context = getContext();
+            mPhysicalKeyboardBreadcrumb = mSiteMapManager.buildBreadCrumb(
+                    context, PHYSICAL_KEYBOARD_FRAGMENT,
+                    context.getString(R.string.physical_keyboard_title));
+        }
+        return mPhysicalKeyboardBreadcrumb;
+    }
+
+
+    private List<String> getVirtualKeyboardBreadCrumb() {
+        if (mVirtualKeyboardBreadcrumb == null || mVirtualKeyboardBreadcrumb.isEmpty()) {
+            final Context context = getContext();
+            mVirtualKeyboardBreadcrumb = mSiteMapManager.buildBreadCrumb(
+                    context, VIRTUAL_KEYBOARD_FRAGMENT,
+                    context.getString(R.string.add_virtual_keyboard));
+        }
+        return mVirtualKeyboardBreadcrumb;
+    }
+
+    private List<InputDevice> getPhysicalFullKeyboards() {
+        final List<InputDevice> keyboards = new ArrayList<>();
+        final int[] deviceIds = InputDevice.getDeviceIds();
+        if (deviceIds != null) {
+            for (int deviceId : deviceIds) {
+                final InputDevice device = InputDevice.getDevice(deviceId);
+                if (device != null && !device.isVirtual() && device.isFullKeyboard()) {
+                    keyboards.add(device);
+                }
+            }
+        }
+        return keyboards;
+    }
+
+    private static List<InputMethodSubtype> getAllSubtypesOf(final InputMethodInfo imi) {
+        final int subtypeCount = imi.getSubtypeCount();
+        final List<InputMethodSubtype> allSubtypes = new ArrayList<>(subtypeCount);
+        for (int index = 0; index < subtypeCount; index++) {
+            allSubtypes.add(imi.getSubtypeAt(index));
+        }
+        return allSubtypes;
+    }
+}
diff --git a/src/com/android/settings/search/SearchFeatureProvider.java b/src/com/android/settings/search/SearchFeatureProvider.java
index 7e0e086..900cefc 100644
--- a/src/com/android/settings/search/SearchFeatureProvider.java
+++ b/src/com/android/settings/search/SearchFeatureProvider.java
@@ -49,6 +49,11 @@
             String query);
 
     /**
+     * Returns a new loader to search input devices.
+     */
+    InputDeviceResultLoader getInputDeviceResultLoader(Context context, String query);
+
+    /**
      * Returns a new loader to get all recently saved queries search terms.
      */
     SavedQueryLoader getSavedQueryLoader(Context context);
diff --git a/src/com/android/settings/search/SearchFeatureProviderImpl.java b/src/com/android/settings/search/SearchFeatureProviderImpl.java
index b90547e..400cf8f 100644
--- a/src/com/android/settings/search/SearchFeatureProviderImpl.java
+++ b/src/com/android/settings/search/SearchFeatureProviderImpl.java
@@ -62,6 +62,11 @@
     }
 
     @Override
+    public InputDeviceResultLoader getInputDeviceResultLoader(Context context, String query) {
+        return new InputDeviceResultLoader(context, cleanQuery(query), getSiteMapManager());
+    }
+
+    @Override
     public SavedQueryLoader getSavedQueryLoader(Context context) {
         return new SavedQueryLoader(context);
     }
diff --git a/src/com/android/settings/search/SearchFragment.java b/src/com/android/settings/search/SearchFragment.java
index fb89d6e..ccc4d61 100644
--- a/src/com/android/settings/search/SearchFragment.java
+++ b/src/com/android/settings/search/SearchFragment.java
@@ -84,8 +84,10 @@
     static final int LOADER_ID_INSTALLED_APPS = 2;
     @VisibleForTesting
     static final int LOADER_ID_ACCESSIBILITY_SERVICES = 3;
+    @VisibleForTesting
+    static final int LOADER_ID_INPUT_DEVICES = 4;
 
-    private static final int NUM_QUERY_LOADERS = 3;
+    private static final int NUM_QUERY_LOADERS = 4;
 
     @VisibleForTesting
     AtomicInteger mUnfinishedLoadersCount = new AtomicInteger(NUM_QUERY_LOADERS);
@@ -284,6 +286,7 @@
             loaderManager.destroyLoader(LOADER_ID_DATABASE);
             loaderManager.destroyLoader(LOADER_ID_INSTALLED_APPS);
             loaderManager.destroyLoader(LOADER_ID_ACCESSIBILITY_SERVICES);
+            loaderManager.destroyLoader(LOADER_ID_INPUT_DEVICES);
             mShowingSavedQuery = true;
             mSavedQueryController.loadSavedQueries();
             mSearchFeatureProvider.hideFeedbackButton();
@@ -314,6 +317,8 @@
                 return mSearchFeatureProvider.getInstalledAppSearchLoader(activity, mQuery);
             case LOADER_ID_ACCESSIBILITY_SERVICES:
                 return mSearchFeatureProvider.getAccessibilityServiceResultLoader(activity, mQuery);
+            case LOADER_ID_INPUT_DEVICES:
+                return mSearchFeatureProvider.getInputDeviceResultLoader(activity, mQuery);
             default:
                 return null;
         }
@@ -351,6 +356,8 @@
                     LOADER_ID_INSTALLED_APPS, null /* args */, this /* callback */);
             loaderManager.initLoader(
                     LOADER_ID_ACCESSIBILITY_SERVICES, null /* args */, this /* callback */);
+            loaderManager.initLoader(
+                    LOADER_ID_INPUT_DEVICES, null /* args */, this /* callback */);
         }
 
         requery();
@@ -392,6 +399,8 @@
         loaderManager.restartLoader(LOADER_ID_INSTALLED_APPS, null /* args */, this /* callback */);
         loaderManager.restartLoader(LOADER_ID_ACCESSIBILITY_SERVICES, null /* args */,
                 this /* callback */);
+        loaderManager.restartLoader(LOADER_ID_INPUT_DEVICES, null /* args */,
+                this /* callback */);
     }
 
     public String getQuery() {
diff --git a/src/com/android/settings/search/SearchResultsAdapter.java b/src/com/android/settings/search/SearchResultsAdapter.java
index 6c58a0d..7b886a4 100644
--- a/src/com/android/settings/search/SearchResultsAdapter.java
+++ b/src/com/android/settings/search/SearchResultsAdapter.java
@@ -57,7 +57,9 @@
     @VisibleForTesting
     static final String APP_RESULTS_LOADER_KEY = InstalledAppResultLoader.class.getName();
     @VisibleForTesting
-    static final String ACCESSIBLITY_LOADER_KEY = AccessibilityServiceResultLoader.class.getName();
+    static final String ACCESSIBILITY_LOADER_KEY = AccessibilityServiceResultLoader.class.getName();
+    @VisibleForTesting
+    static final String INPUT_DEVICE_LOADER_KEY = InputDeviceResultLoader.class.getName();
 
     @VisibleForTesting
     static final int MSG_RANKING_TIMED_OUT = 1;
@@ -265,17 +267,21 @@
         List<? extends SearchResult> installedAppResults =
                 getSortedLoadedResults(APP_RESULTS_LOADER_KEY);
         List<? extends SearchResult> accessibilityResults =
-                getSortedLoadedResults(ACCESSIBLITY_LOADER_KEY);
+                getSortedLoadedResults(ACCESSIBILITY_LOADER_KEY);
+        List<? extends SearchResult> inputDeviceResults =
+                getSortedLoadedResults(INPUT_DEVICE_LOADER_KEY);
 
         int dbSize = databaseResults.size();
         int appSize = installedAppResults.size();
         int a11ySize = accessibilityResults.size();
-
+        int inputDeviceSize = inputDeviceResults.size();
         int dbIndex = 0;
         int appIndex = 0;
         int a11yIndex = 0;
+        int inputDeviceIndex = 0;
         int rank = SearchResult.TOP_RANK;
 
+        // TODO: We need a helper method to do k-way merge.
         mStaticallyRankedSearchResults.clear();
         while (rank <= SearchResult.BOTTOM_RANK) {
             while ((dbIndex < dbSize) && (databaseResults.get(dbIndex).rank == rank)) {
@@ -287,6 +293,10 @@
             while ((a11yIndex < a11ySize) && (accessibilityResults.get(a11yIndex).rank == rank)) {
                 mStaticallyRankedSearchResults.add(accessibilityResults.get(a11yIndex++));
             }
+            while (inputDeviceIndex < inputDeviceSize
+                    && inputDeviceResults.get(inputDeviceIndex).rank == rank) {
+                mStaticallyRankedSearchResults.add(inputDeviceResults.get(inputDeviceIndex++));
+            }
             rank++;
         }
 
@@ -299,6 +309,9 @@
         while(a11yIndex < a11ySize) {
             mStaticallyRankedSearchResults.add(accessibilityResults.get(a11yIndex++));
         }
+        while (inputDeviceIndex < inputDeviceSize) {
+            mStaticallyRankedSearchResults.add(inputDeviceResults.get(inputDeviceIndex++));
+        }
     }
 
     private void updateSearchResults() {
@@ -332,12 +345,16 @@
         List<? extends SearchResult> installedAppResults =
                 getSortedLoadedResults(APP_RESULTS_LOADER_KEY);
         List<? extends SearchResult> accessibilityResults =
-                getSortedLoadedResults(ACCESSIBLITY_LOADER_KEY);
+                getSortedLoadedResults(ACCESSIBILITY_LOADER_KEY);
+        List<? extends SearchResult> inputDeviceResults =
+                getSortedLoadedResults(INPUT_DEVICE_LOADER_KEY);
         int dbSize = databaseResults.size();
         int appSize = installedAppResults.size();
         int a11ySize = accessibilityResults.size();
+        int inputDeviceSize = inputDeviceResults.size();
 
-        final List<SearchResult> asyncRankingResults = new ArrayList<>(dbSize + appSize + a11ySize);
+        final List<SearchResult> asyncRankingResults = new ArrayList<>(
+                dbSize + appSize + a11ySize + inputDeviceSize);
         TreeSet<SearchResult> dbResultsSortedByScores = new TreeSet<>(
                 new Comparator<SearchResult>() {
                     @Override
@@ -358,13 +375,13 @@
         // Other results are not ranked by async ranking and appended at the end of the list.
         asyncRankingResults.addAll(installedAppResults);
         asyncRankingResults.addAll(accessibilityResults);
+        asyncRankingResults.addAll(inputDeviceResults);
         return asyncRankingResults;
     }
 
     @VisibleForTesting
     Set<? extends SearchResult> getUnsortedLoadedResults(String loaderKey) {
-        return mResultsMap.containsKey(loaderKey) ?
-                mResultsMap.get(loaderKey) : new HashSet<SearchResult>();
+        return mResultsMap.containsKey(loaderKey) ? mResultsMap.get(loaderKey) : new HashSet<>();
     }
 
     @VisibleForTesting
diff --git a/tests/robotests/src/com/android/settings/SetupChooseLockPatternTest.java b/tests/robotests/src/com/android/settings/SetupChooseLockPatternTest.java
index 78bc7d1..69a1351 100644
--- a/tests/robotests/src/com/android/settings/SetupChooseLockPatternTest.java
+++ b/tests/robotests/src/com/android/settings/SetupChooseLockPatternTest.java
@@ -30,7 +30,6 @@
 import com.android.settings.password.SetupChooseLockPattern;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settings.testutils.shadow.SettingsShadowResources;
-import com.android.settings.testutils.shadow.ShadowDynamicIndexableContentMonitor;
 import com.android.settings.testutils.shadow.ShadowEventLogWriter;
 import com.android.settings.testutils.shadow.ShadowUtils;
 
@@ -49,7 +48,6 @@
         shadows = {
                 SettingsShadowResources.class,
                 SettingsShadowResources.SettingsShadowTheme.class,
-                ShadowDynamicIndexableContentMonitor.class,
                 ShadowEventLogWriter.class,
                 ShadowUtils.class
         })
diff --git a/tests/robotests/src/com/android/settings/applications/ManageApplicationsTest.java b/tests/robotests/src/com/android/settings/applications/ManageApplicationsTest.java
index 92aa675..8ed7284 100644
--- a/tests/robotests/src/com/android/settings/applications/ManageApplicationsTest.java
+++ b/tests/robotests/src/com/android/settings/applications/ManageApplicationsTest.java
@@ -16,6 +16,17 @@
 
 package com.android.settings.applications;
 
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
 import android.app.Activity;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
@@ -35,7 +46,6 @@
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settings.testutils.shadow.SettingsShadowResources;
 import com.android.settings.testutils.shadow.SettingsShadowResources.SettingsShadowTheme;
-import com.android.settings.testutils.shadow.ShadowDynamicIndexableContentMonitor;
 import com.android.settings.testutils.shadow.ShadowEventLogWriter;
 import com.android.settings.widget.LoadingViewController;
 import com.android.settingslib.applications.ApplicationsState;
@@ -53,17 +63,6 @@
 
 import java.util.ArrayList;
 
-import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
 /**
  * Tests for {@link ManageApplications}.
  */
@@ -74,7 +73,6 @@
         shadows = {
                 SettingsShadowResources.class,
                 SettingsShadowTheme.class,
-                ShadowDynamicIndexableContentMonitor.class,
                 ShadowEventLogWriter.class
         })
 public class ManageApplicationsTest {
diff --git a/tests/robotests/src/com/android/settings/dashboard/DashboardAdapterTest.java b/tests/robotests/src/com/android/settings/dashboard/DashboardAdapterTest.java
index 8a8a13d..6b56c29 100644
--- a/tests/robotests/src/com/android/settings/dashboard/DashboardAdapterTest.java
+++ b/tests/robotests/src/com/android/settings/dashboard/DashboardAdapterTest.java
@@ -15,6 +15,19 @@
  */
 package com.android.settings.dashboard;
 
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -38,7 +51,6 @@
 import com.android.settings.testutils.FakeFeatureFactory;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settings.testutils.shadow.SettingsShadowResources;
-import com.android.settings.testutils.shadow.ShadowDynamicIndexableContentMonitor;
 import com.android.settingslib.drawer.DashboardCategory;
 import com.android.settingslib.drawer.Tile;
 
@@ -56,26 +68,12 @@
 import java.util.ArrayList;
 import java.util.List;
 
-import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
 @RunWith(SettingsRobolectricTestRunner.class)
 @Config(manifest = TestConfig.MANIFEST_PATH,
         sdk = TestConfig.SDK_VERSION,
         shadows = {
                 SettingsShadowResources.class,
                 SettingsShadowResources.SettingsShadowTheme.class,
-                ShadowDynamicIndexableContentMonitor.class
         })
 public class DashboardAdapterTest {
 
diff --git a/tests/robotests/src/com/android/settings/fingerprint/FingerprintEnrollFindSensorTest.java b/tests/robotests/src/com/android/settings/fingerprint/FingerprintEnrollFindSensorTest.java
index 344914a..ad52bf6 100644
--- a/tests/robotests/src/com/android/settings/fingerprint/FingerprintEnrollFindSensorTest.java
+++ b/tests/robotests/src/com/android/settings/fingerprint/FingerprintEnrollFindSensorTest.java
@@ -36,7 +36,6 @@
 import com.android.settings.password.IFingerprintManager;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settings.testutils.shadow.SettingsShadowResources;
-import com.android.settings.testutils.shadow.ShadowDynamicIndexableContentMonitor;
 import com.android.settings.testutils.shadow.ShadowEventLogWriter;
 import com.android.settings.testutils.shadow.ShadowUtils;
 
@@ -61,7 +60,6 @@
         shadows = {
                 SettingsShadowResources.class,
                 SettingsShadowResources.SettingsShadowTheme.class,
-                ShadowDynamicIndexableContentMonitor.class,
                 ShadowEventLogWriter.class,
                 ShadowUtils.class
         })
diff --git a/tests/robotests/src/com/android/settings/fingerprint/SetupFingerprintEnrollFindSensorTest.java b/tests/robotests/src/com/android/settings/fingerprint/SetupFingerprintEnrollFindSensorTest.java
index f1b7ee0..3b67851 100644
--- a/tests/robotests/src/com/android/settings/fingerprint/SetupFingerprintEnrollFindSensorTest.java
+++ b/tests/robotests/src/com/android/settings/fingerprint/SetupFingerprintEnrollFindSensorTest.java
@@ -30,7 +30,6 @@
 import com.android.settings.password.IFingerprintManager;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settings.testutils.shadow.SettingsShadowResources;
-import com.android.settings.testutils.shadow.ShadowDynamicIndexableContentMonitor;
 import com.android.settings.testutils.shadow.ShadowEventLogWriter;
 import com.android.settings.testutils.shadow.ShadowUtils;
 
@@ -53,7 +52,6 @@
         shadows = {
                 SettingsShadowResources.class,
                 SettingsShadowResources.SettingsShadowTheme.class,
-                ShadowDynamicIndexableContentMonitor.class,
                 ShadowEventLogWriter.class,
                 ShadowUtils.class
         })
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryHeaderPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryHeaderPreferenceControllerTest.java
index 013d379..e434bb3 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/BatteryHeaderPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryHeaderPreferenceControllerTest.java
@@ -17,6 +17,15 @@
 
 package com.android.settings.fuelgauge;
 
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.nullable;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
 import android.app.Activity;
 import android.content.Context;
 import android.content.Intent;
@@ -27,11 +36,10 @@
 import android.widget.TextView;
 
 import com.android.settings.R;
-import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settings.TestConfig;
 import com.android.settings.applications.LayoutPreference;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settings.testutils.shadow.SettingsShadowResources;
-import com.android.settings.testutils.shadow.ShadowDynamicIndexableContentMonitor;
 import com.android.settings.testutils.shadow.ShadowEntityHeaderController;
 import com.android.settings.widget.EntityHeaderController;
 import com.android.settingslib.core.lifecycle.Lifecycle;
@@ -45,23 +53,12 @@
 import org.robolectric.RuntimeEnvironment;
 import org.robolectric.annotation.Config;
 
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.nullable;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
 @RunWith(SettingsRobolectricTestRunner.class)
 @Config(manifest = TestConfig.MANIFEST_PATH,
         sdk = TestConfig.SDK_VERSION,
         shadows = {
                 SettingsShadowResources.class,
                 SettingsShadowResources.SettingsShadowTheme.class,
-                ShadowDynamicIndexableContentMonitor.class,
                 ShadowEntityHeaderController.class
         })
 public class BatteryHeaderPreferenceControllerTest {
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryMeterViewTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryMeterViewTest.java
index e3a94d2..3a1fad0 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/BatteryMeterViewTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryMeterViewTest.java
@@ -16,15 +16,17 @@
 package com.android.settings.fuelgauge;
 
 import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
 
 import android.content.Context;
 import android.graphics.ColorFilter;
 
-import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settings.testutils.shadow.SettingsShadowResources;
 import com.android.settings.testutils.shadow.SettingsShadowResources.SettingsShadowTheme;
-import com.android.settings.testutils.shadow.ShadowDynamicIndexableContentMonitor;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -34,10 +36,6 @@
 import org.robolectric.RuntimeEnvironment;
 import org.robolectric.annotation.Config;
 
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
-
 @RunWith(SettingsRobolectricTestRunner.class)
 // TODO: Consider making the shadow class set global using a robolectric.properties file.
 @Config(manifest = TestConfig.MANIFEST_PATH,
@@ -45,7 +43,6 @@
         shadows = {
                 SettingsShadowResources.class,
                 SettingsShadowTheme.class,
-                ShadowDynamicIndexableContentMonitor.class
         })
 public class BatteryMeterViewTest {
     private static final int BATTERY_LEVEL = 100;
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageSummaryTest.java b/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageSummaryTest.java
index 83e630d..48a48b1 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageSummaryTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageSummaryTest.java
@@ -18,9 +18,7 @@
 import static com.android.settings.fuelgauge.PowerUsageSummary.MENU_ADDITIONAL_BATTERY_INFO;
 import static com.android.settings.fuelgauge.PowerUsageSummary.MENU_HIGH_POWER_APPS;
 import static com.android.settings.fuelgauge.PowerUsageSummary.MENU_TOGGLE_APPS;
-
 import static com.google.common.truth.Truth.assertThat;
-
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.anyLong;
@@ -34,8 +32,6 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import java.util.List;
-
 import android.app.LoaderManager;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -68,7 +64,6 @@
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settings.testutils.XmlTestUtils;
 import com.android.settings.testutils.shadow.SettingsShadowResources;
-import com.android.settings.testutils.shadow.ShadowDynamicIndexableContentMonitor;
 import com.android.settingslib.core.AbstractPreferenceController;
 
 import org.junit.Before;
@@ -95,7 +90,6 @@
         shadows = {
                 SettingsShadowResources.class,
                 SettingsShadowResources.SettingsShadowTheme.class,
-                ShadowDynamicIndexableContentMonitor.class
         })
 public class PowerUsageSummaryTest {
     private static final String[] PACKAGE_NAMES = {"com.app1", "com.app2"};
diff --git a/tests/robotests/src/com/android/settings/gestures/SwipeToNotificationPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/gestures/SwipeToNotificationPreferenceControllerTest.java
index 3b92ed1..d4fc014 100644
--- a/tests/robotests/src/com/android/settings/gestures/SwipeToNotificationPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/gestures/SwipeToNotificationPreferenceControllerTest.java
@@ -129,46 +129,6 @@
         assertThat(mController.isSwitchPrefEnabled()).isFalse();
     }
 
-    @Test
-    public void testPreferenceController_ProperResultPayloadType() {
-        when(mContext.getResources().
-                getBoolean(com.android.internal.R.bool.config_supportSystemNavigationKeys))
-                .thenReturn(true);
-
-        SwipeToNotificationPreferenceController controller =
-                new SwipeToNotificationPreferenceController(mContext, null /* lifecycle */,
-                        SYSTEM_NAVIGATION_KEYS_ENABLED);
-        ResultPayload payload = controller.getResultPayload();
-        assertThat(payload).isInstanceOf(InlineSwitchPayload.class);
-    }
-
-    @Test
-    @Config(shadows = ShadowSecureSettings.class)
-    public void testSetValue_updatesCorrectly() {
-        int newValue = 1;
-        ContentResolver resolver = mContext.getContentResolver();
-        Settings.Secure.putInt(resolver, Settings.Secure.SYSTEM_NAVIGATION_KEYS_ENABLED, 0);
-
-        ((InlinePayload) mController.getResultPayload()).setValue(mContext, newValue);
-        int updatedValue = Settings.Secure.getInt(resolver,
-                Settings.Secure.SYSTEM_NAVIGATION_KEYS_ENABLED, -1);
-
-        assertThat(updatedValue).isEqualTo(newValue);
-    }
-
-    @Test
-    @Config(shadows = ShadowSecureSettings.class)
-    public void testGetValue_correctValueReturned() {
-        int currentValue = 1;
-        ContentResolver resolver = mContext.getContentResolver();
-        Settings.Secure.putInt(resolver,
-                Settings.Secure.SYSTEM_NAVIGATION_KEYS_ENABLED, currentValue);
-
-        int newValue = ((InlinePayload) mController.getResultPayload()).getValue(mContext);
-
-        assertThat(newValue).isEqualTo(currentValue);
-    }
-
     private void stubFingerprintSupported(boolean enabled) {
         when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT))
                 .thenReturn(enabled);
diff --git a/tests/robotests/src/com/android/settings/network/MobileNetworkPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/network/MobileNetworkPreferenceControllerTest.java
index e115dad..66e357b 100644
--- a/tests/robotests/src/com/android/settings/network/MobileNetworkPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/network/MobileNetworkPreferenceControllerTest.java
@@ -26,6 +26,7 @@
 
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settings.TestConfig;
+import com.android.settings.testutils.shadow.ShadowRestrictedLockUtilsWrapper;
 import com.android.settingslib.core.lifecycle.Lifecycle;
 
 import org.junit.Before;
@@ -87,6 +88,7 @@
     }
 
     @Test
+    @Config(shadows = ShadowRestrictedLockUtilsWrapper.class)
     public void wifiOnly_prefIsNotAvailable() {
         when(mUserManager.isAdminUser()).thenReturn(true);
         when(mUserManager.hasUserRestriction(anyString(), any(UserHandle.class)))
diff --git a/tests/robotests/src/com/android/settings/password/ChooseLockPasswordTest.java b/tests/robotests/src/com/android/settings/password/ChooseLockPasswordTest.java
index 5fa731e..b8f0679 100644
--- a/tests/robotests/src/com/android/settings/password/ChooseLockPasswordTest.java
+++ b/tests/robotests/src/com/android/settings/password/ChooseLockPasswordTest.java
@@ -31,7 +31,6 @@
 import com.android.settings.password.ChooseLockPassword.IntentBuilder;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settings.testutils.shadow.SettingsShadowResources;
-import com.android.settings.testutils.shadow.ShadowDynamicIndexableContentMonitor;
 import com.android.settings.testutils.shadow.ShadowEventLogWriter;
 import com.android.settings.testutils.shadow.ShadowUtils;
 import com.android.setupwizardlib.GlifLayout;
@@ -52,7 +51,6 @@
         shadows = {
                 SettingsShadowResources.class,
                 SettingsShadowResources.SettingsShadowTheme.class,
-                ShadowDynamicIndexableContentMonitor.class,
                 ShadowEventLogWriter.class,
                 ShadowUtils.class
         })
diff --git a/tests/robotests/src/com/android/settings/password/ChooseLockPatternTest.java b/tests/robotests/src/com/android/settings/password/ChooseLockPatternTest.java
index b238049..c74448b 100644
--- a/tests/robotests/src/com/android/settings/password/ChooseLockPatternTest.java
+++ b/tests/robotests/src/com/android/settings/password/ChooseLockPatternTest.java
@@ -29,7 +29,6 @@
 import com.android.settings.password.ChooseLockPattern.IntentBuilder;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settings.testutils.shadow.SettingsShadowResources;
-import com.android.settings.testutils.shadow.ShadowDynamicIndexableContentMonitor;
 import com.android.settings.testutils.shadow.ShadowEventLogWriter;
 import com.android.settings.testutils.shadow.ShadowUtils;
 import com.android.setupwizardlib.GlifLayout;
@@ -48,7 +47,6 @@
         shadows = {
                 SettingsShadowResources.class,
                 SettingsShadowResources.SettingsShadowTheme.class,
-                ShadowDynamicIndexableContentMonitor.class,
                 ShadowEventLogWriter.class,
                 ShadowUtils.class
         })
diff --git a/tests/robotests/src/com/android/settings/password/SetupChooseLockPasswordTest.java b/tests/robotests/src/com/android/settings/password/SetupChooseLockPasswordTest.java
index 39a0fb6..1195a2c 100644
--- a/tests/robotests/src/com/android/settings/password/SetupChooseLockPasswordTest.java
+++ b/tests/robotests/src/com/android/settings/password/SetupChooseLockPasswordTest.java
@@ -34,7 +34,6 @@
 import com.android.settings.password.SetupChooseLockPassword.SetupChooseLockPasswordFragment;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settings.testutils.shadow.SettingsShadowResources;
-import com.android.settings.testutils.shadow.ShadowDynamicIndexableContentMonitor;
 import com.android.settings.testutils.shadow.ShadowEventLogWriter;
 import com.android.settings.testutils.shadow.ShadowUtils;
 
@@ -62,7 +61,6 @@
         shadows = {
                 SettingsShadowResources.class,
                 SettingsShadowResources.SettingsShadowTheme.class,
-                ShadowDynamicIndexableContentMonitor.class,
                 ShadowEventLogWriter.class,
                 ShadowUtils.class
         })
diff --git a/tests/robotests/src/com/android/settings/password/SetupSkipDialogTest.java b/tests/robotests/src/com/android/settings/password/SetupSkipDialogTest.java
index 639349d..0e6f28a 100644
--- a/tests/robotests/src/com/android/settings/password/SetupSkipDialogTest.java
+++ b/tests/robotests/src/com/android/settings/password/SetupSkipDialogTest.java
@@ -27,7 +27,6 @@
 import com.android.settings.TestConfig;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settings.testutils.shadow.SettingsShadowResources;
-import com.android.settings.testutils.shadow.ShadowDynamicIndexableContentMonitor;
 import com.android.settings.testutils.shadow.ShadowEventLogWriter;
 import com.android.settings.testutils.shadow.ShadowUtils;
 
@@ -40,13 +39,11 @@
 import org.robolectric.shadows.ShadowAlertDialog;
 
 @RunWith(SettingsRobolectricTestRunner.class)
-@Config(
-        manifest = TestConfig.MANIFEST_PATH,
+@Config(manifest = TestConfig.MANIFEST_PATH,
         sdk = TestConfig.SDK_VERSION,
         shadows = {
                 SettingsShadowResources.class,
                 SettingsShadowResources.SettingsShadowTheme.class,
-                ShadowDynamicIndexableContentMonitor.class,
                 ShadowEventLogWriter.class,
                 ShadowUtils.class
         })
diff --git a/tests/robotests/src/com/android/settings/search/DynamicIndexableContentMonitorTest.java b/tests/robotests/src/com/android/settings/search/DynamicIndexableContentMonitorTest.java
deleted file mode 100644
index 26c89d5..0000000
--- a/tests/robotests/src/com/android/settings/search/DynamicIndexableContentMonitorTest.java
+++ /dev/null
@@ -1,563 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.settings.search;
-
-import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyBoolean;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.only;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
-import android.accessibilityservice.AccessibilityServiceInfo;
-import android.app.Activity;
-import android.app.Application;
-import android.app.LoaderManager;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.Loader;
-import android.content.pm.ActivityInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.content.pm.ServiceInfo;
-import android.database.ContentObserver;
-import android.hardware.input.InputManager;
-import android.net.Uri;
-import android.os.Bundle;
-import android.print.PrintManager;
-import android.print.PrintServicesLoader;
-import android.printservice.PrintServiceInfo;
-import android.provider.Settings;
-import android.provider.UserDictionary;
-import android.view.inputmethod.InputMethodInfo;
-
-import com.android.internal.content.PackageMonitor;
-import com.android.settings.TestConfig;
-import com.android.settings.inputmethod.AvailableVirtualKeyboardFragment;
-import com.android.settings.inputmethod.PhysicalKeyboardFragment;
-import com.android.settings.inputmethod.VirtualKeyboardFragment;
-import com.android.settings.language.LanguageAndInputSettings;
-import com.android.settings.print.PrintSettingsFragment;
-import com.android.settings.testutils.DatabaseTestUtils;
-import com.android.settings.testutils.SettingsRobolectricTestRunner;
-import com.android.settings.testutils.shadow.ShadowActivityWithLoadManager;
-import com.android.settings.testutils.shadow.ShadowContextImplWithRegisterReceiver;
-import com.android.settings.testutils.shadow.ShadowInputManager;
-import com.android.settings.testutils.shadow.ShadowInputMethodManagerWithMethodList;
-import com.android.settings.testutils.shadow.ShadowPackageMonitor;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.Robolectric;
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.annotation.Config;
-import org.robolectric.internal.ShadowExtractor;
-import org.robolectric.res.builder.RobolectricPackageManager;
-import org.robolectric.shadows.ShadowAccessibilityManager;
-import org.robolectric.shadows.ShadowApplication;
-import org.robolectric.shadows.ShadowContentResolver;
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-
-@RunWith(SettingsRobolectricTestRunner.class)
-@Config(
-        manifest = TestConfig.MANIFEST_PATH,
-        sdk = TestConfig.SDK_VERSION,
-        shadows = {
-                ShadowActivityWithLoadManager.class,
-                ShadowContextImplWithRegisterReceiver.class,
-                ShadowInputManager.class,
-                ShadowInputMethodManagerWithMethodList.class,
-                ShadowPackageMonitor.class,
-        }
-)
-public class DynamicIndexableContentMonitorTest {
-
-    private static final int LOADER_ID = 1234;
-    private static final String A11Y_PACKAGE_1 = "a11y-1";
-    private static final String A11Y_PACKAGE_2 = "a11y-2";
-    private static final String IME_PACKAGE_1 = "ime-1";
-    private static final String IME_PACKAGE_2 = "ime-2";
-
-    @Mock
-    private LoaderManager mLoaderManager;
-    @Mock
-    private DatabaseIndexingManager mIndexManager;
-
-    private Activity mActivity;
-    private InputManager mInputManager;
-
-    private ShadowContextImplWithRegisterReceiver mShadowContextImpl;
-    private ShadowActivityWithLoadManager mShadowActivity;
-    private ShadowAccessibilityManager mShadowAccessibilityManager;
-    private ShadowInputMethodManagerWithMethodList mShadowInputMethodManager;
-    private RobolectricPackageManager mRobolectricPackageManager;
-
-    private final DynamicIndexableContentMonitor mMonitor = new DynamicIndexableContentMonitor();
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-        mActivity = Robolectric.buildActivity(Activity.class).get();
-        mInputManager = InputManager.getInstance();
-
-        // Robolectric shadows.
-        mShadowContextImpl = (ShadowContextImplWithRegisterReceiver) ShadowExtractor.extract(
-                ((Application) ShadowApplication.getInstance().getApplicationContext())
-                .getBaseContext());
-        mShadowActivity = (ShadowActivityWithLoadManager) ShadowExtractor.extract(mActivity);
-        mShadowAccessibilityManager = (ShadowAccessibilityManager) ShadowExtractor.extract(
-                mActivity.getSystemService(Context.ACCESSIBILITY_SERVICE));
-        mShadowInputMethodManager = (ShadowInputMethodManagerWithMethodList) ShadowExtractor
-                .extract(mActivity.getSystemService(Context.INPUT_METHOD_SERVICE));
-        mRobolectricPackageManager = RuntimeEnvironment.getRobolectricPackageManager();
-
-        // Setup shadows.
-        mShadowContextImpl.setSystemService(Context.PRINT_SERVICE, mock(PrintManager.class));
-        mShadowContextImpl.setSystemService(Context.INPUT_SERVICE, mInputManager);
-        mShadowActivity.setLoaderManager(mLoaderManager);
-        mShadowAccessibilityManager.setInstalledAccessibilityServiceList(Collections.emptyList());
-        mShadowInputMethodManager.setInputMethodList(Collections.emptyList());
-        mRobolectricPackageManager.setSystemFeature(PackageManager.FEATURE_PRINTING, true);
-        mRobolectricPackageManager.setSystemFeature(PackageManager.FEATURE_INPUT_METHODS, true);
-    }
-
-    @After
-    public void shutDown() {
-        mMonitor.unregister(mActivity, LOADER_ID);
-        // BroadcastReceiver must be unregistered.
-        assertThat(extractPackageMonitor()).isNull();
-
-        DynamicIndexableContentMonitor.resetForTesting();
-        mRobolectricPackageManager.reset();
-
-        DatabaseTestUtils.clearDb(mActivity);
-    }
-
-    @Test
-    public void testLockedUser() {
-        mMonitor.register(mActivity, LOADER_ID, mIndexManager, false /* isUserUnlocked */);
-
-        // No loader procedure happens.
-        verify(mLoaderManager, never()).initLoader(
-                anyInt(), any(Bundle.class), any(LoaderManager.LoaderCallbacks.class));
-        // No indexing happens.
-        verify(mIndexManager, never()).updateFromClassNameResource(
-                anyString(), anyBoolean());
-
-        mMonitor.unregister(mActivity, LOADER_ID);
-
-        // No destroy loader should happen.
-        verify(mLoaderManager, never()).destroyLoader(anyInt());
-    }
-
-    @Test
-    public void testWithNoPrintingFeature() {
-        mRobolectricPackageManager.setSystemFeature(PackageManager.FEATURE_PRINTING, false);
-
-        mMonitor.register(mActivity, LOADER_ID, mIndexManager, true /* isUserUnlocked */);
-
-        // No loader procedure happens.
-        verify(mLoaderManager, never()).initLoader(
-                anyInt(), any(Bundle.class), any(LoaderManager.LoaderCallbacks.class));
-        verifyNoIndexing(PrintSettingsFragment.class);
-
-        mMonitor.unregister(mActivity, LOADER_ID);
-
-        // No destroy loader should happen.
-        verify(mLoaderManager, never()).destroyLoader(anyInt());
-        // BroadcastReceiver must be unregistered.
-        assertThat(extractPackageMonitor()).isNull();
-
-        // To suppress spurious test fail in {@link #shutDown()}.
-        mMonitor.register(mActivity, LOADER_ID, mIndexManager, true /* isUserUnlocked */);
-    }
-
-    @Test
-    public void testPrinterServiceIndex() {
-        mMonitor.register(mActivity, LOADER_ID, mIndexManager, true /* isUserUnlocked */);
-
-        // Loader procedure happens.
-        verify(mLoaderManager, only()).initLoader(LOADER_ID, null, mMonitor);
-
-        // Loading print services happens.
-        final Loader<List<PrintServiceInfo>> loader =
-                mMonitor.onCreateLoader(LOADER_ID, null /* args */);
-        assertThat(loader).isInstanceOf(PrintServicesLoader.class);
-        verifyNoIndexing(PrintSettingsFragment.class);
-
-        mMonitor.onLoadFinished(loader, Collections.emptyList());
-
-        verifyIncrementalIndexing(PrintSettingsFragment.class);
-    }
-
-    @Test
-    public void testInputDevicesMonitor() {
-        mMonitor.register(mActivity, LOADER_ID, mIndexManager, true /* isUserUnlocked */);
-
-        // Rebuild indexing should happen.
-        verifyIncrementalIndexing(PhysicalKeyboardFragment.class);
-        // Input monitor should be registered to InputManager.
-        final InputManager.InputDeviceListener listener = extactInputDeviceListener();
-        assertThat(listener).isNotNull();
-
-        /*
-         * Nothing happens on successive register calls.
-         */
-        mMonitor.unregister(mActivity, LOADER_ID);
-        reset(mIndexManager);
-
-        mMonitor.register(mActivity, LOADER_ID, mIndexManager, true /* isUserUnlocked */);
-
-        verifyNoIndexing(PhysicalKeyboardFragment.class);
-        assertThat(extactInputDeviceListener()).isEqualTo(listener);
-
-        /*
-         * A device is added.
-         */
-        reset(mIndexManager);
-
-        listener.onInputDeviceAdded(1 /* deviceId */);
-
-        verifyIncrementalIndexing(PhysicalKeyboardFragment.class);
-
-        /*
-         * A device is removed.
-         */
-        reset(mIndexManager);
-
-        listener.onInputDeviceRemoved(2 /* deviceId */);
-
-        verifyIncrementalIndexing(PhysicalKeyboardFragment.class);
-
-        /*
-         * A device is changed.
-         */
-        reset(mIndexManager);
-
-        listener.onInputDeviceChanged(3 /* deviceId */);
-
-        verifyIncrementalIndexing(PhysicalKeyboardFragment.class);
-    }
-
-    @Test
-    public void testInputMethodServicesMonitor() throws Exception {
-        mMonitor.register(mActivity, LOADER_ID, mIndexManager, true /* isUserUnlocked */);
-
-        verifyIncrementalIndexing(VirtualKeyboardFragment.class);
-        verifyIncrementalIndexing(AvailableVirtualKeyboardFragment.class);
-
-        final Uri enabledInputMethodsContentUri = Settings.Secure.getUriFor(
-                Settings.Secure.ENABLED_INPUT_METHODS);
-        // Content observer should be registered.
-        final ContentObserver observer = extractContentObserver(enabledInputMethodsContentUri);
-        assertThat(observer).isNotNull();
-
-        /*
-         * When an input method service package is installed, incremental indexing happen.
-         */
-        reset(mIndexManager);
-
-        installInputMethodService(IME_PACKAGE_1);
-
-        verifyIncrementalIndexing(VirtualKeyboardFragment.class);
-        verifyIncrementalIndexing(AvailableVirtualKeyboardFragment.class);
-
-        /*
-         * When another input method service package is installed, incremental indexing happens.
-         */
-        reset(mIndexManager);
-
-        installInputMethodService(IME_PACKAGE_2);
-
-        verifyIncrementalIndexing(VirtualKeyboardFragment.class);
-        verifyIncrementalIndexing(AvailableVirtualKeyboardFragment.class);
-
-        /*
-         * When an input method service is disabled, rebuild indexing happens.
-         */
-        reset(mIndexManager);
-
-        disableInstalledPackage(IME_PACKAGE_1);
-
-        verifyIncrementalIndexing(VirtualKeyboardFragment.class);
-        verifyIncrementalIndexing(AvailableVirtualKeyboardFragment.class);
-
-        /*
-         * When an input method service is enabled, incremental indexing happens.
-         */
-        reset(mIndexManager);
-
-        enableInstalledPackage(IME_PACKAGE_1);
-
-        verifyIncrementalIndexing(VirtualKeyboardFragment.class);
-        verifyIncrementalIndexing(AvailableVirtualKeyboardFragment.class);
-
-        /*
-         * When an input method service package is uninstalled, rebuild indexing happens.
-         */
-        reset(mIndexManager);
-
-        uninstallInputMethodService(IME_PACKAGE_1);
-
-        verifyIncrementalIndexing(VirtualKeyboardFragment.class);
-        verifyIncrementalIndexing(AvailableVirtualKeyboardFragment.class);
-
-        /*
-         * When an accessibility service package is installed, nothing happens.
-         */
-        reset(mIndexManager);
-
-        installAccessibilityService(A11Y_PACKAGE_1);
-
-        verifyNoIndexing(VirtualKeyboardFragment.class);
-        verifyNoIndexing(AvailableVirtualKeyboardFragment.class);
-
-        /*
-         * When enabled IMEs list is changed, rebuild indexing happens.
-         */
-        reset(mIndexManager);
-
-        observer.onChange(false /* selfChange */, enabledInputMethodsContentUri);
-
-        verifyIncrementalIndexing(VirtualKeyboardFragment.class);
-        verifyIncrementalIndexing(AvailableVirtualKeyboardFragment.class);
-    }
-
-    @Test
-    public void testUserDictionaryChangeMonitor() throws Exception {
-        mMonitor.register(mActivity, LOADER_ID, mIndexManager, true /* isUserUnlocked */);
-
-        // Content observer should be registered.
-        final ContentObserver observer = extractContentObserver(UserDictionary.Words.CONTENT_URI);
-        assertThat(observer).isNotNull();
-
-        verifyIncrementalIndexing(LanguageAndInputSettings.class);
-
-        /*
-         * When user dictionary content is changed, rebuild indexing happens.
-         */
-        reset(mIndexManager);
-
-        observer.onChange(false /* selfChange */, UserDictionary.Words.CONTENT_URI);
-
-        verifyIncrementalIndexing(LanguageAndInputSettings.class);
-    }
-
-    /*
-     * Verification helpers.
-     */
-
-    private void verifyNoIndexing(Class<?> indexingClass) {
-        verify(mIndexManager, never()).updateFromClassNameResource(eq(indexingClass.getName()),
-                anyBoolean());
-    }
-
-    private void verifyIncrementalIndexing(Class<?> indexingClass) {
-        verify(mIndexManager, times(1)).updateFromClassNameResource(indexingClass.getName(),
-                true /* includeInSearchResults */);
-        verify(mIndexManager, never()).updateFromClassNameResource(indexingClass.getName(),
-                false /* includeInSearchResults */);
-    }
-
-    /*
-     * Testing helper methods.
-     */
-
-    private InputManager.InputDeviceListener extactInputDeviceListener() {
-        List<InputManager.InputDeviceListener> listeners = ((ShadowInputManager) ShadowExtractor
-                .extract(mInputManager))
-                .getRegisteredInputDeviceListeners();
-        InputManager.InputDeviceListener inputDeviceListener = null;
-        for (InputManager.InputDeviceListener listener : listeners) {
-            if (isUnderTest(listener)) {
-                if (inputDeviceListener != null) {
-                    assertThat(listener).isEqualTo(inputDeviceListener);
-                } else {
-                    inputDeviceListener = listener;
-                }
-            }
-        }
-        return inputDeviceListener;
-    }
-
-    private PackageMonitor extractPackageMonitor() {
-        List<ShadowApplication.Wrapper> receivers = ShadowApplication.getInstance()
-                .getRegisteredReceivers();
-        PackageMonitor packageMonitor = null;
-        for (ShadowApplication.Wrapper wrapper : receivers) {
-            BroadcastReceiver receiver = wrapper.getBroadcastReceiver();
-            if (isUnderTest(receiver) && receiver instanceof PackageMonitor) {
-                if (packageMonitor != null) {
-                    assertThat(receiver).isEqualTo(packageMonitor);
-                } else {
-                    packageMonitor = (PackageMonitor) receiver;
-                }
-            }
-        }
-        return packageMonitor;
-    }
-
-    private ContentObserver extractContentObserver(Uri uri) {
-        ShadowContentResolver contentResolver = (ShadowContentResolver) ShadowExtractor
-                .extract(mActivity.getContentResolver());
-        Collection<ContentObserver> observers = contentResolver.getContentObservers(uri);
-        ContentObserver contentObserver = null;
-        for (ContentObserver observer : observers) {
-            if (isUnderTest(observer)) {
-                if (contentObserver != null) {
-                    assertThat(observer).isEqualTo(contentObserver);
-                } else {
-                    contentObserver = observer;
-                }
-            }
-        }
-        return contentObserver;
-    }
-
-    private void enableInstalledPackage(String packageName) {
-        ((PackageManager) mRobolectricPackageManager).setApplicationEnabledSetting(
-                packageName, PackageManager.COMPONENT_ENABLED_STATE_DEFAULT, 0 /* flags */);
-        extractPackageMonitor().onPackageModified(packageName);
-        Robolectric.flushBackgroundThreadScheduler();
-    }
-
-    private void disableInstalledPackage(String packageName) {
-        ((PackageManager) mRobolectricPackageManager).setApplicationEnabledSetting(
-                packageName, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, 0 /* flags */);
-        extractPackageMonitor().onPackageModified(packageName);
-        Robolectric.flushBackgroundThreadScheduler();
-    }
-
-    private void installAccessibilityService(String packageName) throws Exception {
-        final AccessibilityServiceInfo serviceToAdd = buildAccessibilityServiceInfo(packageName);
-
-        final List<AccessibilityServiceInfo> services = new ArrayList<>();
-        services.addAll(mShadowAccessibilityManager.getInstalledAccessibilityServiceList());
-        services.add(serviceToAdd);
-        mShadowAccessibilityManager.setInstalledAccessibilityServiceList(services);
-
-        final Intent intent = DynamicIndexableContentMonitor
-                .getAccessibilityServiceIntent(packageName);
-        mRobolectricPackageManager.addResolveInfoForIntent(intent, serviceToAdd.getResolveInfo());
-        mRobolectricPackageManager.addPackage(packageName);
-
-        extractPackageMonitor()
-                .onPackageAppeared(packageName, PackageMonitor.PACKAGE_PERMANENT_CHANGE);
-        Robolectric.flushBackgroundThreadScheduler();
-    }
-
-    private void uninstallAccessibilityService(String packageName) throws Exception {
-        final AccessibilityServiceInfo serviceToRemove = buildAccessibilityServiceInfo(packageName);
-
-        final List<AccessibilityServiceInfo> services = new ArrayList<>();
-        services.addAll(mShadowAccessibilityManager.getInstalledAccessibilityServiceList());
-        services.remove(serviceToRemove);
-        mShadowAccessibilityManager.setInstalledAccessibilityServiceList(services);
-
-        final Intent intent = DynamicIndexableContentMonitor
-                .getAccessibilityServiceIntent(packageName);
-        mRobolectricPackageManager.removeResolveInfosForIntent(intent, packageName);
-        mRobolectricPackageManager.removePackage(packageName);
-
-        extractPackageMonitor()
-                .onPackageDisappeared(packageName, PackageMonitor.PACKAGE_PERMANENT_CHANGE);
-        Robolectric.flushBackgroundThreadScheduler();
-    }
-
-    private void installInputMethodService(String packageName) throws Exception {
-        final ResolveInfo resolveInfoToAdd = buildResolveInfo(packageName, "imeService");
-        final InputMethodInfo serviceToAdd = buildInputMethodInfo(resolveInfoToAdd);
-
-        final List<InputMethodInfo> services = new ArrayList<>();
-        services.addAll(mShadowInputMethodManager.getInputMethodList());
-        services.add(serviceToAdd);
-        mShadowInputMethodManager.setInputMethodList(services);
-
-        final Intent intent = DynamicIndexableContentMonitor.getIMEServiceIntent(packageName);
-        mRobolectricPackageManager.addResolveInfoForIntent(intent, resolveInfoToAdd);
-        mRobolectricPackageManager.addPackage(packageName);
-
-        extractPackageMonitor()
-                .onPackageAppeared(packageName, PackageMonitor.PACKAGE_PERMANENT_CHANGE);
-        Robolectric.flushBackgroundThreadScheduler();
-    }
-
-    private void uninstallInputMethodService(String packageName) throws Exception {
-        final ResolveInfo resolveInfoToRemove = buildResolveInfo(packageName, "imeService");
-        final InputMethodInfo serviceToRemove = buildInputMethodInfo(resolveInfoToRemove);
-
-        final List<InputMethodInfo> services = new ArrayList<>();
-        services.addAll(mShadowInputMethodManager.getInputMethodList());
-        services.remove(serviceToRemove);
-        mShadowInputMethodManager.setInputMethodList(services);
-
-        final Intent intent = DynamicIndexableContentMonitor.getIMEServiceIntent(packageName);
-        mRobolectricPackageManager.removeResolveInfosForIntent(intent, packageName);
-        mRobolectricPackageManager.removePackage(packageName);
-
-        extractPackageMonitor()
-                .onPackageDisappeared(packageName, PackageMonitor.PACKAGE_PERMANENT_CHANGE);
-        Robolectric.flushBackgroundThreadScheduler();
-    }
-
-    private AccessibilityServiceInfo buildAccessibilityServiceInfo(String packageName)
-            throws IOException, XmlPullParserException {
-        return new AccessibilityServiceInfo(
-                buildResolveInfo(packageName, "A11yService"), mActivity);
-    }
-
-    private static InputMethodInfo buildInputMethodInfo(ResolveInfo resolveInfo) {
-        return new InputMethodInfo(resolveInfo, false /* isAuxIme */, "SettingsActivity",
-                null /* subtypes */,  0 /* defaultResId */, false /* forceDefault */);
-    }
-
-    private static ResolveInfo buildResolveInfo(String packageName, String className) {
-        final ResolveInfo resolveInfo = new ResolveInfo();
-        resolveInfo.serviceInfo = new ServiceInfo();
-        resolveInfo.serviceInfo.packageName = packageName;
-        resolveInfo.serviceInfo.name = className;
-        // To workaround that RobolectricPackageManager.removeResolveInfosForIntent() only works
-        // for activity/broadcast resolver.
-        resolveInfo.activityInfo = new ActivityInfo();
-        resolveInfo.activityInfo.packageName = packageName;
-        resolveInfo.activityInfo.name = className;
-
-        return resolveInfo;
-    }
-
-    private static boolean isUnderTest(Object object) {
-        return object.getClass().getName().startsWith(
-                DynamicIndexableContentMonitor.class.getName());
-    }
-}
diff --git a/tests/robotests/src/com/android/settings/search/InputDeviceResultLoaderTest.java b/tests/robotests/src/com/android/settings/search/InputDeviceResultLoaderTest.java
new file mode 100644
index 0000000..a955af1
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/search/InputDeviceResultLoaderTest.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.search;
+
+import static android.content.Context.INPUT_METHOD_SERVICE;
+import static com.android.settings.search.InputDeviceResultLoader.PHYSICAL_KEYBOARD_FRAGMENT;
+import static com.android.settings.search.InputDeviceResultLoader.VIRTUAL_KEYBOARD_FRAGMENT;
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.ServiceInfo;
+import android.hardware.input.InputManager;
+import android.view.InputDevice;
+import android.view.inputmethod.InputMethodInfo;
+import android.view.inputmethod.InputMethodManager;
+
+import com.android.settings.R;
+import com.android.settings.TestConfig;
+import com.android.settings.dashboard.SiteMapManager;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settings.testutils.shadow.ShadowInputDevice;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Answers;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH,
+        sdk = TestConfig.SDK_VERSION,
+        shadows = {
+                ShadowInputDevice.class
+        })
+public class InputDeviceResultLoaderTest {
+
+    private static final String QUERY = "test_query";
+    private static final List<String> PHYSICAL_KEYBOARD_BREADCRUMB;
+    private static final List<String> VIRTUAL_KEYBOARD_BREADCRUMB;
+
+    static {
+        PHYSICAL_KEYBOARD_BREADCRUMB = new ArrayList<>();
+        VIRTUAL_KEYBOARD_BREADCRUMB = new ArrayList<>();
+        PHYSICAL_KEYBOARD_BREADCRUMB.add("Settings");
+        PHYSICAL_KEYBOARD_BREADCRUMB.add("physical keyboard");
+        VIRTUAL_KEYBOARD_BREADCRUMB.add("Settings");
+        VIRTUAL_KEYBOARD_BREADCRUMB.add("virtual keyboard");
+    }
+
+    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+    private Context mContext;
+    @Mock
+    private SiteMapManager mSiteMapManager;
+    @Mock
+    private InputManager mInputManager;
+    @Mock
+    private InputMethodManager mImm;
+    @Mock
+    private PackageManager mPackageManager;
+
+    private InputDeviceResultLoader mLoader;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        when(mContext.getApplicationContext()).thenReturn(mContext);
+        when(mContext.getSystemService(Context.INPUT_SERVICE))
+                .thenReturn(mInputManager);
+        when(mContext.getSystemService(INPUT_METHOD_SERVICE))
+                .thenReturn(mImm);
+        when(mContext.getPackageManager())
+                .thenReturn(mPackageManager);
+        when(mContext.getString(anyInt()))
+                .thenAnswer(invocation -> RuntimeEnvironment.application.getString(
+                        (Integer) invocation.getArguments()[0]));
+        mLoader = new InputDeviceResultLoader(mContext, QUERY, mSiteMapManager);
+    }
+
+    @After
+    public void tearDown() {
+        ShadowInputDevice.reset();
+    }
+
+    @Test
+    public void query_noKeyboard_shouldNotReturnAnything() {
+        assertThat(mLoader.loadInBackground()).isEmpty();
+    }
+
+    @Test
+    public void query_hasPhysicalKeyboard_match() {
+        addPhysicalKeyboard(QUERY);
+        when(mSiteMapManager.buildBreadCrumb(mContext, PHYSICAL_KEYBOARD_FRAGMENT,
+                RuntimeEnvironment.application.getString(R.string.physical_keyboard_title)))
+                .thenReturn(PHYSICAL_KEYBOARD_BREADCRUMB);
+
+        final List<SearchResult> results = new ArrayList<>(mLoader.loadInBackground());
+
+        assertThat(results).hasSize(1);
+        assertThat(results.get(0).title).isEqualTo(QUERY);
+        assertThat(results.get(0).breadcrumbs)
+                .containsExactlyElementsIn(PHYSICAL_KEYBOARD_BREADCRUMB);
+    }
+
+    @Test
+    public void query_hasVirtualKeyboard_match() {
+        addVirtualKeyboard(QUERY);
+        when(mSiteMapManager.buildBreadCrumb(mContext, VIRTUAL_KEYBOARD_FRAGMENT,
+                RuntimeEnvironment.application.getString(R.string.add_virtual_keyboard)))
+                .thenReturn(VIRTUAL_KEYBOARD_BREADCRUMB);
+
+        final List<SearchResult> results = new ArrayList<>(mLoader.loadInBackground());
+        assertThat(results).hasSize(1);
+        assertThat(results.get(0).title).isEqualTo(QUERY);
+        assertThat(results.get(0).breadcrumbs)
+                .containsExactlyElementsIn(VIRTUAL_KEYBOARD_BREADCRUMB);
+    }
+
+    @Test
+    public void query_hasPhysicalVirtualKeyboard_doNotMatch() {
+        addPhysicalKeyboard("abc");
+        addVirtualKeyboard("def");
+
+        assertThat(mLoader.loadInBackground()).isEmpty();
+        verifyZeroInteractions(mSiteMapManager);
+    }
+
+    private void addPhysicalKeyboard(String name) {
+        final InputDevice device = mock(InputDevice.class);
+        when(device.isVirtual()).thenReturn(false);
+        when(device.isFullKeyboard()).thenReturn(true);
+        when(device.getName()).thenReturn(name);
+        ShadowInputDevice.sDeviceIds = new int[]{0};
+        ShadowInputDevice.addDevice(0, device);
+    }
+
+    private void addVirtualKeyboard(String name) {
+        final List<InputMethodInfo> imis = new ArrayList<>();
+        final InputMethodInfo info = mock(InputMethodInfo.class);
+        imis.add(info);
+        when(info.getServiceInfo()).thenReturn(new ServiceInfo());
+        when(info.loadLabel(mPackageManager)).thenReturn(name);
+        info.getServiceInfo().packageName = "pkg";
+        info.getServiceInfo().name = "class";
+        when(mImm.getInputMethodList()).thenReturn(imis);
+    }
+
+}
diff --git a/tests/robotests/src/com/android/settings/search/MockAccessiblityLoader.java b/tests/robotests/src/com/android/settings/search/MockAccessibilityLoader.java
similarity index 88%
rename from tests/robotests/src/com/android/settings/search/MockAccessiblityLoader.java
rename to tests/robotests/src/com/android/settings/search/MockAccessibilityLoader.java
index aa9b778..0a06a35 100644
--- a/tests/robotests/src/com/android/settings/search/MockAccessiblityLoader.java
+++ b/tests/robotests/src/com/android/settings/search/MockAccessibilityLoader.java
@@ -21,9 +21,9 @@
 import java.util.HashSet;
 import java.util.Set;
 
-public class MockAccessiblityLoader extends AccessibilityServiceResultLoader {
+public class MockAccessibilityLoader extends AccessibilityServiceResultLoader {
 
-    public MockAccessiblityLoader(Context context) {
+    public MockAccessibilityLoader(Context context) {
         super(context, "test_query", null);
     }
 
diff --git a/tests/robotests/src/com/android/settings/search/MockAccessiblityLoader.java b/tests/robotests/src/com/android/settings/search/MockInputDeviceResultLoader.java
similarity index 88%
copy from tests/robotests/src/com/android/settings/search/MockAccessiblityLoader.java
copy to tests/robotests/src/com/android/settings/search/MockInputDeviceResultLoader.java
index aa9b778..2c16b14 100644
--- a/tests/robotests/src/com/android/settings/search/MockAccessiblityLoader.java
+++ b/tests/robotests/src/com/android/settings/search/MockInputDeviceResultLoader.java
@@ -21,9 +21,8 @@
 import java.util.HashSet;
 import java.util.Set;
 
-public class MockAccessiblityLoader extends AccessibilityServiceResultLoader {
-
-    public MockAccessiblityLoader(Context context) {
+public class MockInputDeviceResultLoader extends InputDeviceResultLoader {
+    public MockInputDeviceResultLoader(Context context) {
         super(context, "test_query", null);
     }
 
diff --git a/tests/robotests/src/com/android/settings/search/SearchFragmentTest.java b/tests/robotests/src/com/android/settings/search/SearchFragmentTest.java
index 6588805..71c31aa 100644
--- a/tests/robotests/src/com/android/settings/search/SearchFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/search/SearchFragmentTest.java
@@ -17,6 +17,20 @@
 
 package com.android.settings.search;
 
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.nullable;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.argThat;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
 import android.app.LoaderManager;
 import android.content.Context;
 import android.content.Intent;
@@ -28,10 +42,10 @@
 import com.android.internal.logging.nano.MetricsProto;
 import com.android.settings.R;
 import com.android.settings.SettingsActivity;
-import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settings.TestConfig;
 import com.android.settings.testutils.DatabaseTestUtils;
 import com.android.settings.testutils.FakeFeatureFactory;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settings.testutils.shadow.SettingsShadowResources;
 
 import org.junit.After;
@@ -52,20 +66,6 @@
 
 import java.util.Set;
 
-import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.ArgumentMatchers.nullable;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.argThat;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
 @RunWith(SettingsRobolectricTestRunner.class)
 @Config(manifest = TestConfig.MANIFEST_PATH,
         sdk = TestConfig.SDK_VERSION,
@@ -83,6 +83,8 @@
     private InstalledAppResultLoader mInstalledAppResultLoader;
     @Mock
     private AccessibilityServiceResultLoader mAccessibilityServiceResultLoader;
+    @Mock
+    private InputDeviceResultLoader mInputDeviceResultLoader;
 
     @Mock
     private SavedQueryLoader mSavedQueryLoader;
@@ -118,6 +120,9 @@
         when(mFeatureFactory.searchFeatureProvider
                 .getAccessibilityServiceResultLoader(any(Context.class), anyString()))
                 .thenReturn(mAccessibilityServiceResultLoader);
+        when(mFeatureFactory.searchFeatureProvider
+                .getInputDeviceResultLoader(any(Context.class), anyString()))
+                .thenReturn(mInputDeviceResultLoader);
         when(mFeatureFactory.searchFeatureProvider.getSavedQueryLoader(any(Context.class)))
                 .thenReturn(mSavedQueryLoader);
 
@@ -178,6 +183,9 @@
         when(mFeatureFactory.searchFeatureProvider
                 .getAccessibilityServiceResultLoader(any(Context.class), anyString()))
                 .thenReturn(mAccessibilityServiceResultLoader);
+        when(mFeatureFactory.searchFeatureProvider
+                .getInputDeviceResultLoader(any(Context.class), anyString()))
+                .thenReturn(mInputDeviceResultLoader);
         when(mFeatureFactory.searchFeatureProvider.getSavedQueryLoader(any(Context.class)))
                 .thenReturn(mSavedQueryLoader);
 
@@ -236,6 +244,9 @@
         when(mFeatureFactory.searchFeatureProvider
                 .getAccessibilityServiceResultLoader(any(Context.class), anyString()))
                 .thenReturn(mAccessibilityServiceResultLoader);
+        when(mFeatureFactory.searchFeatureProvider
+                .getInputDeviceResultLoader(any(Context.class), anyString()))
+                .thenReturn(mInputDeviceResultLoader);
         when(mFeatureFactory.searchFeatureProvider.getSavedQueryLoader(any(Context.class)))
                 .thenReturn(mSavedQueryLoader);
         ActivityController<SearchActivity> activityController =
@@ -270,6 +281,9 @@
         when(mFeatureFactory.searchFeatureProvider
                 .getAccessibilityServiceResultLoader(any(Context.class), anyString()))
                 .thenReturn(mAccessibilityServiceResultLoader);
+        when(mFeatureFactory.searchFeatureProvider
+                .getInputDeviceResultLoader(any(Context.class), anyString()))
+                .thenReturn(mInputDeviceResultLoader);
         when(mFeatureFactory.searchFeatureProvider.getSavedQueryLoader(any(Context.class)))
                 .thenReturn(mSavedQueryLoader);
 
@@ -349,7 +363,10 @@
                 .thenReturn(new MockAppLoader(RuntimeEnvironment.application));
         when(mFeatureFactory.searchFeatureProvider
                 .getAccessibilityServiceResultLoader(any(Context.class), anyString()))
-                .thenReturn(new MockAccessiblityLoader(RuntimeEnvironment.application));
+                .thenReturn(new MockAccessibilityLoader(RuntimeEnvironment.application));
+        when(mFeatureFactory.searchFeatureProvider
+                .getInputDeviceResultLoader(any(Context.class), anyString()))
+                .thenReturn(new MockInputDeviceResultLoader(RuntimeEnvironment.application));
         when(mFeatureFactory.searchFeatureProvider.getSavedQueryLoader(any(Context.class)))
                 .thenReturn(mSavedQueryLoader);
         ActivityController<SearchActivity> activityController =
diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowDynamicIndexableContentMonitor.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowDynamicIndexableContentMonitor.java
deleted file mode 100644
index de5d243..0000000
--- a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowDynamicIndexableContentMonitor.java
+++ /dev/null
@@ -1,22 +0,0 @@
-package com.android.settings.testutils.shadow;
-
-import android.app.Activity;
-import android.os.UserManager;
-import com.android.settings.search.DynamicIndexableContentMonitor;
-import org.robolectric.annotation.Implementation;
-import org.robolectric.annotation.Implements;
-import org.robolectric.annotation.RealObject;
-
-/**
- * A shadow class of {@link DynamicIndexableContentMonitor}. The real implementation of
- * {@link DynamicIndexableContentMonitor#register} calls {@link UserManager#isUserUnlocked()}, which
- * Robolectric has not yet been updated to support, so throws a NoSuchMethodError exception.
- */
-// TODO: Delete this once Robolectric is updated to the latest SDK.
-@Implements(DynamicIndexableContentMonitor.class)
-public class ShadowDynamicIndexableContentMonitor {
-
-    @Implementation
-    public void register(Activity activity, int loaderId) {
-    }
-}
diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowMobileNetworkPreferenceController.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowMobileNetworkPreferenceController.java
new file mode 100644
index 0000000..8d76063
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowMobileNetworkPreferenceController.java
@@ -0,0 +1,28 @@
+package com.android.settings.testutils.shadow;
+
+import android.content.Context;
+import com.android.settings.network.MobileNetworkPreferenceController;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+
+@Implements(MobileNetworkPreferenceController.class)
+public class ShadowMobileNetworkPreferenceController {
+    private static boolean mIsRestricted = false;
+
+    public void __constructor__(Context context) {
+    }
+
+    @Implementation
+    public boolean isAvailable() {
+        return mIsRestricted ? false : true;
+    }
+
+    @Implementation
+    public boolean isUserRestricted() {
+        return mIsRestricted;
+    }
+
+    public static void setRestricted(boolean restricted) {
+        mIsRestricted = restricted;
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowRestrictedLockUtilsWrapper.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowRestrictedLockUtilsWrapper.java
new file mode 100644
index 0000000..0e0adce
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowRestrictedLockUtilsWrapper.java
@@ -0,0 +1,25 @@
+package com.android.settings.testutils.shadow;
+
+import android.content.Context;
+import com.android.settings.network.RestrictedLockUtilsWrapper;
+import org.robolectric.annotation.Implements;
+
+/**
+ * Shadow for the wrapper around RestrictedLockUtils. Should be removed/updated once robolectric is
+ * updated to allow usage of new UserManager API's. see
+ * {@link com.android.settingslib.RestrictedLockUtils} and
+ * {@link com.android.settings.network.RestrictedLockUtilsWrapper}
+ */
+@Implements(RestrictedLockUtilsWrapper.class)
+public class ShadowRestrictedLockUtilsWrapper {
+
+    private boolean isRestricted;
+
+    public boolean hasBaseUserRestriction(Context context, String userRestriction, int userId) {
+        return isRestricted;
+    }
+
+    public void setRestricted(boolean restricted) {
+        isRestricted = restricted;
+    }
+}