Add new am option to profile the launching of an activity.

Change-Id: Ie71a8043eafe41f53a0b3dbb5170276d87acbc9b
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 929867b..1271ddd 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -3353,7 +3353,8 @@
                             intent, intent.resolveTypeIfNeeded(
                                     getContentResolver()),
                             null, 0,
-                            mToken, mEmbeddedID, requestCode, true, false);
+                            mToken, mEmbeddedID, requestCode, true, false,
+                            null, null, false);
             } catch (RemoteException e) {
                 // Empty
             }
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index a73e10a..8901fc8 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -124,9 +124,13 @@
             int requestCode = data.readInt();
             boolean onlyIfNeeded = data.readInt() != 0;
             boolean debug = data.readInt() != 0;
+            String profileFile = data.readString();
+            ParcelFileDescriptor profileFd = data.readInt() != 0
+                    ? data.readFileDescriptor() : null;
+            boolean autoStopProfiler = data.readInt() != 0;
             int result = startActivity(app, intent, resolvedType,
                     grantedUriPermissions, grantedMode, resultTo, resultWho,
-                    requestCode, onlyIfNeeded, debug);
+                    requestCode, onlyIfNeeded, debug, profileFile, profileFd, autoStopProfiler);
             reply.writeNoException();
             reply.writeInt(result);
             return true;
@@ -146,9 +150,13 @@
             int requestCode = data.readInt();
             boolean onlyIfNeeded = data.readInt() != 0;
             boolean debug = data.readInt() != 0;
+            String profileFile = data.readString();
+            ParcelFileDescriptor profileFd = data.readInt() != 0
+                    ? data.readFileDescriptor() : null;
+            boolean autoStopProfiler = data.readInt() != 0;
             WaitResult result = startActivityAndWait(app, intent, resolvedType,
                     grantedUriPermissions, grantedMode, resultTo, resultWho,
-                    requestCode, onlyIfNeeded, debug);
+                    requestCode, onlyIfNeeded, debug, profileFile, profileFd, autoStopProfiler);
             reply.writeNoException();
             result.writeToParcel(reply, 0);
             return true;
@@ -349,8 +357,9 @@
             if (data.readInt() != 0) {
                 config = Configuration.CREATOR.createFromParcel(data);
             }
+            boolean stopProfiling = data.readInt() != 0;
             if (token != null) {
-                activityIdle(token, config);
+                activityIdle(token, config, stopProfiling);
             }
             reply.writeNoException();
             return true;
@@ -1572,7 +1581,8 @@
             String resolvedType, Uri[] grantedUriPermissions, int grantedMode,
             IBinder resultTo, String resultWho,
             int requestCode, boolean onlyIfNeeded,
-            boolean debug) throws RemoteException {
+            boolean debug, String profileFile, ParcelFileDescriptor profileFd,
+            boolean autoStopProfiler) throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
         data.writeInterfaceToken(IActivityManager.descriptor);
@@ -1586,6 +1596,14 @@
         data.writeInt(requestCode);
         data.writeInt(onlyIfNeeded ? 1 : 0);
         data.writeInt(debug ? 1 : 0);
+        data.writeString(profileFile);
+        if (profileFd != null) {
+            data.writeInt(1);
+            profileFd.writeToParcel(data, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
+        } else {
+            data.writeInt(0);
+        }
+        data.writeInt(autoStopProfiler ? 1 : 0);
         mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0);
         reply.readException();
         int result = reply.readInt();
@@ -1597,7 +1615,8 @@
             String resolvedType, Uri[] grantedUriPermissions, int grantedMode,
             IBinder resultTo, String resultWho,
             int requestCode, boolean onlyIfNeeded,
-            boolean debug) throws RemoteException {
+            boolean debug, String profileFile, ParcelFileDescriptor profileFd,
+            boolean autoStopProfiler) throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
         data.writeInterfaceToken(IActivityManager.descriptor);
@@ -1611,6 +1630,14 @@
         data.writeInt(requestCode);
         data.writeInt(onlyIfNeeded ? 1 : 0);
         data.writeInt(debug ? 1 : 0);
+        data.writeString(profileFile);
+        if (profileFd != null) {
+            data.writeInt(1);
+            profileFd.writeToParcel(data, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
+        } else {
+            data.writeInt(0);
+        }
+        data.writeInt(autoStopProfiler ? 1 : 0);
         mRemote.transact(START_ACTIVITY_AND_WAIT_TRANSACTION, data, reply, 0);
         reply.readException();
         WaitResult result = WaitResult.CREATOR.createFromParcel(reply);
@@ -1829,7 +1856,8 @@
         data.recycle();
         reply.recycle();
     }
