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

Commit 3db98b11 authored by Sunny Goyal's avatar Sunny Goyal
Browse files

Adding main thread binder tracing in development build.

> Unifying multiple binder tracing implementations
> Removing wallpaper customization check as it has been rolled out
  few years ago, this avoids additional RPC to check for wallpaper
> Removing RaceConditionReproducer as it is not used anywhere and
  is not integrated with this tracing anymore

Bug: 273718665
Test: Presubmit
Flag: N/A
Change-Id: I801af5d039a9d38d5d39e803723ec6da50a9d59f
parent c7953f66
Loading
Loading
Loading
Loading
+0 −159
Original line number Diff line number Diff line
/**
 * Copyright (C) 2019 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 com.android.launcher3.uioverrides;

import static android.os.IBinder.FLAG_ONEWAY;

import android.os.Binder;
import android.os.Build;
import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
import android.util.Log;

import androidx.annotation.MainThread;

import java.util.HashSet;
import java.util.Locale;
import java.util.function.BiConsumer;
import java.util.function.Supplier;

/**
 * A binder proxy transaction listener for tracking non-whitelisted binder calls.
 */
public class DejankBinderTracker implements Binder.ProxyTransactListener {
    private static final String TAG = "DejankBinderTracker";

    private static final Object sLock = new Object();
    private static final HashSet<String> sWhitelistedFrameworkClasses = new HashSet<>();
    static {
        // Common IPCs that are ok to block the main thread.
        sWhitelistedFrameworkClasses.add("android.view.IWindowSession");
        sWhitelistedFrameworkClasses.add("android.os.IPowerManager");
    }
    private static boolean sTemporarilyIgnoreTracking = false;

    // Used by the client to limit binder tracking to specific regions
    private static boolean sTrackingAllowed = false;

    private BiConsumer<String, Integer> mUnexpectedTransactionCallback;
    private boolean mIsTracking = false;

    /**
     * Temporarily ignore blocking binder calls for the duration of this {@link Runnable}.
     */
    @MainThread
    public static void whitelistIpcs(Runnable runnable) {
        sTemporarilyIgnoreTracking = true;
        runnable.run();
        sTemporarilyIgnoreTracking = false;
    }

    /**
     * Temporarily ignore blocking binder calls for the duration of this {@link Supplier}.
     */
    @MainThread
    public static <T> T whitelistIpcs(Supplier<T> supplier) {
        sTemporarilyIgnoreTracking = true;
        T value = supplier.get();
        sTemporarilyIgnoreTracking = false;
        return value;
    }

    /**
     * Enables binder tracking during a test.
     */
    @MainThread
    public static void allowBinderTrackingInTests() {
        sTrackingAllowed = true;
    }

    /**
     * Disables binder tracking during a test.
     */
    @MainThread
    public static void disallowBinderTrackingInTests() {
        sTrackingAllowed = false;
    }

    public DejankBinderTracker(BiConsumer<String, Integer> unexpectedTransactionCallback) {
        mUnexpectedTransactionCallback = unexpectedTransactionCallback;
    }

    @MainThread
    public void startTracking() {
        if (!Build.TYPE.toLowerCase(Locale.ROOT).contains("debug")
                && !Build.TYPE.toLowerCase(Locale.ROOT).equals("eng")) {
            Log.wtf(TAG, "Unexpected use of binder tracker in non-debug build", new Exception());
            return;
        }
        if (mIsTracking) {
            return;
        }
        mIsTracking = true;
        Binder.setProxyTransactListener(this);
    }

    @MainThread
    public void stopTracking() {
        if (!mIsTracking) {
            return;
        }
        mIsTracking = false;
        Binder.setProxyTransactListener(null);
    }

    // Override the hidden Binder#onTransactStarted method
    public synchronized Object onTransactStarted(IBinder binder, int transactionCode, int flags) {
        if (!mIsTracking
                || !sTrackingAllowed
                || sTemporarilyIgnoreTracking
                || (flags & FLAG_ONEWAY) == FLAG_ONEWAY
                || !isMainThread()) {
            return null;
        }

        String descriptor;
        try {
            descriptor = binder.getInterfaceDescriptor();
            if (sWhitelistedFrameworkClasses.contains(descriptor)) {
                return null;
            }
        } catch (RemoteException e) {
            e.printStackTrace();
            descriptor = binder.getClass().getSimpleName();
        }

        mUnexpectedTransactionCallback.accept(descriptor, transactionCode);
        return null;
    }

    @Override
    public Object onTransactStarted(IBinder binder, int transactionCode) {
        // Do nothing
        return null;
    }

    @Override
    public void onTransactEnded(Object session) {
        // Do nothing
    }

