Merge changes from topic "bug_298263955_proxy_warning" into main am: ad2da357f8

Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/3023864

Change-Id: I51fa78d30b1afd87aea45a5c45bde50976e70dc3
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
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 0711b71..d35892e 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