Add binder transaction tracking.

Add the ability to am to be able to track binder transact calls. This
will help us diagnose excessive IPC calls.

This CL adds the trace-ip command to am. The usage is,

To start binder transaction tracking,
am trace-ipc start
To stop tracking and dump the data to a file,
am trace-ipc stop --dump-file <FILE>

Bug: 21398706
Change-Id: Ic0c9b3be757dd0662a2750a0d8447e2a5ef1fa90
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index bf3b455..9f01b56 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -202,6 +202,11 @@
                 "    --abi <ABI>: Launch the instrumented process with the selected ABI.\n"  +
                 "        This assumes that the process supports the selected ABI.\n" +
                 "\n" +
+                "am trace-ipc: Trace IPC transactions.\n" +
+                "  start: start tracing IPC transactions.\n" +
+                "  stop: stop tracing IPC transactions and dump the results to file.\n" +
+                "    --dump-file <FILE>: Specify the file the trace should be dumped to.\n" +
+                "\n" +
                 "am profile: start and stop profiler on a process.  The given <PROCESS> argument\n" +
                 "  may be either a process name or pid.  Options are:\n" +
                 "    --user <USER_ID> | current: When supplying a process name,\n" +
@@ -365,6 +370,8 @@
             runKillAll();
         } else if (op.equals("instrument")) {
             runInstrument();
+        } else if (op.equals("trace-ipc")) {
+            runTraceIpc();
         } else if (op.equals("broadcast")) {
             sendBroadcast();
         } else if (op.equals("profile")) {
@@ -1097,6 +1104,62 @@
         }
     }
 
+    private void runTraceIpc() throws Exception {
+        String op = nextArgRequired();
+        if (op.equals("start")) {
+            runTraceIpcStart();
+        } else if (op.equals("stop")) {
+            runTraceIpcStop();
+        } else {
+            showError("Error: unknown command '" + op + "'");
+            return;
+        }
+    }
+
+    private void runTraceIpcStart() throws Exception {
+        System.out.println("Starting IPC tracing.");
+        mAm.startBinderTracking();
+    }
+
+    private void runTraceIpcStop() throws Exception {
+        String opt;
+        String filename = null;
+        while ((opt=nextOption()) != null) {
+            if (opt.equals("--dump-file")) {
+                filename = nextArgRequired();
+            } else {
+                System.err.println("Error: Unknown option: " + opt);
+                return;
+            }
+        }
+        if (filename == null) {
+            System.err.println("Error: Specify filename to dump logs to.");
+            return;
+        }
+
+        ParcelFileDescriptor fd = null;
+
+        try {
+            File file = new File(filename);
+            file.delete();
+            fd = openForSystemServer(file,
+                    ParcelFileDescriptor.MODE_CREATE |
+                            ParcelFileDescriptor.MODE_TRUNCATE |
+                            ParcelFileDescriptor.MODE_READ_WRITE);
+        } catch (FileNotFoundException e) {
+            System.err.println("Error: Unable to open file: " + filename);
+            System.err.println("Consider using a file under /data/local/tmp/");
+            return;
+        }
+
+        ;
+        if (!mAm.stopBinderTrackingAndDump(fd)) {
+            throw new AndroidException("STOP TRACE FAILED.");
+        }
+
+        System.out.println("Stopped IPC tracing. Dumping logs to: " + filename);
+    }
+
     static void removeWallOption() {
         String props = SystemProperties.get("dalvik.vm.extra-opts");
         if (props != null && props.contains("-Xprofile:wallclock")) {
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 6ae21eb..07b9ebe 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -2554,6 +2554,24 @@
             reply.writeInt(res ? 1 : 0);
             return true;
         }
+
+        case START_BINDER_TRACKING_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            boolean res = startBinderTracking();
+            reply.writeNoException();
+            reply.writeInt(res ? 1 : 0);
+            return true;
+        }
+
+        case STOP_BINDER_TRACKING_AND_DUMP_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            ParcelFileDescriptor fd = data.readInt() != 0
+                    ? ParcelFileDescriptor.CREATOR.createFromParcel(data) : null;
+            boolean res = stopBinderTrackingAndDump(fd);
+            reply.writeNoException();
+            reply.writeInt(res ? 1 : 0);
+            return true;
+        }
         }
 
         return super.onTransact(code, data, reply, flags);
