Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a0ecfde6 authored by Amith Yamasani's avatar Amith Yamasani Committed by Android (Google) Code Review
Browse files

Merge "SearchManagerService made multi-user aware"

parents d1e57d78 5bb87cd9
Loading
Loading
Loading
Loading
+36 −15
Original line number Original line Diff line number Diff line
@@ -29,9 +29,12 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentFilter;
import android.content.pm.ResolveInfo;
import android.content.pm.ResolveInfo;
import android.database.ContentObserver;
import android.database.ContentObserver;
import android.os.Binder;
import android.os.Process;
import android.os.Process;
import android.os.UserId;
import android.provider.Settings;
import android.provider.Settings;
import android.util.Log;
import android.util.Log;
import android.util.SparseArray;


import java.util.List;
import java.util.List;


@@ -48,7 +51,7 @@ public class SearchManagerService extends ISearchManager.Stub {
    private final Context mContext;
    private final Context mContext;


    // This field is initialized lazily in getSearchables(), and then never modified.
    // This field is initialized lazily in getSearchables(), and then never modified.
    private Searchables mSearchables;
    private SparseArray<Searchables> mSearchables;


    private ContentObserver mGlobalSearchObserver;
    private ContentObserver mGlobalSearchObserver;


@@ -66,14 +69,24 @@ public class SearchManagerService extends ISearchManager.Stub {
                mContext.getContentResolver());
                mContext.getContentResolver());
    }
    }


    private synchronized Searchables getSearchables() {
    private synchronized Searchables getSearchables(int userId) {
        if (mSearchables == null) {
        if (mSearchables == null) {
            Log.i(TAG, "Building list of searchable activities");
            new MyPackageMonitor().register(mContext, null, true);
            new MyPackageMonitor().register(mContext, null, true);
            mSearchables = new Searchables(mContext);
            mSearchables = new SparseArray<Searchables>();
            mSearchables.buildSearchableList();
        }
        }
        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 class SearchManagerService extends ISearchManager.Stub {
                public void run() {
                public void run() {
                    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                    mContext.unregisterReceiver(BootCompletedReceiver.this);
                    mContext.unregisterReceiver(BootCompletedReceiver.this);
                    getSearchables();
                    getSearchables(0);
                }
                }
            }.start();
            }.start();
        }
        }
