Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 76c231d0 authored by Olivier Gaillard's avatar Olivier Gaillard
Browse files

Sets ThreadLocalWorkSource to authorized work source.

Test: atest binderLibTest BinderWorkSourceTest BinderCallsStatsServiceTest
Change-Id: I4995c35eed87154312e5e560716d66b3fc2ae221
parent 0e4d61ea
Loading
Loading
Loading
Loading
+3 −2
Original line number Diff line number Diff line
@@ -39,6 +39,7 @@ import org.junit.runner.RunWith;
@LargeTest
public class BinderCallsStatsPerfTest {
    private static final int DEFAULT_BUCKET_SIZE = 1000;
    private static final int WORKSOURCE_UID = 1;
    static class FakeCpuTimeBinderCallsStats extends BinderCallsStats {
        private int mTimeMs;

@@ -117,8 +118,8 @@ public class BinderCallsStatsPerfTest {
        Binder b = new Binder();
        while (state.keepRunning()) {
            for (int i = 0; i < 10000; i++) {
                CallSession s = mBinderCallsStats.callStarted(b, i % maxBucketSize);
                mBinderCallsStats.callEnded(s, 0, 0);
                CallSession s = mBinderCallsStats.callStarted(b, i % maxBucketSize, WORKSOURCE_UID);
                mBinderCallsStats.callEnded(s, 0, 0, WORKSOURCE_UID);
            }
        }
    }
+31 −5
Original line number Diff line number Diff line
@@ -916,23 +916,49 @@ public class Binder implements IBinder {
    private static native long getNativeBBinderHolder();
    private static native long getFinalizer();

    /**
     * By default, we use the calling uid since we can always trust it.
     */
    private static volatile BinderInternal.WorkSourceProvider sWorkSourceProvider =
            Binder::getCallingUid;

    /**
     * Sets the work source provider.
     *
     * <li>The callback is global. Only fast operations should be done to avoid thread
     * contentions.
     * <li>The callback implementation needs to handle synchronization if needed. The methods on the
     * callback can be called concurrently.
     * <li>The callback is called on the critical path of the binder transaction so be careful about
     * performance.
     * <li>Never execute another binder transaction inside the callback.
     * @hide
     */
    public static void setWorkSourceProvider(BinderInternal.WorkSourceProvider workSourceProvider) {
        if (workSourceProvider == null) {
            throw new IllegalArgumentException("workSourceProvider cannot be null");
        }
        sWorkSourceProvider = workSourceProvider;
    }

    // Entry point from android_util_Binder.cpp's onTransact
    private boolean execTransact(int code, long dataObj, long replyObj,
            int flags) {
        final long origWorkSource = ThreadLocalWorkSource.setUid(Binder.getCallingUid());
        final int workSourceUid = sWorkSourceProvider.resolveWorkSourceUid();
        final long origWorkSource = ThreadLocalWorkSource.setUid(workSourceUid);
        try {
            return execTransactInternal(code, dataObj, replyObj, flags);
            return execTransactInternal(code, dataObj, replyObj, flags, workSourceUid);
        } finally {
            ThreadLocalWorkSource.restore(origWorkSource);
        }
    }

    private boolean execTransactInternal(int code, long dataObj, long replyObj,
            int flags) {
            int flags, int workSourceUid) {
        // Make sure the observer won't change while processing a transaction.
        final BinderInternal.Observer observer = sObserver;
        final CallSession callSession =
                observer != null ? observer.callStarted(this, code) : null;
                observer != null ? observer.callStarted(this, code, workSourceUid) : null;
        Parcel data = Parcel.obtain(dataObj);
        Parcel reply = Parcel.obtain(replyObj);
        // theoretically, we should call transact, which will call onTransact,
@@ -972,7 +998,7 @@ public class Binder implements IBinder {
                Trace.traceEnd(Trace.TRACE_TAG_ALWAYS);
            }
            if (observer != null) {
                observer.callEnded(callSession, data.dataSize(), reply.dataSize());
                observer.callEnded(callSession, data.dataSize(), reply.dataSize(), workSourceUid);
            }
        }
        checkParcel(this, code, reply, "Unreasonably large binder reply buffer");
+6 −10
Original line number Diff line number Diff line
@@ -21,7 +21,6 @@ import android.annotation.Nullable;
import android.os.Binder;
import android.os.Process;
import android.os.SystemClock;
import android.os.ThreadLocalWorkSource;
import android.text.format.DateFormat;
import android.util.ArrayMap;
import android.util.Pair;
@@ -105,7 +104,7 @@ public class BinderCallsStats implements BinderInternal.Observer {

    @Override
    @Nullable
    public CallSession callStarted(Binder binder, int code) {
    public CallSession callStarted(Binder binder, int code, int workSourceUid) {
        if (mDeviceState == null || mDeviceState.isCharging()) {
            return null;
        }
@@ -129,19 +128,21 @@ public class BinderCallsStats implements BinderInternal.Observer {
    }

    @Override
    public void callEnded(@Nullable CallSession s, int parcelRequestSize, int parcelReplySize) {
    public void callEnded(@Nullable CallSession s, int parcelRequestSize,
            int parcelReplySize, int workSourceUid) {
        if (s == null) {
            return;
        }

        processCallEnded(s, parcelRequestSize, parcelReplySize);
        processCallEnded(s, parcelRequestSize, parcelReplySize, workSourceUid);

        if (mCallSessionsPool.size() < CALL_SESSIONS_POOL_SIZE) {
            mCallSessionsPool.add(s);
        }
    }

    private void processCallEnded(CallSession s, int parcelRequestSize, int parcelReplySize) {
    private void processCallEnded(CallSession s,
            int parcelRequestSize, int parcelReplySize, int workSourceUid) {
        // Non-negative time signals we need to record data for this call.
        final boolean recordCall = s.cpuTimeStarted >= 0;
        final long duration;
@@ -154,7 +155,6 @@ public class BinderCallsStats implements BinderInternal.Observer {
            latencyDuration = 0;
        }
        final int callingUid = getCallingUid();
        final int workSourceUid = getWorkSourceUid();

        synchronized (mLock) {
            // This was already checked in #callStart but check again while synchronized.
@@ -454,10 +454,6 @@ public class BinderCallsStats implements BinderInternal.Observer {
        return Binder.getCallingUid();
    }

    protected int getWorkSourceUid() {
        return ThreadLocalWorkSource.getUid();
    }

    protected long getElapsedRealtimeMicro() {
        return SystemClock.elapsedRealtimeNanos() / 1000;
    }
+19 −2
Original line number Diff line number Diff line
@@ -85,6 +85,22 @@ public class BinderInternal {
        boolean exceptionThrown;
    }


    /**
     * Responsible for resolving a work source.
     */
    @FunctionalInterface
    public interface WorkSourceProvider {
        /**
         * <p>This method is called in a critical path of the binder transaction.
         * <p>The implementation should never execute a binder call since it is called during a
         * binder transaction.
         *
         * @return the uid of the process to attribute the binder transaction to.
         */
        int resolveWorkSourceUid();
    }

    /**
     * Allows to track various steps of an API call.
     */
@@ -94,7 +110,7 @@ public class BinderInternal {
         *
         * @return a CallSession to pass to the callEnded method.
         */
        CallSession callStarted(Binder binder, int code);
        CallSession callStarted(Binder binder, int code, int workSourceUid);

        /**
         * Called when a binder call stops.
@@ -102,7 +118,8 @@ public class BinderInternal {
         * <li>This method will be called even when an exception is thrown by the binder stub
         * implementation.
         */
        void callEnded(CallSession s, int parcelRequestSize, int parcelReplySize);
        void callEnded(CallSession s, int parcelRequestSize, int parcelReplySize,
                int workSourceUid);

        /**
         * Called if an exception is thrown while executing the binder transaction.
+16 −0
Original line number Diff line number Diff line
@@ -25,9 +25,25 @@ import android.content.Intent;
public class BinderWorkSourceService extends Service {
    private final IBinderWorkSourceService.Stub mBinder =
            new IBinderWorkSourceService.Stub() {
        public int getBinderCallingUid() {
            return Binder.getCallingUid();
        }

        public int getIncomingWorkSourceUid() {
            return Binder.getCallingWorkSourceUid();
        }

        public int getThreadLocalWorkSourceUid() {
            return ThreadLocalWorkSource.getUid();
        }

        public void setWorkSourceProvider(int uid) {
            Binder.setWorkSourceProvider(() -> uid);
        }

        public void clearWorkSourceProvider() {
            Binder.setWorkSourceProvider(Binder::getCallingUid);
        }
    };

    public IBinder onBind(Intent intent) {
Loading