Wake screen from external HID peripherals.

Added some plumbing to enable the policy to intercept motion
events when the screen is off to handle wakeup if needed.

Added a basic concept of an external device to limit the scope
of the wakeup policy to external devices only.  The wakeup policy
for internal devices should be based on explicit rules such as
policy flags in key layout files.

Moved isTouchEvent to native.

Ensure the dispatcher sends the right event type to userActivity
for non-touch pointer events like HOVER_MOVE and SCROLL.

Bug: 3193114
Change-Id: I15dbd48a16810dfaf226ff7ad117d46908ca4f86
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index a26dd04..076f712 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -937,6 +937,7 @@
     private static native int nativeSetSource(int nativePtr, int source);
     private static native int nativeGetAction(int nativePtr);
     private static native void nativeSetAction(int nativePtr, int action);
+    private static native boolean nativeIsTouchEvent(int nativePtr);
     private static native int nativeGetFlags(int nativePtr);
     private static native int nativeGetEdgeFlags(int nativePtr);
     private static native void nativeSetEdgeFlags(int nativePtr, int action);
@@ -1275,19 +1276,7 @@
      * @hide
      */
     public final boolean isTouchEvent() {
-        if ((getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
-            switch (getActionMasked()) {
-                case MotionEvent.ACTION_DOWN:
-                case MotionEvent.ACTION_MOVE:
-                case MotionEvent.ACTION_UP:
-                case MotionEvent.ACTION_POINTER_DOWN:
-                case MotionEvent.ACTION_POINTER_UP:
-                case MotionEvent.ACTION_CANCEL:
-                case MotionEvent.ACTION_OUTSIDE:
-                    return true;
-            }
-        }
-        return false;
+        return nativeIsTouchEvent(mNativePtr);
     }
 
     /**
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index be68cb9..334c68e 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -573,7 +573,21 @@
      *          {@link #ACTION_POKE_USER_ACTIVITY} and {@link #ACTION_GO_TO_SLEEP} flags.
      */
     public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags, boolean isScreenOn);