-    public void activityIdle(IBinder token, Configuration config) throws RemoteException
+    public void activityIdle(IBinder token, Configuration config, boolean stopProfiling)
+            throws RemoteException
     {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
@@ -1841,6 +1869,7 @@
         } else {
             data.writeInt(0);
         }
+        data.writeInt(stopProfiling ? 1 : 0);
         mRemote.transact(ACTIVITY_IDLE_TRANSACTION, data, reply, IBinder.FLAG_ONEWAY);
         reply.readException();
         data.recycle();
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index d5f630a..e376220 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -225,6 +225,10 @@
         Configuration createdConfig;
         ActivityClientRecord nextIdle;
 
+        String profileFile;
+        ParcelFileDescriptor profileFd;
+        boolean autoStopProfiler;
+
         ActivityInfo activityInfo;
         CompatibilityInfo compatInfo;
         LoadedApk packageInfo;
@@ -361,6 +365,9 @@
         List<ProviderInfo> providers;
         ComponentName instrumentationName;
         String profileFile;
+        ParcelFileDescriptor profileFd;
+        boolean autoStopProfiler;
+        boolean profiling;
         Bundle instrumentationArgs;
         IInstrumentationWatcher instrumentationWatcher;
         int debugMode;
@@ -371,6 +378,57 @@
         public String toString() {
             return "AppBindData{appInfo=" + appInfo + "}";
         }
+        public void setProfiler(String file, ParcelFileDescriptor fd) {
+            if (profiling) {
+                if (fd != null) {
+                    try {
+                        fd.close();
+                    } catch (IOException e) {
+                    }
+                }
+                return;
+            }
+            if (profileFd != null) {
+                try {
+                    profileFd.close();
+                } catch (IOException e) {
+                }
+            }
+            profileFile = file;
+            profileFd = fd;
+        }
+        public void startProfiling() {
+            if (profileFd == null || profiling) {
+                return;
+            }
+            try {
+                Debug.startMethodTracing(profileFile, profileFd.getFileDescriptor(),
+                        8 * 1024 * 1024, 0);
+                profiling = true;
+            } catch (RuntimeException e) {
+                Slog.w(TAG, "Profiling failed on path " + profileFile);
+                try {
+                    profileFd.close();
+                    profileFd = null;
+                } catch (IOException e2) {
+                    Slog.w(TAG, "Failure closing profile fd", e2);
+                }
+            }
+        }
+        public void stopProfiling() {
+            if (profiling) {
+                profiling = false;
+                Debug.stopMethodTracing();
+                if (profileFd != null) {
+                    try {
+                        profileFd.close();
+                    } catch (IOException e) {
+                    }
+                }
+                profileFd = null;
+                profileFile = null;
+            }
+        }
     }
 
     static final class DumpComponentInfo {
@@ -463,7 +521,8 @@
         public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
                 ActivityInfo info, CompatibilityInfo compatInfo, Bundle state,
                 List<ResultInfo> pendingResults,
-                List<Intent> pendingNewIntents, boolean notResumed, boolean isForward) {
+                List<Intent> pendingNewIntents, boolean notResumed, boolean isForward,
+                String profileName, ParcelFileDescriptor profileFd, boolean autoStopProfiler) {
             ActivityClientRecord r = new ActivityClientRecord();
 
             r.token = token;
@@ -479,6 +538,10 @@
             r.startsNotResumed = notResumed;
             r.isForward = isForward;
 
+            r.profileFile = profileName;
+            r.profileFd = profileFd;
+            r.autoStopProfiler = autoStopProfiler;
+
             queueOrSendMessage(H.LAUNCH_ACTIVITY, r);
         }
 
