Merge "SearchManagerService made multi-user aware"
diff --git a/core/java/android/server/search/SearchManagerService.java b/core/java/android/server/search/SearchManagerService.java
index c783e6a..2411cec 100644
--- a/core/java/android/server/search/SearchManagerService.java
+++ b/core/java/android/server/search/SearchManagerService.java
@@ -29,9 +29,12 @@
 import android.content.IntentFilter;
 import android.content.pm.ResolveInfo;
 import android.database.ContentObserver;
+import android.os.Binder;
 import android.os.Process;
+import android.os.UserId;
 import android.provider.Settings;
 import android.util.Log;
+import android.util.SparseArray;
 
 import java.util.List;
 
@@ -48,7 +51,7 @@
     private final Context mContext;
 
     // This field is initialized lazily in getSearchables(), and then never modified.
-    private Searchables mSearchables;
+    private SparseArray<Searchables> mSearchables;
 
     private ContentObserver mGlobalSearchObserver;
 
@@ -66,14 +69,24 @@
                 mContext.getContentResolver());
     }
 
-    private synchronized Searchables getSearchables() {
+    private synchronized Searchables getSearchables(int userId) {
         if (mSearchables == null) {
-            Log.i(TAG, "Building list of searchable activities");
             new MyPackageMonitor().register(mContext, null, true);
-            mSearchables = new Searchables(mContext);
-            mSearchables.buildSearchableList();
+            mSearchables = new SparseArray<Searchables>();
         }
-        return mSearchables;
+        Searchables searchables = mSearchables.get(userId);
+
+        long origId = Binder.clearCallingIdentity();
+        boolean userExists = mContext.getPackageManager().getUser(userId) != null;
+        Binder.restoreCallingIdentity(origId);
+
+        if (searchables == null && userExists) {
+            Log.i(TAG, "Building list of searchable activities for userId=" + userId);
+            searchables = new Searchables(mContext, userId);
+            searchables.buildSearchableList();
+            mSearchables.append(userId, searchables);
+        }
+        return searchables;
     }
 
     /**
@@ -87,7 +100,7 @@
                 public void run() {
                     Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                     mContext.unregisterReceiver(BootCompletedReceiver.this);
-                    getSearchables();
+                    getSearchables(0);
                 }
             }.start();
         }
@@ -109,8 +122,12 @@
         }
 
         private void updateSearchables() {
-            // Update list of searchable activities
-            getSearchables().buildSearchableList();
+            synchronized (SearchManagerService.this) {
+                // Update list of searchable activities
+                for (int i = 0; i < mSearchables.size(); i++) {
+                    getSearchables(mSearchables.keyAt(i)).buildSearchableList();
+                }
+            }
             // Inform all listeners that the list of searchables has been updated.
             Intent intent = new Intent(SearchManager.INTENT_ACTION_SEARCHABLES_CHANGED);
             intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
@@ -132,7 +149,11 @@
 
         @Override
         public void onChange(boolean selfChange) {
-            getSearchables().buildSearchableList();
+            synchronized (SearchManagerService.this) {
+                for (int i = 0; i < mSearchables.size(); i++) {
+                    getSearchables(mSearchables.keyAt(i)).buildSearchableList();
+                }
+            }
             Intent intent = new Intent(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED);
             intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
             mContext.sendBroadcast(intent);
@@ -156,32 +177,32 @@
             Log.e(TAG, "getSearchableInfo(), activity == null");
             return null;
         }
-        return getSearchables().getSearchableInfo(launchActivity);
+        return getSearchables(UserId.getCallingUserId()).getSearchableInfo(launchActivity);
     }
 
     /**
      * Returns a list of the searchable activities that can be included in global search.
      */
     public List<SearchableInfo> getSearchablesInGlobalSearch() {
-        return getSearchables().getSearchablesInGlobalSearchList();
+        return getSearchables(UserId.getCallingUserId()).getSearchablesInGlobalSearchList();
     }
 
     public List<ResolveInfo> getGlobalSearchActivities() {
-        return getSearchables().getGlobalSearchActivities();
+        return getSearchables(UserId.getCallingUserId()).getGlobalSearchActivities();
     }
 
     /**
      * Gets the name of the global search activity.
      */
     public ComponentName getGlobalSearchActivity() {
-        return getSearchables().getGlobalSearchActivity();
+        return getSearchables(UserId.getCallingUserId()).getGlobalSearchActivity();
     }
 
     /**
      * Gets the name of the web search activity.
      */
     public ComponentName getWebSearchActivity() {
-        return getSearchables().getWebSearchActivity();
+        return getSearchables(UserId.getCallingUserId()).getWebSearchActivity();
     }
 
 }
diff --git a/core/java/android/server/search/Searchables.java b/core/java/android/server/search/Searchables.java
index f24d52f..4e00ef9 100644
--- a/core/java/android/server/search/Searchables.java
+++ b/core/java/android/server/search/Searchables.java
@@ -16,6 +16,7 @@
 
 package android.server.search;
 
+import android.app.AppGlobals;
 import android.app.SearchManager;
 import android.app.SearchableInfo;
 import android.content.ComponentName;
@@ -23,9 +24,11 @@
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.os.Bundle;
+import android.os.RemoteException;
 import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.Log;
