Loading core/java/android/os/Binder.java +73 −0 Original line number Diff line number Diff line Loading @@ -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. Loading core/java/android/os/BinderProxy.java +22 −0 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package android.os; import android.annotation.NonNull; import android.annotation.Nullable; import android.util.Log; import android.util.SparseIntArray; Loading Loading @@ -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 Loading Loading @@ -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); } Loading core/tests/coretests/src/android/os/BinderProxyTest.java 0 → 100644 +88 −0 Original line number Diff line number Diff line /* * 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(); } } services/core/java/com/android/server/BinderCallsStatsService.java +9 −1 Original line number Diff line number Diff line Loading @@ -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"; Loading Loading @@ -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(); } Loading Loading
core/java/android/os/Binder.java +73 −0 Original line number Diff line number Diff line Loading @@ -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. Loading
core/java/android/os/BinderProxy.java +22 −0 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package android.os; import android.annotation.NonNull; import android.annotation.Nullable; import android.util.Log; import android.util.SparseIntArray; Loading Loading @@ -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 Loading Loading @@ -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); } Loading
core/tests/coretests/src/android/os/BinderProxyTest.java 0 → 100644 +88 −0 Original line number Diff line number Diff line /* * 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(); } }
services/core/java/com/android/server/BinderCallsStatsService.java +9 −1 Original line number Diff line number Diff line Loading @@ -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"; Loading Loading @@ -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(); } Loading