Add looper profiling to adb shell am

To profile the looper, run the following command:

adb shell am profile looper start <process> <file>
adb shell am profile looper stop <process>

Change-Id: I781f156e473d7bdbb6d13aaffeeaae88bc01a69f
diff --git a/api/current.txt b/api/current.txt
index d821c8e..520dfeb 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -22715,10 +22715,8 @@
     ctor public ViewDebug();
     method public static void dumpCapturedView(java.lang.String, java.lang.Object);
     method public static void startHierarchyTracing(java.lang.String, android.view.View);
-    method public static void startLooperProfiling(java.io.File);
     method public static void startRecyclerTracing(java.lang.String, android.view.View);
     method public static void stopHierarchyTracing();
-    method public static void stopLooperProfiling();
     method public static void stopRecyclerTracing();
     method public static void trace(android.view.View, android.view.ViewDebug.RecyclerTraceType, int...);
     method public static void trace(android.view.View, android.view.ViewDebug.HierarchyTraceType);
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index 3fb1736..6dfa12b 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -468,10 +468,16 @@
         String profileFile = null;
         boolean start = false;
         boolean wall = false;
+        int profileType = 0;
         
         String process = null;
         
         String cmd = nextArgRequired();
+        if ("looper".equals(cmd)) {
+            cmd = nextArgRequired();
+            profileType = 1;
+        }
+
         if ("start".equals(cmd)) {
             start = true;
             wall = "--wall".equals(nextOption());
@@ -516,7 +522,7 @@
             } else if (start) {
                 //removeWallOption();
             }
-            if (!mAm.profileControl(process, start, profileFile, fd)) {
+            if (!mAm.profileControl(process, start, profileFile, fd, profileType)) {
                 wall = false;
                 throw new AndroidException("PROFILE FAILED on process " + process);
             }
@@ -1076,8 +1082,8 @@
                 "       am broadcast <INTENT>\n" +
                 "       am instrument [-r] [-e <NAME> <VALUE>] [-p] [-w]\n" +
                 "               [--no-window-animation] <COMPONENT>\n" +
-                "       am profile start <PROCESS> <FILE>\n" +
-                "       am profile stop <PROCESS>\n" +
+                "       am profile [looper] start <PROCESS> <FILE>\n" +
+                "       am profile [looper] stop <PROCESS>\n" +
                 "       am dumpheap [flags] <PROCESS> <FILE>\n" +
                 "       am monitor [--gdb <port>]\n" +
                 "       am screen-compat [on|off] <PACKAGE>\n" +
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 2a731a3..2178971 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -1107,7 +1107,8 @@
             String path = data.readString();
             ParcelFileDescriptor fd = data.readInt() != 0
                     ? data.readFileDescriptor() : null;
-            boolean res = profileControl(process, start, path, fd);
+            int profileType = data.readInt();
+            boolean res = profileControl(process, start, path, fd, profileType);
             reply.writeNoException();
             reply.writeInt(res ? 1 : 0);
             return true;
@@ -2888,7 +2889,7 @@
     }
     
     public boolean profileControl(String process, boolean start,
-            String path, ParcelFileDescriptor fd) throws RemoteException
+            String path, ParcelFileDescriptor fd, int profileType) throws RemoteException
     {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
@@ -2902,6 +2903,7 @@
         } else {
             data.writeInt(0);
         }
+        data.writeInt(profileType);
         mRemote.transact(PROFILE_CONTROL_TRANSACTION, data, reply, 0);
         reply.readException();
         boolean res = reply.readInt() != 0;
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index f6cd866..718c6de 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -676,11 +676,12 @@
             queueOrSendMessage(H.ACTIVITY_CONFIGURATION_CHANGED, token);
         }
 
