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);
+                        }
+                    });
+                }
+            }
+        }
+    }
 }