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

Change-Id: Ie71a8043eafe41f53a0b3dbb5170276d87acbc9b
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()