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

Commit a879f9c4 authored by Winson Chung's avatar Winson Chung
Browse files

Adding binder tracking support in tests

- Whitelist specific binder calls when handling touch during swipe up
  gestures
- Only track binding for touch handling and certain launcher lifecycle
  changes for now

Bug: 140246642
Change-Id: I6ba30280dd17da358662870f8719ae851536ad8b
parent 2e71b835
Loading
Loading
Loading
Loading
+11 −6
Original line number Diff line number Diff line
@@ -65,6 +65,10 @@ import android.view.InputEvent;
import android.view.MotionEvent;
import android.view.Surface;

import androidx.annotation.BinderThread;
import androidx.annotation.UiThread;
import androidx.annotation.WorkerThread;

import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.R;
import com.android.launcher3.ResourceUtils;
@@ -75,6 +79,7 @@ import com.android.launcher3.logging.UserEventDispatcher;
import com.android.launcher3.model.AppLaunchTracker;
import com.android.launcher3.provider.RestoreDbTask;
import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.uioverrides.DejankBinderTracker;
import com.android.launcher3.util.DefaultDisplay;
import com.android.quickstep.SysUINavigationMode.Mode;
import com.android.quickstep.SysUINavigationMode.NavigationModeChangeListener;
@@ -106,10 +111,6 @@ import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;

import androidx.annotation.BinderThread;
import androidx.annotation.UiThread;
import androidx.annotation.WorkerThread;

/**
 * Wrapper around a list for processing arguments.
 */
@@ -536,6 +537,7 @@ public class TouchInteractionService extends Service implements
    }

    private void onInputEvent(InputEvent ev) {
        DejankBinderTracker.allowBinderTrackingInTests();
        if (TestProtocol.sDebugTracing) {
            Log.d(TestProtocol.NO_BACKGROUND_TO_OVERVIEW_TAG, "onInputEvent " + ev);
        }
@@ -571,6 +573,7 @@ public class TouchInteractionService extends Service implements

        TOUCH_INTERACTION_LOG.addLog("onMotionEvent", event.getActionMasked());
        mUncheckedConsumer.onMotionEvent(event);
        DejankBinderTracker.disallowBinderTrackingInTests();
    }

    private boolean validSystemUiFlags() {
@@ -634,7 +637,8 @@ public class TouchInteractionService extends Service implements
    }

    private InputConsumer newBaseConsumer(boolean useSharedState, MotionEvent event) {
        RunningTaskInfo runningTaskInfo = mAM.getRunningTask(0);
        RunningTaskInfo runningTaskInfo = DejankBinderTracker.whitelistIpcs(
                () -> mAM.getRunningTask(0));
        if (!useSharedState) {
            sSwipeSharedState.clearAllState(false /* finishAnimation */);
        }
@@ -650,7 +654,8 @@ public class TouchInteractionService extends Service implements
        if (isExcludedAssistant(runningTaskInfo)) {
            // In the case where we are in the excluded assistant state, ignore it and treat the
            // running activity as the task behind the assistant
            runningTaskInfo = mAM.getRunningTask(ACTIVITY_TYPE_ASSISTANT);
            runningTaskInfo = DejankBinderTracker.whitelistIpcs(
                    () -> mAM.getRunningTask(ACTIVITY_TYPE_ASSISTANT));
            if (!ActivityManagerWrapper.isHomeTask(runningTaskInfo)) {
                final ComponentName homeComponent =
                    mOverviewComponentObserver.getHomeIntent().getComponent();
+8 −3
Original line number Diff line number Diff line
@@ -68,6 +68,7 @@ import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.logging.UserEventDispatcher;
import com.android.launcher3.uioverrides.DejankBinderTracker;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
@@ -423,9 +424,13 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
    private void initializeLauncherAnimationController() {
        buildAnimationController();

        DejankBinderTracker.whitelistIpcs(() -> {
            // Only used in debug builds
            if (LatencyTrackerCompat.isEnabled(mContext)) {
            LatencyTrackerCompat.logToggleRecents((int) (mLauncherFrameDrawnTime - mTouchTimeMs));
                LatencyTrackerCompat.logToggleRecents(
                        (int) (mLauncherFrameDrawnTime - mTouchTimeMs));
            }
        });

        // 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
+2 −1
Original line number Diff line number Diff line
@@ -45,6 +45,7 @@ import com.android.launcher3.R;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.appprediction.PredictionUiStateManager;
import com.android.launcher3.appprediction.PredictionUiStateManager.Client;
import com.android.launcher3.uioverrides.DejankBinderTracker;
import com.android.launcher3.views.ScrimView;
import com.android.quickstep.SysUINavigationMode;
import com.android.quickstep.util.ClipAnimationHelper;
@@ -172,7 +173,7 @@ public class LauncherRecentsView extends RecentsView<Launcher> implements StateL

    @Override
    public boolean shouldUseMultiWindowTaskSizeStrategy() {
        return mActivity.isInMultiWindowMode();
        return DejankBinderTracker.whitelistIpcs(() -> mActivity.isInMultiWindowMode());
    }

    @Override
+159 −0
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();
    }
}
+3 −1
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@ import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.model.AppLaunchTracker;
import com.android.launcher3.shortcuts.DeepShortcutManager;
import com.android.launcher3.uioverrides.DejankBinderTracker;
import com.android.launcher3.uioverrides.DisplayRotationListener;
import com.android.launcher3.uioverrides.WallpaperColorInfo;
import com.android.launcher3.util.PackageManagerHelper;
@@ -65,7 +66,8 @@ public abstract class BaseDraggingActivity extends BaseActivity
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mIsSafeModeEnabled = getPackageManager().isSafeMode();
        mIsSafeModeEnabled = DejankBinderTracker.whitelistIpcs(() ->
                getPackageManager().isSafeMode());
        mRotationListener = new DisplayRotationListener(this, this::onDeviceRotationChanged);

        // Update theme
Loading