SearchManagerService.java revision ce18c8167766f92856f94a8e88e19de4698960e6
1/* 2 * Copyright (C) 2007 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 17package com.android.server.search; 18 19import android.app.ActivityManager; 20import android.app.ActivityManagerNative; 21import android.app.AppGlobals; 22import android.app.IActivityManager; 23import android.app.ISearchManager; 24import android.app.SearchManager; 25import android.app.SearchableInfo; 26import android.content.ComponentName; 27import android.content.ContentResolver; 28import android.content.Context; 29import android.content.Intent; 30import android.content.pm.IPackageManager; 31import android.content.pm.PackageManager; 32import android.content.pm.ResolveInfo; 33import android.database.ContentObserver; 34import android.os.Binder; 35import android.os.Bundle; 36import android.os.RemoteException; 37import android.os.UserHandle; 38import android.os.UserManager; 39import android.provider.Settings; 40import android.util.Log; 41import android.util.SparseArray; 42 43import com.android.internal.annotations.GuardedBy; 44import com.android.internal.content.PackageMonitor; 45import com.android.internal.util.IndentingPrintWriter; 46import com.android.server.LocalServices; 47import com.android.server.SystemService; 48import com.android.server.statusbar.StatusBarManagerInternal; 49 50import java.io.FileDescriptor; 51import java.io.PrintWriter; 52import java.util.List; 53 54/** 55 * The search manager service handles the search UI, and maintains a registry of 56 * searchable activities. 57 */ 58public class SearchManagerService extends ISearchManager.Stub { 59 private static final String TAG = "SearchManagerService"; 60 61 public static class Lifecycle extends SystemService { 62 private SearchManagerService mService; 63 64 public Lifecycle(Context context) { 65 super(context); 66 } 67 68 @Override 69 public void onStart() { 70 mService = new SearchManagerService(getContext()); 71 publishBinderService(Context.SEARCH_SERVICE, mService); 72 } 73 74 @Override 75 public void onUnlockUser(int userHandle) { 76 mService.onUnlockUser(userHandle); 77 } 78 79 @Override 80 public void onCleanupUser(int userHandle) { 81 mService.onCleanupUser(userHandle); 82 } 83 } 84 85 // Context that the service is running in. 86 private final Context mContext; 87 88 // This field is initialized lazily in getSearchables(), and then never modified. 89 @GuardedBy("mSearchables") 90 private final SparseArray<Searchables> mSearchables = new SparseArray<>(); 91 92 /** 93 * Initializes the Search Manager service in the provided system context. 94 * Only one instance of this object should be created! 95 * 96 * @param context to use for accessing DB, window manager, etc. 97 */ 98 public SearchManagerService(Context context) { 99 mContext = context; 100 new MyPackageMonitor().register(context, null, UserHandle.ALL, true); 101 new GlobalSearchProviderObserver(context.getContentResolver()); 102 } 103 104 private Searchables getSearchables(int userId) { 105 return getSearchables(userId, false); 106 } 107 108 private Searchables getSearchables(int userId, boolean forceUpdate) { 109 final long token = Binder.clearCallingIdentity(); 110 try { 111 final UserManager um = mContext.getSystemService(UserManager.class); 112 if (um.getUserInfo(userId) == null) { 113 throw new IllegalStateException("User " + userId + " doesn't exist"); 114 } 115 if (!um.isUserUnlockingOrUnlocked(userId)) { 116 throw new IllegalStateException("User " + userId + " isn't unlocked"); 117 } 118 } finally { 119 Binder.restoreCallingIdentity(token); 120 } 121 synchronized (mSearchables) { 122 Searchables searchables = mSearchables.get(userId); 123 if (searchables == null) { 124 searchables = new Searchables(mContext, userId); 125 searchables.updateSearchableList(); 126 mSearchables.append(userId, searchables); 127 } else if (forceUpdate) { 128 searchables.updateSearchableList(); 129 } 130 return searchables; 131 } 132 } 133 134 private void onUnlockUser(int userId) { 135 getSearchables(userId, true); 136 } 137 138 private void onCleanupUser(int userId) { 139 synchronized (mSearchables) { 140 mSearchables.remove(userId); 141 } 142 } 143 144 /** 145 * Refreshes the "searchables" list when packages are added/removed. 146 */ 147 class MyPackageMonitor extends PackageMonitor { 148 149 @Override 150 public void onSomePackagesChanged() { 151 updateSearchables(); 152 } 153 154 @Override 155 public void onPackageModified(String pkg) { 156 updateSearchables(); 157 } 158 159 private void updateSearchables() { 160 final int changingUserId = getChangingUserId(); 161 synchronized (mSearchables) { 162 // Update list of searchable activities 163 for (int i = 0; i < mSearchables.size(); i++) { 164 if (changingUserId == mSearchables.keyAt(i)) { 165 mSearchables.valueAt(i).updateSearchableList(); 166 break; 167 } 168 } 169 } 170 // Inform all listeners that the list of searchables has been updated. 171 Intent intent = new Intent(SearchManager.INTENT_ACTION_SEARCHABLES_CHANGED); 172 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING 173 | Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 174 mContext.sendBroadcastAsUser(intent, new UserHandle(changingUserId)); 175 } 176 } 177 178 class GlobalSearchProviderObserver extends ContentObserver { 179 private final ContentResolver mResolver; 180 181 public GlobalSearchProviderObserver(ContentResolver resolver) { 182 super(null); 183 mResolver = resolver; 184 mResolver.registerContentObserver( 185 Settings.Secure.getUriFor(Settings.Secure.SEARCH_GLOBAL_SEARCH_ACTIVITY), 186 false /* notifyDescendants */, 187 this); 188 } 189 190 @Override 191 public void onChange(boolean selfChange) { 192 synchronized (mSearchables) { 193 for (int i = 0; i < mSearchables.size(); i++) { 194 mSearchables.valueAt(i).updateSearchableList(); 195 } 196 } 197 Intent intent = new Intent(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED); 198 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); 199 mContext.sendBroadcastAsUser(intent, UserHandle.ALL); 200 } 201 } 202 203 // 204 // Searchable activities API 205 // 206 207 /** 208 * Returns the SearchableInfo for a given activity. 209 * 210 * @param launchActivity The activity from which we're launching this search. 211 * @return Returns a SearchableInfo record describing the parameters of the search, 212 * or null if no searchable metadata was available. 213 */ 214 @Override 215 public SearchableInfo getSearchableInfo(final ComponentName launchActivity) { 216 if (launchActivity == null) { 217 Log.e(TAG, "getSearchableInfo(), activity == null"); 218 return null; 219 } 220 return getSearchables(UserHandle.getCallingUserId()).getSearchableInfo(launchActivity); 221 } 222 223 /** 224 * Returns a list of the searchable activities that can be included in global search. 225 */ 226 @Override 227 public List<SearchableInfo> getSearchablesInGlobalSearch() { 228 return getSearchables(UserHandle.getCallingUserId()).getSearchablesInGlobalSearchList(); 229 } 230 231 @Override 232 public List<ResolveInfo> getGlobalSearchActivities() { 233 return getSearchables(UserHandle.getCallingUserId()).getGlobalSearchActivities(); 234 } 235 236 /** 237 * Gets the name of the global search activity. 238 */ 239 @Override 240 public ComponentName getGlobalSearchActivity() { 241 return getSearchables(UserHandle.getCallingUserId()).getGlobalSearchActivity(); 242 } 243 244 /** 245 * Gets the name of the web search activity. 246 */ 247 @Override 248 public ComponentName getWebSearchActivity() { 249 return getSearchables(UserHandle.getCallingUserId()).getWebSearchActivity(); 250 } 251 252 @Override 253 public void launchAssist(Bundle args) { 254 StatusBarManagerInternal statusBarManager = 255 LocalServices.getService(StatusBarManagerInternal.class); 256 if (statusBarManager != null) { 257 statusBarManager.startAssist(args); 258 } 259 } 260 261 private ComponentName getLegacyAssistComponent(int userHandle) { 262 try { 263 userHandle = ActivityManager.handleIncomingUser(Binder.getCallingPid(), 264 Binder.getCallingUid(), userHandle, true, false, "getLegacyAssistComponent", null); 265 IPackageManager pm = AppGlobals.getPackageManager(); 266 Intent assistIntent = new Intent(Intent.ACTION_ASSIST); 267 ResolveInfo info = 268 pm.resolveIntent(assistIntent, 269 assistIntent.resolveTypeIfNeeded(mContext.getContentResolver()), 270 PackageManager.MATCH_DEFAULT_ONLY, userHandle); 271 if (info != null) { 272 return new ComponentName( 273 info.activityInfo.applicationInfo.packageName, 274 info.activityInfo.name); 275 } 276 } catch (RemoteException re) { 277 // Local call 278 Log.e(TAG, "RemoteException in getLegacyAssistComponent: " + re); 279 } catch (Exception e) { 280 Log.e(TAG, "Exception in getLegacyAssistComponent: " + e); 281 } 282 return null; 283 } 284 285 @Override 286 public boolean launchLegacyAssist(String hint, int userHandle, Bundle args) { 287 ComponentName comp = getLegacyAssistComponent(userHandle); 288 if (comp == null) { 289 return false; 290 } 291 long ident = Binder.clearCallingIdentity(); 292 try { 293 Intent intent = new Intent(Intent.ACTION_ASSIST); 294 intent.setComponent(comp); 295 IActivityManager am = ActivityManagerNative.getDefault(); 296 return am.launchAssistIntent(intent, ActivityManager.ASSIST_CONTEXT_BASIC, hint, 297 userHandle, args); 298 } catch (RemoteException e) { 299 } finally { 300 Binder.restoreCallingIdentity(ident); 301 } 302 return true; 303 } 304 305 @Override 306 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 307 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG); 308 309 IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); 310 synchronized (mSearchables) { 311 for (int i = 0; i < mSearchables.size(); i++) { 312 ipw.print("\nUser: "); ipw.println(mSearchables.keyAt(i)); 313 ipw.increaseIndent(); 314 mSearchables.valueAt(i).dump(fd, ipw, args); 315 ipw.decreaseIndent(); 316 } 317 } 318 } 319} 320