1 /*
2  * Copyright (C) 2010 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 static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
20 import static android.view.WindowManager.INPUT_CONSUMER_NAVIGATION;
21 import static android.view.WindowManager.INPUT_CONSUMER_PIP;
22 import static android.view.WindowManager.INPUT_CONSUMER_RECENTS_ANIMATION;
23 import static android.view.WindowManager.INPUT_CONSUMER_WALLPAPER;
24 import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
25 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_DISABLE_WALLPAPER_TOUCH_EVENTS;
26 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
27 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
28 
29 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS_LIGHT;
30 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_INPUT;
31 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_POSITIONING;
32 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
33 
34 import android.graphics.Rect;
35 import android.os.Handler;
36 import android.os.IBinder;
37 import android.os.Looper;
38 import android.os.Process;
39 import android.os.Trace;
40 import android.os.UserHandle;
41 import android.util.ArrayMap;
42 import android.util.Log;
43 import android.util.Slog;
44 import android.view.InputApplicationHandle;
45 import android.view.InputChannel;
46 import android.view.InputEventReceiver;
47 import android.view.InputWindowHandle;
48 import android.view.SurfaceControl;
49 
50 import com.android.server.AnimationThread;
51 import com.android.server.policy.WindowManagerPolicy;
52 
53 import java.io.PrintWriter;
54 import java.util.Set;
55 import java.util.function.Consumer;
56 
57 final class InputMonitor {
58     private final WindowManagerService mService;
59 
60     // Current window with input focus for keys and other non-touch events.  May be null.
61     private WindowState mInputFocus;
62 
63     // When true, need to call updateInputWindowsLw().
64     private boolean mUpdateInputWindowsNeeded = true;
65     private boolean mUpdateInputWindowsPending;
66     private boolean mApplyImmediately;
67 
68     // Currently focused input window handle.
69     private InputWindowHandle mFocusedInputWindowHandle;
70 
71     private boolean mDisableWallpaperTouchEvents;
72     private final Rect mTmpRect = new Rect();
73     private final UpdateInputForAllWindowsConsumer mUpdateInputForAllWindowsConsumer;
74 
75     private final int mDisplayId;
76     private final DisplayContent mDisplayContent;
77     private boolean mDisplayRemoved;
78 
79     private final SurfaceControl.Transaction mInputTransaction;
80     private final Handler mHandler;
81 
82     /**
83      * The set of input consumer added to the window manager by name, which consumes input events
84      * for the windows below it.
85      */
86     private final ArrayMap<String, InputConsumerImpl> mInputConsumers = new ArrayMap();
87 
88     private static final class EventReceiverInputConsumer extends InputConsumerImpl
89             implements WindowManagerPolicy.InputConsumer {
90         private InputMonitor mInputMonitor;
91         private final InputEventReceiver mInputEventReceiver;
92 
EventReceiverInputConsumer(WindowManagerService service, InputMonitor monitor, Looper looper, String name, InputEventReceiver.Factory inputEventReceiverFactory, int clientPid, UserHandle clientUser, int displayId)93         EventReceiverInputConsumer(WindowManagerService service, InputMonitor monitor,
94                                    Looper looper, String name,
95                                    InputEventReceiver.Factory inputEventReceiverFactory,
96                                    int clientPid, UserHandle clientUser, int displayId) {
97             super(service, null /* token */, name, null /* inputChannel */, clientPid, clientUser,
98                     displayId);
99             mInputMonitor = monitor;
100             mInputEventReceiver = inputEventReceiverFactory.createInputEventReceiver(
101                     mClientChannel, looper);
102         }
103 
104         @Override
dismiss()105         public void dismiss() {
106             synchronized (mService.mGlobalLock) {
107                 if (mInputMonitor.destroyInputConsumer(mWindowHandle.name)) {
108                     mInputEventReceiver.dispose();
109                 }
110             }
111         }
112     }
113 
114     private final Runnable mUpdateInputWindows = new Runnable() {
115         @Override
116         public void run() {
117             synchronized (mService.mGlobalLock) {
118                 mUpdateInputWindowsPending = false;
119                 mUpdateInputWindowsNeeded = false;
120 
121                 if (mDisplayRemoved) {
122                     return;
123                 }
124 
125                 // Populate the input window list with information about all of the windows that
126                 // could potentially receive input.
127                 // As an optimization, we could try to prune the list of windows but this turns
128                 // out to be difficult because only the native code knows for sure which window
129                 // currently has touch focus.
130 
131                 // If there's a drag in flight, provide a pseudo-window to catch drag input
132                 final boolean inDrag = mService.mDragDropController.dragDropActiveLocked();
133                 final boolean inPositioning =
134                         mService.mTaskPositioningController.isPositioningLocked();
135                 if (inPositioning) {
136                     if (DEBUG_TASK_POSITIONING) {
137                         Log.d(TAG_WM, "Inserting window handle for repositioning");
138                     }
139                     mService.mTaskPositioningController.showInputSurface(mInputTransaction,
140                             mDisplayId);
141                 } else {
142                     mService.mTaskPositioningController.hideInputSurface(mInputTransaction,
143                             mDisplayId);
144                 }
145 
146                 // Add all windows on the default display.
147                 mUpdateInputForAllWindowsConsumer.updateInputWindows(inDrag);
148             }
149         }
150     };
151 
InputMonitor(WindowManagerService service, int displayId)152     public InputMonitor(WindowManagerService service, int displayId) {
153         mService = service;
154         mDisplayContent = mService.mRoot.getDisplayContent(displayId);
155         mDisplayId = displayId;
156         mInputTransaction = mService.mTransactionFactory.make();
157         mHandler = AnimationThread.getHandler();
158         mUpdateInputForAllWindowsConsumer = new UpdateInputForAllWindowsConsumer();
159     }
160 
onDisplayRemoved()161     void onDisplayRemoved() {
162         mHandler.removeCallbacks(mUpdateInputWindows);
163         mService.mInputManager.onDisplayRemoved(mDisplayId);
164         mDisplayRemoved = true;
165     }
166 
addInputConsumer(String name, InputConsumerImpl consumer)167     private void addInputConsumer(String name, InputConsumerImpl consumer) {
168         mInputConsumers.put(name, consumer);
169         consumer.linkToDeathRecipient();
170         updateInputWindowsLw(true /* force */);
171     }
172 
destroyInputConsumer(String name)173     boolean destroyInputConsumer(String name) {
174         if (disposeInputConsumer(mInputConsumers.remove(name))) {
175             updateInputWindowsLw(true /* force */);
176             return true;
177         }
178         return false;
179     }
180 
disposeInputConsumer(InputConsumerImpl consumer)181     private boolean disposeInputConsumer(InputConsumerImpl consumer) {
182         if (consumer != null) {
183             consumer.disposeChannelsLw();
184             consumer.hide(mInputTransaction);
185             return true;
186         }
187         return false;
188     }
189 
getInputConsumer(String name)190     InputConsumerImpl getInputConsumer(String name) {
191         return mInputConsumers.get(name);
192     }
193 
layoutInputConsumers(int dw, int dh)194     void layoutInputConsumers(int dw, int dh) {
195         try {
196             Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "layoutInputConsumer");
197             for (int i = mInputConsumers.size() - 1; i >= 0; i--) {
198                 mInputConsumers.valueAt(i).layout(mInputTransaction, dw, dh);
199             }
200         } finally {
201             Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
202         }
203     }
204 
205     // The visibility of the input consumers is recomputed each time we
206     // update the input windows. We use a model where consumers begin invisible
207     // (set so by this function) and must meet some condition for visibility on each update.
resetInputConsumers(SurfaceControl.Transaction t)208     void resetInputConsumers(SurfaceControl.Transaction t) {
209         for (int i = mInputConsumers.size() - 1; i >= 0; i--) {
210             mInputConsumers.valueAt(i).hide(t);
211         }
212     }
213 
createInputConsumer(Looper looper, String name, InputEventReceiver.Factory inputEventReceiverFactory)214     WindowManagerPolicy.InputConsumer createInputConsumer(Looper looper, String name,
215             InputEventReceiver.Factory inputEventReceiverFactory) {
216         if (mInputConsumers.containsKey(name)) {
217             throw new IllegalStateException("Existing input consumer found with name: " + name
218                     + ", display: " + mDisplayId);
219         }
220         final EventReceiverInputConsumer consumer = new EventReceiverInputConsumer(mService,
221                 this, looper, name, inputEventReceiverFactory, Process.myPid(),
222                 UserHandle.SYSTEM, mDisplayId);
223         addInputConsumer(name, consumer);
224         return consumer;
225     }
226 
createInputConsumer(IBinder token, String name, InputChannel inputChannel, int clientPid, UserHandle clientUser)227     void createInputConsumer(IBinder token, String name, InputChannel inputChannel, int clientPid,
228             UserHandle clientUser) {
229         if (mInputConsumers.containsKey(name)) {
230             throw new IllegalStateException("Existing input consumer found with name: " + name
231                     + ", display: " + mDisplayId);
232         }
233 
234         final InputConsumerImpl consumer = new InputConsumerImpl(mService, token, name,
235                 inputChannel, clientPid, clientUser, mDisplayId);
236         switch (name) {
237             case INPUT_CONSUMER_WALLPAPER:
238                 consumer.mWindowHandle.hasWallpaper = true;
239                 break;
240             case INPUT_CONSUMER_PIP:
241                 // The touchable region of the Pip input window is cropped to the bounds of the
242                 // stack, and we need FLAG_NOT_TOUCH_MODAL to ensure other events fall through
243                 consumer.mWindowHandle.layoutParamsFlags |= FLAG_NOT_TOUCH_MODAL;
244                 break;
245         }
246         addInputConsumer(name, consumer);
247     }
248 
249 
populateInputWindowHandle(final InputWindowHandle inputWindowHandle, final WindowState child, int flags, final int type, final boolean isVisible, final boolean hasFocus, final boolean hasWallpaper)250     void populateInputWindowHandle(final InputWindowHandle inputWindowHandle,
251             final WindowState child, int flags, final int type, final boolean isVisible,
252             final boolean hasFocus, final boolean hasWallpaper) {
253         // Add a window to our list of input windows.
254         inputWindowHandle.name = child.toString();
255         flags = child.getSurfaceTouchableRegion(inputWindowHandle, flags);
256         inputWindowHandle.layoutParamsFlags = flags;
257         inputWindowHandle.layoutParamsType = type;
258         inputWindowHandle.dispatchingTimeoutNanos = child.getInputDispatchingTimeoutNanos();
259         inputWindowHandle.visible = isVisible;
260         inputWindowHandle.canReceiveKeys = child.canReceiveKeys();
261         inputWindowHandle.hasFocus = hasFocus;
262         inputWindowHandle.hasWallpaper = hasWallpaper;
263         inputWindowHandle.paused = child.mAppToken != null ? child.mAppToken.paused : false;
264         inputWindowHandle.layer = child.mLayer;
265         inputWindowHandle.ownerPid = child.mSession.mPid;
266         inputWindowHandle.ownerUid = child.mSession.mUid;
267         inputWindowHandle.inputFeatures = child.mAttrs.inputFeatures;
268         inputWindowHandle.displayId = child.getDisplayId();
269 
270         final Rect frame = child.getFrameLw();
271         inputWindowHandle.frameLeft = frame.left;
272         inputWindowHandle.frameTop = frame.top;
273         inputWindowHandle.frameRight = frame.right;
274         inputWindowHandle.frameBottom = frame.bottom;
275 
276         // Surface insets are hardcoded to be the same in all directions
277         // and we could probably deprecate the "left/right/top/bottom" concept.
278         // we avoid reintroducing this concept by just choosing one of them here.
279         inputWindowHandle.surfaceInset = child.getAttrs().surfaceInsets.left;
280 
281         if (child.mGlobalScale != 1) {
282             // If we are scaling the window, input coordinates need
283             // to be inversely scaled to map from what is on screen
284             // to what is actually being touched in the UI.
285             inputWindowHandle.scaleFactor = 1.0f/child.mGlobalScale;
286         } else {
287             inputWindowHandle.scaleFactor = 1;
288         }
289 
290         if (DEBUG_INPUT) {
291             Slog.d(TAG_WM, "addInputWindowHandle: "
292                     + child + ", " + inputWindowHandle);
293         }
294 
295         if (hasFocus) {
296             mFocusedInputWindowHandle = inputWindowHandle;
297         }
298     }
299 
setUpdateInputWindowsNeededLw()300     void setUpdateInputWindowsNeededLw() {
301         mUpdateInputWindowsNeeded = true;
302     }
303 
304     /* Updates the cached window information provided to the input dispatcher. */
updateInputWindowsLw(boolean force)305     void updateInputWindowsLw(boolean force) {
306         if (!force && !mUpdateInputWindowsNeeded) {
307             return;
308         }
309         scheduleUpdateInputWindows();
310     }
311 
scheduleUpdateInputWindows()312     private void scheduleUpdateInputWindows() {
313         if (mDisplayRemoved) {
314             return;
315         }
316 
317         if (!mUpdateInputWindowsPending) {
318             mUpdateInputWindowsPending = true;
319             mHandler.post(mUpdateInputWindows);
320         }
321     }
322 
updateInputWindowsImmediately()323     void updateInputWindowsImmediately() {
324         if (mUpdateInputWindowsPending) {
325             mApplyImmediately = true;
326             mUpdateInputWindows.run();
327             mApplyImmediately = false;
328         }
329     }
330 
331     /* Called when the current input focus changes.
332      * Layer assignment is assumed to be complete by the time this is called.
333      */
setInputFocusLw(WindowState newWindow, boolean updateInputWindows)334     public void setInputFocusLw(WindowState newWindow, boolean updateInputWindows) {
335         if (DEBUG_FOCUS_LIGHT || DEBUG_INPUT) {
336             Slog.d(TAG_WM, "Input focus has changed to " + newWindow);
337         }
338 
339         if (newWindow != mInputFocus) {
340             if (newWindow != null && newWindow.canReceiveKeys()) {
341                 // Displaying a window implicitly causes dispatching to be unpaused.
342                 // This is to protect against bugs if someone pauses dispatching but
343                 // forgets to resume.
344                 newWindow.mToken.paused = false;
345             }
346 
347             mInputFocus = newWindow;
348             setUpdateInputWindowsNeededLw();
349 
350             if (updateInputWindows) {
351                 updateInputWindowsLw(false /*force*/);
352             }
353         }
354     }
355 
setFocusedAppLw(AppWindowToken newApp)356     public void setFocusedAppLw(AppWindowToken newApp) {
357         // Focused app has changed.
358         if (newApp == null) {
359             mService.mInputManager.setFocusedApplication(mDisplayId, null);
360         } else {
361             final InputApplicationHandle handle = newApp.mInputApplicationHandle;
362             handle.name = newApp.toString();
363             handle.dispatchingTimeoutNanos = newApp.mInputDispatchingTimeoutNanos;
364 
365             mService.mInputManager.setFocusedApplication(mDisplayId, handle);
366         }
367     }
368 
pauseDispatchingLw(WindowToken window)369     public void pauseDispatchingLw(WindowToken window) {
370         if (! window.paused) {
371             if (DEBUG_INPUT) {
372                 Slog.v(TAG_WM, "Pausing WindowToken " + window);
373             }
374 
375             window.paused = true;
376             updateInputWindowsLw(true /*force*/);
377         }
378     }
379 
resumeDispatchingLw(WindowToken window)380     public void resumeDispatchingLw(WindowToken window) {
381         if (window.paused) {
382             if (DEBUG_INPUT) {
383                 Slog.v(TAG_WM, "Resuming WindowToken " + window);
384             }
385 
386             window.paused = false;
387             updateInputWindowsLw(true /*force*/);
388         }
389     }
390 
dump(PrintWriter pw, String prefix)391     void dump(PrintWriter pw, String prefix) {
392         final Set<String> inputConsumerKeys = mInputConsumers.keySet();
393         if (!inputConsumerKeys.isEmpty()) {
394             pw.println(prefix + "InputConsumers:");
395             for (String key : inputConsumerKeys) {
396                 mInputConsumers.get(key).dump(pw, key, prefix);
397             }
398         }
399     }
400 
401     private final class UpdateInputForAllWindowsConsumer implements Consumer<WindowState> {
402         InputConsumerImpl navInputConsumer;
403         InputConsumerImpl pipInputConsumer;
404         InputConsumerImpl wallpaperInputConsumer;
405         InputConsumerImpl recentsAnimationInputConsumer;
406 
407         private boolean mAddInputConsumerHandle;
408         private boolean mAddPipInputConsumerHandle;
409         private boolean mAddWallpaperInputConsumerHandle;
410         private boolean mAddRecentsAnimationInputConsumerHandle;
411 
412         boolean inDrag;
413         WallpaperController wallpaperController;
414 
415         // An invalid window handle that tells SurfaceFlinger not update the input info.
416         final InputWindowHandle mInvalidInputWindow = new InputWindowHandle(null, null, mDisplayId);
417 
updateInputWindows(boolean inDrag)418         private void updateInputWindows(boolean inDrag) {
419             Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "updateInputWindows");
420 
421             navInputConsumer = getInputConsumer(INPUT_CONSUMER_NAVIGATION);
422             pipInputConsumer = getInputConsumer(INPUT_CONSUMER_PIP);
423             wallpaperInputConsumer = getInputConsumer(INPUT_CONSUMER_WALLPAPER);
424             recentsAnimationInputConsumer = getInputConsumer(INPUT_CONSUMER_RECENTS_ANIMATION);
425 
426             mAddInputConsumerHandle = navInputConsumer != null;
427             mAddPipInputConsumerHandle = pipInputConsumer != null;
428             mAddWallpaperInputConsumerHandle = wallpaperInputConsumer != null;
429             mAddRecentsAnimationInputConsumerHandle = recentsAnimationInputConsumer != null;
430 
431             mTmpRect.setEmpty();
432             mDisableWallpaperTouchEvents = false;
433             this.inDrag = inDrag;
434             wallpaperController = mDisplayContent.mWallpaperController;
435 
436             resetInputConsumers(mInputTransaction);
437 
438             mDisplayContent.forAllWindows(this,
439                     true /* traverseTopToBottom */);
440 
441             if (mAddWallpaperInputConsumerHandle) {
442                 wallpaperInputConsumer.show(mInputTransaction, 0);
443             }
444 
445             if (mApplyImmediately) {
446                 mInputTransaction.apply();
447             } else {
448                 mDisplayContent.getPendingTransaction().merge(mInputTransaction);
449                 mDisplayContent.scheduleAnimation();
450             }
451 
452             Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
453         }
454 
455         @Override
accept(WindowState w)456         public void accept(WindowState w) {
457             final InputChannel inputChannel = w.mInputChannel;
458             final InputWindowHandle inputWindowHandle = w.mInputWindowHandle;
459             if (inputChannel == null || inputWindowHandle == null || w.mRemoved
460                     || w.cantReceiveTouchInput()) {
461                 if (w.mWinAnimator.hasSurface()) {
462                     mInputTransaction.setInputWindowInfo(
463                             w.mWinAnimator.mSurfaceController.mSurfaceControl, mInvalidInputWindow);
464                 }
465                 // Skip this window because it cannot possibly receive input.
466                 return;
467             }
468 
469             final int flags = w.mAttrs.flags;
470             final int privateFlags = w.mAttrs.privateFlags;
471             final int type = w.mAttrs.type;
472             final boolean hasFocus = w.isFocused();
473             final boolean isVisible = w.isVisibleLw();
474 
475             if (mAddRecentsAnimationInputConsumerHandle) {
476                 final RecentsAnimationController recentsAnimationController =
477                         mService.getRecentsAnimationController();
478                 if (recentsAnimationController != null
479                         && recentsAnimationController.shouldApplyInputConsumer(w.mAppToken)) {
480                     if (recentsAnimationController.updateInputConsumerForApp(
481                             recentsAnimationInputConsumer.mWindowHandle, hasFocus)) {
482                         recentsAnimationInputConsumer.show(mInputTransaction, w);
483                         mAddRecentsAnimationInputConsumerHandle = false;
484                     }
485                 }
486             }
487 
488             if (w.inPinnedWindowingMode()) {
489                 if (mAddPipInputConsumerHandle) {
490                     // Update the bounds of the Pip input consumer to match the window bounds.
491                     w.getBounds(mTmpRect);
492                     pipInputConsumer.layout(mInputTransaction, mTmpRect);
493 
494                     // The touchable region is relative to the surface top-left
495                     mTmpRect.offsetTo(0, 0);
496                     pipInputConsumer.mWindowHandle.touchableRegion.set(mTmpRect);
497                     pipInputConsumer.show(mInputTransaction, w);
498                     mAddPipInputConsumerHandle = false;
499                 }
500             }
501 
502             if (mAddInputConsumerHandle
503                     && inputWindowHandle.layer <= navInputConsumer.mWindowHandle.layer) {
504                 navInputConsumer.show(mInputTransaction, w);
505                 mAddInputConsumerHandle = false;
506             }
507 
508             if (mAddWallpaperInputConsumerHandle) {
509                 if (w.mAttrs.type == TYPE_WALLPAPER && w.isVisibleLw()) {
510                     // Add the wallpaper input consumer above the first visible wallpaper.
511                     wallpaperInputConsumer.show(mInputTransaction, w);
512                     mAddWallpaperInputConsumerHandle = false;
513                 }
514             }
515 
516             if ((privateFlags & PRIVATE_FLAG_DISABLE_WALLPAPER_TOUCH_EVENTS) != 0) {
517                 mDisableWallpaperTouchEvents = true;
518             }
519             final boolean hasWallpaper = wallpaperController.isWallpaperTarget(w)
520                     && (privateFlags & PRIVATE_FLAG_KEYGUARD) == 0
521                     && !mDisableWallpaperTouchEvents;
522 
523             // If there's a drag in progress and 'child' is a potential drop target,
524             // make sure it's been told about the drag
525             if (inDrag && isVisible && w.getDisplayContent().isDefaultDisplay) {
526                 mService.mDragDropController.sendDragStartedIfNeededLocked(w);
527             }
528 
529             populateInputWindowHandle(
530                     inputWindowHandle, w, flags, type, isVisible, hasFocus, hasWallpaper);
531 
532             if (w.mWinAnimator.hasSurface()) {
533                 mInputTransaction.setInputWindowInfo(
534                         w.mWinAnimator.mSurfaceController.mSurfaceControl, inputWindowHandle);
535             }
536         }
537     }
538 }
539