@@ -579,6 +642,7 @@
         public final void bindApplication(String processName,
                 ApplicationInfo appInfo, List<ProviderInfo> providers,
                 ComponentName instrumentationName, String profileFile,
+                ParcelFileDescriptor profileFd, boolean autoStopProfiler,
                 Bundle instrumentationArgs, IInstrumentationWatcher instrumentationWatcher,
                 int debugMode, boolean isRestrictedBackupMode, Configuration config,
                 CompatibilityInfo compatInfo, Map<String, IBinder> services,
@@ -596,7 +660,8 @@
             data.appInfo = appInfo;
             data.providers = providers;
             data.instrumentationName = instrumentationName;
-            data.profileFile = profileFile;
+            data.setProfiler(profileFile, profileFd);
+            data.autoStopProfiler = false;
             data.instrumentationArgs = instrumentationArgs;
             data.instrumentationWatcher = instrumentationWatcher;
             data.debugMode = debugMode;
@@ -1225,6 +1290,10 @@
     private class Idler implements MessageQueue.IdleHandler {
         public final boolean queueIdle() {
             ActivityClientRecord a = mNewActivities;
+            boolean stopProfiling = false;
+            if (mBoundApplication.profileFd != null && mBoundApplication.autoStopProfiler) {
+                stopProfiling = true;
+            }
             if (a != null) {
                 mNewActivities = null;
                 IActivityManager am = ActivityManagerNative.getDefault();
@@ -1236,7 +1305,7 @@
                         (a.activity != null && a.activity.mFinished));
                     if (a.activity != null && !a.activity.mFinished) {
                         try {
-                            am.activityIdle(a.token, a.createdConfig);
+                            am.activityIdle(a.token, a.createdConfig, stopProfiling);
                             a.createdConfig = null;
                         } catch (RemoteException ex) {
                             // Ignore
@@ -1247,6 +1316,9 @@
                     prev.nextIdle = null;
                 } while (a != null);
             }
+            if (stopProfiling) {
+                mBoundApplication.stopProfiling();
+            }
             ensureJitEnabled();
             return false;
         }
@@ -1560,7 +1632,8 @@
     }
 
     public boolean isProfiling() {
-        return mBoundApplication != null && mBoundApplication.profileFile != null;
+        return mBoundApplication != null && mBoundApplication.profileFile != null
+                && mBoundApplication.profileFd == null;
     }
 
     public String getProfileFilePath() {
@@ -1870,6 +1943,13 @@
         // we are back active so skip it.
         unscheduleGcIdler();
 
+        Slog.i(TAG, "Launch: profileFd=" + r.profileFile + " stop=" + r.autoStopProfiler);
+        if (r.profileFd != null) {
+            mBoundApplication.setProfiler(r.profileFile, r.profileFd);
+            mBoundApplication.startProfiling();
+            mBoundApplication.autoStopProfiler = r.autoStopProfiler;
+        }
+
         if (localLOGV) Slog.v(
             TAG, "Handling launch of " + r);
         Activity a = performLaunchActivity(r, customIntent);
@@ -3489,8 +3569,9 @@
                         ViewDebug.startLooperProfiling(pcd.path, pcd.fd.getFileDescriptor());
                         break;
                     default:
-                        Debug.startMethodTracing(pcd.path, pcd.fd.getFileDescriptor(),
-                                8 * 1024 * 1024, 0);
+                        mBoundApplication.setProfiler(pcd.path, pcd.fd);
+                        mBoundApplication.autoStopProfiler = false;
+                        mBoundApplication.startProfiling();
                         break;
                 }
             } catch (RuntimeException e) {
@@ -3509,9 +3590,8 @@
                     ViewDebug.stopLooperProfiling();
                     break;
                 default:
-                    Debug.stopMethodTracing();
+                    mBoundApplication.stopProfiling();
                     break;
-                    
             }
         }
     }
@@ -3607,6 +3687,10 @@
         Process.setArgV0(data.processName);
         android.ddm.DdmHandleAppName.setAppName(data.processName);
 
+        if (data.profileFd != null) {
+            data.startProfiling();
+        }
+
         // If the app is Honeycomb MR1 or earlier, switch its AsyncTask
         // implementation to use the pool executor.  Normally, we use the
         // serialized executor as the default. This has to happen in the
@@ -3745,7 +3829,8 @@
             mInstrumentation.init(this, instrContext, appContext,
                     new ComponentName(ii.packageName, ii.name), data.instrumentationWatcher);
 
