From a879f9c481bb324a7ec38a238c8a7f306b58b868 Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Fri, 6 Sep 2019 12:04:27 -0700 Subject: [PATCH] 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 --- .../quickstep/TouchInteractionService.java | 17 +- .../WindowTransformSwipeHandler.java | 11 +- .../quickstep/views/LauncherRecentsView.java | 3 +- .../uioverrides/DejankBinderTracker.java | 159 ++++++++++++++++++ .../launcher3/BaseDraggingActivity.java | 4 +- src/com/android/launcher3/Launcher.java | 7 + .../util/MainThreadInitializedObject.java | 4 +- .../uioverrides/DejankBinderTracker.java | 57 +++++++ tests/Android.mk | 3 +- 9 files changed, 252 insertions(+), 13 deletions(-) create mode 100644 quickstep/src/com/android/launcher3/uioverrides/DejankBinderTracker.java create mode 100644 src_ui_overrides/com/android/launcher3/uioverrides/DejankBinderTracker.java diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java index d7ed090d93..7c1bc4ed8a 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java @@ -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(); diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java index cd31fe0ad3..db8eb27533 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java @@ -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 private void initializeLauncherAnimationController() { buildAnimationController(); - if (LatencyTrackerCompat.isEnabled(mContext)) { - LatencyTrackerCompat.logToggleRecents((int) (mLauncherFrameDrawnTime - mTouchTimeMs)); - } + DejankBinderTracker.whitelistIpcs(() -> { + // Only used in debug builds + if (LatencyTrackerCompat.isEnabled(mContext)) { + 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 diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java index 03441c87ec..c117361480 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java @@ -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 implements StateL @Override public boolean shouldUseMultiWindowTaskSizeStrategy() { - return mActivity.isInMultiWindowMode(); + return DejankBinderTracker.whitelistIpcs(() -> mActivity.isInMultiWindowMode()); } @Override diff --git a/quickstep/src/com/android/launcher3/uioverrides/DejankBinderTracker.java b/quickstep/src/com/android/launcher3/uioverrides/DejankBinderTracker.java new file mode 100644 index 0000000000..ff4c0f03d1 --- /dev/null +++ b/quickstep/src/com/android/launcher3/uioverrides/DejankBinderTracker.java @@ -0,0 +1,159 @@ +/** + * 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 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 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 whitelistIpcs(Supplier 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 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(); + } +} diff --git a/src/com/android/launcher3/BaseDraggingActivity.java b/src/com/android/launcher3/BaseDraggingActivity.java index 994ba65a40..e2ef337c53 100644 --- a/src/com/android/launcher3/BaseDraggingActivity.java +++ b/src/com/android/launcher3/BaseDraggingActivity.java @@ -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 diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index a2db41133b..4f8b20e0b4 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -116,6 +116,7 @@ import com.android.launcher3.qsb.QsbContainerView; import com.android.launcher3.states.InternalStateHandler; import com.android.launcher3.states.RotationHelper; import com.android.launcher3.touch.ItemClickHandler; +import com.android.launcher3.uioverrides.DejankBinderTracker; import com.android.launcher3.uioverrides.UiFactory; import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper; import com.android.launcher3.userevent.nano.LauncherLogProto; @@ -309,6 +310,7 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, @Override protected void onCreate(Bundle savedInstanceState) { + DejankBinderTracker.allowBinderTrackingInTests(); RaceConditionTracker.onEvent(ON_CREATE_EVT, ENTER); if (DEBUG_STRICT_MODE) { StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder() @@ -430,6 +432,7 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, } } }); + DejankBinderTracker.disallowBinderTrackingInTests(); } protected LauncherOverlayManager getDefaultOverlay() { @@ -941,6 +944,7 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, @Override protected void onStart() { + DejankBinderTracker.allowBinderTrackingInTests(); RaceConditionTracker.onEvent(ON_START_EVT, ENTER); super.onStart(); if (!mDeferOverlayCallbacks) { @@ -949,6 +953,7 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, mAppWidgetHost.setListenIfResumed(true); RaceConditionTracker.onEvent(ON_START_EVT, EXIT); + DejankBinderTracker.disallowBinderTrackingInTests(); } private void handleDeferredResume() { @@ -1043,6 +1048,7 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, @Override protected void onResume() { + DejankBinderTracker.allowBinderTrackingInTests(); RaceConditionTracker.onEvent(ON_RESUME_EVT, ENTER); TraceHelper.beginSection("ON_RESUME"); super.onResume(); @@ -1068,6 +1074,7 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, TraceHelper.endSection("ON_RESUME"); RaceConditionTracker.onEvent(ON_RESUME_EVT, EXIT); + DejankBinderTracker.disallowBinderTrackingInTests(); } @Override diff --git a/src/com/android/launcher3/util/MainThreadInitializedObject.java b/src/com/android/launcher3/util/MainThreadInitializedObject.java index fe9c2c468b..cf4e8c796b 100644 --- a/src/com/android/launcher3/util/MainThreadInitializedObject.java +++ b/src/com/android/launcher3/util/MainThreadInitializedObject.java @@ -22,6 +22,7 @@ import android.os.Looper; import androidx.annotation.VisibleForTesting; +import com.android.launcher3.uioverrides.DejankBinderTracker; import com.android.launcher3.util.ResourceBasedOverride.Overrides; import java.util.concurrent.ExecutionException; @@ -41,7 +42,8 @@ public class MainThreadInitializedObject { public T get(Context context) { if (mValue == null) { if (Looper.myLooper() == Looper.getMainLooper()) { - mValue = mProvider.get(context.getApplicationContext()); + mValue = DejankBinderTracker.whitelistIpcs(() -> + mProvider.get(context.getApplicationContext())); } else { try { return MAIN_EXECUTOR.submit(() -> get(context)).get(); diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/DejankBinderTracker.java b/src_ui_overrides/com/android/launcher3/uioverrides/DejankBinderTracker.java new file mode 100644 index 0000000000..47f6ac6f75 --- /dev/null +++ b/src_ui_overrides/com/android/launcher3/uioverrides/DejankBinderTracker.java @@ -0,0 +1,57 @@ +/** + * 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 android.os.IBinder; + +import java.util.function.BiConsumer; +import java.util.function.Supplier; + +/** + * A binder proxy transaction listener for tracking non-whitelisted binder calls. + */ +public class DejankBinderTracker { + public static void whitelistIpcs(Runnable runnable) {} + + public static T whitelistIpcs(Supplier supplier) { + return null; + } + + public static void allowBinderTrackingInTests() {} + + public static void disallowBinderTrackingInTests() {} + + public DejankBinderTracker(BiConsumer unexpectedTransactionCallback) { } + + public void startTracking() {} + + public void stopTracking() {} + + public Object onTransactStarted(IBinder binder, int transactionCode, int flags) { + return null; + } + + public Object onTransactStarted(IBinder binder, int transactionCode) { + return null; + } + + public void onTransactEnded(Object session) {} + + public static boolean isMainThread() { + return true; + } +} diff --git a/tests/Android.mk b/tests/Android.mk index 02ead4ef42..b5c1dae612 100644 --- a/tests/Android.mk +++ b/tests/Android.mk @@ -30,13 +30,14 @@ else LOCAL_STATIC_JAVA_LIBRARIES += SystemUISharedLib LOCAL_SRC_FILES := $(call all-java-files-under, tapl) \ + ../quickstep/src/com/android/launcher3/uioverrides/DejankBinderTracker.java \ ../src/com/android/launcher3/ResourceUtils.java \ ../src/com/android/launcher3/util/SecureSettingsObserver.java \ ../src/com/android/launcher3/testing/TestProtocol.java endif LOCAL_MODULE := ub-launcher-aosp-tapl -LOCAL_SDK_VERSION := current +LOCAL_SDK_VERSION := system_current include $(BUILD_STATIC_JAVA_LIBRARY)