-    
+
+    /**
+     * Called from the input reader thread before a motion is enqueued when the screen is off.
+     *
+     * <p>There are some actions that need to be handled here because they
+     * affect the power state of the device, for example, waking on motions.
+     * Generally, it's best to keep as little as possible in the queue thread
+     * because it's the most fragile.
+     * @param policyFlags The policy flags associated with the motion.
+     *
+     * @return The bitwise or of the {@link #ACTION_PASS_TO_USER},
+     *          {@link #ACTION_POKE_USER_ACTIVITY} and {@link #ACTION_GO_TO_SLEEP} flags.
+     */
+    public int interceptMotionBeforeQueueingWhenScreenOff(int policyFlags);
+
     /**
      * Called from the input dispatcher thread before a key is dispatched to a window.
      *
diff --git a/core/jni/android_view_MotionEvent.cpp b/core/jni/android_view_MotionEvent.cpp
index b014006..97cba23 100644
--- a/core/jni/android_view_MotionEvent.cpp
+++ b/core/jni/android_view_MotionEvent.cpp
@@ -429,6 +429,12 @@
     event->setAction(action);
 }
 
+static jboolean android_view_MotionEvent_nativeIsTouchEvent(JNIEnv* env, jclass clazz,
+        jint nativePtr) {
+    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+    return event->isTouchEvent();
+}
+
 static jint android_view_MotionEvent_nativeGetFlags(JNIEnv* env, jclass clazz,
         jint nativePtr) {
     MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
@@ -661,6 +667,9 @@
     { "nativeSetAction",
             "(II)V",
             (void*)android_view_MotionEvent_nativeSetAction },
+    { "nativeIsTouchEvent",
+            "(I)Z",
+            (void*)android_view_MotionEvent_nativeIsTouchEvent },
     { "nativeGetFlags",
             "(I)I",
             (void*)android_view_MotionEvent_nativeGetFlags },
diff --git a/include/ui/Input.h b/include/ui/Input.h
index 082f11c..e92d7f5 100644
--- a/include/ui/Input.h
+++ b/include/ui/Input.h
@@ -476,6 +476,11 @@
     status_t writeToParcel(Parcel* parcel) const;
 #endif
 
+    static bool isTouchEvent(int32_t source, int32_t action);
+    inline bool isTouchEvent() const {
+        return isTouchEvent(mSource, mAction);
+    }
+
     // Low-level accessors.
     inline const int32_t* getPointerIds() const { return mPointerIds.array(); }
     inline const nsecs_t* getSampleEventTimes() const { return mSampleEventTimes.array(); }
diff --git a/libs/ui/Input.cpp b/libs/ui/Input.cpp
index a80320e..0ed0866 100644
--- a/libs/ui/Input.cpp
+++ b/libs/ui/Input.cpp
@@ -616,6 +616,23 @@
 }
 #endif
 
+bool MotionEvent::isTouchEvent(int32_t source, int32_t action) {
+    if (source & AINPUT_SOURCE_CLASS_POINTER) {
+        // Specifically excludes HOVER_MOVE and SCROLL.
+        switch (action & AMOTION_EVENT_ACTION_MASK) {
+        case AMOTION_EVENT_ACTION_DOWN:
+        case AMOTION_EVENT_ACTION_MOVE:
+        case AMOTION_EVENT_ACTION_UP:
+        case AMOTION_EVENT_ACTION_POINTER_DOWN:
+        case AMOTION_EVENT_ACTION_POINTER_UP:
+        case AMOTION_EVENT_ACTION_CANCEL:
+        case AMOTION_EVENT_ACTION_OUTSIDE:
+            return true;
+        }
+    }
+    return false;
+}
+
 
 // --- InputDeviceInfo ---
 
diff --git a/policy/src/com/android/internal/policy/impl/KeyguardViewBase.java b/policy/src/com/android/internal/policy/impl/KeyguardViewBase.java
index 36afd75..74dde9c 100644
--- a/policy/src/com/android/internal/policy/impl/KeyguardViewBase.java
+++ b/policy/src/com/android/internal/policy/impl/KeyguardViewBase.java
@@ -108,7 +108,8 @@
      * action should be posted to a handler.
      *
      * @param keyCode The wake key, which may be relevant for configuring the
-     *   keyguard.
+     *   keyguard.  May be {@link KeyEvent#KEYCODE_UNKNOWN} if waking for a reason
+     *   other than a key press.
      */
     abstract public void wakeWhenReadyTq(int keyCode);
 
diff --git a/policy/src/com/android/internal/policy/impl/KeyguardViewManager.java b/policy/src/com/android/internal/policy/impl/KeyguardViewManager.java
index edab690..8d70a7b 100644
--- a/policy/src/com/android/internal/policy/impl/KeyguardViewManager.java
+++ b/policy/src/com/android/internal/policy/impl/KeyguardViewManager.java
@@ -201,7 +201,8 @@
      * Be sure not to take any action that takes a long time; any significant
      * action should be posted to a handler.
      *
-     * @param keyCode The wake key.
+     * @param keyCode The wake key.  May be {@link KeyEvent#KEYCODE_UNKNOWN} if waking
+     * for a reason other than a key press.
      */
     public boolean wakeWhenReadyTq(int keyCode) {
         if (DEBUG) Log.d(TAG, "wakeWhenReady(" + keyCode + ")");
diff --git a/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java b/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java
index b32a729..e7a9eb1 100644
--- a/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java
+++ b/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java
@@ -810,6 +810,28 @@
     }
 
     /**
+     * When a wake motion such as an external mouse movement is received when the screen
+     * is off and the keyguard is showing, we need to decide whether to actually turn
+     * on the screen, and if so, tell the keyguard to prepare itself and poke the wake
+     * lock when it is ready.
+     *
+     * The 'Tq' suffix is per the documentation in {@link WindowManagerPolicy}.
+     * Be sure not to take any action that takes a long time; any significant
+     * action should be posted to a handler.
+     *
+     * @return Whether we poked the wake lock (and turned the screen on)
+     */
+    public boolean onWakeMotionWhenKeyguardShowingTq() {
+        if (DEBUG) Log.d(TAG, "onWakeMotionWhenKeyguardShowing()");
+
+        // give the keyguard view manager a chance to adjust the state of the
+        // keyguard based on the key that woke the device before poking
+        // the wake lock
+        wakeWhenReadyLocked(KeyEvent.KEYCODE_UNKNOWN);
+        return true;
+    }
+
+    /**
      * Callbacks from {@link KeyguardViewManager}.
      */
 
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 75ef762..8e18f2a 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -2364,6 +2364,25 @@
         return result;
     }
 