@@ -38,6 +41,7 @@
 
 /**
  * This class maintains the information about all searchable activities.
+ * This is a hidden class.
  */
 public class Searchables {
 
@@ -65,12 +69,18 @@
     public static String ENHANCED_GOOGLE_SEARCH_COMPONENT_NAME =
             "com.google.android.providers.enhancedgooglesearch/.Launcher";
 
+    // Cache the package manager instance
+    private IPackageManager mPm;
+    // User for which this Searchables caches information
+    private int mUserId;
+
     /**
      *
      * @param context Context to use for looking up activities etc.
      */
-    public Searchables (Context context) {
+    public Searchables (Context context, int userId) {
         mContext = context;
+        mUserId = userId;
     }
 
     /**
@@ -195,16 +205,14 @@
         ArrayList<SearchableInfo> newSearchablesInGlobalSearchList
                                 = new ArrayList<SearchableInfo>();
 
-        final PackageManager pm = mContext.getPackageManager();
-
         // Use intent resolver to generate list of ACTION_SEARCH & ACTION_WEB_SEARCH receivers.
         List<ResolveInfo> searchList;
         final Intent intent = new Intent(Intent.ACTION_SEARCH);
-        searchList = pm.queryIntentActivities(intent, PackageManager.GET_META_DATA);
+        searchList = queryIntentActivities(intent, PackageManager.GET_META_DATA);
 
         List<ResolveInfo> webSearchInfoList;
         final Intent webSearchIntent = new Intent(Intent.ACTION_WEB_SEARCH);
-        webSearchInfoList = pm.queryIntentActivities(webSearchIntent, PackageManager.GET_META_DATA);
+        webSearchInfoList = queryIntentActivities(webSearchIntent, PackageManager.GET_META_DATA);
 
         // analyze each one, generate a Searchables record, and record
         if (searchList != null || webSearchInfoList != null) {
@@ -262,10 +270,8 @@
         // Step 1 : Query the package manager for a list
         // of activities that can handle the GLOBAL_SEARCH intent.
         Intent intent = new Intent(SearchManager.INTENT_ACTION_GLOBAL_SEARCH);
-        PackageManager pm = mContext.getPackageManager();
         List<ResolveInfo> activities =
-                pm.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
-
+                    queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
         if (activities != null && !activities.isEmpty()) {
             // Step 2: Rank matching activities according to our heuristics.
             Collections.sort(activities, GLOBAL_SEARCH_RANKER);
@@ -301,10 +307,8 @@
         Intent intent = new Intent(SearchManager.INTENT_ACTION_GLOBAL_SEARCH);
         intent.setComponent(globalSearch);
 
-        PackageManager pm = mContext.getPackageManager();
-        List<ResolveInfo> activities =
-                pm.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
-
+        List<ResolveInfo> activities = queryIntentActivities(intent,
+                PackageManager.MATCH_DEFAULT_ONLY);
         if (activities != null && !activities.isEmpty()) {
             return true;
         }
@@ -374,9 +378,8 @@
         }
         Intent intent = new Intent(Intent.ACTION_WEB_SEARCH);
         intent.setPackage(globalSearchActivity.getPackageName());
-        PackageManager pm = mContext.getPackageManager();
         List<ResolveInfo> activities =
-                pm.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
+                queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
 
         if (activities != null && !activities.isEmpty()) {
             ActivityInfo ai = activities.get(0).activityInfo;
@@ -387,6 +390,22 @@
         return null;
     }
 
+    private List<ResolveInfo> queryIntentActivities(Intent intent, int flags) {
+        if (mPm == null) {
+            mPm = AppGlobals.getPackageManager();
+        }
+        List<ResolveInfo> activities = null;
+        try {
+            activities =
+                    mPm.queryIntentActivities(intent,
+                    intent.resolveTypeIfNeeded(mContext.getContentResolver()),
+                    flags, mUserId);
+        } catch (RemoteException re) {
+            // Local call
+        }
+        return activities;
+    }
+
     /**
      * Returns the list of searchable activities.
      */
diff --git a/core/tests/coretests/src/android/app/SearchablesTest.java b/core/tests/coretests/src/android/app/SearchablesTest.java
index 6cb31d4..4d3b144 100644
--- a/core/tests/coretests/src/android/app/SearchablesTest.java
+++ b/core/tests/coretests/src/android/app/SearchablesTest.java
@@ -71,7 +71,7 @@
      */
     public void testNonSearchable() {
         // test basic array & hashmap
-        Searchables searchables = new Searchables(mContext);
+        Searchables searchables = new Searchables(mContext, 0);
         searchables.buildSearchableList();
 
         // confirm that we return null for non-searchy activities
@@ -103,7 +103,7 @@
 
         // build item list with real-world source data
         mockPM.setSearchablesMode(MyMockPackageManager.SEARCHABLES_PASSTHROUGH);
-        Searchables searchables = new Searchables(mockContext);
+        Searchables searchables = new Searchables(mockContext, 0);
         searchables.buildSearchableList();
         // tests with "real" searchables (deprecate, this should be a unit test)
         ArrayList<SearchableInfo> searchablesList = searchables.getSearchablesList();
@@ -122,7 +122,7 @@
         MyMockContext mockContext = new MyMockContext(mContext, mockPM);
 
         mockPM.setSearchablesMode(MyMockPackageManager.SEARCHABLES_MOCK_ZERO);
-        Searchables searchables = new Searchables(mockContext);
+        Searchables searchables = new Searchables(mockContext, 0);
         searchables.buildSearchableList();
         ArrayList<SearchableInfo> searchablesList = searchables.getSearchablesList();
         assertNotNull(searchablesList);
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index 08a042a..7898342 100644
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -9383,7 +9383,7 @@
 
     @Override
     public UserInfo getUser(int userId) {
-        enforceSystemOrRoot("Only the system can remove users");
+        enforceSystemOrRoot("Only the system can query user");
         return sUserManager.getUser(userId);
     }