-        public void profilerControl(boolean start, String path, ParcelFileDescriptor fd) {
+        public void profilerControl(boolean start, String path, ParcelFileDescriptor fd,
+                int profileType) {
             ProfilerControlData pcd = new ProfilerControlData();
             pcd.path = path;
             pcd.fd = fd;
-            queueOrSendMessage(H.PROFILER_CONTROL, pcd, start ? 1 : 0);
+            queueOrSendMessage(H.PROFILER_CONTROL, pcd, start ? 1 : 0, profileType);
         }
 
         public void dumpHeap(boolean managed, String path, ParcelFileDescriptor fd) {
@@ -1148,7 +1149,7 @@
                     handleActivityConfigurationChanged((IBinder)msg.obj);
                     break;
                 case PROFILER_CONTROL:
-                    handleProfilerControl(msg.arg1 != 0, (ProfilerControlData)msg.obj);
+                    handleProfilerControl(msg.arg1 != 0, (ProfilerControlData)msg.obj, msg.arg2);
                     break;
                 case CREATE_BACKUP_AGENT:
                     handleCreateBackupAgent((CreateBackupAgentData)msg.obj);
@@ -3469,11 +3470,18 @@
         performConfigurationChanged(r.activity, mCompatConfiguration);
     }
 
-    final void handleProfilerControl(boolean start, ProfilerControlData pcd) {
+    final void handleProfilerControl(boolean start, ProfilerControlData pcd, int profileType) {
         if (start) {
             try {
-                Debug.startMethodTracing(pcd.path, pcd.fd.getFileDescriptor(),
-                        8 * 1024 * 1024, 0);
+                switch (profileType) {
+                    case 1:
+                        ViewDebug.startLooperProfiling(pcd.path, pcd.fd.getFileDescriptor());
+                        break;
+                    default:
+                        Debug.startMethodTracing(pcd.path, pcd.fd.getFileDescriptor(),
+                                8 * 1024 * 1024, 0);
+                        break;
+                }
             } catch (RuntimeException e) {
                 Slog.w(TAG, "Profiling failed on path " + pcd.path
                         + " -- can the process access this path?");
@@ -3485,7 +3493,15 @@
                 }
             }
         } else {
-            Debug.stopMethodTracing();
+            switch (profileType) {
+                case 1:
+                    ViewDebug.stopLooperProfiling();
+                    break;
+                default:
+                    Debug.stopMethodTracing();
+                    break;
+                    
+            }
         }
     }
 
diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java
index 942f245..c8a3a94 100644
--- a/core/java/android/app/ApplicationThreadNative.java
+++ b/core/java/android/app/ApplicationThreadNative.java
@@ -379,7 +379,8 @@
             String path = data.readString();
             ParcelFileDescriptor fd = data.readInt() != 0
                     ? data.readFileDescriptor() : null;
-            profilerControl(start, path, fd);
+            int profileType = data.readInt();
+            profilerControl(start, path, fd, profileType);
             return true;
         }
         
@@ -936,7 +937,7 @@
     }
     
     public void profilerControl(boolean start, String path,
-            ParcelFileDescriptor fd) throws RemoteException {
+            ParcelFileDescriptor fd, int profileType) throws RemoteException {
         Parcel data = Parcel.obtain();
         data.writeInterfaceToken(IApplicationThread.descriptor);
         data.writeInt(start ? 1 : 0);
@@ -947,6 +948,7 @@
         } else {
             data.writeInt(0);
         }
+        data.writeInt(profileType);
         mRemote.transact(PROFILER_CONTROL_TRANSACTION, data, null,
                 IBinder.FLAG_ONEWAY);
         data.recycle();
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 93c821c..64d77e8 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -284,7 +284,7 @@
     
     // Turn on/off profiling in a particular process.
     public boolean profileControl(String process, boolean start,
-            String path, ParcelFileDescriptor fd) throws RemoteException;
+            String path, ParcelFileDescriptor fd, int profileType) throws RemoteException;
     
     public boolean shutdown(int timeout) throws RemoteException;
     
diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java
index 9de0bf4..d0607d0 100644
--- a/core/java/android/app/IApplicationThread.java
+++ b/core/java/android/app/IApplicationThread.java
@@ -105,7 +105,7 @@
             throws RemoteException;
     void scheduleLowMemory() throws RemoteException;
     void scheduleActivityConfigurationChanged(IBinder token) throws RemoteException;
-    void profilerControl(boolean start, String path, ParcelFileDescriptor fd)
+    void profilerControl(boolean start, String path, ParcelFileDescriptor fd, int profileType)
             throws RemoteException;
     void dumpHeap(boolean managed, String path, ParcelFileDescriptor fd)
             throws RemoteException;
diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java
index 3798c9d..5f61cbb 100644
--- a/core/java/android/view/ViewDebug.java
+++ b/core/java/android/view/ViewDebug.java
@@ -36,7 +36,7 @@
 import java.io.ByteArrayOutputStream;
 import java.io.DataOutputStream;
 import java.io.File;