@@ -5505,6 +5523,36 @@
         reply.recycle();
     }
 
+    public boolean startBinderTracking() throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        mRemote.transact(START_BINDER_TRACKING_TRANSACTION, data, reply, 0);
+        reply.readException();
+        boolean res = reply.readInt() != 0;
+        reply.recycle();
+        data.recycle();
+        return res;
+    }
+
+    public boolean stopBinderTrackingAndDump(ParcelFileDescriptor fd) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        if (fd != null) {
+            data.writeInt(1);
+            fd.writeToParcel(data, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
+        } else {
+            data.writeInt(0);
+        }
+        mRemote.transact(STOP_BINDER_TRACKING_AND_DUMP_TRANSACTION, data, reply, 0);
+        reply.readException();
+        boolean res = reply.readInt() != 0;
+        reply.recycle();
+        data.recycle();
+        return res;
+    }
+
     @Override
     public IActivityContainer createStackOnDisplay(int displayId) throws RemoteException {
         Parcel data = Parcel.obtain();
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index e21c04a..c95f876 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -445,6 +445,7 @@
         IInstrumentationWatcher instrumentationWatcher;
         IUiAutomationConnection instrumentationUiAutomationConnection;
         int debugMode;
+        boolean enableBinderTracking;
         boolean enableOpenGlTrace;
         boolean restrictedBackupMode;
         boolean persistent;
@@ -770,7 +771,8 @@
                 ProfilerInfo profilerInfo, Bundle instrumentationArgs,
                 IInstrumentationWatcher instrumentationWatcher,
                 IUiAutomationConnection instrumentationUiConnection, int debugMode,
-                boolean enableOpenGlTrace, boolean isRestrictedBackupMode, boolean persistent,
+                boolean enableBinderTracking, boolean enableOpenGlTrace,
+                boolean isRestrictedBackupMode, boolean persistent,
                 Configuration config, CompatibilityInfo compatInfo, Map<String, IBinder> services,
                 Bundle coreSettings) {
 
@@ -827,6 +829,7 @@
             data.instrumentationWatcher = instrumentationWatcher;
             data.instrumentationUiAutomationConnection = instrumentationUiConnection;
             data.debugMode = debugMode;
+            data.enableBinderTracking = enableBinderTracking;
             data.enableOpenGlTrace = enableOpenGlTrace;
             data.restrictedBackupMode = isRestrictedBackupMode;
             data.persistent = persistent;
@@ -1223,6 +1226,19 @@
                 StrictMode.onCleartextNetworkDetected(firstPacket);
             }
         }
+
+        @Override
+        public void startBinderTracking() {
+            sendMessage(H.START_BINDER_TRACKING, null);
+        }
+
+        @Override
+        public void stopBinderTrackingAndDump(FileDescriptor fd) {
+            try {
+                sendMessage(H.STOP_BINDER_TRACKING_AND_DUMP, ParcelFileDescriptor.dup(fd));
+            } catch (IOException e) {
+            }
+        }
     }
 
     private class H extends Handler {
@@ -1276,6 +1292,8 @@
         public static final int CANCEL_VISIBLE_BEHIND = 147;
         public static final int BACKGROUND_VISIBLE_BEHIND_CHANGED = 148;
         public static final int ENTER_ANIMATION_COMPLETE = 149;
+        public static final int START_BINDER_TRACKING = 150;
+        public static final int STOP_BINDER_TRACKING_AND_DUMP = 151;
 
         String codeToString(int code) {
             if (DEBUG_MESSAGES) {
@@ -1558,6 +1576,12 @@
                 case ENTER_ANIMATION_COMPLETE:
                     handleEnterAnimationComplete((IBinder) msg.obj);
                     break;
+                case START_BINDER_TRACKING:
+                    handleStartBinderTracking();
+                    break;
+                case STOP_BINDER_TRACKING_AND_DUMP:
+                    handleStopBinderTrackingAndDump((ParcelFileDescriptor) msg.obj);
+                    break;
             }
             if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + codeToString(msg.what));
         }
@@ -2663,6 +2687,20 @@
         }
     }
 
