Log the excessive incoming binder proxies into statsd
This CL introduces a new "warning" callback on excessive binder
proxies, where if the total count of binder proxies from certain UID
goes above the threshold, the callback will be triggered. This is
very similar to the existing binder proxy high watermark callback.
We're logging this warning event into statsd, where it may trigger
perfetto dump, before it reaches to the high watermark and gets killed
because of that.
This callback signal could also be used as a hint to the GC in
the future, which could effectively release the references to
remote binder stubs.
(cherry-picked from ag/25078745)
Flag: ACONFIG log_excessive_binder_proxies DEVELOPMENT
Bug: 298263955
Test: atest FrameworksCoreTests:android.os.BinderProxyCountingTest
Merged-In: I3c959a7dd62ee57415c22a8034d774fe49a8f64f
Change-Id: I2eac629a1a63877adc432b23aae36d83a93c538e
diff --git a/core/java/com/android/internal/os/BinderInternal.java b/core/java/com/android/internal/os/BinderInternal.java
index c14d8d8..8063be6 100644
--- a/core/java/com/android/internal/os/BinderInternal.java
+++ b/core/java/com/android/internal/os/BinderInternal.java
@@ -45,8 +45,8 @@
static ArrayList<Runnable> sGcWatchers = new ArrayList<>();
static Runnable[] sTmpWatchers = new Runnable[1];
static long sLastGcTime;
- static final BinderProxyLimitListenerDelegate sBinderProxyLimitListenerDelegate =
- new BinderProxyLimitListenerDelegate();
+ static final BinderProxyCountEventListenerDelegate sBinderProxyCountEventListenerDelegate =
+ new BinderProxyCountEventListenerDelegate();
static final class GcWatcher {
@Override
@@ -226,15 +226,24 @@
* @param low The threshold a binder count must drop below before the callback
* can be called again. (This is to avoid many repeated calls to the
* callback in a brief period of time)
+ * @param warning The threshold between {@code high} and {@code low} where if the binder count
+ * exceeds that, the warning callback would be triggered.
*/
- public static final native void nSetBinderProxyCountWatermarks(int high, int low);
+ public static final native void nSetBinderProxyCountWatermarks(int high, int low, int warning);
/**
* Interface for callback invocation when the Binder Proxy limit is reached. onLimitReached will
* be called with the uid of the app causing too many Binder Proxies
*/
- public interface BinderProxyLimitListener {
+ public interface BinderProxyCountEventListener {
public void onLimitReached(int uid);
+
+ /**
+ * Call when the number of binder proxies from the uid of the app reaches
+ * the warning threshold.
+ */
+ default void onWarningThresholdReached(int uid) {
+ }
}
/**
@@ -243,7 +252,17 @@
* @param uid The uid of the bad behaving app sending too many binders
*/
public static void binderProxyLimitCallbackFromNative(int uid) {
- sBinderProxyLimitListenerDelegate.notifyClient(uid);
+ sBinderProxyCountEventListenerDelegate.notifyLimitReached(uid);
+ }
+
+ /**
+ * Callback used by native code to trigger a callback in java code. The callback will be
+ * triggered when too many binder proxies from a uid hits the warning limit.
+ * @param uid The uid of the bad behaving app sending too many binders
+ */
+ @SuppressWarnings("unused")
+ public static void binderProxyWarningCallbackFromNative(int uid) {
+ sBinderProxyCountEventListenerDelegate.notifyWarningReached(uid);
}
/**
@@ -252,41 +271,45 @@
* @param handler must not be null, callback will be posted through the handler;
*
*/
- public static void setBinderProxyCountCallback(BinderProxyLimitListener listener,
+ public static void setBinderProxyCountCallback(BinderProxyCountEventListener listener,
@NonNull Handler handler) {
Preconditions.checkNotNull(handler,
"Must provide NonNull Handler to setBinderProxyCountCallback when setting "
- + "BinderProxyLimitListener");
- sBinderProxyLimitListenerDelegate.setListener(listener, handler);
+ + "BinderProxyCountEventListener");
+ sBinderProxyCountEventListenerDelegate.setListener(listener, handler);
}
/**
* Clear the Binder Proxy callback
*/
public static void clearBinderProxyCountCallback() {
- sBinderProxyLimitListenerDelegate.setListener(null, null);
+ sBinderProxyCountEventListenerDelegate.setListener(null, null);
}
- static private class BinderProxyLimitListenerDelegate {
- private BinderProxyLimitListener mBinderProxyLimitListener;
+ private static class BinderProxyCountEventListenerDelegate {
+ private BinderProxyCountEventListener mBinderProxyCountEventListener;
private Handler mHandler;
- void setListener(BinderProxyLimitListener listener, Handler handler) {
+ void setListener(BinderProxyCountEventListener listener, Handler handler) {
synchronized (this) {
- mBinderProxyLimitListener = listener;
+ mBinderProxyCountEventListener = listener;
mHandler = handler;
}
}
- void notifyClient(final int uid) {
+ void notifyLimitReached(final int uid) {
synchronized (this) {
- if (mBinderProxyLimitListener != null) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- mBinderProxyLimitListener.onLimitReached(uid);
- }
- });
+ if (mBinderProxyCountEventListener != null) {
+ mHandler.post(() -> mBinderProxyCountEventListener.onLimitReached(uid));
+ }
+ }
+ }
+
+ void notifyWarningReached(final int uid) {
+ synchronized (this) {
+ if (mBinderProxyCountEventListener != null) {
+ mHandler.post(() ->
+ mBinderProxyCountEventListener.onWarningThresholdReached(uid));
}
}
}
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index d2d5186..2068bd7 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -88,6 +88,7 @@
jclass mClass;
jmethodID mForceGc;
jmethodID mProxyLimitCallback;
+ jmethodID mProxyWarningCallback;
} gBinderInternalOffsets;
@@ -1240,7 +1241,7 @@
gCollectedAtRefs = gNumLocalRefsCreated + gNumDeathRefsCreated;
}
-static void android_os_BinderInternal_proxyLimitcallback(int uid)
+static void android_os_BinderInternal_proxyLimitCallback(int uid)
{
JNIEnv *env = AndroidRuntime::getJNIEnv();
env->CallStaticVoidMethod(gBinderInternalOffsets.mClass,
@@ -1254,6 +1255,20 @@
}
}
+static void android_os_BinderInternal_proxyWarningCallback(int uid)
+{
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+ env->CallStaticVoidMethod(gBinderInternalOffsets.mClass,
+ gBinderInternalOffsets.mProxyWarningCallback,
+ uid);
+
+ if (env->ExceptionCheck()) {
+ ScopedLocalRef<jthrowable> excep(env, env->ExceptionOccurred());
+ binder_report_exception(env, excep.get(),
+ "*** Uncaught exception in binderProxyWarningCallbackFromNative");
+ }
+}
+
static void android_os_BinderInternal_setBinderProxyCountEnabled(JNIEnv* env, jobject clazz,
jboolean enable)
{
@@ -1278,9 +1293,10 @@
}
static void android_os_BinderInternal_setBinderProxyCountWatermarks(JNIEnv* env, jobject clazz,
- jint high, jint low)
+ jint high, jint low,
+ jint warning)
{
- BpBinder::setBinderProxyCountWatermarks(high, low);
+ BpBinder::setBinderProxyCountWatermarks(high, low, warning);
}
// ----------------------------------------------------------------------------
@@ -1295,7 +1311,7 @@
{ "nSetBinderProxyCountEnabled", "(Z)V", (void*)android_os_BinderInternal_setBinderProxyCountEnabled },
{ "nGetBinderProxyPerUidCounts", "()Landroid/util/SparseIntArray;", (void*)android_os_BinderInternal_getBinderProxyPerUidCounts },
{ "nGetBinderProxyCount", "(I)I", (void*)android_os_BinderInternal_getBinderProxyCount },
- { "nSetBinderProxyCountWatermarks", "(II)V", (void*)android_os_BinderInternal_setBinderProxyCountWatermarks}
+ { "nSetBinderProxyCountWatermarks", "(III)V", (void*)android_os_BinderInternal_setBinderProxyCountWatermarks}
};
const char* const kBinderInternalPathName = "com/android/internal/os/BinderInternal";
@@ -1307,6 +1323,8 @@
gBinderInternalOffsets.mClass = MakeGlobalRefOrDie(env, clazz);
gBinderInternalOffsets.mForceGc = GetStaticMethodIDOrDie(env, clazz, "forceBinderGc", "()V");
gBinderInternalOffsets.mProxyLimitCallback = GetStaticMethodIDOrDie(env, clazz, "binderProxyLimitCallbackFromNative", "(I)V");
+ gBinderInternalOffsets.mProxyWarningCallback =
+ GetStaticMethodIDOrDie(env, clazz, "binderProxyWarningCallbackFromNative", "(I)V");
jclass SparseIntArrayClass = FindClassOrDie(env, "android/util/SparseIntArray");
gSparseIntArrayOffsets.classObject = MakeGlobalRefOrDie(env, SparseIntArrayClass);
@@ -1315,7 +1333,8 @@
gSparseIntArrayOffsets.put = GetMethodIDOrDie(env, gSparseIntArrayOffsets.classObject, "put",
"(II)V");
- BpBinder::setLimitCallback(android_os_BinderInternal_proxyLimitcallback);
+ BpBinder::setBinderProxyCountEventCallback(android_os_BinderInternal_proxyLimitCallback,
+ android_os_BinderInternal_proxyWarningCallback);
return RegisterMethodsOrDie(
env, kBinderInternalPathName,
diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp
index 1a3ec27..871feb6 100644
--- a/core/tests/coretests/Android.bp
+++ b/core/tests/coretests/Android.bp
@@ -116,6 +116,8 @@
":BinderDeathRecipientHelperApp1",
":BinderDeathRecipientHelperApp2",
":com.android.cts.helpers.aosp",
+ ":BinderProxyCountingTestApp",
+ ":BinderProxyCountingTestService",
],
}
diff --git a/core/tests/coretests/AndroidTest.xml b/core/tests/coretests/AndroidTest.xml
index 05b309b..bf2a5b8 100644
--- a/core/tests/coretests/AndroidTest.xml
+++ b/core/tests/coretests/AndroidTest.xml
@@ -22,6 +22,8 @@
<option name="test-file-name" value="FrameworksCoreTests.apk" />
<option name="test-file-name" value="BinderDeathRecipientHelperApp1.apk" />
<option name="test-file-name" value="BinderDeathRecipientHelperApp2.apk" />
+ <option name="test-file-name" value="BinderProxyCountingTestApp.apk" />
+ <option name="test-file-name" value="BinderProxyCountingTestService.apk" />
</target_preparer>
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
diff --git a/core/tests/coretests/BinderProxyCountingTestApp/AndroidManifest.xml b/core/tests/coretests/BinderProxyCountingTestApp/AndroidManifest.xml
index a971730..c8407b8 100644
--- a/core/tests/coretests/BinderProxyCountingTestApp/AndroidManifest.xml
+++ b/core/tests/coretests/BinderProxyCountingTestApp/AndroidManifest.xml
@@ -16,6 +16,9 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.frameworks.coretests.binderproxycountingtestapp">
+ <queries>
+ <package android:name="com.android.frameworks.coretests.binderproxycountingtestservice" />
+ </queries>
<application>
<service android:name=".BpcTestAppCmdService"
android:exported="true"/>
diff --git a/core/tests/coretests/BinderProxyCountingTestApp/src/com/android/frameworks/coretests/binderproxycountingtestapp/BpcTestAppCmdService.java b/core/tests/coretests/BinderProxyCountingTestApp/src/com/android/frameworks/coretests/binderproxycountingtestapp/BpcTestAppCmdService.java
index 5aae1203..a7e97d3 100644
--- a/core/tests/coretests/BinderProxyCountingTestApp/src/com/android/frameworks/coretests/binderproxycountingtestapp/BpcTestAppCmdService.java
+++ b/core/tests/coretests/BinderProxyCountingTestApp/src/com/android/frameworks/coretests/binderproxycountingtestapp/BpcTestAppCmdService.java
@@ -17,14 +17,15 @@
package com.android.frameworks.coretests.binderproxycountingtestapp;
import android.app.Service;
-import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
-import android.content.IntentFilter;
import android.content.ServiceConnection;
+import android.database.ContentObserver;
+import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
+import android.provider.Settings;
import android.util.Log;
import com.android.frameworks.coretests.aidl.IBinderProxyCountingService;
@@ -49,24 +50,20 @@
private IBpcTestAppCmdService.Stub mBinder = new IBpcTestAppCmdService.Stub() {
- private ArrayList<BroadcastReceiver> mBrList = new ArrayList();
+ private ArrayList<ContentObserver> mCoList = new ArrayList();
private ArrayList<ITestRemoteCallback> mTrcList = new ArrayList();
+ private Handler mHandler = new Handler();
@Override
public void createSystemBinders(int count) {
int i = 0;
while (i++ < count) {
- BroadcastReceiver br = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
-
- }
- };
- IntentFilter filt = new IntentFilter(Intent.ACTION_POWER_DISCONNECTED);
- synchronized (mBrList) {
- mBrList.add(br);
+ final ContentObserver co = new ContentObserver(mHandler) {};
+ synchronized (mCoList) {
+ mCoList.add(co);
}
- registerReceiver(br, filt);
+ getContentResolver().registerContentObserver(
+ Settings.System.CONTENT_URI, false, co);
}
}
@@ -74,11 +71,11 @@
public void releaseSystemBinders(int count) {
int i = 0;
while (i++ < count) {
- BroadcastReceiver br;
- synchronized (mBrList) {
- br = mBrList.remove(0);
+ ContentObserver co;
+ synchronized (mCoList) {
+ co = mCoList.remove(0);
}
- unregisterReceiver(br);
+ getContentResolver().unregisterContentObserver(co);
}
}
@@ -117,9 +114,9 @@
@Override
public void releaseAllBinders() {
- synchronized (mBrList) {
- while (mBrList.size() > 0) {
- unregisterReceiver(mBrList.remove(0));
+ synchronized (mCoList) {
+ while (mCoList.size() > 0) {
+ getContentResolver().unregisterContentObserver(mCoList.remove(0));
}
}
synchronized (mTrcList) {
@@ -179,4 +176,4 @@
public IBinder onBind(Intent intent) {
return mBinder;
}
-}
\ No newline at end of file
+}
diff --git a/core/tests/coretests/BinderProxyCountingTestService/src/com/android/frameworks/coretests/binderproxycountingtestservice/BpcTestServiceCmdService.java b/core/tests/coretests/BinderProxyCountingTestService/src/com/android/frameworks/coretests/binderproxycountingtestservice/BpcTestServiceCmdService.java
index 6bed2a2..0f1accc 100644
--- a/core/tests/coretests/BinderProxyCountingTestService/src/com/android/frameworks/coretests/binderproxycountingtestservice/BpcTestServiceCmdService.java
+++ b/core/tests/coretests/BinderProxyCountingTestService/src/com/android/frameworks/coretests/binderproxycountingtestservice/BpcTestServiceCmdService.java
@@ -55,8 +55,8 @@
}
@Override
- public void setBinderProxyWatermarks(int high, int low) {
- BinderInternal.nSetBinderProxyCountWatermarks(high, low);
+ public void setBinderProxyWatermarks(int high, int low, int warning) {
+ BinderInternal.nSetBinderProxyCountWatermarks(high, low, warning);
}
@Override
@@ -68,12 +68,23 @@
public void setBinderProxyCountCallback(IBpcCallbackObserver observer) {
if (observer != null) {
BinderInternal.setBinderProxyCountCallback(
- new BinderInternal.BinderProxyLimitListener() {
+ new BinderInternal.BinderProxyCountEventListener() {
@Override
public void onLimitReached(int uid) {
try {
synchronized (observer) {
- observer.onCallback(uid);
+ observer.onLimitReached(uid);
+ }
+ } catch (Exception e) {
+ Log.e(TAG, e.toString());
+ }
+ }
+
+ @Override
+ public void onWarningThresholdReached(int uid) {
+ try {
+ synchronized (observer) {
+ observer.onWarningThresholdReached(uid);
}
} catch (Exception e) {
Log.e(TAG, e.toString());
@@ -98,4 +109,4 @@
mHandlerThread.start();
mHandler = new Handler(mHandlerThread.getLooper());
}
-}
\ No newline at end of file
+}
diff --git a/core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/IBpcCallbackObserver.aidl b/core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/IBpcCallbackObserver.aidl
index c4ebd56..ada7d92 100644
--- a/core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/IBpcCallbackObserver.aidl
+++ b/core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/IBpcCallbackObserver.aidl
@@ -17,5 +17,6 @@
package com.android.frameworks.coretests.aidl;
interface IBpcCallbackObserver {
- void onCallback(int uid);
-}
\ No newline at end of file
+ void onLimitReached(int uid);
+ void onWarningThresholdReached(int uid);
+}
diff --git a/core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/IBpcTestServiceCmdService.aidl b/core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/IBpcTestServiceCmdService.aidl
index abdab41..cdcda9d 100644
--- a/core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/IBpcTestServiceCmdService.aidl
+++ b/core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/IBpcTestServiceCmdService.aidl
@@ -20,7 +20,7 @@
interface IBpcTestServiceCmdService {
void forceGc();
int getBinderProxyCount(int uid);
- void setBinderProxyWatermarks(int high, int low);
+ void setBinderProxyWatermarks(int high, int low, int warning);
void enableBinderProxyLimit(boolean enable);
void setBinderProxyCountCallback(IBpcCallbackObserver observer);
-}
\ No newline at end of file
+}
diff --git a/core/tests/coretests/src/android/os/BinderProxyCountingTest.java b/core/tests/coretests/src/android/os/BinderProxyCountingTest.java
index bcd9521..84d2995 100644
--- a/core/tests/coretests/src/android/os/BinderProxyCountingTest.java
+++ b/core/tests/coretests/src/android/os/BinderProxyCountingTest.java
@@ -88,9 +88,10 @@
private static final int BIND_SERVICE_TIMEOUT_SEC = 5;
private static final int TOO_MANY_BINDERS_TIMEOUT_SEC = 2;
+ private static final int TOO_MANY_BINDERS_WITH_KILL_TIMEOUT_SEC = 30;
- // Keep in sync with sBinderProxyCountLimit in BpBinder.cpp
- private static final int BINDER_PROXY_LIMIT = 2500;
+ // Keep in sync with BINDER_PROXY_HIGH_WATERMARK in ActivityManagerService.java
+ private static final int BINDER_PROXY_LIMIT = 6000;
private static Context sContext;
private static UiDevice sUiDevice;
@@ -175,18 +176,26 @@
}
}
- private CountDownLatch createBinderLimitLatch() throws RemoteException {
- final CountDownLatch latch = new CountDownLatch(1);
+ private CountDownLatch[] createBinderLimitLatch() throws RemoteException {
+ final CountDownLatch[] latches = new CountDownLatch[] {
+ new CountDownLatch(1), new CountDownLatch(1)
+ };
sBpcTestServiceCmdService.setBinderProxyCountCallback(
new IBpcCallbackObserver.Stub() {
@Override
- public void onCallback(int uid) {
+ public void onLimitReached(int uid) {
if (uid == sTestPkgUid) {
- latch.countDown();
+ latches[0].countDown();
+ }
+ }
+ @Override
+ public void onWarningThresholdReached(int uid) {
+ if (uid == sTestPkgUid) {
+ latches[1].countDown();
}
}
});
- return latch;
+ return latches;
}
/**
@@ -227,6 +236,7 @@
@Test
public void testBinderProxyLimitBoundary() throws Exception {
final int binderProxyLimit = 2000;
+ final int binderProxyWarning = 1900;
final int rearmThreshold = 1800;
try {
sTestAppConnection = bindService(sTestAppConsumer, sTestAppIntent);
@@ -238,19 +248,33 @@
// Get the baseline of binders naturally held by the test Package
int baseBinderCount = sBpcTestServiceCmdService.getBinderProxyCount(sTestPkgUid);
- final CountDownLatch binderLimitLatch = createBinderLimitLatch();
- sBpcTestServiceCmdService.setBinderProxyWatermarks(binderProxyLimit, rearmThreshold);
+ final CountDownLatch[] binderLatches = createBinderLimitLatch();
+ sBpcTestServiceCmdService.setBinderProxyWatermarks(binderProxyLimit, rearmThreshold,
+ binderProxyWarning);
+
+ // Create Binder Proxies up to the warning;
+ sBpcTestAppCmdService.createTestBinders(binderProxyWarning - baseBinderCount);
+ if (binderLatches[1].await(TOO_MANY_BINDERS_TIMEOUT_SEC, TimeUnit.SECONDS)) {
+ fail("Received BinderProxyLimitCallback for uid " + sTestPkgUid
+ + " when proxy warning should not have been triggered");
+ }
+
+ // Create one more Binder to trigger the warning
+ sBpcTestAppCmdService.createTestBinders(1);
+ if (!binderLatches[1].await(TOO_MANY_BINDERS_TIMEOUT_SEC, TimeUnit.SECONDS)) {
+ fail("Timed out waiting for uid " + sTestPkgUid + " to trigger the warning");
+ }
// Create Binder Proxies up to the limit
- sBpcTestAppCmdService.createTestBinders(binderProxyLimit - baseBinderCount);
- if (binderLimitLatch.await(TOO_MANY_BINDERS_TIMEOUT_SEC, TimeUnit.SECONDS)) {
+ sBpcTestAppCmdService.createTestBinders(binderProxyLimit - binderProxyWarning - 1);
+ if (binderLatches[0].await(TOO_MANY_BINDERS_TIMEOUT_SEC, TimeUnit.SECONDS)) {
fail("Received BinderProxyLimitCallback for uid " + sTestPkgUid
+ " when proxy limit should not have been reached");
}
// Create one more Binder to cross the limit
sBpcTestAppCmdService.createTestBinders(1);
- if (!binderLimitLatch.await(TOO_MANY_BINDERS_TIMEOUT_SEC, TimeUnit.SECONDS)) {
+ if (!binderLatches[0].await(TOO_MANY_BINDERS_TIMEOUT_SEC, TimeUnit.SECONDS)) {
fail("Timed out waiting for uid " + sTestPkgUid + " to hit limit");
}
@@ -274,12 +298,20 @@
sBpcTestServiceCmdService.forceGc();
int baseBinderCount = sBpcTestServiceCmdService.getBinderProxyCount(sTestPkgUid);
for (int testLimit : testLimits) {
- final CountDownLatch binderLimitLatch = createBinderLimitLatch();
+ final CountDownLatch[] binderLatches = createBinderLimitLatch();
// Change the BinderProxyLimit
- sBpcTestServiceCmdService.setBinderProxyWatermarks(testLimit, baseBinderCount + 10);
+ sBpcTestServiceCmdService.setBinderProxyWatermarks(testLimit, baseBinderCount + 10,
+ testLimit - 10);
+
+ // Trigger the new Binder Proxy warning
+ sBpcTestAppCmdService.createTestBinders(testLimit - 9 - baseBinderCount);
+ if (!binderLatches[1].await(TOO_MANY_BINDERS_TIMEOUT_SEC, TimeUnit.SECONDS)) {
+ fail("Timed out waiting for uid " + sTestPkgUid + " to trigger the warning");
+ }
+
// Exceed the new Binder Proxy Limit
- sBpcTestAppCmdService.createTestBinders(testLimit + 1);
- if (!binderLimitLatch.await(TOO_MANY_BINDERS_TIMEOUT_SEC, TimeUnit.SECONDS)) {
+ sBpcTestAppCmdService.createTestBinders(10);
+ if (!binderLatches[0].await(TOO_MANY_BINDERS_TIMEOUT_SEC, TimeUnit.SECONDS)) {
fail("Timed out waiting for uid " + sTestPkgUid + " to hit limit");
}
@@ -297,6 +329,7 @@
public void testRearmCallbackThreshold() throws Exception {
final int binderProxyLimit = 2000;
final int exceedBinderProxyLimit = binderProxyLimit + 10;
+ final int binderProxyWarning = 1900;
final int rearmThreshold = 1800;
try {
sTestAppConnection = bindService(sTestAppConsumer, sTestAppIntent);
@@ -305,11 +338,19 @@
sBpcTestServiceCmdService.enableBinderProxyLimit(true);
sBpcTestServiceCmdService.forceGc();
- final CountDownLatch firstBinderLimitLatch = createBinderLimitLatch();
- sBpcTestServiceCmdService.setBinderProxyWatermarks(binderProxyLimit, rearmThreshold);
+ int baseBinderCount = sBpcTestServiceCmdService.getBinderProxyCount(sTestPkgUid);
+ final CountDownLatch[] firstBinderLatches = createBinderLimitLatch();
+ sBpcTestServiceCmdService.setBinderProxyWatermarks(binderProxyLimit, rearmThreshold,
+ binderProxyWarning);
+ // Trigger the Binder Proxy Waring
+ sBpcTestAppCmdService.createTestBinders(binderProxyWarning - baseBinderCount + 1);
+ if (!firstBinderLatches[1].await(TOO_MANY_BINDERS_TIMEOUT_SEC, TimeUnit.SECONDS)) {
+ fail("Timed out waiting for uid " + sTestPkgUid + " to trigger warning");
+ }
+
// Exceed the Binder Proxy Limit
- sBpcTestAppCmdService.createTestBinders(exceedBinderProxyLimit);
- if (!firstBinderLimitLatch.await(TOO_MANY_BINDERS_TIMEOUT_SEC, TimeUnit.SECONDS)) {
+ sBpcTestAppCmdService.createTestBinders(exceedBinderProxyLimit - binderProxyWarning);
+ if (!firstBinderLatches[0].await(TOO_MANY_BINDERS_TIMEOUT_SEC, TimeUnit.SECONDS)) {
fail("Timed out waiting for uid " + sTestPkgUid + " to hit limit");
}
@@ -321,11 +362,20 @@
sBpcTestServiceCmdService.forceGc();
currentBinderCount = sBpcTestServiceCmdService.getBinderProxyCount(sTestPkgUid);
- final CountDownLatch secondBinderLimitLatch = createBinderLimitLatch();
+ final CountDownLatch[] secondBinderLatches = createBinderLimitLatch();
+
+ // Exceed the Binder Proxy warning which should not cause a callback since there has
+ // been no rearm
+ sBpcTestAppCmdService.createTestBinders(binderProxyWarning - currentBinderCount + 1);
+ if (secondBinderLatches[1].await(TOO_MANY_BINDERS_TIMEOUT_SEC, TimeUnit.SECONDS)) {
+ fail("Received BinderProxyLimitCallback for uid " + sTestPkgUid
+ + " when the callback has not been rearmed yet");
+ }
+
// Exceed the Binder Proxy limit which should not cause a callback since there has
// been no rearm
- sBpcTestAppCmdService.createTestBinders(exceedBinderProxyLimit - currentBinderCount);
- if (secondBinderLimitLatch.await(TOO_MANY_BINDERS_TIMEOUT_SEC, TimeUnit.SECONDS)) {
+ sBpcTestAppCmdService.createTestBinders(exceedBinderProxyLimit - binderProxyWarning);
+ if (secondBinderLatches[0].await(TOO_MANY_BINDERS_TIMEOUT_SEC, TimeUnit.SECONDS)) {
fail("Received BinderProxyLimitCallback for uid " + sTestPkgUid
+ " when the callback has not been rearmed yet");
}
@@ -337,10 +387,16 @@
sBpcTestServiceCmdService.forceGc();
currentBinderCount = sBpcTestServiceCmdService.getBinderProxyCount(sTestPkgUid);
+ // Trigger the Binder Proxy Waring
+ sBpcTestAppCmdService.createTestBinders(binderProxyWarning - currentBinderCount + 1);
+ if (!secondBinderLatches[1].await(TOO_MANY_BINDERS_TIMEOUT_SEC, TimeUnit.SECONDS)) {
+ fail("Timed out waiting for uid " + sTestPkgUid + " to trigger warning");
+ }
+
// Exceed the Binder Proxy limit for the last time
sBpcTestAppCmdService.createTestBinders(exceedBinderProxyLimit - currentBinderCount);
- if (!secondBinderLimitLatch.await(TOO_MANY_BINDERS_TIMEOUT_SEC, TimeUnit.SECONDS)) {
+ if (!secondBinderLatches[0].await(TOO_MANY_BINDERS_TIMEOUT_SEC, TimeUnit.SECONDS)) {
fail("Timed out waiting for uid " + sTestPkgUid + " to hit limit");
}
sBpcTestAppCmdService.releaseTestBinders(currentBinderCount);
@@ -373,7 +429,7 @@
// is not unexpected
}
- if (!binderDeathLatch.await(TOO_MANY_BINDERS_TIMEOUT_SEC, TimeUnit.SECONDS)) {
+ if (!binderDeathLatch.await(TOO_MANY_BINDERS_WITH_KILL_TIMEOUT_SEC, TimeUnit.SECONDS)) {
sBpcTestAppCmdService.releaseSystemBinders(exceedBinderProxyLimit);
fail("Timed out waiting for uid " + sTestPkgUid + " to die.");
}
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIService.java b/packages/SystemUI/src/com/android/systemui/SystemUIService.java
index 76c2282..d596288 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIService.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIService.java
@@ -99,9 +99,10 @@
if (Build.IS_DEBUGGABLE) {
// b/71353150 - looking for leaked binder proxies
BinderInternal.nSetBinderProxyCountEnabled(true);
- BinderInternal.nSetBinderProxyCountWatermarks(1000,900);
+ BinderInternal.nSetBinderProxyCountWatermarks(
+ /* high= */ 1000, /* low= */ 900, /* warning= */ 950);
BinderInternal.setBinderProxyCountCallback(
- new BinderInternal.BinderProxyLimitListener() {
+ new BinderInternal.BinderProxyCountEventListener() {
@Override
public void onLimitReached(int uid) {
Slog.w(SystemUIApplication.TAG,
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 09c1dea..c93f177 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -413,6 +413,7 @@
import com.android.internal.os.BinderCallHeavyHitterWatcher.BinderCallHeavyHitterListener;
import com.android.internal.os.BinderCallHeavyHitterWatcher.HeavyHitterContainer;
import com.android.internal.os.BinderInternal;
+import com.android.internal.os.BinderInternal.BinderProxyCountEventListener;
import com.android.internal.os.BinderTransactionNameResolver;
import com.android.internal.os.ByteTransferPipe;
import com.android.internal.os.IResultReceiver;
@@ -610,8 +611,8 @@
private static final int MINIMUM_MEMORY_GROWTH_THRESHOLD = 10 * 1000; // 10 MB
/**
- * The number of binder proxies we need to have before we start warning and
- * dumping debug info.
+ * The number of binder proxies we need to have before we start dumping debug info
+ * and kill the offenders.
*/
private static final int BINDER_PROXY_HIGH_WATERMARK = 6000;
@@ -621,6 +622,11 @@
*/
private static final int BINDER_PROXY_LOW_WATERMARK = 5500;
+ /**
+ * The number of binder proxies we need to have before we start warning.
+ */
+ private static final int BINDER_PROXY_WARNING_WATERMARK = 5750;
+
// Max character limit for a notification title. If the notification title is larger than this
// the notification will not be legible to the user.
private static final int MAX_BUGREPORT_TITLE_SIZE = 100;
@@ -8926,33 +8932,10 @@
t.traceBegin("setBinderProxies");
BinderInternal.nSetBinderProxyCountWatermarks(BINDER_PROXY_HIGH_WATERMARK,
- BINDER_PROXY_LOW_WATERMARK);
+ BINDER_PROXY_LOW_WATERMARK, BINDER_PROXY_WARNING_WATERMARK);
BinderInternal.nSetBinderProxyCountEnabled(true);
- BinderInternal.setBinderProxyCountCallback(
- (uid) -> {
- Slog.wtf(TAG, "Uid " + uid + " sent too many Binders to uid "
- + Process.myUid());
- BinderProxy.dumpProxyDebugInfo();
- if (uid == Process.SYSTEM_UID) {
- Slog.i(TAG, "Skipping kill (uid is SYSTEM)");
- } else {
- killUid(UserHandle.getAppId(uid), UserHandle.getUserId(uid),
- ApplicationExitInfo.REASON_EXCESSIVE_RESOURCE_USAGE,
- ApplicationExitInfo.SUBREASON_EXCESSIVE_BINDER_OBJECTS,
- "Too many Binders sent to SYSTEM");
- // We need to run a GC here, because killing the processes involved
- // actually isn't guaranteed to free up the proxies; in fact, if the
- // GC doesn't run for a long time, we may even exceed the global
- // proxy limit for a process (20000), resulting in system_server itself
- // being killed.
- // Note that the GC here might not actually clean up all the proxies,
- // because the binder reference decrements will come in asynchronously;
- // but if new processes belonging to the UID keep adding proxies, we
- // will get another callback here, and run the GC again - this time
- // cleaning up the old proxies.
- VMRuntime.getRuntime().requestConcurrentGC();
- }
- }, BackgroundThread.getHandler());
+ BinderInternal.setBinderProxyCountCallback(new MyBinderProxyCountEventListener(),
+ BackgroundThread.getHandler());
t.traceEnd(); // setBinderProxies
t.traceEnd(); // ActivityManagerStartApps
@@ -8967,6 +8950,45 @@
}
}
+ private class MyBinderProxyCountEventListener implements BinderProxyCountEventListener {
+ @Override
+ public void onLimitReached(int uid) {
+ Slog.wtf(TAG, "Uid " + uid + " sent too many Binders to uid "
+ + Process.myUid());
+ BinderProxy.dumpProxyDebugInfo();
+ if (uid == Process.SYSTEM_UID) {
+ Slog.i(TAG, "Skipping kill (uid is SYSTEM)");
+ } else {
+ killUid(UserHandle.getAppId(uid), UserHandle.getUserId(uid),
+ ApplicationExitInfo.REASON_EXCESSIVE_RESOURCE_USAGE,
+ ApplicationExitInfo.SUBREASON_EXCESSIVE_BINDER_OBJECTS,
+ "Too many Binders sent to SYSTEM");
+ // We need to run a GC here, because killing the processes involved
+ // actually isn't guaranteed to free up the proxies; in fact, if the
+ // GC doesn't run for a long time, we may even exceed the global
+ // proxy limit for a process (20000), resulting in system_server itself
+ // being killed.
+ // Note that the GC here might not actually clean up all the proxies,
+ // because the binder reference decrements will come in asynchronously;
+ // but if new processes belonging to the UID keep adding proxies, we
+ // will get another callback here, and run the GC again - this time
+ // cleaning up the old proxies.
+ VMRuntime.getRuntime().requestConcurrentGC();
+ }
+ }
+
+ @Override
+ public void onWarningThresholdReached(int uid) {
+ if (com.android.server.am.Flags.logExcessiveBinderProxies()) {
+ Slog.w(TAG, "Uid " + uid + " sent too many ("
+ + BINDER_PROXY_WARNING_WATERMARK + ") Binders to uid " + Process.myUid());
+ FrameworkStatsLog.write(
+ FrameworkStatsLog.EXCESSIVE_BINDER_PROXY_COUNT_REPORTED,
+ uid);
+ }
+ }
+ }
+
private void watchDeviceProvisioning(Context context) {
// setting system property based on whether device is provisioned