-import java.io.FileNotFoundException;
+import java.io.FileDescriptor;
 import java.io.FileOutputStream;
 import java.io.FileWriter;
 import java.io.IOException;
@@ -426,22 +426,22 @@
      * and obtain the traces. Both methods must be invoked on the
      * same thread.
      * 
-     * @param traceFile The path where to write the looper traces
-     * 
-     * @see #stopLooperProfiling() 
+     * @hide
      */
-    public static void startLooperProfiling(File traceFile) {
+    public static void startLooperProfiling(String path, FileDescriptor fileDescriptor) {
         if (sLooperProfilerStorage.get() == null) {
-            LooperProfiler profiler = new LooperProfiler(traceFile);
+            LooperProfiler profiler = new LooperProfiler(path, fileDescriptor);
             sLooperProfilerStorage.set(profiler);
             Looper.myLooper().setMessageLogging(profiler);
         }
-    }
+    }    
 
     /**
      * Stops profiling the looper associated with the current thread.
      * 
-     * @see #startLooperProfiling(java.io.File) 
+     * @see #startLooperProfiling(String, java.io.FileDescriptor) 
+     * 
+     * @hide
      */
     public static void stopLooperProfiling() {
         LooperProfiler profiler = sLooperProfilerStorage.get();
@@ -461,13 +461,16 @@
         private final long mTraceThreadStart;
         
         private final ArrayList<Entry> mTraces = new ArrayList<Entry>(512);
-        private final File mTraceFile;
 
         private final HashMap<String, Short> mTraceNames = new HashMap<String, Short>(32);
         private short mTraceId = 0;
 
-        LooperProfiler(File traceFile) {
-            mTraceFile = traceFile;
+        private final String mPath;
+        private final FileDescriptor mFileDescriptor;
+
+        LooperProfiler(String path, FileDescriptor fileDescriptor) {
+            mPath = path;
+            mFileDescriptor = fileDescriptor;
             mTraceWallStart = SystemClock.currentTimeMicro();
             mTraceThreadStart = SystemClock.currentThreadTimeMicro();            
         }
@@ -507,18 +510,11 @@
                 public void run() {
                     saveTraces();
                 }
-            }, "LooperProfiler[" + mTraceFile + "]").start();
+            }, "LooperProfiler[" + mPath + "]").start();
         }
 
         private void saveTraces() {
-            FileOutputStream fos;
-            try {
-                fos = new FileOutputStream(mTraceFile);
-            } catch (FileNotFoundException e) {
-                Log.e(LOG_TAG, "Could not open trace file: " + mTraceFile);
-                return;
-            }
-
+            FileOutputStream fos = new FileOutputStream(mFileDescriptor);
             DataOutputStream out = new DataOutputStream(new BufferedOutputStream(fos));
 
             try {
@@ -536,7 +532,7 @@
                     saveTrace(entry, out);
                 }
 
-                Log.d(LOG_TAG, "Looper traces ready: " + mTraceFile);
+                Log.d(LOG_TAG, "Looper traces ready: " + mPath);
             } catch (IOException e) {
                 Log.e(LOG_TAG, "Could not write trace file: ", e);
             } finally {
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 0924b86..e696eea 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -13442,7 +13442,7 @@
     }
 
     public boolean profileControl(String process, boolean start,
-            String path, ParcelFileDescriptor fd) throws RemoteException {
+            String path, ParcelFileDescriptor fd, int profileType) throws RemoteException {
 
         try {
             synchronized (this) {
@@ -13487,7 +13487,7 @@
                     }
                 }
             
-                proc.thread.profilerControl(start, path, fd);
+                proc.thread.profilerControl(start, path, fd, profileType);
                 fd = null;
                 return true;
             }
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ListActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/ListActivity.java
index 1493ab9..13b6129 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/ListActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/ListActivity.java
@@ -93,12 +93,9 @@
     }
     
     public void startProfiling(View v) {
-        ViewDebug.startLooperProfiling(new File(Environment.getExternalStorageDirectory(),
-                "looper.trace"));
     }
     
     public void stopProfiling(View v) {
-        ViewDebug.stopLooperProfiling();
     }
 
     @Override