1 /* 2 * Copyright (C) 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.wm; 18 19 import android.app.compat.CompatChanges; 20 import android.compat.annotation.ChangeId; 21 import android.os.InputConfig; 22 import android.view.InputWindowHandle; 23 import android.view.SurfaceControl; 24 import android.view.WindowManager; 25 26 /** 27 * Creates a InputWindowHandle that catches all touches that would otherwise pass through an 28 * Activity. 29 */ 30 class ActivityRecordInputSink { 31 32 /** 33 * Feature flag for making Activities consume all touches within their task bounds. 34 */ 35 @ChangeId 36 static final long ENABLE_TOUCH_OPAQUE_ACTIVITIES = 194480991L; 37 38 private final ActivityRecord mActivityRecord; 39 private final boolean mIsCompatEnabled; 40 private final String mName; 41 42 private InputWindowHandleWrapper mInputWindowHandleWrapper; 43 private SurfaceControl mSurfaceControl; 44 ActivityRecordInputSink(ActivityRecord activityRecord, ActivityRecord sourceRecord)45 ActivityRecordInputSink(ActivityRecord activityRecord, ActivityRecord sourceRecord) { 46 mActivityRecord = activityRecord; 47 mIsCompatEnabled = CompatChanges.isChangeEnabled(ENABLE_TOUCH_OPAQUE_ACTIVITIES, 48 mActivityRecord.getUid()); 49 mName = Integer.toHexString(System.identityHashCode(this)) + " ActivityRecordInputSink " 50 + mActivityRecord.mActivityComponent.flattenToShortString(); 51 if (sourceRecord != null) { 52 sourceRecord.mAllowedTouchUid = mActivityRecord.getUid(); 53 } 54 } 55 applyChangesToSurfaceIfChanged(SurfaceControl.Transaction transaction)56 public void applyChangesToSurfaceIfChanged(SurfaceControl.Transaction transaction) { 57 InputWindowHandleWrapper inputWindowHandleWrapper = getInputWindowHandleWrapper(); 58 if (mSurfaceControl == null) { 59 mSurfaceControl = createSurface(transaction); 60 } 61 if (inputWindowHandleWrapper.isChanged()) { 62 inputWindowHandleWrapper.applyChangesToSurface(transaction, mSurfaceControl); 63 } 64 } 65 createSurface(SurfaceControl.Transaction t)66 private SurfaceControl createSurface(SurfaceControl.Transaction t) { 67 SurfaceControl surfaceControl = mActivityRecord.makeChildSurface(null) 68 .setName(mName) 69 .setHidden(false) 70 .setCallsite("ActivityRecordInputSink.createSurface") 71 .build(); 72 // Put layer below all siblings (and the parent surface too) 73 t.setLayer(surfaceControl, Integer.MIN_VALUE); 74 return surfaceControl; 75 } 76 getInputWindowHandleWrapper()77 private InputWindowHandleWrapper getInputWindowHandleWrapper() { 78 if (mInputWindowHandleWrapper == null) { 79 mInputWindowHandleWrapper = new InputWindowHandleWrapper(createInputWindowHandle()); 80 } 81 // Don't block touches from passing through to an activity below us in the same task, if 82 // that activity is either from the same uid or if that activity has launched an activity 83 // in our uid. 84 final ActivityRecord activityBelowInTask = mActivityRecord.getTask() != null 85 ? mActivityRecord.getTask().getActivityBelow(mActivityRecord) : null; 86 final boolean allowPassthrough = activityBelowInTask != null && ( 87 activityBelowInTask.mAllowedTouchUid == mActivityRecord.getUid() 88 || activityBelowInTask.isUid(mActivityRecord.getUid())); 89 if (allowPassthrough || !mIsCompatEnabled || mActivityRecord.isInTransition() 90 || !mActivityRecord.mActivityRecordInputSinkEnabled) { 91 // Set to non-touchable, so the touch events can pass through. 92 mInputWindowHandleWrapper.setInputConfigMasked(InputConfig.NOT_TOUCHABLE, 93 InputConfig.NOT_TOUCHABLE); 94 } else { 95 // Set to touchable, so it can block by intercepting the touch events. 96 mInputWindowHandleWrapper.setInputConfigMasked(0, InputConfig.NOT_TOUCHABLE); 97 } 98 mInputWindowHandleWrapper.setDisplayId(mActivityRecord.getDisplayId()); 99 return mInputWindowHandleWrapper; 100 } 101 createInputWindowHandle()102 private InputWindowHandle createInputWindowHandle() { 103 InputWindowHandle inputWindowHandle = new InputWindowHandle(null, 104 mActivityRecord.getDisplayId()); 105 inputWindowHandle.replaceTouchableRegionWithCrop = true; 106 inputWindowHandle.name = mName; 107 inputWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_INPUT_CONSUMER; 108 inputWindowHandle.ownerPid = WindowManagerService.MY_PID; 109 inputWindowHandle.ownerUid = WindowManagerService.MY_UID; 110 inputWindowHandle.inputConfig = InputConfig.NOT_FOCUSABLE | InputConfig.NO_INPUT_CHANNEL; 111 return inputWindowHandle; 112 } 113 releaseSurfaceControl()114 void releaseSurfaceControl() { 115 if (mSurfaceControl != null) { 116 mSurfaceControl.release(); 117 mSurfaceControl = null; 118 } 119 } 120 121 } 122