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 android.graphics.Rect;
     20 import android.os.RemoteException;
     21 import android.util.Log;
     22 import android.util.Slog;
     23 import android.view.InputChannel;
     24 import android.view.KeyEvent;
     25 import android.view.WindowManager;
     26 
     27 import java.util.ArrayList;
     28 import java.util.Arrays;
     29 
     30 final class InputMonitor {
     31     private final WindowManagerService mService;
     32 
     33     // Current window with input focus for keys and other non-touch events.  May be null.
     34     private WindowState mInputFocus;
     35 
     36     // When true, prevents input dispatch from proceeding until set to false again.
     37     private boolean mInputDispatchFrozen;
     38 
     39     // When true, input dispatch proceeds normally.  Otherwise all events are dropped.
     40     private boolean mInputDispatchEnabled = true;
     41 
     42     // When true, need to call updateInputWindowsLw().
     43     private boolean mUpdateInputWindowsNeeded = true;
     44 
     45     // Array of window handles to provide to the input dispatcher.
     46     private InputWindowHandle[] mInputWindowHandles;
     47     private int mInputWindowHandleCount;
     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     public InputMonitor(WindowManagerService service) {
     55         mService = service;
     56     }
     57 
     58     /* Notifies the window manager about a broken input channel.
     59      *
     60      * Called by the InputManager.
     61      */
     62     public void notifyInputChannelBroken(InputWindowHandle inputWindowHandle) {
     63         if (inputWindowHandle == null) {
     64             return;
     65         }
     66 
     67         synchronized (mService.mWindowMap) {
     68             WindowState windowState = (WindowState) inputWindowHandle.windowState;
     69             if (windowState != null) {
     70                 Slog.i(WindowManagerService.TAG, "WINDOW DIED " + windowState);
     71                 mService.removeWindowLocked(windowState.mSession, windowState);
     72             }
     73         }
     74     }
     75 
     76     /* Notifies the window manager about an application that is not responding.
     77      * Returns a new timeout to continue waiting in nanoseconds, or 0 to abort dispatch.
     78      *
     79      * Called by the InputManager.
     80      */
     81     public long notifyANR(InputApplicationHandle inputApplicationHandle,
     82             InputWindowHandle inputWindowHandle) {
     83         AppWindowToken appWindowToken = null;
     84         if (inputWindowHandle != null) {
     85             synchronized (mService.mWindowMap) {
     86                 WindowState windowState = (WindowState) inputWindowHandle.windowState;
     87                 if (windowState != null) {
     88                     Slog.i(WindowManagerService.TAG, "Input event dispatching timed out sending to "
     89                             + windowState.mAttrs.getTitle());
     90                     appWindowToken = windowState.mAppToken;
     91                 }
     92             }
     93         }
     94 
     95         if (appWindowToken == null && inputApplicationHandle != null) {
     96             appWindowToken = inputApplicationHandle.appWindowToken;
     97             if (appWindowToken != null) {
     98                 Slog.i(WindowManagerService.TAG,
     99                         "Input event dispatching timed out sending to application "
    100                                 + appWindowToken.stringName);
    101             }
    102         }
    103 
    104         if (appWindowToken != null && appWindowToken.appToken != null) {
    105             try {
    106                 // Notify the activity manager about the timeout and let it decide whether
    107                 // to abort dispatching or keep waiting.
    108                 boolean abort = appWindowToken.appToken.keyDispatchingTimedOut();
    109                 if (! abort) {
    110                     // The activity manager declined to abort dispatching.
    111                     // Wait a bit longer and timeout again later.
    112                     return appWindowToken.inputDispatchingTimeoutNanos;
    113                 }
    114             } catch (RemoteException ex) {
    115             }
    116         }
    117         return 0; // abort dispatching
    118     }
    119 
    120     private void addInputWindowHandleLw(InputWindowHandle windowHandle) {
    121         if (mInputWindowHandles == null) {
    122             mInputWindowHandles = new InputWindowHandle[16];
    123         }
    124         if (mInputWindowHandleCount >= mInputWindowHandles.length) {
    125             mInputWindowHandles = Arrays.copyOf(mInputWindowHandles,
    126                     mInputWindowHandleCount * 2);
    127         }
    128         mInputWindowHandles[mInputWindowHandleCount++] = windowHandle;
    129     }
    130 
    131     private void clearInputWindowHandlesLw() {
    132         while (mInputWindowHandleCount != 0) {
    133             mInputWindowHandles[--mInputWindowHandleCount] = null;
    134         }
    135     }
    136 
    137     public void setUpdateInputWindowsNeededLw() {
    138         mUpdateInputWindowsNeeded = true;
    139     }
    140 
    141     /* Updates the cached window information provided to the input dispatcher. */
    142     public void updateInputWindowsLw(boolean force) {
    143         if (!force && !mUpdateInputWindowsNeeded) {
    144             return;
    145         }
    146         mUpdateInputWindowsNeeded = false;
    147 
    148         if (false) Slog.d(WindowManagerService.TAG, ">>>>>> ENTERED updateInputWindowsLw");
    149 
    150         // Populate the input window list with information about all of the windows that
    151         // could potentially receive input.
    152         // As an optimization, we could try to prune the list of windows but this turns
    153         // out to be difficult because only the native code knows for sure which window
    154         // currently has touch focus.
    155         final ArrayList<WindowState> windows = mService.mWindows;
    156 
    157         // If there's a drag in flight, provide a pseudowindow to catch drag input
    158         final boolean inDrag = (mService.mDragState != null);
    159         if (inDrag) {
    160             if (WindowManagerService.DEBUG_DRAG) {
    161                 Log.d(WindowManagerService.TAG, "Inserting drag window");
    162             }
    163             final InputWindowHandle dragWindowHandle = mService.mDragState.mDragWindowHandle;
    164             if (dragWindowHandle != null) {
    165                 addInputWindowHandleLw(dragWindowHandle);
    166             } else {
    167                 Slog.w(WindowManagerService.TAG, "Drag is in progress but there is no "
    168                         + "drag window handle.");
    169             }
    170         }
    171 
    172         final int NFW = mService.mFakeWindows.size();
    173         for (int i = 0; i < NFW; i++) {
    174             addInputWindowHandleLw(mService.mFakeWindows.get(i).mWindowHandle);
    175         }
    176 
    177         final int N = windows.size();
    178         for (int i = N - 1; i >= 0; i--) {
    179             final WindowState child = windows.get(i);
    180             final InputChannel inputChannel = child.mInputChannel;
    181             final InputWindowHandle inputWindowHandle = child.mInputWindowHandle;
    182             if (inputChannel == null || inputWindowHandle == null || child.mRemoved) {
    183                 // Skip this window because it cannot possibly receive input.
    184                 continue;
    185             }
    186 
    187             final int flags = child.mAttrs.flags;
    188             final int type = child.mAttrs.type;
    189 
    190             final boolean hasFocus = (child == mInputFocus);
    191             final boolean isVisible = child.isVisibleLw();
    192             final boolean hasWallpaper = (child == mService.mWallpaperTarget)
    193                     && (type != WindowManager.LayoutParams.TYPE_KEYGUARD);
    194 
    195             // If there's a drag in progress and 'child' is a potential drop target,
    196             // make sure it's been told about the drag
    197             if (inDrag && isVisible) {
    198                 mService.mDragState.sendDragStartedIfNeededLw(child);
    199             }
    200 
    201             // Add a window to our list of input windows.
    202             inputWindowHandle.name = child.toString();
    203             inputWindowHandle.layoutParamsFlags = flags;
    204             inputWindowHandle.layoutParamsType = type;
    205             inputWindowHandle.dispatchingTimeoutNanos = child.getInputDispatchingTimeoutNanos();
    206             inputWindowHandle.visible = isVisible;
    207             inputWindowHandle.canReceiveKeys = child.canReceiveKeys();
    208             inputWindowHandle.hasFocus = hasFocus;
    209             inputWindowHandle.hasWallpaper = hasWallpaper;
    210             inputWindowHandle.paused = child.mAppToken != null ? child.mAppToken.paused : false;
    211             inputWindowHandle.layer = child.mLayer;
    212             inputWindowHandle.ownerPid = child.mSession.mPid;
    213             inputWindowHandle.ownerUid = child.mSession.mUid;
    214             inputWindowHandle.inputFeatures = child.mAttrs.inputFeatures;
    215 
    216             final Rect frame = child.mFrame;
    217             inputWindowHandle.frameLeft = frame.left;
    218             inputWindowHandle.frameTop = frame.top;
    219             inputWindowHandle.frameRight = frame.right;
    220             inputWindowHandle.frameBottom = frame.bottom;
    221 
    222             if (child.mGlobalScale != 1) {
    223                 // If we are scaling the window, input coordinates need
    224                 // to be inversely scaled to map from what is on screen
    225                 // to what is actually being touched in the UI.
    226                 inputWindowHandle.scaleFactor = 1.0f/child.mGlobalScale;
    227             } else {
    228                 inputWindowHandle.scaleFactor = 1;
    229             }
    230 
    231             child.getTouchableRegion(inputWindowHandle.touchableRegion);
    232 
    233             addInputWindowHandleLw(inputWindowHandle);
    234         }
    235 
    236         // Send windows to native code.
    237         mService.mInputManager.setInputWindows(mInputWindowHandles);
    238 
    239         // Clear the list in preparation for the next round.
    240         clearInputWindowHandlesLw();
    241 
    242         if (false) Slog.d(WindowManagerService.TAG, "<<<<<<< EXITED updateInputWindowsLw");
    243     }
    244 
    245     /* Notifies that the input device configuration has changed. */
    246     public void notifyConfigurationChanged() {
    247         mService.sendNewConfiguration();
    248 
    249         synchronized (mInputDevicesReadyMonitor) {
    250             if (!mInputDevicesReady) {
    251                 mInputDevicesReady = true;
    252                 mInputDevicesReadyMonitor.notifyAll();
    253             }
    254         }
    255     }
    256 
    257     /* Waits until the built-in input devices have been configured. */
    258     public boolean waitForInputDevicesReady(long timeoutMillis) {
    259         synchronized (mInputDevicesReadyMonitor) {
    260             if (!mInputDevicesReady) {
    261                 try {
    262                     mInputDevicesReadyMonitor.wait(timeoutMillis);
    263                 } catch (InterruptedException ex) {
    264                 }
    265             }
    266             return mInputDevicesReady;
    267         }
    268     }
    269 
    270     /* Notifies that the lid switch changed state. */
    271     public void notifyLidSwitchChanged(long whenNanos, boolean lidOpen) {
    272         mService.mPolicy.notifyLidSwitchChanged(whenNanos, lidOpen);
    273     }
    274 
    275     /* Provides an opportunity for the window manager policy to intercept early key
    276      * processing as soon as the key has been read from the device. */
    277     public int interceptKeyBeforeQueueing(
    278             KeyEvent event, int policyFlags, boolean isScreenOn) {
    279         return mService.mPolicy.interceptKeyBeforeQueueing(event, policyFlags, isScreenOn);
    280     }
    281 
    282     /* Provides an opportunity for the window manager policy to intercept early
    283      * motion event processing when the screen is off since these events are normally
    284      * dropped. */
    285     public int interceptMotionBeforeQueueingWhenScreenOff(int policyFlags) {
    286         return mService.mPolicy.interceptMotionBeforeQueueingWhenScreenOff(policyFlags);
    287     }
    288 
    289     /* Provides an opportunity for the window manager policy to process a key before
    290      * ordinary dispatch. */
    291     public long interceptKeyBeforeDispatching(
    292             InputWindowHandle focus, KeyEvent event, int policyFlags) {
    293         WindowState windowState = focus != null ? (WindowState) focus.windowState : null;
    294         return mService.mPolicy.interceptKeyBeforeDispatching(windowState, event, policyFlags);
    295     }
    296 
    297     /* Provides an opportunity for the window manager policy to process a key that
    298      * the application did not handle. */
    299     public KeyEvent dispatchUnhandledKey(
    300             InputWindowHandle focus, KeyEvent event, int policyFlags) {
    301         WindowState windowState = focus != null ? (WindowState) focus.windowState : null;
    302         return mService.mPolicy.dispatchUnhandledKey(windowState, event, policyFlags);
    303     }
    304 
    305     /* Called when the current input focus changes.
    306      * Layer assignment is assumed to be complete by the time this is called.
    307      */
    308     public void setInputFocusLw(WindowState newWindow, boolean updateInputWindows) {
    309         if (WindowManagerService.DEBUG_INPUT) {
    310             Slog.d(WindowManagerService.TAG, "Input focus has changed to " + newWindow);
    311         }
    312 
    313         if (newWindow != mInputFocus) {
    314             if (newWindow != null && newWindow.canReceiveKeys()) {
    315                 // Displaying a window implicitly causes dispatching to be unpaused.
    316                 // This is to protect against bugs if someone pauses dispatching but
    317                 // forgets to resume.
    318                 newWindow.mToken.paused = false;
    319             }
    320 
    321             mInputFocus = newWindow;
    322             setUpdateInputWindowsNeededLw();
    323 
    324             if (updateInputWindows) {
    325                 updateInputWindowsLw(false /*force*/);
    326             }
    327         }
    328     }
    329 
    330     public void setFocusedAppLw(AppWindowToken newApp) {
    331         // Focused app has changed.
    332         if (newApp == null) {
    333             mService.mInputManager.setFocusedApplication(null);
    334         } else {
    335             final InputApplicationHandle handle = newApp.mInputApplicationHandle;
    336             handle.name = newApp.toString();
    337             handle.dispatchingTimeoutNanos = newApp.inputDispatchingTimeoutNanos;
    338 
    339             mService.mInputManager.setFocusedApplication(handle);
    340         }
    341     }
    342 
    343     public void pauseDispatchingLw(WindowToken window) {
    344         if (! window.paused) {
    345             if (WindowManagerService.DEBUG_INPUT) {
    346                 Slog.v(WindowManagerService.TAG, "Pausing WindowToken " + window);
    347             }
    348 
    349             window.paused = true;
    350             updateInputWindowsLw(true /*force*/);
    351         }
    352     }
    353 
    354     public void resumeDispatchingLw(WindowToken window) {
    355         if (window.paused) {
    356             if (WindowManagerService.DEBUG_INPUT) {
    357                 Slog.v(WindowManagerService.TAG, "Resuming WindowToken " + window);
    358             }
    359 
    360             window.paused = false;
    361             updateInputWindowsLw(true /*force*/);
    362         }
    363     }
    364 
    365     public void freezeInputDispatchingLw() {
    366         if (! mInputDispatchFrozen) {
    367             if (WindowManagerService.DEBUG_INPUT) {
    368                 Slog.v(WindowManagerService.TAG, "Freezing input dispatching");
    369             }
    370 
    371             mInputDispatchFrozen = true;
    372             updateInputDispatchModeLw();
    373         }
    374     }
    375 
    376     public void thawInputDispatchingLw() {
    377         if (mInputDispatchFrozen) {
    378             if (WindowManagerService.DEBUG_INPUT) {
    379                 Slog.v(WindowManagerService.TAG, "Thawing input dispatching");
    380             }
    381 
    382             mInputDispatchFrozen = false;
    383             updateInputDispatchModeLw();
    384         }
    385     }
    386 
    387     public void setEventDispatchingLw(boolean enabled) {
    388         if (mInputDispatchEnabled != enabled) {
    389             if (WindowManagerService.DEBUG_INPUT) {
    390                 Slog.v(WindowManagerService.TAG, "Setting event dispatching to " + enabled);
    391             }
    392 
    393             mInputDispatchEnabled = enabled;
    394             updateInputDispatchModeLw();
    395         }
    396     }
    397 
    398     private void updateInputDispatchModeLw() {
    399         mService.mInputManager.setInputDispatchMode(mInputDispatchEnabled, mInputDispatchFrozen);
    400     }
    401 }
    402