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

Commit 5e29d89c authored by Olivier Gaillard's avatar Olivier Gaillard Committed by Android (Google) Code Review
Browse files

Merge changes I99ba18d8,Id04f35f5,I4995c35e,Ia836da7d

* changes:
  Only keep one overflow entry per uid.
  Add app id instead of uid to WorkSourceProvider whitelist.
  Sets ThreadLocalWorkSource to authorized work source.
  Make sure callEnded is always called.
parents dc7fba4b a3686dd0
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);
            }
        }
    }
+33 −9
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,
@@ -971,10 +997,11 @@ public class Binder implements IBinder {
            if (tracingEnabled) {
                Trace.traceEnd(Trace.TRACE_TAG_ALWAYS);
            }
            if (observer != null) {
                observer.callEnded(callSession, data.dataSize(), reply.dataSize(), workSourceUid);
            }
        }
        checkParcel(this, code, reply, "Unreasonably large binder reply buffer");
        int replySizeBytes = reply.dataSize();
        int requestSizeBytes = data.dataSize();
        reply.recycle();
        data.recycle();

@@ -984,9 +1011,6 @@ public class Binder implements IBinder {
        // to the main transaction loop to wait for another incoming transaction.  Either
        // way, strict mode begone!
        StrictMode.clearGatheredViolations();
        if (observer != null) {
            observer.callEnded(callSession, requestSizeBytes, replySizeBytes);
        }
        return res;
    }
}
+14 −12
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;
@@ -61,7 +60,11 @@ public class BinderCallsStats implements BinderInternal.Observer {
    private static final int CALL_SESSIONS_POOL_SIZE = 100;
    private static final int MAX_EXCEPTION_COUNT_SIZE = 50;
    private static final String EXCEPTION_COUNT_OVERFLOW_NAME = "overflow";
    // Default values for overflow entry. The work source uid does not use a default value in order
    // to have on overflow entry per work source uid.
    private static final Class<? extends Binder> OVERFLOW_BINDER = OverflowBinder.class;
    private static final boolean OVERFLOW_SCREEN_INTERACTIVE = false;
    private static final int OVERFLOW_DIRECT_CALLING_UID = -1;
    private static final int OVERFLOW_TRANSACTION_CODE = -1;

    // Whether to collect all the data: cpu + exceptions + reply/request sizes.
@@ -105,7 +108,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 +132,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 +159,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 +458,6 @@ public class BinderCallsStats implements BinderInternal.Observer {
        return Binder.getCallingUid();
    }

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

    protected long getElapsedRealtimeMicro() {
        return SystemClock.elapsedRealtimeNanos() / 1000;
    }
@@ -660,14 +660,16 @@ public class BinderCallsStats implements BinderInternal.Observer {
            // Only create CallStat if it's a new entry, otherwise update existing instance.
            if (mapCallStat == null) {
                if (maxCallStatsReached) {
                    mapCallStat = get(callingUid, OVERFLOW_BINDER, OVERFLOW_TRANSACTION_CODE,
                            screenInteractive);
                    mapCallStat = get(OVERFLOW_DIRECT_CALLING_UID, OVERFLOW_BINDER,
                            OVERFLOW_TRANSACTION_CODE, OVERFLOW_SCREEN_INTERACTIVE);
                    if (mapCallStat != null) {
                        return mapCallStat;
                    }

                    callingUid = OVERFLOW_DIRECT_CALLING_UID;
                    binderClass = OVERFLOW_BINDER;
                    transactionCode = OVERFLOW_TRANSACTION_CODE;
                    screenInteractive = OVERFLOW_SCREEN_INTERACTIVE;
                }

                mapCallStat = new CallStat(callingUid, binderClass, transactionCode,
+21 −4
Original line number Diff line number Diff line
@@ -22,7 +22,6 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.SystemClock;
import android.util.EventLog;
import android.util.Log;
import android.util.SparseIntArray;

import com.android.internal.util.Preconditions;
@@ -86,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.
     */
@@ -95,14 +110,16 @@ 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.
         *
         * <li>This method will be called even when an exception is thrown.
         * <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