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/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");