-            if (data.profileFile != null && !ii.handleProfiling) {
+            if (data.profileFile != null && !ii.handleProfiling
+                    && data.profileFd == null) {
                 data.handlingProfiling = true;
                 File file = new File(data.profileFile);
                 file.getParentFile().mkdirs();
@@ -3799,7 +3884,8 @@
 
     /*package*/ final void finishInstrumentation(int resultCode, Bundle results) {
         IActivityManager am = ActivityManagerNative.getDefault();
-        if (mBoundApplication.profileFile != null && mBoundApplication.handlingProfiling) {
+        if (mBoundApplication.profileFile != null && mBoundApplication.handlingProfiling
+                && mBoundApplication.profileFd == null) {
             Debug.stopMethodTracing();
         }
         //Slog.i(TAG, "am: " + ActivityManagerNative.getDefault()
diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java
index bea057e..0a6fdd4 100644
--- a/core/java/android/app/ApplicationThreadNative.java
+++ b/core/java/android/app/ApplicationThreadNative.java
@@ -138,8 +138,12 @@
             List<Intent> pi = data.createTypedArrayList(Intent.CREATOR);
             boolean notResumed = data.readInt() != 0;
             boolean isForward = data.readInt() != 0;
+            String profileName = data.readString();
+            ParcelFileDescriptor profileFd = data.readInt() != 0
+                    ? data.readFileDescriptor() : null;
+            boolean autoStopProfiler = data.readInt() != 0;
             scheduleLaunchActivity(intent, b, ident, info, compatInfo, state, ri, pi,
-                    notResumed, isForward);
+                    notResumed, isForward, profileName, profileFd, autoStopProfiler);
             return true;
         }
         
@@ -255,6 +259,9 @@
             ComponentName testName = (data.readInt() != 0)
                 ? new ComponentName(data) : null;
             String profileName = data.readString();
+            ParcelFileDescriptor profileFd = data.readInt() != 0
+                    ? data.readFileDescriptor() : null;
+            boolean autoStopProfiler = data.readInt() != 0;
             Bundle testArgs = data.readBundle();
             IBinder binder = data.readStrongBinder();
             IInstrumentationWatcher testWatcher = IInstrumentationWatcher.Stub.asInterface(binder);
@@ -265,7 +272,7 @@
             HashMap<String, IBinder> services = data.readHashMap(null);
             Bundle coreSettings = data.readBundle();
             bindApplication(packageName, info,
-                            providers, testName, profileName,
+                            providers, testName, profileName, profileFd, autoStopProfiler,
                             testArgs, testWatcher, testMode, restrictedBackupMode,
                             config, compatInfo, services, coreSettings);
             return true;
@@ -624,7 +631,8 @@
     public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
             ActivityInfo info, CompatibilityInfo compatInfo, Bundle state,
             List<ResultInfo> pendingResults,
-    		List<Intent> pendingNewIntents, boolean notResumed, boolean isForward)
+		List<Intent> pendingNewIntents, boolean notResumed, boolean isForward,
+		String profileName, ParcelFileDescriptor profileFd, boolean autoStopProfiler)
     		throws RemoteException {
         Parcel data = Parcel.obtain();
         data.writeInterfaceToken(IApplicationThread.descriptor);
@@ -638,6 +646,14 @@
         data.writeTypedList(pendingNewIntents);
         data.writeInt(notResumed ? 1 : 0);
         data.writeInt(isForward ? 1 : 0);
+        data.writeString(profileName);
+        if (profileFd != null) {
+            data.writeInt(1);
+            profileFd.writeToParcel(data, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
+        } else {
+            data.writeInt(0);
+        }
+        data.writeInt(autoStopProfiler ? 1 : 0);
         mRemote.transact(SCHEDULE_LAUNCH_ACTIVITY_TRANSACTION, data, null,
                 IBinder.FLAG_ONEWAY);
         data.recycle();
@@ -793,8 +809,9 @@
     }
 
     public final void bindApplication(String packageName, ApplicationInfo info,
-            List<ProviderInfo> providers, ComponentName testName,
-            String profileName, Bundle testArgs, IInstrumentationWatcher testWatcher, int debugMode,
+            List<ProviderInfo> providers, ComponentName testName, String profileName,
+            ParcelFileDescriptor profileFd, boolean autoStopProfiler, Bundle testArgs,
+            IInstrumentationWatcher testWatcher, int debugMode,
             boolean restrictedBackupMode, Configuration config, CompatibilityInfo compatInfo,
             Map<String, IBinder> services, Bundle coreSettings) throws RemoteException {
         Parcel data = Parcel.obtain();
@@ -809,6 +826,13 @@
             testName.writeToParcel(data, 0);
         }
         data.writeString(profileName);
+        if (profileFd != null) {
+            data.writeInt(1);
+            profileFd.writeToParcel(data, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
+        } else {
+            data.writeInt(0);
+        }
+        data.writeInt(autoStopProfiler ? 1 : 0);
         data.writeBundle(testArgs);
         data.writeStrongInterface(testWatcher);
         data.writeInt(debugMode);
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index b1b0583..49f8449 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -84,11 +84,13 @@
     public int startActivity(IApplicationThread caller,
             Intent intent, String resolvedType, Uri[] grantedUriPermissions,
             int grantedMode, IBinder resultTo, String resultWho, int requestCode,
-            boolean onlyIfNeeded, boolean debug) throws RemoteException;
+            boolean onlyIfNeeded, boolean debug, String profileFile,
+            ParcelFileDescriptor profileFd, boolean autoStopProfiler) 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;
+            boolean onlyIfNeeded, boolean debug, String profileFile,
+            ParcelFileDescriptor profileFd, boolean autoStopProfiler) throws RemoteException;
     public int startActivityWithConfig(IApplicationThread caller,
             Intent intent, String resolvedType, Uri[] grantedUriPermissions,
             int grantedMode, IBinder resultTo, String resultWho, int requestCode,
@@ -118,7 +120,8 @@
     public void finishReceiver(IBinder who, int resultCode, String resultData, Bundle map, boolean abortBroadcast) throws RemoteException;
     public void attachApplication(IApplicationThread app) throws RemoteException;
     /* oneway */
-    public void activityIdle(IBinder token, Configuration config) throws RemoteException;
+    public void activityIdle(IBinder token, Configuration config,
+            boolean stopProfiling) throws RemoteException;
     public void activityPaused(IBinder token) throws RemoteException;
     /* oneway */
     public void activityStopped(IBinder token, Bundle state,
diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java
index 3a8eb28..9ae5ab1 100644
--- a/core/java/android/app/IApplicationThread.java
+++ b/core/java/android/app/IApplicationThread.java
@@ -55,7 +55,8 @@
     void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
             ActivityInfo info, CompatibilityInfo compatInfo, Bundle state,
             List<ResultInfo> pendingResults,
-    		List<Intent> pendingNewIntents, boolean notResumed, boolean isForward)
+		List<Intent> pendingNewIntents, boolean notResumed, boolean isForward,
+		String profileName, ParcelFileDescriptor profileFd, boolean autoStopProfiler)
     		throws RemoteException;
     void scheduleRelaunchActivity(IBinder token, List<ResultInfo> pendingResults,
             List<Intent> pendingNewIntents, int configChanges,
@@ -86,7 +87,8 @@
     static final int DEBUG_ON = 1;
     static final int DEBUG_WAIT = 2;
     void bindApplication(String packageName, ApplicationInfo info, List<ProviderInfo> providers,
-            ComponentName testName, String profileName, Bundle testArguments, 
+            ComponentName testName, String profileName, ParcelFileDescriptor profileFd,
+            boolean autoStopProfiler, Bundle testArguments,
             IInstrumentationWatcher testWatcher, int debugMode, boolean restrictedBackupMode,
             Configuration config, CompatibilityInfo compatInfo, Map<String, IBinder> services,
             Bundle coreSettings) throws RemoteException;
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index f99b420..f3bc495 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -1379,7 +1379,7 @@
                 .startActivity(whoThread, intent,
                         intent.resolveTypeIfNeeded(who.getContentResolver()),
                         null, 0, token, target != null ? target.mEmbeddedID : null,
-                        requestCode, false, false);
+                        requestCode, false, false, null, null, false);
             checkStartActivityResult(result, intent);
         } catch (RemoteException e) {
         }
