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.IActivityManager;
     23 import android.app.ISearchManager;
     24 import android.app.SearchManager;
     25 import android.app.SearchableInfo;
     26 import android.content.BroadcastReceiver;
     27 import android.content.ComponentName;
     28 import android.content.ContentResolver;
     29 import android.content.Context;
     30 import android.content.Intent;
     31 import android.content.IntentFilter;
     32 import android.content.pm.IPackageManager;
     33 import android.content.pm.PackageManager;
     34 import android.content.pm.ResolveInfo;
     35 import android.database.ContentObserver;
     36 import android.os.Binder;
     37 import android.os.Bundle;
     38 import android.os.Process;
     39 import android.os.RemoteException;
     40 import android.os.UserHandle;
     41 import android.os.UserManager;
     42 import android.provider.Settings;
     43 import android.util.Log;
     44 import android.util.SparseArray;
     45 
     46 import com.android.internal.content.PackageMonitor;
     47 import com.android.internal.util.IndentingPrintWriter;
     48 
     49 import java.io.FileDescriptor;
     50 import java.io.PrintWriter;
     51 import java.util.List;
     52 
     53 /**
     54  * The search manager service handles the search UI, and maintains a registry of searchable
     55  * activities.
     56  */
     57 public 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