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