diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java index ea7462bd9113f..0c81e5a23291c 100644 --- a/core/java/android/os/Binder.java +++ b/core/java/android/os/Binder.java @@ -122,6 +122,13 @@ public class Binder implements IBinder { private static native long getNativeFinalizer(); + /** + * Returns the TID (task ID) for the current thread. Same as {@link Thread#getNativeTid()} + * + * @hide + */ + public static native int getNativeTid(); + // Use a Holder to allow static initialization of Binder in the boot image, and // possibly to avoid some initialization ordering issues. private static class NoImagePreloadHolder { diff --git a/core/java/com/android/internal/os/BinderCallsStats.java b/core/java/com/android/internal/os/BinderCallsStats.java index cc55cff262a37..e09ef49acd10d 100644 --- a/core/java/com/android/internal/os/BinderCallsStats.java +++ b/core/java/com/android/internal/os/BinderCallsStats.java @@ -27,6 +27,7 @@ import android.os.UserHandle; import android.text.format.DateFormat; import android.util.ArrayMap; import android.util.ArraySet; +import android.util.IntArray; import android.util.Pair; import android.util.Slog; import android.util.SparseArray; @@ -126,6 +127,11 @@ public class BinderCallsStats implements BinderInternal.Observer { } }; + private final Object mNativeTidsLock = new Object(); + // @GuardedBy("mNativeTidsLock") // Cannot mark it as "GuardedBy" because it's read + // directly, as a volatile field. + private volatile IntArray mNativeTids = new IntArray(0); + /** Injector for {@link BinderCallsStats}. */ public static class Injector { public Random getRandomGenerator() { @@ -175,6 +181,8 @@ public class BinderCallsStats implements BinderInternal.Observer { return null; } + noteNativeThreadId(); + final CallSession s = obtainCallSession(); s.binderClass = binder.getClass(); s.transactionCode = code; @@ -312,6 +320,27 @@ public class BinderCallsStats implements BinderInternal.Observer { } } + private void noteNativeThreadId() { + final int tid = getNativeTid(); + int index = mNativeTids.binarySearch(tid); + if (index >= 0) { + return; + } + + // Use the copy-on-write approach. The changes occur exceedingly infrequently, so + // this code path is exercised just a few times per boot + synchronized (mNativeTidsLock) { + IntArray nativeTids = mNativeTids; + index = nativeTids.binarySearch(tid); + if (index < 0) { + IntArray copyOnWriteArray = new IntArray(nativeTids.size() + 1); + copyOnWriteArray.addAll(nativeTids); + copyOnWriteArray.add(-index - 1, tid); + mNativeTids = copyOnWriteArray; + } + } + } + /** * This method is expensive to call. */ @@ -505,6 +534,17 @@ public class BinderCallsStats implements BinderInternal.Observer { return Binder.getCallingUid(); } + protected int getNativeTid() { + return Binder.getNativeTid(); + } + + /** + * Returns known Linux TIDs for threads taking incoming binder calls. + */ + public int[] getNativeTids() { + return mNativeTids.toArray(); + } + protected long getElapsedRealtimeMicro() { return SystemClock.elapsedRealtimeNanos() / 1000; } diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp index 4c9d7abeb5a1a..aef53a4b33182 100644 --- a/core/jni/android_util_Binder.cpp +++ b/core/jni/android_util_Binder.cpp @@ -30,6 +30,7 @@ #include #include +#include #include #include #include @@ -1047,6 +1048,10 @@ static void android_os_Binder_setExtension(JNIEnv* env, jobject obj, jobject ext jbh->setExtension(extension); } +JNIEXPORT jint JNICALL android_os_Binder_getNativeTid(JNIEnv* env, jobject clazz) { + return (jint)android::base::GetThreadId(); +} + // ---------------------------------------------------------------------------- static const JNINativeMethod gBinderMethods[] = { @@ -1078,6 +1083,7 @@ static const JNINativeMethod gBinderMethods[] = { { "blockUntilThreadAvailable", "()V", (void*)android_os_Binder_blockUntilThreadAvailable }, { "getExtension", "()Landroid/os/IBinder;", (void*)android_os_Binder_getExtension }, { "setExtension", "(Landroid/os/IBinder;)V", (void*)android_os_Binder_setExtension }, + { "getNativeTid", "()I", (void*)android_os_Binder_getNativeTid }, }; const char* const kBinderPathName = "android/os/Binder"; diff --git a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java index b7d1c9b993d9f..a5117a3e7cc36 100644 --- a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java @@ -807,6 +807,38 @@ public class BinderCallsStatsTest { } } + @Test + public void testNativeTids() { + TestBinderCallsStats bcs = new TestBinderCallsStats(); + Binder binder = new Binder(); + + bcs.nativeTid = 3; + + CallSession callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID); + bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID); + + bcs.nativeTid = 1; + + callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID); + bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID); + + bcs.nativeTid = 1; + + callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID); + bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID); + + bcs.nativeTid = 2; + + callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID); + bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID); + + int[] tids = bcs.getNativeTids(); + assertEquals(3, tids.length); + assertEquals(1, tids[0]); + assertEquals(2, tids[1]); + assertEquals(3, tids[2]); + } + private static class TestHandler extends Handler { ArrayList mRunnables = new ArrayList<>(); @@ -825,6 +857,7 @@ public class BinderCallsStatsTest { public int callingUid = CALLING_UID; public long time = 1234; public long elapsedTime = 0; + public int nativeTid; TestBinderCallsStats() { this(mDeviceState); @@ -874,6 +907,10 @@ public class BinderCallsStatsTest { protected void setCallingUid(int uid) { callingUid = uid; } - } + @Override + protected int getNativeTid() { + return nativeTid; + } + } }