OSDN Git Service

Reindex db when package w/ searchIndexProvider changes
authorFan Zhang <zhfan@google.com>
Fri, 21 Jul 2017 00:05:01 +0000 (17:05 -0700)
committerFan Zhang <zhfan@google.com>
Fri, 21 Jul 2017 20:06:03 +0000 (13:06 -0700)
We do this by tracking a list of package/version that provides search
indexing data. When they change, we do a full reindex.

Change-Id: I85d4c4a994c7ff808662371c857cac1929a8b1cd
Merged-In: I906a1524f5b1292932f63727d605283ddb7d6ee2
Bug: 63903835
Test: robotests

src/com/android/settings/search/DatabaseIndexingManager.java
src/com/android/settings/search/DatabaseIndexingUtils.java
src/com/android/settings/search/IndexDatabaseHelper.java
tests/robotests/src/com/android/settings/search/DatabaseIndexingManagerTest.java

index 6a6c737..d2b2d23 100644 (file)
@@ -166,18 +166,22 @@ public class DatabaseIndexingManager {
      */
     public void performIndexing() {
         final Intent intent = new Intent(SearchIndexablesContract.PROVIDER_INTERFACE);
-        final List<ResolveInfo> list =
+        final List<ResolveInfo> providers =
                 mContext.getPackageManager().queryIntentContentProviders(intent, 0);
 
-        String localeStr = Locale.getDefault().toString();
-        String fingerprint = Build.FINGERPRINT;
-        final boolean isFullIndex = isFullIndex(localeStr, fingerprint);
+        final String localeStr = Locale.getDefault().toString();
+        final String fingerprint = Build.FINGERPRINT;
+        final String providerVersionedNames =
+                IndexDatabaseHelper.buildProviderVersionedNames(providers);
+
+        final boolean isFullIndex = IndexDatabaseHelper.isFullIndex(mContext, localeStr,
+                fingerprint, providerVersionedNames);
 
         if (isFullIndex) {
             rebuildDatabase();
         }
 
-        for (final ResolveInfo info : list) {
+        for (final ResolveInfo info : providers) {
             if (!DatabaseIndexingUtils.isWellKnownProvider(info, mContext)) {
                 continue;
             }
@@ -192,24 +196,10 @@ public class DatabaseIndexingManager {
 
         updateDatabase(isFullIndex, localeStr);
 
+        //TODO(63922686): Setting indexed should be a single method, not 3 separate setters.
         IndexDatabaseHelper.setLocaleIndexed(mContext, localeStr);
         IndexDatabaseHelper.setBuildIndexed(mContext, fingerprint);
-    }
-
-    /**
-     * Perform a full index on an OTA or when the locale has changed
-     *
-     * @param locale is the default for the device
-     * @param fingerprint id for the current build.
-     * @return true when the locale or build has changed since last index.
-     */
-    @VisibleForTesting
-    boolean isFullIndex(String locale, String fingerprint) {
-        final boolean isLocaleIndexed = IndexDatabaseHelper.getInstance(mContext)
-                .isLocaleAlreadyIndexed(mContext, locale);
-        final boolean isBuildIndexed = IndexDatabaseHelper.getInstance(mContext)
-                .isBuildIndexed(mContext, fingerprint);
-        return !isLocaleIndexed || !isBuildIndexed;
+        IndexDatabaseHelper.setProvidersIndexed(mContext, providerVersionedNames);
     }
 
     /**
@@ -1318,4 +1308,4 @@ public class DatabaseIndexingManager {
             }
         }
     }
-}
\ No newline at end of file
+}
index a6f3cb1..40ba7ee 100644 (file)
@@ -174,7 +174,7 @@ public class DatabaseIndexingUtils {
      * - have read/write {@link Manifest.permission#READ_SEARCH_INDEXABLES}
      * - be from a privileged package
      */
-    public static boolean isWellKnownProvider(ResolveInfo info, Context context) {
+    static boolean isWellKnownProvider(ResolveInfo info, Context context) {
         final String authority = info.providerInfo.authority;
         final String packageName = info.providerInfo.applicationInfo.packageName;
 
@@ -197,29 +197,29 @@ public class DatabaseIndexingUtils {
         return isPrivilegedPackage(packageName, context);
     }
 
-    public static boolean isPrivilegedPackage(String packageName, Context context) {
-        final PackageManager pm = context.getPackageManager();
-        try {
-            PackageInfo packInfo = pm.getPackageInfo(packageName, 0);
-            return ((packInfo.applicationInfo.privateFlags
-                    & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0);
-        } catch (PackageManager.NameNotFoundException e) {
-            return false;
-        }
-    }
-
-    public static String normalizeHyphen(String input) {
+    static String normalizeHyphen(String input) {
         return (input != null) ? input.replaceAll(NON_BREAKING_HYPHEN, HYPHEN) : EMPTY;
     }
 
-    public static String normalizeString(String input) {
+    static String normalizeString(String input) {
         final String nohyphen = (input != null) ? input.replaceAll(HYPHEN, EMPTY) : EMPTY;
         final String normalized = Normalizer.normalize(nohyphen, Normalizer.Form.NFD);
 
         return REMOVE_DIACRITICALS_PATTERN.matcher(normalized).replaceAll("").toLowerCase();
     }
 
-    public static String normalizeKeywords(String input) {
+    static String normalizeKeywords(String input) {
         return (input != null) ? input.replaceAll(LIST_DELIMITERS, SPACE) : EMPTY;
     }
+
+    private static boolean isPrivilegedPackage(String packageName, Context context) {
+        final PackageManager pm = context.getPackageManager();
+        try {
+            PackageInfo packInfo = pm.getPackageInfo(packageName, 0);
+            return ((packInfo.applicationInfo.privateFlags
+                    & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0);
+        } catch (PackageManager.NameNotFoundException e) {
+            return false;
+        }
+    }
 }
index 76346ec..d78b611 100644 (file)
 package com.android.settings.search;
 
 import android.content.Context;
+import android.content.pm.ResolveInfo;
 import android.database.Cursor;
 import android.database.sqlite.SQLiteDatabase;
 import android.database.sqlite.SQLiteOpenHelper;
 import android.os.Build;
+import android.support.annotation.VisibleForTesting;
+import android.text.TextUtils;
 import android.util.Log;
 
+import java.util.List;
+
 public class IndexDatabaseHelper extends SQLiteOpenHelper {
 
     private static final String TAG = "IndexDatabaseHelper";
@@ -32,6 +37,8 @@ public class IndexDatabaseHelper extends SQLiteOpenHelper {
 
     private static final String INDEX = "index";
 
+    private static final String PREF_KEY_INDEXED_PROVIDERS = "indexed_providers";
+
     public interface Tables {
         String TABLE_PREFS_INDEX = "prefs_index";
         String TABLE_SITE_MAP = "site_map";
@@ -245,23 +252,69 @@ public class IndexDatabaseHelper extends SQLiteOpenHelper {
         return version;
     }
 
-    public static void clearCachedIndexed(Context context) {
-        context.getSharedPreferences(INDEX, 0).edit().clear().commit();
+    /**
+     * Perform a full index on an OTA or when the locale has changed
+     *
+     * @param locale      is the default for the device
+     * @param fingerprint id for the current build.
+     * @return true when the locale or build has changed since last index.
+     */
+    @VisibleForTesting
+    static boolean isFullIndex(Context context, String locale, String fingerprint,
+            String providerVersionedNames) {
+        final boolean isLocaleIndexed = IndexDatabaseHelper.isLocaleAlreadyIndexed(context, locale);
+        final boolean isBuildIndexed = IndexDatabaseHelper.isBuildIndexed(context, fingerprint);
+        final boolean areProvidersIndexed = IndexDatabaseHelper
+                .areProvidersIndexed(context, providerVersionedNames);
+
+        return !(isLocaleIndexed && isBuildIndexed && areProvidersIndexed);
+    }
+
+    @VisibleForTesting
+    static String buildProviderVersionedNames(List<ResolveInfo> providers) {
+        StringBuilder sb = new StringBuilder();
+        for (ResolveInfo info : providers) {
+            sb.append(info.providerInfo.packageName)
+                    .append(':')
+                    .append(info.providerInfo.applicationInfo.versionCode)
+                    .append(',');
+        }
+        return sb.toString();
+    }
+
+    static void clearCachedIndexed(Context context) {
+        context.getSharedPreferences(INDEX, Context.MODE_PRIVATE).edit().clear().commit();
+    }
+
+    static void setLocaleIndexed(Context context, String locale) {
+        context.getSharedPreferences(INDEX, Context.MODE_PRIVATE)
+                .edit()
+                .putBoolean(locale, true)
+                .apply();
+    }
+
+    static void setProvidersIndexed(Context context, String providerVersionedNames) {
+        context.getSharedPreferences(INDEX, Context.MODE_PRIVATE)
+                .edit()
+                .putString(PREF_KEY_INDEXED_PROVIDERS, providerVersionedNames)
+                .apply();
     }
 
-    public static void setLocaleIndexed(Context context, String locale) {
-        context.getSharedPreferences(INDEX, 0).edit().putBoolean(locale, true).commit();
+    static boolean isLocaleAlreadyIndexed(Context context, String locale) {
+        return context.getSharedPreferences(INDEX, Context.MODE_PRIVATE).getBoolean(locale, false);
     }
 
-    public static boolean isLocaleAlreadyIndexed(Context context, String locale) {
-        return context.getSharedPreferences(INDEX, 0).getBoolean(locale, false);
+    static boolean areProvidersIndexed(Context context, String providerVersionedNames) {
+        final String indexedProviders = context.getSharedPreferences(INDEX, Context.MODE_PRIVATE)
+                .getString(PREF_KEY_INDEXED_PROVIDERS, null);
+        return TextUtils.equals(indexedProviders, providerVersionedNames);
     }
 
-    public static boolean isBuildIndexed(Context context, String buildNo) {
-        return context.getSharedPreferences(INDEX, 0).getBoolean(buildNo, false);
+    static boolean isBuildIndexed(Context context, String buildNo) {
+        return context.getSharedPreferences(INDEX, Context.MODE_PRIVATE).getBoolean(buildNo, false);
     }
 
-    public static void setBuildIndexed(Context context, String buildNo) {
+    static void setBuildIndexed(Context context, String buildNo) {
         context.getSharedPreferences(INDEX, 0).edit().putBoolean(buildNo, true).commit();
     }
 
index 711b355..3641368 100644 (file)
 
 package com.android.settings.search;
 
+import static android.provider.SearchIndexablesContract.INDEXABLES_RAW_COLUMNS;
+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.anyList;
+import static org.mockito.Matchers.anyMap;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+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.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.ContentProvider;
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ProviderInfo;
 import android.content.pm.ResolveInfo;
@@ -35,10 +51,10 @@ import android.provider.SearchIndexableResource;
 import android.util.ArrayMap;
 
 import com.android.settings.R;
-import com.android.settings.testutils.FakeFeatureFactory;
-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.ShadowDatabaseIndexingUtils;
 import com.android.settings.testutils.shadow.ShadowRunnableAsyncTask;
 
@@ -62,21 +78,6 @@ import java.util.Locale;
 import java.util.Map;
 import java.util.Set;
 
-import static android.provider.SearchIndexablesContract.INDEXABLES_RAW_COLUMNS;
-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.anyList;
-import static org.mockito.Matchers.anyMap;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
-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 = {ShadowRunnableAsyncTask.class})
@@ -746,7 +747,7 @@ public class DatabaseIndexingManagerTest {
     // Test new public indexing flow
 
     @Test
-    @Config(shadows = {ShadowDatabaseIndexingUtils.class,})
+    @Config(shadows = {ShadowDatabaseIndexingUtils.class})
     public void testPerformIndexing_fullIndex_getsDataFromProviders() {
         DummyProvider provider = new DummyProvider();
         provider.onCreate();
@@ -758,7 +759,6 @@ public class DatabaseIndexingManagerTest {
 
         DatabaseIndexingManager manager =
                 spy(new DatabaseIndexingManager(mContext, PACKAGE_ONE));
-        doReturn(true).when(manager).isFullIndex(anyString(), anyString());
 
         manager.performIndexing();
 
@@ -769,17 +769,17 @@ public class DatabaseIndexingManagerTest {
     @Test
     @Config(shadows = {ShadowDatabaseIndexingUtils.class,})
     public void testPerformIndexing_incrementalIndex_noDataAdded() {
+        final List<ResolveInfo> providerInfo = getDummyResolveInfo();
+        skipFullIndex(providerInfo);
         DummyProvider provider = new DummyProvider();
         provider.onCreate();
         ShadowContentResolver.registerProvider(AUTHORITY_ONE, provider);
-
         // Test that Indexables are added for Full indexing
         when(mPackageManager.queryIntentContentProviders(any(Intent.class), anyInt()))
-                .thenReturn(getDummyResolveInfo());
+                .thenReturn(providerInfo);
 
         DatabaseIndexingManager manager =
                 spy(new DatabaseIndexingManager(mContext, PACKAGE_ONE));
-        doReturn(false).when(manager).isFullIndex(anyString(), anyString());
 
         manager.mDataToProcess.dataToUpdate.clear();
 
@@ -805,7 +805,6 @@ public class DatabaseIndexingManagerTest {
         // Initialize the Manager
         DatabaseIndexingManager manager =
                 spy(new DatabaseIndexingManager(mContext, PACKAGE_ONE));
-        doReturn(true).when(manager).isFullIndex(anyString(), anyString());
 
         // Insert data point which will be dropped
         final String oldTitle = "This is French";
@@ -845,7 +844,6 @@ public class DatabaseIndexingManagerTest {
 
         DatabaseIndexingManager manager =
                 spy(new DatabaseIndexingManager(mContext, PACKAGE_ONE));
-        doReturn(true).when(manager).isFullIndex(anyString(), anyString());
 
         manager.performIndexing();
 
@@ -854,6 +852,28 @@ public class DatabaseIndexingManagerTest {
 
     @Test
     @Config(shadows = {ShadowDatabaseIndexingUtils.class,})
+    public void testPerformIndexing_onPackageChange_shouldFullIndex() {
+        final List<ResolveInfo> providers = getDummyResolveInfo();
+        final String buildNumber = Build.FINGERPRINT;
+        final String locale = Locale.getDefault().toString();
+        skipFullIndex(providers);
+
+        // This snapshot is already indexed. Should return false
+        assertThat(IndexDatabaseHelper.isFullIndex(
+                mContext, locale, buildNumber,
+                IndexDatabaseHelper.buildProviderVersionedNames(providers)))
+                .isFalse();
+
+        // Change provider version number, this should trigger full index.
+        providers.get(0).providerInfo.applicationInfo.versionCode++;
+
+        assertThat(IndexDatabaseHelper.isFullIndex(mContext, locale, buildNumber,
+                IndexDatabaseHelper.buildProviderVersionedNames(providers)))
+                .isTrue();
+    }
+
+    @Test
+    @Config(shadows = {ShadowDatabaseIndexingUtils.class,})
     public void testPerformIndexing_onOta_buildNumberIsCached() {
         DummyProvider provider = new DummyProvider();
         provider.onCreate();
@@ -867,7 +887,6 @@ public class DatabaseIndexingManagerTest {
 
         DatabaseIndexingManager manager =
                 spy(new DatabaseIndexingManager(mContext, PACKAGE_ONE));
-        doReturn(true).when(manager).isFullIndex(anyString(), anyString());
 
         manager.performIndexing();
 
@@ -1037,6 +1056,13 @@ public class DatabaseIndexingManagerTest {
 
     // Util functions
 
+    private void skipFullIndex(List<ResolveInfo> providers) {
+        IndexDatabaseHelper.setLocaleIndexed(mContext, Locale.getDefault().toString());
+        IndexDatabaseHelper.setBuildIndexed(mContext, Build.FINGERPRINT);
+        IndexDatabaseHelper.setProvidersIndexed(mContext,
+                IndexDatabaseHelper.buildProviderVersionedNames(providers));
+    }
+
     private SearchIndexableRaw getFakeRaw() {
         return getFakeRaw(localeStr);
     }
@@ -1092,6 +1118,7 @@ public class DatabaseIndexingManagerTest {
         info.providerInfo.exported = true;
         info.providerInfo.authority = AUTHORITY_ONE;
         info.providerInfo.packageName = PACKAGE_ONE;
+        info.providerInfo.applicationInfo = new ApplicationInfo();
         infoList.add(info);
 
         return infoList;