diff options
| author | Bjorn Bringert <bringert@android.com> | 2009-06-05 13:22:28 +0100 |
|---|---|---|
| committer | Bjorn Bringert <bringert@android.com> | 2009-06-15 09:05:26 +0100 |
| commit | 8d17f3f24bbda9a9cd7ea08c5925508dc2c011be (patch) | |
| tree | cf7e5ebb721354bfffc41d2ab4932cb70ea3c033 /core/java/android/server | |
| parent | 3d59ee7aa66a5b1e80f5042f8d872dd9819b5f40 (diff) | |
| download | frameworks_base-8d17f3f24bbda9a9cd7ea08c5925508dc2c011be.zip frameworks_base-8d17f3f24bbda9a9cd7ea08c5925508dc2c011be.tar.gz frameworks_base-8d17f3f24bbda9a9cd7ea08c5925508dc2c011be.tar.bz2 | |
Run search dialog in the system process.
Fixes http://b/issue?id=1905863
This is needed to address two security issues with global search:
http://b/issue?id=1871088 (Apps can read content providers through GlobalSearch)
http://b/issue?id=1819627 (Apps can use GlobalSearch to launch arbirtrary intents)
This also fixes http://b/issue?id=1693153 (SearchManager.OnDismissListener
never gets called)
To fix the security issues, GlobalSearch also needs to require
a non-app permission to access its content provider and launch intents.
Diffstat (limited to 'core/java/android/server')
| -rw-r--r-- | core/java/android/server/search/SearchManagerService.java | 195 | ||||
| -rw-r--r-- | core/java/android/server/search/SearchableInfo.java | 20 |
2 files changed, 205 insertions, 10 deletions
diff --git a/core/java/android/server/search/SearchManagerService.java b/core/java/android/server/search/SearchManagerService.java index 060bcea..db812d1 100644 --- a/core/java/android/server/search/SearchManagerService.java +++ b/core/java/android/server/search/SearchManagerService.java @@ -17,15 +17,25 @@ package android.server.search; import android.app.ISearchManager; +import android.app.ISearchManagerCallback; +import android.app.SearchDialog; +import android.app.SearchManager; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; +import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; +import android.content.res.Configuration; +import android.os.Bundle; import android.os.Handler; import android.os.RemoteException; +import android.util.Log; import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.FutureTask; /** * This is a simplified version of the Search Manager service. It no longer handles @@ -34,16 +44,20 @@ import java.util.List; * invoked search) to specific searchable activities (where the search will be dispatched). */ public class SearchManagerService extends ISearchManager.Stub + implements DialogInterface.OnCancelListener, DialogInterface.OnDismissListener { // general debugging support private static final String TAG = "SearchManagerService"; - private static final boolean DEBUG = false; + private static final boolean DBG = false; // class maintenance and general shared data private final Context mContext; private final Handler mHandler; private boolean mSearchablesDirty; - private Searchables mSearchables; + private final Searchables mSearchables; + + final SearchDialog mSearchDialog; + ISearchManagerCallback mCallback = null; /** * Initializes the Search Manager service in the provided system context. @@ -56,6 +70,9 @@ public class SearchManagerService extends ISearchManager.Stub mHandler = new Handler(); mSearchablesDirty = true; mSearchables = new Searchables(context); + mSearchDialog = new SearchDialog(context); + mSearchDialog.setOnCancelListener(this); + mSearchDialog.setOnDismissListener(this); // Setup the infrastructure for updating and maintaining the list // of searchable activities. @@ -107,6 +124,7 @@ public class SearchManagerService extends ISearchManager.Stub * a package add/remove broadcast message. */ private void updateSearchables() { + if (DBG) debug("updateSearchables()"); mSearchables.buildSearchableList(); mSearchablesDirty = false; } @@ -137,6 +155,10 @@ public class SearchManagerService extends ISearchManager.Stub if (globalSearch) { si = mSearchables.getDefaultSearchable(); } else { + if (launchActivity == null) { + Log.e(TAG, "getSearchableInfo(), activity == null"); + return null; + } si = mSearchables.getSearchableInfo(launchActivity); } @@ -150,6 +172,145 @@ public class SearchManagerService extends ISearchManager.Stub updateSearchablesIfDirty(); return mSearchables.getSearchablesInGlobalSearchList(); } + /** + * Launches the search UI on the main thread of the service. + * + * @see SearchManager#startSearch(String, boolean, ComponentName, Bundle, boolean) + */ + public void startSearch(final String initialQuery, + final boolean selectInitialQuery, + final ComponentName launchActivity, + final Bundle appSearchData, + final boolean globalSearch, + final ISearchManagerCallback searchManagerCallback) { + if (DBG) debug("startSearch()"); + Runnable task = new Runnable() { + public void run() { + performStartSearch(initialQuery, + selectInitialQuery, + launchActivity, + appSearchData, + globalSearch, + searchManagerCallback); + } + }; + mHandler.post(task); + } + + /** + * Actually launches the search. This must be called on the service UI thread. + */ + /*package*/ void performStartSearch(String initialQuery, + boolean selectInitialQuery, + ComponentName launchActivity, + Bundle appSearchData, + boolean globalSearch, + ISearchManagerCallback searchManagerCallback) { + if (DBG) debug("performStartSearch()"); + mSearchDialog.show(initialQuery, selectInitialQuery, launchActivity, appSearchData, + globalSearch); + if (searchManagerCallback != null) { + mCallback = searchManagerCallback; + } + } + + /** + * Cancels the search dialog. Can be called from any thread. + */ + public void stopSearch() { + if (DBG) debug("stopSearch()"); + mHandler.post(new Runnable() { + public void run() { + performStopSearch(); + } + }); + } + + /** + * Cancels the search dialog. Must be called from the service UI thread. + */ + /*package*/ void performStopSearch() { + if (DBG) debug("performStopSearch()"); + mSearchDialog.cancel(); + } + + /** + * Determines if the Search UI is currently displayed. + * + * @see SearchManager#isVisible() + */ + public boolean isVisible() { + return postAndWait(mIsShowing, false, "isShowing()"); + } + + private final Callable<Boolean> mIsShowing = new Callable<Boolean>() { + public Boolean call() { + return mSearchDialog.isShowing(); + } + }; + + public Bundle onSaveInstanceState() { + return postAndWait(mOnSaveInstanceState, null, "onSaveInstanceState()"); + } + + private final Callable<Bundle> mOnSaveInstanceState = new Callable<Bundle>() { + public Bundle call() { + if (mSearchDialog.isShowing()) { + return mSearchDialog.onSaveInstanceState(); + } else { + return null; + } + } + }; + + public void onRestoreInstanceState(final Bundle searchDialogState) { + if (searchDialogState != null) { + mHandler.post(new Runnable() { + public void run() { + mSearchDialog.onRestoreInstanceState(searchDialogState); + } + }); + } + } + + public void onConfigurationChanged(final Configuration newConfig) { + mHandler.post(new Runnable() { + public void run() { + if (mSearchDialog.isShowing()) { + mSearchDialog.onConfigurationChanged(newConfig); + } + } + }); + } + + /** + * Called by {@link SearchDialog} when it goes away. + */ + public void onDismiss(DialogInterface dialog) { + if (DBG) debug("onDismiss()"); + if (mCallback != null) { + try { + mCallback.onDismiss(); + } catch (RemoteException ex) { + Log.e(TAG, "onDismiss() failed: " + ex); + } + } + } + + /** + * Called by {@link SearchDialog} when the user or activity cancels search. + * When this is called, {@link #onDismiss} is called too. + */ + public void onCancel(DialogInterface dialog) { + if (DBG) debug("onCancel()"); + if (mCallback != null) { + try { + mCallback.onCancel(); + } catch (RemoteException ex) { + Log.e(TAG, "onCancel() failed: " + ex); + } + } + } /** * Returns a list of the searchable activities that handle web searches. @@ -173,4 +334,34 @@ public class SearchManagerService extends ISearchManager.Stub public void setDefaultWebSearch(ComponentName component) { mSearchables.setDefaultWebSearch(component); } + + /** + * Runs an operation on the handler for the service, blocks until it returns, + * and returns the value returned by the operation. + * + * @param <V> Return value type. + * @param callable Operation to run. + * @param errorResult Value to return if the operations throws an exception. + * @param name Operation name to include in error log messages. + * @return The value returned by the operation. + */ + private <V> V postAndWait(Callable<V> callable, V errorResult, String name) { + FutureTask<V> task = new FutureTask<V>(callable); + mHandler.post(task); + try { + return task.get(); + } catch (InterruptedException ex) { + Log.e(TAG, "Error calling " + name + ": " + ex); + return errorResult; + } catch (ExecutionException ex) { + Log.e(TAG, "Error calling " + name + ": " + ex); + return errorResult; + } + } + + private static void debug(String msg) { + Thread thread = Thread.currentThread(); + Log.d(TAG, msg + " (" + thread.getName() + "-" + thread.getId() + ")"); + } + } diff --git a/core/java/android/server/search/SearchableInfo.java b/core/java/android/server/search/SearchableInfo.java index 4df7368..90dfa0b 100644 --- a/core/java/android/server/search/SearchableInfo.java +++ b/core/java/android/server/search/SearchableInfo.java @@ -320,7 +320,7 @@ public final class SearchableInfo implements Parcelable { // for now, implement some form of rules - minimal data if (mLabelId == 0) { - throw new IllegalArgumentException("No label."); + throw new IllegalArgumentException("Search label must be a resource reference."); } } @@ -441,13 +441,17 @@ public final class SearchableInfo implements Parcelable { xml.close(); if (DBG) { - Log.d(LOG_TAG, "Checked " + activityInfo.name - + ",label=" + searchable.getLabelId() - + ",icon=" + searchable.getIconId() - + ",suggestAuthority=" + searchable.getSuggestAuthority() - + ",target=" + searchable.getSearchActivity().getClassName() - + ",global=" + searchable.shouldIncludeInGlobalSearch() - + ",threshold=" + searchable.getSuggestThreshold()); + if (searchable != null) { + Log.d(LOG_TAG, "Checked " + activityInfo.name + + ",label=" + searchable.getLabelId() + + ",icon=" + searchable.getIconId() + + ",suggestAuthority=" + searchable.getSuggestAuthority() + + ",target=" + searchable.getSearchActivity().getClassName() + + ",global=" + searchable.shouldIncludeInGlobalSearch() + + ",threshold=" + searchable.getSuggestThreshold()); + } else { + Log.d(LOG_TAG, "Checked " + activityInfo.name + ", no searchable meta-data"); + } } return searchable; } |
