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) {