Java side setup and access to Binder Proxy Tracking

Expose Binder Proxy Tracking by Uid from the native side. Enable
 tracking for SYSTEM and killing of any bad behaving uids.

Change-Id: Ifd6d0f30a93fad406417dd83c1495c105bced974
Fixes: 63901963
Test: bit FrameworksCoreTests:android.os.BinderProxyCountingTest
diff --git a/core/java/com/android/internal/os/BinderInternal.java b/core/java/com/android/internal/os/BinderInternal.java
index ea4575a..5bddd2f 100644
--- a/core/java/com/android/internal/os/BinderInternal.java
+++ b/core/java/com/android/internal/os/BinderInternal.java
@@ -16,9 +16,15 @@
 
 package com.android.internal.os;
 
+import android.annotation.NonNull;
+import android.os.Handler;
 import android.os.IBinder;
 import android.os.SystemClock;
 import android.util.EventLog;
+import android.util.Log;
+import android.util.SparseIntArray;
+
+import com.android.internal.util.Preconditions;
 
 import dalvik.system.VMRuntime;
 
@@ -31,11 +37,14 @@
  * @see IBinder
  */
 public class BinderInternal {
+    private static final String TAG = "BinderInternal";
     static WeakReference<GcWatcher> sGcWatcher
             = new WeakReference<GcWatcher>(new GcWatcher());
     static ArrayList<Runnable> sGcWatchers = new ArrayList<>();
     static Runnable[] sTmpWatchers = new Runnable[1];
     static long sLastGcTime;
+    static final BinderProxyLimitListenerDelegate sBinderProxyLimitListenerDelegate =
+            new BinderProxyLimitListenerDelegate();
 
     static final class GcWatcher {
         @Override
@@ -106,4 +115,96 @@
     static void forceBinderGc() {
         forceGc("Binder");
     }
+
+    /**
+     * Enable/disable Binder Proxy Instance Counting by Uid. While enabled, the set callback will
+     * be called if this process holds too many Binder Proxies on behalf of a Uid.
+     * @param enabled true to enable counting, false to disable
+     */
+    public static final native void nSetBinderProxyCountEnabled(boolean enabled);
+
+    /**
+     * Get the current number of Binder Proxies held for each uid.
+     * @return SparseIntArray mapping uids to the number of Binder Proxies currently held
+     */
+    public static final native SparseIntArray nGetBinderProxyPerUidCounts();
+
+    /**
+     * Get the current number of Binder Proxies held for an individual uid.
+     * @param uid Requested uid for Binder Proxy count
+     * @return int with the number of Binder proxies held for a uid
+     */
+    public static final native int nGetBinderProxyCount(int uid);
+
+    /**
+     * Set the Binder Proxy watermarks. Default high watermark = 2500. Default low watermark = 2000
+     * @param high  The limit at which the BinderProxyListener callback will be called.
+     * @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)
+     */
+    public static final native void nSetBinderProxyCountWatermarks(int high, int low);
+
+    /**
+     * 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 void onLimitReached(int 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 allowed limit.
+     * @param uid The uid of the bad behaving app sending too many binders
+     */
+    public static void binderProxyLimitCallbackFromNative(int uid) {
+       sBinderProxyLimitListenerDelegate.notifyClient(uid);
+    }
+
+    /**
+     * Set a callback to be triggered when a uid's Binder Proxy limit is reached for this process.
+     * @param listener OnLimitReached of listener will be called in the thread provided by handler
+     * @param handler must not be null, callback will be posted through the handler;
+     *
+     */
+    public static void setBinderProxyCountCallback(BinderProxyLimitListener listener,
+            @NonNull Handler handler) {
+        Preconditions.checkNotNull(handler,
+                "Must provide NonNull Handler to setBinderProxyCountCallback when setting "
+                        + "BinderProxyLimitListener");
+        sBinderProxyLimitListenerDelegate.setListener(listener, handler);
+    }
+
+    /**
+     * Clear the Binder Proxy callback
+     */
+    public static void clearBinderProxyCountCallback() {
+        sBinderProxyLimitListenerDelegate.setListener(null, null);
+    }
+
+    static private class BinderProxyLimitListenerDelegate {
+        private BinderProxyLimitListener mBinderProxyLimitListener;
+        private Handler mHandler;
+
+        void setListener(BinderProxyLimitListener listener, Handler handler) {
+            synchronized (this) {
+                mBinderProxyLimitListener = listener;
+                mHandler = handler;
+            }
+        }
+
+        void notifyClient(final int uid) {
+            synchronized (this) {
+                if (mBinderProxyLimitListener != null) {
+                    mHandler.post(new Runnable() {
+                        @Override
+                        public void run() {
+                            mBinderProxyLimitListener.onLimitReached(uid);
+                        }
+                    });
+                }
+            }
+        }
+    }
 }
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index 7908c9d..6362bc7 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -32,6 +32,7 @@
 #include <binder/IServiceManager.h>
 #include <binder/IPCThreadState.h>
 #include <binder/Parcel.h>
+#include <binder/BpBinder.h>
 #include <binder/ProcessState.h>
 #include <log/log.h>
 #include <utils/Atomic.h>
@@ -80,9 +81,17 @@
     // Class state.
     jclass mClass;
     jmethodID mForceGc;
+    jmethodID mProxyLimitCallback;
 
 } gBinderInternalOffsets;
 