+    private void handleStartBinderTracking() {
+        Binder.enableTracing();
+    }
+
+    private void handleStopBinderTrackingAndDump(ParcelFileDescriptor fd) {
+        try {
+            Binder.disableTracing();
+            Binder.getTransactionTracker().writeTracesToFile(fd);
+        } finally {
+            IoUtils.closeQuietly(fd);
+            Binder.getTransactionTracker().clearTraces();
+        }
+    }
+
     private static final ThreadLocal<Intent> sCurrentBroadcastIntent = new ThreadLocal<Intent>();
 
     /**
@@ -4583,8 +4621,11 @@
         }
 
         // Allow application-generated systrace messages if we're debuggable.
-        boolean appTracingAllowed = (data.appInfo.flags&ApplicationInfo.FLAG_DEBUGGABLE) != 0;
-        Trace.setAppTracingAllowed(appTracingAllowed);
+        boolean isAppDebuggable = (data.appInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
+        Trace.setAppTracingAllowed(isAppDebuggable);
+        if (isAppDebuggable && data.enableBinderTracking) {
+            Binder.enableTracing();
+        }
 
         /**
          * Initialize the default http proxy in this process for the reasons we set the time zone.
diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java
index 1461380..372fdaa 100644
--- a/core/java/android/app/ApplicationThreadNative.java
+++ b/core/java/android/app/ApplicationThreadNative.java
@@ -288,6 +288,7 @@
             IUiAutomationConnection uiAutomationConnection =
                     IUiAutomationConnection.Stub.asInterface(binder);
             int testMode = data.readInt();
+            boolean enableBinderTracking = data.readInt() != 0;
             boolean openGlTrace = data.readInt() != 0;
             boolean restrictedBackupMode = (data.readInt() != 0);
             boolean persistent = (data.readInt() != 0);
@@ -296,8 +297,8 @@
             HashMap<String, IBinder> services = data.readHashMap(null);
             Bundle coreSettings = data.readBundle();
             bindApplication(packageName, info, providers, testName, profilerInfo, testArgs,
-                    testWatcher, uiAutomationConnection, testMode, openGlTrace,
-                    restrictedBackupMode, persistent, config, compatInfo, services, coreSettings);
+                    testWatcher, uiAutomationConnection, testMode, enableBinderTracking,
+                    openGlTrace, restrictedBackupMode, persistent, config, compatInfo, services, coreSettings);
             return true;
         }
 
@@ -690,6 +691,28 @@
             reply.writeNoException();
             return true;
         }
+
+        case START_BINDER_TRACKING_TRANSACTION:
+        {
+            data.enforceInterface(IApplicationThread.descriptor);
+            startBinderTracking();
+            return true;
+        }
+
+        case STOP_BINDER_TRACKING_AND_DUMP_TRANSACTION:
+        {
+            data.enforceInterface(IApplicationThread.descriptor);
+            ParcelFileDescriptor fd = data.readFileDescriptor();
+            if (fd != null) {
+                stopBinderTrackingAndDump(fd.getFileDescriptor());
+                try {
+                    fd.close();
+                } catch (IOException e) {
+                }
+            }
+            return true;
+        }
+
         }
 
         return super.onTransact(code, data, reply, flags);
@@ -980,12 +1003,12 @@
     }
 
     public final void bindApplication(String packageName, ApplicationInfo info,
-            List<ProviderInfo> providers, ComponentName testName, ProfilerInfo profilerInfo,
-            Bundle testArgs, IInstrumentationWatcher testWatcher,
-            IUiAutomationConnection uiAutomationConnection, int debugMode,
-            boolean openGlTrace, boolean restrictedBackupMode, boolean persistent,
-            Configuration config, CompatibilityInfo compatInfo, Map<String, IBinder> services,
-            Bundle coreSettings) throws RemoteException {
+                                      List<ProviderInfo> providers, ComponentName testName, ProfilerInfo profilerInfo,
+                                      Bundle testArgs, IInstrumentationWatcher testWatcher,
+                                      IUiAutomationConnection uiAutomationConnection, int debugMode,
+                                      boolean enableBinderTracking, boolean openGlTrace, boolean restrictedBackupMode, boolean persistent,
+                                      Configuration config, CompatibilityInfo compatInfo, Map<String, IBinder> services,
+                                      Bundle coreSettings) throws RemoteException {
         Parcel data = Parcel.obtain();
         data.writeInterfaceToken(IApplicationThread.descriptor);
         data.writeString(packageName);
@@ -1007,6 +1030,7 @@
         data.writeStrongInterface(testWatcher);
         data.writeStrongInterface(uiAutomationConnection);
         data.writeInt(debugMode);
+        data.writeInt(enableBinderTracking ? 1 : 0);
         data.writeInt(openGlTrace ? 1 : 0);
         data.writeInt(restrictedBackupMode ? 1 : 0);
         data.writeInt(persistent ? 1 : 0);
@@ -1396,4 +1420,23 @@
         mRemote.transact(NOTIFY_CLEARTEXT_NETWORK_TRANSACTION, data, null, IBinder.FLAG_ONEWAY);
         data.recycle();
     }
+
+    @Override
+    public void startBinderTracking() throws RemoteException {
+        Parcel data = Parcel.obtain();
+        data.writeInterfaceToken(IApplicationThread.descriptor);
+        mRemote.transact(START_BINDER_TRACKING_TRANSACTION, data, null,
+                IBinder.FLAG_ONEWAY);
+        data.recycle();
+    }
+
+    @Override
+    public void stopBinderTrackingAndDump(FileDescriptor fd) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        data.writeInterfaceToken(IApplicationThread.descriptor);
+        data.writeFileDescriptor(fd);
+        mRemote.transact(STOP_BINDER_TRACKING_AND_DUMP_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 9311e5e..cbbe845 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -508,6 +508,13 @@
     public boolean setProcessMemoryTrimLevel(String process, int uid, int level)
             throws RemoteException;
 
+    // Start Binder transaction tracking for all applications.
+    public boolean startBinderTracking() throws RemoteException;
+
+    // Stop Binder transaction tracking for all applications and dump trace data to the given file
+    // descriptor.
+    public boolean stopBinderTrackingAndDump(ParcelFileDescriptor fd) throws RemoteException;
+
     /*
      * Private non-Binder interfaces
      */
