summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTreehugger Robot <treehugger-gerrit@google.com>2018-07-10 16:24:47 +0000
committerGerrit Code Review <noreply-gerritcodereview@google.com>2018-07-10 16:24:47 +0000
commit62d5e041a2c219b1ce6aaa8ccbc83e0fa7989ab7 (patch)
treebcc5976e7007f4495faf93e7858ce374478c5477
parent1035f48f612f259cfa4b6433a5d62cda7355f4ff (diff)
parentd02c5d8400d2b5c9782119e8cb9f36c59c37d0bd (diff)
downloadbase-62d5e041a2c219b1ce6aaa8ccbc83e0fa7989ab7.tar.gz
Merge "Java side setup and access to Binder Proxy Tracking"android-o-mr1-iot-release-1.0.2
-rw-r--r--core/java/com/android/internal/os/BinderInternal.java101
-rw-r--r--core/jni/android_util_Binder.cpp62
-rw-r--r--core/tests/coretests/Android.mk7
-rw-r--r--core/tests/coretests/AndroidManifest.xml1
-rw-r--r--core/tests/coretests/BinderProxyCountingTestApp/Android.mk28
-rw-r--r--core/tests/coretests/BinderProxyCountingTestApp/AndroidManifest.xml23
-rw-r--r--core/tests/coretests/BinderProxyCountingTestApp/src/com/android/frameworks/coretests/binderproxycountingtestapp/BpcTestAppCmdService.java182
-rw-r--r--core/tests/coretests/BinderProxyCountingTestService/Android.mk28
-rw-r--r--core/tests/coretests/BinderProxyCountingTestService/AndroidManifest.xml25
-rw-r--r--core/tests/coretests/BinderProxyCountingTestService/src/com/android/frameworks/coretests/binderproxycountingtestservice/BinderProxyCountingService.java53
-rw-r--r--core/tests/coretests/BinderProxyCountingTestService/src/com/android/frameworks/coretests/binderproxycountingtestservice/BpcTestServiceCmdService.java101
-rw-r--r--core/tests/coretests/aidl/Android.mk22
-rw-r--r--core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/IBinderProxyCountingService.aidl23
-rw-r--r--core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/IBpcCallbackObserver.aidl21
-rw-r--r--core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/IBpcTestAppCmdService.aidl30
-rw-r--r--core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/IBpcTestServiceCmdService.aidl26
-rw-r--r--core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/ITestRemoteCallback.aidl20
-rw-r--r--core/tests/coretests/src/android/os/BinderProxyCountingTest.java378
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java59
19 files changed, 1188 insertions, 2 deletions
diff --git a/core/java/com/android/internal/os/BinderInternal.java b/core/java/com/android/internal/os/BinderInternal.java
index ea4575aba9c0..5bddd2f98983 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 @@ 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
@@ -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);
+ }
+ });
+ }
+ }
+ }
+ }
}
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index a04094099cc2..aa68e75202b4 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -33,6 +33,7 @@
#include <binder/IServiceManager.h>
#include <binder/IPCThreadState.h>
#include <binder/Parcel.h>
+#include <binder/BpBinder.h>
#include <binder/ProcessState.h>
#include <cutils/atomic.h>
#include <log/log.h>
@@ -81,9 +82,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
@@ -993,6 +1002,43 @@ static void android_os_BinderInternal_handleGc(JNIEnv* env, jobject clazz)
gCollectedAtRefs = gNumLocalRefsCreated + gNumDeathRefsCreated;
}
+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[] = {
@@ -1001,7 +1047,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";
@@ -1012,6 +1062,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,
diff --git a/core/tests/coretests/Android.mk b/core/tests/coretests/Android.mk
index a97e79bda4ab..b99006848dc3 100644
--- a/core/tests/coretests/Android.mk
+++ b/core/tests/coretests/Android.mk
@@ -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
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index c0a8acda628e..cfb235999029 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 000000000000..c3af6bd123b8
--- /dev/null
+++ b/core/tests/coretests/BinderProxyCountingTestApp/Android.mk
@@ -0,0 +1,28 @@
+# 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_SDK_VERSION := current
+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 000000000000..a971730f389d
--- /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 000000000000..5aae1203e559
--- /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 000000000000..34016ed633b1
--- /dev/null
+++ b/core/tests/coretests/BinderProxyCountingTestService/Android.mk
@@ -0,0 +1,28 @@
+# 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_PRIVATE_PLATFORM_APIS := true
+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 000000000000..777bd20db212
--- /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 000000000000..41b4c69232f4
--- /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 000000000000..6bed2a2ec53f
--- /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 000000000000..86e36b61a5ae
--- /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 000000000000..a69b0c58233a
--- /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 000000000000..c4ebd56bed6c
--- /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 000000000000..86a0aa0ffc12
--- /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 000000000000..abdab41ce537
--- /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 000000000000..36bdb6cc9c41
--- /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 000000000000..6cdb35abce5a
--- /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);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 1546ec176f39..02d5d26723dc 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -380,6 +380,7 @@ import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.internal.notification.SystemNotificationChannels;
import com.android.internal.os.BackgroundThread;
import com.android.internal.os.BatteryStatsImpl;
+import com.android.internal.os.BinderInternal;
import com.android.internal.os.IResultReceiver;
import com.android.internal.os.ProcessCpuTracker;
import com.android.internal.os.TransferPipe;
@@ -14366,6 +14367,23 @@ public class ActivityManagerService extends IActivityManager.Stub
}
mStackSupervisor.resumeFocusedStackTopActivityLocked();
mUserController.sendUserSwitchBroadcastsLocked(-1, currentUserId);
+
+ BinderInternal.nSetBinderProxyCountEnabled(true);
+ BinderInternal.setBinderProxyCountCallback(
+ new BinderInternal.BinderProxyLimitListener() {
+ @Override
+ public void onLimitReached(int uid) {
+ Slog.wtf(TAG, "Uid " + uid + " sent too many Binders to uid "
+ + Process.myUid());
+ if (uid == Process.SYSTEM_UID) {
+ Slog.i(TAG, "Skipping kill (uid is SYSTEM)");
+ } else {
+ killUid(UserHandle.getAppId(uid), UserHandle.getUserId(uid),
+ "Too many Binders sent to SYSTEM");
+ }
+ }
+ }, mHandler);
+
traceLog.traceEnd(); // ActivityManagerStartApps
traceLog.traceEnd(); // PhaseActivityManagerReady
}
@@ -15134,6 +15152,19 @@ public class ActivityManagerService extends IActivityManager.Stub
synchronized (this) {
dumpRecentsLocked(fd, pw, args, opti, true, dumpPackage);
}
+ } else if ("binder-proxies".equals(cmd)) {
+ if (opti >= args.length) {
+ dumpBinderProxiesCounts(pw, BinderInternal.nGetBinderProxyPerUidCounts(),
+ "Counts of Binder Proxies held by SYSTEM");
+ } else {
+ String uid = args[opti];
+ opti++;
+ // Ensure Binder Proxy Count is as up to date as possible
+ System.gc();
+ System.runFinalization();
+ System.gc();
+ pw.println(BinderInternal.nGetBinderProxyCount(Integer.parseInt(uid)));
+ }
} else if ("broadcasts".equals(cmd) || "b".equals(cmd)) {
String[] newArgs;
String name;
@@ -15663,6 +15694,34 @@ public class ActivityManagerService extends IActivityManager.Stub
return printed;
}
+ boolean dumpBinderProxiesCounts(PrintWriter pw, SparseIntArray counts, String header) {
+ if(counts != null) {
+ pw.println(header);
+ for (int i = 0; i < counts.size(); i++) {
+ final int uid = counts.keyAt(i);
+ final int binderCount = counts.valueAt(i);
+ pw.print(" UID ");
+ pw.print(uid);
+ pw.print(", binder count = ");
+ pw.print(binderCount);
+ pw.print(", package(s)= ");
+ final String[] pkgNames = mContext.getPackageManager().getPackagesForUid(uid);
+ if (pkgNames != null) {
+ for (int j = 0; j < pkgNames.length; j++) {
+ pw.print(pkgNames[j]);
+ pw.print("; ");
+ }
+ } else {
+ pw.print("NO PACKAGE NAME FOUND");
+ }
+ pw.println();
+ }
+ pw.println();
+ return true;
+ }
+ return false;
+ }
+
void dumpProcessesLocked(FileDescriptor fd, PrintWriter pw, String[] args,
int opti, boolean dumpAll, String dumpPackage) {
boolean needSep = false;