Reindex db when package w/ searchIndexProvider changes
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
This commit is contained in:
@@ -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 {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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,7 +197,22 @@ public class DatabaseIndexingUtils {
|
||||
return isPrivilegedPackage(packageName, context);
|
||||
}
|
||||
|
||||
public static boolean isPrivilegedPackage(String packageName, Context context) {
|
||||
static String normalizeHyphen(String input) {
|
||||
return (input != null) ? input.replaceAll(NON_BREAKING_HYPHEN, HYPHEN) : EMPTY;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -207,19 +222,4 @@ public class DatabaseIndexingUtils {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static String normalizeHyphen(String input) {
|
||||
return (input != null) ? input.replaceAll(NON_BREAKING_HYPHEN, HYPHEN) : EMPTY;
|
||||
}
|
||||
|
||||
public 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) {
|
||||
return (input != null) ? input.replaceAll(LIST_DELIMITERS, SPACE) : EMPTY;
|
||||
}
|
||||
}
|
||||
|
@@ -17,12 +17,17 @@
|
||||
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);
|
||||
}
|
||||
|
||||
public static void setLocaleIndexed(Context context, String locale) {
|
||||
context.getSharedPreferences(INDEX, 0).edit().putBoolean(locale, true).commit();
|
||||
@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();
|
||||
}
|
||||
|
||||
public static boolean isLocaleAlreadyIndexed(Context context, String locale) {
|
||||
return context.getSharedPreferences(INDEX, 0).getBoolean(locale, false);
|
||||
static void clearCachedIndexed(Context context) {
|
||||
context.getSharedPreferences(INDEX, Context.MODE_PRIVATE).edit().clear().commit();
|
||||
}
|
||||
|
||||
public static boolean isBuildIndexed(Context context, String buildNo) {
|
||||
return context.getSharedPreferences(INDEX, 0).getBoolean(buildNo, false);
|
||||
static void setLocaleIndexed(Context context, String locale) {
|
||||
context.getSharedPreferences(INDEX, Context.MODE_PRIVATE)
|
||||
.edit()
|
||||
.putBoolean(locale, true)
|
||||
.apply();
|
||||
}
|
||||
|
||||
public static void setBuildIndexed(Context context, String buildNo) {
|
||||
static void setProvidersIndexed(Context context, String providerVersionedNames) {
|
||||
context.getSharedPreferences(INDEX, Context.MODE_PRIVATE)
|
||||
.edit()
|
||||
.putString(PREF_KEY_INDEXED_PROVIDERS, providerVersionedNames)
|
||||
.apply();
|
||||
}
|
||||
|
||||
static boolean isLocaleAlreadyIndexed(Context context, String locale) {
|
||||
return context.getSharedPreferences(INDEX, Context.MODE_PRIVATE).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);
|
||||
}
|
||||
|
||||
static boolean isBuildIndexed(Context context, String buildNo) {
|
||||
return context.getSharedPreferences(INDEX, Context.MODE_PRIVATE).getBoolean(buildNo, false);
|
||||
}
|
||||
|
||||
static void setBuildIndexed(Context context, String buildNo) {
|
||||
context.getSharedPreferences(INDEX, 0).edit().putBoolean(buildNo, true).commit();
|
||||
}
|
||||
|
||||
|
@@ -17,12 +17,28 @@
|
||||
|
||||
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,13 +844,34 @@ public class DatabaseIndexingManagerTest {
|
||||
|
||||
DatabaseIndexingManager manager =
|
||||
spy(new DatabaseIndexingManager(mContext, PACKAGE_ONE));
|
||||
doReturn(true).when(manager).isFullIndex(anyString(), anyString());
|
||||
|
||||
manager.performIndexing();
|
||||
|
||||
verify(manager).updateDatabase(true /* isFullIndex */, Locale.getDefault().toString());
|
||||
}
|
||||
|
||||
@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() {
|
||||
@@ -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;
|
||||
|
Reference in New Issue
Block a user