• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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