    public static boolean isMainThread() {
        return Thread.currentThread() == Looper.getMainLooper().getThread();
    }
}
+9 −10
Original line number Diff line number Diff line
@@ -112,6 +112,7 @@ import com.android.launcher3.tracing.SwipeHandlerProto;
import com.android.launcher3.uioverrides.QuickstepLauncher;
import com.android.launcher3.util.ActivityLifecycleCallbacksAdapter;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.SafeCloseable;
import com.android.launcher3.util.TraceHelper;
import com.android.launcher3.util.VibratorWrapper;
import com.android.launcher3.util.WindowBounds;
@@ -587,7 +588,7 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
        if (mWasLauncherAlreadyVisible) {
            mStateCallback.setState(STATE_LAUNCHER_DRAWN);
        } else {
            Object traceToken = TraceHelper.INSTANCE.beginSection("WTS-init");
            SafeCloseable traceToken = TraceHelper.INSTANCE.beginAsyncSection("WTS-init");
            View dragLayer = activity.getDragLayer();
            dragLayer.getViewTreeObserver().addOnDrawListener(new OnDrawListener() {
                boolean mHandled = false;
@@ -599,7 +600,7 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
                    }
                    mHandled = true;

                    TraceHelper.INSTANCE.endSection(traceToken);
                    traceToken.close();
                    dragLayer.post(() ->
                            dragLayer.getViewTreeObserver().removeOnDrawListener(this));
                    if (activity != mActivity) {
@@ -681,11 +682,10 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
    private void initializeLauncherAnimationController() {
        buildAnimationController();

        Object traceToken = TraceHelper.INSTANCE.beginSection("logToggleRecents",
                TraceHelper.FLAG_IGNORE_BINDERS);
        try (SafeCloseable c = TraceHelper.INSTANCE.allowIpcs("logToggleRecents")) {
            LatencyTracker.getInstance(mContext).logAction(LatencyTracker.ACTION_TOGGLE_RECENTS,
                    (int) (mLauncherFrameDrawnTime - mTouchTimeMs));
        TraceHelper.INSTANCE.endSection(traceToken);
        }

        // This method is only called when STATE_GESTURE_STARTED is set, so we can enable the
        // high-res thumbnail loader here once we are sure that we will end up in an overview state
@@ -2039,10 +2039,9 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,

    private void setScreenshotCapturedState() {
        // If we haven't posted a draw callback, set the state immediately.
        Object traceToken = TraceHelper.INSTANCE.beginSection(SCREENSHOT_CAPTURED_EVT,
                TraceHelper.FLAG_CHECK_FOR_RACE_CONDITIONS);
        TraceHelper.INSTANCE.beginSection(SCREENSHOT_CAPTURED_EVT);
        mStateCallback.setStateOnUiThread(STATE_SCREENSHOT_CAPTURED);
        TraceHelper.INSTANCE.endSection(traceToken);
        TraceHelper.INSTANCE.endSection();
    }

    private void finishCurrentTransitionToRecents() {
+187 −0
Original line number Diff line number Diff line
/**
 * Copyright (C) 2023 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 com.android.quickstep;

import static android.os.IBinder.FLAG_ONEWAY;

import android.os.Binder;
import android.os.Binder.ProxyTransactListener;
import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
import android.os.Trace;
import android.util.Log;

import androidx.annotation.Nullable;

import com.android.launcher3.util.SafeCloseable;
import com.android.launcher3.util.TraceHelper;

import java.util.LinkedList;
import java.util.Set;
import java.util.function.Consumer;

import kotlin.random.Random;

/**
 * A binder proxy transaction listener for tracking binder calls on main thread.
 */
public class BinderTracker {

    private static final String TAG = "BinderTracker";

    // Common IPCs that are ok to block the main thread.
    private static final Set<String> sAllowedFrameworkClasses = Set.of(
            "android.view.IWindowSession",
            "android.os.IPowerManager",
            "android.os.IServiceManager");

    /**
     * Starts tracking binder class and returns a {@link SafeCloseable} to end tracking
     */
    public static SafeCloseable startTracking(Consumer<BinderCallSite> callback) {
        TraceHelper current = TraceHelper.INSTANCE;

        TraceHelperExtension helper = new TraceHelperExtension(callback);
        TraceHelper.INSTANCE = helper;
        Binder.setProxyTransactListener(helper);

        return () -> {
            Binder.setProxyTransactListener(null);
            TraceHelper.INSTANCE = current;
        };
    }

    private static final LinkedList<String> mMainThreadTraceStack = new LinkedList<>();
    private static final LinkedList<String> mMainThreadIgnoreIpcStack = new LinkedList<>();

    private static class TraceHelperExtension extends TraceHelper implements ProxyTransactListener {

        private final Consumer<BinderCallSite> mUnexpectedTransactionCallback;

        TraceHelperExtension(Consumer<BinderCallSite> unexpectedTransactionCallback) {
            mUnexpectedTransactionCallback = unexpectedTransactionCallback;
        }

        @Override
        public void beginSection(String sectionName) {
            if (isMainThread()) {
                mMainThreadTraceStack.add(sectionName);
            }
            super.beginSection(sectionName);
        }

        @Override
        public SafeCloseable beginAsyncSection(String sectionName) {
            if (!isMainThread()) {
                return super.beginAsyncSection(sectionName);
            }

            mMainThreadTraceStack.add(sectionName);
            int cookie = Random.Default.nextInt();
            Trace.beginAsyncSection(sectionName, cookie);
            return () -> {
                Trace.endAsyncSection(sectionName, cookie);
                mMainThreadTraceStack.remove(sectionName);
            };
        }

        @Override
        public void endSection() {
            super.endSection();
            if (isMainThread()) {
                mMainThreadTraceStack.pollLast();
            }
        }

        @Override
        public SafeCloseable allowIpcs(String rpcName) {
            if (!isMainThread()) {
                return super.allowIpcs(rpcName);
            }

            mMainThreadTraceStack.add(rpcName);
            mMainThreadIgnoreIpcStack.add(rpcName);
            int cookie = Random.Default.nextInt();
            Trace.beginAsyncSection(rpcName, cookie);
            return () -> {
                Trace.endAsyncSection(rpcName, cookie);
                mMainThreadTraceStack.remove(rpcName);
                mMainThreadIgnoreIpcStack.remove(rpcName);
            };
        }

        @Override
        public Object onTransactStarted(IBinder binder, int transactionCode, int flags) {
            if (!isMainThread() || (flags & FLAG_ONEWAY) == FLAG_ONEWAY) {
                return null;
            }

            String ipcBypass = mMainThreadIgnoreIpcStack.peekLast();
            String descriptor;
            try {
                descriptor = binder.getInterfaceDescriptor();
                if (sAllowedFrameworkClasses.contains(descriptor)) {
                    return null;
                }
            } catch (RemoteException e) {
                Log.e(TAG, "Error getting IPC descriptor", e);
                descriptor = binder.getClass().getSimpleName();
            }

            if (ipcBypass == null) {
                mUnexpectedTransactionCallback.accept(new BinderCallSite(
                        mMainThreadTraceStack.peekLast(), descriptor, transactionCode));
            } else {
                Log.d(TAG, "MainThread-IPC " + descriptor + " ignored due to " + ipcBypass);
            }
            return null;
        }

        @Override
        public Object onTransactStarted(IBinder binder, int transactionCode) {
            // Do nothing
            return null;
        }

        @Override
        public void onTransactEnded(Object session) {
            // Do nothing
        }
    }

    private static boolean isMainThread() {
        return Thread.currentThread() == Looper.getMainLooper().getThread();
    }

    /**
     * Information about a binder call
     */
    public static class BinderCallSite {

        @Nullable
        public final String activeTrace;
        public final String descriptor;
        public final int transactionCode;

        BinderCallSite(String activeTrace, String descriptor, int transactionCode) {
            this.activeTrace = activeTrace;
            this.descriptor = descriptor;
            this.transactionCode = transactionCode;
        }
    }
}
+13 −0
Original line number Diff line number Diff line
@@ -16,10 +16,13 @@

package com.android.quickstep;

import android.app.ActivityThread;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.os.RemoteException;
import android.util.Log;

import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.util.InstantAppResolver;
@@ -49,4 +52,14 @@ public class InstantAppResolverImpl extends InstantAppResolver {
        ComponentName cn = info.getTargetComponent();
        return cn != null && cn.getClassName().equals(COMPONENT_CLASS_MARKER);
    }

    @Override
    public boolean isInstantApp(String packageName, int userId) {
        try {
            return ActivityThread.getPackageManager().isInstantApp(packageName, userId);
        } catch (RemoteException e) {
            Log.e(TAG, "Failed to determine whether package is instant app " + packageName, e);
            return false;
        }
    }
}
+5 −0
Original line number Diff line number Diff line
@@ -60,5 +60,10 @@ public class QuickstepProcessInitializer extends MainProcessInitializer {
        // Elevate GPU priority for Quickstep and Remote animations.
        ThreadedRenderer.setContextPriority(
                ThreadedRenderer.EGL_CONTEXT_PRIORITY_HIGH_IMG);

        if (BuildConfig.IS_STUDIO_BUILD) {
            BinderTracker.startTracking(call ->  Log.e("BinderCall",
                    call.descriptor + " called on mainthread under " + call.activeTrace));
        }
    }
}
Loading