Home | History | Annotate | Download | only in wm
      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 com.android.server.wm.WindowManagerDebugConfig.DEBUG_DRAG;
     20 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS_LIGHT;
     21 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_INPUT;
     22 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_POSITIONING;
     23 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
     24 
     25 import android.app.ActivityManagerNative;
     26 import android.graphics.Rect;
     27 import android.os.Debug;
     28 import android.os.RemoteException;
     29 import android.util.Log;
     30 import android.util.Slog;
     31 import android.view.Display;
     32 import android.view.InputChannel;
     33 import android.view.KeyEvent;
     34 import android.view.WindowManager;
     35 
     36 import com.android.server.input.InputApplicationHandle;
     37 import com.android.server.input.InputManagerService;
     38 import com.android.server.input.InputWindowHandle;
     39 
     40 import java.io.PrintWriter;
     41 import java.util.Arrays;
     42 
     43 final class InputMonitor implements InputManagerService.WindowManagerCallbacks {
     44     private final WindowManagerService mService;
     45 
     46     // Current window with input focus for keys and other non-touch events.  May be null.
     47     private WindowState mInputFocus;
     48 
     49     // When true, prevents input dispatch from proceeding until set to false again.
     50     private boolean mInputDispatchFrozen;
     51 
     52     // The reason the input is currently frozen or null if the input isn't frozen.
     53     private String mInputFreezeReason = null;
     54 
     55     // When true, input dispatch proceeds normally.  Otherwise all events are dropped.
     56     // Initially false, so that input does not get dispatched until boot is finished at
     57     // which point the ActivityManager will enable dispatching.
     58     private boolean mInputDispatchEnabled;
     59 
     60     // When true, need to call updateInputWindowsLw().
     61     private boolean mUpdateInputWindowsNeeded = true;
     62 
     63     // Array of window handles to provide to the input dispatcher.
     64     private InputWindowHandle[] mInputWindowHandles;
     65     private int mInputWindowHandleCount;
     66 
     67     // Set to true when the first input device configuration change notification
     68     // is received to indicate that the input devices are ready.
     69     private final Object mInputDevicesReadyMonitor = new Object();
     70     private boolean mInputDevicesReady;
     71 
     72     public InputMonitor(WindowManagerService service) {
     73         mService = service;
     74     }
     75 
     76     /* Notifies the window manager about a broken input channel.
     77      *
     78      * Called by the InputManager.
     79      */
     80     @Override
     81     public void notifyInputChannelBroken(InputWindowHandle inputWindowHandle) {
     82         if (inputWindowHandle == null) {
     83             return;
     84         }
     85 
     86         synchronized (mService.mWindowMap) {
     87             WindowState windowState = (WindowState) inputWindowHandle.windowState;
     88             if (windowState != null) {
     89                 Slog.i(TAG_WM, "WINDOW DIED " + windowState);
     90                 mService.removeWindowLocked(windowState);
     91             }
     92         }
     93     }
     94 
     95     /* Notifies the window manager about an application that is not responding.
     96      * Returns a new timeout to continue waiting in nanoseconds, or 0 to abort dispatch.
     97      *
     98      * Called by the InputManager.
     99      */
    100     @Override
    101     public long notifyANR(InputApplicationHandle inputApplicationHandle,
    102             InputWindowHandle inputWindowHandle, String reason) {
    103         AppWindowToken appWindowToken = null;
    104         WindowState windowState = null;
    105         boolean aboveSystem = false;
    106         synchronized (mService.mWindowMap) {
    107             if (inputWindowHandle != null) {
    108                 windowState = (WindowState) inputWindowHandle.windowState;
    109                 if (windowState != null) {
    110                     appWindowToken = windowState.mAppToken;
    111                 }
    112             }
    113             if (appWindowToken == null && inputApplicationHandle != null) {
    114                 appWindowToken = (AppWindowToken)inputApplicationHandle.appWindowToken;
    115             }
    116 
    117             if (windowState != null) {
    118                 Slog.i(TAG_WM, "Input event dispatching timed out "
    119                         + "sending to " + windowState.mAttrs.getTitle()
    120                         + ".  Reason: " + reason);
    121                 // Figure out whether this window is layered above system windows.
    122                 // We need to do this here to help the activity manager know how to
    123                 // layer its ANR dialog.
    124                 int systemAlertLayer = mService.mPolicy.windowTypeToLayerLw(
    125                         WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
    126                 aboveSystem = windowState.mBaseLayer > systemAlertLayer;
    127             } else if (appWindowToken != null) {
    128                 Slog.i(TAG_WM, "Input event dispatching timed out "
    129                         + "sending to application " + appWindowToken.stringName
    130                         + ".  Reason: " + reason);
    131             } else {
    132                 Slog.i(TAG_WM, "Input event dispatching timed out "
    133                         + ".  Reason: " + reason);
    134             }
    135 
    136             mService.saveANRStateLocked(appWindowToken, windowState, reason);
    137         }
    138 
    139         if (appWindowToken != null && appWindowToken.appToken != null) {
    140             try {
    141                 // Notify the activity manager about the timeout and let it decide whether
    142                 // to abort dispatching or keep waiting.
    143                 boolean abort = appWindowToken.appToken.keyDispatchingTimedOut(reason);
    144                 if (! abort) {
    145                     // The activity manager declined to abort dispatching.
    146                     // Wait a bit longer and timeout again later.
    147                     return appWindowToken.inputDispatchingTimeoutNanos;
    148                 }
    149             } catch (RemoteException ex) {
    150             }
    151         } else if (windowState != null) {
    152             try {
    153                 // Notify the activity manager about the timeout and let it decide whether
    154                 // to abort dispatching or keep waiting.
    155                 long timeout = ActivityManagerNative.getDefault().inputDispatchingTimedOut(
    156                         windowState.mSession.mPid, aboveSystem, reason);
    157                 if (timeout >= 0) {
    158                     // The activity manager declined to abort dispatching.
    159                     // Wait a bit longer and timeout again later.
    160                     return timeout * 1000000L; // nanoseconds
    161                 }
    162             } catch (RemoteException ex) {
    163             }
    164         }
    165         return 0; // abort dispatching
    166     }
    167 
    168     private void addInputWindowHandleLw(final InputWindowHandle windowHandle) {
    169         if (mInputWindowHandles == null) {
    170             mInputWindowHandles = new InputWindowHandle[16];
    171         }
    172         if (mInputWindowHandleCount >= mInputWindowHandles.length) {
    173             mInputWindowHandles = Arrays.copyOf(mInputWindowHandles,
    174                     mInputWindowHandleCount * 2);
    175         }
    176         mInputWindowHandles[mInputWindowHandleCount++] = windowHandle;
    177     }
    178 
    179     private void addInputWindowHandleLw(final InputWindowHandle inputWindowHandle,
    180             final WindowState child, int flags, final int type, final boolean isVisible,
    181             final boolean hasFocus, final boolean hasWallpaper) {
    182         // Add a window to our list of input windows.
    183         inputWindowHandle.name = child.toString();
    184         flags = child.getTouchableRegion(inputWindowHandle.touchableRegion, flags);
    185         inputWindowHandle.layoutParamsFlags = flags;
    186         inputWindowHandle.layoutParamsType = type;
    187         inputWindowHandle.dispatchingTimeoutNanos = child.getInputDispatchingTimeoutNanos();
    188         inputWindowHandle.visible = isVisible;
    189         inputWindowHandle.canReceiveKeys = child.canReceiveKeys();
    190         inputWindowHandle.hasFocus = hasFocus;
    191         inputWindowHandle.hasWallpaper = hasWallpaper;
    192         inputWindowHandle.paused = child.mAppToken != null ? child.mAppToken.paused : false;
    193         inputWindowHandle.layer = child.mLayer;
    194         inputWindowHandle.ownerPid = child.mSession.mPid;
    195         inputWindowHandle.ownerUid = child.mSession.mUid;
    196         inputWindowHandle.inputFeatures = child.mAttrs.inputFeatures;
    197 
    198         final Rect frame = child.mFrame;
    199         inputWindowHandle.frameLeft = frame.left;
    200         inputWindowHandle.frameTop = frame.top;
    201         inputWindowHandle.frameRight = frame.right;
    202         inputWindowHandle.frameBottom = frame.bottom;
    203 
    204         if (child.isDockedInEffect()) {
    205             // Adjust to account for non-resizeable tasks that's scrolled
    206             inputWindowHandle.frameLeft += child.mXOffset;
    207             inputWindowHandle.frameTop += child.mYOffset;
    208             inputWindowHandle.frameRight += child.mXOffset;
    209             inputWindowHandle.frameBottom += child.mYOffset;
    210         }
    211 
    212         if (child.mGlobalScale != 1) {
    213             // If we are scaling the window, input coordinates need
    214             // to be inversely scaled to map from what is on screen
    215             // to what is actually being touched in the UI.
    216             inputWindowHandle.scaleFactor = 1.0f/child.mGlobalScale;
    217         } else {
    218             inputWindowHandle.scaleFactor = 1;
    219         }
    220 
    221         if (DEBUG_INPUT) {
    222             Slog.d(TAG_WM, "addInputWindowHandle: "
    223                     + child + ", " + inputWindowHandle);
    224         }
    225         addInputWindowHandleLw(inputWindowHandle);
    226     }
    227 
    228     private void clearInputWindowHandlesLw() {
    229         while (mInputWindowHandleCount != 0) {
    230             mInputWindowHandles[--mInputWindowHandleCount] = null;
    231         }
    232     }
    233 
    234     public void setUpdateInputWindowsNeededLw() {
    235         mUpdateInputWindowsNeeded = true;
    236     }
    237 
    238     /* Updates the cached window information provided to the input dispatcher. */
    239     public void updateInputWindowsLw(boolean force) {
    240         if (!force && !mUpdateInputWindowsNeeded) {
    241             return;
    242         }
    243         mUpdateInputWindowsNeeded = false;
    244 
    245         if (false) Slog.d(TAG_WM, ">>>>>> ENTERED updateInputWindowsLw");
    246 
    247         // Populate the input window list with information about all of the windows that
    248         // could potentially receive input.
    249         // As an optimization, we could try to prune the list of windows but this turns
    250         // out to be difficult because only the native code knows for sure which window
    251         // currently has touch focus.
    252         boolean disableWallpaperTouchEvents = false;
    253 
    254         // If there's a drag in flight, provide a pseudowindow to catch drag input
    255         final boolean inDrag = (mService.mDragState != null);
    256         if (inDrag) {
    257             if (DEBUG_DRAG) {
    258                 Log.d(TAG_WM, "Inserting drag window");
    259             }
    260             final InputWindowHandle dragWindowHandle = mService.mDragState.mDragWindowHandle;
    261             if (dragWindowHandle != null) {
    262                 addInputWindowHandleLw(dragWindowHandle);
    263             } else {
    264                 Slog.w(TAG_WM, "Drag is in progress but there is no "
    265                         + "drag window handle.");
    266             }
    267         }
    268 
    269         final boolean inPositioning = (mService.mTaskPositioner != null);
    270         if (inPositioning) {
    271             if (DEBUG_TASK_POSITIONING) {
    272                 Log.d(TAG_WM, "Inserting window handle for repositioning");
    273             }
    274             final InputWindowHandle dragWindowHandle = mService.mTaskPositioner.mDragWindowHandle;
    275             if (dragWindowHandle != null) {
    276                 addInputWindowHandleLw(dragWindowHandle);
    277             } else {
    278                 Slog.e(TAG_WM,
    279                         "Repositioning is in progress but there is no drag window handle.");
    280             }
    281         }
    282 
    283         boolean addInputConsumerHandle = mService.mInputConsumer != null;
    284 
    285         boolean addWallpaperInputConsumerHandle = mService.mWallpaperInputConsumer != null;
    286 
    287         // Add all windows on the default display.
    288         final int numDisplays = mService.mDisplayContents.size();
    289         final WallpaperController wallpaperController = mService.mWallpaperControllerLocked;
    290         for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
    291             final DisplayContent displayContent = mService.mDisplayContents.valueAt(displayNdx);
    292             final WindowList windows = displayContent.getWindowList();
    293             for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
    294                 final WindowState child = windows.get(winNdx);
    295                 final InputChannel inputChannel = child.mInputChannel;
    296                 final InputWindowHandle inputWindowHandle = child.mInputWindowHandle;
    297                 if (inputChannel == null || inputWindowHandle == null || child.mRemoved
    298                         || child.isAdjustedForMinimizedDock()) {
    299                     // Skip this window because it cannot possibly receive input.
    300                     continue;
    301                 }
    302                 if (addInputConsumerHandle
    303                         && inputWindowHandle.layer <= mService.mInputConsumer.mWindowHandle.layer) {
    304                     addInputWindowHandleLw(mService.mInputConsumer.mWindowHandle);
    305                     addInputConsumerHandle = false;
    306                 }
    307 
    308                 if (addWallpaperInputConsumerHandle) {
    309                     if (child.mAttrs.type == WindowManager.LayoutParams.TYPE_WALLPAPER) {
    310                         // Add the wallpaper input consumer above the first wallpaper window.
    311                         addInputWindowHandleLw(mService.mWallpaperInputConsumer.mWindowHandle);
    312                         addWallpaperInputConsumerHandle = false;
    313                     }
    314                 }
    315 
    316                 final int flags = child.mAttrs.flags;
    317                 final int privateFlags = child.mAttrs.privateFlags;
    318                 final int type = child.mAttrs.type;
    319 
    320                 final boolean hasFocus = (child == mInputFocus);
    321                 final boolean isVisible = child.isVisibleLw();
    322                 if ((privateFlags
    323                         & WindowManager.LayoutParams.PRIVATE_FLAG_DISABLE_WALLPAPER_TOUCH_EVENTS)
    324                             != 0) {
    325                     disableWallpaperTouchEvents = true;
    326                 }
    327                 final boolean hasWallpaper = wallpaperController.isWallpaperTarget(child)
    328                         && (privateFlags & WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD) == 0
    329                         && !disableWallpaperTouchEvents;
    330                 final boolean onDefaultDisplay = (child.getDisplayId() == Display.DEFAULT_DISPLAY);
    331 
    332                 // If there's a drag in progress and 'child' is a potential drop target,
    333                 // make sure it's been told about the drag
    334                 if (inDrag && isVisible && onDefaultDisplay) {
    335                     mService.mDragState.sendDragStartedIfNeededLw(child);
    336                 }
    337 
    338                 addInputWindowHandleLw(
    339                         inputWindowHandle, child, flags, type, isVisible, hasFocus, hasWallpaper);
    340             }
    341         }
    342 
    343         if (addWallpaperInputConsumerHandle) {
    344             // No wallpaper found, add the wallpaper input consumer at the end.
    345             addInputWindowHandleLw(mService.mWallpaperInputConsumer.mWindowHandle);
    346         }
    347 
    348         // Send windows to native code.
    349         mService.mInputManager.setInputWindows(mInputWindowHandles);
    350 
    351         // Clear the list in preparation for the next round.
    352         clearInputWindowHandlesLw();
    353 
    354         if (false) Slog.d(TAG_WM, "<<<<<<< EXITED updateInputWindowsLw");
    355     }
    356 
    357     /* Notifies that the input device configuration has changed. */
    358     @Override
    359     public void notifyConfigurationChanged() {
    360         mService.sendNewConfiguration();
    361 
    362         synchronized (mInputDevicesReadyMonitor) {
    363             if (!mInputDevicesReady) {
    364                 mInputDevicesReady = true;
    365                 mInputDevicesReadyMonitor.notifyAll();
    366             }
    367         }
    368     }
    369 
    370     /* Waits until the built-in input devices have been configured. */
    371     public boolean waitForInputDevicesReady(long timeoutMillis) {
    372         synchronized (mInputDevicesReadyMonitor) {
    373             if (!mInputDevicesReady) {
    374                 try {
    375                     mInputDevicesReadyMonitor.wait(timeoutMillis);
    376                 } catch (InterruptedException ex) {
    377                 }
    378             }
    379             return mInputDevicesReady;
    380         }
    381     }
    382 
    383     /* Notifies that the lid switch changed state. */
    384     @Override
    385     public void notifyLidSwitchChanged(long whenNanos, boolean lidOpen) {
    386         mService.mPolicy.notifyLidSwitchChanged(whenNanos, lidOpen);
    387     }
    388 
    389     /* Notifies that the camera lens cover state has changed. */
    390     @Override
    391     public void notifyCameraLensCoverSwitchChanged(long whenNanos, boolean lensCovered) {
    392         mService.mPolicy.notifyCameraLensCoverSwitchChanged(whenNanos, lensCovered);
    393     }
    394 
    395     /* Provides an opportunity for the window manager policy to intercept early key
    396      * processing as soon as the key has been read from the device. */
    397     @Override
    398     public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
    399         return mService.mPolicy.interceptKeyBeforeQueueing(event, policyFlags);
    400     }
    401 
    402     /* Provides an opportunity for the window manager policy to intercept early motion event
    403      * processing when the device is in a non-interactive state since these events are normally
    404      * dropped. */
    405     @Override
    406     public int interceptMotionBeforeQueueingNonInteractive(long whenNanos, int policyFlags) {
    407         return mService.mPolicy.interceptMotionBeforeQueueingNonInteractive(
    408                 whenNanos, policyFlags);
    409     }
    410 
    411     /* Provides an opportunity for the window manager policy to process a key before
    412      * ordinary dispatch. */
    413     @Override
    414     public long interceptKeyBeforeDispatching(
    415             InputWindowHandle focus, KeyEvent event, int policyFlags) {
    416         WindowState windowState = focus != null ? (WindowState) focus.windowState : null;
    417         return mService.mPolicy.interceptKeyBeforeDispatching(windowState, event, policyFlags);
    418     }
    419 
    420     /* Provides an opportunity for the window manager policy to process a key that
    421      * the application did not handle. */
    422     @Override
    423     public KeyEvent dispatchUnhandledKey(
    424             InputWindowHandle focus, KeyEvent event, int policyFlags) {
    425         WindowState windowState = focus != null ? (WindowState) focus.windowState : null;
    426         return mService.mPolicy.dispatchUnhandledKey(windowState, event, policyFlags);
    427     }
    428 
    429     /* Callback to get pointer layer. */
    430     @Override
    431     public int getPointerLayer() {
    432         return mService.mPolicy.windowTypeToLayerLw(WindowManager.LayoutParams.TYPE_POINTER)
    433                 * WindowManagerService.TYPE_LAYER_MULTIPLIER
    434                 + WindowManagerService.TYPE_LAYER_OFFSET;
    435     }
    436 
    437     /* Called when the current input focus changes.
    438      * Layer assignment is assumed to be complete by the time this is called.
    439      */
    440     public void setInputFocusLw(WindowState newWindow, boolean updateInputWindows) {
    441         if (DEBUG_FOCUS_LIGHT || DEBUG_INPUT) {
    442             Slog.d(TAG_WM, "Input focus has changed to " + newWindow);
    443         }
    444 
    445         if (newWindow != mInputFocus) {
    446             if (newWindow != null && newWindow.canReceiveKeys()) {
    447                 // Displaying a window implicitly causes dispatching to be unpaused.
    448                 // This is to protect against bugs if someone pauses dispatching but
    449                 // forgets to resume.
    450                 newWindow.mToken.paused = false;
    451             }
    452 
    453             mInputFocus = newWindow;
    454             setUpdateInputWindowsNeededLw();
    455 
    456             if (updateInputWindows) {
    457                 updateInputWindowsLw(false /*force*/);
    458             }
    459         }
    460     }
    461 
    462     public void setFocusedAppLw(AppWindowToken newApp) {
    463         // Focused app has changed.
    464         if (newApp == null) {
    465             mService.mInputManager.setFocusedApplication(null);
    466         } else {
    467             final InputApplicationHandle handle = newApp.mInputApplicationHandle;
    468             handle.name = newApp.toString();
    469             handle.dispatchingTimeoutNanos = newApp.inputDispatchingTimeoutNanos;
    470 
    471             mService.mInputManager.setFocusedApplication(handle);
    472         }
    473     }
    474 
    475     public void pauseDispatchingLw(WindowToken window) {
    476         if (! window.paused) {
    477             if (DEBUG_INPUT) {
    478                 Slog.v(TAG_WM, "Pausing WindowToken " + window);
    479             }
    480 
    481             window.paused = true;
    482             updateInputWindowsLw(true /*force*/);
    483         }
    484     }
    485 
    486     public void resumeDispatchingLw(WindowToken window) {
    487         if (window.paused) {
    488             if (DEBUG_INPUT) {
    489                 Slog.v(TAG_WM, "Resuming WindowToken " + window);
    490             }
    491 
    492             window.paused = false;
    493             updateInputWindowsLw(true /*force*/);
    494         }
    495     }
    496 
    497     public void freezeInputDispatchingLw() {
    498         if (!mInputDispatchFrozen) {
    499             if (DEBUG_INPUT) {
    500                 Slog.v(TAG_WM, "Freezing input dispatching");
    501             }
    502 
    503             mInputDispatchFrozen = true;
    504 
    505             if (DEBUG_INPUT || true) {
    506                 mInputFreezeReason = Debug.getCallers(6);
    507             }
    508             updateInputDispatchModeLw();
    509         }
    510     }
    511 
    512     public void thawInputDispatchingLw() {
    513         if (mInputDispatchFrozen) {
    514             if (DEBUG_INPUT) {
    515                 Slog.v(TAG_WM, "Thawing input dispatching");
    516             }
    517 
    518             mInputDispatchFrozen = false;
    519             mInputFreezeReason = null;
    520             updateInputDispatchModeLw();
    521         }
    522     }
    523 
    524     public void setEventDispatchingLw(boolean enabled) {
    525         if (mInputDispatchEnabled != enabled) {
    526             if (DEBUG_INPUT) {
    527                 Slog.v(TAG_WM, "Setting event dispatching to " + enabled);
    528             }
    529 
    530             mInputDispatchEnabled = enabled;
    531             updateInputDispatchModeLw();
    532         }
    533     }
    534 
    535     private void updateInputDispatchModeLw() {
    536         mService.mInputManager.setInputDispatchMode(mInputDispatchEnabled, mInputDispatchFrozen);
    537     }
    538 
    539     void dump(PrintWriter pw, String prefix) {
    540         if (mInputFreezeReason != null) {
    541             pw.println(prefix + "mInputFreezeReason=" + mInputFreezeReason);
    542         }
    543     }
    544 }
    545