2 * Copyright (C) 2007 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.server.search;
19 import android.app.ActivityManager;
20 import android.app.ActivityManagerNative;
21 import android.app.AppGlobals;
22 import android.app.IActivityManager;
23 import android.app.ISearchManager;
24 import android.app.SearchManager;
25 import android.app.SearchableInfo;
26 import android.content.ComponentName;
27 import android.content.ContentResolver;
28 import android.content.Context;
29 import android.content.Intent;
30 import android.content.pm.IPackageManager;
31 import android.content.pm.PackageManager;
32 import android.content.pm.ResolveInfo;
33 import android.database.ContentObserver;
34 import android.os.Binder;
35 import android.os.Bundle;
36 import android.os.Handler;
37 import android.os.RemoteException;
38 import android.os.UserHandle;
39 import android.os.UserManager;
40 import android.provider.Settings;
41 import android.util.Log;
42 import android.util.SparseArray;
44 import com.android.internal.annotations.GuardedBy;
45 import com.android.internal.content.PackageMonitor;
46 import com.android.internal.os.BackgroundThread;
47 import com.android.internal.util.IndentingPrintWriter;
48 import com.android.server.LocalServices;
49 import com.android.server.SystemService;
50 import com.android.server.statusbar.StatusBarManagerInternal;
52 import java.io.FileDescriptor;
53 import java.io.PrintWriter;
54 import java.util.List;
57 * The search manager service handles the search UI, and maintains a registry of
58 * searchable activities.
60 public class SearchManagerService extends ISearchManager.Stub {
61 private static final String TAG = "SearchManagerService";
62 final Handler mHandler;
64 public static class Lifecycle extends SystemService {
65 private SearchManagerService mService;
67 public Lifecycle(Context context) {
72 public void onStart() {
73 mService = new SearchManagerService(getContext());
74 publishBinderService(Context.SEARCH_SERVICE, mService);
78 public void onUnlockUser(final int userId) {
79 mService.mHandler.post(new Runnable() {
82 mService.onUnlockUser(userId);
88 public void onCleanupUser(int userHandle) {
89 mService.onCleanupUser(userHandle);
93 // Context that the service is running in.
94 private final Context mContext;
96 // This field is initialized lazily in getSearchables(), and then never modified.
97 @GuardedBy("mSearchables")
98 private final SparseArray<Searchables> mSearchables = new SparseArray<>();
101 * Initializes the Search Manager service in the provided system context.
102 * Only one instance of this object should be created!
104 * @param context to use for accessing DB, window manager, etc.
106 public SearchManagerService(Context context) {
108 new MyPackageMonitor().register(context, null, UserHandle.ALL, true);
109 new GlobalSearchProviderObserver(context.getContentResolver());
110 mHandler = BackgroundThread.getHandler();
113 private Searchables getSearchables(int userId) {
114 return getSearchables(userId, false);
117 private Searchables getSearchables(int userId, boolean forceUpdate) {
118 final long token = Binder.clearCallingIdentity();
120 final UserManager um = mContext.getSystemService(UserManager.class);
121 if (um.getUserInfo(userId) == null) {
122 throw new IllegalStateException("User " + userId + " doesn't exist");
124 if (!um.isUserUnlockingOrUnlocked(userId)) {
125 throw new IllegalStateException("User " + userId + " isn't unlocked");
128 Binder.restoreCallingIdentity(token);
130 synchronized (mSearchables) {
131 Searchables searchables = mSearchables.get(userId);
132 if (searchables == null) {
133 searchables = new Searchables(mContext, userId);
134 searchables.updateSearchableList();
135 mSearchables.append(userId, searchables);
136 } else if (forceUpdate) {
137 searchables.updateSearchableList();
143 private void onUnlockUser(int userId) {
145 getSearchables(userId, true);
146 } catch (IllegalStateException ignored) {
147 // We're just trying to warm a cache, so we don't mind if the user
148 // was stopped or destroyed before we got here.
152 private void onCleanupUser(int userId) {
153 synchronized (mSearchables) {
154 mSearchables.remove(userId);
159 * Refreshes the "searchables" list when packages are added/removed.
161 class MyPackageMonitor extends PackageMonitor {
164 public void onSomePackagesChanged() {
169 public void onPackageModified(String pkg) {
173 private void updateSearchables() {
174 final int changingUserId = getChangingUserId();
175 synchronized (mSearchables) {
176 // Update list of searchable activities
177 for (int i = 0; i < mSearchables.size(); i++) {
178 if (changingUserId == mSearchables.keyAt(i)) {
179 mSearchables.valueAt(i).updateSearchableList();
184 // Inform all listeners that the list of searchables has been updated.
185 Intent intent = new Intent(SearchManager.INTENT_ACTION_SEARCHABLES_CHANGED);
186 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
187 | Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
188 mContext.sendBroadcastAsUser(intent, new UserHandle(changingUserId));
192 class GlobalSearchProviderObserver extends ContentObserver {
193 private final ContentResolver mResolver;
195 public GlobalSearchProviderObserver(ContentResolver resolver) {
197 mResolver = resolver;
198 mResolver.registerContentObserver(
199 Settings.Secure.getUriFor(Settings.Secure.SEARCH_GLOBAL_SEARCH_ACTIVITY),
200 false /* notifyDescendants */,
205 public void onChange(boolean selfChange) {
206 synchronized (mSearchables) {
207 for (int i = 0; i < mSearchables.size(); i++) {
208 mSearchables.valueAt(i).updateSearchableList();
211 Intent intent = new Intent(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED);
212 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
213 mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
218 // Searchable activities API
222 * Returns the SearchableInfo for a given activity.
224 * @param launchActivity The activity from which we're launching this search.
225 * @return Returns a SearchableInfo record describing the parameters of the search,
226 * or null if no searchable metadata was available.
229 public SearchableInfo getSearchableInfo(final ComponentName launchActivity) {
230 if (launchActivity == null) {
231 Log.e(TAG, "getSearchableInfo(), activity == null");
234 return getSearchables(UserHandle.getCallingUserId()).getSearchableInfo(launchActivity);
238 * Returns a list of the searchable activities that can be included in global search.
241 public List<SearchableInfo> getSearchablesInGlobalSearch() {
242 return getSearchables(UserHandle.getCallingUserId()).getSearchablesInGlobalSearchList();
246 public List<ResolveInfo> getGlobalSearchActivities() {
247 return getSearchables(UserHandle.getCallingUserId()).getGlobalSearchActivities();
251 * Gets the name of the global search activity.
254 public ComponentName getGlobalSearchActivity() {
255 return getSearchables(UserHandle.getCallingUserId()).getGlobalSearchActivity();
259 * Gets the name of the web search activity.
262 public ComponentName getWebSearchActivity() {
263 return getSearchables(UserHandle.getCallingUserId()).getWebSearchActivity();
267 public void launchAssist(Bundle args) {
268 StatusBarManagerInternal statusBarManager =
269 LocalServices.getService(StatusBarManagerInternal.class);
270 if (statusBarManager != null) {
271 statusBarManager.startAssist(args);
275 private ComponentName getLegacyAssistComponent(int userHandle) {
277 userHandle = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
278 Binder.getCallingUid(), userHandle, true, false, "getLegacyAssistComponent", null);
279 IPackageManager pm = AppGlobals.getPackageManager();
280 Intent assistIntent = new Intent(Intent.ACTION_ASSIST);
282 pm.resolveIntent(assistIntent,
283 assistIntent.resolveTypeIfNeeded(mContext.getContentResolver()),
284 PackageManager.MATCH_DEFAULT_ONLY, userHandle);
286 return new ComponentName(
287 info.activityInfo.applicationInfo.packageName,
288 info.activityInfo.name);
290 } catch (RemoteException re) {
292 Log.e(TAG, "RemoteException in getLegacyAssistComponent: " + re);
293 } catch (Exception e) {
294 Log.e(TAG, "Exception in getLegacyAssistComponent: " + e);
300 public boolean launchLegacyAssist(String hint, int userHandle, Bundle args) {
301 ComponentName comp = getLegacyAssistComponent(userHandle);
305 long ident = Binder.clearCallingIdentity();
307 Intent intent = new Intent(Intent.ACTION_ASSIST);
308 intent.setComponent(comp);
309 IActivityManager am = ActivityManagerNative.getDefault();
310 return am.launchAssistIntent(intent, ActivityManager.ASSIST_CONTEXT_BASIC, hint,
312 } catch (RemoteException e) {
314 Binder.restoreCallingIdentity(ident);
320 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
321 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
323 IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
324 synchronized (mSearchables) {
325 for (int i = 0; i < mSearchables.size(); i++) {
326 ipw.print("\nUser: "); ipw.println(mSearchables.keyAt(i));
327 ipw.increaseIndent();
328 mSearchables.valueAt(i).dump(fd, ipw, args);
329 ipw.decreaseIndent();