@@ -851,4 +858,7 @@
     int KEYGUARD_GOING_AWAY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+296;
     int REGISTER_UID_OBSERVER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+297;
     int UNREGISTER_UID_OBSERVER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+298;
+
+    int START_BINDER_TRACKING_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 299;
+    int STOP_BINDER_TRACKING_AND_DUMP_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 300;
 }
diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java
index 185578f..5c4d359 100644
--- a/core/java/android/app/IApplicationThread.java
+++ b/core/java/android/app/IApplicationThread.java
@@ -95,9 +95,10 @@
     void bindApplication(String packageName, ApplicationInfo info, List<ProviderInfo> providers,
             ComponentName testName, ProfilerInfo profilerInfo, Bundle testArguments,
             IInstrumentationWatcher testWatcher, IUiAutomationConnection uiAutomationConnection,
-            int debugMode, boolean openGlTrace, boolean restrictedBackupMode, boolean persistent,
-            Configuration config, CompatibilityInfo compatInfo, Map<String, IBinder> services,
-            Bundle coreSettings) throws RemoteException;
+            int debugMode, boolean enableBinderTracking, boolean openGlTrace,
+            boolean restrictedBackupMode, boolean persistent, Configuration config,
+            CompatibilityInfo compatInfo, Map<String, IBinder> services, Bundle coreSettings)
+            throws RemoteException;
     void scheduleExit() throws RemoteException;
     void scheduleSuicide() throws RemoteException;
     void scheduleConfigurationChanged(Configuration config) throws RemoteException;
@@ -148,6 +149,8 @@
     void scheduleBackgroundVisibleBehindChanged(IBinder token, boolean enabled) throws RemoteException;
     void scheduleEnterAnimationComplete(IBinder token) throws RemoteException;
     void notifyCleartextNetwork(byte[] firstPacket) throws RemoteException;
+    void startBinderTracking() throws RemoteException;
+    void stopBinderTrackingAndDump(FileDescriptor fd) throws RemoteException;
 
     String descriptor = "android.app.IApplicationThread";
 
@@ -206,4 +209,6 @@
     int BACKGROUND_VISIBLE_BEHIND_CHANGED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+53;
     int ENTER_ANIMATION_COMPLETE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+54;
     int NOTIFY_CLEARTEXT_NETWORK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+55;
+    int START_BINDER_TRACKING_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+56;
+    int STOP_BINDER_TRACKING_AND_DUMP_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+57;
 }
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index 64562a4..cfa6164 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -74,11 +74,62 @@
      */
     private static String sDumpDisabled = null;
 
