blob: 8af76a17f08434d72e08d7b09719a8efb1b89ed8 [file] [log] [blame]
Karl Rosaen875d50a2009-04-23 19:00:21 -07001/*
2 * Copyright (C) 2009 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Jeff Sharkey7a96c392012-11-15 14:01:46 -080017package com.android.server.search;
Karl Rosaen875d50a2009-04-23 19:00:21 -070018
Amith Yamasani5bb87cd2012-06-14 11:32:13 -070019import android.app.AppGlobals;
Bjorn Bringert74708bb2009-04-28 11:26:52 +010020import android.app.SearchManager;
Bjorn Bringert2126aac2009-12-03 15:48:19 +000021import android.app.SearchableInfo;
Karl Rosaen875d50a2009-04-23 19:00:21 -070022import android.content.ComponentName;
23import android.content.Context;
24import android.content.Intent;
25import android.content.pm.ActivityInfo;
Narayan Kamathee69ff42011-06-28 12:07:18 +010026import android.content.pm.ApplicationInfo;
Amith Yamasani5bb87cd2012-06-14 11:32:13 -070027import android.content.pm.IPackageManager;
Karl Rosaen875d50a2009-04-23 19:00:21 -070028import android.content.pm.PackageManager;
Todd Kennedy662504f2018-03-14 08:09:00 -070029import android.content.pm.PackageManagerInternal;
Karl Rosaen875d50a2009-04-23 19:00:21 -070030import android.content.pm.ResolveInfo;
Amith Yamasanif203aee2012-08-29 18:41:53 -070031import android.os.Binder;
Karl Rosaen875d50a2009-04-23 19:00:21 -070032import android.os.Bundle;
Amith Yamasani5bb87cd2012-06-14 11:32:13 -070033import android.os.RemoteException;
Todd Kennedy662504f2018-03-14 08:09:00 -070034import android.os.UserHandle;
Narayan Kamathee69ff42011-06-28 12:07:18 +010035import android.provider.Settings;
36import android.text.TextUtils;
Satish Sampathcbd8a242009-05-15 21:47:07 +010037import android.util.Log;
Karl Rosaen875d50a2009-04-23 19:00:21 -070038
Todd Kennedy662504f2018-03-14 08:09:00 -070039import com.android.server.LocalServices;
40
Amith Yamasani64442c12012-10-07 08:17:46 -070041import java.io.FileDescriptor;
42import java.io.PrintWriter;
Karl Rosaen875d50a2009-04-23 19:00:21 -070043import java.util.ArrayList;
Narayan Kamathee69ff42011-06-28 12:07:18 +010044import java.util.Collections;
45import java.util.Comparator;
Karl Rosaen875d50a2009-04-23 19:00:21 -070046import java.util.HashMap;
47import java.util.List;
48
49/**
Satish Sampathf9acde22009-06-04 11:51:17 +010050 * This class maintains the information about all searchable activities.
Amith Yamasani5bb87cd2012-06-14 11:32:13 -070051 * This is a hidden class.
Karl Rosaen875d50a2009-04-23 19:00:21 -070052 */
53public class Searchables {
54
Satish Sampathcbd8a242009-05-15 21:47:07 +010055 private static final String LOG_TAG = "Searchables";
56
Karl Rosaen875d50a2009-04-23 19:00:21 -070057 // static strings used for XML lookups, etc.
Satish Sampathf9acde22009-06-04 11:51:17 +010058 // TODO how should these be documented for the developer, in a more structured way than
Karl Rosaen875d50a2009-04-23 19:00:21 -070059 // the current long wordy javadoc in SearchManager.java ?
60 private static final String MD_LABEL_DEFAULT_SEARCHABLE = "android.app.default_searchable";
61 private static final String MD_SEARCHABLE_SYSTEM_SEARCH = "*";
Satish Sampathf9acde22009-06-04 11:51:17 +010062
Karl Rosaen875d50a2009-04-23 19:00:21 -070063 private Context mContext;
Satish Sampathf9acde22009-06-04 11:51:17 +010064
Karl Rosaen875d50a2009-04-23 19:00:21 -070065 private HashMap<ComponentName, SearchableInfo> mSearchablesMap = null;
66 private ArrayList<SearchableInfo> mSearchablesList = null;
Bjorn Bringert6d72e022009-04-29 14:56:12 +010067 private ArrayList<SearchableInfo> mSearchablesInGlobalSearchList = null;
Narayan Kamathee69ff42011-06-28 12:07:18 +010068 // Contains all installed activities that handle the global search
69 // intent.
70 private List<ResolveInfo> mGlobalSearchActivities;
71 private ComponentName mCurrentGlobalSearchActivity = null;
Bjorn Bringert6cf7a322010-02-23 13:17:06 +000072 private ComponentName mWebSearchActivity = null;
Satish Sampathf9acde22009-06-04 11:51:17 +010073
Satish Sampath41282a32009-06-23 17:05:35 +010074 public static String GOOGLE_SEARCH_COMPONENT_NAME =
75 "com.android.googlesearch/.GoogleSearch";
76 public static String ENHANCED_GOOGLE_SEARCH_COMPONENT_NAME =
77 "com.google.android.providers.enhancedgooglesearch/.Launcher";
78
Amith Yamasani5bb87cd2012-06-14 11:32:13 -070079 // Cache the package manager instance
Amith Yamasanif203aee2012-08-29 18:41:53 -070080 final private IPackageManager mPm;
Amith Yamasani5bb87cd2012-06-14 11:32:13 -070081 // User for which this Searchables caches information
82 private int mUserId;
83
Karl Rosaen875d50a2009-04-23 19:00:21 -070084 /**
Satish Sampathf9acde22009-06-04 11:51:17 +010085 *
Karl Rosaen875d50a2009-04-23 19:00:21 -070086 * @param context Context to use for looking up activities etc.
87 */
Amith Yamasani5bb87cd2012-06-14 11:32:13 -070088 public Searchables (Context context, int userId) {
Karl Rosaen875d50a2009-04-23 19:00:21 -070089 mContext = context;
Amith Yamasani5bb87cd2012-06-14 11:32:13 -070090 mUserId = userId;
Amith Yamasanif203aee2012-08-29 18:41:53 -070091 mPm = AppGlobals.getPackageManager();
Karl Rosaen875d50a2009-04-23 19:00:21 -070092 }
Satish Sampathf9acde22009-06-04 11:51:17 +010093
Karl Rosaen875d50a2009-04-23 19:00:21 -070094 /**
95 * Look up, or construct, based on the activity.
Satish Sampathf9acde22009-06-04 11:51:17 +010096 *
97 * The activities fall into three cases, based on meta-data found in
Karl Rosaen875d50a2009-04-23 19:00:21 -070098 * the manifest entry:
99 * <ol>
100 * <li>The activity itself implements search. This is indicated by the
101 * presence of a "android.app.searchable" meta-data attribute.
102 * The value is a reference to an XML file containing search information.</li>
103 * <li>A related activity implements search. This is indicated by the
104 * presence of a "android.app.default_searchable" meta-data attribute.
105 * The value is a string naming the activity implementing search. In this
106 * case the factory will "redirect" and return the searchable data.</li>
107 * <li>No searchability data is provided. We return null here and other
108 * code will insert the "default" (e.g. contacts) search.
Satish Sampathf9acde22009-06-04 11:51:17 +0100109 *
Karl Rosaen875d50a2009-04-23 19:00:21 -0700110 * TODO: cache the result in the map, and check the map first.
111 * TODO: it might make sense to implement the searchable reference as
112 * an application meta-data entry. This way we don't have to pepper each
113 * and every activity.
114 * TODO: can we skip the constructor step if it's a non-searchable?
Satish Sampathf9acde22009-06-04 11:51:17 +0100115 * TODO: does it make sense to plug the default into a slot here for
Karl Rosaen875d50a2009-04-23 19:00:21 -0700116 * automatic return? Probably not, but it's one way to do it.
117 *
Satish Sampathf9acde22009-06-04 11:51:17 +0100118 * @param activity The name of the current activity, or null if the
Karl Rosaen875d50a2009-04-23 19:00:21 -0700119 * activity does not define any explicit searchable metadata.
120 */
121 public SearchableInfo getSearchableInfo(ComponentName activity) {
122 // Step 1. Is the result already hashed? (case 1)
123 SearchableInfo result;
124 synchronized (this) {
125 result = mSearchablesMap.get(activity);
Todd Kennedy662504f2018-03-14 08:09:00 -0700126 if (result != null) {
127 final PackageManagerInternal pm =
128 LocalServices.getService(PackageManagerInternal.class);
129 if (pm.canAccessComponent(Binder.getCallingUid(), result.getSearchActivity(),
130 UserHandle.getCallingUserId())) {
131 return result;
132 }
133 return null;
134 }
Karl Rosaen875d50a2009-04-23 19:00:21 -0700135 }
Satish Sampathf9acde22009-06-04 11:51:17 +0100136
Karl Rosaen875d50a2009-04-23 19:00:21 -0700137 // Step 2. See if the current activity references a searchable.
138 // Note: Conceptually, this could be a while(true) loop, but there's
Satish Sampathf9acde22009-06-04 11:51:17 +0100139 // no point in implementing reference chaining here and risking a loop.
Karl Rosaen875d50a2009-04-23 19:00:21 -0700140 // References must point directly to searchable activities.
Satish Sampathf9acde22009-06-04 11:51:17 +0100141
Karl Rosaen875d50a2009-04-23 19:00:21 -0700142 ActivityInfo ai = null;
143 try {
Amith Yamasanif203aee2012-08-29 18:41:53 -0700144 ai = mPm.getActivityInfo(activity, PackageManager.GET_META_DATA, mUserId);
145 } catch (RemoteException re) {
146 Log.e(LOG_TAG, "Error getting activity info " + re);
147 return null;
148 }
149 String refActivityName = null;
Satish Sampathf9acde22009-06-04 11:51:17 +0100150
Amith Yamasanif203aee2012-08-29 18:41:53 -0700151 // First look for activity-specific reference
152 Bundle md = ai.metaData;
153 if (md != null) {
154 refActivityName = md.getString(MD_LABEL_DEFAULT_SEARCHABLE);
155 }
156 // If not found, try for app-wide reference
157 if (refActivityName == null) {
158 md = ai.applicationInfo.metaData;
Karl Rosaen875d50a2009-04-23 19:00:21 -0700159 if (md != null) {
160 refActivityName = md.getString(MD_LABEL_DEFAULT_SEARCHABLE);
161 }
Amith Yamasanif203aee2012-08-29 18:41:53 -0700162 }
163
164 // Irrespective of source, if a reference was found, follow it.
165 if (refActivityName != null)
166 {
167 // This value is deprecated, return null
168 if (refActivityName.equals(MD_SEARCHABLE_SYSTEM_SEARCH)) {
169 return null;
170 }
171 String pkg = activity.getPackageName();
172 ComponentName referredActivity;
173 if (refActivityName.charAt(0) == '.') {
174 referredActivity = new ComponentName(pkg, pkg + refActivityName);
175 } else {
176 referredActivity = new ComponentName(pkg, refActivityName);
Karl Rosaen875d50a2009-04-23 19:00:21 -0700177 }
Satish Sampathf9acde22009-06-04 11:51:17 +0100178
Amith Yamasanif203aee2012-08-29 18:41:53 -0700179 // Now try the referred activity, and if found, cache
180 // it against the original name so we can skip the check
181 synchronized (this) {
182 result = mSearchablesMap.get(referredActivity);
183 if (result != null) {
184 mSearchablesMap.put(activity, result);
Todd Kennedy662504f2018-03-14 08:09:00 -0700185 }
186 }
187 if (result != null) {
188 final PackageManagerInternal pm =
189 LocalServices.getService(PackageManagerInternal.class);
190 if (pm.canAccessComponent(Binder.getCallingUid(), result.getSearchActivity(),
191 UserHandle.getCallingUserId())) {
Amith Yamasanif203aee2012-08-29 18:41:53 -0700192 return result;
Karl Rosaen875d50a2009-04-23 19:00:21 -0700193 }
Todd Kennedy662504f2018-03-14 08:09:00 -0700194 return null;
Karl Rosaen875d50a2009-04-23 19:00:21 -0700195 }
Karl Rosaen875d50a2009-04-23 19:00:21 -0700196 }
Satish Sampathf9acde22009-06-04 11:51:17 +0100197
Karl Rosaen875d50a2009-04-23 19:00:21 -0700198 // Step 3. None found. Return null.
199 return null;
Satish Sampathf9acde22009-06-04 11:51:17 +0100200
Karl Rosaen875d50a2009-04-23 19:00:21 -0700201 }
Satish Sampathf9acde22009-06-04 11:51:17 +0100202
Karl Rosaen875d50a2009-04-23 19:00:21 -0700203 /**
Satish Sampathf9acde22009-06-04 11:51:17 +0100204 * Builds an entire list (suitable for display) of
205 * activities that are searchable, by iterating the entire set of
206 * ACTION_SEARCH & ACTION_WEB_SEARCH intents.
207 *
Karl Rosaen875d50a2009-04-23 19:00:21 -0700208 * Also clears the hash of all activities -> searches which will
209 * refill as the user clicks "search".
Satish Sampathf9acde22009-06-04 11:51:17 +0100210 *
Karl Rosaen875d50a2009-04-23 19:00:21 -0700211 * This should only be done at startup and again if we know that the
212 * list has changed.
Satish Sampathf9acde22009-06-04 11:51:17 +0100213 *
Karl Rosaen875d50a2009-04-23 19:00:21 -0700214 * TODO: every activity that provides a ACTION_SEARCH intent should
215 * also provide searchability meta-data. There are a bunch of checks here
216 * that, if data is not found, silently skip to the next activity. This
217 * won't help a developer trying to figure out why their activity isn't
218 * showing up in the list, but an exception here is too rough. I would
219 * like to find a better notification mechanism.
Satish Sampathf9acde22009-06-04 11:51:17 +0100220 *
Karl Rosaen875d50a2009-04-23 19:00:21 -0700221 * TODO: sort the list somehow? UI choice.
222 */
Jeff Sharkey4175be22016-01-09 14:57:45 -0700223 public void updateSearchableList() {
Bjorn Bringert74708bb2009-04-28 11:26:52 +0100224 // These will become the new values at the end of the method
Satish Sampathf9acde22009-06-04 11:51:17 +0100225 HashMap<ComponentName, SearchableInfo> newSearchablesMap
Karl Rosaen875d50a2009-04-23 19:00:21 -0700226 = new HashMap<ComponentName, SearchableInfo>();
227 ArrayList<SearchableInfo> newSearchablesList
228 = new ArrayList<SearchableInfo>();
Bjorn Bringert6d72e022009-04-29 14:56:12 +0100229 ArrayList<SearchableInfo> newSearchablesInGlobalSearchList
230 = new ArrayList<SearchableInfo>();
Karl Rosaen875d50a2009-04-23 19:00:21 -0700231
Satish Sampathf9acde22009-06-04 11:51:17 +0100232 // Use intent resolver to generate list of ACTION_SEARCH & ACTION_WEB_SEARCH receivers.
233 List<ResolveInfo> searchList;
Karl Rosaen875d50a2009-04-23 19:00:21 -0700234 final Intent intent = new Intent(Intent.ACTION_SEARCH);
Satish Sampathf9acde22009-06-04 11:51:17 +0100235
Amith Yamasani64442c12012-10-07 08:17:46 -0700236 long ident = Binder.clearCallingIdentity();
237 try {
Jeff Sharkey4175be22016-01-09 14:57:45 -0700238 searchList = queryIntentActivities(intent,
239 PackageManager.GET_META_DATA | PackageManager.MATCH_DEBUG_TRIAGED_MISSING);
Satish Sampathf9acde22009-06-04 11:51:17 +0100240
Amith Yamasani64442c12012-10-07 08:17:46 -0700241 List<ResolveInfo> webSearchInfoList;
242 final Intent webSearchIntent = new Intent(Intent.ACTION_WEB_SEARCH);
Jeff Sharkey4175be22016-01-09 14:57:45 -0700243 webSearchInfoList = queryIntentActivities(webSearchIntent,
244 PackageManager.GET_META_DATA | PackageManager.MATCH_DEBUG_TRIAGED_MISSING);
Amith Yamasani64442c12012-10-07 08:17:46 -0700245
246 // analyze each one, generate a Searchables record, and record
247 if (searchList != null || webSearchInfoList != null) {
248 int search_count = (searchList == null ? 0 : searchList.size());
249 int web_search_count = (webSearchInfoList == null ? 0 : webSearchInfoList.size());
250 int count = search_count + web_search_count;
251 for (int ii = 0; ii < count; ii++) {
252 // for each component, try to find metadata
253 ResolveInfo info = (ii < search_count)
254 ? searchList.get(ii)
255 : webSearchInfoList.get(ii - search_count);
256 ActivityInfo ai = info.activityInfo;
257 // Check first to avoid duplicate entries.
258 if (newSearchablesMap.get(new ComponentName(ai.packageName, ai.name)) == null) {
259 SearchableInfo searchable = SearchableInfo.getActivityMetaData(mContext, ai,
260 mUserId);
261 if (searchable != null) {
262 newSearchablesList.add(searchable);
263 newSearchablesMap.put(searchable.getSearchActivity(), searchable);
264 if (searchable.shouldIncludeInGlobalSearch()) {
265 newSearchablesInGlobalSearchList.add(searchable);
266 }
Karl Rosaen590f6342009-08-27 17:42:48 -0700267 }
Bjorn Bringert6d72e022009-04-29 14:56:12 +0100268 }
Karl Rosaen875d50a2009-04-23 19:00:21 -0700269 }
270 }
Satish Sampathf9acde22009-06-04 11:51:17 +0100271
Amith Yamasani64442c12012-10-07 08:17:46 -0700272 List<ResolveInfo> newGlobalSearchActivities = findGlobalSearchActivities();
Narayan Kamathee69ff42011-06-28 12:07:18 +0100273
Amith Yamasani64442c12012-10-07 08:17:46 -0700274 // Find the global search activity
275 ComponentName newGlobalSearchActivity = findGlobalSearchActivity(
276 newGlobalSearchActivities);
Satish Sampathf9acde22009-06-04 11:51:17 +0100277
Amith Yamasani64442c12012-10-07 08:17:46 -0700278 // Find the web search activity
279 ComponentName newWebSearchActivity = findWebSearchActivity(newGlobalSearchActivity);
Satish Sampathf9acde22009-06-04 11:51:17 +0100280
Amith Yamasani64442c12012-10-07 08:17:46 -0700281 // Store a consistent set of new values
282 synchronized (this) {
283 mSearchablesMap = newSearchablesMap;
284 mSearchablesList = newSearchablesList;
285 mSearchablesInGlobalSearchList = newSearchablesInGlobalSearchList;
286 mGlobalSearchActivities = newGlobalSearchActivities;
287 mCurrentGlobalSearchActivity = newGlobalSearchActivity;
288 mWebSearchActivity = newWebSearchActivity;
289 }
290 } finally {
291 Binder.restoreCallingIdentity(ident);
Karl Rosaen875d50a2009-04-23 19:00:21 -0700292 }
293 }
Amith Yamasani64442c12012-10-07 08:17:46 -0700294
Satish Sampathf9acde22009-06-04 11:51:17 +0100295 /**
Narayan Kamathee69ff42011-06-28 12:07:18 +0100296 * Returns a sorted list of installed search providers as per
297 * the following heuristics:
Bjorn Bringert6cf7a322010-02-23 13:17:06 +0000298 *
Narayan Kamathee69ff42011-06-28 12:07:18 +0100299 * (a) System apps are given priority over non system apps.
300 * (b) Among system apps and non system apps, the relative ordering
301 * is defined by their declared priority.
Satish Sampathf9acde22009-06-04 11:51:17 +0100302 */
Narayan Kamathee69ff42011-06-28 12:07:18 +0100303 private List<ResolveInfo> findGlobalSearchActivities() {
304 // Step 1 : Query the package manager for a list
305 // of activities that can handle the GLOBAL_SEARCH intent.
Bjorn Bringert6cf7a322010-02-23 13:17:06 +0000306 Intent intent = new Intent(SearchManager.INTENT_ACTION_GLOBAL_SEARCH);
Jeff Sharkey4175be22016-01-09 14:57:45 -0700307 List<ResolveInfo> activities = queryIntentActivities(intent,
308 PackageManager.MATCH_DEFAULT_ONLY | PackageManager.MATCH_DEBUG_TRIAGED_MISSING);
Narayan Kamathee69ff42011-06-28 12:07:18 +0100309 if (activities != null && !activities.isEmpty()) {
310 // Step 2: Rank matching activities according to our heuristics.
311 Collections.sort(activities, GLOBAL_SEARCH_RANKER);
312 }
313
314 return activities;
315 }
316
317 /**
318 * Finds the global search activity.
319 */
320 private ComponentName findGlobalSearchActivity(List<ResolveInfo> installed) {
321 // Fetch the global search provider from the system settings,
322 // and if it's still installed, return it.
323 final String searchProviderSetting = getGlobalSearchProviderSetting();
324 if (!TextUtils.isEmpty(searchProviderSetting)) {
325 final ComponentName globalSearchComponent = ComponentName.unflattenFromString(
326 searchProviderSetting);
327 if (globalSearchComponent != null && isInstalled(globalSearchComponent)) {
328 return globalSearchComponent;
Bjorn Bringert6cf7a322010-02-23 13:17:06 +0000329 }
Satish Sampathf9acde22009-06-04 11:51:17 +0100330 }
Narayan Kamathee69ff42011-06-28 12:07:18 +0100331
332 return getDefaultGlobalSearchProvider(installed);
333 }
334
335 /**
336 * Checks whether the global search provider with a given
337 * component name is installed on the system or not. This deals with
338 * cases such as the removal of an installed provider.
339 */
340 private boolean isInstalled(ComponentName globalSearch) {
341 Intent intent = new Intent(SearchManager.INTENT_ACTION_GLOBAL_SEARCH);
342 intent.setComponent(globalSearch);
343
Amith Yamasani5bb87cd2012-06-14 11:32:13 -0700344 List<ResolveInfo> activities = queryIntentActivities(intent,
345 PackageManager.MATCH_DEFAULT_ONLY);
Narayan Kamathee69ff42011-06-28 12:07:18 +0100346 if (activities != null && !activities.isEmpty()) {
347 return true;
348 }
349
350 return false;
351 }
352
353 private static final Comparator<ResolveInfo> GLOBAL_SEARCH_RANKER =
354 new Comparator<ResolveInfo>() {
355 @Override
356 public int compare(ResolveInfo lhs, ResolveInfo rhs) {
357 if (lhs == rhs) {
358 return 0;
359 }
360 boolean lhsSystem = isSystemApp(lhs);
361 boolean rhsSystem = isSystemApp(rhs);
362
363 if (lhsSystem && !rhsSystem) {
364 return -1;
365 } else if (rhsSystem && !lhsSystem) {
366 return 1;
367 } else {
368 // Either both system engines, or both non system
369 // engines.
370 //
371 // Note, this isn't a typo. Higher priority numbers imply
372 // higher priority, but are "lower" in the sort order.
373 return rhs.priority - lhs.priority;
374 }
375 }
376 };
377
378 /**
379 * @return true iff. the resolve info corresponds to a system application.
380 */
381 private static final boolean isSystemApp(ResolveInfo res) {
382 return (res.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
383 }
384
385 /**
386 * Returns the highest ranked search provider as per the
387 * ranking defined in {@link #getGlobalSearchActivities()}.
388 */
389 private ComponentName getDefaultGlobalSearchProvider(List<ResolveInfo> providerList) {
390 if (providerList != null && !providerList.isEmpty()) {
391 ActivityInfo ai = providerList.get(0).activityInfo;
392 return new ComponentName(ai.packageName, ai.name);
393 }
394
Bjorn Bringert6cf7a322010-02-23 13:17:06 +0000395 Log.w(LOG_TAG, "No global search activity found");
396 return null;
Satish Sampathf9acde22009-06-04 11:51:17 +0100397 }
398
Narayan Kamathee69ff42011-06-28 12:07:18 +0100399 private String getGlobalSearchProviderSetting() {
400 return Settings.Secure.getString(mContext.getContentResolver(),
401 Settings.Secure.SEARCH_GLOBAL_SEARCH_ACTIVITY);
402 }
403
Bjorn Bringert6cf7a322010-02-23 13:17:06 +0000404 /**
405 * Finds the web search activity.
406 *
407 * Only looks in the package of the global search activity.
408 */
409 private ComponentName findWebSearchActivity(ComponentName globalSearchActivity) {
410 if (globalSearchActivity == null) {
411 return null;
Satish Sampathf9acde22009-06-04 11:51:17 +0100412 }
Bjorn Bringert6cf7a322010-02-23 13:17:06 +0000413 Intent intent = new Intent(Intent.ACTION_WEB_SEARCH);
414 intent.setPackage(globalSearchActivity.getPackageName());
Bjorn Bringert6cf7a322010-02-23 13:17:06 +0000415 List<ResolveInfo> activities =
Amith Yamasani5bb87cd2012-06-14 11:32:13 -0700416 queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
Narayan Kamathee69ff42011-06-28 12:07:18 +0100417
418 if (activities != null && !activities.isEmpty()) {
419 ActivityInfo ai = activities.get(0).activityInfo;
Bjorn Bringert6cf7a322010-02-23 13:17:06 +0000420 // TODO: do some sanity checks here?
421 return new ComponentName(ai.packageName, ai.name);
422 }
423 Log.w(LOG_TAG, "No web search activity found");
424 return null;
Satish Sampathf9acde22009-06-04 11:51:17 +0100425 }
426
Amith Yamasani5bb87cd2012-06-14 11:32:13 -0700427 private List<ResolveInfo> queryIntentActivities(Intent intent, int flags) {
Amith Yamasani5bb87cd2012-06-14 11:32:13 -0700428 List<ResolveInfo> activities = null;
429 try {
430 activities =
431 mPm.queryIntentActivities(intent,
432 intent.resolveTypeIfNeeded(mContext.getContentResolver()),
Todd Kennedy662504f2018-03-14 08:09:00 -0700433 flags | PackageManager.MATCH_INSTANT, mUserId).getList();
Amith Yamasani5bb87cd2012-06-14 11:32:13 -0700434 } catch (RemoteException re) {
435 // Local call
436 }
437 return activities;
438 }
439
Karl Rosaen875d50a2009-04-23 19:00:21 -0700440 /**
441 * Returns the list of searchable activities.
442 */
443 public synchronized ArrayList<SearchableInfo> getSearchablesList() {
Todd Kennedy662504f2018-03-14 08:09:00 -0700444 return createFilterdSearchableInfoList(mSearchablesList);
Karl Rosaen875d50a2009-04-23 19:00:21 -0700445 }
Satish Sampathf9acde22009-06-04 11:51:17 +0100446
Bjorn Bringert6d72e022009-04-29 14:56:12 +0100447 /**
448 * Returns a list of the searchable activities that can be included in global search.
449 */
450 public synchronized ArrayList<SearchableInfo> getSearchablesInGlobalSearchList() {
Todd Kennedy662504f2018-03-14 08:09:00 -0700451 return createFilterdSearchableInfoList(mSearchablesInGlobalSearchList);
Bjorn Bringert6d72e022009-04-29 14:56:12 +0100452 }
Satish Sampathf9acde22009-06-04 11:51:17 +0100453
454 /**
Narayan Kamathee69ff42011-06-28 12:07:18 +0100455 * Returns a list of activities that handle the global search intent.
456 */
457 public synchronized ArrayList<ResolveInfo> getGlobalSearchActivities() {
Todd Kennedy662504f2018-03-14 08:09:00 -0700458 return createFilterdResolveInfoList(mGlobalSearchActivities);
459 }
460
461 private ArrayList<SearchableInfo> createFilterdSearchableInfoList(List<SearchableInfo> list) {
462 if (list == null) {
463 return null;
464 }
465 final ArrayList<SearchableInfo> resultList = new ArrayList<>(list.size());
466 final PackageManagerInternal pm = LocalServices.getService(PackageManagerInternal.class);
467 final int callingUid = Binder.getCallingUid();
468 final int callingUserId = UserHandle.getCallingUserId();
469 for (SearchableInfo info : list) {
470 if (pm.canAccessComponent(callingUid, info.getSearchActivity(), callingUserId)) {
471 resultList.add(info);
472 }
473 }
474 return resultList;
475 }
476
477 private ArrayList<ResolveInfo> createFilterdResolveInfoList(List<ResolveInfo> list) {
478 if (list == null) {
479 return null;
480 }
481 final ArrayList<ResolveInfo> resultList = new ArrayList<>(list.size());
482 final PackageManagerInternal pm = LocalServices.getService(PackageManagerInternal.class);
483 final int callingUid = Binder.getCallingUid();
484 final int callingUserId = UserHandle.getCallingUserId();
485 for (ResolveInfo info : list) {
486 if (pm.canAccessComponent(
487 callingUid, info.activityInfo.getComponentName(), callingUserId)) {
488 resultList.add(info);
489 }
490 }
491 return resultList;
Narayan Kamathee69ff42011-06-28 12:07:18 +0100492 }
493
494 /**
Bjorn Bringert6cf7a322010-02-23 13:17:06 +0000495 * Gets the name of the global search activity.
Satish Sampathf9acde22009-06-04 11:51:17 +0100496 */
Bjorn Bringert6cf7a322010-02-23 13:17:06 +0000497 public synchronized ComponentName getGlobalSearchActivity() {
Todd Kennedy662504f2018-03-14 08:09:00 -0700498 final PackageManagerInternal pm = LocalServices.getService(PackageManagerInternal.class);
499 final int callingUid = Binder.getCallingUid();
500 final int callingUserId = UserHandle.getCallingUserId();
501 if (mCurrentGlobalSearchActivity != null
502 && pm.canAccessComponent(callingUid, mCurrentGlobalSearchActivity, callingUserId)) {
503 return mCurrentGlobalSearchActivity;
504 }
505 return null;
Satish Sampathf9acde22009-06-04 11:51:17 +0100506 }
507
508 /**
Bjorn Bringert6cf7a322010-02-23 13:17:06 +0000509 * Gets the name of the web search activity.
Satish Sampathf9acde22009-06-04 11:51:17 +0100510 */
Bjorn Bringert6cf7a322010-02-23 13:17:06 +0000511 public synchronized ComponentName getWebSearchActivity() {
Todd Kennedy662504f2018-03-14 08:09:00 -0700512 final PackageManagerInternal pm = LocalServices.getService(PackageManagerInternal.class);
513 final int callingUid = Binder.getCallingUid();
514 final int callingUserId = UserHandle.getCallingUserId();
515 if (mWebSearchActivity != null
516 && pm.canAccessComponent(callingUid, mWebSearchActivity, callingUserId)) {
517 return mWebSearchActivity;
518 }
519 return null;
Satish Sampathf9acde22009-06-04 11:51:17 +0100520 }
Amith Yamasani64442c12012-10-07 08:17:46 -0700521
522 void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
523 pw.println("Searchable authorities:");
524 synchronized (this) {
525 if (mSearchablesList != null) {
526 for (SearchableInfo info: mSearchablesList) {
527 pw.print(" "); pw.println(info.getSuggestAuthority());
528 }
529 }
530 }
531 }
Karl Rosaen875d50a2009-04-23 19:00:21 -0700532}