diff options
| -rw-r--r-- | core/java/android/os/Binder.java | 73 | ||||
| -rw-r--r-- | core/java/android/os/BinderProxy.java | 22 | ||||
| -rw-r--r-- | core/tests/coretests/src/android/os/BinderProxyTest.java | 88 | ||||
| -rw-r--r-- | services/core/java/com/android/server/BinderCallsStatsService.java | 10 |
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(); } |