From 4175be2f4e1e27490f38c387525d0c0cba88b47b Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Sat, 9 Jan 2016 14:57:45 -0700 Subject: [PATCH] Triage SearchManagerService for missing apps. Searchables aren't available until after the user is unlocked. We could scan for them while locked, but we're aiming for a minimalist environment, and scanning them while locked would require us to send a changed broadcast on every boot. Switch to using new SystemService lifecycle for faster event delivery. Restore broken global search observer. Verified that apps on external storage are handled correctly. Bug: 26471205 Change-Id: Id99ffe2ea6db37a394454cc7dfa4eab10280ff35 --- .../server/search/SearchManagerService.java | 110 ++++++++++-------- .../android/server/search/Searchables.java | 12 +- .../java/com/android/server/SystemServer.java | 8 +- .../server/search/SearchablesTest.java | 6 +- 4 files changed, 73 insertions(+), 63 deletions(-) diff --git a/services/core/java/com/android/server/search/SearchManagerService.java b/services/core/java/com/android/server/search/SearchManagerService.java index 4c7f88888adff..e3e1097cd04d6 100644 --- a/services/core/java/com/android/server/search/SearchManagerService.java +++ b/services/core/java/com/android/server/search/SearchManagerService.java @@ -23,19 +23,16 @@ import android.app.IActivityManager; import android.app.ISearchManager; import android.app.SearchManager; import android.app.SearchableInfo; -import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; -import android.content.IntentFilter; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.database.ContentObserver; import android.os.Binder; import android.os.Bundle; -import android.os.Process; import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; @@ -43,9 +40,11 @@ import android.provider.Settings; import android.util.Log; import android.util.SparseArray; +import com.android.internal.annotations.GuardedBy; import com.android.internal.content.PackageMonitor; import com.android.internal.util.IndentingPrintWriter; import com.android.server.LocalServices; +import com.android.server.SystemService; import com.android.server.statusbar.StatusBarManagerInternal; import java.io.FileDescriptor; @@ -53,19 +52,42 @@ import java.io.PrintWriter; import java.util.List; /** - * The search manager service handles the search UI, and maintains a registry of searchable - * activities. + * The search manager service handles the search UI, and maintains a registry of + * searchable activities. */ public class SearchManagerService extends ISearchManager.Stub { - - // general debugging support private static final String TAG = "SearchManagerService"; + public static class Lifecycle extends SystemService { + private SearchManagerService mService; + + public Lifecycle(Context context) { + super(context); + } + + @Override + public void onStart() { + mService = new SearchManagerService(getContext()); + publishBinderService(Context.SEARCH_SERVICE, mService); + } + + @Override + public void onUnlockUser(int userHandle) { + mService.onUnlockUser(userHandle); + } + + @Override + public void onCleanupUser(int userHandle) { + mService.onCleanupUser(userHandle); + } + } + // Context that the service is running in. private final Context mContext; // This field is initialized lazily in getSearchables(), and then never modified. - private final SparseArray mSearchables = new SparseArray(); + @GuardedBy("mSearchables") + private final SparseArray mSearchables = new SparseArray<>(); /** * Initializes the Search Manager service in the provided system context. @@ -75,65 +97,47 @@ public class SearchManagerService extends ISearchManager.Stub { */ public SearchManagerService(Context context) { mContext = context; - IntentFilter filter = new IntentFilter(Intent.ACTION_BOOT_COMPLETED); - filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); - mContext.registerReceiver(new BootCompletedReceiver(), filter); - mContext.registerReceiver(new UserReceiver(), - new IntentFilter(Intent.ACTION_USER_REMOVED)); new MyPackageMonitor().register(context, null, UserHandle.ALL, true); + new GlobalSearchProviderObserver(context.getContentResolver()); } private Searchables getSearchables(int userId) { - long origId = Binder.clearCallingIdentity(); + return getSearchables(userId, false); + } + + private Searchables getSearchables(int userId, boolean forceUpdate) { + final long token = Binder.clearCallingIdentity(); try { - boolean userExists = ((UserManager) mContext.getSystemService(Context.USER_SERVICE)) - .getUserInfo(userId) != null; - if (!userExists) return null; + final UserManager um = mContext.getSystemService(UserManager.class); + if (um.getUserInfo(userId) == null) { + throw new IllegalStateException("User " + userId + " doesn't exist"); + } + if (!um.isUserUnlocked(userId)) { + throw new IllegalStateException("User " + userId + " isn't unlocked"); + } } finally { - Binder.restoreCallingIdentity(origId); + Binder.restoreCallingIdentity(token); } synchronized (mSearchables) { Searchables searchables = mSearchables.get(userId); - if (searchables == null) { - //Log.i(TAG, "Building list of searchable activities for userId=" + userId); searchables = new Searchables(mContext, userId); - searchables.buildSearchableList(); + searchables.updateSearchableList(); mSearchables.append(userId, searchables); + } else if (forceUpdate) { + searchables.updateSearchableList(); } return searchables; } } - private void onUserRemoved(int userId) { - if (userId != UserHandle.USER_NULL) { - synchronized (mSearchables) { - mSearchables.remove(userId); - } - } + private void onUnlockUser(int userId) { + getSearchables(userId, true); } - /** - * Creates the initial searchables list after boot. - */ - private final class BootCompletedReceiver extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - new Thread() { - @Override - public void run() { - Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); - mContext.unregisterReceiver(BootCompletedReceiver.this); - getSearchables(0); - } - }.start(); - } - } - - private final class UserReceiver extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - onUserRemoved(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL)); + private void onCleanupUser(int userId) { + synchronized (mSearchables) { + mSearchables.remove(userId); } } @@ -158,7 +162,7 @@ public class SearchManagerService extends ISearchManager.Stub { // Update list of searchable activities for (int i = 0; i < mSearchables.size(); i++) { if (changingUserId == mSearchables.keyAt(i)) { - getSearchables(mSearchables.keyAt(i)).buildSearchableList(); + mSearchables.valueAt(i).updateSearchableList(); break; } } @@ -187,14 +191,13 @@ public class SearchManagerService extends ISearchManager.Stub { public void onChange(boolean selfChange) { synchronized (mSearchables) { for (int i = 0; i < mSearchables.size(); i++) { - getSearchables(mSearchables.keyAt(i)).buildSearchableList(); + mSearchables.valueAt(i).updateSearchableList(); } } Intent intent = new Intent(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED); intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); mContext.sendBroadcastAsUser(intent, UserHandle.ALL); } - } // @@ -208,6 +211,7 @@ public class SearchManagerService extends ISearchManager.Stub { * @return Returns a SearchableInfo record describing the parameters of the search, * or null if no searchable metadata was available. */ + @Override public SearchableInfo getSearchableInfo(final ComponentName launchActivity) { if (launchActivity == null) { Log.e(TAG, "getSearchableInfo(), activity == null"); @@ -219,10 +223,12 @@ public class SearchManagerService extends ISearchManager.Stub { /** * Returns a list of the searchable activities that can be included in global search. */ + @Override public List getSearchablesInGlobalSearch() { return getSearchables(UserHandle.getCallingUserId()).getSearchablesInGlobalSearchList(); } + @Override public List getGlobalSearchActivities() { return getSearchables(UserHandle.getCallingUserId()).getGlobalSearchActivities(); } @@ -230,6 +236,7 @@ public class SearchManagerService extends ISearchManager.Stub { /** * Gets the name of the global search activity. */ + @Override public ComponentName getGlobalSearchActivity() { return getSearchables(UserHandle.getCallingUserId()).getGlobalSearchActivity(); } @@ -237,6 +244,7 @@ public class SearchManagerService extends ISearchManager.Stub { /** * Gets the name of the web search activity. */ + @Override public ComponentName getWebSearchActivity() { return getSearchables(UserHandle.getCallingUserId()).getWebSearchActivity(); } diff --git a/services/core/java/com/android/server/search/Searchables.java b/services/core/java/com/android/server/search/Searchables.java index 0ffbb7d0aa590..0046fbb9a2610 100644 --- a/services/core/java/com/android/server/search/Searchables.java +++ b/services/core/java/com/android/server/search/Searchables.java @@ -200,7 +200,7 @@ public class Searchables { * * TODO: sort the list somehow? UI choice. */ - public void buildSearchableList() { + public void updateSearchableList() { // These will become the new values at the end of the method HashMap newSearchablesMap = new HashMap(); @@ -215,11 +215,13 @@ public class Searchables { long ident = Binder.clearCallingIdentity(); try { - searchList = queryIntentActivities(intent, PackageManager.GET_META_DATA); + searchList = queryIntentActivities(intent, + PackageManager.GET_META_DATA | PackageManager.MATCH_DEBUG_TRIAGED_MISSING); List webSearchInfoList; final Intent webSearchIntent = new Intent(Intent.ACTION_WEB_SEARCH); - webSearchInfoList = queryIntentActivities(webSearchIntent, PackageManager.GET_META_DATA); + webSearchInfoList = queryIntentActivities(webSearchIntent, + PackageManager.GET_META_DATA | PackageManager.MATCH_DEBUG_TRIAGED_MISSING); // analyze each one, generate a Searchables record, and record if (searchList != null || webSearchInfoList != null) { @@ -282,8 +284,8 @@ public class Searchables { // 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); - List activities = - queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY); + List activities = queryIntentActivities(intent, + PackageManager.MATCH_DEFAULT_ONLY | PackageManager.MATCH_DEBUG_TRIAGED_MISSING); if (activities != null && !activities.isEmpty()) { // Step 2: Rank matching activities according to our heuristics. Collections.sort(activities, GLOBAL_SEARCH_RANKER); diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index dd6493c585f76..287b39c235c3e 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -45,7 +45,6 @@ import android.util.DisplayMetrics; import android.util.EventLog; import android.util.Slog; import android.view.WindowManager; -import android.webkit.WebViewFactory; import com.android.internal.R; import com.android.internal.os.BinderInternal; @@ -81,7 +80,6 @@ import com.android.server.pm.UserManagerService; import com.android.server.power.PowerManagerService; import com.android.server.power.ShutdownThread; import com.android.server.restrictions.RestrictionsManagerService; -import com.android.server.search.SearchManagerService; import com.android.server.statusbar.StatusBarManagerService; import com.android.server.storage.DeviceStorageMonitorService; import com.android.server.telecom.TelecomLoaderService; @@ -138,6 +136,9 @@ public final class SystemServer { "com.android.server.job.JobSchedulerService"; private static final String MOUNT_SERVICE_CLASS = "com.android.server.MountService$Lifecycle"; + private static final String SEARCH_MANAGER_SERVICE_CLASS = + "com.android.server.search.SearchManagerService$Lifecycle"; + private static final String PERSISTENT_DATA_BLOCK_PROP = "ro.frp.pst"; /** @@ -844,8 +845,7 @@ public final class SystemServer { if (!disableNonCoreServices) { traceBeginAndSlog("StartSearchManagerService"); try { - ServiceManager.addService(Context.SEARCH_SERVICE, - new SearchManagerService(context)); + mSystemServiceManager.startService(SEARCH_MANAGER_SERVICE_CLASS); } catch (Throwable e) { reportWtf("starting Search Service", e); } diff --git a/services/tests/servicestests/src/com/android/server/search/SearchablesTest.java b/services/tests/servicestests/src/com/android/server/search/SearchablesTest.java index 79b9135d9466b..0f9bf2f6cd860 100644 --- a/services/tests/servicestests/src/com/android/server/search/SearchablesTest.java +++ b/services/tests/servicestests/src/com/android/server/search/SearchablesTest.java @@ -72,7 +72,7 @@ public class SearchablesTest extends AndroidTestCase { public void testNonSearchable() { // test basic array & hashmap Searchables searchables = new Searchables(mContext, 0); - searchables.buildSearchableList(); + searchables.updateSearchableList(); // confirm that we return null for non-searchy activities ComponentName nonActivity = new ComponentName( @@ -104,7 +104,7 @@ public class SearchablesTest extends AndroidTestCase { // build item list with real-world source data mockPM.setSearchablesMode(MyMockPackageManager.SEARCHABLES_PASSTHROUGH); Searchables searchables = new Searchables(mockContext, 0); - searchables.buildSearchableList(); + searchables.updateSearchableList(); // tests with "real" searchables (deprecate, this should be a unit test) ArrayList searchablesList = searchables.getSearchablesList(); int count = searchablesList.size(); @@ -123,7 +123,7 @@ public class SearchablesTest extends AndroidTestCase { mockPM.setSearchablesMode(MyMockPackageManager.SEARCHABLES_MOCK_ZERO); Searchables searchables = new Searchables(mockContext, 0); - searchables.buildSearchableList(); + searchables.updateSearchableList(); ArrayList searchablesList = searchables.getSearchablesList(); assertNotNull(searchablesList); MoreAsserts.assertEmpty(searchablesList);