@@ -109,8 +122,12 @@ public class SearchManagerService extends ISearchManager.Stub {
        }
        }


        private void updateSearchables() {
        private void updateSearchables() {
            synchronized (SearchManagerService.this) {
                // Update list of searchable activities
                // Update list of searchable activities
            getSearchables().buildSearchableList();
                for (int i = 0; i < mSearchables.size(); i++) {
                    getSearchables(mSearchables.keyAt(i)).buildSearchableList();
                }
            }
            // Inform all listeners that the list of searchables has been updated.
            // Inform all listeners that the list of searchables has been updated.
            Intent intent = new Intent(SearchManager.INTENT_ACTION_SEARCHABLES_CHANGED);
            Intent intent = new Intent(SearchManager.INTENT_ACTION_SEARCHABLES_CHANGED);
            intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
            intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
@@ -132,7 +149,11 @@ public class SearchManagerService extends ISearchManager.Stub {


        @Override
        @Override
        public void onChange(boolean selfChange) {
        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 intent = new Intent(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED);
            intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
            intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
            mContext.sendBroadcast(intent);
            mContext.sendBroadcast(intent);
@@ -156,32 +177,32 @@ public class SearchManagerService extends ISearchManager.Stub {
            Log.e(TAG, "getSearchableInfo(), activity == null");
            Log.e(TAG, "getSearchableInfo(), activity == null");
            return 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.
     * Returns a list of the searchable activities that can be included in global search.
     */
     */
    public List<SearchableInfo> getSearchablesInGlobalSearch() {
    public List<SearchableInfo> getSearchablesInGlobalSearch() {
        return getSearchables().getSearchablesInGlobalSearchList();
        return getSearchables(UserId.getCallingUserId()).getSearchablesInGlobalSearchList();
    }
    }


    public List<ResolveInfo> getGlobalSearchActivities() {
    public List<ResolveInfo> getGlobalSearchActivities() {
        return getSearchables().getGlobalSearchActivities();
        return getSearchables(UserId.getCallingUserId()).getGlobalSearchActivities();
    }
    }


    /**
    /**
     * Gets the name of the global search activity.
     * Gets the name of the global search activity.
     */
     */
    public ComponentName getGlobalSearchActivity() {
    public ComponentName getGlobalSearchActivity() {
        return getSearchables().getGlobalSearchActivity();
        return getSearchables(UserId.getCallingUserId()).getGlobalSearchActivity();
    }
    }


    /**
    /**
     * Gets the name of the web search activity.
     * Gets the name of the web search activity.
     */
     */
    public ComponentName getWebSearchActivity() {
    public ComponentName getWebSearchActivity() {
        return getSearchables().getWebSearchActivity();
        return getSearchables(UserId.getCallingUserId()).getWebSearchActivity();
    }
    }


}
}
+33 −14
Original line number Original line Diff line number Diff line
@@ -16,6 +16,7 @@


package android.server.search;
package android.server.search;


import android.app.AppGlobals;
import android.app.SearchManager;
import android.app.SearchManager;
import android.app.SearchableInfo;
import android.app.SearchableInfo;
import android.content.ComponentName;
import android.content.ComponentName;
@@ -23,9 +24,11 @@ import android.content.Context;
import android.content.Intent;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ResolveInfo;
import android.os.Bundle;
import android.os.Bundle;
import android.os.RemoteException;
import android.provider.Settings;
import android.provider.Settings;
import android.text.TextUtils;
import android.text.TextUtils;
import android.util.Log;
import android.util.Log;
@@ -38,6 +41,7 @@ import java.util.List;


/**
/**
 * This class maintains the information about all searchable activities.
 * This class maintains the information about all searchable activities.
 * This is a hidden class.
 */
 */
public class Searchables {
public class Searchables {


@@ -65,12 +69,18 @@ public class Searchables {
    public static String ENHANCED_GOOGLE_SEARCH_COMPONENT_NAME =
    public static String ENHANCED_GOOGLE_SEARCH_COMPONENT_NAME =
            "com.google.android.providers.enhancedgooglesearch/.Launcher";
            "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.
     * @param context Context to use for looking up activities etc.
     */
     */
    public Searchables (Context context) {
    public Searchables (Context context, int userId) {
        mContext = context;
        mContext = context;
        mUserId = userId;
    }
    }


    /**
    /**
@@ -195,16 +205,14 @@ public class Searchables {
        ArrayList<SearchableInfo> newSearchablesInGlobalSearchList
        ArrayList<SearchableInfo> newSearchablesInGlobalSearchList
                                = new ArrayList<SearchableInfo>();
                                = new ArrayList<SearchableInfo>();


        final PackageManager pm = mContext.getPackageManager();

        // Use intent resolver to generate list of ACTION_SEARCH & ACTION_WEB_SEARCH receivers.
        // Use intent resolver to generate list of ACTION_SEARCH & ACTION_WEB_SEARCH receivers.
        List<ResolveInfo> searchList;
        List<ResolveInfo> searchList;
        final Intent intent = new Intent(Intent.ACTION_SEARCH);
        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;
        List<ResolveInfo> webSearchInfoList;
        final Intent webSearchIntent = new Intent(Intent.ACTION_WEB_SEARCH);
        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
        // analyze each one, generate a Searchables record, and record
        if (searchList != null || webSearchInfoList != null) {
        if (searchList != null || webSearchInfoList != null) {
@@ -262,10 +270,8 @@ public class Searchables {
        // Step 1 : Query the package manager for a list
        // Step 1 : Query the package manager for a list
        // of activities that can handle the GLOBAL_SEARCH intent.
        // of activities that can handle the GLOBAL_SEARCH intent.
        Intent intent = new Intent(SearchManager.INTENT_ACTION_GLOBAL_SEARCH);
        Intent intent = new Intent(SearchManager.INTENT_ACTION_GLOBAL_SEARCH);
        PackageManager pm = mContext.getPackageManager();
        List<ResolveInfo> activities =
        List<ResolveInfo> activities =
                pm.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
                    queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);

        if (activities != null && !activities.isEmpty()) {
        if (activities != null && !activities.isEmpty()) {
            // Step 2: Rank matching activities according to our heuristics.
            // Step 2: Rank matching activities according to our heuristics.
            Collections.sort(activities, GLOBAL_SEARCH_RANKER);
            Collections.sort(activities, GLOBAL_SEARCH_RANKER);
@@ -301,10 +307,8 @@ public class Searchables {
        Intent intent = new Intent(SearchManager.INTENT_ACTION_GLOBAL_SEARCH);
        Intent intent = new Intent(SearchManager.INTENT_ACTION_GLOBAL_SEARCH);
        intent.setComponent(globalSearch);
        intent.setComponent(globalSearch);


        PackageManager pm = mContext.getPackageManager();
        List<ResolveInfo> activities = queryIntentActivities(intent,
        List<ResolveInfo> activities =
                PackageManager.MATCH_DEFAULT_ONLY);
                pm.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);

        if (activities != null && !activities.isEmpty()) {
        if (activities != null && !activities.isEmpty()) {
            return true;
            return true;
        }
        }
@@ -374,9 +378,8 @@ public class Searchables {
        }
        }
        Intent intent = new Intent(Intent.ACTION_WEB_SEARCH);
        Intent intent = new Intent(Intent.ACTION_WEB_SEARCH);
        intent.setPackage(globalSearchActivity.getPackageName());
        intent.setPackage(globalSearchActivity.getPackageName());
        PackageManager pm = mContext.getPackageManager();
        List<ResolveInfo> activities =
        List<ResolveInfo> activities =
                pm.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
                queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);


        if (activities != null && !activities.isEmpty()) {
        if (activities != null && !activities.isEmpty()) {
            ActivityInfo ai = activities.get(0).activityInfo;
            ActivityInfo ai = activities.get(0).activityInfo;
@@ -387,6 +390,22 @@ public class Searchables {
        return null;
        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.
     * Returns the list of searchable activities.
     */
     */
+3 −3
Original line number Original line Diff line number Diff line
@@ -71,7 +71,7 @@ public class SearchablesTest extends AndroidTestCase {
     */
     */
    public void testNonSearchable() {
    public void testNonSearchable() {
        // test basic array & hashmap
        // test basic array & hashmap
        Searchables searchables = new Searchables(mContext);
        Searchables searchables = new Searchables(mContext, 0);
        searchables.buildSearchableList();
        searchables.buildSearchableList();


        // confirm that we return null for non-searchy activities
        // confirm that we return null for non-searchy activities
@@ -103,7 +103,7 @@ public class SearchablesTest extends AndroidTestCase {


        // build item list with real-world source data
        // build item list with real-world source data
        mockPM.setSearchablesMode(MyMockPackageManager.SEARCHABLES_PASSTHROUGH);
        mockPM.setSearchablesMode(MyMockPackageManager.SEARCHABLES_PASSTHROUGH);
        Searchables searchables = new Searchables(mockContext);
        Searchables searchables = new Searchables(mockContext, 0);
        searchables.buildSearchableList();
        searchables.buildSearchableList();
        // tests with "real" searchables (deprecate, this should be a unit test)
        // tests with "real" searchables (deprecate, this should be a unit test)
        ArrayList<SearchableInfo> searchablesList = searchables.getSearchablesList();
        ArrayList<SearchableInfo> searchablesList = searchables.getSearchablesList();
@@ -122,7 +122,7 @@ public class SearchablesTest extends AndroidTestCase {
        MyMockContext mockContext = new MyMockContext(mContext, mockPM);
        MyMockContext mockContext = new MyMockContext(mContext, mockPM);


        mockPM.setSearchablesMode(MyMockPackageManager.SEARCHABLES_MOCK_ZERO);
        mockPM.setSearchablesMode(MyMockPackageManager.SEARCHABLES_MOCK_ZERO);
        Searchables searchables = new Searchables(mockContext);
        Searchables searchables = new Searchables(mockContext, 0);
        searchables.buildSearchableList();
        searchables.buildSearchableList();
        ArrayList<SearchableInfo> searchablesList = searchables.getSearchablesList();
        ArrayList<SearchableInfo> searchablesList = searchables.getSearchablesList();
        assertNotNull(searchablesList);
        assertNotNull(searchablesList);
+1 −1
Original line number Original line Diff line number Diff line
@@ -9383,7 +9383,7 @@ public class PackageManagerService extends IPackageManager.Stub {


    @Override
    @Override
    public UserInfo getUser(int userId) {
    public UserInfo getUser(int userId) {
        enforceSystemOrRoot("Only the system can remove users");
        enforceSystemOrRoot("Only the system can query user");
        return sUserManager.getUser(userId);
        return sUserManager.getUser(userId);
    }
    }