DatabaseIndexingManagerTest.java revision bdd57aad3eb4ffeb106feb7b3d300ac6680def0d
1/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 */
17
18package com.android.settings.search;
19
20import android.annotation.NonNull;
21import android.annotation.Nullable;
22import android.content.ContentProvider;
23import android.content.ContentValues;
24import android.content.Context;
25import android.content.Intent;
26import android.content.pm.PackageManager;
27import android.content.pm.ProviderInfo;
28import android.content.pm.ResolveInfo;
29import android.database.Cursor;
30import android.database.MatrixCursor;
31import android.database.sqlite.SQLiteDatabase;
32import android.net.Uri;
33import android.os.Build;
34import android.provider.SearchIndexableResource;
35import android.util.ArrayMap;
36
37import com.android.settings.R;
38import com.android.settings.testutils.FakeFeatureFactory;
39import com.android.settings.testutils.SettingsRobolectricTestRunner;
40import com.android.settings.TestConfig;
41import com.android.settings.testutils.DatabaseTestUtils;
42import com.android.settings.testutils.shadow.ShadowDatabaseIndexingUtils;
43import com.android.settings.testutils.shadow.ShadowRunnableAsyncTask;
44
45import org.junit.After;
46import org.junit.Before;
47import org.junit.Test;
48import org.junit.runner.RunWith;
49import org.mockito.Mock;
50import org.mockito.MockitoAnnotations;
51import org.robolectric.Robolectric;
52import org.robolectric.RuntimeEnvironment;
53import org.robolectric.annotation.Config;
54import org.robolectric.shadows.ShadowContentResolver;
55
56import java.util.ArrayList;
57import java.util.Arrays;
58import java.util.HashMap;
59import java.util.HashSet;
60import java.util.List;
61import java.util.Locale;
62import java.util.Map;
63import java.util.Set;
64
65import static android.provider.SearchIndexablesContract.INDEXABLES_RAW_COLUMNS;
66import static com.google.common.truth.Truth.assertThat;
67import static org.mockito.Matchers.any;
68import static org.mockito.Matchers.anyInt;
69import static org.mockito.Matchers.anyList;
70import static org.mockito.Matchers.anyMap;
71import static org.mockito.Matchers.anyString;
72import static org.mockito.Mockito.doNothing;
73import static org.mockito.Mockito.doReturn;
74import static org.mockito.Mockito.mock;
75import static org.mockito.Mockito.spy;
76import static org.mockito.Mockito.times;
77import static org.mockito.Mockito.verify;
78import static org.mockito.Mockito.when;
79
80@RunWith(SettingsRobolectricTestRunner.class)
81@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION,
82        shadows = {ShadowRunnableAsyncTask.class})
83public class DatabaseIndexingManagerTest {
84    private final String localeStr = "en_US";
85
86    private final int rank = 8;
87    private final String title = "title\u2011title";
88    private final String updatedTitle = "title-title";
89    private final String normalizedTitle = "titletitle";
90    private final String summaryOn = "summary\u2011on";
91    private final String updatedSummaryOn = "summary-on";
92    private final String normalizedSummaryOn = "summaryon";
93    private final String summaryOff = "summary\u2011off";
94    private final String updatedSummaryOff = "summary-off";
95    private final String normalizedSummaryOff = "summaryoff";
96    private final String entries = "entries";
97    private final String keywords = "keywords, keywordss, keywordsss";
98    private final String spaceDelimittedKeywords = "keywords keywordss keywordsss";
99    private final String screenTitle = "screen title";
100    private final String className = "class name";
101    private final int iconResId = 0xff;
102    private final int noIcon = 0;
103    private final String action = "action";
104    private final String targetPackage = "target package";
105    private final String targetClass = "target class";
106    private final String packageName = "package name";
107    private final String key = "key";
108    private final int userId = -1;
109    private final boolean enabled = true;
110
111    private final String AUTHORITY_ONE = "authority";
112    private final String PACKAGE_ONE = "com.android.settings";
113
114    private final String TITLE_ONE = "title one";
115    private final String TITLE_TWO = "title two";
116    private final String KEY_ONE = "key one";
117    private final String KEY_TWO = "key two";
118
119    private Context mContext;
120
121    private DatabaseIndexingManager mManager;
122    private SQLiteDatabase mDb;
123
124    @Mock
125    private PackageManager mPackageManager;
126
127    @Before
128    public void setUp() {
129        MockitoAnnotations.initMocks(this);
130        mContext = spy(RuntimeEnvironment.application);
131        mManager = spy(new DatabaseIndexingManager(mContext, PACKAGE_ONE));
132        mDb = IndexDatabaseHelper.getInstance(mContext).getWritableDatabase();
133
134        doReturn(mPackageManager).when(mContext).getPackageManager();
135        FakeFeatureFactory.setupForTest(mContext);
136    }
137
138    @After
139    public void cleanUp() {
140        DatabaseTestUtils.clearDb(mContext);
141    }
142
143    @Test
144    public void testDatabaseSchema() {
145        Cursor dbCursor = mDb.query("prefs_index", null, null, null, null, null, null);
146        List<String> columnNames = new ArrayList<>(Arrays.asList(dbCursor.getColumnNames()));
147        // Note that docid is not included.
148        List<String> expColumnNames = new ArrayList<>(Arrays.asList(new String[]{
149                "locale",
150                "data_rank",
151                "data_title",
152                "data_title_normalized",
153                "data_summary_on",
154                "data_summary_on_normalized",
155                "data_summary_off",
156                "data_summary_off_normalized",
157                "data_entries",
158                "data_keywords",
159                "class_name",
160                "screen_title",
161                "intent_action",
162                "intent_target_package",
163                "intent_target_class",
164                "icon",
165                "enabled",
166                "data_key_reference",
167                "user_id",
168                "payload_type",
169                "payload"
170        }));
171        // Prevent database schema regressions
172        assertThat(columnNames).containsAllIn(expColumnNames);
173    }
174
175    // Tests for the flow: IndexOneRaw -> UpdateOneRowWithFilteredData -> UpdateOneRow
176
177    @Test
178    public void testInsertRawColumn_rowInserted() {
179        SearchIndexableRaw raw = getFakeRaw();
180        mManager.indexOneSearchIndexableData(mDb, localeStr, raw,
181                new HashMap<>()/* Non-indexable keys */);
182        Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index", null);
183        assertThat(cursor.getCount()).isEqualTo(1);
184    }
185
186    @Test
187    public void testInsertRawColumn_nonIndexableKey_resultIsDisabled() {
188        SearchIndexableRaw raw = getFakeRaw();
189        Map<String, Set<String>> niks = new HashMap<>();
190        Set<String> keys = new HashSet<>();
191        keys.add(raw.key);
192        niks.put(raw.intentTargetPackage, keys);
193
194        mManager.indexOneSearchIndexableData(mDb, localeStr, raw, niks);
195        Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index WHERE enabled = 0", null);
196        assertThat(cursor.getCount()).isEqualTo(1);
197    }
198
199    @Test
200    public void testInsertRawColumn_rowMatches() {
201        SearchIndexableRaw raw = getFakeRaw();
202        mManager.indexOneSearchIndexableData(mDb, localeStr, raw,
203                new HashMap<>()/* Non-indexable keys */);
204        Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index", null);
205        cursor.moveToPosition(0);
206
207        // Locale
208        assertThat(cursor.getString(0)).isEqualTo(localeStr);
209        // Data Rank
210        assertThat(cursor.getInt(1)).isEqualTo(raw.rank);
211        // Data Title
212        assertThat(cursor.getString(2)).isEqualTo(updatedTitle);
213        // Normalized Title
214        assertThat(cursor.getString(3)).isEqualTo(normalizedTitle);
215        // Summary On
216        assertThat(cursor.getString(4)).isEqualTo(updatedSummaryOn);
217        // Summary On Normalized
218        assertThat(cursor.getString(5)).isEqualTo(normalizedSummaryOn);
219        // Summary Off
220        assertThat(cursor.getString(6)).isEqualTo(updatedSummaryOff);
221        // Summary off normalized
222        assertThat(cursor.getString(7)).isEqualTo(normalizedSummaryOff);
223        // Entries
224        assertThat(cursor.getString(8)).isEqualTo(raw.entries);
225        // Keywords
226        assertThat(cursor.getString(9)).isEqualTo(spaceDelimittedKeywords);
227        // Screen Title
228        assertThat(cursor.getString(10)).isEqualTo(raw.screenTitle);
229        // Class Name
230        assertThat(cursor.getString(11)).isEqualTo(raw.className);
231        // Icon
232        assertThat(cursor.getInt(12)).isEqualTo(raw.iconResId);
233        // Intent Action
234        assertThat(cursor.getString(13)).isEqualTo(raw.intentAction);
235        // Target Package
236        assertThat(cursor.getString(14)).isEqualTo(raw.intentTargetPackage);
237        // Target Class
238        assertThat(cursor.getString(15)).isEqualTo(raw.intentTargetClass);
239        // Enabled
240        assertThat(cursor.getInt(16) == 1).isEqualTo(raw.enabled);
241        // Data ref key
242        assertThat(cursor.getString(17)).isNotNull();
243        // User Id
244        assertThat(cursor.getInt(18)).isEqualTo(raw.userId);
245        // Payload Type - default is 0
246        assertThat(cursor.getInt(19)).isEqualTo(0);
247        // Payload
248        byte[] payload = cursor.getBlob(20);
249        ResultPayload unmarshalledPayload = ResultPayloadUtils.unmarshall(payload,
250                ResultPayload.CREATOR);
251        assertThat(unmarshalledPayload).isInstanceOf(ResultPayload.class);
252    }
253
254    @Test
255    public void testInsertRawColumn_mismatchedLocale_noRowInserted() {
256        SearchIndexableRaw raw = getFakeRaw("ca-fr");
257        mManager.indexOneSearchIndexableData(mDb, localeStr, raw, null /* Non-indexable keys */);
258        Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index", null);
259        assertThat(cursor.getCount()).isEqualTo(0);
260    }
261
262    // Tests for the flow: IndexOneResource -> IndexFromResource ->
263    //                     UpdateOneRowWithFilteredData -> UpdateOneRow
264
265    @Test
266    public void testNullResource_NothingInserted() {
267        mManager.indexOneSearchIndexableData(mDb, localeStr, null /* searchIndexableResource */,
268                new HashMap<>());
269        Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index", null);
270        assertThat(cursor.getCount()).isEqualTo(0);
271    }
272
273    @Test
274    public void testAddResource_RowsInserted() {
275        SearchIndexableResource resource = getFakeResource(R.xml.display_settings);
276        mManager.indexOneSearchIndexableData(mDb, localeStr, resource, new HashMap<>());
277        Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index", null);
278        assertThat(cursor.getCount()).isEqualTo(17);
279    }
280
281    @Test
282    public void testAddResource_withNIKs_rowsInsertedDisabled() {
283        SearchIndexableResource resource = getFakeResource(R.xml.display_settings);
284        // Only add 2 of 16 items to be disabled.
285        String[] keys = {"brightness", "wallpaper"};
286        Map<String, Set<String>> niks = getNonIndexableKeys(keys);
287
288        mManager.indexOneSearchIndexableData(mDb, localeStr, resource, niks);
289
290        Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index WHERE enabled = 0", null);
291        assertThat(cursor.getCount()).isEqualTo(2);
292        cursor = mDb.rawQuery("SELECT * FROM prefs_index WHERE enabled = 1", null);
293        assertThat(cursor.getCount()).isEqualTo(15);
294    }
295
296    @Test
297    public void testAddResourceHeader_rowsMatch() {
298        SearchIndexableResource resource = getFakeResource(R.xml.application_settings);
299        mManager.indexOneSearchIndexableData(mDb, localeStr, resource, new HashMap<>());
300
301        Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index ORDER BY data_title", null);
302        cursor.moveToPosition(1);
303
304        // Locale
305        assertThat(cursor.getString(0)).isEqualTo(localeStr);
306        // Data Rank
307        assertThat(cursor.getInt(1)).isEqualTo(rank);
308        // Data Title
309        assertThat(cursor.getString(2)).isEqualTo("App info");
310        // Normalized Title
311        assertThat(cursor.getString(3)).isEqualTo("app info");
312        // Summary On
313        assertThat(cursor.getString(4)).isEqualTo("Manage apps, set up quick launch shortcuts");
314        // Summary On Normalized
315        assertThat(cursor.getString(5)).isEqualTo("manage apps, set up quick launch shortcuts");
316        // Summary Off - only on for checkbox preferences
317        assertThat(cursor.getString(6)).isEmpty();
318        // Summary off normalized - only on for checkbox preferences
319        assertThat(cursor.getString(7)).isEmpty();
320        // Entries - only on for list preferences
321        assertThat(cursor.getString(8)).isNull();
322        // Keywords
323        assertThat(cursor.getString(9)).isEmpty();
324        // Screen Title
325        assertThat(cursor.getString(10)).isEqualTo("App info");
326        // Class Name
327        assertThat(cursor.getString(11)).isEqualTo(className);
328        // Icon
329        assertThat(cursor.getInt(12)).isEqualTo(0);
330        // Intent Action
331        assertThat(cursor.getString(13)).isEqualTo(action);
332        // Target Package
333        assertThat(cursor.getString(14)).isEqualTo(targetPackage);
334        // Target Class
335        assertThat(cursor.getString(15)).isEqualTo(targetClass);
336        // Enabled
337        assertThat(cursor.getInt(16) == 1).isEqualTo(enabled);
338        // Data ref key
339        assertThat(cursor.getString(17)).isEqualTo("applications_settings");
340        // User Id
341        assertThat(cursor.getInt(18)).isEqualTo(userId);
342        // Payload Type - default is 0
343        assertThat(cursor.getInt(19)).isEqualTo(0);
344        // Payload - should be updated to real payloads as controllers are added
345        byte[] payload = cursor.getBlob(20);
346        ResultPayload unmarshalledPayload = ResultPayloadUtils.unmarshall(payload,
347                ResultPayload.CREATOR);
348        assertThat(unmarshalledPayload).isInstanceOf(ResultPayload.class);
349    }
350
351    @Test
352    public void testAddResource_withChildFragment_shouldUpdateSiteMapDb() {
353        // FIXME: This test was failing. (count = 6 at the end)
354
355//        SearchIndexableResource resource = getFakeResource(R.xml.network_and_internet);
356//        mManager.indexOneSearchIndexableData(mDb, localeStr, resource,
357//                new HashMap<>());
358//        Cursor query = mDb.query(IndexDatabaseHelper.Tables.TABLE_SITE_MAP, SITE_MAP_COLUMNS,
359//                null, null, null, null, null);
360//        query.moveToPosition(-1);
361//        int count = 0;
362//        while (query.moveToNext()) {
363//            count++;
364//            assertThat(query.getString(query.getColumnIndex(SiteMapColumns.PARENT_CLASS)))
365//                    .isEqualTo(className);
366//            assertThat(query.getString(query.getColumnIndex(SiteMapColumns.PARENT_TITLE)))
367//                    .isEqualTo(mContext.getString(R.string.network_dashboard_title));
368//            assertThat(query.getString(query.getColumnIndex(SiteMapColumns.CHILD_CLASS)))
369//                    .isNotEmpty();
370//            assertThat(query.getString(query.getColumnIndex(SiteMapColumns.CHILD_TITLE)))
371//                    .isNotEmpty();
372//        }
373//        assertThat(count).isEqualTo(5);
374    }
375
376    @Test
377    public void testAddResource_customSetting_rowsMatch() {
378        SearchIndexableResource resource = getFakeResource(R.xml.swipe_to_notification_settings);
379        mManager.indexOneSearchIndexableData(mDb, localeStr, resource, new HashMap<>());
380        final String prefTitle =
381                mContext.getString(R.string.fingerprint_swipe_for_notifications_title);
382        final String prefSummary =
383                mContext.getString(R.string.fingerprint_swipe_for_notifications_summary);
384        final String keywords = mContext.getString(R.string.keywords_gesture);
385        Cursor cursor = mDb.rawQuery(
386                "SELECT * FROM prefs_index where data_title='" + prefTitle + "'", null);
387        cursor.moveToFirst();
388
389        // Locale
390        assertThat(cursor.getString(0)).isEqualTo(localeStr);
391        // Data Rank
392        assertThat(cursor.getInt(1)).isEqualTo(rank);
393        // Data Title
394        assertThat(cursor.getString(2)).isEqualTo(prefTitle);
395        // Normalized Title
396        assertThat(cursor.getString(3)).isEqualTo(prefTitle.toLowerCase());
397        // Summary On
398        assertThat(cursor.getString(4)).isEqualTo(prefSummary);
399        // Summary On Normalized
400        assertThat(cursor.getString(5)).isEqualTo(prefSummary.toLowerCase());
401        // Summary Off - only on for checkbox preferences
402        assertThat(cursor.getString(6)).isEmpty();
403        // Summary off normalized - only on for checkbox preferences
404        assertThat(cursor.getString(7)).isEmpty();
405        // Entries - only on for list preferences
406        assertThat(cursor.getString(8)).isNull();
407        // Keywords
408        assertThat(cursor.getString(9)).isEqualTo(keywords);
409        // Screen Title
410        assertThat(cursor.getString(10)).isEqualTo(
411                mContext.getString(R.string.fingerprint_swipe_for_notifications_title));
412        // Class Name
413        assertThat(cursor.getString(11)).isEqualTo(className);
414        // Icon
415        assertThat(cursor.getInt(12)).isEqualTo(noIcon);
416        // Intent Action
417        assertThat(cursor.getString(13)).isEqualTo(action);
418        // Target Package
419        assertThat(cursor.getString(14)).isEqualTo(targetPackage);
420        // Target Class
421        assertThat(cursor.getString(15)).isEqualTo(targetClass);
422        // Enabled
423        assertThat(cursor.getInt(16) == 1).isEqualTo(enabled);
424        // Data ref key
425        assertThat(cursor.getString(17)).isEqualTo("gesture_swipe_down_fingerprint");
426        // User Id
427        assertThat(cursor.getInt(18)).isEqualTo(userId);
428        // Payload Type - default is 0
429        assertThat(cursor.getInt(19)).isEqualTo(0);
430        // Payload - should be updated to real payloads as controllers are added
431        byte[] payload = cursor.getBlob(20);
432        ResultPayload unmarshalledPayload = ResultPayloadUtils.unmarshall(payload,
433                ResultPayload.CREATOR);
434        assertThat(unmarshalledPayload).isInstanceOf(ResultPayload.class);
435    }
436
437    @Test
438    public void testAddResource_checkboxPreference_rowsMatch() {
439        SearchIndexableResource resource = getFakeResource(R.xml.application_settings);
440        mManager.indexOneSearchIndexableData(mDb, localeStr, resource, new HashMap<>());
441
442        /* Should return 6 results, with the following titles:
443         * Advanced Settings, Apps, Manage Apps, Preferred install location, Running Services
444         */
445        Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index ORDER BY data_title", null);
446        cursor.moveToPosition(0);
447        // Locale
448        assertThat(cursor.getString(0)).isEqualTo(localeStr);
449        // Data Rank
450        assertThat(cursor.getInt(1)).isEqualTo(rank);
451        // Data Title
452        assertThat(cursor.getString(2)).isEqualTo("Advanced settings");
453        // Normalized Title
454        assertThat(cursor.getString(3)).isEqualTo("advanced settings");
455        // Summary On
456        assertThat(cursor.getString(4)).isEqualTo("Enable more settings options");
457        // Summary On Normalized
458        assertThat(cursor.getString(5)).isEqualTo("enable more settings options");
459        // Summary Off
460        assertThat(cursor.getString(6)).isEqualTo("Enable more settings options");
461        // Summary Off
462        assertThat(cursor.getString(7)).isEqualTo("enable more settings options");
463        // Entries - only on for list preferences
464        assertThat(cursor.getString(8)).isNull();
465        // Keywords
466        assertThat(cursor.getString(9)).isEmpty();
467        // Screen Title
468        assertThat(cursor.getString(10)).isEqualTo("App info");
469        // Class Name
470        assertThat(cursor.getString(11)).isEqualTo(className);
471        // Icon
472        assertThat(cursor.getInt(12)).isEqualTo(noIcon);
473        // Intent Action
474        assertThat(cursor.getString(13)).isEqualTo(action);
475        // Target Package
476        assertThat(cursor.getString(14)).isEqualTo(targetPackage);
477        // Target Class
478        assertThat(cursor.getString(15)).isEqualTo(targetClass);
479        // Enabled
480        assertThat(cursor.getInt(16) == 1).isEqualTo(enabled);
481        // Data ref key
482        assertThat(cursor.getString(17)).isEqualTo("toggle_advanced_settings");
483        // User Id
484        assertThat(cursor.getInt(18)).isEqualTo(userId);
485        // Payload Type - default is 0
486        assertThat(cursor.getInt(19)).isEqualTo(0);
487        // Payload - should be updated to real payloads as controllers are added
488        byte[] payload = cursor.getBlob(20);
489        ResultPayload unmarshalledPayload = ResultPayloadUtils.unmarshall(payload,
490                ResultPayload.CREATOR);
491        assertThat(unmarshalledPayload).isInstanceOf(ResultPayload.class);
492    }
493
494    @Test
495    public void testAddResource_listPreference_rowsMatch() {
496        SearchIndexableResource resource = getFakeResource(R.xml.application_settings);
497        mManager.indexOneSearchIndexableData(mDb, localeStr, resource, new HashMap<>());
498
499        Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index ORDER BY data_title", null);
500        cursor.moveToPosition(3);
501        // Locale
502        assertThat(cursor.getString(0)).isEqualTo(localeStr);
503        // Data Rank
504        assertThat(cursor.getInt(1)).isEqualTo(rank);
505        // Data Title
506        assertThat(cursor.getString(2)).isEqualTo("Preferred install location");
507        // Normalized Title
508        assertThat(cursor.getString(3)).isEqualTo("preferred install location");
509        // Summary On
510        assertThat(cursor.getString(4)).isEqualTo(
511                "Change the preferred installation location for new apps");
512        // Summary On Normalized
513        assertThat(cursor.getString(5)).isEqualTo(
514                "change the preferred installation location for new apps");
515        // Summary Off - only on for checkbox preferences
516        assertThat(cursor.getString(6)).isEmpty();
517        // Summary off normalized - only on for checkbox preferences
518        assertThat(cursor.getString(7)).isEmpty();
519        // Entries - only on for list preferences
520        assertThat(cursor.getString(8)).isEqualTo("Internal device storage|Removable SD card|" +
521                "Let the system decide|");
522        // Keywords
523        assertThat(cursor.getString(9)).isEmpty();
524        // Screen Title
525        assertThat(cursor.getString(10)).isEqualTo("App info");
526        // Class Name
527        assertThat(cursor.getString(11)).isEqualTo(className);
528        // Icon
529        assertThat(cursor.getInt(12)).isEqualTo(noIcon);
530        // Intent Action
531        assertThat(cursor.getString(13)).isEqualTo(action);
532        // Target Package
533        assertThat(cursor.getString(14)).isEqualTo(targetPackage);
534        // Target Class
535        assertThat(cursor.getString(15)).isEqualTo(targetClass);
536        // Enabled
537        assertThat(cursor.getInt(16) == 1).isEqualTo(enabled);
538        // Data ref key
539        assertThat(cursor.getString(17)).isEqualTo("app_install_location");
540        // User Id
541        assertThat(cursor.getInt(18)).isEqualTo(userId);
542        // Payload Type - default is 0
543        assertThat(cursor.getInt(19)).isEqualTo(0);
544        // Payload - should be updated to real payloads as controllers are added
545        byte[] payload = cursor.getBlob(20);
546        ResultPayload unmarshalledPayload = ResultPayloadUtils.unmarshall(payload,
547                ResultPayload.CREATOR);
548        assertThat(unmarshalledPayload).isInstanceOf(ResultPayload.class);
549    }
550
551    @Test
552    public void testAddResource_iconAddedFromXml() {
553        SearchIndexableResource resource = getFakeResource(R.xml.connected_devices);
554        mManager.indexOneSearchIndexableData(mDb, localeStr, resource, new HashMap<>());
555
556        Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index ORDER BY data_title", null);
557        cursor.moveToPosition(0);
558
559        // Icon
560        assertThat(cursor.getInt(12)).isNotEqualTo(noIcon);
561    }
562
563    // Tests for the flow: IndexOneResource -> IndexFromProvider -> IndexFromResource ->
564    //                     UpdateOneRowWithFilteredData -> UpdateOneRow
565
566    @Test
567    public void testResourceProvider_rowInserted() {
568        SearchIndexableResource resource = getFakeResource(R.xml.swipe_to_notification_settings);
569        resource.xmlResId = 0;
570        resource.className = "com.android.settings.display.ScreenZoomSettings";
571
572        mManager.indexOneSearchIndexableData(mDb, localeStr, resource, new HashMap<>());
573        Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index", null);
574        assertThat(cursor.getCount()).isEqualTo(1);
575    }
576
577    @Test
578    public void testResourceProvider_rowMatches() {
579        SearchIndexableResource resource = getFakeResource(R.xml.swipe_to_notification_settings);
580        resource.xmlResId = 0;
581        resource.className = "com.android.settings.display.ScreenZoomSettings";
582
583        mManager.indexOneSearchIndexableData(mDb, localeStr, resource, new HashMap<>());
584        Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index", null);
585        cursor.moveToPosition(0);
586
587        // Locale
588        assertThat(cursor.getString(0)).isEqualTo(localeStr);
589        // Data Rank
590        assertThat(cursor.getInt(1)).isEqualTo(rank);
591        // Data Title
592        assertThat(cursor.getString(2)).isEqualTo("Display size");
593        // Normalized Title
594        assertThat(cursor.getString(3)).isEqualTo("display size");
595        // Summary On
596        assertThat(cursor.getString(4)).isEmpty();
597        // Summary On Normalized
598        assertThat(cursor.getString(5)).isEmpty();
599        // Summary Off - only on for checkbox preferences
600        assertThat(cursor.getString(6)).isEmpty();
601        // Summary off normalized - only on for checkbox preferences
602        assertThat(cursor.getString(7)).isEmpty();
603        // Entries - only on for list preferences
604        assertThat(cursor.getString(8)).isNull();
605        // Keywords
606        assertThat(cursor.getString(9)).isEqualTo("display density screen zoom scale scaling");
607        // Screen Title
608        assertThat(cursor.getString(10)).isEqualTo("Display size");
609        // Class Name
610        assertThat(cursor.getString(11))
611                .isEqualTo("com.android.settings.display.ScreenZoomSettings");
612        // Icon
613        assertThat(cursor.getInt(12)).isEqualTo(noIcon);
614        // Intent Action
615        assertThat(cursor.getString(13)).isNull();
616        // Target Package
617        assertThat(cursor.getString(14)).isNull();
618        // Target Class
619        assertThat(cursor.getString(15)).isNull();
620        // Enabled
621        assertThat(cursor.getInt(16) == 1).isEqualTo(enabled);
622        // Data ref key
623        assertThat(cursor.getString(17)).isNull();
624        // User Id
625        assertThat(cursor.getInt(18)).isEqualTo(userId);
626        // Payload Type - default is 0
627        assertThat(cursor.getInt(19)).isEqualTo(0);
628        // Payload - should be updated to real payloads as controllers are added
629        byte[] payload = cursor.getBlob(20);
630        ResultPayload unmarshalledPayload = ResultPayloadUtils.unmarshall(payload,
631                ResultPayload.CREATOR);
632        assertThat(unmarshalledPayload).isInstanceOf(ResultPayload.class);
633    }
634
635    @Test
636    public void testResourceProvider_resourceRowInserted() {
637        SearchIndexableResource resource = getFakeResource(0);
638        resource.className = "com.android.settings.LegalSettings";
639
640        mManager.indexOneSearchIndexableData(mDb, localeStr, resource, new HashMap<>());
641        Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index", null);
642        assertThat(cursor.getCount()).isEqualTo(6);
643    }
644
645    @Test
646    public void testResourceProvider_resourceRowMatches() {
647        SearchIndexableResource resource = getFakeResource(0);
648        resource.className = "com.android.settings.display.ScreenZoomSettings";
649
650        mManager.indexOneSearchIndexableData(mDb, localeStr, resource, new HashMap<>());
651        Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index ORDER BY data_title", null);
652        cursor.moveToPosition(0);
653
654        // Locale
655        assertThat(cursor.getString(0)).isEqualTo(localeStr);
656        // Data Rank
657        assertThat(cursor.getInt(1)).isEqualTo(rank);
658        // Data Title
659        assertThat(cursor.getString(2)).isEqualTo("Display size");
660        // Normalized Title
661        assertThat(cursor.getString(3)).isEqualTo("display size");
662        // Summary On
663        assertThat(cursor.getString(4)).isEmpty();
664        // Summary On Normalized
665        assertThat(cursor.getString(5)).isEmpty();
666        // Summary Off - only on for checkbox preferences
667        assertThat(cursor.getString(6)).isEmpty();
668        // Summary off normalized - only on for checkbox preferences
669        assertThat(cursor.getString(7)).isEmpty();
670        // Entries - only on for list preferences
671        assertThat(cursor.getString(8)).isNull();
672        // Keywords
673        assertThat(cursor.getString(9)).isEqualTo(
674                "display density screen zoom scale scaling");
675        // Screen Title
676        assertThat(cursor.getString(10)).isEqualTo("Display size");
677        // Class Name
678        assertThat(cursor.getString(11))
679                .isEqualTo("com.android.settings.display.ScreenZoomSettings");
680        // Icon
681        assertThat(cursor.getInt(12)).isEqualTo(noIcon);
682        // Intent Action
683        assertThat(cursor.getString(13)).isNull();
684        // Target Package
685        assertThat(cursor.getString(14)).isNull();
686        // Target Class
687        assertThat(cursor.getString(15)).isNull();
688        // Enabled
689        assertThat(cursor.getInt(16) == 1).isEqualTo(enabled);
690        // Data ref key
691        assertThat(cursor.getString(17)).isNull();
692        // User Id
693        assertThat(cursor.getInt(18)).isEqualTo(userId);
694        // Payload Type - default is 0
695        assertThat(cursor.getInt(19)).isEqualTo(0);
696        // Payload - should be updated to real payloads as controllers are added
697        byte[] payload = cursor.getBlob(20);
698        ResultPayload unmarshalledPayload = ResultPayloadUtils.unmarshall(payload,
699                ResultPayload.CREATOR);
700        assertThat(unmarshalledPayload).isInstanceOf(ResultPayload.class);
701    }
702
703    @Test
704    public void testResourceProvider_disabledResource_rowsInserted() {
705        SearchIndexableResource resource = getFakeResource(0);
706        resource.className = "com.android.settings.LegalSettings";
707
708        mManager.indexOneSearchIndexableData(mDb, localeStr, resource,
709                new HashMap<String, Set<String>>());
710
711        Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index WHERE enabled = 1", null);
712        assertThat(cursor.getCount()).isEqualTo(1);
713        cursor = mDb.rawQuery("SELECT * FROM prefs_index WHERE enabled = 0", null);
714        assertThat(cursor.getCount()).isEqualTo(5);
715    }
716
717    @Test
718    public void testResource_withTitleAndSettingName_titleNotInserted() {
719        SearchIndexableResource resource = getFakeResource(R.xml.swipe_to_notification_settings);
720        mManager.indexFromResource(mDb, localeStr, resource, new ArrayList<String>());
721
722        Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index WHERE" +
723                " enabled = 1", null);
724        assertThat(cursor.getCount()).isEqualTo(1);
725    }
726
727    // Test new public indexing flow
728
729    @Test
730    @Config(shadows = {ShadowDatabaseIndexingUtils.class,})
731    public void testPerformIndexing_fullIndex_getsDataFromProviders() {
732        DummyProvider provider = new DummyProvider();
733        provider.onCreate();
734        ShadowContentResolver.registerProvider(AUTHORITY_ONE, provider);
735
736        // Test that Indexables are added for Full indexing
737        when(mPackageManager.queryIntentContentProviders(any(Intent.class), anyInt()))
738                .thenReturn(getDummyResolveInfo());
739
740        DatabaseIndexingManager manager =
741                spy(new DatabaseIndexingManager(mContext, PACKAGE_ONE));
742        doReturn(true).when(manager).isFullIndex(anyString(), anyString());
743
744        manager.performIndexing();
745
746        verify(manager).addIndexablesFromRemoteProvider(PACKAGE_ONE, AUTHORITY_ONE);
747        verify(manager).updateDatabase(true /* isFullIndex */, Locale.getDefault().toString());
748    }
749
750    @Test
751    @Config(shadows = {ShadowDatabaseIndexingUtils.class,})
752    public void testPerformIndexing_incrementalIndex_noDataAdded() {
753        DummyProvider provider = new DummyProvider();
754        provider.onCreate();
755        ShadowContentResolver.registerProvider(AUTHORITY_ONE, provider);
756
757        // Test that Indexables are added for Full indexing
758        when(mPackageManager.queryIntentContentProviders(any(Intent.class), anyInt()))
759                .thenReturn(getDummyResolveInfo());
760
761        DatabaseIndexingManager manager =
762                spy(new DatabaseIndexingManager(mContext, PACKAGE_ONE));
763        doReturn(false).when(manager).isFullIndex(anyString(), anyString());
764
765        manager.mDataToProcess.dataToUpdate.clear();
766
767        manager.performIndexing();
768
769        verify(manager, times(0)).addDataToDatabase(any(SQLiteDatabase.class), anyString(),
770                anyList(), anyMap());
771        verify(manager, times(0)).addIndexablesFromRemoteProvider(PACKAGE_ONE, AUTHORITY_ONE);
772        verify(manager).updateDataInDatabase(any(SQLiteDatabase.class), anyMap());
773    }
774
775    @Test
776    @Config(shadows = {ShadowDatabaseIndexingUtils.class,})
777    public void testPerformIndexing_localeChanged_databaseDropped() {
778        DummyProvider provider = new DummyProvider();
779        provider.onCreate();
780        ShadowContentResolver.registerProvider(AUTHORITY_ONE, provider);
781
782        // Test that Indexables are added for Full indexing
783        when(mPackageManager.queryIntentContentProviders(any(Intent.class), anyInt()))
784                .thenReturn(getDummyResolveInfo());
785
786        // Initialize the Manager
787        DatabaseIndexingManager manager =
788                spy(new DatabaseIndexingManager(mContext, PACKAGE_ONE));
789        doReturn(true).when(manager).isFullIndex(anyString(), anyString());
790
791        // Insert data point which will be dropped
792        final String oldTitle = "This is French";
793        insertSpecialCase(oldTitle, true, "key");
794
795        // Add a data point to be added by the indexing
796        SearchIndexableRaw raw = new SearchIndexableRaw(mContext);
797        final String newTitle = "This is English";
798        raw.title = newTitle;
799        manager.mDataToProcess.dataToUpdate.add(raw);
800
801        manager.performIndexing();
802
803        // Assert that the New Title is inserted
804        final Cursor newCursor = mDb.rawQuery("SELECT * FROM prefs_index WHERE data_title = '" +
805                newTitle + "'", null);
806        assertThat(newCursor.getCount()).isEqualTo(1);
807
808        // Assert that the Old Title is no longer in the database, since it was dropped
809        final Cursor oldCursor = mDb.rawQuery("SELECT * FROM prefs_index WHERE data_title = '" +
810                oldTitle + "'", null);
811        assertThat(oldCursor.getCount()).isEqualTo(0);
812    }
813
814    @Test
815    @Config(shadows = {ShadowDatabaseIndexingUtils.class,})
816    public void testPerformIndexing_onOta_FullIndex() {
817        DummyProvider provider = new DummyProvider();
818        provider.onCreate();
819        ShadowContentResolver.registerProvider(
820                AUTHORITY_ONE, provider
821        );
822
823        // Test that Indexables are added for Full indexing
824        when(mPackageManager.queryIntentContentProviders(any(Intent.class), anyInt()))
825                .thenReturn(getDummyResolveInfo());
826
827        DatabaseIndexingManager manager =
828                spy(new DatabaseIndexingManager(mContext, PACKAGE_ONE));
829        doReturn(true).when(manager).isFullIndex(anyString(), anyString());
830
831        manager.performIndexing();
832
833        verify(manager).updateDatabase(true /* isFullIndex */, Locale.getDefault().toString());
834    }
835
836    @Test
837    @Config(shadows = {ShadowDatabaseIndexingUtils.class,})
838    public void testPerformIndexing_onOta_buildNumberIsCached() {
839        DummyProvider provider = new DummyProvider();
840        provider.onCreate();
841        ShadowContentResolver.registerProvider(
842                AUTHORITY_ONE, provider
843        );
844
845        // Test that Indexables are added for Full indexing
846        when(mPackageManager.queryIntentContentProviders(any(Intent.class), anyInt()))
847                .thenReturn(getDummyResolveInfo());
848
849        DatabaseIndexingManager manager =
850                spy(new DatabaseIndexingManager(mContext, PACKAGE_ONE));
851        doReturn(true).when(manager).isFullIndex(anyString(), anyString());
852
853        manager.performIndexing();
854
855        assertThat(IndexDatabaseHelper.getInstance(mContext).isBuildIndexed(mContext,
856                Build.FINGERPRINT)).isTrue();
857    }
858
859    @Test
860    public void testFullUpdatedDatabase_noData_addDataToDatabaseNotCalled() {
861        mManager.updateDatabase(true /* isFullIndex */, localeStr);
862        mManager.mDataToProcess.dataToUpdate.clear();
863        verify(mManager, times(0)).addDataToDatabase(any(SQLiteDatabase.class), anyString(),
864                anyList(), anyMap());
865    }
866
867    @Test
868    public void testFullUpdatedDatabase_updatedDataInDatabaseNotCalled() {
869        mManager.updateDatabase(true /* isFullIndex */, localeStr);
870        verify(mManager, times(0)).updateDataInDatabase(any(SQLiteDatabase.class), anyMap());
871    }
872
873    @Test
874    public void testLocaleUpdated_afterIndexing_localeNotAdded() {
875        mManager.updateDatabase(true /* isFullIndex */, localeStr);
876        assertThat(IndexDatabaseHelper.getInstance(mContext)
877                .isLocaleAlreadyIndexed(mContext, localeStr)).isFalse();
878    }
879
880    @Test
881    public void testLocaleUpdated_afterFullIndexing_localeAdded() {
882        mManager.performIndexing();
883        assertThat(IndexDatabaseHelper.getInstance(mContext)
884                .isLocaleAlreadyIndexed(mContext, localeStr)).isTrue();
885    }
886
887    @Test
888    public void testUpdateDatabase_newEligibleData_addedToDatabase() {
889        // Test that addDataToDatabase is called when dataToUpdate is non-empty
890        mManager.mDataToProcess.dataToUpdate.add(getFakeRaw());
891        mManager.updateDatabase(true /* isFullIndex */, localeStr);
892
893        Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index", null);
894        cursor.moveToPosition(0);
895
896        // Locale
897        assertThat(cursor.getString(0)).isEqualTo(localeStr);
898        // Data Rank
899        assertThat(cursor.getInt(1)).isEqualTo(rank);
900        // Data Title
901        assertThat(cursor.getString(2)).isEqualTo(updatedTitle);
902        // Normalized Title
903        assertThat(cursor.getString(3)).isEqualTo(normalizedTitle);
904        // Summary On
905        assertThat(cursor.getString(4)).isEqualTo(updatedSummaryOn);
906        // Summary On Normalized
907        assertThat(cursor.getString(5)).isEqualTo(normalizedSummaryOn);
908        // Summary Off
909        assertThat(cursor.getString(6)).isEqualTo(updatedSummaryOff);
910        // Summary off normalized
911        assertThat(cursor.getString(7)).isEqualTo(normalizedSummaryOff);
912        // Entries
913        assertThat(cursor.getString(8)).isEqualTo(entries);
914        // Keywords
915        assertThat(cursor.getString(9)).isEqualTo(spaceDelimittedKeywords);
916        // Screen Title
917        assertThat(cursor.getString(10)).isEqualTo(screenTitle);
918        // Class Name
919        assertThat(cursor.getString(11)).isEqualTo(className);
920        // Icon
921        assertThat(cursor.getInt(12)).isEqualTo(iconResId);
922        // Intent Action
923        assertThat(cursor.getString(13)).isEqualTo(action);
924        // Target Package
925        assertThat(cursor.getString(14)).isEqualTo(targetPackage);
926        // Target Class
927        assertThat(cursor.getString(15)).isEqualTo(targetClass);
928        // Enabled
929        assertThat(cursor.getInt(16) == 1).isEqualTo(enabled);
930        // Data ref key
931        assertThat(cursor.getString(17)).isNotNull();
932        // User Id
933        assertThat(cursor.getInt(18)).isEqualTo(userId);
934        // Payload Type - default is 0
935        assertThat(cursor.getInt(19)).isEqualTo(0);
936        // Payload
937        byte[] payload = cursor.getBlob(20);
938        ResultPayload unmarshalledPayload = ResultPayloadUtils.unmarshall(payload,
939                ResultPayload.CREATOR);
940        assertThat(unmarshalledPayload).isInstanceOf(ResultPayload.class);
941    }
942
943    @Test
944    public void testUpdateDataInDatabase_enabledResultsAreNonIndexable_becomeDisabled() {
945        // Both results are enabled, and then TITLE_ONE gets disabled.
946        final boolean enabled = true;
947        insertSpecialCase(TITLE_ONE, enabled, KEY_ONE);
948        insertSpecialCase(TITLE_TWO, enabled, KEY_TWO);
949        Map<String, Set<String>> niks = new ArrayMap<>();
950        Set<String> keys = new HashSet<>();
951        keys.add(KEY_ONE);
952        niks.put(targetPackage, keys);
953
954        mManager.updateDataInDatabase(mDb, niks);
955
956        Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index WHERE enabled = 0", null);
957        cursor.moveToPosition(0);
958
959        assertThat(cursor.getString(2)).isEqualTo(TITLE_ONE);
960    }
961
962    @Test
963    public void testUpdateDataInDatabase_disabledResultsAreIndexable_becomeEnabled() {
964        // Both results are initially disabled, and then TITLE_TWO gets enabled.
965        final boolean enabled = false;
966        insertSpecialCase(TITLE_ONE, enabled, KEY_ONE);
967        insertSpecialCase(TITLE_TWO, enabled, KEY_TWO);
968        Map<String, Set<String>> niks = new ArrayMap<>();
969        Set<String> keys = new HashSet<>();
970        keys.add(KEY_ONE);
971        niks.put(targetPackage, keys);
972
973        mManager.updateDataInDatabase(mDb, niks);
974
975        Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index WHERE enabled = 1", null);
976        cursor.moveToPosition(0);
977
978        assertThat(cursor.getString(2)).isEqualTo(TITLE_TWO);
979    }
980
981    @Test
982    @Config(shadows = {ShadowContentResolver.class})
983    public void testEmptyNonIndexableKeys_emptyDataKeyResources_addedToDatabase() {
984        insertSpecialCase(TITLE_ONE, true /* enabled */, null /* dataReferenceKey */);
985
986        mManager.updateDatabase(false, localeStr);
987
988        Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index WHERE enabled = 1", null);
989        cursor.moveToPosition(0);
990        assertThat(cursor.getCount()).isEqualTo(1);
991        assertThat(cursor.getString(2)).isEqualTo(TITLE_ONE);
992    }
993
994    @Test
995    public void testUpdateAsyncTask_onPostExecute_performsCallback() {
996        IndexingCallback callback = mock(IndexingCallback.class);
997
998        DatabaseIndexingManager.IndexingTask task = mManager.new IndexingTask(callback);
999        task.execute();
1000
1001        Robolectric.flushForegroundThreadScheduler();
1002
1003        verify(callback).onIndexingFinished();
1004    }
1005
1006    @Test
1007    public void testUpdateAsyncTask_onPostExecute_setsIndexingComplete() {
1008        SearchFeatureProviderImpl provider = new SearchFeatureProviderImpl();
1009        DatabaseIndexingManager manager = spy(provider.getIndexingManager(mContext));
1010        DatabaseIndexingManager.IndexingTask task = manager.new IndexingTask(null);
1011        doNothing().when(manager).performIndexing();
1012
1013        task.execute();
1014        Robolectric.flushForegroundThreadScheduler();
1015
1016        assertThat(provider.isIndexingComplete(mContext)).isTrue();
1017    }
1018
1019    // Util functions
1020
1021    private SearchIndexableRaw getFakeRaw() {
1022        return getFakeRaw(localeStr);
1023    }
1024
1025    private SearchIndexableRaw getFakeRaw(String localeStr) {
1026        SearchIndexableRaw data = new SearchIndexableRaw(mContext);
1027        data.locale = new Locale(localeStr);
1028        data.rank = rank;
1029        data.title = title;
1030        data.summaryOn = summaryOn;
1031        data.summaryOff = summaryOff;
1032        data.entries = entries;
1033        data.keywords = keywords;
1034        data.screenTitle = screenTitle;
1035        data.className = className;
1036        data.packageName = packageName;
1037        data.iconResId = iconResId;
1038        data.intentAction = action;
1039        data.intentTargetPackage = targetPackage;
1040        data.intentTargetClass = targetClass;
1041        data.key = key;
1042        data.userId = userId;
1043        data.enabled = enabled;
1044        return data;
1045    }
1046
1047    private SearchIndexableResource getFakeResource(int xml) {
1048        SearchIndexableResource sir = new SearchIndexableResource(mContext);
1049        sir.rank = rank;
1050        sir.xmlResId = xml;
1051        sir.className = className;
1052        sir.packageName = packageName;
1053        sir.iconResId = iconResId;
1054        sir.intentAction = action;
1055        sir.intentTargetPackage = targetPackage;
1056        sir.intentTargetClass = targetClass;
1057        sir.enabled = enabled;
1058        return sir;
1059    }
1060
1061    private Map<String, Set<String>> getNonIndexableKeys(String[] keys) {
1062        Map<String, Set<String>> niks = new HashMap<>();
1063        Set<String> keysList = new HashSet<>();
1064        keysList.addAll(Arrays.asList(keys));
1065        niks.put(packageName, keysList);
1066        return niks;
1067    }
1068
1069    private List<ResolveInfo> getDummyResolveInfo() {
1070        List<ResolveInfo> infoList = new ArrayList<>();
1071        ResolveInfo info = new ResolveInfo();
1072        info.providerInfo = new ProviderInfo();
1073        info.providerInfo.exported = true;
1074        info.providerInfo.authority = AUTHORITY_ONE;
1075        info.providerInfo.packageName = PACKAGE_ONE;
1076        infoList.add(info);
1077
1078        return infoList;
1079    }
1080
1081    // TODO move this method and its counterpart in CursorToSearchResultConverterTest into
1082    // a util class with public fields to assert values.
1083    private Cursor getDummyCursor() {
1084        MatrixCursor cursor = new MatrixCursor(INDEXABLES_RAW_COLUMNS);
1085        final String BLANK = "";
1086
1087        ArrayList<String> item =
1088                new ArrayList<>(INDEXABLES_RAW_COLUMNS.length);
1089        item.add("42"); // Rank
1090        item.add(TITLE_ONE); // Title
1091        item.add(BLANK); // Summary on
1092        item.add(BLANK); // summary off
1093        item.add(BLANK); // entries
1094        item.add(BLANK); // keywords
1095        item.add(BLANK); // screen title
1096        item.add(BLANK); // classname
1097        item.add("123"); // Icon
1098        item.add(BLANK); // Intent action
1099        item.add(BLANK); // target package
1100        item.add(BLANK); // target class
1101        item.add(KEY_ONE); // Key
1102        item.add("-1"); // userId
1103        cursor.addRow(item);
1104
1105        return cursor;
1106    }
1107
1108    private void insertSpecialCase(String specialCase, boolean enabled, String key) {
1109        ContentValues values = new ContentValues();
1110        values.put(IndexDatabaseHelper.IndexColumns.DOCID, specialCase.hashCode());
1111        values.put(IndexDatabaseHelper.IndexColumns.LOCALE, localeStr);
1112        values.put(IndexDatabaseHelper.IndexColumns.DATA_RANK, 1);
1113        values.put(IndexDatabaseHelper.IndexColumns.DATA_TITLE, specialCase);
1114        values.put(IndexDatabaseHelper.IndexColumns.DATA_TITLE_NORMALIZED, "");
1115        values.put(IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_ON, "");
1116        values.put(IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_ON_NORMALIZED, "");
1117        values.put(IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_OFF, "");
1118        values.put(IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_OFF_NORMALIZED, "");
1119        values.put(IndexDatabaseHelper.IndexColumns.DATA_ENTRIES, "");
1120        values.put(IndexDatabaseHelper.IndexColumns.DATA_KEYWORDS, "");
1121        values.put(IndexDatabaseHelper.IndexColumns.CLASS_NAME, "");
1122        values.put(IndexDatabaseHelper.IndexColumns.SCREEN_TITLE, "Moves");
1123        values.put(IndexDatabaseHelper.IndexColumns.INTENT_ACTION, "");
1124        values.put(IndexDatabaseHelper.IndexColumns.INTENT_TARGET_PACKAGE, targetPackage);
1125        values.put(IndexDatabaseHelper.IndexColumns.INTENT_TARGET_CLASS, "");
1126        values.put(IndexDatabaseHelper.IndexColumns.ICON, "");
1127        values.put(IndexDatabaseHelper.IndexColumns.ENABLED, enabled);
1128        values.put(IndexDatabaseHelper.IndexColumns.DATA_KEY_REF, key);
1129        values.put(IndexDatabaseHelper.IndexColumns.USER_ID, 0);
1130        values.put(IndexDatabaseHelper.IndexColumns.PAYLOAD_TYPE, 0);
1131        values.put(IndexDatabaseHelper.IndexColumns.PAYLOAD, (String) null);
1132
1133        mDb.replaceOrThrow(IndexDatabaseHelper.Tables.TABLE_PREFS_INDEX, null, values);
1134    }
1135
1136    private class DummyProvider extends ContentProvider {
1137
1138        @Override
1139        public boolean onCreate() {
1140            return false;
1141        }
1142
1143        @Override
1144        public Cursor query(@NonNull Uri uri, @Nullable String[] projection,
1145                @Nullable String selection, @Nullable String[] selectionArgs,
1146                @Nullable String sortOrder) {
1147            if (uri.toString().contains("xml")) {
1148                return null;
1149            }
1150            return getDummyCursor();
1151        }
1152
1153        @Override
1154        public String getType(@NonNull Uri uri) {
1155            return null;
1156        }
1157
1158        @Override
1159        public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
1160            return null;
1161        }
1162
1163        @Override
1164        public int delete(@NonNull Uri uri, @Nullable String selection,
1165                @Nullable String[] selectionArgs) {
1166            return 0;
1167        }
1168
1169        @Override
1170        public int update(@NonNull Uri uri, @Nullable ContentValues values,
1171                @Nullable String selection, @Nullable String[] selectionArgs) {
1172            return 0;
1173        }
1174    }
1175}
1176