Merge "Block touches from passing through activities"
This commit is contained in:
committed by
Android (Google) Code Review
commit
691e2ff5b0
@@ -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() {
|
||||
@@ -1785,6 +1787,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
createTime = _createTime;
|
||||
}
|
||||
mAtmService.mPackageConfigPersister.updateConfigIfNeeded(this, mUserId, packageName);
|
||||
|
||||
mActivityRecordInputSink = new ActivityRecordInputSink(this);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -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);
|
||||
|
||||
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user