+    /** {@inheritDoc} */
+    @Override
+    public int interceptMotionBeforeQueueingWhenScreenOff(int policyFlags) {
+        int result = 0;
+
+        final boolean isWakeMotion = (policyFlags
+                & (WindowManagerPolicy.FLAG_WAKE | WindowManagerPolicy.FLAG_WAKE_DROPPED)) != 0;
+        if (isWakeMotion) {
+            if (mKeyguardMediator.isShowing()) {
+                // If the keyguard is showing, let it decide what to do with the wake motion.
+                mKeyguardMediator.onWakeMotionWhenKeyguardShowingTq();
+            } else {
+                // Otherwise, wake the device ourselves.
+                result |= ACTION_POKE_USER_ACTIVITY;
+            }
+        }
+        return result;
+    }
+
     class PassHeadsetKey implements Runnable {
         KeyEvent mKeyEvent;
 
diff --git a/services/input/EventHub.cpp b/services/input/EventHub.cpp
index 25db25e..2fe5980 100644
--- a/services/input/EventHub.cpp
+++ b/services/input/EventHub.cpp
@@ -933,6 +933,11 @@
         return -1;
     }
 
+    // Determine whether the device is external or internal.
+    if (isExternalDevice(device)) {
+        device->classes |= INPUT_DEVICE_CLASS_EXTERNAL;
+    }
+
     LOGI("New device: id=%d, fd=%d, path='%s', name='%s', classes=0x%x, "
             "configuration='%s', keyLayout='%s', keyCharacterMap='%s', builtinKeyboard=%s",
          deviceId, fd, devicePath, device->identifier.name.string(),
@@ -997,6 +1002,17 @@
     android::clearKeyboardProperties(id);
 }
 
+bool EventHub::isExternalDevice(Device* device) {
+    if (device->configuration) {
+        bool value;
+        if (device->configuration->tryGetProperty(String8("device.internal"), value)
+                && value) {
+            return false;
+        }
+    }
+    return device->identifier.bus == BUS_USB || device->identifier.bus == BUS_BLUETOOTH;
+}
+
 bool EventHub::hasKeycodeLocked(Device* device, int keycode) const {
     if (!device->keyMap.haveKeyLayout() || !device->keyBitmask) {
         return false;
diff --git a/services/input/EventHub.h b/services/input/EventHub.h
index f7936d2..445c04b 100644
--- a/services/input/EventHub.h
+++ b/services/input/EventHub.h
@@ -126,6 +126,9 @@
 
     /* The input device is a joystick (implies gamepad, has joystick absolute axes). */
     INPUT_DEVICE_CLASS_JOYSTICK      = 0x00000100,
+
+    /* The input device is external (not built-in). */
+    INPUT_DEVICE_CLASS_EXTERNAL      = 0x80000000,
 };
 
 /*
@@ -304,6 +307,8 @@
     void setKeyboardProperties(Device* device, bool builtInKeyboard);
     void clearKeyboardProperties(Device* device, bool builtInKeyboard);
 
+    bool isExternalDevice(Device* device);
+
     // Protect all internal state.
     mutable Mutex mLock;
 
diff --git a/services/input/InputDispatcher.cpp b/services/input/InputDispatcher.cpp
index c064a9c..655b672 100644
--- a/services/input/InputDispatcher.cpp
+++ b/services/input/InputDispatcher.cpp
@@ -1572,7 +1572,7 @@
 }
 
 void InputDispatcher::pokeUserActivityLocked(const EventEntry* eventEntry) {
-    int32_t eventType = POWER_MANAGER_BUTTON_EVENT;
+    int32_t eventType = POWER_MANAGER_OTHER_EVENT;
     switch (eventEntry->type) {
     case EventEntry::TYPE_MOTION: {
         const MotionEntry* motionEntry = static_cast<const MotionEntry*>(eventEntry);
@@ -1580,7 +1580,7 @@
             return;
         }
 
-        if (motionEntry->source & AINPUT_SOURCE_CLASS_POINTER) {
+        if (MotionEvent::isTouchEvent(motionEntry->source, motionEntry->action)) {
             eventType = POWER_MANAGER_TOUCH_EVENT;
         }
         break;
@@ -1590,6 +1590,7 @@
         if (keyEntry->flags & AKEY_EVENT_FLAG_CANCELED) {
             return;
         }
+        eventType = POWER_MANAGER_BUTTON_EVENT;
         break;
     }
     }
@@ -2304,7 +2305,7 @@
     }
 
     policyFlags |= POLICY_FLAG_TRUSTED;
-    mPolicy->interceptGenericBeforeQueueing(eventTime, /*byref*/ policyFlags);
+    mPolicy->interceptMotionBeforeQueueing(eventTime, /*byref*/ policyFlags);
 
     bool needWake;
     { // acquire lock
@@ -2498,7 +2499,7 @@
         }
 
         nsecs_t eventTime = motionEvent->getEventTime();
