summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/os/Binder.java73
-rw-r--r--core/java/android/os/BinderProxy.java22
-rw-r--r--core/tests/coretests/src/android/os/BinderProxyTest.java88
-rw-r--r--services/core/java/com/android/server/BinderCallsStatsService.java10
4 files changed, 192 insertions, 1 deletions
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index 8b6194c29707..e4f0358173e0 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -555,6 +555,79 @@ public class Binder implements IBinder {
}
/**
+ * 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.
*
diff --git a/core/java/android/os/BinderProxy.java b/core/java/android/os/BinderProxy.java
index 591370ff728b..720c16723c63 100644
--- a/core/java/android/os/BinderProxy.java
+++ b/core/java/android/os/BinderProxy.java
@@ -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);
}
diff --git a/core/tests/coretests/src/android/os/BinderProxyTest.java b/core/tests/coretests/src/android/os/BinderProxyTest.java
new file mode 100644
index 000000000000..4c36b5c359a2
--- /dev/null
+++ b/core/tests/coretests/src/android/os/BinderProxyTest.java
@@ -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();
+ }
+}
diff --git a/services/core/java/com/android/server/BinderCallsStatsService.java b/services/core/java/com/android/server/BinderCallsStatsService.java
index 872261a035e5..dd960751ab21 100644
--- a/services/core/java/com/android/server/BinderCallsStatsService.java
+++ b/services/core/java/com/android/server/BinderCallsStatsService.java
@@ -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();
}