blob: 95b5cec9a144431df98b24cfd045a306dba3ccbd [file] [log] [blame]
/*
* Copyright (C) 2022 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 android.os.Process;
import android.view.InputWindowHandle;
import android.view.SurfaceControl;
import android.view.WindowManager;
/**
* Creates a InputWindowHandle that catches all touches that would otherwise pass through an
* Activity.
*/
class ActivityRecordInputSink {
private final ActivityRecord mActivityRecord;
private final String mName;
private InputWindowHandle mInputWindowHandle;
private SurfaceControl mSurfaceControl;
ActivityRecordInputSink(ActivityRecord activityRecord, ActivityRecord sourceRecord) {
mActivityRecord = activityRecord;
mName = Integer.toHexString(System.identityHashCode(this)) + " ActivityRecordInputSink "
+ mActivityRecord.mActivityComponent.flattenToShortString();
if (sourceRecord != null) {
sourceRecord.mAllowedTouchUid = mActivityRecord.getUid();
}
}
public void applyChangesToSurfaceIfChanged(SurfaceControl.Transaction transaction) {
boolean windowHandleChanged = updateInputWindowHandle();
if (mSurfaceControl == null) {
mSurfaceControl = createSurface(transaction);
}
if (windowHandleChanged) {
transaction.setInputWindowInfo(mSurfaceControl, mInputWindowHandle);
}
}
private SurfaceControl createSurface(SurfaceControl.Transaction t) {
SurfaceControl surfaceControl = mActivityRecord.makeChildSurface(null)
.setName(mName)
.setHidden(false)
.setCallsite("ActivityRecordInputSink.createSurface")
.build();
// Put layer below all siblings (and the parent surface too)
t.setLayer(surfaceControl, Integer.MIN_VALUE);
return surfaceControl;
}
private boolean updateInputWindowHandle() {
boolean changed = false;
if (mInputWindowHandle == null) {
mInputWindowHandle = createInputWindowHandle();
changed = true;
}
// Don't block touches from passing through to an activity below us in the same task, if
// that activity is either from the same uid or if that activity has launched an activity
// in our uid.
final ActivityRecord activityBelowInTask =
mActivityRecord.getTask().getActivityBelow(mActivityRecord);
final boolean allowPassthrough = activityBelowInTask != null && (
activityBelowInTask.mAllowedTouchUid == mActivityRecord.getUid()
|| activityBelowInTask.isUid(mActivityRecord.getUid()));
boolean notTouchable = (mInputWindowHandle.layoutParamsFlags
& WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0;
if (allowPassthrough || mActivityRecord.isAppTransitioning()) {
mInputWindowHandle.layoutParamsFlags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
changed |= !notTouchable;
} else {
mInputWindowHandle.layoutParamsFlags &= ~WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
changed |= notTouchable;
}
return changed;
}
private InputWindowHandle createInputWindowHandle() {
InputWindowHandle inputWindowHandle = new InputWindowHandle(null,
mActivityRecord.getDisplayId());
inputWindowHandle.replaceTouchableRegionWithCrop = true;
inputWindowHandle.name = mName;
inputWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_INPUT_CONSUMER;
inputWindowHandle.ownerUid = Process.myUid();
inputWindowHandle.ownerPid = Process.myPid();
inputWindowHandle.layoutParamsFlags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
inputWindowHandle.inputFeatures =
WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL;
return inputWindowHandle;
}
void releaseSurfaceControl() {
if (mSurfaceControl != null) {
mSurfaceControl.release();
mSurfaceControl = null;
}
}
}