@@ -1475,7 +1475,7 @@
                 .startActivity(whoThread, intent,
                         intent.resolveTypeIfNeeded(who.getContentResolver()),
                         null, 0, token, target != null ? target.mWho : null,
-                        requestCode, false, false);
+                        requestCode, false, false, null, null, false);
             checkStartActivityResult(result, intent);
         } catch (RemoteException e) {
         }
diff --git a/core/java/android/os/ParcelFileDescriptor.java b/core/java/android/os/ParcelFileDescriptor.java
index 3ea3f56..ac15d9c 100644
--- a/core/java/android/os/ParcelFileDescriptor.java
+++ b/core/java/android/os/ParcelFileDescriptor.java
@@ -129,6 +129,16 @@
     }
 
     /**
+     * Create a new ParcelFileDescriptor that is a dup of the existing
+     * FileDescriptor.  This obeys standard POSIX semantics, where the
+     * new file descriptor shared state such as file position with the
+     * original file descriptor.
+     */
+    public ParcelFileDescriptor dup() throws IOException {
+        return dup(getFileDescriptor());
+    }
+
+    /**
      * Create a new ParcelFileDescriptor from a raw native fd.  The new
      * ParcelFileDescriptor holds a dup of the original fd passed in here,
      * so you must still close that fd as well as the new ParcelFileDescriptor.