diff options
Diffstat (limited to 'core/java/android/server')
-rw-r--r-- | core/java/android/server/search/SearchManagerService.java | 34 | ||||
-rw-r--r-- | core/java/android/server/search/Searchables.java | 145 |
2 files changed, 158 insertions, 21 deletions
diff --git a/core/java/android/server/search/SearchManagerService.java b/core/java/android/server/search/SearchManagerService.java index 3826a01..79ade26 100644 --- a/core/java/android/server/search/SearchManagerService.java +++ b/core/java/android/server/search/SearchManagerService.java @@ -23,10 +23,14 @@ import android.app.SearchManager; import android.app.SearchableInfo; import android.content.BroadcastReceiver; import android.content.ComponentName; +import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.pm.ResolveInfo; +import android.database.ContentObserver; import android.os.Process; +import android.provider.Settings; import android.util.Log; import java.util.List; @@ -46,6 +50,8 @@ public class SearchManagerService extends ISearchManager.Stub { // This field is initialized lazily in getSearchables(), and then never modified. private Searchables mSearchables; + private ContentObserver mGlobalSearchObserver; + /** * Initializes the Search Manager service in the provided system context. * Only one instance of this object should be created! @@ -56,6 +62,8 @@ public class SearchManagerService extends ISearchManager.Stub { mContext = context; mContext.registerReceiver(new BootCompletedReceiver(), new IntentFilter(Intent.ACTION_BOOT_COMPLETED)); + mGlobalSearchObserver = new GlobalSearchProviderObserver( + mContext.getContentResolver()); } private synchronized Searchables getSearchables() { @@ -100,6 +108,28 @@ public class SearchManagerService extends ISearchManager.Stub { } } + class GlobalSearchProviderObserver extends ContentObserver { + private final ContentResolver mResolver; + + public GlobalSearchProviderObserver(ContentResolver resolver) { + super(null); + mResolver = resolver; + mResolver.registerContentObserver( + Settings.Secure.getUriFor(Settings.Secure.SEARCH_GLOBAL_SEARCH_ACTIVITY), + false /* notifyDescendants */, + this); + } + + @Override + public void onChange(boolean selfChange) { + getSearchables().buildSearchableList(); + Intent intent = new Intent(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED); + intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); + mContext.sendBroadcast(intent); + } + + } + // // Searchable activities API // @@ -126,6 +156,10 @@ public class SearchManagerService extends ISearchManager.Stub { return getSearchables().getSearchablesInGlobalSearchList(); } + public List<ResolveInfo> getGlobalSearchActivities() { + return getSearchables().getGlobalSearchActivities(); + } + /** * Gets the name of the global search activity. */ diff --git a/core/java/android/server/search/Searchables.java b/core/java/android/server/search/Searchables.java index 279c17d..f24d52f 100644 --- a/core/java/android/server/search/Searchables.java +++ b/core/java/android/server/search/Searchables.java @@ -16,19 +16,23 @@ package android.server.search; -import android.Manifest; import android.app.SearchManager; import android.app.SearchableInfo; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.os.Bundle; +import android.provider.Settings; +import android.text.TextUtils; import android.util.Log; import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; import java.util.List; @@ -50,7 +54,10 @@ public class Searchables { private HashMap<ComponentName, SearchableInfo> mSearchablesMap = null; private ArrayList<SearchableInfo> mSearchablesList = null; private ArrayList<SearchableInfo> mSearchablesInGlobalSearchList = null; - private ComponentName mGlobalSearchActivity = null; + // Contains all installed activities that handle the global search + // intent. + private List<ResolveInfo> mGlobalSearchActivities; + private ComponentName mCurrentGlobalSearchActivity = null; private ComponentName mWebSearchActivity = null; public static String GOOGLE_SEARCH_COMPONENT_NAME = @@ -224,8 +231,11 @@ public class Searchables { } } + List<ResolveInfo> newGlobalSearchActivities = findGlobalSearchActivities(); + // Find the global search activity - ComponentName newGlobalSearchActivity = findGlobalSearchActivity(); + ComponentName newGlobalSearchActivity = findGlobalSearchActivity( + newGlobalSearchActivities); // Find the web search activity ComponentName newWebSearchActivity = findWebSearchActivity(newGlobalSearchActivity); @@ -235,38 +245,124 @@ public class Searchables { mSearchablesMap = newSearchablesMap; mSearchablesList = newSearchablesList; mSearchablesInGlobalSearchList = newSearchablesInGlobalSearchList; - mGlobalSearchActivity = newGlobalSearchActivity; + mGlobalSearchActivities = newGlobalSearchActivities; + mCurrentGlobalSearchActivity = newGlobalSearchActivity; mWebSearchActivity = newWebSearchActivity; } } + /** + * Returns a sorted list of installed search providers as per + * the following heuristics: + * + * (a) System apps are given priority over non system apps. + * (b) Among system apps and non system apps, the relative ordering + * is defined by their declared priority. + */ + private List<ResolveInfo> findGlobalSearchActivities() { + // Step 1 : Query the package manager for a list + // of activities that can handle the GLOBAL_SEARCH intent. + Intent intent = new Intent(SearchManager.INTENT_ACTION_GLOBAL_SEARCH); + PackageManager pm = mContext.getPackageManager(); + List<ResolveInfo> activities = + pm.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY); + + if (activities != null && !activities.isEmpty()) { + // Step 2: Rank matching activities according to our heuristics. + Collections.sort(activities, GLOBAL_SEARCH_RANKER); + } + + return activities; + } /** * Finds the global search activity. - * - * This is currently implemented by returning the first activity that handles - * the GLOBAL_SEARCH intent and has the GLOBAL_SEARCH permission. If we allow - * more than one global search activity to be installed, this code must be changed. */ - private ComponentName findGlobalSearchActivity() { + private ComponentName findGlobalSearchActivity(List<ResolveInfo> installed) { + // Fetch the global search provider from the system settings, + // and if it's still installed, return it. + final String searchProviderSetting = getGlobalSearchProviderSetting(); + if (!TextUtils.isEmpty(searchProviderSetting)) { + final ComponentName globalSearchComponent = ComponentName.unflattenFromString( + searchProviderSetting); + if (globalSearchComponent != null && isInstalled(globalSearchComponent)) { + return globalSearchComponent; + } + } + + return getDefaultGlobalSearchProvider(installed); + } + + /** + * Checks whether the global search provider with a given + * component name is installed on the system or not. This deals with + * cases such as the removal of an installed provider. + */ + private boolean isInstalled(ComponentName globalSearch) { Intent intent = new Intent(SearchManager.INTENT_ACTION_GLOBAL_SEARCH); + intent.setComponent(globalSearch); + PackageManager pm = mContext.getPackageManager(); List<ResolveInfo> activities = pm.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY); - int count = activities == null ? 0 : activities.size(); - for (int i = 0; i < count; i++) { - ActivityInfo ai = activities.get(i).activityInfo; - if (pm.checkPermission(Manifest.permission.GLOBAL_SEARCH, - ai.packageName) == PackageManager.PERMISSION_GRANTED) { - return new ComponentName(ai.packageName, ai.name); + + if (activities != null && !activities.isEmpty()) { + return true; + } + + return false; + } + + private static final Comparator<ResolveInfo> GLOBAL_SEARCH_RANKER = + new Comparator<ResolveInfo>() { + @Override + public int compare(ResolveInfo lhs, ResolveInfo rhs) { + if (lhs == rhs) { + return 0; + } + boolean lhsSystem = isSystemApp(lhs); + boolean rhsSystem = isSystemApp(rhs); + + if (lhsSystem && !rhsSystem) { + return -1; + } else if (rhsSystem && !lhsSystem) { + return 1; } else { - Log.w(LOG_TAG, "Package " + ai.packageName + " wants to handle GLOBAL_SEARCH, " - + "but does not have the GLOBAL_SEARCH permission."); + // Either both system engines, or both non system + // engines. + // + // Note, this isn't a typo. Higher priority numbers imply + // higher priority, but are "lower" in the sort order. + return rhs.priority - lhs.priority; } } + }; + + /** + * @return true iff. the resolve info corresponds to a system application. + */ + private static final boolean isSystemApp(ResolveInfo res) { + return (res.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0; + } + + /** + * Returns the highest ranked search provider as per the + * ranking defined in {@link #getGlobalSearchActivities()}. + */ + private ComponentName getDefaultGlobalSearchProvider(List<ResolveInfo> providerList) { + if (providerList != null && !providerList.isEmpty()) { + ActivityInfo ai = providerList.get(0).activityInfo; + return new ComponentName(ai.packageName, ai.name); + } + Log.w(LOG_TAG, "No global search activity found"); return null; } + private String getGlobalSearchProviderSetting() { + return Settings.Secure.getString(mContext.getContentResolver(), + Settings.Secure.SEARCH_GLOBAL_SEARCH_ACTIVITY); + } + /** * Finds the web search activity. * @@ -281,9 +377,9 @@ public class Searchables { PackageManager pm = mContext.getPackageManager(); List<ResolveInfo> activities = pm.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY); - int count = activities == null ? 0 : activities.size(); - for (int i = 0; i < count; i++) { - ActivityInfo ai = activities.get(i).activityInfo; + + if (activities != null && !activities.isEmpty()) { + ActivityInfo ai = activities.get(0).activityInfo; // TODO: do some sanity checks here? return new ComponentName(ai.packageName, ai.name); } @@ -307,10 +403,17 @@ public class Searchables { } /** + * Returns a list of activities that handle the global search intent. + */ + public synchronized ArrayList<ResolveInfo> getGlobalSearchActivities() { + return new ArrayList<ResolveInfo>(mGlobalSearchActivities); + } + + /** * Gets the name of the global search activity. */ public synchronized ComponentName getGlobalSearchActivity() { - return mGlobalSearchActivity; + return mCurrentGlobalSearchActivity; } /** |