Add new -W option to Am to wait for the start to complete.
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index b6c9de4..5d6970a 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -35,6 +35,7 @@
 
 import java.io.File;
 import java.io.FileNotFoundException;
+import java.io.PrintStream;
 import java.net.URISyntaxException;
 import java.util.Iterator;
 import java.util.Set;
@@ -47,6 +48,7 @@
     private String mCurArgData;
 
     private boolean mDebugOption = false;
+    private boolean mWaitOption = false;
 
     // These are magic strings understood by the Eclipse plugin.
     private static final String FATAL_ERROR_CODE = "Error type 1";
@@ -106,6 +108,7 @@
         boolean hasIntentInfo = false;
 
         mDebugOption = false;
+        mWaitOption = false;
         Uri data = null;
         String type = null;
 
@@ -153,6 +156,8 @@
                 intent.setFlags(Integer.decode(str).intValue());
             } else if (opt.equals("-D")) {
                 mDebugOption = true;
+            } else if (opt.equals("-W")) {
+                mWaitOption = true;
             } else {
                 System.err.println("Error: Unknown option: " + opt);
                 showUsage();
@@ -199,58 +204,90 @@
         System.out.println("Starting: " + intent);
         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
         // XXX should do something to determine the MIME type.
-        int res = mAm.startActivity(null, intent, intent.getType(),
-                null, 0, null, null, 0, false, mDebugOption);
+        IActivityManager.WaitResult result = null;
+        int res;
+        if (mWaitOption) {
+            result = mAm.startActivityAndWait(null, intent, intent.getType(),
+                        null, 0, null, null, 0, false, mDebugOption);
+            res = result.result;
+        } else {
+            res = mAm.startActivity(null, intent, intent.getType(),
+                    null, 0, null, null, 0, false, mDebugOption);
+        }
+        PrintStream out = mWaitOption ? System.out : System.err;
+        boolean launched = false;
         switch (res) {
             case IActivityManager.START_SUCCESS:
+                launched = true;
                 break;
             case IActivityManager.START_SWITCHES_CANCELED:
-                System.err.println(
+                launched = true;
+                out.println(
                         "Warning: Activity not started because the "
                         + " current activity is being kept for the user.");
                 break;
             case IActivityManager.START_DELIVERED_TO_TOP:
-                System.err.println(
+                launched = true;
+                out.println(
                         "Warning: Activity not started, intent has "
                         + "been delivered to currently running "
                         + "top-most instance.");
                 break;
             case IActivityManager.START_RETURN_INTENT_TO_CALLER:
-                System.err.println(
+                launched = true;
+                out.println(
                         "Warning: Activity not started because intent "
                         + "should be handled by the caller");
                 break;
             case IActivityManager.START_TASK_TO_FRONT:
-                System.err.println(
+                launched = true;
+                out.println(
                         "Warning: Activity not started, its current "
                         + "task has been brought to the front");
                 break;
             case IActivityManager.START_INTENT_NOT_RESOLVED:
-                System.err.println(
+                out.println(
                         "Error: Activity not started, unable to "
                         + "resolve " + intent.toString());
                 break;
             case IActivityManager.START_CLASS_NOT_FOUND:
-                System.err.println(NO_CLASS_ERROR_CODE);
-                System.err.println("Error: Activity class " +
+                out.println(NO_CLASS_ERROR_CODE);
+                out.println("Error: Activity class " +
                         intent.getComponent().toShortString()
                         + " does not exist.");
                 break;
             case IActivityManager.START_FORWARD_AND_REQUEST_CONFLICT:
-                System.err.println(
+                out.println(
                         "Error: Activity not started, you requested to "
                         + "both forward and receive its result");
                 break;
             case IActivityManager.START_PERMISSION_DENIED:
-                System.err.println(
+                out.println(
                         "Error: Activity not started, you do not "
                         + "have permission to access it.");
                 break;
             default:
-                System.err.println(
+                out.println(
                         "Error: Activity not started, unknown error code " + res);
                 break;
         }
+        if (mWaitOption && launched) {
+            if (result == null) {
+                result = new IActivityManager.WaitResult();
+                result.who = intent.getComponent();
+            }
+            System.out.println("Status: " + (result.timeout ? "timeout" : "ok"));
+            if (result.who != null) {
+                System.out.println("Activity: " + result.who.flattenToShortString());
+            }
+            if (result.thisTime >= 0) {
+                System.out.println("ThisTime: " + result.thisTime);
+            }
+            if (result.totalTime >= 0) {
+                System.out.println("TotalTime: " + result.totalTime);
+            }
+            System.out.println("Complete");
+        }
     }
 
     private void sendBroadcast() throws Exception {
@@ -504,8 +541,9 @@
         System.err.println(
                 "usage: am [subcommand] [options]\n" +
                 "\n" +
-                "    start an Activity: am start [-D] <INTENT>\n" +
+                "    start an Activity: am start [-D] [-W] <INTENT>\n" +
                 "        -D: enable debugging\n" +
+                "        -W: wait for launch to complete\n" +
                 "\n" +
                 "    start a Service: am startservice <INTENT>\n" +
                 "\n" +
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 2e39c10..6849fd76 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -146,6 +146,28 @@
             return true;
         }
 
+        case START_ACTIVITY_AND_WAIT_TRANSACTION:
+        {
+            data.enforceInterface(IActivityManager.descriptor);
+            IBinder b = data.readStrongBinder();
+            IApplicationThread app = ApplicationThreadNative.asInterface(b);
+            Intent intent = Intent.CREATOR.createFromParcel(data);
+            String resolvedType = data.readString();
+            Uri[] grantedUriPermissions = data.createTypedArray(Uri.CREATOR);
+            int grantedMode = data.readInt();
+            IBinder resultTo = data.readStrongBinder();
+            String resultWho = data.readString();    
+            int requestCode = data.readInt();
+            boolean onlyIfNeeded = data.readInt() != 0;
+            boolean debug = data.readInt() != 0;
+            WaitResult result = startActivityAndWait(app, intent, resolvedType,
+                    grantedUriPermissions, grantedMode, resultTo, resultWho,
+                    requestCode, onlyIfNeeded, debug);
+            reply.writeNoException();
+            result.writeToParcel(reply, 0);
+            return true;
+        }
+
         case START_ACTIVITY_INTENT_SENDER_TRANSACTION:
         {
             data.enforceInterface(IActivityManager.descriptor);
@@ -1238,6 +1260,31 @@
         data.recycle();
         return result;
     }
+    public WaitResult startActivityAndWait(IApplicationThread caller, Intent intent,
+            String resolvedType, Uri[] grantedUriPermissions, int grantedMode,
+            IBinder resultTo, String resultWho,
+            int requestCode, boolean onlyIfNeeded,
+            boolean debug) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeStrongBinder(caller != null ? caller.asBinder() : null);
+        intent.writeToParcel(data, 0);
+        data.writeString(resolvedType);
+        data.writeTypedArray(grantedUriPermissions, 0);
+        data.writeInt(grantedMode);
+        data.writeStrongBinder(resultTo);
+        data.writeString(resultWho);
+        data.writeInt(requestCode);
+        data.writeInt(onlyIfNeeded ? 1 : 0);
+        data.writeInt(debug ? 1 : 0);
+        mRemote.transact(START_ACTIVITY_AND_WAIT_TRANSACTION, data, reply, 0);
+        reply.readException();
+        WaitResult result = WaitResult.CREATOR.createFromParcel(reply);
+        reply.recycle();
+        data.recycle();
+        return result;
+    }
     public int startActivityIntentSender(IApplicationThread caller,
             IntentSender intent, Intent fillInIntent, String resolvedType,
             IBinder resultTo, String resultWho, int requestCode,
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 86f28bf..3913ed5 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -84,6 +84,10 @@
             Intent intent, String resolvedType, Uri[] grantedUriPermissions,
             int grantedMode, IBinder resultTo, String resultWho, int requestCode,
             boolean onlyIfNeeded, boolean debug) throws RemoteException;
+    public WaitResult startActivityAndWait(IApplicationThread caller,
+            Intent intent, String resolvedType, Uri[] grantedUriPermissions,
+            int grantedMode, IBinder resultTo, String resultWho, int requestCode,
+            boolean onlyIfNeeded, boolean debug) throws RemoteException;
     public int startActivityIntentSender(IApplicationThread caller,
             IntentSender intent, Intent fillInIntent, String resolvedType,
             IBinder resultTo, String resultWho, int requestCode,
@@ -348,6 +352,49 @@
         }
     };
 
+    /** Information returned after waiting for an activity start. */
+    public static class WaitResult implements Parcelable {
+        public int result;
+        public boolean timeout;
+        public ComponentName who;
+        public long thisTime;
+        public long totalTime;
+
+        public WaitResult() {
+        }
+
+        public int describeContents() {
+            return 0;
+        }
+
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeInt(result);
+            dest.writeInt(timeout ? 1 : 0);
+            ComponentName.writeToParcel(who, dest);
+            dest.writeLong(thisTime);
+            dest.writeLong(totalTime);
+        }
+
+        public static final Parcelable.Creator<WaitResult> CREATOR
+                = new Parcelable.Creator<WaitResult>() {
+            public WaitResult createFromParcel(Parcel source) {
+                return new WaitResult(source);
+            }
+
+            public WaitResult[] newArray(int size) {
+                return new WaitResult[size];
+            }
+        };
+
+        private WaitResult(Parcel source) {
+            result = source.readInt();
+            timeout = source.readInt() != 0;
+            who = ComponentName.readFromParcel(source);
+            thisTime = source.readLong();
+            totalTime = source.readLong();
+        }
+    };
+
     String descriptor = "android.app.IActivityManager";
 
     // Please keep these transaction codes the same -- they are also
@@ -453,4 +500,5 @@
     int HANDLE_APPLICATION_WTF_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+101;
     int KILL_BACKGROUND_PROCESSES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+102;
     int IS_USER_A_MONKEY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+103;
+    int START_ACTIVITY_AND_WAIT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+104;
 }
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 7b64704..a404ec5 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -35,6 +35,7 @@
 import android.app.ApplicationErrorReport;
 import android.app.Dialog;
 import android.app.IActivityController;
+import android.app.IActivityManager;
 import android.app.IActivityWatcher;
 import android.app.IApplicationThread;
 import android.app.IInstrumentationWatcher;
@@ -388,6 +389,18 @@
             = new ArrayList<PendingActivityLaunch>();
     
     /**
+     * List of people waiting to find out about the next launched activity.
+     */
+    final ArrayList<IActivityManager.WaitResult> mWaitingActivityLaunched
+            = new ArrayList<IActivityManager.WaitResult>();
+    
+    /**
+     * List of people waiting to find out about the next visible activity.
+     */
+    final ArrayList<IActivityManager.WaitResult> mWaitingActivityVisible
+            = new ArrayList<IActivityManager.WaitResult>();
+    
+    /**
      * List of all active broadcasts that are to be executed immediately
      * (without waiting for another broadcast to finish).  Currently this only
      * contains broadcasts to registered receivers, to avoid spinning up
@@ -3559,11 +3572,38 @@
         return START_SUCCESS;
     }
 
-    public final int startActivity(IApplicationThread caller,
+    void reportActivityLaunchedLocked(boolean timeout, HistoryRecord r,
+            long thisTime, long totalTime) {
+        for (int i=mWaitingActivityLaunched.size()-1; i>=0; i--) {
+            WaitResult w = mWaitingActivityLaunched.get(i);
+            w.timeout = timeout;
+            if (r != null) {
+                w.who = new ComponentName(r.info.packageName, r.info.name);
+            }
+            w.thisTime = thisTime;
+            w.totalTime = totalTime;
+        }
+        notify();
+    }
+    
+    void reportActivityVisibleLocked(HistoryRecord r) {
+        for (int i=mWaitingActivityVisible.size()-1; i>=0; i--) {
+            WaitResult w = mWaitingActivityVisible.get(i);
+            w.timeout = false;
+            if (r != null) {
+                w.who = new ComponentName(r.info.packageName, r.info.name);
+            }
+            w.totalTime = SystemClock.uptimeMillis() - w.thisTime;
+            w.thisTime = w.totalTime;
+        }
+        notify();
+    }
+    
+    private final int startActivityMayWait(IApplicationThread caller,
             Intent intent, String resolvedType, Uri[] grantedUriPermissions,
             int grantedMode, IBinder resultTo,
             String resultWho, int requestCode, boolean onlyIfNeeded,
-            boolean debug) {
+            boolean debug, WaitResult outResult) {
         // Refuse possible leaked file descriptors
         if (intent != null && intent.hasFileDescriptors()) {
             throw new IllegalArgumentException("File descriptors passed in Intent");
@@ -3603,7 +3643,7 @@
             }
         }
 
-        synchronized(this) {
+        synchronized (this) {
             int callingPid;
             int callingUid;
             if (caller == null) {
@@ -3618,11 +3658,62 @@
                     resultTo, resultWho, requestCode, callingPid, callingUid,
                     onlyIfNeeded, componentSpecified);
             Binder.restoreCallingIdentity(origId);
+            
+            if (outResult != null) {
+                outResult.result = res;
+                if (res == IActivityManager.START_SUCCESS) {
+                    mWaitingActivityLaunched.add(outResult);
+                    do {
+                        try {
+                            wait();
+                        } catch (InterruptedException e) {
+                        }
+                    } while (!outResult.timeout && outResult.who == null);
+                } else if (res == IActivityManager.START_TASK_TO_FRONT) {
+                    HistoryRecord r = this.topRunningActivityLocked(null);
+                    if (r.nowVisible) {
+                        outResult.timeout = false;
+                        outResult.who = new ComponentName(r.info.packageName, r.info.name);
+                        outResult.totalTime = 0;
+                        outResult.thisTime = 0;
+                    } else {
+                        outResult.thisTime = SystemClock.uptimeMillis();
+                        mWaitingActivityVisible.add(outResult);
+                        do {
+                            try {
+                                wait();
+                            } catch (InterruptedException e) {
+                            }
+                        } while (!outResult.timeout && outResult.who == null);
+                    }
+                }
+            }
+            
             return res;
         }
     }
 
-    public int startActivityIntentSender(IApplicationThread caller,
+    public final int startActivity(IApplicationThread caller,
+            Intent intent, String resolvedType, Uri[] grantedUriPermissions,
+            int grantedMode, IBinder resultTo,
+            String resultWho, int requestCode, boolean onlyIfNeeded,
+            boolean debug) {
+        return startActivityMayWait(caller, intent, resolvedType, grantedUriPermissions,
+                grantedMode, resultTo, resultWho, requestCode, onlyIfNeeded, debug, null);
+    }
+
+    public final WaitResult startActivityAndWait(IApplicationThread caller,
+            Intent intent, String resolvedType, Uri[] grantedUriPermissions,
+            int grantedMode, IBinder resultTo,
+            String resultWho, int requestCode, boolean onlyIfNeeded,
+            boolean debug) {
+        WaitResult res = new WaitResult();
+        startActivityMayWait(caller, intent, resolvedType, grantedUriPermissions,
+                grantedMode, resultTo, resultWho, requestCode, onlyIfNeeded, debug, res);
+        return res;
+    }
+    
+     public int startActivityIntentSender(IApplicationThread caller,
             IntentSender intent, Intent fillInIntent, String resolvedType,
             IBinder resultTo, String resultWho, int requestCode,
             int flagsMask, int flagsValues) {
@@ -5505,6 +5596,10 @@
             if (index >= 0) {
                 HistoryRecord r = (HistoryRecord)mHistory.get(index);
 
+                if (fromTimeout) {
+                    reportActivityLaunchedLocked(fromTimeout, r, -1, -1);
+                }
+                
                 // This is a hack to semi-deal with a race condition
                 // in the client where it can be constructed with a
                 // newer configuration from when we asked it to launch.
@@ -5539,6 +5634,9 @@
                     mBooted = true;
                     enableScreen = true;
                 }
+                
+            } else if (fromTimeout) {
+                reportActivityLaunchedLocked(fromTimeout, null, -1, -1);
             }
 
             // Atomically retrieve all of the other things to do.
diff --git a/services/java/com/android/server/am/HistoryRecord.java b/services/java/com/android/server/am/HistoryRecord.java
index 0b34f7c..7d7247c 100644
--- a/services/java/com/android/server/am/HistoryRecord.java
+++ b/services/java/com/android/server/am/HistoryRecord.java
@@ -387,12 +387,14 @@
                     sb.append(" ms)");
                     Log.i(ActivityManagerService.TAG, sb.toString());
                 }
+                service.reportActivityLaunchedLocked(false, this, thisTime, totalTime);
                 if (totalTime > 0) {
                     service.mUsageStatsService.noteLaunchTime(realActivity, (int)totalTime);
                 }
                 startTime = 0;
                 service.mInitialStartTime = 0;
             }
+            service.reportActivityVisibleLocked(this);
             if (ActivityManagerService.DEBUG_SWITCH) Log.v(
                     ActivityManagerService.TAG, "windowsVisible(): " + this);
             if (!nowVisible) {