1 package com.android.server.wm;
2 
3 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
4 import static android.view.Display.DEFAULT_DISPLAY;
5 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
6 
7 import static com.android.server.wm.ActivityRecord.INVALID_PID;
8 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_INPUT;
9 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
10 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
11 import static com.android.server.wm.WindowManagerService.H.ON_POINTER_DOWN_OUTSIDE_FOCUS;
12 
13 import android.os.Build;
14 import android.os.Debug;
15 import android.os.IBinder;
16 import android.os.Process;
17 import android.os.RemoteException;
18 import android.os.SystemClock;
19 import android.util.ArrayMap;
20 import android.util.Slog;
21 import android.view.IWindow;
22 import android.view.InputApplicationHandle;
23 import android.view.KeyEvent;
24 import android.view.WindowManager;
25 
26 import com.android.server.am.ActivityManagerService;
27 import com.android.server.input.InputManagerService;
28 import com.android.server.wm.EmbeddedWindowController.EmbeddedWindow;
29 
30 import java.io.File;
31 import java.io.PrintWriter;
32 import java.util.ArrayList;
33 import java.util.concurrent.CountDownLatch;
34 import java.util.concurrent.TimeUnit;
35 import java.util.concurrent.atomic.AtomicReference;
36 
37 final class InputManagerCallback implements InputManagerService.WindowManagerCallbacks {
38     private static final String TAG = TAG_WITH_CLASS_NAME ? "InputManagerCallback" : TAG_WM;
39 
40     /** Prevent spamming the traces because pre-dump cannot aware duplicated ANR. */
41     private static final long PRE_DUMP_MIN_INTERVAL_MS = TimeUnit.SECONDS.toMillis(20);
42     /** The timeout to detect if a monitor is held for a while. */
43     private static final long PRE_DUMP_MONITOR_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(1);
44     /** The last time pre-dump was executed. */
45     private volatile long mLastPreDumpTimeMs;
46 
47     private final WindowManagerService mService;
48 
49     // Set to true when the first input device configuration change notification
50     // is received to indicate that the input devices are ready.
51     private final Object mInputDevicesReadyMonitor = new Object();
52     private boolean mInputDevicesReady;
53 
54     // When true, prevents input dispatch from proceeding until set to false again.
55     private boolean mInputDispatchFrozen;
56 
57     // The reason the input is currently frozen or null if the input isn't frozen.
58     private String mInputFreezeReason = null;
59 
60     // When true, input dispatch proceeds normally.  Otherwise all events are dropped.
61     // Initially false, so that input does not get dispatched until boot is finished at
62     // which point the ActivityManager will enable dispatching.
63     private boolean mInputDispatchEnabled;
64 
65     // TODO(b/141749603)) investigate if this can be part of client focus change dispatch
66     // Tracks the currently focused window used to update pointer capture state in clients
67     private AtomicReference<IWindow> mFocusedWindow = new AtomicReference<>();
68 
69     // Tracks focused window pointer capture state
70     private boolean mFocusedWindowHasCapture;
71 
InputManagerCallback(WindowManagerService service)72     public InputManagerCallback(WindowManagerService service) {
73         mService = service;
74     }
75 
76     /**
77      * Notifies the window manager about a broken input channel.
78      *
79      * Called by the InputManager.
80      */
81     @Override
notifyInputChannelBroken(IBinder token)82     public void notifyInputChannelBroken(IBinder token) {
83         if (token == null) {
84             return;
85         }
86 
87         synchronized (mService.mGlobalLock) {
88             WindowState windowState = mService.mInputToWindowMap.get(token);
89             if (windowState != null) {
90                 Slog.i(TAG_WM, "WINDOW DIED " + windowState);
91                 windowState.removeIfPossible();
92             }
93         }
94     }
95 
96     /**
97      * Pre-dump stack trace if the locks of activity manager or window manager (they may be locked
98      * in the path of reporting ANR) cannot be acquired in time. That provides the stack traces
99      * before the real blocking symptom has gone.
100      * <p>
101      * Do not hold the {@link WindowManagerGlobalLock} while calling this method.
102      */
preDumpIfLockTooSlow()103     private void preDumpIfLockTooSlow() {
104         if (!Build.IS_DEBUGGABLE)  {
105             return;
106         }
107         final long now = SystemClock.uptimeMillis();
108         if (mLastPreDumpTimeMs > 0 && now - mLastPreDumpTimeMs < PRE_DUMP_MIN_INTERVAL_MS) {
109             return;
110         }
111 
112         final boolean[] shouldDumpSf = { true };
113         final ArrayMap<String, Runnable> monitors = new ArrayMap<>(2);
114         monitors.put(TAG_WM, mService::monitor);
115         monitors.put("ActivityManager", mService.mAmInternal::monitor);
116         final CountDownLatch latch = new CountDownLatch(monitors.size());
117         // The pre-dump will execute if one of the monitors doesn't complete within the timeout.
118         for (int i = 0; i < monitors.size(); i++) {
119             final String name = monitors.keyAt(i);
120             final Runnable monitor = monitors.valueAt(i);
121             // Always create new thread to avoid noise of existing threads. Suppose here won't
122             // create too many threads because it means that watchdog will be triggered first.
123             new Thread() {
124                 @Override
125                 public void run() {
126                     monitor.run();
127                     latch.countDown();
128                     final long elapsed = SystemClock.uptimeMillis() - now;
129                     if (elapsed > PRE_DUMP_MONITOR_TIMEOUT_MS) {
130                         Slog.i(TAG_WM, "Pre-dump acquired " + name + " in " + elapsed + "ms");
131                     } else if (TAG_WM.equals(name)) {
132                         // Window manager is the main client of SurfaceFlinger. If window manager
133                         // is responsive, the stack traces of SurfaceFlinger may not be important.
134                         shouldDumpSf[0] = false;
135                     }
136                 };
137             }.start();
138         }
139         try {
140             if (latch.await(PRE_DUMP_MONITOR_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
141                 return;
142             }
143         } catch (InterruptedException ignored) { }
144         mLastPreDumpTimeMs = now;
145         Slog.i(TAG_WM, "Pre-dump for unresponsive");
146 
147         final ArrayList<Integer> firstPids = new ArrayList<>(1);
148         firstPids.add(ActivityManagerService.MY_PID);
149         ArrayList<Integer> nativePids = null;
150         final int[] pids = shouldDumpSf[0]
151                 ? Process.getPidsForCommands(new String[] { "/system/bin/surfaceflinger" })
152                 : null;
153         if (pids != null) {
154             nativePids = new ArrayList<>(1);
155             for (int pid : pids) {
156                 nativePids.add(pid);
157             }
158         }
159 
160         final File tracesFile = ActivityManagerService.dumpStackTraces(firstPids,
161                 null /* processCpuTracker */, null /* lastPids */, nativePids,
162                 null /* logExceptionCreatingFile */);
163         if (tracesFile != null) {
164             tracesFile.renameTo(new File(tracesFile.getParent(), tracesFile.getName() + "_pre"));
165         }
166     }
167 
168     /**
169      * Notifies the window manager about an application that is not responding.
170      * Returns a new timeout to continue waiting in nanoseconds, or 0 to abort dispatch.
171      *
172      * Called by the InputManager.
173      */
174     @Override
notifyANR(InputApplicationHandle inputApplicationHandle, IBinder token, String reason)175     public long notifyANR(InputApplicationHandle inputApplicationHandle, IBinder token,
176             String reason) {
177         final long startTime = SystemClock.uptimeMillis();
178         try {
179             return notifyANRInner(inputApplicationHandle, token, reason);
180         } finally {
181             // Log the time because the method is called from InputDispatcher thread. It shouldn't
182             // take too long that may affect input response time.
183             Slog.d(TAG_WM, "notifyANR took " + (SystemClock.uptimeMillis() - startTime) + "ms");
184         }
185     }
186 
notifyANRInner(InputApplicationHandle inputApplicationHandle, IBinder token, String reason)187     private long notifyANRInner(InputApplicationHandle inputApplicationHandle, IBinder token,
188             String reason) {
189         ActivityRecord activity = null;
190         WindowState windowState = null;
191         boolean aboveSystem = false;
192         int windowPid = INVALID_PID;
193 
194         preDumpIfLockTooSlow();
195 
196         //TODO(b/141764879) Limit scope of wm lock when input calls notifyANR
197         synchronized (mService.mGlobalLock) {
198 
199             // Check if we can blame a window
200             if (token != null) {
201                 windowState = mService.mInputToWindowMap.get(token);
202                 if (windowState != null) {
203                     activity = windowState.mActivityRecord;
204                     windowPid = windowState.mSession.mPid;
205                     // Figure out whether this window is layered above system windows.
206                     // We need to do this here to help the activity manager know how to
207                     // layer its ANR dialog.
208                     aboveSystem = isWindowAboveSystem(windowState);
209                 }
210             }
211 
212             // Check if we can blame an embedded window
213             if (token != null && windowState == null) {
214                 EmbeddedWindow embeddedWindow = mService.mEmbeddedWindowController.get(token);
215                 if (embeddedWindow != null) {
216                     windowPid = embeddedWindow.mOwnerPid;
217                     WindowState hostWindowState = embeddedWindow.mHostWindowState;
218                     if (hostWindowState == null) {
219                         // The embedded window has no host window and we cannot easily determine
220                         // its z order. Try to place the anr dialog as high as possible.
221                         aboveSystem = true;
222                     } else {
223                         aboveSystem = isWindowAboveSystem(hostWindowState);
224                     }
225                 }
226             }
227 
228             // Check if we can blame an activity. If we don't have an activity to blame, pull out
229             // the token passed in via input application handle. This can happen if there are no
230             // focused windows but input dispatcher knows the focused app.
231             if (activity == null && inputApplicationHandle != null) {
232                 activity = ActivityRecord.forTokenLocked(inputApplicationHandle.token);
233             }
234 
235             if (windowState != null) {
236                 Slog.i(TAG_WM, "Input event dispatching timed out "
237                         + "sending to " + windowState.mAttrs.getTitle()
238                         + ".  Reason: " + reason);
239             } else if (activity != null) {
240                 Slog.i(TAG_WM, "Input event dispatching timed out "
241                         + "sending to application " + activity.stringName
242                         + ".  Reason: " + reason);
243             } else {
244                 Slog.i(TAG_WM, "Input event dispatching timed out "
245                         + ".  Reason: " + reason);
246             }
247 
248             mService.saveANRStateLocked(activity, windowState, reason);
249         }
250 
251         // All the calls below need to happen without the WM lock held since they call into AM.
252         mService.mAtmInternal.saveANRState(reason);
253 
254         if (activity != null && activity.appToken != null) {
255             // Notify the activity manager about the timeout and let it decide whether
256             // to abort dispatching or keep waiting.
257             final boolean abort = activity.keyDispatchingTimedOut(reason, windowPid);
258             if (!abort) {
259                 // The activity manager declined to abort dispatching.
260                 // Wait a bit longer and timeout again later.
261                 return activity.mInputDispatchingTimeoutNanos;
262             }
263         } else if (windowState != null || windowPid != INVALID_PID) {
264             // Notify the activity manager about the timeout and let it decide whether
265             // to abort dispatching or keep waiting.
266             long timeout = mService.mAmInternal.inputDispatchingTimedOut(windowPid, aboveSystem,
267                     reason);
268             if (timeout >= 0) {
269                 // The activity manager declined to abort dispatching.
270                 // Wait a bit longer and timeout again later.
271                 return timeout * 1000000L; // nanoseconds
272             }
273         }
274         return 0; // abort dispatching
275     }
276 
isWindowAboveSystem(WindowState windowState)277     private boolean isWindowAboveSystem(WindowState windowState) {
278         int systemAlertLayer = mService.mPolicy.getWindowLayerFromTypeLw(
279                 TYPE_APPLICATION_OVERLAY, windowState.mOwnerCanAddInternalSystemWindow);
280         return windowState.mBaseLayer > systemAlertLayer;
281     }
282 
283     /** Notifies that the input device configuration has changed. */
284     @Override
notifyConfigurationChanged()285     public void notifyConfigurationChanged() {
286         // TODO(multi-display): Notify proper displays that are associated with this input device.
287 
288         synchronized (mService.mGlobalLock) {
289             mService.getDefaultDisplayContentLocked().sendNewConfiguration();
290         }
291 
292         synchronized (mInputDevicesReadyMonitor) {
293             if (!mInputDevicesReady) {
294                 mInputDevicesReady = true;
295                 mInputDevicesReadyMonitor.notifyAll();
296             }
297         }
298     }
299 
300     /** Notifies that the lid switch changed state. */
301     @Override
notifyLidSwitchChanged(long whenNanos, boolean lidOpen)302     public void notifyLidSwitchChanged(long whenNanos, boolean lidOpen) {
303         mService.mPolicy.notifyLidSwitchChanged(whenNanos, lidOpen);
304     }
305 
306     /** Notifies that the camera lens cover state has changed. */
307     @Override
notifyCameraLensCoverSwitchChanged(long whenNanos, boolean lensCovered)308     public void notifyCameraLensCoverSwitchChanged(long whenNanos, boolean lensCovered) {
309         mService.mPolicy.notifyCameraLensCoverSwitchChanged(whenNanos, lensCovered);
310     }
311 
312     /**
313      * Provides an opportunity for the window manager policy to intercept early key
314      * processing as soon as the key has been read from the device.
315      */
316     @Override
interceptKeyBeforeQueueing(KeyEvent event, int policyFlags)317     public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
318         return mService.mPolicy.interceptKeyBeforeQueueing(event, policyFlags);
319     }
320 
321     /** {@inheritDoc} */
322     @Override
interceptMotionBeforeQueueingNonInteractive(int displayId, long whenNanos, int policyFlags)323     public int interceptMotionBeforeQueueingNonInteractive(int displayId, long whenNanos,
324             int policyFlags) {
325         return mService.mPolicy.interceptMotionBeforeQueueingNonInteractive(
326                 displayId, whenNanos, policyFlags);
327     }
328 
329     /**
330      * Provides an opportunity for the window manager policy to process a key before
331      * ordinary dispatch.
332      */
333     @Override
interceptKeyBeforeDispatching( IBinder focusedToken, KeyEvent event, int policyFlags)334     public long interceptKeyBeforeDispatching(
335             IBinder focusedToken, KeyEvent event, int policyFlags) {
336         return mService.mPolicy.interceptKeyBeforeDispatching(focusedToken, event, policyFlags);
337     }
338 
339     /**
340      * Provides an opportunity for the window manager policy to process a key that
341      * the application did not handle.
342      */
343     @Override
dispatchUnhandledKey( IBinder focusedToken, KeyEvent event, int policyFlags)344     public KeyEvent dispatchUnhandledKey(
345             IBinder focusedToken, KeyEvent event, int policyFlags) {
346         return mService.mPolicy.dispatchUnhandledKey(focusedToken, event, policyFlags);
347     }
348 
349     /** Callback to get pointer layer. */
350     @Override
getPointerLayer()351     public int getPointerLayer() {
352         return mService.mPolicy.getWindowLayerFromTypeLw(WindowManager.LayoutParams.TYPE_POINTER)
353                 * WindowManagerService.TYPE_LAYER_MULTIPLIER
354                 + WindowManagerService.TYPE_LAYER_OFFSET;
355     }
356 
357     /** Callback to get pointer display id. */
358     @Override
getPointerDisplayId()359     public int getPointerDisplayId() {
360         synchronized (mService.mGlobalLock) {
361             // If desktop mode is not enabled, show on the default display.
362             if (!mService.mForceDesktopModeOnExternalDisplays) {
363                 return DEFAULT_DISPLAY;
364             }
365 
366             // Look for the topmost freeform display.
367             int firstExternalDisplayId = DEFAULT_DISPLAY;
368             for (int i = mService.mRoot.mChildren.size() - 1; i >= 0; --i) {
369                 final DisplayContent displayContent = mService.mRoot.mChildren.get(i);
370                 // Heuristic solution here. Currently when "Freeform windows" developer option is
371                 // enabled we automatically put secondary displays in freeform mode and emulating
372                 // "desktop mode". It also makes sense to show the pointer on the same display.
373                 if (displayContent.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
374                     return displayContent.getDisplayId();
375                 }
376 
377                 if (firstExternalDisplayId == DEFAULT_DISPLAY
378                         && displayContent.getDisplayId() != DEFAULT_DISPLAY) {
379                     firstExternalDisplayId = displayContent.getDisplayId();
380                 }
381             }
382 
383             // Look for the topmost non-default display
384             return firstExternalDisplayId;
385         }
386     }
387 
388     @Override
onPointerDownOutsideFocus(IBinder touchedToken)389     public void onPointerDownOutsideFocus(IBinder touchedToken) {
390         mService.mH.obtainMessage(ON_POINTER_DOWN_OUTSIDE_FOCUS, touchedToken).sendToTarget();
391     }
392 
393     @Override
notifyFocusChanged(IBinder oldToken, IBinder newToken)394     public boolean notifyFocusChanged(IBinder oldToken, IBinder newToken) {
395         boolean requestRefreshConfiguration = false;
396         final IWindow newFocusedWindow;
397         final WindowState win;
398 
399         // TODO(b/141749603) investigate if this can be part of client focus change dispatch
400         synchronized (mService.mGlobalLock) {
401             win = mService.mInputToWindowMap.get(newToken);
402         }
403         newFocusedWindow = (win != null) ? win.mClient : null;
404 
405         final IWindow focusedWindow = mFocusedWindow.get();
406         if (focusedWindow != null) {
407             if (newFocusedWindow != null
408                     && newFocusedWindow.asBinder() == focusedWindow.asBinder()) {
409                 Slog.w(TAG, "notifyFocusChanged called with unchanged mFocusedWindow="
410                         + focusedWindow);
411                 return false;
412             }
413             requestRefreshConfiguration = dispatchPointerCaptureChanged(focusedWindow, false);
414         }
415         mFocusedWindow.set(newFocusedWindow);
416         return requestRefreshConfiguration;
417     }
418 
419     @Override
requestPointerCapture(IBinder windowToken, boolean enabled)420     public boolean requestPointerCapture(IBinder windowToken, boolean enabled) {
421         final IWindow focusedWindow = mFocusedWindow.get();
422         if (focusedWindow == null || focusedWindow.asBinder() != windowToken) {
423             Slog.e(TAG, "requestPointerCapture called for a window that has no focus: "
424                     + windowToken);
425             return false;
426         }
427         if (mFocusedWindowHasCapture == enabled) {
428             Slog.i(TAG, "requestPointerCapture: already " + (enabled ? "enabled" : "disabled"));
429             return false;
430         }
431         return dispatchPointerCaptureChanged(focusedWindow, enabled);
432     }
433 
dispatchPointerCaptureChanged(IWindow focusedWindow, boolean enabled)434     private boolean dispatchPointerCaptureChanged(IWindow focusedWindow, boolean enabled) {
435         if (mFocusedWindowHasCapture != enabled) {
436             mFocusedWindowHasCapture = enabled;
437             try {
438                 focusedWindow.dispatchPointerCaptureChanged(enabled);
439             } catch (RemoteException ex) {
440                 /* ignore */
441             }
442             return true;
443         }
444         return false;
445     }
446 
447     /** Waits until the built-in input devices have been configured. */
waitForInputDevicesReady(long timeoutMillis)448     public boolean waitForInputDevicesReady(long timeoutMillis) {
449         synchronized (mInputDevicesReadyMonitor) {
450             if (!mInputDevicesReady) {
451                 try {
452                     mInputDevicesReadyMonitor.wait(timeoutMillis);
453                 } catch (InterruptedException ex) {
454                 }
455             }
456             return mInputDevicesReady;
457         }
458     }
459 
freezeInputDispatchingLw()460     public void freezeInputDispatchingLw() {
461         if (!mInputDispatchFrozen) {
462             if (DEBUG_INPUT) {
463                 Slog.v(TAG_WM, "Freezing input dispatching");
464             }
465 
466             mInputDispatchFrozen = true;
467 
468             if (DEBUG_INPUT) {
469                 mInputFreezeReason = Debug.getCallers(6);
470             }
471             updateInputDispatchModeLw();
472         }
473     }
474 
thawInputDispatchingLw()475     public void thawInputDispatchingLw() {
476         if (mInputDispatchFrozen) {
477             if (DEBUG_INPUT) {
478                 Slog.v(TAG_WM, "Thawing input dispatching");
479             }
480 
481             mInputDispatchFrozen = false;
482             mInputFreezeReason = null;
483             updateInputDispatchModeLw();
484         }
485     }
486 
setEventDispatchingLw(boolean enabled)487     public void setEventDispatchingLw(boolean enabled) {
488         if (mInputDispatchEnabled != enabled) {
489             if (DEBUG_INPUT) {
490                 Slog.v(TAG_WM, "Setting event dispatching to " + enabled);
491             }
492 
493             mInputDispatchEnabled = enabled;
494             updateInputDispatchModeLw();
495         }
496     }
497 
updateInputDispatchModeLw()498     private void updateInputDispatchModeLw() {
499         mService.mInputManager.setInputDispatchMode(mInputDispatchEnabled, mInputDispatchFrozen);
500     }
501 
dump(PrintWriter pw, String prefix)502     void dump(PrintWriter pw, String prefix) {
503         if (mInputFreezeReason != null) {
504             pw.println(prefix + "mInputFreezeReason=" + mInputFreezeReason);
505         }
506     }
507 }
508