Add cheap aidl tracing suitable for always-on-tracing (1/3)

Instead of having only one switch to enable all binder tracing,
we seperate between stack tracking and tracing. Stack tracking
still works as before. However, the more lightweight option,
tracing using android.os.Trace, is toggleable separately and will
work on the receiver side without dumping the entire stack trace.

We also make the tracing more efficient by caching
getTransactionName and concatting the class name.

Also, enable this selectively for SysUI+Launcher, which is
currently the focus of always-on-tracing effort to figure out
what kind of binder calls these processes are calling which seems
to contribute a huge amount of jank (long binder calls are in
>= 50% of janky traces).

Test: external/perfetto/tools/record_android_trace gfx view freq sched wm am aidl
Test: atest aidl_unittests
Bug: 202278427
Change-Id: I4d803d073cc4b69d07fa433bfef7e1c7f45132b7
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 15f67d0..01cd969 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -4074,12 +4074,12 @@
     }
 
     private void handleStartBinderTracking() {
-        Binder.enableTracing();
+        Binder.enableStackTracking();
     }
 
     private void handleStopBinderTrackingAndDump(ParcelFileDescriptor fd) {
         try {
-            Binder.disableTracing();
+            Binder.disableStackTracking();
             Binder.getTransactionTracker().writeTracesToFile(fd);
         } finally {
             IoUtils.closeQuietly(fd);
@@ -6618,7 +6618,7 @@
         boolean isAppProfileable = isAppDebuggable || data.appInfo.isProfileable();
         Trace.setAppTracingAllowed(isAppProfileable);
         if ((isAppProfileable || Build.IS_DEBUGGABLE) && data.enableBinderTracking) {
-            Binder.enableTracing();
+            Binder.enableStackTracking();
         }
 
         // Initialize heap profiling.
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 8f904b5..a2578d6 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -520,6 +520,10 @@
     // descriptor.
     @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     boolean stopBinderTrackingAndDump(in ParcelFileDescriptor fd);
+
+    /** Enables server-side binder tracing for the calling uid. */
+    void enableBinderTracing();
+
     @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     void suppressResizeConfigChanges(boolean suppress);
     @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index b069fb3..e2d7847 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -22,9 +22,11 @@
 import android.app.AppOpsManager;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.util.ExceptionUtils;
+import android.util.IntArray;
 import android.util.Log;
 import android.util.Slog;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.os.BinderCallHeavyHitterWatcher;
 import com.android.internal.os.BinderCallHeavyHitterWatcher.BinderCallHeavyHitterListener;
 import com.android.internal.os.BinderInternal;
@@ -140,33 +142,55 @@
     /**
      * Flag indicating whether we should be tracing transact calls.
      */
-    private static volatile boolean sTracingEnabled = false;
+    private static volatile boolean sStackTrackingEnabled = false;
+
+    private static final Object sTracingUidsWriteLock = new Object();
+    private static volatile IntArray sTracingUidsImmutable = new IntArray();
 
     /**
-     * Enable Binder IPC tracing.
+     * Enable Binder IPC stack tracking. If enabled, every binder transaction will be logged to
+     * {@link TransactionTracker}.
      *
      * @hide
      */
-    public static void enableTracing() {
-        sTracingEnabled = true;
+    public static void enableStackTracking() {
+        sStackTrackingEnabled = true;
     }
 
     /**
-     * Disable Binder IPC tracing.
+     * Disable Binder IPC stack tracking.
      *
      * @hide
      */
-    public static void disableTracing() {
-        sTracingEnabled = false;
+    public static void disableStackTracking() {
+        sStackTrackingEnabled = false;
     }
 
     /**
-     * Check if binder transaction tracing is enabled.
+     * @hide
+     */
+    public static void enableTracingForUid(int uid) {
+        synchronized (sTracingUidsWriteLock) {
+            final IntArray copy = sTracingUidsImmutable.clone();
+            copy.add(uid);
+            sTracingUidsImmutable = copy;
+        }
+    }
+
+    /**
+     * Check if binder transaction stack tracking is enabled.
      *
      * @hide
      */
-    public static boolean isTracingEnabled() {
-        return sTracingEnabled;
+    public static boolean isStackTrackingEnabled() {
+        return sStackTrackingEnabled;
+    }
+
+    /**
+     * @hide
+     */
+    public static boolean isTracingEnabled(int callingUid) {
+        return sTracingUidsImmutable.indexOf(callingUid) != -1;
     }
 
     /**
@@ -288,6 +312,9 @@
 
     private IInterface mOwner;
     private String mDescriptor;
+    private volatile String[] mTransactionTraceNames = null;
+    private volatile String mSimpleDescriptor = null;
+    private static final int TRANSACTION_TRACE_NAME_ID_LIMIT = 1024;
 
     /**
      * Return the ID of the process that sent you the current transaction
@@ -884,6 +911,53 @@
     }
 
     /**
+     * @hide
+     */
+    @VisibleForTesting
+    public final @NonNull String getTransactionTraceName(int transactionCode) {
+        if (mTransactionTraceNames == null) {
+            final String descriptor = getSimpleDescriptor();
+            final int highestId = Math.min(getMaxTransactionId(), TRANSACTION_TRACE_NAME_ID_LIMIT);
+            final String[] transactionNames = new String[highestId + 1];
+            final StringBuffer buf = new StringBuffer();
+            for (int i = 0; i <= highestId; i++) {
+                String transactionName = getTransactionName(i + FIRST_CALL_TRANSACTION);
+                if (transactionName != null) {
+                    buf.append(descriptor).append(':').append(transactionName);
+                } else {
+                    buf.append(descriptor).append('#').append(i + FIRST_CALL_TRANSACTION);
+                }
+                transactionNames[i] = buf.toString();
+                buf.setLength(0);
+            }
+            mTransactionTraceNames = transactionNames;
+            mSimpleDescriptor = descriptor;
+        }
+        final int index = transactionCode - FIRST_CALL_TRANSACTION;
+        if (index < 0 || index >= mTransactionTraceNames.length) {
+            return mSimpleDescriptor + "#" + transactionCode;
+        }
+        return mTransactionTraceNames[index];
+    }
+
+    private String getSimpleDescriptor() {
+        final int dot = mDescriptor.lastIndexOf(".");
+        if (dot > 0) {
+            // Strip the package name
+            return mDescriptor.substring(dot + 1);
+        }
+        return mDescriptor;
+    }
+
+    /**
+     * @return The highest user-defined transaction id of all transactions.
+     * @hide
+     */
+    public int getMaxTransactionId() {
+        return 0;
+    }
+
+    /**
      * Implemented to call the more convenient version
      * {@link #dump(FileDescriptor, PrintWriter, String[])}.
      */
@@ -1181,7 +1255,8 @@
         // Log any exceptions as warnings, don't silently suppress them.
         // If the call was {@link IBinder#FLAG_ONEWAY} then these exceptions
         // disappear into the ether.
-        final boolean tracingEnabled = Binder.isTracingEnabled();
+        final boolean tracingEnabled = Trace.isTagEnabled(Trace.TRACE_TAG_AIDL) &&
+                (Binder.isStackTrackingEnabled() || Binder.isTracingEnabled(callingUid));
         try {
             final BinderCallHeavyHitterWatcher heavyHitterWatcher = sHeavyHitterWatcher;
             if (heavyHitterWatcher != null) {
@@ -1189,9 +1264,7 @@
                 heavyHitterWatcher.onTransaction(callingUid, getClass(), code);
             }
             if (tracingEnabled) {
-                final String transactionName = getTransactionName(code);
-                Trace.traceBegin(Trace.TRACE_TAG_ALWAYS, getClass().getName() + ":"
-                        + (transactionName != null ? transactionName : code));
+                Trace.traceBegin(Trace.TRACE_TAG_AIDL, getTransactionTraceName(code));
             }
 
             if ((flags & FLAG_COLLECT_NOTED_APP_OPS) != 0) {
@@ -1226,7 +1299,7 @@
             res = true;
         } finally {
             if (tracingEnabled) {
-                Trace.traceEnd(Trace.TRACE_TAG_ALWAYS);
+                Trace.traceEnd(Trace.TRACE_TAG_AIDL);
             }
             if (observer != null) {
                 // The parcel RPC headers have been called during onTransact so we can now access
diff --git a/core/java/android/os/BinderProxy.java b/core/java/android/os/BinderProxy.java
index 483b549..e929920 100644
--- a/core/java/android/os/BinderProxy.java
+++ b/core/java/android/os/BinderProxy.java
@@ -545,7 +545,7 @@
             }
         }
 
-        final boolean tracingEnabled = Binder.isTracingEnabled();
+        final boolean tracingEnabled = Binder.isStackTrackingEnabled();
         if (tracingEnabled) {
             final Throwable tr = new Throwable();
             Binder.getTransactionTracker().addTrace(tr);
diff --git a/core/java/android/util/IntArray.java b/core/java/android/util/IntArray.java
index b77265b..7b28b8a 100644
--- a/core/java/android/util/IntArray.java
+++ b/core/java/android/util/IntArray.java
@@ -34,7 +34,7 @@
     private int[] mValues;
     private int mSize;
 
-    private  IntArray(int[] array, int size) {
+    private IntArray(int[] array, int size) {
         mValues = array;
         mSize = Preconditions.checkArgumentInRange(size, 0, array.length, "size");
     }
@@ -178,10 +178,8 @@
     }
 
     @Override
-    public IntArray clone() throws CloneNotSupportedException {
-        final IntArray clone = (IntArray) super.clone();
-        clone.mValues = mValues.clone();
-        return clone;
+    public IntArray clone() {
+        return new IntArray(mValues.clone(), mSize);
     }
 
     /**
diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp
index bcd794e..1c41e00 100644
--- a/core/tests/coretests/Android.bp
+++ b/core/tests/coretests/Android.bp
@@ -23,6 +23,7 @@
     ],
 
     aidl: {
+        generate_get_transaction_name: true,
         local_include_dirs: ["aidl"],
     },
 
diff --git a/core/tests/coretests/src/android/os/AidlTest.java b/core/tests/coretests/src/android/os/AidlTest.java
index 4c51415..5f54b09 100644
--- a/core/tests/coretests/src/android/os/AidlTest.java
+++ b/core/tests/coretests/src/android/os/AidlTest.java
@@ -27,11 +27,12 @@
 public class AidlTest extends TestCase {
 
     private IAidlTest mRemote;
+    private AidlObject mLocal;
 
     @Override
     protected void setUp() throws Exception {
         super.setUp();
-        AidlObject mLocal = new AidlObject();
+        mLocal = new AidlObject();
         mRemote = IAidlTest.Stub.asInterface(mLocal);
     }
 
@@ -417,5 +418,24 @@
         }
         assertEquals(good, true);
     }
+
+    @SmallTest
+    public void testGetTransactionName() throws Exception {
+        assertEquals(15, mLocal.getMaxTransactionId());
+
+        assertEquals("booleanArray",
+                mLocal.getTransactionName(IAidlTest.Stub.TRANSACTION_booleanArray));
+        assertEquals("voidSecurityException",
+                mLocal.getTransactionName(IAidlTest.Stub.TRANSACTION_voidSecurityException));
+        assertEquals("parcelableIn",
+                mLocal.getTransactionName(IAidlTest.Stub.TRANSACTION_parcelableIn));
+
+        assertEquals("IAidlTest:booleanArray",
+                mLocal.getTransactionTraceName(IAidlTest.Stub.TRANSACTION_booleanArray));
+        assertEquals("IAidlTest:voidSecurityException",
+                mLocal.getTransactionTraceName(IAidlTest.Stub.TRANSACTION_voidSecurityException));
+        assertEquals("IAidlTest:parcelableIn",
+                mLocal.getTransactionTraceName(IAidlTest.Stub.TRANSACTION_parcelableIn));
+    }
 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index 8c405ca..63962fa 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui;
 
+import android.app.ActivityManager;
 import android.app.ActivityThread;
 import android.app.Application;
 import android.app.Notification;
@@ -27,6 +28,7 @@
 import android.content.res.Configuration;
 import android.os.Bundle;
 import android.os.Process;
+import android.os.RemoteException;
 import android.os.SystemProperties;
 import android.os.Trace;
 import android.os.UserHandle;
@@ -111,6 +113,13 @@
                         ThreadedRendererCompat.EGL_CONTEXT_PRIORITY_HIGH_IMG);
             }
 
+            // Enable binder tracing on system server for calls originating from SysUI
+            try {
+                ActivityManager.getService().enableBinderTracing();
+            } catch (RemoteException e) {
+                Log.e(TAG, "Unable to enable binder tracing", e);
+            }
+
             registerReceiver(new BroadcastReceiver() {
                 @Override
                 public void onReceive(Context context, Intent intent) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index a33aa60..a745e5a 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -15659,6 +15659,11 @@
         }
     }
 
+    @Override
+    public void enableBinderTracing() {
+        Binder.enableTracingForUid(Binder.getCallingUid());
+    }
+
     @VisibleForTesting
     public final class LocalService extends ActivityManagerInternal
             implements ActivityManagerLocal {