diff options
18 files changed, 353 insertions, 132 deletions
@@ -65,15 +65,16 @@ endif ## READ ME: ######################################################## LOCAL_SRC_FILES += \ core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl \ - core/java/android/accessibilityservice/IEventListener.aidl \ + core/java/android/accessibilityservice/IEventListener.aidl \ core/java/android/accounts/IAccountManager.aidl \ core/java/android/accounts/IAccountManagerResponse.aidl \ core/java/android/accounts/IAccountAuthenticator.aidl \ core/java/android/accounts/IAccountAuthenticatorResponse.aidl \ + core/java/android/app/IActivityController.aidl \ core/java/android/app/IActivityPendingResult.aidl \ core/java/android/app/IActivityWatcher.aidl \ core/java/android/app/IAlarmManager.aidl \ - core/java/android/app/IBackupAgent.aidl \ + core/java/android/app/IBackupAgent.aidl \ core/java/android/app/IInstrumentationWatcher.aidl \ core/java/android/app/INotificationManager.aidl \ core/java/android/app/ISearchManager.aidl \ @@ -90,12 +91,12 @@ LOCAL_SRC_FILES += \ core/java/android/bluetooth/IBluetoothA2dp.aidl \ core/java/android/bluetooth/IBluetoothDevice.aidl \ core/java/android/bluetooth/IBluetoothHeadset.aidl \ - core/java/android/content/IContentService.aidl \ + core/java/android/content/IContentService.aidl \ core/java/android/content/IIntentReceiver.aidl \ core/java/android/content/IIntentSender.aidl \ core/java/android/content/ISyncAdapter.aidl \ core/java/android/content/ISyncContext.aidl \ - core/java/android/content/ISyncStatusObserver.aidl \ + core/java/android/content/ISyncStatusObserver.aidl \ core/java/android/content/pm/IPackageDataObserver.aidl \ core/java/android/content/pm/IPackageDeleteObserver.aidl \ core/java/android/content/pm/IPackageInstallObserver.aidl \ diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 6c2560d..4ac3b9e 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -612,6 +612,7 @@ public class Activity extends ContextThemeWrapper // set by the thread after the constructor and before onCreate(Bundle savedInstanceState) is called. private Instrumentation mInstrumentation; private IBinder mToken; + private int mIdent; /*package*/ String mEmbeddedID; private Application mApplication; /*package*/ Intent mIntent; @@ -789,9 +790,6 @@ public class Activity extends ContextThemeWrapper protected void onCreate(Bundle savedInstanceState) { mVisibleFromClient = mWindow.getWindowStyle().getBoolean( com.android.internal.R.styleable.Window_windowNoDisplay, true); - // uses super.getSystemService() since this.getSystemService() looks at the - // mSearchManager field. - mSearchManager = (SearchManager) super.getSystemService(Context.SEARCH_SERVICE); mCalled = true; } @@ -2531,6 +2529,7 @@ public class Activity extends ContextThemeWrapper */ public void startSearch(String initialQuery, boolean selectInitialQuery, Bundle appSearchData, boolean globalSearch) { + ensureSearchManager(); mSearchManager.startSearch(initialQuery, selectInitialQuery, getComponentName(), appSearchData, globalSearch); } @@ -3241,6 +3240,24 @@ public class Activity extends ContextThemeWrapper return getSharedPreferences(getLocalClassName(), mode); } + private void ensureSearchManager() { + if (mSearchManager != null) { + return; + } + + // uses super.getSystemService() since this.getSystemService() looks at the + // mSearchManager field. + mSearchManager = (SearchManager) super.getSystemService(Context.SEARCH_SERVICE); + int ident = mIdent; + if (ident == 0) { + if (mParent != null) ident = mParent.mIdent; + if (ident == 0) { + throw new IllegalArgumentException("no ident"); + } + } + mSearchManager.setIdent(ident); + } + @Override public Object getSystemService(String name) { if (getBaseContext() == null) { @@ -3251,6 +3268,7 @@ public class Activity extends ContextThemeWrapper if (WINDOW_SERVICE.equals(name)) { return mWindowManager; } else if (SEARCH_SERVICE.equals(name)) { + ensureSearchManager(); return mSearchManager; } return super.getSystemService(name); @@ -3450,14 +3468,17 @@ public class Activity extends ContextThemeWrapper Application application, Intent intent, ActivityInfo info, CharSequence title, Activity parent, String id, Object lastNonConfigurationInstance, Configuration config) { - attach(context, aThread, instr, token, application, intent, info, title, parent, id, + attach(context, aThread, instr, token, 0, application, intent, info, title, parent, id, lastNonConfigurationInstance, null, config); } - final void attach(Context context, ActivityThread aThread, Instrumentation instr, IBinder token, - Application application, Intent intent, ActivityInfo info, CharSequence title, - Activity parent, String id, Object lastNonConfigurationInstance, - HashMap<String,Object> lastNonConfigurationChildInstances, Configuration config) { + final void attach(Context context, ActivityThread aThread, + Instrumentation instr, IBinder token, int ident, + Application application, Intent intent, ActivityInfo info, + CharSequence title, Activity parent, String id, + Object lastNonConfigurationInstance, + HashMap<String,Object> lastNonConfigurationChildInstances, + Configuration config) { attachBaseContext(context); mWindow = PolicyManager.makeNewWindow(this); @@ -3470,6 +3491,7 @@ public class Activity extends ContextThemeWrapper mMainThread = aThread; mInstrumentation = instr; mToken = token; + mIdent = ident; mApplication = application; mIntent = intent; mComponent = intent.getComponent(); @@ -3554,9 +3576,6 @@ public class Activity extends ContextThemeWrapper final void performPause() { onPause(); - - // dismiss the search dialog if it is open - mSearchManager.stopSearch(); } final void performUserLeaving() { diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index dfa8139..ec7714d 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -881,11 +881,11 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM return true; } - case SET_ACTIVITY_WATCHER_TRANSACTION: { + case SET_ACTIVITY_CONTROLLER_TRANSACTION: { data.enforceInterface(IActivityManager.descriptor); - IActivityWatcher watcher = IActivityWatcher.Stub.asInterface( + IActivityController watcher = IActivityController.Stub.asInterface( data.readStrongBinder()); - setActivityWatcher(watcher); + setActivityController(watcher); return true; } @@ -1052,6 +1052,22 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM reply.writeNoException(); return true; } + + case REGISTER_ACTIVITY_WATCHER_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + IActivityWatcher watcher = IActivityWatcher.Stub.asInterface( + data.readStrongBinder()); + registerActivityWatcher(watcher); + return true; + } + + case UNREGISTER_ACTIVITY_WATCHER_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + IActivityWatcher watcher = IActivityWatcher.Stub.asInterface( + data.readStrongBinder()); + unregisterActivityWatcher(watcher); + return true; + } } return super.onTransact(code, data, reply, flags); @@ -2105,13 +2121,13 @@ class ActivityManagerProxy implements IActivityManager data.recycle(); reply.recycle(); } - public void setActivityWatcher(IActivityWatcher watcher) throws RemoteException + public void setActivityController(IActivityController watcher) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IActivityManager.descriptor); data.writeStrongBinder(watcher != null ? watcher.asBinder() : null); - mRemote.transact(SET_ACTIVITY_WATCHER_TRANSACTION, data, reply, 0); + mRemote.transact(SET_ACTIVITY_CONTROLLER_TRANSACTION, data, reply, 0); reply.readException(); data.recycle(); reply.recycle(); @@ -2290,5 +2306,29 @@ class ActivityManagerProxy implements IActivityManager data.recycle(); } + public void registerActivityWatcher(IActivityWatcher watcher) + throws RemoteException { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + data.writeStrongBinder(watcher != null ? watcher.asBinder() : null); + mRemote.transact(REGISTER_ACTIVITY_WATCHER_TRANSACTION, data, reply, 0); + reply.readException(); + data.recycle(); + reply.recycle(); + } + + public void unregisterActivityWatcher(IActivityWatcher watcher) + throws RemoteException { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + data.writeStrongBinder(watcher != null ? watcher.asBinder() : null); + mRemote.transact(UNREGISTER_ACTIVITY_WATCHER_TRANSACTION, data, reply, 0); + reply.readException(); + data.recycle(); + reply.recycle(); + } + private IBinder mRemote; } diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 27e8fb5..f2814f2 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -1090,6 +1090,7 @@ public final class ActivityThread { private static final class ActivityRecord { IBinder token; + int ident; Intent intent; Bundle state; Activity activity; @@ -1299,12 +1300,13 @@ public final class ActivityThread { // we use token to identify this activity without having to send the // activity itself back to the activity manager. (matters more with ipc) - public final void scheduleLaunchActivity(Intent intent, IBinder token, + public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident, ActivityInfo info, Bundle state, List<ResultInfo> pendingResults, List<Intent> pendingNewIntents, boolean notResumed, boolean isForward) { ActivityRecord r = new ActivityRecord(); r.token = token; + r.ident = ident; r.intent = intent; r.activityInfo = info; r.state = state; @@ -2197,21 +2199,11 @@ public final class ActivityThread { } public final Activity startActivityNow(Activity parent, String id, - Intent intent, IBinder token, Bundle state) { - ActivityInfo aInfo = resolveActivityInfo(intent); - return startActivityNow(parent, id, intent, aInfo, token, state); - } - - public final Activity startActivityNow(Activity parent, String id, - Intent intent, ActivityInfo activityInfo, IBinder token, Bundle state) { - return startActivityNow(parent, id, intent, activityInfo, token, state, null); - } - - public final Activity startActivityNow(Activity parent, String id, Intent intent, ActivityInfo activityInfo, IBinder token, Bundle state, Object lastNonConfigurationInstance) { ActivityRecord r = new ActivityRecord(); r.token = token; + r.ident = 0; r.intent = intent; r.state = state; r.parent = parent; @@ -2335,10 +2327,10 @@ public final class ActivityThread { appContext.setOuterContext(activity); CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager()); Configuration config = new Configuration(mConfiguration); - activity.attach(appContext, this, getInstrumentation(), r.token, app, - r.intent, r.activityInfo, title, r.parent, r.embeddedID, - r.lastNonConfigurationInstance, r.lastNonConfigurationChildInstances, - config); + activity.attach(appContext, this, getInstrumentation(), r.token, + r.ident, app, r.intent, r.activityInfo, title, r.parent, + r.embeddedID, r.lastNonConfigurationInstance, + r.lastNonConfigurationChildInstances, config); if (customIntent != null) { activity.mIntent = customIntent; diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java index b052c99..a3c6325 100644 --- a/core/java/android/app/ApplicationThreadNative.java +++ b/core/java/android/app/ApplicationThreadNative.java @@ -119,13 +119,15 @@ public abstract class ApplicationThreadNative extends Binder data.enforceInterface(IApplicationThread.descriptor); Intent intent = Intent.CREATOR.createFromParcel(data); IBinder b = data.readStrongBinder(); + int ident = data.readInt(); ActivityInfo info = ActivityInfo.CREATOR.createFromParcel(data); Bundle state = data.readBundle(); List<ResultInfo> ri = data.createTypedArrayList(ResultInfo.CREATOR); List<Intent> pi = data.createTypedArrayList(Intent.CREATOR); boolean notResumed = data.readInt() != 0; boolean isForward = data.readInt() != 0; - scheduleLaunchActivity(intent, b, info, state, ri, pi, notResumed, isForward); + scheduleLaunchActivity(intent, b, ident, info, state, ri, pi, + notResumed, isForward); return true; } @@ -442,7 +444,7 @@ class ApplicationThreadProxy implements IApplicationThread { data.recycle(); } - public final void scheduleLaunchActivity(Intent intent, IBinder token, + public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident, ActivityInfo info, Bundle state, List<ResultInfo> pendingResults, List<Intent> pendingNewIntents, boolean notResumed, boolean isForward) throws RemoteException { @@ -450,6 +452,7 @@ class ApplicationThreadProxy implements IApplicationThread { data.writeInterfaceToken(IApplicationThread.descriptor); intent.writeToParcel(data, 0); data.writeStrongBinder(token); + data.writeInt(ident); info.writeToParcel(data, 0); data.writeBundle(state); data.writeTypedList(pendingResults); diff --git a/core/java/android/app/IActivityController.aidl b/core/java/android/app/IActivityController.aidl new file mode 100644 index 0000000..8f6b252 --- /dev/null +++ b/core/java/android/app/IActivityController.aidl @@ -0,0 +1,55 @@ +/* +** +** Copyright 2009, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +package android.app; + +import android.content.Intent; + +/** + * Testing interface to monitor what is happening in the activity manager + * while tests are running. Not for normal application development. + * {@hide} + */ +interface IActivityController +{ + /** + * The system is trying to start an activity. Return true to allow + * it to be started as normal, or false to cancel/reject this activity. + */ + boolean activityStarting(in Intent intent, String pkg); + + /** + * The system is trying to return to an activity. Return true to allow + * it to be resumed as normal, or false to cancel/reject this activity. + */ + boolean activityResuming(String pkg); + + /** + * An application process has crashed (in Java). Return true for the + * normal error recovery (app crash dialog) to occur, false to kill + * it immediately. + */ + boolean appCrashed(String processName, int pid, String shortMsg, + String longMsg, in byte[] crashData); + + /** + * An application process is not responding. Return 0 to show the "app + * not responding" dialog, 1 to continue waiting, or -1 to kill it + * immediately. + */ + int appNotResponding(String processName, int pid, String processStats); +} diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java index 3ec7938..ee1b69b 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -216,7 +216,7 @@ public interface IActivityManager extends IInterface { String packageName, boolean waitForDebugger, boolean persistent) throws RemoteException; public void setAlwaysFinish(boolean enabled) throws RemoteException; - public void setActivityWatcher(IActivityWatcher watcher) + public void setActivityController(IActivityController watcher) throws RemoteException; public void enterSafeMode() throws RemoteException; @@ -257,6 +257,11 @@ public interface IActivityManager extends IInterface { public void stopAppSwitches() throws RemoteException; public void resumeAppSwitches() throws RemoteException; + public void registerActivityWatcher(IActivityWatcher watcher) + throws RemoteException; + public void unregisterActivityWatcher(IActivityWatcher watcher) + throws RemoteException; + /* * Private non-Binder interfaces */ @@ -372,7 +377,7 @@ public interface IActivityManager extends IInterface { int CHECK_URI_PERMISSION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+53; int GRANT_URI_PERMISSION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+54; int REVOKE_URI_PERMISSION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+55; - int SET_ACTIVITY_WATCHER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+56; + int SET_ACTIVITY_CONTROLLER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+56; int SHOW_WAITING_FOR_DEBUGGER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+57; int SIGNAL_PERSISTENT_PROCESSES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+58; int GET_RECENT_TASKS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+59; @@ -408,4 +413,6 @@ public interface IActivityManager extends IInterface { int START_BACKUP_AGENT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+89; int BACKUP_AGENT_CREATED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+90; int UNBIND_BACKUP_AGENT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+91; + int REGISTER_ACTIVITY_WATCHER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+92; + int UNREGISTER_ACTIVITY_WATCHER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+93; } diff --git a/core/java/android/app/IActivityWatcher.aidl b/core/java/android/app/IActivityWatcher.aidl index f13a385..5d36e3f 100644 --- a/core/java/android/app/IActivityWatcher.aidl +++ b/core/java/android/app/IActivityWatcher.aidl @@ -1,6 +1,6 @@ -/* //device/java/android/android/app/IInstrumentationWatcher.aidl +/* ** -** Copyright 2007, The Android Open Source Project +** Copyright 2009, The Android Open Source Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); ** you may not use this file except in compliance with the License. @@ -17,39 +17,10 @@ package android.app; -import android.content.Intent; - /** - * Testing interface to monitor what is happening in the activity manager - * while tests are running. Not for normal application development. + * Callback interface to watch the user's traversal through activities. * {@hide} */ -interface IActivityWatcher -{ - /** - * The system is trying to start an activity. Return true to allow - * it to be started as normal, or false to cancel/reject this activity. - */ - boolean activityStarting(in Intent intent, String pkg); - - /** - * The system is trying to return to an activity. Return true to allow - * it to be resumed as normal, or false to cancel/reject this activity. - */ - boolean activityResuming(String pkg); - - /** - * An application process has crashed (in Java). Return true for the - * normal error recovery (app crash dialog) to occur, false to kill - * it immediately. - */ - boolean appCrashed(String processName, int pid, String shortMsg, - String longMsg, in byte[] crashData); - - /** - * An application process is not responding. Return 0 to show the "app - * not responding" dialog, 1 to continue waiting, or -1 to kill it - * immediately. - */ - int appNotResponding(String processName, int pid, String processStats); +oneway interface IActivityWatcher { + void activityResuming(int activityId); } diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java index c0bc2a0..c915770 100644 --- a/core/java/android/app/IApplicationThread.java +++ b/core/java/android/app/IApplicationThread.java @@ -49,7 +49,7 @@ public interface IApplicationThread extends IInterface { void scheduleWindowVisibility(IBinder token, boolean showWindow) throws RemoteException; void scheduleResumeActivity(IBinder token, boolean isForward) throws RemoteException; void scheduleSendResult(IBinder token, List<ResultInfo> results) throws RemoteException; - void scheduleLaunchActivity(Intent intent, IBinder token, + void scheduleLaunchActivity(Intent intent, IBinder token, int ident, ActivityInfo info, Bundle state, List<ResultInfo> pendingResults, List<Intent> pendingNewIntents, boolean notResumed, boolean isForward) throws RemoteException; diff --git a/core/java/android/app/ISearchManager.aidl b/core/java/android/app/ISearchManager.aidl index 5b62192..84a6085 100644 --- a/core/java/android/app/ISearchManager.aidl +++ b/core/java/android/app/ISearchManager.aidl @@ -34,6 +34,7 @@ interface ISearchManager { in ComponentName launchActivity, in Bundle appSearchData, boolean globalSearch, - ISearchManagerCallback searchManagerCallback); + ISearchManagerCallback searchManagerCallback, + int ident); void stopSearch(); } diff --git a/core/java/android/app/SearchDialog.java b/core/java/android/app/SearchDialog.java index 9f44c7e..13eb034 100644 --- a/core/java/android/app/SearchDialog.java +++ b/core/java/android/app/SearchDialog.java @@ -752,6 +752,9 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS } public void afterTextChanged(Editable s) { + if (mSearchable == null) { + return; + } if (mSearchable.autoUrlDetect() && !mSearchAutoComplete.isPerformingCompletion()) { // The user changed the query, check if it is a URL and if so change the search // button in the soft keyboard to the 'Go' button. diff --git a/core/java/android/app/SearchManager.java b/core/java/android/app/SearchManager.java index 0291882..b795a54 100644 --- a/core/java/android/app/SearchManager.java +++ b/core/java/android/app/SearchManager.java @@ -1531,6 +1531,8 @@ public class SearchManager private final Context mContext; + private int mIdent; + // package private since they are used by the inner class SearchManagerCallback /* package */ boolean mIsShowing = false; /* package */ final Handler mHandler; @@ -1546,6 +1548,13 @@ public class SearchManager ServiceManager.getService(Context.SEARCH_SERVICE)); } + /*package*/ void setIdent(int ident) { + if (mIdent != 0) { + throw new IllegalStateException("mIdent already set"); + } + mIdent = ident; + } + /** * Launch search UI. * @@ -1593,11 +1602,13 @@ public class SearchManager boolean globalSearch) { if (DBG) debug("startSearch(), mIsShowing=" + mIsShowing); if (mIsShowing) return; + if (mIdent == 0) throw new IllegalArgumentException( + "Called from outside of an Activity context"); try { mIsShowing = true; // activate the search manager and start it up! mService.startSearch(initialQuery, selectInitialQuery, launchActivity, appSearchData, - globalSearch, mSearchManagerCallback); + globalSearch, mSearchManagerCallback, mIdent); } catch (RemoteException ex) { Log.e(TAG, "startSearch() failed: " + ex); } diff --git a/core/java/android/content/SyncStorageEngine.java b/core/java/android/content/SyncStorageEngine.java index 13bcdd3..9d2efb5 100644 --- a/core/java/android/content/SyncStorageEngine.java +++ b/core/java/android/content/SyncStorageEngine.java @@ -326,6 +326,7 @@ public class SyncStorageEngine extends Handler { } reports.add(mChangeListeners.getBroadcastItem(i)); } + mChangeListeners.finishBroadcast(); } if (DEBUG) Log.v(TAG, "reportChange " + which + " to: " + reports); diff --git a/core/java/android/os/RemoteCallbackList.java b/core/java/android/os/RemoteCallbackList.java index 5ab305e..b74af16 100644 --- a/core/java/android/os/RemoteCallbackList.java +++ b/core/java/android/os/RemoteCallbackList.java @@ -50,6 +50,7 @@ public class RemoteCallbackList<E extends IInterface> { /*package*/ HashMap<IBinder, Callback> mCallbacks = new HashMap<IBinder, Callback>(); private Object[] mActiveBroadcast; + private int mBroadcastCount = -1; private boolean mKilled = false; private final class Callback implements IBinder.DeathRecipient { @@ -196,15 +197,16 @@ public class RemoteCallbackList<E extends IInterface> { * This creates a copy of the callback list, which you can retrieve items * from using {@link #getBroadcastItem}. Note that only one broadcast can * be active at a time, so you must be sure to always call this from the - * same thread (usually by scheduling with {@link Handler} or + * same thread (usually by scheduling with {@link Handler}) or * do your own synchronization. You must call {@link #finishBroadcast} * when done. * * <p>A typical loop delivering a broadcast looks like this: * * <pre> - * final int N = callbacks.beginBroadcast(); - * for (int i=0; i<N; i++) { + * int i = callbacks.beginBroadcast(); + * while (i > 0) { + * i--; * try { * callbacks.getBroadcastItem(i).somethingHappened(); * } catch (RemoteException e) { @@ -223,7 +225,12 @@ public class RemoteCallbackList<E extends IInterface> { */ public int beginBroadcast() { synchronized (mCallbacks) { - final int N = mCallbacks.size(); + if (mBroadcastCount > 0) { + throw new IllegalStateException( + "beginBroadcast() called while already in a broadcast"); + } + + final int N = mBroadcastCount = mCallbacks.size(); if (N <= 0) { return 0; } @@ -282,12 +289,19 @@ public class RemoteCallbackList<E extends IInterface> { * @see #beginBroadcast */ public void finishBroadcast() { + if (mBroadcastCount < 0) { + throw new IllegalStateException( + "finishBroadcast() called outside of a broadcast"); + } + Object[] active = mActiveBroadcast; if (active != null) { - final int N = active.length; + final int N = mBroadcastCount; for (int i=0; i<N; i++) { active[i] = null; } } + + mBroadcastCount = -1; } } diff --git a/core/java/android/server/search/SearchDialogWrapper.java b/core/java/android/server/search/SearchDialogWrapper.java index dbc1e7f..67be6a6 100644 --- a/core/java/android/server/search/SearchDialogWrapper.java +++ b/core/java/android/server/search/SearchDialogWrapper.java @@ -63,12 +63,13 @@ implements DialogInterface.OnCancelListener, DialogInterface.OnDismissListener { private static final int MSG_START_SEARCH = 1; // Takes no arguments private static final int MSG_STOP_SEARCH = 2; - // Takes no arguments - private static final int MSG_ON_CONFIGURATION_CHANGED = 3; + // arg1 is activity id + private static final int MSG_ACTIVITY_RESUMING = 3; private static final String KEY_INITIAL_QUERY = "q"; private static final String KEY_LAUNCH_ACTIVITY = "a"; private static final String KEY_APP_SEARCH_DATA = "d"; + private static final String KEY_IDENT= "i"; // Context used for getting search UI resources private final Context mContext; @@ -82,9 +83,18 @@ implements DialogInterface.OnCancelListener, DialogInterface.OnDismissListener { // If the search UI is visible, this is the callback for the client that showed it. ISearchManagerCallback mCallback = null; + // Identity of last activity that started search. + private int mStartedIdent = 0; + + // Identity of currently resumed activity. + private int mResumedIdent = 0; + // Allows disabling of search dialog for stress testing runs private final boolean mDisabledOnBoot; + // True if we have registered our receivers. + private boolean mReceiverRegistered; + /** * Creates a new search dialog wrapper and a search UI thread. The search dialog itself will * be created some asynchronously on the search UI thread. @@ -116,15 +126,21 @@ implements DialogInterface.OnCancelListener, DialogInterface.OnDismissListener { } private void registerBroadcastReceiver() { - IntentFilter closeDialogsFilter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); - mContext.registerReceiver(mBroadcastReceiver, closeDialogsFilter); - IntentFilter configurationChangedFilter = - new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED); - mContext.registerReceiver(mBroadcastReceiver, configurationChangedFilter); + if (!mReceiverRegistered) { + IntentFilter filter = new IntentFilter( + Intent.ACTION_CLOSE_SYSTEM_DIALOGS); + filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED); + mContext.registerReceiver(mBroadcastReceiver, filter, null, + mSearchUiThread); + mReceiverRegistered = true; + } } private void unregisterBroadcastReceiver() { - mContext.unregisterReceiver(mBroadcastReceiver); + if (mReceiverRegistered) { + mContext.unregisterReceiver(mBroadcastReceiver); + mReceiverRegistered = false; + } } /** @@ -136,10 +152,10 @@ implements DialogInterface.OnCancelListener, DialogInterface.OnDismissListener { String action = intent.getAction(); if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) { if (DBG) debug(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); - stopSearch(); + performStopSearch(); } else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) { if (DBG) debug(Intent.ACTION_CONFIGURATION_CHANGED); - onConfigurationChanged(); + performOnConfigurationChanged(); } } }; @@ -159,7 +175,8 @@ implements DialogInterface.OnCancelListener, DialogInterface.OnDismissListener { final ComponentName launchActivity, final Bundle appSearchData, final boolean globalSearch, - final ISearchManagerCallback searchManagerCallback) { + final ISearchManagerCallback searchManagerCallback, + int ident) { if (DBG) debug("startSearch()"); Message msg = Message.obtain(); msg.what = MSG_START_SEARCH; @@ -170,6 +187,7 @@ implements DialogInterface.OnCancelListener, DialogInterface.OnDismissListener { msgData.putString(KEY_INITIAL_QUERY, initialQuery); msgData.putParcelable(KEY_LAUNCH_ACTIVITY, launchActivity); msgData.putBundle(KEY_APP_SEARCH_DATA, appSearchData); + msgData.putInt(KEY_IDENT, ident); mSearchUiThread.sendMessage(msg); } @@ -183,12 +201,15 @@ implements DialogInterface.OnCancelListener, DialogInterface.OnDismissListener { } /** - * Updates the search UI in response to a configuration change. + * Updates the currently resumed activity. * Can be called from any thread. */ - void onConfigurationChanged() { - if (DBG) debug("onConfigurationChanged()"); - mSearchUiThread.sendEmptyMessage(MSG_ON_CONFIGURATION_CHANGED); + public void activityResuming(int ident) { + if (DBG) debug("startSearch()"); + Message msg = Message.obtain(); + msg.what = MSG_ACTIVITY_RESUMING; + msg.arg1 = ident; + mSearchUiThread.sendMessage(msg); } // @@ -213,8 +234,8 @@ implements DialogInterface.OnCancelListener, DialogInterface.OnDismissListener { case MSG_STOP_SEARCH: performStopSearch(); break; - case MSG_ON_CONFIGURATION_CHANGED: - performOnConfigurationChanged(); + case MSG_ACTIVITY_RESUMING: + performActivityResuming(msg.arg1); break; } } @@ -228,12 +249,27 @@ implements DialogInterface.OnCancelListener, DialogInterface.OnDismissListener { Bundle appSearchData = msgData.getBundle(KEY_APP_SEARCH_DATA); boolean globalSearch = msg.arg2 != 0; ISearchManagerCallback searchManagerCallback = (ISearchManagerCallback) msg.obj; + int ident = msgData.getInt(KEY_IDENT); performStartSearch(initialQuery, selectInitialQuery, launchActivity, - appSearchData, globalSearch, searchManagerCallback); + appSearchData, globalSearch, searchManagerCallback, ident); } } + void updateDialogVisibility() { + if (mStartedIdent != 0) { + // mResumedIdent == 0 means we have just booted and the user + // hasn't yet gone anywhere. + if (mResumedIdent == 0 || mStartedIdent == mResumedIdent) { + if (DBG) Log.v(TAG, "******************* DIALOG: show"); + mSearchDialog.show(); + } else { + if (DBG) Log.v(TAG, "******************* DIALOG: hide"); + mSearchDialog.hide(); + } + } + } + /** * Actually launches the search UI. * This must be called on the search UI thread. @@ -243,7 +279,8 @@ implements DialogInterface.OnCancelListener, DialogInterface.OnDismissListener { ComponentName launchActivity, Bundle appSearchData, boolean globalSearch, - ISearchManagerCallback searchManagerCallback) { + ISearchManagerCallback searchManagerCallback, + int ident) { if (DBG) debug("performStartSearch()"); if (mDisabledOnBoot) { @@ -254,8 +291,11 @@ implements DialogInterface.OnCancelListener, DialogInterface.OnDismissListener { registerBroadcastReceiver(); mCallback = searchManagerCallback; + mStartedIdent = ident; + if (DBG) Log.v(TAG, "******************* DIALOG: start"); mSearchDialog.show(initialQuery, selectInitialQuery, launchActivity, appSearchData, globalSearch); + updateDialogVisibility(); } /** @@ -264,7 +304,20 @@ implements DialogInterface.OnCancelListener, DialogInterface.OnDismissListener { */ void performStopSearch() { if (DBG) debug("performStopSearch()"); + if (DBG) Log.v(TAG, "******************* DIALOG: cancel"); mSearchDialog.cancel(); + mStartedIdent = 0; + } + + /** + * Updates the resumed activity + * This must be called on the search UI thread. + */ + void performActivityResuming(int ident) { + if (DBG) debug("performResumingActivity(): mStartedIdent=" + + mStartedIdent + ", resuming: " + ident); + this.mResumedIdent = ident; + updateDialogVisibility(); } /** diff --git a/core/java/android/server/search/SearchManagerService.java b/core/java/android/server/search/SearchManagerService.java index f9c0f1a..7629912 100644 --- a/core/java/android/server/search/SearchManagerService.java +++ b/core/java/android/server/search/SearchManagerService.java @@ -16,6 +16,8 @@ package android.server.search; +import android.app.ActivityManagerNative; +import android.app.IActivityWatcher; import android.app.ISearchManager; import android.app.ISearchManagerCallback; import android.app.SearchManager; @@ -26,6 +28,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.os.Bundle; import android.os.Handler; +import android.os.RemoteException; import android.util.Log; import java.util.List; @@ -71,8 +74,11 @@ public class SearchManagerService extends ISearchManager.Stub { * Initializes the list of searchable activities and the search UI. */ void initialize() { - ensureSearchablesCreated(); - ensureSearchDialogCreated(); + try { + ActivityManagerNative.getDefault().registerActivityWatcher( + mActivityWatcher); + } catch (RemoteException e) { + } } private synchronized void ensureSearchablesCreated() { @@ -126,6 +132,14 @@ public class SearchManagerService extends ISearchManager.Stub { } }; + private IActivityWatcher.Stub mActivityWatcher = new IActivityWatcher.Stub() { + public void activityResuming(int activityId) throws RemoteException { + if (DBG) Log.i("foo", "********************** resuming: " + activityId); + if (mSearchDialog == null) return; + mSearchDialog.activityResuming(activityId); + } + }; + /** * Informs all listeners that the list of searchables has been updated. */ @@ -206,13 +220,15 @@ public class SearchManagerService extends ISearchManager.Stub { ComponentName launchActivity, Bundle appSearchData, boolean globalSearch, - ISearchManagerCallback searchManagerCallback) { + ISearchManagerCallback searchManagerCallback, + int ident) { getSearchDialog().startSearch(initialQuery, selectInitialQuery, launchActivity, appSearchData, globalSearch, - searchManagerCallback); + searchManagerCallback, + ident); } /** diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index 856ae42..25991f2 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -32,6 +32,7 @@ import android.app.ActivityThread; import android.app.AlertDialog; import android.app.ApplicationErrorReport; import android.app.Dialog; +import android.app.IActivityController; import android.app.IActivityWatcher; import android.app.IApplicationThread; import android.app.IInstrumentationWatcher; @@ -76,6 +77,7 @@ import android.os.Parcel; import android.os.ParcelFileDescriptor; import android.os.PowerManager; import android.os.Process; +import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; @@ -831,8 +833,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen String mOrigDebugApp = null; boolean mOrigWaitForDebugger = false; boolean mAlwaysFinishActivities = false; - IActivityWatcher mWatcher = null; + IActivityController mController = null; + final RemoteCallbackList<IActivityWatcher> mWatchers + = new RemoteCallbackList<IActivityWatcher>(); + /** * Callback of last caller to {@link #requestPss}. */ @@ -1629,7 +1634,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen /** * This is a simplified version of topRunningActivityLocked that provides a number of - * optional skip-over modes. It is intended for use with the ActivityWatcher hook only. + * optional skip-over modes. It is intended for use with the ActivityController hook only. * * @param token If non-null, any history records matching this token will be skipped. * @param taskId If non-zero, we'll attempt to skip over records with the same task ID. @@ -1734,10 +1739,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } ensurePackageDexOpt(r.intent.getComponent().getPackageName()); app.thread.scheduleLaunchActivity(new Intent(r.intent), r, + System.identityHashCode(r), r.info, r.icicle, results, newIntents, !andResume, isNextTransitionForward()); - // Update usage stats for launched activity - updateUsageStats(r, true); } catch (RemoteException e) { if (r.launchFailed) { // This is the second time we failed -- finish activity @@ -2190,6 +2194,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen mHandler.sendMessage(msg); } + reportResumedActivity(next); + next.thumbnail = null; setFocusedActivityLocked(next); next.resumeKeyDispatchingLocked(); @@ -2460,6 +2466,26 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } + private void reportResumedActivity(HistoryRecord r) { + //Log.i(TAG, "**** REPORT RESUME: " + r); + + final int identHash = System.identityHashCode(r); + updateUsageStats(r, true); + + int i = mWatchers.beginBroadcast(); + while (i > 0) { + i--; + IActivityWatcher w = mWatchers.getBroadcastItem(i); + if (w != null) { + try { + w.activityResuming(identHash); + } catch (RemoteException e) { + } + } + } + mWatchers.finishBroadcast(); + } + /** * Ensure that the top activity in the stack is resumed. * @@ -2648,10 +2674,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen EventLog.writeEvent(LOG_AM_RESUME_ACTIVITY, System.identityHashCode(next), next.task.taskId, next.shortComponentName); - updateUsageStats(next, true); next.app.thread.scheduleResumeActivity(next, isNextTransitionForward()); + pauseIfSleepingLocked(); } catch (Exception e) { @@ -3068,16 +3094,16 @@ public final class ActivityManagerService extends ActivityManagerNative implemen throw new SecurityException(msg); } - if (mWatcher != null) { + if (mController != null) { boolean abort = false; try { // The Intent we give to the watcher has the extra data // stripped off, since it can contain private information. Intent watchIntent = intent.cloneFilter(); - abort = !mWatcher.activityStarting(watchIntent, + abort = !mController.activityStarting(watchIntent, aInfo.applicationInfo.packageName); } catch (RemoteException e) { - mWatcher = null; + mController = null; } if (abort) { @@ -3974,16 +4000,16 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } synchronized(this) { - if (mWatcher != null) { + if (mController != null) { // Find the first activity that is not finishing. HistoryRecord next = topRunningActivityLocked(token, 0); if (next != null) { // ask watcher if this is allowed boolean resumeOK = true; try { - resumeOK = mWatcher.activityResuming(next.packageName); + resumeOK = mController.activityResuming(next.packageName); } catch (RemoteException e) { - mWatcher = null; + mController = null; } if (!resumeOK) { @@ -4475,9 +4501,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } - if (mWatcher != null) { + if (mController != null) { try { - int res = mWatcher.appNotResponding(app.processName, + int res = mController.appNotResponding(app.processName, app.pid, info.toString()); if (res != 0) { if (res < 0) { @@ -4493,7 +4519,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } } catch (RemoteException e) { - mWatcher = null; + mController = null; } } @@ -6618,7 +6644,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } /** - * TODO: Add mWatcher hook + * TODO: Add mController hook */ public void moveTaskToFront(int task) { enforceCallingPermission(android.Manifest.permission.REORDER_TASKS, @@ -6763,7 +6789,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // If we have a watcher, preflight the move before committing to it. First check // for *other* available tasks, but if none are available, then try again allowing the // current task to be selected. - if (mWatcher != null) { + if (mController != null) { HistoryRecord next = topRunningActivityLocked(null, task); if (next == null) { next = topRunningActivityLocked(null, 0); @@ -6772,9 +6798,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // ask watcher if this is allowed boolean moveOK = true; try { - moveOK = mWatcher.activityResuming(next.packageName); + moveOK = mController.activityResuming(next.packageName); } catch (RemoteException e) { - mWatcher = null; + mController = null; } if (!moveOK) { return false; @@ -7685,14 +7711,22 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } - public void setActivityWatcher(IActivityWatcher watcher) { + public void setActivityController(IActivityController controller) { enforceCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER, - "setActivityWatcher()"); + "setActivityController()"); synchronized (this) { - mWatcher = watcher; + mController = controller; } } + public void registerActivityWatcher(IActivityWatcher watcher) { + mWatchers.register(watcher); + } + + public void unregisterActivityWatcher(IActivityWatcher watcher) { + mWatchers.unregister(watcher); + } + public final void enterSafeMode() { synchronized(this) { // It only makes sense to do this before the system is ready @@ -8288,11 +8322,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen //Process.sendSignal(MY_PID, Process.SIGNAL_QUIT); } - if (mWatcher != null) { + if (mController != null) { try { String name = r != null ? r.processName : null; int pid = r != null ? r.pid : Binder.getCallingPid(); - if (!mWatcher.appCrashed(name, pid, + if (!mController.appCrashed(name, pid, shortMsg, longMsg, crashData)) { Log.w(TAG, "Force-killing crashed app " + name + " at watcher's request"); @@ -8300,7 +8334,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen return 0; } } catch (RemoteException e) { - mWatcher = null; + mController = null; } } @@ -8727,7 +8761,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen + " mDebugTransient=" + mDebugTransient + " mOrigWaitForDebugger=" + mOrigWaitForDebugger); pw.println(" mAlwaysFinishActivities=" + mAlwaysFinishActivities - + " mWatcher=" + mWatcher); + + " mController=" + mController); } } diff --git a/tests/permission/src/com/android/framework/permission/tests/ActivityManagerPermissionTests.java b/tests/permission/src/com/android/framework/permission/tests/ActivityManagerPermissionTests.java index 14d3d73..c782045 100644 --- a/tests/permission/src/com/android/framework/permission/tests/ActivityManagerPermissionTests.java +++ b/tests/permission/src/com/android/framework/permission/tests/ActivityManagerPermissionTests.java @@ -134,8 +134,8 @@ public class ActivityManagerPermissionTests extends TestCase { @SmallTest public void testSET_ACTIVITY_WATCHER() { try { - mAm.setActivityWatcher(null); - fail("IActivityManager.setActivityWatcher did not throw SecurityException as" + mAm.setActivityController(null); + fail("IActivityManager.setActivityController did not throw SecurityException as" + " expected"); } catch (SecurityException e) { // expected |