+    /**
+     * Global transaction tracker instance for this process.
+     */
+    private static TransactionTracker sTransactionTracker = null;
+
+    // Transaction tracking code.
+
+    /**
+     * Flag indicating whether we should be tracing transact calls.
+     *
+     */
+    private static boolean sTracingEnabled = false;
+
+    /**
+     * Enable Binder IPC tracing.
+     *
+     * @hide
+     */
+    public static void  enableTracing() {
+        sTracingEnabled = true;
+    };
+
+    /**
+     * Disable Binder IPC tracing.
+     *
+     * @hide
+     */
+    public static void  disableTracing() {
+        sTracingEnabled = false;
+    }
+
+    /**
+     * Check if binder transaction tracing is enabled.
+     *
+     * @hide
+     */
+    public static boolean isTracingEnabled() {
+        return sTracingEnabled;
+    }
+
+    /**
+     * Get the binder transaction tracker for this process.
+     *
+     * @hide
+     */
+    public synchronized static TransactionTracker getTransactionTracker() {
+        if (sTransactionTracker == null)
+            sTransactionTracker = new TransactionTracker();
+        return sTransactionTracker;
+    }
+
     /* mObject is used by native code, do not remove or rename */
     private long mObject;
     private IInterface mOwner;
     private String mDescriptor;
-    
+
     /**
      * Return the ID of the process that sent you the current transaction
      * that is being processed.  This pid can be used with higher-level
@@ -381,6 +432,7 @@
     public final boolean transact(int code, Parcel data, Parcel reply,
             int flags) throws RemoteException {
         if (false) Log.v("Binder", "Transact: " + code + " to " + this);
+
         if (data != null) {
             data.setDataPosition(0);
         }
@@ -500,6 +552,7 @@
 
     public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
         Binder.checkParcel(this, code, data, "Unreasonably large binder buffer");
+        if (Binder.isTracingEnabled()) { Binder.getTransactionTracker().addTrace(); }
         return transactNative(code, data, reply, flags);
     }
 
diff --git a/core/java/android/os/TransactionTracker.java b/core/java/android/os/TransactionTracker.java
new file mode 100644
index 0000000..77f3e02
--- /dev/null
+++ b/core/java/android/os/TransactionTracker.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.os;
+
+import android.util.Log;
+import android.util.Size;
+import com.android.internal.util.FastPrintWriter;
+
+import java.io.FileOutputStream;
+import java.io.PrintWriter;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Class used to track binder transactions. It indexes the transactions by the stack trace.
+ *
+ * @hide
+ */
+public class TransactionTracker {
+    private Map<String, Long> mTraces;
+
+    private void resetTraces() {
+        synchronized (this) {
+            mTraces = new HashMap<String, Long>();
+        }
+    }
+
+    TransactionTracker() {
+        resetTraces();
+    }
+
+    public void addTrace() {
+        String trace = Log.getStackTraceString(new Throwable());
+        synchronized (this) {
+            if (mTraces.containsKey(trace)) {
+                mTraces.put(trace, mTraces.get(trace) + 1);
+            } else {
+                mTraces.put(trace, Long.valueOf(1));
+            }
+        }
+    }
+
+    public void writeTracesToFile(ParcelFileDescriptor fd) {
+        if (mTraces.isEmpty()) {
+            return;
+        }
+
+        PrintWriter pw = new FastPrintWriter(new FileOutputStream(fd.getFileDescriptor()));
+        synchronized (this) {
+            for (String trace : mTraces.keySet()) {
+                pw.println("Count: " + mTraces.get(trace));
+                pw.println("Trace: " + trace);
+                pw.println();
+            }
+        }
+        pw.flush();
+    }
+
+    public void clearTraces(){
+        resetTraces();
+    }
+}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 45dcfd3..db4f395 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -641,6 +641,8 @@
      */
     final ArrayList<ProcessRecord> mPendingPssProcesses = new ArrayList<ProcessRecord>();
 
+    private boolean mBinderTransactionTrackingEnabled = false;
+
     /**
      * Last time we requested PSS data of all processes.
      */