-        mPolicy->interceptGenericBeforeQueueing(eventTime, /*byref*/ policyFlags);
+        mPolicy->interceptMotionBeforeQueueing(eventTime, /*byref*/ policyFlags);
 
         mLock.lock();
         const nsecs_t* sampleEventTimes = motionEvent->getSampleEventTimes();
diff --git a/services/input/InputDispatcher.h b/services/input/InputDispatcher.h
index 304b1bb..1e118c4 100644
--- a/services/input/InputDispatcher.h
+++ b/services/input/InputDispatcher.h
@@ -165,14 +165,14 @@
      */
     virtual void interceptKeyBeforeQueueing(const KeyEvent* keyEvent, uint32_t& policyFlags) = 0;
 
-    /* Intercepts a generic touch, trackball or other event before queueing it.
+    /* Intercepts a touch, trackball or other motion event before queueing it.
      * The policy can use this method as an opportunity to perform power management functions
      * and early event preprocessing such as updating policy flags.
      *
      * This method is expected to set the POLICY_FLAG_PASS_TO_USER policy flag if the event
      * should be dispatched to applications.
      */
-    virtual void interceptGenericBeforeQueueing(nsecs_t when, uint32_t& policyFlags) = 0;
+    virtual void interceptMotionBeforeQueueing(nsecs_t when, uint32_t& policyFlags) = 0;
 
     /* Allows the policy a chance to intercept a key before dispatching. */
     virtual bool interceptKeyBeforeDispatching(const sp<InputWindowHandle>& inputWindowHandle,
diff --git a/services/input/InputReader.cpp b/services/input/InputReader.cpp
index c3c143c..b92c3b5 100644
--- a/services/input/InputReader.cpp
+++ b/services/input/InputReader.cpp
@@ -233,6 +233,11 @@
 InputDevice* InputReader::createDevice(int32_t deviceId, const String8& name, uint32_t classes) {
     InputDevice* device = new InputDevice(this, deviceId, name);
 
+    // External devices.
+    if (classes & INPUT_DEVICE_CLASS_EXTERNAL) {
+        device->setExternal(true);
+    }
+
     // Switch-like devices.
     if (classes & INPUT_DEVICE_CLASS_SWITCH) {
         device->addMapper(new SwitchInputMapper(device));
@@ -565,7 +570,7 @@
 // --- InputDevice ---
 
 InputDevice::InputDevice(InputReaderContext* context, int32_t id, const String8& name) :
-        mContext(context), mId(id), mName(name), mSources(0) {
+        mContext(context), mId(id), mName(name), mSources(0), mIsExternal(false) {
 }
 
 InputDevice::~InputDevice() {
@@ -582,6 +587,7 @@
 
     dump.appendFormat(INDENT "Device %d: %s\n", deviceInfo.getId(),
             deviceInfo.getName().string());
+    dump.appendFormat(INDENT2 "IsExternal: %s\n", toString(mIsExternal));
     dump.appendFormat(INDENT2 "Sources: 0x%08x\n", deviceInfo.getSources());
     dump.appendFormat(INDENT2 "KeyboardType: %d\n", deviceInfo.getKeyboardType());
 
@@ -985,6 +991,16 @@
         downTime = mLocked.downTime;
     } // release lock
 
+    // Key down on external an keyboard should wake the device.
+    // We don't do this for internal keyboards to prevent them from waking up in your pocket.
+    // For internal keyboards, the key layout file should specify the policy flags for
+    // each wake key individually.
+    // TODO: Use the input device configuration to control this behavior more finely.
+    if (down && getDevice()->isExternal()
+            && !(policyFlags & (POLICY_FLAG_WAKE | POLICY_FLAG_WAKE_DROPPED))) {
+        policyFlags |= POLICY_FLAG_WAKE_DROPPED;
+    }
+
     if (metaStateChanged) {
         getContext()->updateGlobalMetaState();
     }
@@ -1379,9 +1395,18 @@
         }
     } // release lock
 
+    // Moving an external trackball or mouse should wake the device.
+    // We don't do this for internal cursor devices to prevent them from waking up
+    // the device in your pocket.
+    // TODO: Use the input device configuration to control this behavior more finely.
+    uint32_t policyFlags = 0;
+    if (getDevice()->isExternal()) {
+        policyFlags |= POLICY_FLAG_WAKE_DROPPED;
+    }
+
     int32_t metaState = mContext->getGlobalMetaState();
     int32_t pointerId = 0;
-    getDispatcher()->notifyMotion(when, getDeviceId(), mSources, 0,
+    getDispatcher()->notifyMotion(when, getDeviceId(), mSources, policyFlags,
             motionEventAction, 0, metaState, AMOTION_EVENT_EDGE_FLAG_NONE,
             1, &pointerId, &pointerCoords, mXPrecision, mYPrecision, downTime);
 
@@ -1391,7 +1416,7 @@
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, vscroll);
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll);
 
-        getDispatcher()->notifyMotion(when, getDeviceId(), mSources, 0,
+        getDispatcher()->notifyMotion(when, getDeviceId(), mSources, policyFlags,
                 AMOTION_EVENT_ACTION_SCROLL, 0, metaState, AMOTION_EVENT_EDGE_FLAG_NONE,
                 1, &pointerId, &pointerCoords, mXPrecision, mYPrecision, downTime);
     }
@@ -2308,8 +2333,6 @@
 }
 
 void TouchInputMapper::syncTouch(nsecs_t when, bool havePointerIds) {
-    uint32_t policyFlags = 0;
-
     // Preprocess pointer data.
     if (mParameters.useBadTouchFilter) {
         if (applyBadTouchFilter()) {
@@ -2338,9 +2361,18 @@
         savedTouch = & mCurrentTouch;
     }
 
-    // Hide the pointer on an initial down.
+    uint32_t policyFlags = 0;
     if (mLastTouch.pointerCount == 0 && mCurrentTouch.pointerCount != 0) {
+        // Hide the pointer on an initial down.
         getContext()->fadePointer();
+
+        // Initial downs on external touch devices should wake the device.
+        // We don't do this for internal touch screens to prevent them from waking
+        // up in your pocket.
+        // TODO: Use the input device configuration to control this behavior more finely.
+        if (getDevice()->isExternal()) {
+            policyFlags |= POLICY_FLAG_WAKE_DROPPED;
+        }
     }
 
     // Process touches and virtual keys.
@@ -4017,8 +4049,14 @@
         axis.oldValue = axis.newValue;
     }
 
+    // Moving a joystick axis should not wake the devide because joysticks can
+    // be fairly noisy even when not in use.  On the other hand, pushing a gamepad
+    // button will likely wake the device.
+    // TODO: Use the input device configuration to control this behavior more finely.
+    uint32_t policyFlags = 0;
+
     int32_t pointerId = 0;
-    getDispatcher()->notifyMotion(when, getDeviceId(), AINPUT_SOURCE_JOYSTICK, 0,
+    getDispatcher()->notifyMotion(when, getDeviceId(), AINPUT_SOURCE_JOYSTICK, policyFlags,
             AMOTION_EVENT_ACTION_MOVE, 0, metaState, AMOTION_EVENT_EDGE_FLAG_NONE,
             1, &pointerId, &pointerCoords, 0, 0, 0);
 }
diff --git a/services/input/InputReader.h b/services/input/InputReader.h
index b344ffe..655f0f0 100644
--- a/services/input/InputReader.h
+++ b/services/input/InputReader.h
@@ -286,6 +286,9 @@
     inline const String8& getName() { return mName; }
     inline uint32_t getSources() { return mSources; }
 
+    inline bool isExternal() { return mIsExternal; }
+    inline void setExternal(bool external) { mIsExternal = external; }
+
     inline bool isIgnored() { return mMappers.isEmpty(); }
 
     void dump(String8& dump);
@@ -317,6 +320,7 @@
 
     String8 mName;
     uint32_t mSources;
+    bool mIsExternal;
 
     typedef int32_t (InputMapper::*GetStateFunc)(uint32_t sourceMask, int32_t code);
     int32_t getState(uint32_t sourceMask, int32_t code, GetStateFunc getStateFunc);
diff --git a/services/input/tests/InputDispatcher_test.cpp b/services/input/tests/InputDispatcher_test.cpp
index 5ba1867..bb3449b 100644
--- a/services/input/tests/InputDispatcher_test.cpp
+++ b/services/input/tests/InputDispatcher_test.cpp
@@ -58,7 +58,7 @@
     virtual void interceptKeyBeforeQueueing(const KeyEvent* keyEvent, uint32_t& policyFlags) {
     }
 
-    virtual void interceptGenericBeforeQueueing(nsecs_t when, uint32_t& policyFlags) {
+    virtual void interceptMotionBeforeQueueing(nsecs_t when, uint32_t& policyFlags) {
     }
 
     virtual bool interceptKeyBeforeDispatching(const sp<InputWindowHandle>& inputWindowHandle,
diff --git a/services/input/tests/InputReader_test.cpp b/services/input/tests/InputReader_test.cpp
index 864241e..8404b3a 100644
--- a/services/input/tests/InputReader_test.cpp
+++ b/services/input/tests/InputReader_test.cpp
@@ -188,6 +188,8 @@
 class FakeInputDispatcher : public InputDispatcherInterface {
 public:
     struct NotifyConfigurationChangedArgs {
+        NotifyConfigurationChangedArgs() : eventTime(0) { }
+
         nsecs_t eventTime;
     };
 
@@ -687,6 +689,10 @@
         }
     }
 
+    virtual bool isExternal(int32_t deviceId) const {
+        return false;
+    }
+
     virtual void dump(String8& dump) {
     }
 };
diff --git a/services/java/com/android/server/wm/InputManager.java b/services/java/com/android/server/wm/InputManager.java
index 326eca7..77cec5d 100644
--- a/services/java/com/android/server/wm/InputManager.java
+++ b/services/java/com/android/server/wm/InputManager.java
@@ -448,7 +448,13 @@
             return mWindowManagerService.mInputMonitor.interceptKeyBeforeQueueing(
                     event, policyFlags, isScreenOn);
         }
