Merge changes I02e88c93,Id0a2f52c

* changes:
  Use the BinderProxy#TransactListener to propagate the UID.
  Adds a mechanism to listen to proxy transact method calls.
This commit is contained in:
Olivier Gaillard
2018-10-30 10:27:23 +00:00
committed by Android (Google) Code Review
4 changed files with 192 additions and 1 deletions

View File

@@ -554,6 +554,79 @@ public class Binder implements IBinder {
sDumpDisabled = msg;
}
/**
* Listener to be notified about each proxy-side binder call.
*
* See {@link setProxyTransactListener}.
* @hide
*/
public interface ProxyTransactListener {
/**
* Called before onTransact.
*
* @return an object that will be passed back to #onTransactEnded (or null).
*/
Object onTransactStarted(IBinder binder, int transactionCode);
/**
* Called after onTranact (even when an exception is thrown).
*
* @param session The object return by #onTransactStarted.
*/
void onTransactEnded(@Nullable Object session);
}
/**
* Propagates the work source to binder calls executed by the system server.
*
* <li>By default, this listener will propagate the worksource if the outgoing call happens on
* the same thread as the incoming binder call.
* <li>Custom attribution can be done by calling {@link ThreadLocalWorkSourceUid#set(int)}.
* @hide
*/
public static class PropagateWorkSourceTransactListener implements ProxyTransactListener {
@Override
public Object onTransactStarted(IBinder binder, int transactionCode) {
// Note that {@link Binder#getCallingUid()} is already set to the UID of the current
// process when this method is called.
//
// We use ThreadLocalWorkSourceUid instead. It also allows feature owners to set
// {@link ThreadLocalWorkSourceUid#set(int) manually to attribute resources to a UID.
int uid = ThreadLocalWorkSourceUid.get();
if (uid >= 0) {
int originalUid = Binder.setThreadWorkSource(uid);
return Integer.valueOf(originalUid);
}
return null;
}
@Override
public void onTransactEnded(Object session) {
if (session != null) {
int uid = (int) session;
Binder.setThreadWorkSource(uid);
}
}
}
/**
* Sets a listener for the transact method on the proxy-side.
*
* <li>The listener is global. Only fast operations should be done to avoid thread
* contentions.
* <li>The listener implementation needs to handle synchronization if needed. The methods on the
* listener can be called concurrently.
* <li>Listener set will be used for new transactions. On-going transaction will still use the
* previous listener (if already set).
* <li>The listener is called on the critical path of the binder transaction so be careful about
* performance.
* <li>Never execute another binder transaction inside the listener.
* @hide
*/
public static void setProxyTransactListener(@Nullable ProxyTransactListener listener) {
BinderProxy.setTransactListener(listener);
}
/**
* Default implementation is a stub that returns false. You will want
* to override this to do the appropriate unmarshalling of transactions.

View File

@@ -17,6 +17,7 @@
package android.os;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.util.Log;
import android.util.SparseIntArray;
@@ -45,6 +46,15 @@ public final class BinderProxy implements IBinder {
// Assume the process-wide default value when created
volatile boolean mWarnOnBlocking = Binder.sWarnOnBlocking;
private static volatile Binder.ProxyTransactListener sTransactListener = null;
/**
* @see {@link Binder#setProxyTransactListener(listener)}.
*/
public static void setTransactListener(@Nullable Binder.ProxyTransactListener listener) {
sTransactListener = listener;
}
/*
* Map from longs to BinderProxy, retaining only a WeakReference to the BinderProxies.
* We roll our own only because we need to lazily remove WeakReferences during accesses
@@ -469,9 +479,21 @@ public final class BinderProxy implements IBinder {
Trace.traceBegin(Trace.TRACE_TAG_ALWAYS,
stackTraceElement.getClassName() + "." + stackTraceElement.getMethodName());
}
// Make sure the listener won't change while processing a transaction.
final Binder.ProxyTransactListener transactListener = sTransactListener;
Object session = null;
if (transactListener != null) {
session = transactListener.onTransactStarted(this, code);
}
try {
return transactNative(code, data, reply, flags);
} finally {
if (transactListener != null) {
transactListener.onTransactEnded(session);
}
if (tracingEnabled) {
Trace.traceEnd(Trace.TRACE_TAG_ALWAYS);
}

View File

@@ -0,0 +1,88 @@
/*
* Copyright (C) 2018 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 android.annotation.Nullable;
import android.content.Context;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.MediumTest;
public class BinderProxyTest extends AndroidTestCase {
private static class CountingListener implements Binder.ProxyTransactListener {
int mStartedCount;
int mEndedCount;
public Object onTransactStarted(IBinder binder, int transactionCode) {
mStartedCount++;
return null;
}
public void onTransactEnded(@Nullable Object session) {
mEndedCount++;
}
};
private PowerManager mPowerManager;
/**
* Setup any common data for the upcoming tests.
*/
@Override
public void setUp() throws Exception {
super.setUp();
mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
}
@MediumTest
public void testNoListener() throws Exception {
CountingListener listener = new CountingListener();
Binder.setProxyTransactListener(listener);
Binder.setProxyTransactListener(null);
mPowerManager.isInteractive();
assertEquals(0, listener.mStartedCount);
assertEquals(0, listener.mEndedCount);
}
@MediumTest
public void testListener() throws Exception {
CountingListener listener = new CountingListener();
Binder.setProxyTransactListener(listener);
mPowerManager.isInteractive();
assertEquals(1, listener.mStartedCount);
assertEquals(1, listener.mEndedCount);
}
@MediumTest
public void testSessionPropagated() throws Exception {
Binder.setProxyTransactListener(new Binder.ProxyTransactListener() {
public Object onTransactStarted(IBinder binder, int transactionCode) {
return "foo";
}
public void onTransactEnded(@Nullable Object session) {
assertEquals("foo", session);
}
});
// Check it does not throw..
mPowerManager.isInteractive();
}
}

View File

@@ -49,6 +49,7 @@ public class BinderCallsStatsService extends Binder {
private static final String PERSIST_SYS_BINDER_CALLS_DETAILED_TRACKING
= "persist.sys.binder_calls_detailed_tracking";
/** Listens for flag changes. */
private static class SettingsObserver extends ContentObserver {
private static final String SETTINGS_ENABLED_KEY = "enabled";
@@ -101,7 +102,14 @@ public class BinderCallsStatsService extends Binder {
final boolean enabled =
mParser.getBoolean(SETTINGS_ENABLED_KEY, BinderCallsStats.ENABLED_DEFAULT);
if (mEnabled != enabled) {
Binder.setObserver(enabled ? mBinderCallsStats : null);
if (enabled) {
Binder.setObserver(mBinderCallsStats);
Binder.setProxyTransactListener(
new Binder.PropagateWorkSourceTransactListener());
} else {
Binder.setObserver(null);
Binder.setProxyTransactListener(null);
}
mEnabled = enabled;
mBinderCallsStats.reset();
}