Merge "Java side setup and access to Binder Proxy Tracking"

This commit is contained in:
Treehugger Robot
2018-07-10 16:24:47 +00:00
committed by Gerrit Code Review
19 changed files with 1188 additions and 2 deletions

View File

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

View File

@@ -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,

View File

@@ -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

View File

@@ -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"/>

View File

@@ -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)

View File

@@ -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>

View File

@@ -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;
}
}

View File

@@ -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)

View File

@@ -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>

View File

@@ -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;
}
}

View File

@@ -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());
}
}

View File

@@ -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)

View File

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

View File

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

View File

@@ -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();
}

View File

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

View File

@@ -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 {
}

View File

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

View File

@@ -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;