-        
+
+        @SuppressWarnings("unused")
+        public int interceptMotionBeforeQueueingWhenScreenOff(int policyFlags) {
+            return mWindowManagerService.mInputMonitor.interceptMotionBeforeQueueingWhenScreenOff(
+                    policyFlags);
+        }
+
         @SuppressWarnings("unused")
         public boolean interceptKeyBeforeDispatching(InputWindowHandle focus,
                 KeyEvent event, int policyFlags) {
diff --git a/services/java/com/android/server/wm/InputMonitor.java b/services/java/com/android/server/wm/InputMonitor.java
index b1833c4..45a78af 100644
--- a/services/java/com/android/server/wm/InputMonitor.java
+++ b/services/java/com/android/server/wm/InputMonitor.java
@@ -258,7 +258,14 @@
             KeyEvent event, int policyFlags, boolean isScreenOn) {
         return mService.mPolicy.interceptKeyBeforeQueueing(event, policyFlags, isScreenOn);
     }
-    
+
+    /* Provides an opportunity for the window manager policy to intercept early
+     * motion event processing when the screen is off since these events are normally
+     * dropped. */
+    public int interceptMotionBeforeQueueingWhenScreenOff(int policyFlags) {
+        return mService.mPolicy.interceptMotionBeforeQueueingWhenScreenOff(policyFlags);
+    }
+
     /* Provides an opportunity for the window manager policy to process a key before
      * ordinary dispatch. */
     public boolean interceptKeyBeforeDispatching(
diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp
index 5ef234a..00d0af1 100644
--- a/services/jni/com_android_server_InputManager.cpp
+++ b/services/jni/com_android_server_InputManager.cpp
@@ -59,6 +59,7 @@
     jmethodID notifyInputChannelBroken;
     jmethodID notifyANR;
     jmethodID interceptKeyBeforeQueueing;
+    jmethodID interceptMotionBeforeQueueingWhenScreenOff;
     jmethodID interceptKeyBeforeDispatching;
     jmethodID dispatchUnhandledKey;
     jmethodID checkInjectEventsPermission;
@@ -178,7 +179,7 @@
     virtual nsecs_t getKeyRepeatDelay();
     virtual int32_t getMaxEventsPerSecond();
     virtual void interceptKeyBeforeQueueing(const KeyEvent* keyEvent, uint32_t& policyFlags);
-    virtual void interceptGenericBeforeQueueing(nsecs_t when, uint32_t& policyFlags);
+    virtual void interceptMotionBeforeQueueing(nsecs_t when, uint32_t& policyFlags);
     virtual bool interceptKeyBeforeDispatching(const sp<InputWindowHandle>& inputWindowHandle,
             const KeyEvent* keyEvent, uint32_t policyFlags);
     virtual bool dispatchUnhandledKey(const sp<InputWindowHandle>& inputWindowHandle,
@@ -215,6 +216,7 @@
     } mLocked;
 
     void updateInactivityFadeDelayLocked(const sp<PointerController>& controller);
+    void handleInterceptActions(jint wmActions, nsecs_t when, uint32_t& policyFlags);
 
     // Power manager interactions.
     bool isScreenOn();
@@ -620,10 +622,6 @@
     // - Ask the window manager what to do with normal events and trusted injected events.
     // - For normal events wake and brighten the screen if currently off or dim.
     if ((policyFlags & POLICY_FLAG_TRUSTED)) {
-        const int32_t WM_ACTION_PASS_TO_USER = 1;
-        const int32_t WM_ACTION_POKE_USER_ACTIVITY = 2;
-        const int32_t WM_ACTION_GO_TO_SLEEP = 4;
-
         nsecs_t when = keyEvent->getEventTime();
         bool isScreenOn = this->isScreenOn();
         bool isScreenBright = this->isScreenBright();
@@ -655,23 +653,13 @@
             }
         }
 
