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 17 package com.android.server.search; 18 19 import android.app.ActivityManager; 20 import android.app.ActivityManagerNative; 21 import android.app.AppGlobals; 22 import android.app.ISearchManager; 23 import android.app.SearchManager; 24 import android.app.SearchableInfo; 25 import android.content.BroadcastReceiver; 26 import android.content.ComponentName; 27 import android.content.ContentResolver; 28 import android.content.Context; 29 import android.content.Intent; 30 import android.content.IntentFilter; 31 import android.content.pm.IPackageManager; 32 import android.content.pm.PackageManager; 33 import android.content.pm.ResolveInfo; 34 import android.database.ContentObserver; 35 import android.os.Binder; 36 import android.os.Process; 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.Slog; 43 import android.util.SparseArray; 44 45 import com.android.internal.content.PackageMonitor; 46 import com.android.internal.util.IndentingPrintWriter; 47 48 import java.io.FileDescriptor; 49 import java.io.PrintWriter; 50 import java.util.List; 51 52 /** 53 * The search manager service handles the search UI, and maintains a registry of searchable 54 * activities. 55 */ 56 public class SearchManagerService extends ISearchManager.Stub { 57 58 // general debugging support 59 private static final String TAG = "SearchManagerService"; 60 61 // Context that the service is running in. 62 private final Context mContext; 63 64 // This field is initialized lazily in getSearchables(), and then never modified. 65 private final SparseArray<Searchables> mSearchables = new SparseArray<Searchables>(); 66 67 /** 68 * Initializes the Search Manager service in the provided system context. 69 * Only one instance of this object should be created! 70 * 71 * @param context to use for accessing DB, window manager, etc. 72 */ SearchManagerService(Context context)73 public SearchManagerService(Context context) { 74 mContext = context; 75 mContext.registerReceiver(new BootCompletedReceiver(), 76 new IntentFilter(Intent.ACTION_BOOT_COMPLETED)); 77 mContext.registerReceiver(new UserReceiver(), 78 new IntentFilter(Intent.ACTION_USER_REMOVED)); 79 new MyPackageMonitor().register(context, null, UserHandle.ALL, true); 80 } 81 getSearchables(int userId)82 private Searchables getSearchables(int userId) { 83 long origId = Binder.clearCallingIdentity(); 84 try { 85 boolean userExists = ((UserManager) mContext.getSystemService(Context.USER_SERVICE)) 86 .getUserInfo(userId) != null; 87 if (!userExists) return null; 88 } finally { 89 Binder.restoreCallingIdentity(origId); 90 } 91 synchronized (mSearchables) { 92 Searchables searchables = mSearchables.get(userId); 93 94 if (searchables == null) { 95 //Log.i(TAG, "Building list of searchable activities for userId=" + userId); 96 searchables = new Searchables(mContext, userId); 97 searchables.buildSearchableList(); 98 mSearchables.append(userId, searchables); 99 } 100 return searchables; 101 } 102 } 103 onUserRemoved(int userId)104 private void onUserRemoved(int userId) { 105 if (userId != UserHandle.USER_OWNER) { 106 synchronized (mSearchables) { 107 mSearchables.remove(userId); 108 } 109 } 110 } 111 112 /** 113 * Creates the initial searchables list after boot. 114 */ 115 private final class BootCompletedReceiver extends BroadcastReceiver { 116 @Override onReceive(Context context, Intent intent)117 public void onReceive(Context context, Intent intent) { 118 new Thread() { 119 @Override 120 public void run() { 121 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); 122 mContext.unregisterReceiver(BootCompletedReceiver.this); 123 getSearchables(0); 124 } 125 }.start(); 126 } 127 } 128 129 private final class UserReceiver extends BroadcastReceiver { 130 @Override onReceive(Context context, Intent intent)131 public void onReceive(Context context, Intent intent) { 132 onUserRemoved(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_OWNER)); 133 } 134 } 135 136 /** 137 * Refreshes the "searchables" list when packages are added/removed. 138 */ 139 class MyPackageMonitor extends PackageMonitor { 140 141 @Override onSomePackagesChanged()142 public void onSomePackagesChanged() { 143 updateSearchables(); 144 } 145 146 @Override onPackageModified(String pkg)147 public void onPackageModified(String pkg) { 148 updateSearchables(); 149 } 150 updateSearchables()151 private void updateSearchables() { 152 final int changingUserId = getChangingUserId(); 153 synchronized (mSearchables) { 154 // Update list of searchable activities 155 for (int i = 0; i < mSearchables.size(); i++) { 156 if (changingUserId == mSearchables.keyAt(i)) { 157 getSearchables(mSearchables.keyAt(i)).buildSearchableList(); 158 break; 159 } 160 } 161 } 162 // Inform all listeners that the list of searchables has been updated. 163 Intent intent = new Intent(SearchManager.INTENT_ACTION_SEARCHABLES_CHANGED); 164 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING 165 | Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 166 mContext.sendBroadcastAsUser(intent, new UserHandle(changingUserId)); 167 } 168 } 169 170 class GlobalSearchProviderObserver extends ContentObserver { 171 private final ContentResolver mResolver; 172 GlobalSearchProviderObserver(ContentResolver resolver)173 public GlobalSearchProviderObserver(ContentResolver resolver) { 174 super(null); 175 mResolver = resolver; 176 mResolver.registerContentObserver( 177 Settings.Secure.getUriFor(Settings.Secure.SEARCH_GLOBAL_SEARCH_ACTIVITY), 178 false /* notifyDescendants */, 179 this); 180 } 181 182 @Override onChange(boolean selfChange)183 public void onChange(boolean selfChange) { 184 synchronized (mSearchables) { 185 for (int i = 0; i < mSearchables.size(); i++) { 186 getSearchables(mSearchables.keyAt(i)).buildSearchableList(); 187 } 188 } 189 Intent intent = new Intent(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED); 190 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); 191 mContext.sendBroadcastAsUser(intent, UserHandle.ALL); 192 } 193 194 } 195 196 // 197 // Searchable activities API 198 // 199 200 /** 201 * Returns the SearchableInfo for a given activity. 202 * 203 * @param launchActivity The activity from which we're launching this search. 204 * @return Returns a SearchableInfo record describing the parameters of the search, 205 * or null if no searchable metadata was available. 206 */ getSearchableInfo(final ComponentName launchActivity)207 public SearchableInfo getSearchableInfo(final ComponentName launchActivity) { 208 if (launchActivity == null) { 209 Log.e(TAG, "getSearchableInfo(), activity == null"); 210 return null; 211 } 212 return getSearchables(UserHandle.getCallingUserId()).getSearchableInfo(launchActivity); 213 } 214 215 /** 216 * Returns a list of the searchable activities that can be included in global search. 217 */ getSearchablesInGlobalSearch()218 public List<SearchableInfo> getSearchablesInGlobalSearch() { 219 return getSearchables(UserHandle.getCallingUserId()).getSearchablesInGlobalSearchList(); 220 } 221 getGlobalSearchActivities()222 public List<ResolveInfo> getGlobalSearchActivities() { 223 return getSearchables(UserHandle.getCallingUserId()).getGlobalSearchActivities(); 224 } 225 226 /** 227 * Gets the name of the global search activity. 228 */ getGlobalSearchActivity()229 public ComponentName getGlobalSearchActivity() { 230 return getSearchables(UserHandle.getCallingUserId()).getGlobalSearchActivity(); 231 } 232 233 /** 234 * Gets the name of the web search activity. 235 */ getWebSearchActivity()236 public ComponentName getWebSearchActivity() { 237 return getSearchables(UserHandle.getCallingUserId()).getWebSearchActivity(); 238 } 239 240 @Override getAssistIntent(int userHandle)241 public ComponentName getAssistIntent(int userHandle) { 242 try { 243 userHandle = ActivityManager.handleIncomingUser(Binder.getCallingPid(), 244 Binder.getCallingUid(), userHandle, true, false, "getAssistIntent", null); 245 IPackageManager pm = AppGlobals.getPackageManager(); 246 Intent assistIntent = new Intent(Intent.ACTION_ASSIST); 247 ResolveInfo info = 248 pm.resolveIntent(assistIntent, 249 assistIntent.resolveTypeIfNeeded(mContext.getContentResolver()), 250 PackageManager.MATCH_DEFAULT_ONLY, userHandle); 251 if (info != null) { 252 return new ComponentName( 253 info.activityInfo.applicationInfo.packageName, 254 info.activityInfo.name); 255 } 256 } catch (RemoteException re) { 257 // Local call 258 Log.e(TAG, "RemoteException in getAssistIntent: " + re); 259 } catch (Exception e) { 260 Log.e(TAG, "Exception in getAssistIntent: " + e); 261 } 262 return null; 263 } 264 265 @Override dump(FileDescriptor fd, PrintWriter pw, String[] args)266 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 267 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG); 268 269 IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); 270 synchronized (mSearchables) { 271 for (int i = 0; i < mSearchables.size(); i++) { 272 ipw.print("\nUser: "); ipw.println(mSearchables.keyAt(i)); 273 ipw.increaseIndent(); 274 mSearchables.valueAt(i).dump(fd, ipw, args); 275 ipw.decreaseIndent(); 276 } 277 } 278 } 279 } 280