Loading core/java/com/android/internal/os/BinderInternal.java +101 −0 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -31,11 +37,14 @@ import java.util.ArrayList; * @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 Loading Loading @@ -106,4 +115,96 @@ public class BinderInternal { 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); } }); } } } } } core/jni/android_util_Binder.cpp +61 −1 Original line number Diff line number Diff line Loading @@ -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> Loading Loading @@ -80,9 +81,17 @@ static struct binderinternal_offsets_t // 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 Loading Loading @@ -973,6 +982,43 @@ static void android_os_BinderInternal_handleGc(JNIEnv* env, jobject clazz) 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[] = { Loading @@ -981,7 +1027,11 @@ static const JNINativeMethod gBinderInternalMethods[] = { { "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"; Loading @@ -992,6 +1042,16 @@ static int int_register_android_os_BinderInternal(JNIEnv* env) 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, Loading core/tests/coretests/Android.mk +6 −1 Original line number Diff line number Diff line Loading @@ -19,7 +19,12 @@ LOCAL_SRC_FILES := \ $(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 Loading core/tests/coretests/AndroidManifest.xml +1 −0 Original line number Diff line number Diff line Loading @@ -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"/> Loading core/tests/coretests/BinderProxyCountingTestApp/Android.mk 0 → 100644 +27 −0 Original line number Diff line number Diff line # 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) Loading
core/java/com/android/internal/os/BinderInternal.java +101 −0 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -31,11 +37,14 @@ import java.util.ArrayList; * @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 Loading Loading @@ -106,4 +115,96 @@ public class BinderInternal { 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); } }); } } } } }
core/jni/android_util_Binder.cpp +61 −1 Original line number Diff line number Diff line Loading @@ -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> Loading Loading @@ -80,9 +81,17 @@ static struct binderinternal_offsets_t // 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 Loading Loading @@ -973,6 +982,43 @@ static void android_os_BinderInternal_handleGc(JNIEnv* env, jobject clazz) 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[] = { Loading @@ -981,7 +1027,11 @@ static const JNINativeMethod gBinderInternalMethods[] = { { "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"; Loading @@ -992,6 +1042,16 @@ static int int_register_android_os_BinderInternal(JNIEnv* env) 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, Loading
core/tests/coretests/Android.mk +6 −1 Original line number Diff line number Diff line Loading @@ -19,7 +19,12 @@ LOCAL_SRC_FILES := \ $(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 Loading
core/tests/coretests/AndroidManifest.xml +1 −0 Original line number Diff line number Diff line Loading @@ -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"/> Loading
core/tests/coretests/BinderProxyCountingTestApp/Android.mk 0 → 100644 +27 −0 Original line number Diff line number Diff line # 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)