Merge "Separate collection of indexable data from indexing"
This commit is contained in:
committed by
Android (Google) Code Review
commit
6f7f25e3fe
@@ -17,27 +17,6 @@
|
|||||||
|
|
||||||
package com.android.settings.search;
|
package com.android.settings.search;
|
||||||
|
|
||||||
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_NON_INDEXABLE_KEYS_KEY_VALUE;
|
|
||||||
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_CLASS_NAME;
|
|
||||||
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_ENTRIES;
|
|
||||||
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_ICON_RESID;
|
|
||||||
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_INTENT_ACTION;
|
|
||||||
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_INTENT_TARGET_CLASS;
|
|
||||||
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_INTENT_TARGET_PACKAGE;
|
|
||||||
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_KEY;
|
|
||||||
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_KEYWORDS;
|
|
||||||
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_RANK;
|
|
||||||
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_SCREEN_TITLE;
|
|
||||||
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_SUMMARY_OFF;
|
|
||||||
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_SUMMARY_ON;
|
|
||||||
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_TITLE;
|
|
||||||
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_USER_ID;
|
|
||||||
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_CLASS_NAME;
|
|
||||||
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_ICON_RESID;
|
|
||||||
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_INTENT_ACTION;
|
|
||||||
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_INTENT_TARGET_CLASS;
|
|
||||||
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_INTENT_TARGET_PACKAGE;
|
|
||||||
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_RESID;
|
|
||||||
import static com.android.settings.search.DatabaseResultLoader.COLUMN_INDEX_ID;
|
import static com.android.settings.search.DatabaseResultLoader.COLUMN_INDEX_ID;
|
||||||
import static com.android.settings.search.DatabaseResultLoader
|
import static com.android.settings.search.DatabaseResultLoader
|
||||||
.COLUMN_INDEX_INTENT_ACTION_TARGET_PACKAGE;
|
.COLUMN_INDEX_INTENT_ACTION_TARGET_PACKAGE;
|
||||||
@@ -70,17 +49,14 @@ import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.USER_
|
|||||||
import static com.android.settings.search.IndexDatabaseHelper.Tables.TABLE_PREFS_INDEX;
|
import static com.android.settings.search.IndexDatabaseHelper.Tables.TABLE_PREFS_INDEX;
|
||||||
|
|
||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
import android.content.ContentResolver;
|
|
||||||
import android.content.ContentValues;
|
import android.content.ContentValues;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.pm.PackageManager;
|
|
||||||
import android.content.pm.ResolveInfo;
|
import android.content.pm.ResolveInfo;
|
||||||
import android.content.res.XmlResourceParser;
|
import android.content.res.XmlResourceParser;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.database.sqlite.SQLiteDatabase;
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
import android.database.sqlite.SQLiteException;
|
import android.database.sqlite.SQLiteException;
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.provider.SearchIndexableData;
|
import android.provider.SearchIndexableData;
|
||||||
@@ -89,7 +65,6 @@ import android.provider.SearchIndexablesContract;
|
|||||||
import android.support.annotation.DrawableRes;
|
import android.support.annotation.DrawableRes;
|
||||||
import android.support.annotation.VisibleForTesting;
|
import android.support.annotation.VisibleForTesting;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.ArraySet;
|
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.util.Xml;
|
import android.util.Xml;
|
||||||
@@ -98,13 +73,13 @@ import com.android.settings.SettingsActivity;
|
|||||||
import com.android.settings.core.PreferenceControllerMixin;
|
import com.android.settings.core.PreferenceControllerMixin;
|
||||||
import com.android.settings.overlay.FeatureFactory;
|
import com.android.settings.overlay.FeatureFactory;
|
||||||
|
|
||||||
|
import com.android.settings.search.indexing.IndexableDataCollector;
|
||||||
|
import com.android.settings.search.indexing.PreIndexData;
|
||||||
import org.xmlpull.v1.XmlPullParser;
|
import org.xmlpull.v1.XmlPullParser;
|
||||||
import org.xmlpull.v1.XmlPullParserException;
|
import org.xmlpull.v1.XmlPullParserException;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -132,23 +107,14 @@ public class DatabaseIndexingManager {
|
|||||||
private static final String NODE_NAME_CHECK_BOX_PREFERENCE = "CheckBoxPreference";
|
private static final String NODE_NAME_CHECK_BOX_PREFERENCE = "CheckBoxPreference";
|
||||||
private static final String NODE_NAME_LIST_PREFERENCE = "ListPreference";
|
private static final String NODE_NAME_LIST_PREFERENCE = "ListPreference";
|
||||||
|
|
||||||
private static final List<String> EMPTY_LIST = Collections.emptyList();
|
|
||||||
|
|
||||||
private final String mBaseAuthority;
|
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
final AtomicBoolean mIsIndexingComplete = new AtomicBoolean(false);
|
final AtomicBoolean mIsIndexingComplete = new AtomicBoolean(false);
|
||||||
|
|
||||||
@VisibleForTesting
|
private IndexableDataCollector mCollector;
|
||||||
final UpdateData mDataToProcess = new UpdateData();
|
|
||||||
private Context mContext;
|
private Context mContext;
|
||||||
|
|
||||||
public DatabaseIndexingManager(Context context, String baseAuthority) {
|
public DatabaseIndexingManager(Context context) {
|
||||||
mContext = context;
|
|
||||||
mBaseAuthority = baseAuthority;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setContext(Context context) {
|
|
||||||
mContext = context;
|
mContext = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -177,33 +143,17 @@ public class DatabaseIndexingManager {
|
|||||||
final String providerVersionedNames =
|
final String providerVersionedNames =
|
||||||
IndexDatabaseHelper.buildProviderVersionedNames(providers);
|
IndexDatabaseHelper.buildProviderVersionedNames(providers);
|
||||||
|
|
||||||
final boolean isFullIndex = IndexDatabaseHelper.isFullIndex(mContext, localeStr,
|
final boolean isFullIndex = isFullIndex(mContext, localeStr,
|
||||||
fingerprint, providerVersionedNames);
|
fingerprint, providerVersionedNames);
|
||||||
|
|
||||||
if (isFullIndex) {
|
if (isFullIndex) {
|
||||||
rebuildDatabase();
|
rebuildDatabase();
|
||||||
}
|
}
|
||||||
|
|
||||||
for (final ResolveInfo info : providers) {
|
PreIndexData indexData = getIndexDataFromProviders(providers, isFullIndex);
|
||||||
if (!DatabaseIndexingUtils.isWellKnownProvider(info, mContext)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
final String authority = info.providerInfo.authority;
|
|
||||||
final String packageName = info.providerInfo.packageName;
|
|
||||||
|
|
||||||
if (isFullIndex) {
|
|
||||||
addIndexablesFromRemoteProvider(packageName, authority);
|
|
||||||
}
|
|
||||||
final long nonIndexableStartTime = System.currentTimeMillis();
|
|
||||||
addNonIndexablesKeysFromRemoteProvider(packageName, authority);
|
|
||||||
if (SettingsSearchIndexablesProvider.DEBUG) {
|
|
||||||
final long nonIndextableTime = System.currentTimeMillis() - nonIndexableStartTime;
|
|
||||||
Log.d(LOG_TAG, "performIndexing update non-indexable for package " + packageName
|
|
||||||
+ " took time: " + nonIndextableTime);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
final long updateDatabaseStartTime = System.currentTimeMillis();
|
final long updateDatabaseStartTime = System.currentTimeMillis();
|
||||||
updateDatabase(isFullIndex, localeStr);
|
updateDatabase(indexData, isFullIndex, localeStr);
|
||||||
if (SettingsSearchIndexablesProvider.DEBUG) {
|
if (SettingsSearchIndexablesProvider.DEBUG) {
|
||||||
final long updateDatabaseTime = System.currentTimeMillis() - updateDatabaseStartTime;
|
final long updateDatabaseTime = System.currentTimeMillis() - updateDatabaseStartTime;
|
||||||
Log.d(LOG_TAG, "performIndexing updateDatabase took time: " + updateDatabaseTime);
|
Log.d(LOG_TAG, "performIndexing updateDatabase took time: " + updateDatabaseTime);
|
||||||
@@ -221,10 +171,37 @@ public class DatabaseIndexingManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
PreIndexData getIndexDataFromProviders(List<ResolveInfo> providers, boolean isFullIndex) {
|
||||||
|
if (mCollector == null) {
|
||||||
|
mCollector = new IndexableDataCollector(mContext);
|
||||||
|
}
|
||||||
|
return mCollector.collectIndexableData(providers, isFullIndex);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reconstruct the database in the following cases:
|
* Checks if the indexed data is obsolete, when either:
|
||||||
* - Language has changed
|
* - Device language has changed
|
||||||
* - Build has changed
|
* - Device has taken an OTA.
|
||||||
|
* In both cases, the device requires a full index.
|
||||||
|
*
|
||||||
|
* @param locale is the default for the device
|
||||||
|
* @param fingerprint id for the current build.
|
||||||
|
* @return true if a full index should be preformed.
|
||||||
|
*/
|
||||||
|
@VisibleForTesting
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Drop the currently stored database, and clear the flags which mark the database as indexed.
|
||||||
*/
|
*/
|
||||||
private void rebuildDatabase() {
|
private void rebuildDatabase() {
|
||||||
// Drop the database when the locale or build has changed. This eliminates rows which are
|
// Drop the database when the locale or build has changed. This eliminates rows which are
|
||||||
@@ -244,16 +221,9 @@ public class DatabaseIndexingManager {
|
|||||||
* @param localeStr the default locale for the device.
|
* @param localeStr the default locale for the device.
|
||||||
*/
|
*/
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
void updateDatabase(boolean needsReindexing, String localeStr) {
|
void updateDatabase(PreIndexData indexData, boolean needsReindexing, String localeStr) {
|
||||||
final UpdateData copy;
|
final List<SearchIndexableData> dataToUpdate = indexData.dataToUpdate;
|
||||||
|
final Map<String, Set<String>> nonIndexableKeys = indexData.nonIndexableKeys;
|
||||||
synchronized (mDataToProcess) {
|
|
||||||
copy = mDataToProcess.copy();
|
|
||||||
mDataToProcess.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
final List<SearchIndexableData> dataToUpdate = copy.dataToUpdate;
|
|
||||||
final Map<String, Set<String>> nonIndexableKeys = copy.nonIndexableKeys;
|
|
||||||
|
|
||||||
final SQLiteDatabase database = getWritableDatabase();
|
final SQLiteDatabase database = getWritableDatabase();
|
||||||
if (database == null) {
|
if (database == null) {
|
||||||
@@ -378,99 +348,10 @@ public class DatabaseIndexingManager {
|
|||||||
disabledResults.close();
|
disabledResults.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
boolean addIndexablesFromRemoteProvider(String packageName, String authority) {
|
|
||||||
try {
|
|
||||||
final Context context = mBaseAuthority.equals(authority) ?
|
|
||||||
mContext : mContext.createPackageContext(packageName, 0);
|
|
||||||
|
|
||||||
final Uri uriForResources = buildUriForXmlResources(authority);
|
|
||||||
addIndexablesForXmlResourceUri(context, packageName, uriForResources,
|
|
||||||
SearchIndexablesContract.INDEXABLES_XML_RES_COLUMNS);
|
|
||||||
|
|
||||||
final Uri uriForRawData = buildUriForRawData(authority);
|
|
||||||
addIndexablesForRawDataUri(context, packageName, uriForRawData,
|
|
||||||
SearchIndexablesContract.INDEXABLES_RAW_COLUMNS);
|
|
||||||
return true;
|
|
||||||
} catch (PackageManager.NameNotFoundException e) {
|
|
||||||
Log.w(LOG_TAG, "Could not create context for " + packageName + ": "
|
|
||||||
+ Log.getStackTraceString(e));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
void addNonIndexablesKeysFromRemoteProvider(String packageName,
|
|
||||||
String authority) {
|
|
||||||
final List<String> keys =
|
|
||||||
getNonIndexablesKeysFromRemoteProvider(packageName, authority);
|
|
||||||
|
|
||||||
addNonIndexableKeys(packageName, keys);
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<String> getNonIndexablesKeysFromRemoteProvider(String packageName,
|
|
||||||
String authority) {
|
|
||||||
try {
|
|
||||||
final Context packageContext = mContext.createPackageContext(packageName, 0);
|
|
||||||
|
|
||||||
final Uri uriForNonIndexableKeys = buildUriForNonIndexableKeys(authority);
|
|
||||||
return getNonIndexablesKeys(packageContext, uriForNonIndexableKeys,
|
|
||||||
SearchIndexablesContract.NON_INDEXABLES_KEYS_COLUMNS);
|
|
||||||
} catch (PackageManager.NameNotFoundException e) {
|
|
||||||
Log.w(LOG_TAG, "Could not create context for " + packageName + ": "
|
|
||||||
+ Log.getStackTraceString(e));
|
|
||||||
return EMPTY_LIST;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<String> getNonIndexablesKeys(Context packageContext, Uri uri,
|
|
||||||
String[] projection) {
|
|
||||||
|
|
||||||
final ContentResolver resolver = packageContext.getContentResolver();
|
|
||||||
final Cursor cursor = resolver.query(uri, projection, null, null, null);
|
|
||||||
|
|
||||||
if (cursor == null) {
|
|
||||||
Log.w(LOG_TAG, "Cannot add index data for Uri: " + uri.toString());
|
|
||||||
return EMPTY_LIST;
|
|
||||||
}
|
|
||||||
|
|
||||||
final List<String> result = new ArrayList<>();
|
|
||||||
try {
|
|
||||||
final int count = cursor.getCount();
|
|
||||||
if (count > 0) {
|
|
||||||
while (cursor.moveToNext()) {
|
|
||||||
final String key = cursor.getString(COLUMN_INDEX_NON_INDEXABLE_KEYS_KEY_VALUE);
|
|
||||||
|
|
||||||
if (TextUtils.isEmpty(key) && Log.isLoggable(LOG_TAG, Log.VERBOSE)) {
|
|
||||||
Log.v(LOG_TAG, "Empty non-indexable key from: "
|
|
||||||
+ packageContext.getPackageName());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
result.add(key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
} finally {
|
|
||||||
cursor.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addIndexableData(SearchIndexableData data) {
|
|
||||||
synchronized (mDataToProcess) {
|
|
||||||
mDataToProcess.dataToUpdate.add(data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addNonIndexableKeys(String authority, List<String> keys) {
|
|
||||||
synchronized (mDataToProcess) {
|
|
||||||
if (keys != null && !keys.isEmpty()) {
|
|
||||||
mDataToProcess.nonIndexableKeys.put(authority, new ArraySet<>(keys));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* TODO (b/64951285): Deprecate this method
|
||||||
|
*
|
||||||
* Update the Index for a specific class name resources
|
* Update the Index for a specific class name resources
|
||||||
*
|
*
|
||||||
* @param className the class name (typically a fragment name).
|
* @param className the class name (typically a fragment name).
|
||||||
@@ -491,9 +372,9 @@ public class DatabaseIndexingManager {
|
|||||||
AsyncTask.execute(new Runnable() {
|
AsyncTask.execute(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
addIndexableData(res);
|
// addIndexableData(res);
|
||||||
updateDatabase(false, Locale.getDefault().toString());
|
// updateDatabase(false, Locale.getDefault().toString());
|
||||||
res.enabled = false;
|
// res.enabled = false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -507,126 +388,9 @@ public class DatabaseIndexingManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Uri buildUriForXmlResources(String authority) {
|
|
||||||
return Uri.parse("content://" + authority + "/" +
|
|
||||||
SearchIndexablesContract.INDEXABLES_XML_RES_PATH);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Uri buildUriForRawData(String authority) {
|
@VisibleForTesting
|
||||||
return Uri.parse("content://" + authority + "/" +
|
void indexOneSearchIndexableData(SQLiteDatabase database, String localeStr,
|
||||||
SearchIndexablesContract.INDEXABLES_RAW_PATH);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Uri buildUriForNonIndexableKeys(String authority) {
|
|
||||||
return Uri.parse("content://" + authority + "/" +
|
|
||||||
SearchIndexablesContract.NON_INDEXABLES_KEYS_PATH);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addIndexablesForXmlResourceUri(Context packageContext, String packageName,
|
|
||||||
Uri uri, String[] projection) {
|
|
||||||
|
|
||||||
final ContentResolver resolver = packageContext.getContentResolver();
|
|
||||||
final Cursor cursor = resolver.query(uri, projection, null, null, null);
|
|
||||||
|
|
||||||
if (cursor == null) {
|
|
||||||
Log.w(LOG_TAG, "Cannot add index data for Uri: " + uri.toString());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
final int count = cursor.getCount();
|
|
||||||
if (count > 0) {
|
|
||||||
while (cursor.moveToNext()) {
|
|
||||||
final int xmlResId = cursor.getInt(COLUMN_INDEX_XML_RES_RESID);
|
|
||||||
|
|
||||||
final String className = cursor.getString(COLUMN_INDEX_XML_RES_CLASS_NAME);
|
|
||||||
final int iconResId = cursor.getInt(COLUMN_INDEX_XML_RES_ICON_RESID);
|
|
||||||
|
|
||||||
final String action = cursor.getString(COLUMN_INDEX_XML_RES_INTENT_ACTION);
|
|
||||||
final String targetPackage = cursor.getString(
|
|
||||||
COLUMN_INDEX_XML_RES_INTENT_TARGET_PACKAGE);
|
|
||||||
final String targetClass = cursor.getString(
|
|
||||||
COLUMN_INDEX_XML_RES_INTENT_TARGET_CLASS);
|
|
||||||
|
|
||||||
SearchIndexableResource sir = new SearchIndexableResource(packageContext);
|
|
||||||
sir.xmlResId = xmlResId;
|
|
||||||
sir.className = className;
|
|
||||||
sir.packageName = packageName;
|
|
||||||
sir.iconResId = iconResId;
|
|
||||||
sir.intentAction = action;
|
|
||||||
sir.intentTargetPackage = targetPackage;
|
|
||||||
sir.intentTargetClass = targetClass;
|
|
||||||
|
|
||||||
addIndexableData(sir);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
cursor.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addIndexablesForRawDataUri(Context packageContext, String packageName,
|
|
||||||
Uri uri, String[] projection) {
|
|
||||||
|
|
||||||
final ContentResolver resolver = packageContext.getContentResolver();
|
|
||||||
final Cursor cursor = resolver.query(uri, projection, null, null, null);
|
|
||||||
|
|
||||||
if (cursor == null) {
|
|
||||||
Log.w(LOG_TAG, "Cannot add index data for Uri: " + uri.toString());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
final int count = cursor.getCount();
|
|
||||||
if (count > 0) {
|
|
||||||
while (cursor.moveToNext()) {
|
|
||||||
final int providerRank = cursor.getInt(COLUMN_INDEX_RAW_RANK);
|
|
||||||
// TODO Remove rank
|
|
||||||
final String title = cursor.getString(COLUMN_INDEX_RAW_TITLE);
|
|
||||||
final String summaryOn = cursor.getString(COLUMN_INDEX_RAW_SUMMARY_ON);
|
|
||||||
final String summaryOff = cursor.getString(COLUMN_INDEX_RAW_SUMMARY_OFF);
|
|
||||||
final String entries = cursor.getString(COLUMN_INDEX_RAW_ENTRIES);
|
|
||||||
final String keywords = cursor.getString(COLUMN_INDEX_RAW_KEYWORDS);
|
|
||||||
|
|
||||||
final String screenTitle = cursor.getString(COLUMN_INDEX_RAW_SCREEN_TITLE);
|
|
||||||
|
|
||||||
final String className = cursor.getString(COLUMN_INDEX_RAW_CLASS_NAME);
|
|
||||||
final int iconResId = cursor.getInt(COLUMN_INDEX_RAW_ICON_RESID);
|
|
||||||
|
|
||||||
final String action = cursor.getString(COLUMN_INDEX_RAW_INTENT_ACTION);
|
|
||||||
final String targetPackage = cursor.getString(
|
|
||||||
COLUMN_INDEX_RAW_INTENT_TARGET_PACKAGE);
|
|
||||||
final String targetClass = cursor.getString(
|
|
||||||
COLUMN_INDEX_RAW_INTENT_TARGET_CLASS);
|
|
||||||
|
|
||||||
final String key = cursor.getString(COLUMN_INDEX_RAW_KEY);
|
|
||||||
final int userId = cursor.getInt(COLUMN_INDEX_RAW_USER_ID);
|
|
||||||
|
|
||||||
SearchIndexableRaw data = new SearchIndexableRaw(packageContext);
|
|
||||||
data.title = title;
|
|
||||||
data.summaryOn = summaryOn;
|
|
||||||
data.summaryOff = summaryOff;
|
|
||||||
data.entries = entries;
|
|
||||||
data.keywords = keywords;
|
|
||||||
data.screenTitle = screenTitle;
|
|
||||||
data.className = className;
|
|
||||||
data.packageName = packageName;
|
|
||||||
data.iconResId = iconResId;
|
|
||||||
data.intentAction = action;
|
|
||||||
data.intentTargetPackage = targetPackage;
|
|
||||||
data.intentTargetClass = targetClass;
|
|
||||||
data.key = key;
|
|
||||||
data.userId = userId;
|
|
||||||
|
|
||||||
addIndexableData(data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
cursor.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void indexOneSearchIndexableData(SQLiteDatabase database, String localeStr,
|
|
||||||
SearchIndexableData data, Map<String, Set<String>> nonIndexableKeys) {
|
SearchIndexableData data, Map<String, Set<String>> nonIndexableKeys) {
|
||||||
if (data instanceof SearchIndexableResource) {
|
if (data instanceof SearchIndexableResource) {
|
||||||
indexOneResource(database, localeStr, (SearchIndexableResource) data, nonIndexableKeys);
|
indexOneResource(database, localeStr, (SearchIndexableResource) data, nonIndexableKeys);
|
||||||
@@ -1010,38 +774,6 @@ public class DatabaseIndexingManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* A private class to describe the indexDatabase data for the Index database
|
|
||||||
*/
|
|
||||||
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
|
|
||||||
static class UpdateData {
|
|
||||||
public List<SearchIndexableData> dataToUpdate;
|
|
||||||
public List<SearchIndexableData> dataToDisable;
|
|
||||||
public Map<String, Set<String>> nonIndexableKeys;
|
|
||||||
|
|
||||||
public UpdateData() {
|
|
||||||
dataToUpdate = new ArrayList<>();
|
|
||||||
dataToDisable = new ArrayList<>();
|
|
||||||
nonIndexableKeys = new HashMap<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public UpdateData(UpdateData other) {
|
|
||||||
dataToUpdate = new ArrayList<>(other.dataToUpdate);
|
|
||||||
dataToDisable = new ArrayList<>(other.dataToDisable);
|
|
||||||
nonIndexableKeys = new HashMap<>(other.nonIndexableKeys);
|
|
||||||
}
|
|
||||||
|
|
||||||
public UpdateData copy() {
|
|
||||||
return new UpdateData(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void clear() {
|
|
||||||
dataToUpdate.clear();
|
|
||||||
dataToDisable.clear();
|
|
||||||
nonIndexableKeys.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class DatabaseRow {
|
public static class DatabaseRow {
|
||||||
public final String locale;
|
public final String locale;
|
||||||
public final String updatedTitle;
|
public final String updatedTitle;
|
||||||
|
|||||||
@@ -174,35 +174,6 @@ public class DatabaseIndexingUtils {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Only allow a "well known" SearchIndexablesProvider. The provider should:
|
|
||||||
*
|
|
||||||
* - have read/write {@link Manifest.permission#READ_SEARCH_INDEXABLES}
|
|
||||||
* - be from a privileged package
|
|
||||||
*/
|
|
||||||
static boolean isWellKnownProvider(ResolveInfo info, Context context) {
|
|
||||||
final String authority = info.providerInfo.authority;
|
|
||||||
final String packageName = info.providerInfo.applicationInfo.packageName;
|
|
||||||
|
|
||||||
if (TextUtils.isEmpty(authority) || TextUtils.isEmpty(packageName)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
final String readPermission = info.providerInfo.readPermission;
|
|
||||||
final String writePermission = info.providerInfo.writePermission;
|
|
||||||
|
|
||||||
if (TextUtils.isEmpty(readPermission) || TextUtils.isEmpty(writePermission)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!android.Manifest.permission.READ_SEARCH_INDEXABLES.equals(readPermission) ||
|
|
||||||
!android.Manifest.permission.READ_SEARCH_INDEXABLES.equals(writePermission)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return isPrivilegedPackage(packageName, context);
|
|
||||||
}
|
|
||||||
|
|
||||||
static String normalizeHyphen(String input) {
|
static String normalizeHyphen(String input) {
|
||||||
return (input != null) ? input.replaceAll(NON_BREAKING_HYPHEN, HYPHEN) : EMPTY;
|
return (input != null) ? input.replaceAll(NON_BREAKING_HYPHEN, HYPHEN) : EMPTY;
|
||||||
}
|
}
|
||||||
@@ -217,15 +188,4 @@ public class DatabaseIndexingUtils {
|
|||||||
static String normalizeKeywords(String input) {
|
static String normalizeKeywords(String input) {
|
||||||
return (input != null) ? input.replaceAll(LIST_DELIMITERS, SPACE) : EMPTY;
|
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,19 +40,19 @@ public class DatabaseResultLoader extends AsyncLoader<Set<? extends SearchResult
|
|||||||
/* These indices are used to match the columns of the this loader's SELECT statement.
|
/* These indices are used to match the columns of the this loader's SELECT statement.
|
||||||
These are not necessarily the same order nor similar coverage as the schema defined in
|
These are not necessarily the same order nor similar coverage as the schema defined in
|
||||||
IndexDatabaseHelper */
|
IndexDatabaseHelper */
|
||||||
static final int COLUMN_INDEX_ID = 0;
|
public static final int COLUMN_INDEX_ID = 0;
|
||||||
static final int COLUMN_INDEX_TITLE = 1;
|
public static final int COLUMN_INDEX_TITLE = 1;
|
||||||
static final int COLUMN_INDEX_SUMMARY_ON = 2;
|
public static final int COLUMN_INDEX_SUMMARY_ON = 2;
|
||||||
static final int COLUMN_INDEX_SUMMARY_OFF = 3;
|
public static final int COLUMN_INDEX_SUMMARY_OFF = 3;
|
||||||
static final int COLUMN_INDEX_CLASS_NAME = 4;
|
public static final int COLUMN_INDEX_CLASS_NAME = 4;
|
||||||
static final int COLUMN_INDEX_SCREEN_TITLE = 5;
|
public static final int COLUMN_INDEX_SCREEN_TITLE = 5;
|
||||||
static final int COLUMN_INDEX_ICON = 6;
|
public static final int COLUMN_INDEX_ICON = 6;
|
||||||
static final int COLUMN_INDEX_INTENT_ACTION = 7;
|
public static final int COLUMN_INDEX_INTENT_ACTION = 7;
|
||||||
static final int COLUMN_INDEX_INTENT_ACTION_TARGET_PACKAGE = 8;
|
public static final int COLUMN_INDEX_INTENT_ACTION_TARGET_PACKAGE = 8;
|
||||||
static final int COLUMN_INDEX_INTENT_ACTION_TARGET_CLASS = 9;
|
public static final int COLUMN_INDEX_INTENT_ACTION_TARGET_CLASS = 9;
|
||||||
static final int COLUMN_INDEX_KEY = 10;
|
public static final int COLUMN_INDEX_KEY = 10;
|
||||||
static final int COLUMN_INDEX_PAYLOAD_TYPE = 11;
|
public static final int COLUMN_INDEX_PAYLOAD_TYPE = 11;
|
||||||
static final int COLUMN_INDEX_PAYLOAD = 12;
|
public static final int COLUMN_INDEX_PAYLOAD = 12;
|
||||||
|
|
||||||
public static final String[] SELECT_COLUMNS = {
|
public static final String[] SELECT_COLUMNS = {
|
||||||
IndexColumns.DOCID,
|
IndexColumns.DOCID,
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ public class IndexDatabaseHelper extends SQLiteOpenHelper {
|
|||||||
private static final String DATABASE_NAME = "search_index.db";
|
private static final String DATABASE_NAME = "search_index.db";
|
||||||
private static final int DATABASE_VERSION = 117;
|
private static final int DATABASE_VERSION = 117;
|
||||||
|
|
||||||
private static final String INDEX = "index";
|
private static final String SHARED_PREFS_TAG = "indexing_manager";
|
||||||
|
|
||||||
private static final String PREF_KEY_INDEXED_PROVIDERS = "indexed_providers";
|
private static final String PREF_KEY_INDEXED_PROVIDERS = "indexed_providers";
|
||||||
|
|
||||||
@@ -179,7 +179,7 @@ public class IndexDatabaseHelper extends SQLiteOpenHelper {
|
|||||||
|
|
||||||
public IndexDatabaseHelper(Context context) {
|
public IndexDatabaseHelper(Context context) {
|
||||||
super(context, DATABASE_NAME, null, DATABASE_VERSION);
|
super(context, DATABASE_NAME, null, DATABASE_VERSION);
|
||||||
mContext = context;
|
mContext = context.getApplicationContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -230,6 +230,10 @@ public class IndexDatabaseHelper extends SQLiteOpenHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void reconstruct(SQLiteDatabase db) {
|
public void reconstruct(SQLiteDatabase db) {
|
||||||
|
mContext.getSharedPreferences(SHARED_PREFS_TAG, Context.MODE_PRIVATE)
|
||||||
|
.edit()
|
||||||
|
.clear()
|
||||||
|
.commit();
|
||||||
dropTables(db);
|
dropTables(db);
|
||||||
bootstrapDB(db);
|
bootstrapDB(db);
|
||||||
}
|
}
|
||||||
@@ -252,24 +256,6 @@ public class IndexDatabaseHelper extends SQLiteOpenHelper {
|
|||||||
return version;
|
return version;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 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
|
@VisibleForTesting
|
||||||
static String buildProviderVersionedNames(List<ResolveInfo> providers) {
|
static String buildProviderVersionedNames(List<ResolveInfo> providers) {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
@@ -282,44 +268,42 @@ public class IndexDatabaseHelper extends SQLiteOpenHelper {
|
|||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void clearCachedIndexed(Context context) {
|
|
||||||
context.getSharedPreferences(INDEX, Context.MODE_PRIVATE).edit().clear().commit();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void setLocaleIndexed(Context context, String locale) {
|
static void setLocaleIndexed(Context context, String locale) {
|
||||||
context.getSharedPreferences(INDEX, Context.MODE_PRIVATE)
|
context.getSharedPreferences(SHARED_PREFS_TAG, Context.MODE_PRIVATE)
|
||||||
.edit()
|
.edit()
|
||||||
.putBoolean(locale, true)
|
.putBoolean(locale, true)
|
||||||
.apply();
|
.apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void setProvidersIndexed(Context context, String providerVersionedNames) {
|
static void setProvidersIndexed(Context context, String providerVersionedNames) {
|
||||||
context.getSharedPreferences(INDEX, Context.MODE_PRIVATE)
|
context.getSharedPreferences(SHARED_PREFS_TAG, Context.MODE_PRIVATE)
|
||||||
.edit()
|
.edit()
|
||||||
.putString(PREF_KEY_INDEXED_PROVIDERS, providerVersionedNames)
|
.putString(PREF_KEY_INDEXED_PROVIDERS, providerVersionedNames)
|
||||||
.apply();
|
.apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
static boolean isLocaleAlreadyIndexed(Context context, String locale) {
|
static boolean isLocaleAlreadyIndexed(Context context, String locale) {
|
||||||
return context.getSharedPreferences(INDEX, Context.MODE_PRIVATE).getBoolean(locale, false);
|
return context.getSharedPreferences(SHARED_PREFS_TAG, Context.MODE_PRIVATE)
|
||||||
|
.getBoolean(locale, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
static boolean areProvidersIndexed(Context context, String providerVersionedNames) {
|
static boolean areProvidersIndexed(Context context, String providerVersionedNames) {
|
||||||
final String indexedProviders = context.getSharedPreferences(INDEX, Context.MODE_PRIVATE)
|
final String indexedProviders =
|
||||||
.getString(PREF_KEY_INDEXED_PROVIDERS, null);
|
context.getSharedPreferences(SHARED_PREFS_TAG, Context.MODE_PRIVATE)
|
||||||
|
.getString(PREF_KEY_INDEXED_PROVIDERS, null);
|
||||||
return TextUtils.equals(indexedProviders, providerVersionedNames);
|
return TextUtils.equals(indexedProviders, providerVersionedNames);
|
||||||
}
|
}
|
||||||
|
|
||||||
static boolean isBuildIndexed(Context context, String buildNo) {
|
static boolean isBuildIndexed(Context context, String buildNo) {
|
||||||
return context.getSharedPreferences(INDEX, Context.MODE_PRIVATE).getBoolean(buildNo, false);
|
return context.getSharedPreferences(SHARED_PREFS_TAG,
|
||||||
|
Context.MODE_PRIVATE).getBoolean(buildNo, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void setBuildIndexed(Context context, String buildNo) {
|
static void setBuildIndexed(Context context, String buildNo) {
|
||||||
context.getSharedPreferences(INDEX, 0).edit().putBoolean(buildNo, true).commit();
|
context.getSharedPreferences(SHARED_PREFS_TAG, 0).edit().putBoolean(buildNo, true).commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void dropTables(SQLiteDatabase db) {
|
private void dropTables(SQLiteDatabase db) {
|
||||||
clearCachedIndexed(mContext);
|
|
||||||
db.execSQL("DROP TABLE IF EXISTS " + Tables.TABLE_META_INDEX);
|
db.execSQL("DROP TABLE IF EXISTS " + Tables.TABLE_META_INDEX);
|
||||||
db.execSQL("DROP TABLE IF EXISTS " + Tables.TABLE_PREFS_INDEX);
|
db.execSQL("DROP TABLE IF EXISTS " + Tables.TABLE_PREFS_INDEX);
|
||||||
db.execSQL("DROP TABLE IF EXISTS " + Tables.TABLE_SAVED_QUERIES);
|
db.execSQL("DROP TABLE IF EXISTS " + Tables.TABLE_SAVED_QUERIES);
|
||||||
|
|||||||
@@ -74,8 +74,7 @@ public class SearchFeatureProviderImpl implements SearchFeatureProvider {
|
|||||||
@Override
|
@Override
|
||||||
public DatabaseIndexingManager getIndexingManager(Context context) {
|
public DatabaseIndexingManager getIndexingManager(Context context) {
|
||||||
if (mDatabaseIndexingManager == null) {
|
if (mDatabaseIndexingManager == null) {
|
||||||
mDatabaseIndexingManager = new DatabaseIndexingManager(context.getApplicationContext(),
|
mDatabaseIndexingManager = new DatabaseIndexingManager(context.getApplicationContext());
|
||||||
context.getPackageName());
|
|
||||||
}
|
}
|
||||||
return mDatabaseIndexingManager;
|
return mDatabaseIndexingManager;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,361 @@
|
|||||||
|
/*
|
||||||
|
* 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.indexing;
|
||||||
|
|
||||||
|
import android.Manifest;
|
||||||
|
import android.content.ContentResolver;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.pm.ApplicationInfo;
|
||||||
|
import android.content.pm.PackageInfo;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.content.pm.ResolveInfo;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.provider.SearchIndexableResource;
|
||||||
|
import android.provider.SearchIndexablesContract;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.util.ArraySet;
|
||||||
|
import android.util.Log;
|
||||||
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
|
import com.android.settings.search.SearchIndexableRaw;
|
||||||
|
import com.android.settings.search.SettingsSearchIndexablesProvider;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_CLASS_NAME;
|
||||||
|
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_ICON_RESID;
|
||||||
|
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_RESID;
|
||||||
|
|
||||||
|
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_NON_INDEXABLE_KEYS_KEY_VALUE;
|
||||||
|
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_CLASS_NAME;
|
||||||
|
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_ENTRIES;
|
||||||
|
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_ICON_RESID;
|
||||||
|
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_INTENT_ACTION;
|
||||||
|
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_INTENT_TARGET_CLASS;
|
||||||
|
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_INTENT_TARGET_PACKAGE;
|
||||||
|
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_KEY;
|
||||||
|
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_KEYWORDS;
|
||||||
|
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_RANK;
|
||||||
|
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_SCREEN_TITLE;
|
||||||
|
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_SUMMARY_OFF;
|
||||||
|
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_SUMMARY_ON;
|
||||||
|
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_TITLE;
|
||||||
|
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_USER_ID;
|
||||||
|
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_INTENT_ACTION;
|
||||||
|
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_INTENT_TARGET_CLASS;
|
||||||
|
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_INTENT_TARGET_PACKAGE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collects all data from {@link android.provider.SearchIndexablesProvider} to be indexed.
|
||||||
|
*/
|
||||||
|
public class IndexableDataCollector {
|
||||||
|
|
||||||
|
private static final String TAG = "IndexableDataCollector";
|
||||||
|
|
||||||
|
// TODO (b/64938328) update to new search package.
|
||||||
|
private final String BASE_AUTHORITY = "com.android.settings";
|
||||||
|
|
||||||
|
private static final List<String> EMPTY_LIST = Collections.emptyList();
|
||||||
|
|
||||||
|
private Context mContext;
|
||||||
|
|
||||||
|
private PreIndexData mIndexData;
|
||||||
|
|
||||||
|
public IndexableDataCollector(Context context) {
|
||||||
|
mContext = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PreIndexData collectIndexableData(List<ResolveInfo> providers, boolean isFullIndex) {
|
||||||
|
mIndexData = new PreIndexData();
|
||||||
|
|
||||||
|
for (final ResolveInfo info : providers) {
|
||||||
|
if (!isWellKnownProvider(info)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
final String authority = info.providerInfo.authority;
|
||||||
|
final String packageName = info.providerInfo.packageName;
|
||||||
|
|
||||||
|
if (isFullIndex) {
|
||||||
|
addIndexablesFromRemoteProvider(packageName, authority);
|
||||||
|
}
|
||||||
|
|
||||||
|
final long nonIndexableStartTime = System.currentTimeMillis();
|
||||||
|
addNonIndexablesKeysFromRemoteProvider(packageName, authority);
|
||||||
|
if (SettingsSearchIndexablesProvider.DEBUG) {
|
||||||
|
final long nonIndexableTime = System.currentTimeMillis() - nonIndexableStartTime;
|
||||||
|
Log.d(TAG, "performIndexing update non-indexable for package " + packageName
|
||||||
|
+ " took time: " + nonIndexableTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return mIndexData;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean addIndexablesFromRemoteProvider(String packageName, String authority) {
|
||||||
|
try {
|
||||||
|
final Context context = BASE_AUTHORITY.equals(authority) ?
|
||||||
|
mContext : mContext.createPackageContext(packageName, 0);
|
||||||
|
|
||||||
|
final Uri uriForResources = buildUriForXmlResources(authority);
|
||||||
|
mIndexData.dataToUpdate.addAll(getIndexablesForXmlResourceUri(context, packageName,
|
||||||
|
uriForResources, SearchIndexablesContract.INDEXABLES_XML_RES_COLUMNS));
|
||||||
|
|
||||||
|
final Uri uriForRawData = buildUriForRawData(authority);
|
||||||
|
mIndexData.dataToUpdate.addAll(getIndexablesForRawDataUri(context, packageName,
|
||||||
|
uriForRawData, SearchIndexablesContract.INDEXABLES_RAW_COLUMNS));
|
||||||
|
return true;
|
||||||
|
} catch (PackageManager.NameNotFoundException e) {
|
||||||
|
Log.w(TAG, "Could not create context for " + packageName + ": "
|
||||||
|
+ Log.getStackTraceString(e));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
List<SearchIndexableResource> getIndexablesForXmlResourceUri(Context packageContext,
|
||||||
|
String packageName, Uri uri, String[] projection) {
|
||||||
|
|
||||||
|
final ContentResolver resolver = packageContext.getContentResolver();
|
||||||
|
final Cursor cursor = resolver.query(uri, projection, null, null, null);
|
||||||
|
List<SearchIndexableResource> resources = new ArrayList<>();
|
||||||
|
|
||||||
|
if (cursor == null) {
|
||||||
|
Log.w(TAG, "Cannot add index data for Uri: " + uri.toString());
|
||||||
|
return resources;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
final int count = cursor.getCount();
|
||||||
|
if (count > 0) {
|
||||||
|
while (cursor.moveToNext()) {
|
||||||
|
final int xmlResId = cursor.getInt(COLUMN_INDEX_XML_RES_RESID);
|
||||||
|
|
||||||
|
final String className = cursor.getString(COLUMN_INDEX_XML_RES_CLASS_NAME);
|
||||||
|
final int iconResId = cursor.getInt(COLUMN_INDEX_XML_RES_ICON_RESID);
|
||||||
|
|
||||||
|
final String action = cursor.getString(COLUMN_INDEX_XML_RES_INTENT_ACTION);
|
||||||
|
final String targetPackage = cursor.getString(
|
||||||
|
COLUMN_INDEX_XML_RES_INTENT_TARGET_PACKAGE);
|
||||||
|
final String targetClass = cursor.getString(
|
||||||
|
COLUMN_INDEX_XML_RES_INTENT_TARGET_CLASS);
|
||||||
|
|
||||||
|
SearchIndexableResource sir = new SearchIndexableResource(packageContext);
|
||||||
|
sir.xmlResId = xmlResId;
|
||||||
|
sir.className = className;
|
||||||
|
sir.packageName = packageName;
|
||||||
|
sir.iconResId = iconResId;
|
||||||
|
sir.intentAction = action;
|
||||||
|
sir.intentTargetPackage = targetPackage;
|
||||||
|
sir.intentTargetClass = targetClass;
|
||||||
|
|
||||||
|
resources.add(sir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
cursor.close();
|
||||||
|
}
|
||||||
|
return resources;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addNonIndexablesKeysFromRemoteProvider(String packageName,
|
||||||
|
String authority) {
|
||||||
|
final List<String> keys =
|
||||||
|
getNonIndexablesKeysFromRemoteProvider(packageName, authority);
|
||||||
|
|
||||||
|
if (keys != null && !keys.isEmpty()) {
|
||||||
|
mIndexData.nonIndexableKeys.put(authority, new ArraySet<>(keys));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
List<String> getNonIndexablesKeysFromRemoteProvider(String packageName,
|
||||||
|
String authority) {
|
||||||
|
try {
|
||||||
|
final Context packageContext = mContext.createPackageContext(packageName, 0);
|
||||||
|
|
||||||
|
final Uri uriForNonIndexableKeys = buildUriForNonIndexableKeys(authority);
|
||||||
|
return getNonIndexablesKeys(packageContext, uriForNonIndexableKeys,
|
||||||
|
SearchIndexablesContract.NON_INDEXABLES_KEYS_COLUMNS);
|
||||||
|
} catch (PackageManager.NameNotFoundException e) {
|
||||||
|
Log.w(TAG, "Could not create context for " + packageName + ": "
|
||||||
|
+ Log.getStackTraceString(e));
|
||||||
|
return EMPTY_LIST;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
Uri buildUriForXmlResources(String authority) {
|
||||||
|
return Uri.parse("content://" + authority + "/" +
|
||||||
|
SearchIndexablesContract.INDEXABLES_XML_RES_PATH);
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
Uri buildUriForRawData(String authority) {
|
||||||
|
return Uri.parse("content://" + authority + "/" +
|
||||||
|
SearchIndexablesContract.INDEXABLES_RAW_PATH);
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
Uri buildUriForNonIndexableKeys(String authority) {
|
||||||
|
return Uri.parse("content://" + authority + "/" +
|
||||||
|
SearchIndexablesContract.NON_INDEXABLES_KEYS_PATH);
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
List<SearchIndexableRaw> getIndexablesForRawDataUri(Context packageContext, String packageName,
|
||||||
|
Uri uri, String[] projection) {
|
||||||
|
final ContentResolver resolver = packageContext.getContentResolver();
|
||||||
|
final Cursor cursor = resolver.query(uri, projection, null, null, null);
|
||||||
|
List<SearchIndexableRaw> rawData = new ArrayList<>();
|
||||||
|
|
||||||
|
if (cursor == null) {
|
||||||
|
Log.w(TAG, "Cannot add index data for Uri: " + uri.toString());
|
||||||
|
return rawData;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
final int count = cursor.getCount();
|
||||||
|
if (count > 0) {
|
||||||
|
while (cursor.moveToNext()) {
|
||||||
|
final int providerRank = cursor.getInt(COLUMN_INDEX_RAW_RANK);
|
||||||
|
// TODO Remove rank
|
||||||
|
final String title = cursor.getString(COLUMN_INDEX_RAW_TITLE);
|
||||||
|
final String summaryOn = cursor.getString(COLUMN_INDEX_RAW_SUMMARY_ON);
|
||||||
|
final String summaryOff = cursor.getString(COLUMN_INDEX_RAW_SUMMARY_OFF);
|
||||||
|
final String entries = cursor.getString(COLUMN_INDEX_RAW_ENTRIES);
|
||||||
|
final String keywords = cursor.getString(COLUMN_INDEX_RAW_KEYWORDS);
|
||||||
|
|
||||||
|
final String screenTitle = cursor.getString(COLUMN_INDEX_RAW_SCREEN_TITLE);
|
||||||
|
|
||||||
|
final String className = cursor.getString(COLUMN_INDEX_RAW_CLASS_NAME);
|
||||||
|
final int iconResId = cursor.getInt(COLUMN_INDEX_RAW_ICON_RESID);
|
||||||
|
|
||||||
|
final String action = cursor.getString(COLUMN_INDEX_RAW_INTENT_ACTION);
|
||||||
|
final String targetPackage = cursor.getString(
|
||||||
|
COLUMN_INDEX_RAW_INTENT_TARGET_PACKAGE);
|
||||||
|
final String targetClass = cursor.getString(
|
||||||
|
COLUMN_INDEX_RAW_INTENT_TARGET_CLASS);
|
||||||
|
|
||||||
|
final String key = cursor.getString(COLUMN_INDEX_RAW_KEY);
|
||||||
|
final int userId = cursor.getInt(COLUMN_INDEX_RAW_USER_ID);
|
||||||
|
|
||||||
|
SearchIndexableRaw data = new SearchIndexableRaw(packageContext);
|
||||||
|
data.title = title;
|
||||||
|
data.summaryOn = summaryOn;
|
||||||
|
data.summaryOff = summaryOff;
|
||||||
|
data.entries = entries;
|
||||||
|
data.keywords = keywords;
|
||||||
|
data.screenTitle = screenTitle;
|
||||||
|
data.className = className;
|
||||||
|
data.packageName = packageName;
|
||||||
|
data.iconResId = iconResId;
|
||||||
|
data.intentAction = action;
|
||||||
|
data.intentTargetPackage = targetPackage;
|
||||||
|
data.intentTargetClass = targetClass;
|
||||||
|
data.key = key;
|
||||||
|
data.userId = userId;
|
||||||
|
|
||||||
|
rawData.add(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
cursor.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
return rawData;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> getNonIndexablesKeys(Context packageContext, Uri uri,
|
||||||
|
String[] projection) {
|
||||||
|
|
||||||
|
final ContentResolver resolver = packageContext.getContentResolver();
|
||||||
|
final Cursor cursor = resolver.query(uri, projection, null, null, null);
|
||||||
|
final List<String> result = new ArrayList<>();
|
||||||
|
|
||||||
|
if (cursor == null) {
|
||||||
|
Log.w(TAG, "Cannot add index data for Uri: " + uri.toString());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
final int count = cursor.getCount();
|
||||||
|
if (count > 0) {
|
||||||
|
while (cursor.moveToNext()) {
|
||||||
|
final String key = cursor.getString(COLUMN_INDEX_NON_INDEXABLE_KEYS_KEY_VALUE);
|
||||||
|
|
||||||
|
if (TextUtils.isEmpty(key) && Log.isLoggable(TAG, Log.VERBOSE)) {
|
||||||
|
Log.v(TAG, "Empty non-indexable key from: "
|
||||||
|
+ packageContext.getPackageName());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
result.add(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
} finally {
|
||||||
|
cursor.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Only allow a "well known" SearchIndexablesProvider. The provider should:
|
||||||
|
*
|
||||||
|
* - have read/write {@link Manifest.permission#READ_SEARCH_INDEXABLES}
|
||||||
|
* - be from a privileged package
|
||||||
|
*/
|
||||||
|
@VisibleForTesting
|
||||||
|
boolean isWellKnownProvider(ResolveInfo info) {
|
||||||
|
final String authority = info.providerInfo.authority;
|
||||||
|
final String packageName = info.providerInfo.applicationInfo.packageName;
|
||||||
|
|
||||||
|
if (TextUtils.isEmpty(authority) || TextUtils.isEmpty(packageName)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final String readPermission = info.providerInfo.readPermission;
|
||||||
|
final String writePermission = info.providerInfo.writePermission;
|
||||||
|
|
||||||
|
if (TextUtils.isEmpty(readPermission) || TextUtils.isEmpty(writePermission)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!android.Manifest.permission.READ_SEARCH_INDEXABLES.equals(readPermission) ||
|
||||||
|
!android.Manifest.permission.READ_SEARCH_INDEXABLES.equals(writePermission)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return isPrivilegedPackage(packageName, mContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return true if the {@param packageName} is privileged.
|
||||||
|
*/
|
||||||
|
private 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
55
src/com/android/settings/search/indexing/PreIndexData.java
Normal file
55
src/com/android/settings/search/indexing/PreIndexData.java
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
/*
|
||||||
|
* 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.indexing;
|
||||||
|
|
||||||
|
import android.provider.SearchIndexableData;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds Data sources for indexable data.
|
||||||
|
* TODO (b/33577327) add getters and setters for data.
|
||||||
|
*/
|
||||||
|
public class PreIndexData {
|
||||||
|
public List<SearchIndexableData> dataToUpdate;
|
||||||
|
public Map<String, Set<String>> nonIndexableKeys;
|
||||||
|
|
||||||
|
public PreIndexData() {
|
||||||
|
dataToUpdate = new ArrayList<>();
|
||||||
|
nonIndexableKeys = new HashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public PreIndexData(PreIndexData other) {
|
||||||
|
dataToUpdate = new ArrayList<>(other.dataToUpdate);
|
||||||
|
nonIndexableKeys = new HashMap<>(other.nonIndexableKeys);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PreIndexData copy() {
|
||||||
|
return new PreIndexData(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clear() {
|
||||||
|
dataToUpdate.clear();
|
||||||
|
nonIndexableKeys.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,24 +17,20 @@
|
|||||||
|
|
||||||
package com.android.settings.search;
|
package com.android.settings.search;
|
||||||
|
|
||||||
import static android.provider.SearchIndexablesContract.INDEXABLES_RAW_COLUMNS;
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
import static org.mockito.Matchers.any;
|
import static org.mockito.Matchers.any;
|
||||||
import static org.mockito.Matchers.anyInt;
|
import static org.mockito.Matchers.anyInt;
|
||||||
import static org.mockito.Matchers.anyList;
|
import static org.mockito.Matchers.anyList;
|
||||||
import static org.mockito.Matchers.anyMap;
|
import static org.mockito.Matchers.anyMap;
|
||||||
import static org.mockito.Matchers.anyString;
|
import static org.mockito.Matchers.anyString;
|
||||||
|
import static org.mockito.Matchers.anyBoolean;
|
||||||
import static org.mockito.Mockito.doNothing;
|
import static org.mockito.Mockito.doNothing;
|
||||||
import static org.mockito.Mockito.doReturn;
|
import static org.mockito.Mockito.doReturn;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.spy;
|
import static org.mockito.Mockito.spy;
|
||||||
import static org.mockito.Mockito.times;
|
import static org.mockito.Mockito.times;
|
||||||
import static org.mockito.Mockito.verify;
|
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.ContentValues;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
@@ -43,19 +39,18 @@ import android.content.pm.PackageManager;
|
|||||||
import android.content.pm.ProviderInfo;
|
import android.content.pm.ProviderInfo;
|
||||||
import android.content.pm.ResolveInfo;
|
import android.content.pm.ResolveInfo;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.database.MatrixCursor;
|
|
||||||
import android.database.sqlite.SQLiteDatabase;
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
|
import android.provider.SearchIndexableData;
|
||||||
import android.provider.SearchIndexableResource;
|
import android.provider.SearchIndexableResource;
|
||||||
import android.util.ArrayMap;
|
import android.util.ArrayMap;
|
||||||
|
|
||||||
import com.android.settings.R;
|
import com.android.settings.R;
|
||||||
import com.android.settings.TestConfig;
|
import com.android.settings.TestConfig;
|
||||||
|
import com.android.settings.search.indexing.PreIndexData;
|
||||||
import com.android.settings.testutils.DatabaseTestUtils;
|
import com.android.settings.testutils.DatabaseTestUtils;
|
||||||
import com.android.settings.testutils.FakeFeatureFactory;
|
import com.android.settings.testutils.FakeFeatureFactory;
|
||||||
import com.android.settings.testutils.SettingsRobolectricTestRunner;
|
import com.android.settings.testutils.SettingsRobolectricTestRunner;
|
||||||
import com.android.settings.testutils.shadow.ShadowDatabaseIndexingUtils;
|
|
||||||
import com.android.settings.testutils.shadow.ShadowRunnableAsyncTask;
|
import com.android.settings.testutils.shadow.ShadowRunnableAsyncTask;
|
||||||
|
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
@@ -67,7 +62,6 @@ import org.mockito.MockitoAnnotations;
|
|||||||
import org.robolectric.Robolectric;
|
import org.robolectric.Robolectric;
|
||||||
import org.robolectric.RuntimeEnvironment;
|
import org.robolectric.RuntimeEnvironment;
|
||||||
import org.robolectric.annotation.Config;
|
import org.robolectric.annotation.Config;
|
||||||
import org.robolectric.shadows.ShadowContentResolver;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
@@ -84,8 +78,6 @@ import java.util.Set;
|
|||||||
sdk = TestConfig.SDK_VERSION,
|
sdk = TestConfig.SDK_VERSION,
|
||||||
shadows = {
|
shadows = {
|
||||||
ShadowRunnableAsyncTask.class,
|
ShadowRunnableAsyncTask.class,
|
||||||
ShadowDatabaseIndexingUtils.class,
|
|
||||||
ShadowContentResolver.class
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
public class DatabaseIndexingManagerTest {
|
public class DatabaseIndexingManagerTest {
|
||||||
@@ -129,6 +121,8 @@ public class DatabaseIndexingManagerTest {
|
|||||||
private DatabaseIndexingManager mManager;
|
private DatabaseIndexingManager mManager;
|
||||||
private SQLiteDatabase mDb;
|
private SQLiteDatabase mDb;
|
||||||
|
|
||||||
|
private final List<ResolveInfo> FAKE_PROVIDER_LIST = new ArrayList<>();
|
||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
private PackageManager mPackageManager;
|
private PackageManager mPackageManager;
|
||||||
|
|
||||||
@@ -136,10 +130,12 @@ public class DatabaseIndexingManagerTest {
|
|||||||
public void setUp() {
|
public void setUp() {
|
||||||
MockitoAnnotations.initMocks(this);
|
MockitoAnnotations.initMocks(this);
|
||||||
mContext = spy(RuntimeEnvironment.application);
|
mContext = spy(RuntimeEnvironment.application);
|
||||||
mManager = spy(new DatabaseIndexingManager(mContext, PACKAGE_ONE));
|
mManager = spy(new DatabaseIndexingManager(mContext));
|
||||||
mDb = IndexDatabaseHelper.getInstance(mContext).getWritableDatabase();
|
mDb = IndexDatabaseHelper.getInstance(mContext).getWritableDatabase();
|
||||||
|
|
||||||
doReturn(mPackageManager).when(mContext).getPackageManager();
|
doReturn(mPackageManager).when(mContext).getPackageManager();
|
||||||
|
doReturn(FAKE_PROVIDER_LIST).when(mPackageManager)
|
||||||
|
.queryIntentContentProviders(any(Intent.class), anyInt());
|
||||||
FakeFeatureFactory.setupForTest(mContext);
|
FakeFeatureFactory.setupForTest(mContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -755,113 +751,60 @@ public class DatabaseIndexingManagerTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testPerformIndexing_fullIndex_getsDataFromProviders() {
|
public void testPerformIndexing_fullIndex_getsDataFromProviders() {
|
||||||
DummyProvider provider = new DummyProvider();
|
SearchIndexableRaw rawData = getFakeRaw();
|
||||||
provider.onCreate();
|
PreIndexData data = getPreIndexData(rawData);
|
||||||
ShadowContentResolver.registerProvider(AUTHORITY_ONE, provider);
|
doReturn(data).when(mManager).getIndexDataFromProviders(anyList(), anyBoolean());
|
||||||
|
doReturn(true).when(mManager).isFullIndex(any(Context.class), anyString(), anyString(),
|
||||||
|
anyString());
|
||||||
|
|
||||||
// Test that Indexables are added for Full indexing
|
mManager.performIndexing();
|
||||||
when(mPackageManager.queryIntentContentProviders(any(Intent.class), anyInt()))
|
|
||||||
.thenReturn(getDummyResolveInfo());
|
|
||||||
|
|
||||||
DatabaseIndexingManager manager =
|
verify(mManager).updateDatabase(data, true /* isFullIndex */,
|
||||||
spy(new DatabaseIndexingManager(mContext, PACKAGE_ONE));
|
Locale.getDefault().toString());
|
||||||
|
|
||||||
manager.performIndexing();
|
|
||||||
|
|
||||||
verify(manager).addIndexablesFromRemoteProvider(PACKAGE_ONE, AUTHORITY_ONE);
|
|
||||||
verify(manager).updateDatabase(true /* isFullIndex */, Locale.getDefault().toString());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testPerformIndexing_incrementalIndex_noDataAdded() {
|
public void testPerformIndexing_fullIndex_databaseDropped() {
|
||||||
final List<ResolveInfo> providerInfo = getDummyResolveInfo();
|
// Initialize the Manager and force rebuild
|
||||||
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(providerInfo);
|
|
||||||
|
|
||||||
DatabaseIndexingManager manager =
|
DatabaseIndexingManager manager =
|
||||||
spy(new DatabaseIndexingManager(mContext, PACKAGE_ONE));
|
spy(new DatabaseIndexingManager(mContext));
|
||||||
|
doReturn(false).when(mManager).isFullIndex(any(Context.class), anyString(), anyString(),
|
||||||
manager.mDataToProcess.dataToUpdate.clear();
|
anyString());
|
||||||
|
|
||||||
manager.performIndexing();
|
|
||||||
|
|
||||||
verify(manager, times(0)).addDataToDatabase(any(SQLiteDatabase.class), anyString(),
|
|
||||||
anyList(), anyMap());
|
|
||||||
verify(manager, times(0)).addIndexablesFromRemoteProvider(PACKAGE_ONE, AUTHORITY_ONE);
|
|
||||||
verify(manager).updateDataInDatabase(any(SQLiteDatabase.class), anyMap());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testPerformIndexing_localeChanged_databaseDropped() {
|
|
||||||
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());
|
|
||||||
|
|
||||||
// Initialize the Manager
|
|
||||||
DatabaseIndexingManager manager =
|
|
||||||
spy(new DatabaseIndexingManager(mContext, PACKAGE_ONE));
|
|
||||||
|
|
||||||
// Insert data point which will be dropped
|
// Insert data point which will be dropped
|
||||||
final String oldTitle = "This is French";
|
insertSpecialCase("Ceci n'est pas un pipe", true, "oui oui mon ami");
|
||||||
insertSpecialCase(oldTitle, true, "key");
|
|
||||||
|
|
||||||
// Add a data point to be added by the indexing
|
|
||||||
SearchIndexableRaw raw = new SearchIndexableRaw(mContext);
|
|
||||||
final String newTitle = "This is English";
|
|
||||||
raw.title = newTitle;
|
|
||||||
manager.mDataToProcess.dataToUpdate.add(raw);
|
|
||||||
|
|
||||||
manager.performIndexing();
|
manager.performIndexing();
|
||||||
|
|
||||||
// Assert that the New Title is inserted
|
|
||||||
final Cursor newCursor = mDb.rawQuery("SELECT * FROM prefs_index WHERE data_title = '" +
|
|
||||||
newTitle + "'", null);
|
|
||||||
assertThat(newCursor.getCount()).isEqualTo(1);
|
|
||||||
|
|
||||||
// Assert that the Old Title is no longer in the database, since it was dropped
|
// Assert that the Old Title is no longer in the database, since it was dropped
|
||||||
final Cursor oldCursor = mDb.rawQuery("SELECT * FROM prefs_index WHERE data_title = '" +
|
final Cursor oldCursor = mDb.rawQuery("SELECT * FROM prefs_index", null);
|
||||||
oldTitle + "'", null);
|
|
||||||
assertThat(oldCursor.getCount()).isEqualTo(0);
|
assertThat(oldCursor.getCount()).isEqualTo(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testPerformIndexing_onOta_FullIndex() {
|
public void testPerformIndexing_isfullIndex() {
|
||||||
DummyProvider provider = new DummyProvider();
|
SearchIndexableRaw rawData = getFakeRaw();
|
||||||
provider.onCreate();
|
PreIndexData data = getPreIndexData(rawData);
|
||||||
ShadowContentResolver.registerProvider(
|
doReturn(data).when(mManager).getIndexDataFromProviders(anyList(), anyBoolean());
|
||||||
AUTHORITY_ONE, provider
|
doReturn(true).when(mManager).isFullIndex(any(Context.class), anyString(), anyString(),
|
||||||
);
|
anyString());
|
||||||
|
|
||||||
// Test that Indexables are added for Full indexing
|
mManager.performIndexing();
|
||||||
when(mPackageManager.queryIntentContentProviders(any(Intent.class), anyInt()))
|
|
||||||
.thenReturn(getDummyResolveInfo());
|
|
||||||
|
|
||||||
DatabaseIndexingManager manager =
|
verify(mManager).updateDatabase(data, true /* isFullIndex */,
|
||||||
spy(new DatabaseIndexingManager(mContext, PACKAGE_ONE));
|
Locale.getDefault().toString());
|
||||||
|
|
||||||
manager.performIndexing();
|
|
||||||
|
|
||||||
verify(manager).updateDatabase(true /* isFullIndex */, Locale.getDefault().toString());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testPerformIndexing_onPackageChange_shouldFullIndex() {
|
public void testPerformIndexing_onPackageChange_fullIndex() {
|
||||||
final List<ResolveInfo> providers = getDummyResolveInfo();
|
final List<ResolveInfo> providers = getDummyResolveInfo();
|
||||||
final String buildNumber = Build.FINGERPRINT;
|
final String buildNumber = Build.FINGERPRINT;
|
||||||
final String locale = Locale.getDefault().toString();
|
final String locale = Locale.getDefault().toString();
|
||||||
skipFullIndex(providers);
|
skipFullIndex(providers);
|
||||||
|
|
||||||
// This snapshot is already indexed. Should return false
|
// This snapshot is already indexed. Should return false
|
||||||
assertThat(IndexDatabaseHelper.isFullIndex(
|
assertThat(mManager.isFullIndex(
|
||||||
mContext, locale, buildNumber,
|
mContext, locale, buildNumber,
|
||||||
IndexDatabaseHelper.buildProviderVersionedNames(providers)))
|
IndexDatabaseHelper.buildProviderVersionedNames(providers)))
|
||||||
.isFalse();
|
.isFalse();
|
||||||
@@ -869,65 +812,46 @@ public class DatabaseIndexingManagerTest {
|
|||||||
// Change provider version number, this should trigger full index.
|
// Change provider version number, this should trigger full index.
|
||||||
providers.get(0).providerInfo.applicationInfo.versionCode++;
|
providers.get(0).providerInfo.applicationInfo.versionCode++;
|
||||||
|
|
||||||
assertThat(IndexDatabaseHelper.isFullIndex(mContext, locale, buildNumber,
|
assertThat(mManager.isFullIndex(mContext, locale, buildNumber,
|
||||||
IndexDatabaseHelper.buildProviderVersionedNames(providers)))
|
IndexDatabaseHelper.buildProviderVersionedNames(providers)))
|
||||||
.isTrue();
|
.isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testPerformIndexing_onOta_buildNumberIsCached() {
|
public void testPerformIndexing_onOta_buildNumberIsCached() {
|
||||||
DummyProvider provider = new DummyProvider();
|
mManager.performIndexing();
|
||||||
provider.onCreate();
|
|
||||||
ShadowContentResolver.registerProvider(
|
|
||||||
AUTHORITY_ONE, provider
|
|
||||||
);
|
|
||||||
|
|
||||||
// Test that Indexables are added for Full indexing
|
assertThat(IndexDatabaseHelper.isBuildIndexed(mContext, Build.FINGERPRINT)).isTrue();
|
||||||
when(mPackageManager.queryIntentContentProviders(any(Intent.class), anyInt()))
|
|
||||||
.thenReturn(getDummyResolveInfo());
|
|
||||||
|
|
||||||
DatabaseIndexingManager manager =
|
|
||||||
spy(new DatabaseIndexingManager(mContext, PACKAGE_ONE));
|
|
||||||
|
|
||||||
manager.performIndexing();
|
|
||||||
|
|
||||||
assertThat(IndexDatabaseHelper.getInstance(mContext).isBuildIndexed(mContext,
|
|
||||||
Build.FINGERPRINT)).isTrue();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFullUpdatedDatabase_noData_addDataToDatabaseNotCalled() {
|
public void testFullUpdatedDatabase_noData_addDataToDatabaseNotCalled() {
|
||||||
mManager.updateDatabase(true /* isFullIndex */, localeStr);
|
PreIndexData emptydata = new PreIndexData();
|
||||||
mManager.mDataToProcess.dataToUpdate.clear();
|
mManager.updateDatabase(emptydata, true /* isFullIndex */, localeStr);
|
||||||
verify(mManager, times(0)).addDataToDatabase(any(SQLiteDatabase.class), anyString(),
|
verify(mManager, times(0)).addDataToDatabase(any(SQLiteDatabase.class), anyString(),
|
||||||
anyList(), anyMap());
|
anyList(), anyMap());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testFullUpdatedDatabase_updatedDataInDatabaseNotCalled() {
|
|
||||||
mManager.updateDatabase(true /* isFullIndex */, localeStr);
|
|
||||||
verify(mManager, times(0)).updateDataInDatabase(any(SQLiteDatabase.class), anyMap());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testLocaleUpdated_afterIndexing_localeNotAdded() {
|
public void testLocaleUpdated_afterIndexing_localeNotAdded() {
|
||||||
mManager.updateDatabase(true /* isFullIndex */, localeStr);
|
PreIndexData emptydata = new PreIndexData();
|
||||||
assertThat(IndexDatabaseHelper.getInstance(mContext)
|
mManager.updateDatabase(emptydata, true /* isFullIndex */, localeStr);
|
||||||
.isLocaleAlreadyIndexed(mContext, localeStr)).isFalse();
|
|
||||||
|
assertThat(IndexDatabaseHelper.isLocaleAlreadyIndexed(mContext, localeStr)).isFalse();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testLocaleUpdated_afterFullIndexing_localeAdded() {
|
public void testLocaleUpdated_afterFullIndexing_localeAdded() {
|
||||||
mManager.performIndexing();
|
mManager.performIndexing();
|
||||||
assertThat(IndexDatabaseHelper.getInstance(mContext)
|
assertThat(IndexDatabaseHelper.isLocaleAlreadyIndexed(mContext, localeStr)).isTrue();
|
||||||
.isLocaleAlreadyIndexed(mContext, localeStr)).isTrue();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testUpdateDatabase_newEligibleData_addedToDatabase() {
|
public void testUpdateDatabase_newEligibleData_addedToDatabase() {
|
||||||
// Test that addDataToDatabase is called when dataToUpdate is non-empty
|
// Test that addDataToDatabase is called when dataToUpdate is non-empty
|
||||||
mManager.mDataToProcess.dataToUpdate.add(getFakeRaw());
|
PreIndexData indexData = new PreIndexData();
|
||||||
mManager.updateDatabase(true /* isFullIndex */, localeStr);
|
indexData.dataToUpdate.add(getFakeRaw());
|
||||||
|
mManager.updateDatabase(indexData, true /* isFullIndex */, localeStr);
|
||||||
|
|
||||||
Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index", null);
|
Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index", null);
|
||||||
cursor.moveToPosition(0);
|
cursor.moveToPosition(0);
|
||||||
@@ -1020,8 +944,8 @@ public class DatabaseIndexingManagerTest {
|
|||||||
@Test
|
@Test
|
||||||
public void testEmptyNonIndexableKeys_emptyDataKeyResources_addedToDatabase() {
|
public void testEmptyNonIndexableKeys_emptyDataKeyResources_addedToDatabase() {
|
||||||
insertSpecialCase(TITLE_ONE, true /* enabled */, null /* dataReferenceKey */);
|
insertSpecialCase(TITLE_ONE, true /* enabled */, null /* dataReferenceKey */);
|
||||||
|
PreIndexData emptydata = new PreIndexData();
|
||||||
mManager.updateDatabase(false, localeStr);
|
mManager.updateDatabase(emptydata, false /* needsReindexing */, localeStr);
|
||||||
|
|
||||||
Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index WHERE enabled = 1", null);
|
Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index WHERE enabled = 1", null);
|
||||||
cursor.moveToPosition(0);
|
cursor.moveToPosition(0);
|
||||||
@@ -1111,46 +1035,6 @@ public class DatabaseIndexingManagerTest {
|
|||||||
return niks;
|
return niks;
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<ResolveInfo> getDummyResolveInfo() {
|
|
||||||
List<ResolveInfo> infoList = new ArrayList<>();
|
|
||||||
ResolveInfo info = new ResolveInfo();
|
|
||||||
info.providerInfo = new ProviderInfo();
|
|
||||||
info.providerInfo.exported = true;
|
|
||||||
info.providerInfo.authority = AUTHORITY_ONE;
|
|
||||||
info.providerInfo.packageName = PACKAGE_ONE;
|
|
||||||
info.providerInfo.applicationInfo = new ApplicationInfo();
|
|
||||||
infoList.add(info);
|
|
||||||
|
|
||||||
return infoList;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO move this method and its counterpart in CursorToSearchResultConverterTest into
|
|
||||||
// a util class with public fields to assert values.
|
|
||||||
private Cursor getDummyCursor() {
|
|
||||||
MatrixCursor cursor = new MatrixCursor(INDEXABLES_RAW_COLUMNS);
|
|
||||||
final String BLANK = "";
|
|
||||||
|
|
||||||
ArrayList<String> item =
|
|
||||||
new ArrayList<>(INDEXABLES_RAW_COLUMNS.length);
|
|
||||||
item.add("42"); // Rank
|
|
||||||
item.add(TITLE_ONE); // Title
|
|
||||||
item.add(BLANK); // Summary on
|
|
||||||
item.add(BLANK); // summary off
|
|
||||||
item.add(BLANK); // entries
|
|
||||||
item.add(BLANK); // keywords
|
|
||||||
item.add(BLANK); // screen title
|
|
||||||
item.add(BLANK); // classname
|
|
||||||
item.add("123"); // Icon
|
|
||||||
item.add(BLANK); // Intent action
|
|
||||||
item.add(BLANK); // target package
|
|
||||||
item.add(BLANK); // target class
|
|
||||||
item.add(KEY_ONE); // Key
|
|
||||||
item.add("-1"); // userId
|
|
||||||
cursor.addRow(item);
|
|
||||||
|
|
||||||
return cursor;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void insertSpecialCase(String specialCase, boolean enabled, String key) {
|
private void insertSpecialCase(String specialCase, boolean enabled, String key) {
|
||||||
ContentValues values = new ContentValues();
|
ContentValues values = new ContentValues();
|
||||||
values.put(IndexDatabaseHelper.IndexColumns.DOCID, specialCase.hashCode());
|
values.put(IndexDatabaseHelper.IndexColumns.DOCID, specialCase.hashCode());
|
||||||
@@ -1179,43 +1063,22 @@ public class DatabaseIndexingManagerTest {
|
|||||||
mDb.replaceOrThrow(IndexDatabaseHelper.Tables.TABLE_PREFS_INDEX, null, values);
|
mDb.replaceOrThrow(IndexDatabaseHelper.Tables.TABLE_PREFS_INDEX, null, values);
|
||||||
}
|
}
|
||||||
|
|
||||||
private class DummyProvider extends ContentProvider {
|
private PreIndexData getPreIndexData(SearchIndexableData fakeData) {
|
||||||
|
PreIndexData data = new PreIndexData();
|
||||||
|
data.dataToUpdate.add(fakeData);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
private List<ResolveInfo> getDummyResolveInfo() {
|
||||||
public boolean onCreate() {
|
List<ResolveInfo> infoList = new ArrayList<>();
|
||||||
return false;
|
ResolveInfo info = new ResolveInfo();
|
||||||
}
|
info.providerInfo = new ProviderInfo();
|
||||||
|
info.providerInfo.exported = true;
|
||||||
|
info.providerInfo.authority = AUTHORITY_ONE;
|
||||||
|
info.providerInfo.packageName = PACKAGE_ONE;
|
||||||
|
info.providerInfo.applicationInfo = new ApplicationInfo();
|
||||||
|
infoList.add(info);
|
||||||
|
|
||||||
@Override
|
return infoList;
|
||||||
public Cursor query(@NonNull Uri uri, @Nullable String[] projection,
|
|
||||||
@Nullable String selection, @Nullable String[] selectionArgs,
|
|
||||||
@Nullable String sortOrder) {
|
|
||||||
if (uri.toString().contains("xml")) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return getDummyCursor();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getType(@NonNull Uri uri) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int delete(@NonNull Uri uri, @Nullable String selection,
|
|
||||||
@Nullable String[] selectionArgs) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int update(@NonNull Uri uri, @Nullable ContentValues values,
|
|
||||||
@Nullable String selection, @Nullable String[] selectionArgs) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,170 @@
|
|||||||
|
/*
|
||||||
|
* 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.indexing;
|
||||||
|
|
||||||
|
import android.content.ContentResolver;
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import android.content.pm.ApplicationInfo;
|
||||||
|
import android.content.pm.ProviderInfo;
|
||||||
|
import android.content.pm.ResolveInfo;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.provider.SearchIndexableResource;
|
||||||
|
import com.android.settings.TestConfig;
|
||||||
|
import com.android.settings.search.SearchIndexableRaw;
|
||||||
|
import com.android.settings.testutils.SettingsRobolectricTestRunner;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
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;
|
||||||
|
|
||||||
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
import static org.mockito.Matchers.any;
|
||||||
|
import static org.mockito.Matchers.anyString;
|
||||||
|
import static org.mockito.Mockito.doReturn;
|
||||||
|
import static org.mockito.Mockito.spy;
|
||||||
|
|
||||||
|
@RunWith(SettingsRobolectricTestRunner.class)
|
||||||
|
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
|
||||||
|
public class IndexableDataCollectorTest {
|
||||||
|
|
||||||
|
private final String AUTHORITY_ONE = "authority";
|
||||||
|
private final String PACKAGE_ONE = "com.android.settings";
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
ContentResolver mResolver;
|
||||||
|
|
||||||
|
Context mContext;
|
||||||
|
|
||||||
|
IndexableDataCollector mDataCollector;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
MockitoAnnotations.initMocks(this);
|
||||||
|
mContext = spy(RuntimeEnvironment.application);
|
||||||
|
doReturn(mResolver).when(mContext).getContentResolver();
|
||||||
|
//doReturn(mPackageManager).when(mContext).getPackageManager();
|
||||||
|
|
||||||
|
mDataCollector = spy(new IndexableDataCollector(mContext));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCollectIndexableData_addsResourceData() {
|
||||||
|
final List<ResolveInfo> providerInfo = getDummyResolveInfo();
|
||||||
|
doReturn(true).when(mDataCollector).isWellKnownProvider(any(ResolveInfo.class));
|
||||||
|
|
||||||
|
List<SearchIndexableResource> resources = getFakeResource();
|
||||||
|
doReturn(resources).when(mDataCollector).getIndexablesForXmlResourceUri(
|
||||||
|
any(Context.class), anyString(), any(Uri.class), any(String[].class));
|
||||||
|
|
||||||
|
PreIndexData data = mDataCollector.collectIndexableData(providerInfo,
|
||||||
|
true /* isFullIndex */);
|
||||||
|
|
||||||
|
assertThat(data.dataToUpdate).containsAllIn(resources);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCollectIndexableData_addsRawData() {
|
||||||
|
final List<ResolveInfo> providerInfo = getDummyResolveInfo();
|
||||||
|
doReturn(true).when(mDataCollector).isWellKnownProvider(any(ResolveInfo.class));
|
||||||
|
|
||||||
|
List<SearchIndexableRaw> rawData = getFakeRaw();
|
||||||
|
doReturn(rawData).when(mDataCollector).getIndexablesForRawDataUri(any(Context.class),
|
||||||
|
anyString(), any(Uri.class), any(String[].class));
|
||||||
|
|
||||||
|
|
||||||
|
PreIndexData data = mDataCollector.collectIndexableData(providerInfo,
|
||||||
|
true /* isFullIndex */);
|
||||||
|
|
||||||
|
assertThat(data.dataToUpdate).containsAllIn(rawData);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCollectIndexableData_addsNonIndexables() {
|
||||||
|
final List<ResolveInfo> providerInfo = getDummyResolveInfo();
|
||||||
|
doReturn(true).when(mDataCollector).isWellKnownProvider(any(ResolveInfo.class));
|
||||||
|
|
||||||
|
List<String> niks = getFakeNonIndexables();
|
||||||
|
|
||||||
|
doReturn(niks).when(mDataCollector).getNonIndexablesKeysFromRemoteProvider(anyString(),
|
||||||
|
anyString());
|
||||||
|
|
||||||
|
PreIndexData data = mDataCollector.collectIndexableData(providerInfo,
|
||||||
|
true /* isFullIndex */);
|
||||||
|
|
||||||
|
assertThat(data.nonIndexableKeys.get(AUTHORITY_ONE)).containsAllIn(niks);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<ResolveInfo> getDummyResolveInfo() {
|
||||||
|
List<ResolveInfo> infoList = new ArrayList<>();
|
||||||
|
ResolveInfo info = new ResolveInfo();
|
||||||
|
info.providerInfo = new ProviderInfo();
|
||||||
|
info.providerInfo.exported = true;
|
||||||
|
info.providerInfo.authority = AUTHORITY_ONE;
|
||||||
|
info.providerInfo.packageName = PACKAGE_ONE;
|
||||||
|
info.providerInfo.applicationInfo = new ApplicationInfo();
|
||||||
|
infoList.add(info);
|
||||||
|
|
||||||
|
return infoList;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<SearchIndexableResource> getFakeResource() {
|
||||||
|
List<SearchIndexableResource> resources = new ArrayList<>();
|
||||||
|
final String BLANK = "";
|
||||||
|
|
||||||
|
SearchIndexableResource sir = new SearchIndexableResource(mContext);
|
||||||
|
sir.rank = 0;
|
||||||
|
sir.xmlResId = 0;
|
||||||
|
sir.className = BLANK;
|
||||||
|
sir.packageName = BLANK;
|
||||||
|
sir.iconResId = 0;
|
||||||
|
sir.intentAction = BLANK;
|
||||||
|
sir.intentTargetPackage = BLANK;
|
||||||
|
sir.intentTargetClass = BLANK;
|
||||||
|
sir.enabled = true;
|
||||||
|
resources.add(sir);
|
||||||
|
|
||||||
|
return resources;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<SearchIndexableRaw> getFakeRaw() {
|
||||||
|
List<SearchIndexableRaw> rawData = new ArrayList<>();
|
||||||
|
|
||||||
|
SearchIndexableRaw data = new SearchIndexableRaw(mContext);
|
||||||
|
data.title = "bront";
|
||||||
|
data.key = "brint";
|
||||||
|
rawData.add(data);
|
||||||
|
|
||||||
|
return rawData;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> getFakeNonIndexables() {
|
||||||
|
List<String> niks = new ArrayList<>();
|
||||||
|
niks.add("they're");
|
||||||
|
niks.add("good");
|
||||||
|
niks.add("dogs");
|
||||||
|
niks.add("brent");
|
||||||
|
return niks;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user