Home | History | Annotate | Download | only in search
      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      */
     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 
     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 
    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
    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
    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
    142         public void onSomePackagesChanged() {
    143             updateSearchables();
    144         }
    145 
    146         @Override
    147         public void onPackageModified(String pkg) {
    148             updateSearchables();
    149         }
    150 
    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 
    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
    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      */
    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      */
    218     public List<SearchableInfo> getSearchablesInGlobalSearch() {
    219         return getSearchables(UserHandle.getCallingUserId()).getSearchablesInGlobalSearchList();
    220     }
    221 
    222     public List<ResolveInfo> getGlobalSearchActivities() {
    223         return getSearchables(UserHandle.getCallingUserId()).getGlobalSearchActivities();
    224     }
    225 
    226     /**
    227      * Gets the name of the global search activity.
    228      */
    229     public ComponentName getGlobalSearchActivity() {
    230         return getSearchables(UserHandle.getCallingUserId()).getGlobalSearchActivity();
    231     }
    232 
    233     /**
    234      * Gets the name of the web search activity.
    235      */
    236     public ComponentName getWebSearchActivity() {
    237         return getSearchables(UserHandle.getCallingUserId()).getWebSearchActivity();
    238     }
    239 
    240     @Override
    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
    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