OSDN Git Service

DO NOT MERGE. Grant MMS Uri permissions as the calling UID.
[android-x86/frameworks-base.git] / services / core / java / com / android / server / search / SearchManagerService.java
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.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;
43
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;
51
52 import java.io.FileDescriptor;
53 import java.io.PrintWriter;
54 import java.util.List;
55
56 /**
57  * The search manager service handles the search UI, and maintains a registry of
58  * searchable activities.
59  */
60 public class SearchManagerService extends ISearchManager.Stub {
61     private static final String TAG = "SearchManagerService";
62     final Handler mHandler;
63
64     public static class Lifecycle extends SystemService {
65         private SearchManagerService mService;
66
67         public Lifecycle(Context context) {
68             super(context);
69         }
70
71         @Override
72         public void onStart() {
73             mService = new SearchManagerService(getContext());
74             publishBinderService(Context.SEARCH_SERVICE, mService);
75         }
76
77         @Override
78         public void onUnlockUser(final int userId) {
79             mService.mHandler.post(new Runnable() {
80                 @Override
81                 public void run() {
82                     mService.onUnlockUser(userId);
83                 }
84             });
85         }
86
87         @Override
88         public void onCleanupUser(int userHandle) {
89             mService.onCleanupUser(userHandle);
90         }
91     }
92
93     // Context that the service is running in.
94     private final Context mContext;
95
96     // This field is initialized lazily in getSearchables(), and then never modified.
97     @GuardedBy("mSearchables")
98     private final SparseArray<Searchables> mSearchables = new SparseArray<>();
99
100     /**
101      * Initializes the Search Manager service in the provided system context.
102      * Only one instance of this object should be created!
103      *
104      * @param context to use for accessing DB, window manager, etc.
105      */
106     public SearchManagerService(Context context)  {
107         mContext = context;
108         new MyPackageMonitor().register(context, null, UserHandle.ALL, true);
109         new GlobalSearchProviderObserver(context.getContentResolver());
110         mHandler = BackgroundThread.getHandler();
111     }
112
113     private Searchables getSearchables(int userId) {
114         return getSearchables(userId, false);
115     }
116
117     private Searchables getSearchables(int userId, boolean forceUpdate) {
118         final long token = Binder.clearCallingIdentity();
119         try {
120             final UserManager um = mContext.getSystemService(UserManager.class);
121             if (um.getUserInfo(userId) == null) {
122                 throw new IllegalStateException("User " + userId + " doesn't exist");
123             }
124             if (!um.isUserUnlockingOrUnlocked(userId)) {
125                 throw new IllegalStateException("User " + userId + " isn't unlocked");
126             }
127         } finally {
128             Binder.restoreCallingIdentity(token);
129         }
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();
138             }
139             return searchables;
140         }
141     }
142
143     private void onUnlockUser(int userId) {
144         try {
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.
149         }
150     }
151
152     private void onCleanupUser(int userId) {
153         synchronized (mSearchables) {
154             mSearchables.remove(userId);
155         }
156     }
157
158     /**
159      * Refreshes the "searchables" list when packages are added/removed.
160      */
161     class MyPackageMonitor extends PackageMonitor {
162
163         @Override
164         public void onSomePackagesChanged() {
165             updateSearchables();
166         }
167
168         @Override
169         public void onPackageModified(String pkg) {
170             updateSearchables();
171         }
172
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();
180                         break;
181                     }
182                 }
183             }
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));
189         }
190     }
191
192     class GlobalSearchProviderObserver extends ContentObserver {
193         private final ContentResolver mResolver;
194
195         public GlobalSearchProviderObserver(ContentResolver resolver) {
196             super(null);
197             mResolver = resolver;
198             mResolver.registerContentObserver(
199                     Settings.Secure.getUriFor(Settings.Secure.SEARCH_GLOBAL_SEARCH_ACTIVITY),
200                     false /* notifyDescendants */,
201                     this);
202         }
203
204         @Override
205         public void onChange(boolean selfChange) {
206             synchronized (mSearchables) {
207                 for (int i = 0; i < mSearchables.size(); i++) {
208                     mSearchables.valueAt(i).updateSearchableList();
209                 }
210             }
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);
214         }
215     }
216
217     //
218     // Searchable activities API
219     //
220
221     /**
222      * Returns the SearchableInfo for a given activity.
223      *
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.
227      */
228     @Override
229     public SearchableInfo getSearchableInfo(final ComponentName launchActivity) {
230         if (launchActivity == null) {
231             Log.e(TAG, "getSearchableInfo(), activity == null");
232             return null;
233         }
234         return getSearchables(UserHandle.getCallingUserId()).getSearchableInfo(launchActivity);
235     }
236
237     /**
238      * Returns a list of the searchable activities that can be included in global search.
239      */
240     @Override
241     public List<SearchableInfo> getSearchablesInGlobalSearch() {
242         return getSearchables(UserHandle.getCallingUserId()).getSearchablesInGlobalSearchList();
243     }
244
245     @Override
246     public List<ResolveInfo> getGlobalSearchActivities() {
247         return getSearchables(UserHandle.getCallingUserId()).getGlobalSearchActivities();
248     }
249
250     /**
251      * Gets the name of the global search activity.
252      */
253     @Override
254     public ComponentName getGlobalSearchActivity() {
255         return getSearchables(UserHandle.getCallingUserId()).getGlobalSearchActivity();
256     }
257
258     /**
259      * Gets the name of the web search activity.
260      */
261     @Override
262     public ComponentName getWebSearchActivity() {
263         return getSearchables(UserHandle.getCallingUserId()).getWebSearchActivity();
264     }
265
266     @Override
267     public void launchAssist(Bundle args) {
268         StatusBarManagerInternal statusBarManager =
269                 LocalServices.getService(StatusBarManagerInternal.class);
270         if (statusBarManager != null) {
271             statusBarManager.startAssist(args);
272         }
273     }
274
275     private ComponentName getLegacyAssistComponent(int userHandle) {
276         try {
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);
281             ResolveInfo info =
282                     pm.resolveIntent(assistIntent,
283                             assistIntent.resolveTypeIfNeeded(mContext.getContentResolver()),
284                             PackageManager.MATCH_DEFAULT_ONLY, userHandle);
285             if (info != null) {
286                 return new ComponentName(
287                         info.activityInfo.applicationInfo.packageName,
288                         info.activityInfo.name);
289             }
290         } catch (RemoteException re) {
291             // Local call
292             Log.e(TAG, "RemoteException in getLegacyAssistComponent: " + re);
293         } catch (Exception e) {
294             Log.e(TAG, "Exception in getLegacyAssistComponent: " + e);
295         }
296         return null;
297     }
298
299     @Override
300     public boolean launchLegacyAssist(String hint, int userHandle, Bundle args) {
301         ComponentName comp = getLegacyAssistComponent(userHandle);
302         if (comp == null) {
303             return false;
304         }
305         long ident = Binder.clearCallingIdentity();
306         try {
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,
311                     userHandle, args);
312         } catch (RemoteException e) {
313         } finally {
314             Binder.restoreCallingIdentity(ident);
315         }
316         return true;
317     }
318
319     @Override
320     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
321         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
322
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();
330             }
331         }
332     }
333 }