Loading services/core/java/com/android/server/wm/ActivityRecord.java +8 −0 Original line number Diff line number Diff line Loading @@ -830,6 +830,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // SystemUi sets the pinned mode on activity after transition is done. boolean mWaitForEnteringPinnedMode; private final ActivityRecordInputSink mActivityRecordInputSink; private final Runnable mPauseTimeoutRunnable = new Runnable() { @Override public void run() { Loading Loading @@ -1785,6 +1787,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A createTime = _createTime; } mAtmService.mPackageConfigPersister.updateConfigIfNeeded(this, mUserId, packageName); mActivityRecordInputSink = new ActivityRecordInputSink(this); } /** Loading Loading @@ -6771,6 +6775,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } else if (!show && mLastSurfaceShowing) { getSyncTransaction().hide(mSurfaceControl); } if (show) { mActivityRecordInputSink.applyChangesToSurfaceIfChanged( getSyncTransaction(), mSurfaceControl); } } if (mThumbnail != null) { mThumbnail.setShowing(getPendingTransaction(), show); Loading services/core/java/com/android/server/wm/ActivityRecordInputSink.java 0 → 100644 +171 −0 Original line number Diff line number Diff line /* * Copyright (C) 2021 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.server.wm; import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION; import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS; import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION; import android.app.compat.CompatChanges; import android.compat.annotation.ChangeId; import android.compat.annotation.Disabled; import android.os.IBinder; import android.os.InputConstants; import android.os.Looper; import android.util.Slog; import android.view.InputChannel; import android.view.InputEvent; import android.view.InputEventReceiver; import android.view.InputWindowHandle; import android.view.MotionEvent; import android.view.SurfaceControl; import android.view.WindowManager; import android.widget.Toast; /** * Creates a InputWindowHandle that catches all touches that would otherwise pass through an * Activity. */ class ActivityRecordInputSink { /** * Feature flag for making Activities consume all touches within their task bounds. */ @ChangeId @Disabled static final long ENABLE_TOUCH_OPAQUE_ACTIVITIES = 194480991L; private static final String TAG = "ActivityRecordInputSink"; private static final int NUMBER_OF_TOUCHES_TO_DISABLE = 3; private static final long TOAST_COOL_DOWN_MILLIS = 3000L; private final ActivityRecord mActivityRecord; private final boolean mIsCompatEnabled; // Hold on to InputEventReceiver to prevent it from getting GCd. private InputEventReceiver mInputEventReceiver; private InputWindowHandleWrapper mInputWindowHandleWrapper; private final String mName = Integer.toHexString(System.identityHashCode(this)) + " ActivityRecordInputSink"; private int mRapidTouchCount = 0; private IBinder mToken; private boolean mDisabled = false; ActivityRecordInputSink(ActivityRecord activityRecord) { mActivityRecord = activityRecord; mIsCompatEnabled = CompatChanges.isChangeEnabled(ENABLE_TOUCH_OPAQUE_ACTIVITIES, mActivityRecord.getUid()); } public void applyChangesToSurfaceIfChanged( SurfaceControl.Transaction transaction, SurfaceControl surfaceControl) { InputWindowHandleWrapper inputWindowHandleWrapper = getInputWindowHandleWrapper(); if (inputWindowHandleWrapper.isChanged()) { inputWindowHandleWrapper.applyChangesToSurface(transaction, surfaceControl); } } private InputWindowHandleWrapper getInputWindowHandleWrapper() { if (mInputWindowHandleWrapper == null) { mInputWindowHandleWrapper = new InputWindowHandleWrapper(createInputWindowHandle()); InputChannel inputChannel = mActivityRecord.mWmService.mInputManager.createInputChannel(mName); mToken = inputChannel.getToken(); mInputEventReceiver = createInputEventReceiver(inputChannel); } if (mDisabled || !mIsCompatEnabled || mActivityRecord.isAnimating(TRANSITION | PARENTS, ANIMATION_TYPE_APP_TRANSITION)) { // TODO(b/208662670): Investigate if we can have feature active during animations. mInputWindowHandleWrapper.setToken(null); } else if (mActivityRecord.mStartingData != null) { // TODO(b/208659130): Remove this special case // Don't block touches during splash screen. This is done to not show toasts for // touches passing through splash screens. b/171772640 mInputWindowHandleWrapper.setToken(null); } else { mInputWindowHandleWrapper.setToken(mToken); } return mInputWindowHandleWrapper; } private InputWindowHandle createInputWindowHandle() { InputWindowHandle inputWindowHandle = new InputWindowHandle( mActivityRecord.getInputApplicationHandle(false), mActivityRecord.getDisplayId()); inputWindowHandle.replaceTouchableRegionWithCrop( mActivityRecord.getParentSurfaceControl()); inputWindowHandle.name = mName; inputWindowHandle.ownerUid = mActivityRecord.getUid(); inputWindowHandle.ownerPid = mActivityRecord.getPid(); inputWindowHandle.layoutParamsFlags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH; inputWindowHandle.dispatchingTimeoutMillis = InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS; return inputWindowHandle; } private InputEventReceiver createInputEventReceiver(InputChannel inputChannel) { return new SinkInputEventReceiver(inputChannel, mActivityRecord.mAtmService.mUiHandler.getLooper()); } private void showAsToastAndLog(String message) { Toast.makeText(mActivityRecord.mAtmService.mUiContext, message, Toast.LENGTH_LONG).show(); Slog.wtf(TAG, message + " " + mActivityRecord.mActivityComponent); } private class SinkInputEventReceiver extends InputEventReceiver { private long mLastToast = 0; SinkInputEventReceiver(InputChannel inputChannel, Looper looper) { super(inputChannel, looper); } public void onInputEvent(InputEvent event) { if (!(event instanceof MotionEvent)) { Slog.wtf(TAG, "Received InputEvent that was not a MotionEvent"); finishInputEvent(event, true); return; } MotionEvent motionEvent = (MotionEvent) event; if (motionEvent.getAction() != MotionEvent.ACTION_DOWN) { finishInputEvent(event, true); return; } if (event.getEventTime() - mLastToast > TOAST_COOL_DOWN_MILLIS) { String message = "go/activity-touch-opaque - " + mActivityRecord.mActivityComponent.getPackageName() + " blocked the touch!"; showAsToastAndLog(message); mLastToast = event.getEventTime(); mRapidTouchCount = 1; } else if (++mRapidTouchCount >= NUMBER_OF_TOUCHES_TO_DISABLE && !mDisabled) { // Disable touch blocking until Activity Record is recreated. String message = "Disabled go/activity-touch-opaque - " + mActivityRecord.mActivityComponent.getPackageName(); showAsToastAndLog(message); mDisabled = true; } finishInputEvent(event, true); } } } Loading
services/core/java/com/android/server/wm/ActivityRecord.java +8 −0 Original line number Diff line number Diff line Loading @@ -830,6 +830,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // SystemUi sets the pinned mode on activity after transition is done. boolean mWaitForEnteringPinnedMode; private final ActivityRecordInputSink mActivityRecordInputSink; private final Runnable mPauseTimeoutRunnable = new Runnable() { @Override public void run() { Loading Loading @@ -1785,6 +1787,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A createTime = _createTime; } mAtmService.mPackageConfigPersister.updateConfigIfNeeded(this, mUserId, packageName); mActivityRecordInputSink = new ActivityRecordInputSink(this); } /** Loading Loading @@ -6771,6 +6775,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } else if (!show && mLastSurfaceShowing) { getSyncTransaction().hide(mSurfaceControl); } if (show) { mActivityRecordInputSink.applyChangesToSurfaceIfChanged( getSyncTransaction(), mSurfaceControl); } } if (mThumbnail != null) { mThumbnail.setShowing(getPendingTransaction(), show); Loading
services/core/java/com/android/server/wm/ActivityRecordInputSink.java 0 → 100644 +171 −0 Original line number Diff line number Diff line /* * Copyright (C) 2021 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.server.wm; import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION; import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS; import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION; import android.app.compat.CompatChanges; import android.compat.annotation.ChangeId; import android.compat.annotation.Disabled; import android.os.IBinder; import android.os.InputConstants; import android.os.Looper; import android.util.Slog; import android.view.InputChannel; import android.view.InputEvent; import android.view.InputEventReceiver; import android.view.InputWindowHandle; import android.view.MotionEvent; import android.view.SurfaceControl; import android.view.WindowManager; import android.widget.Toast; /** * Creates a InputWindowHandle that catches all touches that would otherwise pass through an * Activity. */ class ActivityRecordInputSink { /** * Feature flag for making Activities consume all touches within their task bounds. */ @ChangeId @Disabled static final long ENABLE_TOUCH_OPAQUE_ACTIVITIES = 194480991L; private static final String TAG = "ActivityRecordInputSink"; private static final int NUMBER_OF_TOUCHES_TO_DISABLE = 3; private static final long TOAST_COOL_DOWN_MILLIS = 3000L; private final ActivityRecord mActivityRecord; private final boolean mIsCompatEnabled; // Hold on to InputEventReceiver to prevent it from getting GCd. private InputEventReceiver mInputEventReceiver; private InputWindowHandleWrapper mInputWindowHandleWrapper; private final String mName = Integer.toHexString(System.identityHashCode(this)) + " ActivityRecordInputSink"; private int mRapidTouchCount = 0; private IBinder mToken; private boolean mDisabled = false; ActivityRecordInputSink(ActivityRecord activityRecord) { mActivityRecord = activityRecord; mIsCompatEnabled = CompatChanges.isChangeEnabled(ENABLE_TOUCH_OPAQUE_ACTIVITIES, mActivityRecord.getUid()); } public void applyChangesToSurfaceIfChanged( SurfaceControl.Transaction transaction, SurfaceControl surfaceControl) { InputWindowHandleWrapper inputWindowHandleWrapper = getInputWindowHandleWrapper(); if (inputWindowHandleWrapper.isChanged()) { inputWindowHandleWrapper.applyChangesToSurface(transaction, surfaceControl); } } private InputWindowHandleWrapper getInputWindowHandleWrapper() { if (mInputWindowHandleWrapper == null) { mInputWindowHandleWrapper = new InputWindowHandleWrapper(createInputWindowHandle()); InputChannel inputChannel = mActivityRecord.mWmService.mInputManager.createInputChannel(mName); mToken = inputChannel.getToken(); mInputEventReceiver = createInputEventReceiver(inputChannel); } if (mDisabled || !mIsCompatEnabled || mActivityRecord.isAnimating(TRANSITION | PARENTS, ANIMATION_TYPE_APP_TRANSITION)) { // TODO(b/208662670): Investigate if we can have feature active during animations. mInputWindowHandleWrapper.setToken(null); } else if (mActivityRecord.mStartingData != null) { // TODO(b/208659130): Remove this special case // Don't block touches during splash screen. This is done to not show toasts for // touches passing through splash screens. b/171772640 mInputWindowHandleWrapper.setToken(null); } else { mInputWindowHandleWrapper.setToken(mToken); } return mInputWindowHandleWrapper; } private InputWindowHandle createInputWindowHandle() { InputWindowHandle inputWindowHandle = new InputWindowHandle( mActivityRecord.getInputApplicationHandle(false), mActivityRecord.getDisplayId()); inputWindowHandle.replaceTouchableRegionWithCrop( mActivityRecord.getParentSurfaceControl()); inputWindowHandle.name = mName; inputWindowHandle.ownerUid = mActivityRecord.getUid(); inputWindowHandle.ownerPid = mActivityRecord.getPid(); inputWindowHandle.layoutParamsFlags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH; inputWindowHandle.dispatchingTimeoutMillis = InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS; return inputWindowHandle; } private InputEventReceiver createInputEventReceiver(InputChannel inputChannel) { return new SinkInputEventReceiver(inputChannel, mActivityRecord.mAtmService.mUiHandler.getLooper()); } private void showAsToastAndLog(String message) { Toast.makeText(mActivityRecord.mAtmService.mUiContext, message, Toast.LENGTH_LONG).show(); Slog.wtf(TAG, message + " " + mActivityRecord.mActivityComponent); } private class SinkInputEventReceiver extends InputEventReceiver { private long mLastToast = 0; SinkInputEventReceiver(InputChannel inputChannel, Looper looper) { super(inputChannel, looper); } public void onInputEvent(InputEvent event) { if (!(event instanceof MotionEvent)) { Slog.wtf(TAG, "Received InputEvent that was not a MotionEvent"); finishInputEvent(event, true); return; } MotionEvent motionEvent = (MotionEvent) event; if (motionEvent.getAction() != MotionEvent.ACTION_DOWN) { finishInputEvent(event, true); return; } if (event.getEventTime() - mLastToast > TOAST_COOL_DOWN_MILLIS) { String message = "go/activity-touch-opaque - " + mActivityRecord.mActivityComponent.getPackageName() + " blocked the touch!"; showAsToastAndLog(message); mLastToast = event.getEventTime(); mRapidTouchCount = 1; } else if (++mRapidTouchCount >= NUMBER_OF_TOUCHES_TO_DISABLE && !mDisabled) { // Disable touch blocking until Activity Record is recreated. String message = "Disabled go/activity-touch-opaque - " + mActivityRecord.mActivityComponent.getPackageName(); showAsToastAndLog(message); mDisabled = true; } finishInputEvent(event, true); } } }