Add reporting of activity movement for search manager.

This adds a new API with the activity manager to find out about movement between
activities.  For my sanity, the old IActivityWatcher is now renamed to
IActivityController, and the new activity movement interface is named
IActivityWatcher.

This changes the search manager itself to use the new API to manage its state.
Note that there are still problems when going back to the search dialog after
it was hidden -- the suggestions window no longer appears until you explicitly
dismiss and re-show it.
diff --git a/Android.mk b/Android.mk
index f32129e..038af0d 100644
--- a/Android.mk
+++ b/Android.mk
@@ -67,10 +67,11 @@
 	core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl \
   core/java/android/accessibilityservice/IEventListener.aidl \
 	core/java/android/accounts/IAccountsService.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 \
@@ -88,12 +89,12 @@
 	core/java/android/bluetooth/IBluetoothDevice.aidl \
 	core/java/android/bluetooth/IBluetoothDeviceCallback.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 @@
     // 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 @@
     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 void startSearch(String initialQuery, boolean selectInitialQuery, 
             Bundle appSearchData, boolean globalSearch) {
+        ensureSearchManager();
         mSearchManager.startSearch(initialQuery, selectInitialQuery, getComponentName(),
                         appSearchData, globalSearch); 
     }
@@ -3241,6 +3240,24 @@
         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 @@
         if (WINDOW_SERVICE.equals(name)) {
             return mWindowManager;
         } else if (SEARCH_SERVICE.equals(name)) {
+            ensureSearchManager();
             return mSearchManager;
         }
         return super.getSystemService(name);
@@ -3450,14 +3468,17 @@
             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 @@
         mMainThread = aThread;
         mInstrumentation = instr;
         mToken = token;
+        mIdent = ident;
         mApplication = application;
         mIntent = intent;
         mComponent = intent.getComponent();
@@ -3554,9 +3576,6 @@
 
     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 @@
             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 @@
             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 @@
         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 @@
         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 @@
 
     private static final class ActivityRecord {
         IBinder token;
+        int ident;
         Intent intent;
         Bundle state;
         Activity activity;
@@ -1299,12 +1300,13 @@
 
         // 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 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 @@
                 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 @@
             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 @@
         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 @@
         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 @@
         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 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 @@
     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 @@
     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 @@
     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 @@
             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 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 @@
 
     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 @@
                 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 @@
                             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 f781e0d0..756f35c 100644
--- a/core/java/android/content/SyncStorageEngine.java
+++ b/core/java/android/content/SyncStorageEngine.java
@@ -338,6 +338,7 @@
                 }
                 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 23c0a7bf..584224f 100644
--- a/core/java/android/os/RemoteCallbackList.java
+++ b/core/java/android/os/RemoteCallbackList.java
@@ -50,6 +50,7 @@
     /*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 {
@@ -195,15 +196,16 @@
      * 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) {
@@ -222,7 +224,12 @@
      */
     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;
             }
@@ -281,12 +288,19 @@
      * @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 @@
     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 @@
     // 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 @@
     }
 
     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 @@
             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 @@
             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 @@
         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 @@
     }
 
     /**
-     * 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 @@
                 case MSG_STOP_SEARCH:
                     performStopSearch();
                     break;
-                case MSG_ON_CONFIGURATION_CHANGED:
-                    performOnConfigurationChanged();
+                case MSG_ACTIVITY_RESUMING:
+                    performActivityResuming(msg.arg1);
                     break;
             }
         }
@@ -228,12 +249,27 @@
             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 @@
             ComponentName launchActivity,
             Bundle appSearchData,
             boolean globalSearch,
-            ISearchManagerCallback searchManagerCallback) {
+            ISearchManagerCallback searchManagerCallback,
+            int ident) {
         if (DBG) debug("performStartSearch()");
 
         if (mDisabledOnBoot) {
@@ -254,8 +291,11 @@
 
         registerBroadcastReceiver();
         mCallback = searchManagerCallback;
+        mStartedIdent = ident;
+        if (DBG) Log.v(TAG, "******************* DIALOG: start");
         mSearchDialog.show(initialQuery, selectInitialQuery, launchActivity, appSearchData,
                 globalSearch);
+        updateDialogVisibility();
     }
 
     /**
@@ -264,7 +304,20 @@
      */
     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.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 @@
      * 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 @@
         }
     };
 
+    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 @@
             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 134ab80..442e9ce 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.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.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;
@@ -825,8 +827,11 @@
     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}.
      */
@@ -1623,7 +1628,7 @@
 
     /**
      * 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.
@@ -1728,10 +1733,9 @@
             }
             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
@@ -2184,6 +2188,8 @@
             mHandler.sendMessage(msg);
         }
 
+        reportResumedActivity(next);
+        
         next.thumbnail = null;
         setFocusedActivityLocked(next);
         next.resumeKeyDispatchingLocked();
@@ -2454,6 +2460,26 @@
         }
     }
     
+    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.
      *
@@ -2642,10 +2668,10 @@
                 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) {
@@ -3062,16 +3088,16 @@
             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) {
@@ -3968,16 +3994,16 @@
         }
 
         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) {
@@ -4469,9 +4495,9 @@
             }
         }
 
-        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) {
@@ -4487,7 +4513,7 @@
                     }
                 }
             } catch (RemoteException e) {
-                mWatcher = null;
+                mController = null;
             }
         }
 
@@ -6612,7 +6638,7 @@
     }
     
     /**
-     * TODO: Add mWatcher hook
+     * TODO: Add mController hook
      */
     public void moveTaskToFront(int task) {
         enforceCallingPermission(android.Manifest.permission.REORDER_TASKS,
@@ -6757,7 +6783,7 @@
         // 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);
@@ -6766,9 +6792,9 @@
                 // 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;
@@ -7679,14 +7705,22 @@
         }
     }
 
-    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
@@ -8247,11 +8281,11 @@
                 //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");
@@ -8259,7 +8293,7 @@
                         return 0;
                     }
                 } catch (RemoteException e) {
-                    mWatcher = null;
+                    mController = null;
                 }
             }
 
@@ -8685,7 +8719,7 @@
                     + " 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 @@
     @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