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
This commit is contained in:
@@ -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<Searchables> mSearchables = new SparseArray<Searchables>();
|
||||
@GuardedBy("mSearchables")
|
||||
private final SparseArray<Searchables> 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<SearchableInfo> getSearchablesInGlobalSearch() {
|
||||
return getSearchables(UserHandle.getCallingUserId()).getSearchablesInGlobalSearchList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ResolveInfo> 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();
|
||||
}
|
||||
|
||||
@@ -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<ComponentName, SearchableInfo> newSearchablesMap
|
||||
= new HashMap<ComponentName, SearchableInfo>();
|
||||
@@ -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<ResolveInfo> 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<ResolveInfo> activities =
|
||||
queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
|
||||
List<ResolveInfo> 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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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<SearchableInfo> 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<SearchableInfo> searchablesList = searchables.getSearchablesList();
|
||||
assertNotNull(searchablesList);
|
||||
MoreAsserts.assertEmpty(searchablesList);
|
||||
|
||||
Reference in New Issue
Block a user