-        if (wmActions & WM_ACTION_GO_TO_SLEEP) {
-            android_server_PowerManagerService_goToSleep(when);
-        }
-
-        if (wmActions & WM_ACTION_POKE_USER_ACTIVITY) {
-            android_server_PowerManagerService_userActivity(when, POWER_MANAGER_BUTTON_EVENT);
-        }
-
-        if (wmActions & WM_ACTION_PASS_TO_USER) {
-            policyFlags |= POLICY_FLAG_PASS_TO_USER;
-        }
+        handleInterceptActions(wmActions, when, /*byref*/ policyFlags);
     } else {
         policyFlags |= POLICY_FLAG_PASS_TO_USER;
     }
 }
 
-void NativeInputManager::interceptGenericBeforeQueueing(nsecs_t when, uint32_t& policyFlags) {
+void NativeInputManager::interceptMotionBeforeQueueing(nsecs_t when, uint32_t& policyFlags) {
     // Policy:
     // - Ignore untrusted events and pass them along.
     // - No special filtering for injected events required at this time.
@@ -684,12 +672,55 @@
             if (!isScreenBright()) {
                 policyFlags |= POLICY_FLAG_BRIGHT_HERE;
             }
+        } else {
+            JNIEnv* env = jniEnv();
+            jint wmActions = env->CallIntMethod(mCallbacksObj,
+                        gCallbacksClassInfo.interceptMotionBeforeQueueingWhenScreenOff,
+                        policyFlags);
+            if (checkAndClearExceptionFromCallback(env,
+                    "interceptMotionBeforeQueueingWhenScreenOff")) {
+                wmActions = 0;
+            }
+
+            policyFlags |= POLICY_FLAG_WOKE_HERE | POLICY_FLAG_BRIGHT_HERE;
+            handleInterceptActions(wmActions, when, /*byref*/ policyFlags);
         }
     } else {
         policyFlags |= POLICY_FLAG_PASS_TO_USER;
     }
 }
 