+static struct sparseintarray_offsets_t
+{
+    jclass classObject;
+    jmethodID constructor;
+    jmethodID put;
+} gSparseIntArrayOffsets;
+
 // ----------------------------------------------------------------------------
 
 static struct error_offsets_t
@@ -973,6 +982,43 @@
     android_atomic_and(0, &gNumRefsCreated);
 }
 
+static void android_os_BinderInternal_proxyLimitcallback(int uid)
+{
+    JNIEnv *env = AndroidRuntime::getJNIEnv();
+    env->CallStaticVoidMethod(gBinderInternalOffsets.mClass,
+                              gBinderInternalOffsets.mProxyLimitCallback,
+                              uid);
+}
+
+static void android_os_BinderInternal_setBinderProxyCountEnabled(JNIEnv* env, jobject clazz,
+                                                                 jboolean enable)
+{
+    BpBinder::setCountByUidEnabled((bool) enable);
+}
+
+static jobject android_os_BinderInternal_getBinderProxyPerUidCounts(JNIEnv* env, jclass clazz)
+{
+    Vector<uint32_t> uids, counts;
+    BpBinder::getCountByUid(uids, counts);
+    jobject sparseIntArray = env->NewObject(gSparseIntArrayOffsets.classObject,
+                                            gSparseIntArrayOffsets.constructor);
+    for (size_t i = 0; i < uids.size(); i++) {
+        env->CallVoidMethod(sparseIntArray, gSparseIntArrayOffsets.put,
+                            static_cast<jint>(uids[i]), static_cast<jint>(counts[i]));
+    }
+    return sparseIntArray;
+}
+
+static jint android_os_BinderInternal_getBinderProxyCount(JNIEnv* env, jobject clazz, jint uid) {
+    return static_cast<jint>(BpBinder::getBinderProxyCount(static_cast<uint32_t>(uid)));
+}
+
+static void android_os_BinderInternal_setBinderProxyCountWatermarks(JNIEnv* env, jobject clazz,
+                                                                    jint high, jint low)
+{
+    BpBinder::setBinderProxyCountWatermarks(high, low);
+}
+
 // ----------------------------------------------------------------------------
 
 static const JNINativeMethod gBinderInternalMethods[] = {
@@ -981,7 +1027,11 @@
     { "joinThreadPool", "()V", (void*)android_os_BinderInternal_joinThreadPool },
     { "disableBackgroundScheduling", "(Z)V", (void*)android_os_BinderInternal_disableBackgroundScheduling },
     { "setMaxThreads", "(I)V", (void*)android_os_BinderInternal_setMaxThreads },
-    { "handleGc", "()V", (void*)android_os_BinderInternal_handleGc }
+    { "handleGc", "()V", (void*)android_os_BinderInternal_handleGc },
+    { "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}
 };
 
 const char* const kBinderInternalPathName = "com/android/internal/os/BinderInternal";
@@ -992,6 +1042,16 @@
 
     gBinderInternalOffsets.mClass = MakeGlobalRefOrDie(env, clazz);
     gBinderInternalOffsets.mForceGc = GetStaticMethodIDOrDie(env, clazz, "forceBinderGc", "()V");
+    gBinderInternalOffsets.mProxyLimitCallback = GetStaticMethodIDOrDie(env, clazz, "binderProxyLimitCallbackFromNative", "(I)V");
+
+    jclass SparseIntArrayClass = FindClassOrDie(env, "android/util/SparseIntArray");
+    gSparseIntArrayOffsets.classObject = MakeGlobalRefOrDie(env, SparseIntArrayClass);
+    gSparseIntArrayOffsets.constructor = GetMethodIDOrDie(env, gSparseIntArrayOffsets.classObject,
+                                                           "<init>", "()V");
+    gSparseIntArrayOffsets.put = GetMethodIDOrDie(env, gSparseIntArrayOffsets.classObject, "put",
+                                                   "(II)V");
+
+    BpBinder::setLimitCallback(android_os_BinderInternal_proxyLimitcallback);
 
     return RegisterMethodsOrDie(
         env, kBinderInternalPathName,
diff --git a/core/tests/coretests/Android.mk b/core/tests/coretests/Android.mk
index 15eab1f..f2eb872 100644
--- a/core/tests/coretests/Android.mk
+++ b/core/tests/coretests/Android.mk
@@ -19,7 +19,12 @@
 	$(call all-java-files-under, src) \
 	$(call all-Iaidl-files-under, src) \
 	$(call all-java-files-under, DisabledTestApp/src) \
-	$(call all-java-files-under, EnabledTestApp/src)
+	$(call all-java-files-under, EnabledTestApp/src) \
+	$(call all-java-files-under, BinderProxyCountingTestApp/src) \
+	$(call all-java-files-under, BinderProxyCountingTestService/src) \
+	$(call all-Iaidl-files-under, aidl)
+
+LOCAL_AIDL_INCLUDES := $(LOCAL_PATH)/aidl
 
 LOCAL_DX_FLAGS := --core-library
 LOCAL_JACK_FLAGS := --multi-dex native
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index 9c0543b..51bfc20 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -86,6 +86,7 @@
     <uses-permission android:name="android.permission.BROADCAST_STICKY" />
 
     <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
+    <uses-permission android:name="android.permission.KILL_UID" />
 
     <!-- location test permissions -->
     <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
diff --git a/core/tests/coretests/BinderProxyCountingTestApp/Android.mk b/core/tests/coretests/BinderProxyCountingTestApp/Android.mk
new file mode 100644
index 0000000..e31d50f
--- /dev/null
+++ b/core/tests/coretests/BinderProxyCountingTestApp/Android.mk
@@ -0,0 +1,27 @@
+# Copyright (C) 2017 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.
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_STATIC_JAVA_LIBRARIES := coretests-aidl
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := BinderProxyCountingTestApp
+LOCAL_CERTIFICATE := platform
+
+include $(BUILD_PACKAGE)
+
diff --git a/core/tests/coretests/BinderProxyCountingTestApp/AndroidManifest.xml b/core/tests/coretests/BinderProxyCountingTestApp/AndroidManifest.xml
new file mode 100644
index 0000000..a971730
--- /dev/null
+++ b/core/tests/coretests/BinderProxyCountingTestApp/AndroidManifest.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.frameworks.coretests.binderproxycountingtestapp">
+
+    <application>
+        <service android:name=".BpcTestAppCmdService"
+                 android:exported="true"/>
+    </application>
+</manifest>
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
new file mode 100644
index 0000000..5aae1203
--- /dev/null
+++ b/core/tests/coretests/BinderProxyCountingTestApp/src/com/android/frameworks/coretests/binderproxycountingtestapp/BpcTestAppCmdService.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2017 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 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.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.frameworks.coretests.aidl.IBinderProxyCountingService;
+import com.android.frameworks.coretests.aidl.IBpcTestAppCmdService;
+import com.android.frameworks.coretests.aidl.ITestRemoteCallback;
+
+import java.util.ArrayList;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+public class BpcTestAppCmdService extends Service {
+    private static final String TAG = BpcTestAppCmdService.class.getSimpleName();
+
+    private static final String TEST_SERVICE_PKG =
+            "com.android.frameworks.coretests.binderproxycountingtestservice";
+    private static final String TEST_SERVICE_CLASS =
+            TEST_SERVICE_PKG + ".BinderProxyCountingService";
+    private static final int BIND_SERVICE_TIMEOUT_SEC = 5;
+
+    private static ServiceConnection mServiceConnection;
+    private static IBinderProxyCountingService mBpcService;
+
+    private IBpcTestAppCmdService.Stub mBinder = new IBpcTestAppCmdService.Stub() {
+
+        private ArrayList<BroadcastReceiver> mBrList = new ArrayList();
+        private ArrayList<ITestRemoteCallback> mTrcList = new ArrayList();
+
+        @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);
+                }
+                registerReceiver(br, filt);
+            }
+        }
+
+        @Override
+        public void releaseSystemBinders(int count) {
+            int i = 0;
+            while (i++ < count) {
+                BroadcastReceiver br;
+                synchronized (mBrList) {
+                    br = mBrList.remove(0);
+                }
+                unregisterReceiver(br);
+            }
+        }
+
+        @Override
+        public void createTestBinders(int count) {
+            int i = 0;
+            while (i++ < count) {
+                ITestRemoteCallback cb = new ITestRemoteCallback.Stub() {};
+                synchronized (mTrcList) {
+                    mTrcList.add(cb);
+                }
+                try {
+                    mBpcService.registerCallback(cb);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "RemoteException caught! " + e);
+                }
+            }
+        }
+
+        @Override
+        public void releaseTestBinders(int count) {
+            int i = 0;
+            while (i++ < count) {
+
+                ITestRemoteCallback cb;
+                synchronized (mTrcList) {
+                    cb = mTrcList.remove(0);
+                }
+                try {
+                    mBpcService.unregisterCallback(cb);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "RemoteException caught! " + e);
+                }
+            }
+        }
+
+        @Override
+        public void releaseAllBinders() {
+            synchronized (mBrList) {
+                while (mBrList.size() > 0) {
+                    unregisterReceiver(mBrList.remove(0));
+                }
+            }
+            synchronized (mTrcList) {
+                while (mTrcList.size() > 0) {
+                    try {
+                        mBpcService.unregisterCallback(mTrcList.remove(0));
+                    } catch (RemoteException e) {
+                        Log.e(TAG, "RemoteException caught! " + e);
+                    }
+                }
+            }
+        }
+
+        @Override
+        public String bindToTestService() {
+            try {
+                final CountDownLatch bindLatch = new CountDownLatch(1);
+                mServiceConnection = new ServiceConnection() {
+                    @Override
+                    public void onServiceConnected(ComponentName name, IBinder service) {
+                        Log.i(TAG, "Service connected");
+                        mBpcService = IBinderProxyCountingService.Stub.asInterface(service);
+                        bindLatch.countDown();
+                    }
+
+                    @Override
+                    public void onServiceDisconnected(ComponentName name) {
+                        Log.i(TAG, "Service disconnected");
+                    }
+                };
+                final Intent intent = new Intent()
+                        .setComponent(new ComponentName(TEST_SERVICE_PKG, TEST_SERVICE_CLASS));
+                bindService(intent, mServiceConnection,
+                        Context.BIND_AUTO_CREATE
+                                | Context.BIND_ALLOW_OOM_MANAGEMENT
+                                | Context.BIND_NOT_FOREGROUND);
+                if (!bindLatch.await(BIND_SERVICE_TIMEOUT_SEC, TimeUnit.SECONDS)) {
+                    throw new RuntimeException("Failed to bind to " + TEST_SERVICE_CLASS);
+                }
+            } catch (Exception e) {
+                unbindFromTestService();
+                Log.e(TAG, e.toString());
+                return e.toString();
+            }
+            return null;
+        }
+
+        @Override
+        public void unbindFromTestService() {
+            if (mBpcService != null) {
+                unbindService(mServiceConnection);
+            }
+        }
+    };
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return mBinder;
+    }
+}
\ No newline at end of file
diff --git a/core/tests/coretests/BinderProxyCountingTestService/Android.mk b/core/tests/coretests/BinderProxyCountingTestService/Android.mk
new file mode 100644
index 0000000..a63cf0e
--- /dev/null
+++ b/core/tests/coretests/BinderProxyCountingTestService/Android.mk
@@ -0,0 +1,27 @@
+# Copyright (C) 2017 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.
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_STATIC_JAVA_LIBRARIES := coretests-aidl
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := BinderProxyCountingTestService
+LOCAL_CERTIFICATE := platform
+
+include $(BUILD_PACKAGE)
+
diff --git a/core/tests/coretests/BinderProxyCountingTestService/AndroidManifest.xml b/core/tests/coretests/BinderProxyCountingTestService/AndroidManifest.xml
new file mode 100644
index 0000000..777bd20
--- /dev/null
+++ b/core/tests/coretests/BinderProxyCountingTestService/AndroidManifest.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.frameworks.coretests.binderproxycountingtestservice">
+
+    <application>
+        <service android:name=".BpcTestServiceCmdService"
+                 android:exported="true" />
+        <service android:name=".BinderProxyCountingService"
+                 android:exported="true" />
+    </application>
+</manifest>
diff --git a/core/tests/coretests/BinderProxyCountingTestService/src/com/android/frameworks/coretests/binderproxycountingtestservice/BinderProxyCountingService.java b/core/tests/coretests/BinderProxyCountingTestService/src/com/android/frameworks/coretests/binderproxycountingtestservice/BinderProxyCountingService.java
new file mode 100644
index 0000000..41b4c69
--- /dev/null
+++ b/core/tests/coretests/BinderProxyCountingTestService/src/com/android/frameworks/coretests/binderproxycountingtestservice/BinderProxyCountingService.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2017 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 com.android.frameworks.coretests.binderproxycountingtestservice;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+import android.os.RemoteCallbackList;
+
+import com.android.frameworks.coretests.aidl.IBinderProxyCountingService;
+import com.android.frameworks.coretests.aidl.ITestRemoteCallback;
+
+public class BinderProxyCountingService extends Service {
+    private static final String TAG = BinderProxyCountingService.class.getSimpleName();
+
+    private IBinderProxyCountingService.Stub mBinder = new IBinderProxyCountingService.Stub() {
+
+        final RemoteCallbackList<ITestRemoteCallback> mTestCallbacks = new RemoteCallbackList<>();
+
+        @Override
+        public void registerCallback(ITestRemoteCallback callback) {
+            synchronized (this) {
+                mTestCallbacks.register(callback);
+            }
+        }
+
+        @Override
+        public void unregisterCallback(ITestRemoteCallback callback) {
+            synchronized (this) {
+                mTestCallbacks.unregister(callback);
+            }
+        }
+    };
+
+    @Override
+    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
new file mode 100644
index 0000000..6bed2a2
--- /dev/null
+++ b/core/tests/coretests/BinderProxyCountingTestService/src/com/android/frameworks/coretests/binderproxycountingtestservice/BpcTestServiceCmdService.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2017 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 com.android.frameworks.coretests.binderproxycountingtestservice;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.Debug;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.util.Log;
+
+import com.android.frameworks.coretests.aidl.IBpcCallbackObserver;
+import com.android.frameworks.coretests.aidl.IBpcTestServiceCmdService;
+import com.android.internal.os.BinderInternal;
+
+public class BpcTestServiceCmdService extends Service {
+    private static final String TAG = BpcTestServiceCmdService.class.getSimpleName();
+
+    //ServiceThread mHandlerThread;
+    Handler mHandler;
+    HandlerThread mHandlerThread;
+
+    private IBpcTestServiceCmdService.Stub mBinder = new IBpcTestServiceCmdService.Stub() {
+        IBpcCallbackObserver mCallbackObserver;
+
+        @Override
+        public void forceGc() {
+            int gcCount = Integer.parseInt(Debug.getRuntimeStat("art.gc.gc-count"));
+            int i = 20;
+            while (gcCount == Integer.parseInt(Debug.getRuntimeStat("art.gc.gc-count")) && i > 0) {
+                System.gc();
+                System.runFinalization();
+                i--;
+            }
+        }
+
+        @Override
+        public int getBinderProxyCount(int uid) {
+            return BinderInternal.nGetBinderProxyCount(uid);
+        }
+
+        @Override
+        public void setBinderProxyWatermarks(int high, int low) {
+            BinderInternal.nSetBinderProxyCountWatermarks(high, low);
+        }
+
+        @Override
+        public void enableBinderProxyLimit(boolean enable) {
+            BinderInternal.nSetBinderProxyCountEnabled(enable);
+        }
+
+        @Override
+        public void setBinderProxyCountCallback(IBpcCallbackObserver observer) {
+            if (observer != null) {
+                BinderInternal.setBinderProxyCountCallback(
+                        new BinderInternal.BinderProxyLimitListener() {
+                            @Override
+                            public void onLimitReached(int uid) {
+                                try {
+                                    synchronized (observer) {
+                                        observer.onCallback(uid);
+                                    }
+                                } catch (Exception e) {
+                                    Log.e(TAG, e.toString());
+                                }
+                            }
+                        }, mHandler);
+            } else {
+                BinderInternal.clearBinderProxyCountCallback();
+            }
+        }
+    };
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return mBinder;
+    }
+
+    @Override
+    public void onCreate()
+    {
+        mHandlerThread = new HandlerThread("BinderProxyCountingServiceThread");
+        mHandlerThread.start();
+        mHandler = new Handler(mHandlerThread.getLooper());
+    }
+}
\ No newline at end of file
diff --git a/core/tests/coretests/aidl/Android.mk b/core/tests/coretests/aidl/Android.mk
new file mode 100644
index 0000000..86e36b6
--- /dev/null
+++ b/core/tests/coretests/aidl/Android.mk
@@ -0,0 +1,22 @@
+# Copyright (C) 2017 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.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE_TAGS := tests
+LOCAL_SDK_VERSION := current
+LOCAL_SRC_FILES := $(call all-subdir-Iaidl-files)
+LOCAL_MODULE := coretests-aidl
+include $(BUILD_STATIC_JAVA_LIBRARY)
\ No newline at end of file
diff --git a/core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/IBinderProxyCountingService.aidl b/core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/IBinderProxyCountingService.aidl
new file mode 100644
index 0000000..a69b0c5
--- /dev/null
+++ b/core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/IBinderProxyCountingService.aidl
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2017 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 com.android.frameworks.coretests.aidl;
+import com.android.frameworks.coretests.aidl.ITestRemoteCallback;
+
+interface IBinderProxyCountingService {
+    void registerCallback(in ITestRemoteCallback callback);
+    void unregisterCallback(in ITestRemoteCallback callback);
+}
\ 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
new file mode 100644
index 0000000..c4ebd56
--- /dev/null
+++ b/core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/IBpcCallbackObserver.aidl
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2017 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 com.android.frameworks.coretests.aidl;
+
+interface IBpcCallbackObserver {
+    void onCallback(int uid);
+}
\ No newline at end of file
diff --git a/core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/IBpcTestAppCmdService.aidl b/core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/IBpcTestAppCmdService.aidl
new file mode 100644
index 0000000..86a0aa0f
--- /dev/null
+++ b/core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/IBpcTestAppCmdService.aidl
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2017 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 com.android.frameworks.coretests.aidl;
+
+interface IBpcTestAppCmdService {
+   void createSystemBinders(int count);
+   void releaseSystemBinders(int count);
+
+   void createTestBinders(int count);
+   void releaseTestBinders(int count);
+
+   void releaseAllBinders();
+
+   String bindToTestService();
+   void unbindFromTestService();
+}
\ No newline at end of file
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
new file mode 100644
index 0000000..abdab41
--- /dev/null
+++ b/core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/IBpcTestServiceCmdService.aidl
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2017 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 com.android.frameworks.coretests.aidl;
+import com.android.frameworks.coretests.aidl.IBpcCallbackObserver;
+
+interface IBpcTestServiceCmdService {
+   void forceGc();
+   int getBinderProxyCount(int uid);
+   void setBinderProxyWatermarks(int high, int low);
+   void enableBinderProxyLimit(boolean enable);
+   void setBinderProxyCountCallback(IBpcCallbackObserver observer);
+}
\ No newline at end of file
diff --git a/core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/ITestRemoteCallback.aidl b/core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/ITestRemoteCallback.aidl
new file mode 100644
index 0000000..36bdb6c
--- /dev/null
+++ b/core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/ITestRemoteCallback.aidl
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2017 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 com.android.frameworks.coretests.aidl;
+
+interface ITestRemoteCallback {
+}
\ 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
new file mode 100644
index 0000000..6cdb35ab
--- /dev/null
+++ b/core/tests/coretests/src/android/os/BinderProxyCountingTest.java
@@ -0,0 +1,378 @@
+/*
+ * Copyright (C) 2017 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 static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import android.app.ActivityManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.test.uiautomator.UiDevice;
+import android.util.Log;
+
+import com.android.frameworks.coretests.aidl.IBpcCallbackObserver;
+import com.android.frameworks.coretests.aidl.IBpcTestAppCmdService;
+import com.android.frameworks.coretests.aidl.IBpcTestServiceCmdService;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
+
+/**
+ * Tests for verifying the Binder Proxy Counting and Limiting.
+ *
+ * To manually build and install relevant test apps
+ *
+ * Build:
+ * mmma frameworks/base/core/tests/coretests/BinderProxyCountingTestApp
+ * mmma frameworks/base/core/tests/coretests/BinderProxyCountingTestService
+ * Install:
+ * adb install -r \
+ * ${ANDROID_PRODUCT_OUT}/data/app/BinderProxyCountingTestApp/BinderProxyCountingTestApp.apk
+ * adb install -r \
+ * ${ANDROID_PRODUCT_OUT}/data/app/BinderProxyCountingTestService/BinderProxyCountingTestService.apk
+ *
+ * To run the tests, use
+ *
+ * Build: m FrameworksCoreTests
+ * Install: adb install -r \
+ * ${ANDROID_PRODUCT_OUT}/data/app/FrameworksCoreTests/FrameworksCoreTests.apk
+ * Run: adb shell am instrument -e class android.os.BinderProxyCountingTest -w \
+ * com.android.frameworks.coretests/android.support.test.runner.AndroidJUnitRunner
+ *
+ * or
+ *
+ * bit FrameworksCoreTests:android.os.BinderProxyCountingTest
+ */
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class BinderProxyCountingTest {
+    private static final String TAG = BinderProxyCountingTest.class.getSimpleName();
+
+    private static final String TEST_APP_PKG =
+            "com.android.frameworks.coretests.binderproxycountingtestapp";
+    private static final String TEST_APP_CMD_SERVICE = TEST_APP_PKG + ".BpcTestAppCmdService";
+    private static final String TEST_SERVICE_PKG =
+            "com.android.frameworks.coretests.binderproxycountingtestservice";
+    private static final String TEST_SERVICE_CMD_SERVICE =
+            TEST_SERVICE_PKG + ".BpcTestServiceCmdService";
+
+    private static final int BIND_SERVICE_TIMEOUT_SEC = 5;
+    private static final int TOO_MANY_BINDERS_TIMEOUT_SEC = 2;
+
+    // Keep in sync with sBinderProxyCountLimit in BpBinder.cpp
+    private static final int BINDER_PROXY_LIMIT = 2500;
+
+    private static Context sContext;
+    private static UiDevice sUiDevice;
+
+    private static ServiceConnection sTestAppConnection;
+    private static ServiceConnection sTestServiceConnection;
+    private static IBpcTestAppCmdService sBpcTestAppCmdService;
+    private static IBpcTestServiceCmdService sBpcTestServiceCmdService;
+    private static final Intent sTestAppIntent = new Intent()
+            .setComponent(new ComponentName(TEST_APP_PKG, TEST_APP_CMD_SERVICE));
+    private static final Intent sTestServiceIntent = new Intent()
+            .setComponent(new ComponentName(TEST_SERVICE_PKG, TEST_SERVICE_CMD_SERVICE));
+    private static final Consumer<IBinder> sTestAppConsumer = (service) -> {
+        sBpcTestAppCmdService = IBpcTestAppCmdService.Stub.asInterface(service);
+    };
+    private static final Consumer<IBinder> sTestServiceConsumer = (service) -> {
+        sBpcTestServiceCmdService = IBpcTestServiceCmdService.Stub.asInterface(service);
+    };
+    private static int sTestPkgUid;
+
+    /**
+     * Setup any common data for the upcoming tests.
+     */
+    @BeforeClass
+    public static void setUpOnce() throws Exception {
+        sContext = InstrumentationRegistry.getContext();
+        sTestPkgUid = sContext.getPackageManager().getPackageUid(TEST_APP_PKG, 0);
+        ((ActivityManager) sContext.getSystemService(Context.ACTIVITY_SERVICE)).killUid(sTestPkgUid,
+                "Wiping Test Package");
+
+        sUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+    }
+
+    private ServiceConnection bindService(final Consumer<IBinder> consumer, Intent intent)
+            throws Exception {
+        final CountDownLatch bindLatch = new CountDownLatch(1);
+        ServiceConnection connection = new ServiceConnection() {
+            @Override
+            public void onServiceConnected(ComponentName name, IBinder service) {
+                Log.i(TAG, "Service connected");
+                consumer.accept(service);
+                bindLatch.countDown();
+            }
+
+            @Override
+            public void onServiceDisconnected(ComponentName name) {
+                Log.i(TAG, "Service disconnected");
+            }
+        };
+        sContext.bindService(intent, connection,
+                Context.BIND_AUTO_CREATE
+                        | Context.BIND_ALLOW_OOM_MANAGEMENT
+                        | Context.BIND_NOT_FOREGROUND);
+        if (!bindLatch.await(BIND_SERVICE_TIMEOUT_SEC, TimeUnit.SECONDS)) {
+            fail("Timed out waiting for the service to bind in " + sTestPkgUid);
+        }
+        return connection;
+    }
+
+
+    private void unbindService(ServiceConnection service) {
+        if (service != null) {
+            sContext.unbindService(service);
+        }
+    }
+
+    private void bindTestAppToTestService() throws Exception {
+        if (sBpcTestAppCmdService != null) {
+            String errorMessage = sBpcTestAppCmdService.bindToTestService();
+            if (errorMessage != null) {
+                fail(errorMessage);
+            }
+        }
+    }
+
+    private void unbindTestAppFromTestService() throws Exception {
+        if (sBpcTestAppCmdService != null) {
+            sBpcTestAppCmdService.unbindFromTestService();
+        }
+    }
+
+    private CountDownLatch createBinderLimitLatch() throws RemoteException {
+        final CountDownLatch latch = new CountDownLatch(1);
+        sBpcTestServiceCmdService.setBinderProxyCountCallback(
+                new IBpcCallbackObserver.Stub() {
+                    @Override
+                    public void onCallback(int uid) {
+                        if (uid == sTestPkgUid) {
+                            latch.countDown();
+                        }
+                    }
+                });
+        return latch;
+    }
+
+    /**
+     * Get the Binder Proxy count held by SYSTEM for a given uid
+     */
+    private int getSystemBinderCount(int uid) throws Exception {
+        return Integer.parseInt(sUiDevice.executeShellCommand(
+                "dumpsys activity binder-proxies " + uid).trim());
+    }
+
+    @Test
+    public void testBinderProxyCount() throws Exception {
+        // Arbitrary list of Binder create and release
+        // Should cumulatively equal 0 and must never add up past the binder limit at any point
+        int[] testValues = {223, -103, -13, 25, 90, -222};
+        try {
+            sTestAppConnection = bindService(sTestAppConsumer, sTestAppIntent);
+            // Get the baseline of binders naturally held by the test Package
+            int expectedBinderCount = getSystemBinderCount(sTestPkgUid);
+
+            for (int testValue : testValues) {
+                if (testValue > 0) {
+                    sBpcTestAppCmdService.createSystemBinders(testValue);
+                } else {
+                    sBpcTestAppCmdService.releaseSystemBinders(-testValue);
+                }
+                expectedBinderCount += testValue;
+                int currentBinderCount = getSystemBinderCount(sTestPkgUid);
+                assertEquals("Current Binder Count (" + currentBinderCount
+                        + ") does not equal expected Binder Count (" + expectedBinderCount
+                        + ")", expectedBinderCount, currentBinderCount);
+            }
+        } finally {
+            unbindService(sTestAppConnection);
+        }
+    }
+
+    @Test
+    public void testBinderProxyLimitBoundary() throws Exception {
+        final int binderProxyLimit = 2000;
+        final int rearmThreshold = 1800;
+        try {
+            sTestAppConnection = bindService(sTestAppConsumer, sTestAppIntent);
+            sTestServiceConnection = bindService(sTestServiceConsumer, sTestServiceIntent);
+            bindTestAppToTestService();
+            sBpcTestServiceCmdService.enableBinderProxyLimit(true);
+
+            sBpcTestServiceCmdService.forceGc();
+            // Get the baseline of binders naturally held by the test Package
+            int baseBinderCount = sBpcTestServiceCmdService.getBinderProxyCount(sTestPkgUid);
+
+            final CountDownLatch binderLimitLatch = createBinderLimitLatch();
+            sBpcTestServiceCmdService.setBinderProxyWatermarks(binderProxyLimit, rearmThreshold);
+
+            // Create Binder Proxies up to the limit
+            sBpcTestAppCmdService.createTestBinders(binderProxyLimit - baseBinderCount);
+            if (binderLimitLatch.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)) {
+                fail("Timed out waiting for uid " + sTestPkgUid + " to hit limit");
+            }
+
+            sBpcTestAppCmdService.releaseAllBinders();
+        } finally {
+            unbindTestAppFromTestService();
+            unbindService(sTestAppConnection);
+            unbindService(sTestServiceConnection);
+        }
+    }
+
+    @Test
+    public void testSetBinderProxyLimit() throws Exception {
+        int[] testLimits = {1000, 222, 800};
+        try {
+            sTestAppConnection = bindService(sTestAppConsumer, sTestAppIntent);
+            sTestServiceConnection = bindService(sTestServiceConsumer, sTestServiceIntent);
+            bindTestAppToTestService();
+            sBpcTestServiceCmdService.enableBinderProxyLimit(true);
+
+            sBpcTestServiceCmdService.forceGc();
+            int baseBinderCount = sBpcTestServiceCmdService.getBinderProxyCount(sTestPkgUid);
+            for (int testLimit : testLimits) {
+                final CountDownLatch binderLimitLatch = createBinderLimitLatch();
+                // Change the BinderProxyLimit
+                sBpcTestServiceCmdService.setBinderProxyWatermarks(testLimit, baseBinderCount + 10);
+                // Exceed the new Binder Proxy Limit
+                sBpcTestAppCmdService.createTestBinders(testLimit + 1);
+                if (!binderLimitLatch.await(TOO_MANY_BINDERS_TIMEOUT_SEC, TimeUnit.SECONDS)) {
+                    fail("Timed out waiting for uid " + sTestPkgUid + " to hit limit");
+                }
+
+                sBpcTestAppCmdService.releaseTestBinders(testLimit + 1);
+                sBpcTestServiceCmdService.forceGc();
+            }
+        } finally {
+            unbindTestAppFromTestService();
+            unbindService(sTestAppConnection);
+            unbindService(sTestServiceConnection);
+        }
+    }
+
+    @Test
+    public void testRearmCallbackThreshold() throws Exception {
+        final int binderProxyLimit = 2000;
+        final int exceedBinderProxyLimit = binderProxyLimit + 10;
+        final int rearmThreshold = 1800;
+        try {
+            sTestAppConnection = bindService(sTestAppConsumer, sTestAppIntent);
+            sTestServiceConnection = bindService(sTestServiceConsumer, sTestServiceIntent);
+            bindTestAppToTestService();
+            sBpcTestServiceCmdService.enableBinderProxyLimit(true);
+
+            sBpcTestServiceCmdService.forceGc();
+            final CountDownLatch firstBinderLimitLatch = createBinderLimitLatch();
+            sBpcTestServiceCmdService.setBinderProxyWatermarks(binderProxyLimit, rearmThreshold);
+            // Exceed the Binder Proxy Limit
+            sBpcTestAppCmdService.createTestBinders(exceedBinderProxyLimit);
+            if (!firstBinderLimitLatch.await(TOO_MANY_BINDERS_TIMEOUT_SEC, TimeUnit.SECONDS)) {
+                fail("Timed out waiting for uid " + sTestPkgUid + " to hit limit");
+            }
+
+            sBpcTestServiceCmdService.forceGc();
+            int currentBinderCount = sBpcTestServiceCmdService.getBinderProxyCount(sTestPkgUid);
+            // Drop to the threshold, this should not rearm the callback
+            sBpcTestAppCmdService.releaseTestBinders(currentBinderCount - rearmThreshold);
+
+            sBpcTestServiceCmdService.forceGc();
+            currentBinderCount = sBpcTestServiceCmdService.getBinderProxyCount(sTestPkgUid);
+
+            final CountDownLatch secondBinderLimitLatch = createBinderLimitLatch();
+            // 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)) {
+                fail("Received BinderProxyLimitCallback for uid " + sTestPkgUid
+                        + " when the callback has not been rearmed yet");
+            }
+
+            sBpcTestServiceCmdService.forceGc();
+            currentBinderCount = sBpcTestServiceCmdService.getBinderProxyCount(sTestPkgUid);
+            // Drop below the rearmThreshold to rearm the BinderProxyLimitCallback
+            sBpcTestAppCmdService.releaseTestBinders(currentBinderCount - rearmThreshold + 1);
+
+            sBpcTestServiceCmdService.forceGc();
+            currentBinderCount = sBpcTestServiceCmdService.getBinderProxyCount(sTestPkgUid);
+            // Exceed the Binder Proxy limit for the last time
+            sBpcTestAppCmdService.createTestBinders(exceedBinderProxyLimit - currentBinderCount);
+
+            if (!secondBinderLimitLatch.await(TOO_MANY_BINDERS_TIMEOUT_SEC, TimeUnit.SECONDS)) {
+                fail("Timed out waiting for uid " + sTestPkgUid + " to hit limit");
+            }
+            sBpcTestAppCmdService.releaseTestBinders(currentBinderCount);
+        } finally {
+            unbindTestAppFromTestService();
+            unbindService(sTestAppConnection);
+            unbindService(sTestServiceConnection);
+        }
+    }
+
+    @Test
+    public void testKillBadBehavingApp() throws Exception {
+        final CountDownLatch binderDeathLatch = new CountDownLatch(1);
+        final int exceedBinderProxyLimit = BINDER_PROXY_LIMIT + 1;
+
+        try {
+            sTestAppConnection = bindService(sTestAppConsumer, sTestAppIntent);
+            sBpcTestAppCmdService.asBinder().linkToDeath(new IBinder.DeathRecipient() {
+                @Override
+                public void binderDied() {
+                    Log.v(TAG, "BpcTestAppCmdService died!");
+                    binderDeathLatch.countDown();
+                }
+            }, 0);
+            try {
+                // Exceed the Binder Proxy Limit emulating a bad behaving app
+                sBpcTestAppCmdService.createSystemBinders(exceedBinderProxyLimit);
+            } catch (DeadObjectException doe) {
+                // We are expecting the service to get killed mid call, so a DeadObjectException
+                // is not unexpected
+            }
+
+            if (!binderDeathLatch.await(TOO_MANY_BINDERS_TIMEOUT_SEC, TimeUnit.SECONDS)) {
+                sBpcTestAppCmdService.releaseSystemBinders(exceedBinderProxyLimit);
+                fail("Timed out waiting for uid " + sTestPkgUid + " to die.");
+            }
+
+        } finally {
+            unbindService(sTestAppConnection);
+        }
+    }
+}