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