+void NativeInputManager::handleInterceptActions(jint wmActions, nsecs_t when,
+        uint32_t& policyFlags) {
+    enum {
+        WM_ACTION_PASS_TO_USER = 1,
+        WM_ACTION_POKE_USER_ACTIVITY = 2,
+        WM_ACTION_GO_TO_SLEEP = 4,
+    };
+
+    if (wmActions & WM_ACTION_GO_TO_SLEEP) {
+#ifdef DEBUG_INPUT_DISPATCHER_POLICY
+        LOGD("handleInterceptActions: Going to sleep.");
+#endif
+        android_server_PowerManagerService_goToSleep(when);
+    }
+
+    if (wmActions & WM_ACTION_POKE_USER_ACTIVITY) {
+#ifdef DEBUG_INPUT_DISPATCHER_POLICY
+        LOGD("handleInterceptActions: Poking user activity.");
+#endif
+        android_server_PowerManagerService_userActivity(when, POWER_MANAGER_BUTTON_EVENT);
+    }
+
+    if (wmActions & WM_ACTION_PASS_TO_USER) {
+        policyFlags |= POLICY_FLAG_PASS_TO_USER;
+    } else {
+#ifdef DEBUG_INPUT_DISPATCHER_POLICY
+        LOGD("handleInterceptActions: Not passing key to user.");
+#endif
+    }
+}
+
 bool NativeInputManager::interceptKeyBeforeDispatching(
         const sp<InputWindowHandle>& inputWindowHandle,
         const KeyEvent* keyEvent, uint32_t policyFlags) {
@@ -1204,6 +1235,10 @@
     GET_METHOD_ID(gCallbacksClassInfo.interceptKeyBeforeQueueing, gCallbacksClassInfo.clazz,
             "interceptKeyBeforeQueueing", "(Landroid/view/KeyEvent;IZ)I");
 
+    GET_METHOD_ID(gCallbacksClassInfo.interceptMotionBeforeQueueingWhenScreenOff,
+            gCallbacksClassInfo.clazz,
+            "interceptMotionBeforeQueueingWhenScreenOff", "(I)I");
+
     GET_METHOD_ID(gCallbacksClassInfo.interceptKeyBeforeDispatching, gCallbacksClassInfo.clazz,
             "interceptKeyBeforeDispatching",
             "(Lcom/android/server/wm/InputWindowHandle;Landroid/view/KeyEvent;I)Z");