diff options
author | Treehugger Robot <treehugger-gerrit@google.com> | 2018-07-10 16:24:47 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2018-07-10 16:24:47 +0000 |
commit | 62d5e041a2c219b1ce6aaa8ccbc83e0fa7989ab7 (patch) | |
tree | bcc5976e7007f4495faf93e7858ce374478c5477 | |
parent | 1035f48f612f259cfa4b6433a5d62cda7355f4ff (diff) | |
parent | d02c5d8400d2b5c9782119e8cb9f36c59c37d0bd (diff) | |
download | base-62d5e041a2c219b1ce6aaa8ccbc83e0fa7989ab7.tar.gz |
Merge "Java side setup and access to Binder Proxy Tracking"android-o-mr1-iot-release-1.0.2
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; |