diff options
| author | Dianne Hackborn <hackbod@google.com> | 2010-02-25 18:48:12 -0800 |
|---|---|---|
| committer | Dianne Hackborn <hackbod@google.com> | 2010-02-26 16:34:33 -0800 |
| commit | 8f7f35e0ccd91c964b9d3eeef81ff829622dfa74 (patch) | |
| tree | f00fafb472259f5b1cfa416b54cf62a2c3bef735 | |
| parent | 59dc9ecec6e4f53a1a2934b08c7f779d2b0a0d9a (diff) | |
| download | frameworks_base-8f7f35e0ccd91c964b9d3eeef81ff829622dfa74.zip frameworks_base-8f7f35e0ccd91c964b9d3eeef81ff829622dfa74.tar.gz frameworks_base-8f7f35e0ccd91c964b9d3eeef81ff829622dfa74.tar.bz2 | |
Add new -W option to Am to wait for the start to complete.
| -rw-r--r-- | cmds/am/src/com/android/commands/am/Am.java | 64 | ||||
| -rw-r--r-- | core/java/android/app/ActivityManagerNative.java | 47 | ||||
| -rw-r--r-- | core/java/android/app/IActivityManager.java | 48 | ||||
| -rw-r--r-- | services/java/com/android/server/am/ActivityManagerService.java | 106 | ||||
| -rw-r--r-- | services/java/com/android/server/am/HistoryRecord.java | 2 |
5 files changed, 250 insertions, 17 deletions
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 android.view.IWindowManager; 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 @@ public class Am { 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 @@ public class Am { boolean hasIntentInfo = false; mDebugOption = false; + mWaitOption = false; Uri data = null; String type = null; @@ -153,6 +156,8 @@ public class Am { 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 @@ public class Am { 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 @@ public class Am { 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..6849fd7 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -146,6 +146,28 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM 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 @@ class ActivityManagerProxy implements IActivityManager 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 @@ public interface IActivityManager extends IInterface { 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 @@ public interface IActivityManager extends IInterface { } }; + /** 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 @@ public interface IActivityManager extends IInterface { 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.AlertDialog; 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 @@ public final class ActivityManagerService extends ActivityManagerNative implemen = 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 @@ public final class ActivityManagerService extends ActivityManagerNative implemen 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 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } - synchronized(this) { + synchronized (this) { int callingPid; int callingUid; if (caller == null) { @@ -3618,11 +3658,62 @@ public final class ActivityManagerService extends ActivityManagerNative implemen 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 @@ public final class ActivityManagerService extends ActivityManagerNative implemen 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 @@ public final class ActivityManagerService extends ActivityManagerNative implemen 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 @@ class HistoryRecord extends IApplicationToken.Stub { 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) { |