@@ -6042,9 +6044,9 @@
                     : new ProfilerInfo(profileFile, profileFd, samplingInterval, profileAutoStop);
             thread.bindApplication(processName, appInfo, providers, app.instrumentationClass,
                     profilerInfo, app.instrumentationArguments, app.instrumentationWatcher,
-                    app.instrumentationUiAutomationConnection, testMode, enableOpenGlTrace,
-                    isRestrictedBackupMode || !normalMode, app.persistent,
-                    new Configuration(mConfiguration), app.compat,
+                    app.instrumentationUiAutomationConnection, testMode,
+                    mBinderTransactionTrackingEnabled, enableOpenGlTrace, isRestrictedBackupMode
+                    || !normalMode, app.persistent, new Configuration(mConfiguration), app.compat,
                     getCommonServicesLocked(app.isolated),
                     mCoreSettingsObserver.getCoreSettingsLocked());
             updateLruProcessLocked(app, false, null);
@@ -20288,6 +20290,104 @@
         return info;
     }
 
+    private boolean processSanityChecksLocked(ProcessRecord process) {
+        if (process == null || process.thread == null) {
+            return false;
+        }
+
+        boolean isDebuggable = "1".equals(SystemProperties.get(SYSTEM_DEBUGGABLE, "0"));
+        if (!isDebuggable) {
+            if ((process.info.flags&ApplicationInfo.FLAG_DEBUGGABLE) == 0) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    public boolean startBinderTracking() throws RemoteException {
+        synchronized (this) {
+            mBinderTransactionTrackingEnabled = true;
+            // TODO: hijacking SET_ACTIVITY_WATCHER, but should be changed to its own
+            // permission (same as profileControl).
+            if (checkCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER)
+                    != PackageManager.PERMISSION_GRANTED) {
+                throw new SecurityException("Requires permission "
+                        + android.Manifest.permission.SET_ACTIVITY_WATCHER);
+            }
+
+            for (int i = 0; i < mLruProcesses.size(); i++) {
+                ProcessRecord process = mLruProcesses.get(i);
+                if (!processSanityChecksLocked(process)) {
+                    continue;
+                }
+                try {
+                    process.thread.startBinderTracking();
+                } catch (RemoteException e) {
+                    Log.v(TAG, "Process disappared");
+                }
+            }
+            return true;
+        }
+    }
+
+    public boolean stopBinderTrackingAndDump(ParcelFileDescriptor fd) throws RemoteException {
+        try {
+            synchronized (this) {
+                mBinderTransactionTrackingEnabled = false;
+                // TODO: hijacking SET_ACTIVITY_WATCHER, but should be changed to its own
+                // permission (same as profileControl).
+                if (checkCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER)
+                        != PackageManager.PERMISSION_GRANTED) {
+                    throw new SecurityException("Requires permission "
+                            + android.Manifest.permission.SET_ACTIVITY_WATCHER);
+                }
+
+                if (fd == null) {
+                    throw new IllegalArgumentException("null fd");
+                }
+
+                PrintWriter pw = new FastPrintWriter(new FileOutputStream(fd.getFileDescriptor()));
+                pw.println("Binder transaction traces for all processes.\n");
+                for (ProcessRecord process : mLruProcesses) {
+                    if (!processSanityChecksLocked(process)) {
+                        continue;
+                    }
+
+                    pw.println("Traces for process: " + process.processName);
+                    pw.flush();
+                    try {
+                        TransferPipe tp = new TransferPipe();
+                        try {
+                            process.thread.stopBinderTrackingAndDump(
+                                    tp.getWriteFd().getFileDescriptor());
+                            tp.go(fd.getFileDescriptor());
+                        } finally {
+                            tp.kill();
+                        }
+                    } catch (IOException e) {
+                        pw.println("Failure while dumping IPC traces from " + process +
+                                ".  Exception: " + e);
+                        pw.flush();
+                    } catch (RemoteException e) {
+                        pw.println("Got a RemoteException while dumping IPC traces from " +
+                                process + ".  Exception: " + e);
+                        pw.flush();
+                    }
+                }
+                fd = null;
+                return true;
+            }
+        } finally {
+            if (fd != null) {
+                try {
+                    fd.close();
+                } catch (IOException e) {
+                }
+            }
+        }
+    }
+
     private final class LocalService extends ActivityManagerInternal {
         @Override
         public void onWakefulnessChanged(int wakefulness) {