Add support for new input sources.

Added several new coordinate values to MotionEvents to capture
touch major/minor area, tool major/minor area and orientation.

Renamed NDK input constants per convention.

Added InputDevice class in Java which will eventually provide
useful information about available input devices.

Added APIs for manufacturing new MotionEvent objects with multiple
pointers and all necessary coordinate data.

Fixed a bug in the input dispatcher where it could get stuck with
a pointer down forever.

Fixed a bug in the WindowManager where the input window list could
end up containing stale removed windows.

Fixed a bug in the WindowManager where the input channel was being
removed only after the final animation transition had taken place
which caused spurious WINDOW DIED log messages to be printed.

Change-Id: Ie55084da319b20aad29b28a0499b8dd98bb5da68
diff --git a/services/java/com/android/server/InputManager.java b/services/java/com/android/server/InputManager.java
index cdae27c..3c60a98 100644
--- a/services/java/com/android/server/InputManager.java
+++ b/services/java/com/android/server/InputManager.java
@@ -77,9 +77,9 @@
     private static native boolean nativeHasKeys(int[] keyCodes, boolean[] keyExists);
     private static native void nativeRegisterInputChannel(InputChannel inputChannel);
     private static native void nativeUnregisterInputChannel(InputChannel inputChannel);
-    private static native int nativeInjectKeyEvent(KeyEvent event, int nature,
+    private static native int nativeInjectKeyEvent(KeyEvent event,
             int injectorPid, int injectorUid, boolean sync, int timeoutMillis);
-    private static native int nativeInjectMotionEvent(MotionEvent event, int nature,
+    private static native int nativeInjectMotionEvent(MotionEvent event,
             int injectorPid, int injectorUid, boolean sync, int timeoutMillis);
     private static native void nativeSetInputWindows(InputWindow[] windows);
     private static native void nativeSetInputDispatchMode(boolean enabled, boolean frozen);
@@ -229,14 +229,13 @@
      * to be dispatched before it can determine whether input event injection will be
      * permitted based on the current input focus.
      * @param event The event to inject.
-     * @param nature The nature of the event.
      * @param injectorPid The pid of the injecting application.
      * @param injectorUid The uid of the injecting application.
      * @param sync If true, waits for the event to be completed before returning.
      * @param timeoutMillis The injection timeout in milliseconds.
      * @return One of the INPUT_EVENT_INJECTION_XXX constants.
      */
-    public int injectKeyEvent(KeyEvent event, int nature, int injectorPid, int injectorUid,
+    public int injectKeyEvent(KeyEvent event, int injectorPid, int injectorUid,
             boolean sync, int timeoutMillis) {
         if (event == null) {
             throw new IllegalArgumentException("event must not be null");
@@ -248,7 +247,7 @@
             throw new IllegalArgumentException("timeoutMillis must be positive");
         }
         
-        return nativeInjectKeyEvent(event, nature, injectorPid, injectorUid,
+        return nativeInjectKeyEvent(event, injectorPid, injectorUid,
                 sync, timeoutMillis);
     }
     
@@ -258,7 +257,6 @@
      * to be dispatched before it can determine whether input event injection will be
      * permitted based on the current input focus.
      * @param event The event to inject.
-     * @param nature The nature of the event.
      * @param sync If true, waits for the event to be completed before returning.
      * @param injectorPid The pid of the injecting application.
      * @param injectorUid The uid of the injecting application.
@@ -266,7 +264,7 @@
      * @param timeoutMillis The injection timeout in milliseconds.
      * @return One of the INPUT_EVENT_INJECTION_XXX constants.
      */
-    public int injectMotionEvent(MotionEvent event, int nature, int injectorPid, int injectorUid,
+    public int injectMotionEvent(MotionEvent event, int injectorPid, int injectorUid,
             boolean sync, int timeoutMillis) {
         if (event == null) {
             throw new IllegalArgumentException("event must not be null");
@@ -278,7 +276,7 @@
             throw new IllegalArgumentException("timeoutMillis must be positive");
         }
         
-        return nativeInjectMotionEvent(event, nature, injectorPid, injectorUid,
+        return nativeInjectMotionEvent(event, injectorPid, injectorUid,
                 sync, timeoutMillis);
     }
     
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index c6e5cd2..37a2a58 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -102,6 +102,7 @@
 import android.view.IWindowManager;
 import android.view.IWindowSession;
 import android.view.InputChannel;
+import android.view.InputDevice;
 import android.view.InputQueue;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
@@ -2017,6 +2018,8 @@
             + ", surface=" + win.mSurface);
 
         final long origId = Binder.clearCallingIdentity();
+        
+        win.disposeInputChannel();
 
         if (DEBUG_APP_TRANSITIONS) Slog.v(
                 TAG, "Remove " + win + ": mSurface=" + win.mSurface
@@ -2077,8 +2080,6 @@
     }
 
     private void removeWindowInnerLocked(Session session, WindowState win) {
-        mInputMonitor.windowIsBeingRemovedLw(win);
-
         win.mRemoved = true;
 
         if (mInputMethodTarget == win) {
@@ -2157,6 +2158,8 @@
                 win.mAppToken.updateReportedVisibilityLocked();
             }
         }
+        
+        mInputMonitor.updateInputWindowsLw();
     }
 
     private static void logSurface(WindowState w, String msg, RuntimeException where) {
@@ -2907,7 +2910,6 @@
                         if (win.isVisibleNow()) {
                             applyAnimationLocked(win,
                                     WindowManagerPolicy.TRANSIT_EXIT, false);
-                            mInputMonitor.windowIsBeingRemovedLw(win);
                             changed = true;
                         }
                     }
@@ -2925,6 +2927,7 @@
                     }
                 }
 
+                mInputMonitor.updateInputWindowsLw();
             } else {
                 Slog.w(TAG, "Attempted to remove non-existing token: " + token);
             }
@@ -5111,7 +5114,7 @@
             final int N = windows.size();
             for (int i = N - 1; i >= 0; i--) {
                 final WindowState child = (WindowState) windows.get(i);
-                if (child.mInputChannel == null) {
+                if (child.mInputChannel == null || child.mRemoved) {
                     // Skip this window because it cannot possibly receive input.
                     continue;
                 }
@@ -5288,11 +5291,6 @@
             }
         }
         
-        public void windowIsBeingRemovedLw(WindowState window) {
-            // Window is being removed.
-            updateInputWindowsLw();
-        }
-        
         public void pauseDispatchingLw(WindowToken window) {
             if (! window.paused) {
                 if (DEBUG_INPUT) {
@@ -5410,19 +5408,24 @@
         int metaState = ev.getMetaState();
         int deviceId = ev.getDeviceId();
         int scancode = ev.getScanCode();
+        int source = ev.getSource();
+        
+        if (source == InputDevice.SOURCE_UNKNOWN) {
+            source = InputDevice.SOURCE_KEYBOARD;
+        }
 
         if (eventTime == 0) eventTime = SystemClock.uptimeMillis();
         if (downTime == 0) downTime = eventTime;
 
         KeyEvent newEvent = new KeyEvent(downTime, eventTime, action, code, repeatCount, metaState,
-                deviceId, scancode, KeyEvent.FLAG_FROM_SYSTEM);
+                deviceId, scancode, KeyEvent.FLAG_FROM_SYSTEM, source);
 
         final int pid = Binder.getCallingPid();
         final int uid = Binder.getCallingUid();
         final long ident = Binder.clearCallingIdentity();
         
         final int result = mInputManager.injectKeyEvent(newEvent,
-                InputQueue.INPUT_EVENT_NATURE_KEY, pid, uid, sync, INJECTION_TIMEOUT_MILLIS);
+                pid, uid, sync, INJECTION_TIMEOUT_MILLIS);
         
         Binder.restoreCallingIdentity(ident);
         return reportInjectionResult(result);
@@ -5442,8 +5445,13 @@
         final int uid = Binder.getCallingUid();
         final long ident = Binder.clearCallingIdentity();
         
-        final int result = mInputManager.injectMotionEvent(ev,
-                InputQueue.INPUT_EVENT_NATURE_TOUCH, pid, uid, sync, INJECTION_TIMEOUT_MILLIS);
+        MotionEvent newEvent = MotionEvent.obtain(ev);
+        if ((newEvent.getSource() & InputDevice.SOURCE_CLASS_POINTER) == 0) {
+            newEvent.setSource(InputDevice.SOURCE_TOUCHSCREEN);
+        }
+        
+        final int result = mInputManager.injectMotionEvent(newEvent,
+                pid, uid, sync, INJECTION_TIMEOUT_MILLIS);
         
         Binder.restoreCallingIdentity(ident);
         return reportInjectionResult(result);
@@ -5463,8 +5471,13 @@
         final int uid = Binder.getCallingUid();
         final long ident = Binder.clearCallingIdentity();
         
-        final int result = mInputManager.injectMotionEvent(ev,
-                InputQueue.INPUT_EVENT_NATURE_TRACKBALL, pid, uid, sync, INJECTION_TIMEOUT_MILLIS);
+        MotionEvent newEvent = MotionEvent.obtain(ev);
+        if ((newEvent.getSource() & InputDevice.SOURCE_CLASS_TRACKBALL) == 0) {
+            newEvent.setSource(InputDevice.SOURCE_TRACKBALL);
+        }
+        
+        final int result = mInputManager.injectMotionEvent(newEvent,
+                pid, uid, sync, INJECTION_TIMEOUT_MILLIS);
         
         Binder.restoreCallingIdentity(ident);
         return reportInjectionResult(result);
@@ -6960,6 +6973,8 @@
         }
 
         void removeLocked() {
+            disposeInputChannel();
+            
             if (mAttachedWindow != null) {
                 mAttachedWindow.mChildWindows.remove(this);
             }
@@ -6971,7 +6986,9 @@
                 // Ignore if it has already been removed (usually because
                 // we are doing this as part of processing a death note.)
             }
-            
+        }
+        
+        void disposeInputChannel() {
             if (mInputChannel != null) {
                 mInputManager.unregisterInputChannel(mInputChannel);
                 
diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp
index 26e105a..bc052a0 100644
--- a/services/jni/com_android_server_InputManager.cpp
+++ b/services/jni/com_android_server_InputManager.cpp
@@ -354,7 +354,7 @@
 
     void releaseTouchedWindowLd();
 
-    int32_t waitForTrackballEventTargets(MotionEvent* motionEvent, uint32_t policyFlags,
+    int32_t waitForNonTouchEventTargets(MotionEvent* motionEvent, uint32_t policyFlags,
             int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets);
     int32_t waitForTouchEventTargets(MotionEvent* motionEvent, uint32_t policyFlags,
             int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets);
@@ -1222,14 +1222,17 @@
     return injectionResult;
 }
 
+enum InjectionPermission {
+    INJECTION_PERMISSION_UNKNOWN,
+    INJECTION_PERMISSION_GRANTED,
+    INJECTION_PERMISSION_DENIED
+};
+
 int32_t NativeInputManager::waitForTouchedWindowLd(MotionEvent* motionEvent, uint32_t policyFlags,
         int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets,
         InputWindow*& outTouchedWindow) {
     nsecs_t startTime = now();
 
-    int32_t injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
-    int32_t action = motionEvent->getAction();
-
     // For security reasons, we defer updating the touch state until we are sure that
     // event injection will be allowed.
     //
@@ -1255,8 +1258,13 @@
     //       instead of POLICY_FLAG_WOKE_HERE...
     //
     bool screenWasOff = false; // original policy: policyFlags & POLICY_FLAG_BRIGHT_HERE;
+
+    int32_t action = motionEvent->getAction();
+
     bool firstIteration = true;
     ANRTimer anrTimer;
+    int32_t injectionResult;
+    InjectionPermission injectionPermission;
     for (;;) {
         if (firstIteration) {
             firstIteration = false;
@@ -1265,7 +1273,8 @@
                 LOGW("Dropping event because the dispatcher timed out waiting to identify "
                         "the window that should receive it.");
                 injectionResult = INPUT_EVENT_INJECTION_TIMED_OUT;
-                break;
+                injectionPermission = INJECTION_PERMISSION_UNKNOWN;
+                break; // timed out, exit wait loop
             }
         }
 
@@ -1273,6 +1282,7 @@
         if (! mDispatchEnabled) {
             LOGI("Dropping event because input dispatch is disabled.");
             injectionResult = INPUT_EVENT_INJECTION_FAILED;
+            injectionPermission = INJECTION_PERMISSION_UNKNOWN;
             break; // failed, exit wait loop
         }
 
@@ -1286,7 +1296,9 @@
         }
 
         // Update the touch state as needed based on the properties of the touch event.
-        if (action == MOTION_EVENT_ACTION_DOWN) {
+        if (action == AMOTION_EVENT_ACTION_DOWN) {
+            /* Case 1: ACTION_DOWN */
+
             InputWindow* newTouchedWindow = NULL;
             mTempTouchedOutsideWindows.clear();
 
@@ -1348,12 +1360,14 @@
 
                 LOGI("Dropping event because there is no touched window or focused application.");
                 injectionResult = INPUT_EVENT_INJECTION_FAILED;
+                injectionPermission = INJECTION_PERMISSION_UNKNOWN;
                 break; // failed, exit wait loop
             }
 
             // Check permissions.
             if (! checkInjectionPermission(newTouchedWindow, injectorPid, injectorUid)) {
                 injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
+                injectionPermission = INJECTION_PERMISSION_DENIED;
                 break; // failed, exit wait loop
             }
 
@@ -1374,18 +1388,33 @@
             if (newTouchedWindow->hasWallpaper) {
                 mTouchedWallpaperWindows.appendVector(mWallpaperWindows);
             }
+
+            injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
+            injectionPermission = INJECTION_PERMISSION_GRANTED;
             break; // done
         } else {
+            /* Case 2: Everything but ACTION_DOWN */
+
             // Check permissions.
             if (! checkInjectionPermission(mTouchedWindow, injectorPid, injectorUid)) {
                 injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
+                injectionPermission = INJECTION_PERMISSION_DENIED;
+                break; // failed, exit wait loop
+            }
+
+            // If the pointer is not currently down, then ignore the event.
+            if (! mTouchDown) {
+                LOGI("Dropping event because the pointer is not down.");
+                injectionResult = INPUT_EVENT_INJECTION_FAILED;
+                injectionPermission = INJECTION_PERMISSION_GRANTED;
                 break; // failed, exit wait loop
             }
 
             // If there is no currently touched window then fail.
-            if (! mTouchedWindow || ! mTouchDown) {
-                LOGI("Dropping event because touched window is no longer valid.");
+            if (! mTouchedWindow) {
+                LOGW("Dropping event because there is no touched window to receive it.");
                 injectionResult = INPUT_EVENT_INJECTION_FAILED;
+                injectionPermission = INJECTION_PERMISSION_GRANTED;
                 break; // failed, exit wait loop
             }
 
@@ -1399,16 +1428,14 @@
             }
 
             // Success!
+            injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
+            injectionPermission = INJECTION_PERMISSION_GRANTED;
             break; // done
         }
     }
 
     // Output targets.
-    bool havePermission;
     if (injectionResult == INPUT_EVENT_INJECTION_SUCCEEDED) {
-        // Injection succeeded so the injector must have permission.
-        havePermission = true;
-
         size_t numWallpaperWindows = mTouchedWallpaperWindows.size();
         for (size_t i = 0; i < numWallpaperWindows; i++) {
             addTarget(mTouchedWallpaperWindows[i], 0, 0, outTargets);
@@ -1423,25 +1450,23 @@
                 anrTimer.getTimeSpentWaitingForApplication(), outTargets);
         outTouchedWindow = mTouchedWindow;
     } else {
-        if (injectionResult != INPUT_EVENT_INJECTION_PERMISSION_DENIED
-                && checkInjectionPermission(NULL, injectorPid, injectorUid)) {
-            // Injection failed but the injector does have permission to inject events.
-            // While we might not have found a valid target for the injected event, we
-            // still want to update the dispatch state to take it into account.
-            havePermission = true;
-        } else {
-            // Injector does not have permission to inject events.
-            // We make sure to leave the dispatch state unchanged.
-            havePermission = false;
-        }
         outTouchedWindow = NULL;
     }
     mTempTouchedOutsideWindows.clear();
 
-    // Update final pieces of touch state now that we know for sure whether the injector
-    // had permission to perform the injection.
-    if (havePermission) {
-        if (action == MOTION_EVENT_ACTION_DOWN) {
+    // Check injection permission once and for all.
+    if (injectionPermission == INJECTION_PERMISSION_UNKNOWN) {
+        if (checkInjectionPermission(action == AMOTION_EVENT_ACTION_DOWN ? NULL : mTouchedWindow,
+                injectorPid, injectorUid)) {
+            injectionPermission = INJECTION_PERMISSION_GRANTED;
+        } else {
+            injectionPermission = INJECTION_PERMISSION_DENIED;
+        }
+    }
+
+    // Update final pieces of touch state if the injector had permission.
+    if (injectionPermission == INJECTION_PERMISSION_GRANTED) {
+        if (action == AMOTION_EVENT_ACTION_DOWN) {
             if (mTouchDown) {
                 // This is weird.  We got a down but we thought it was already down!
                 LOGW("Pointer down received while already down.");
@@ -1454,10 +1479,12 @@
                 // be holding on to an earlier target from a previous touch down.  Release it.
                 releaseTouchedWindowLd();
             }
-        } else if (action == MOTION_EVENT_ACTION_UP) {
+        } else if (action == AMOTION_EVENT_ACTION_UP) {
             mTouchDown = false;
             releaseTouchedWindowLd();
         }
+    } else {
+        LOGW("Not updating touch focus because injection was denied.");
     }
 
 #if DEBUG_FOCUS
@@ -1557,26 +1584,21 @@
             policyFlags, injectorPid, injectorUid);
 #endif
 
-    switch (motionEvent->getNature()) {
-    case INPUT_EVENT_NATURE_TRACKBALL:
-        return waitForTrackballEventTargets(motionEvent, policyFlags, injectorPid, injectorUid,
-                outTargets);
-
-    case INPUT_EVENT_NATURE_TOUCH:
+    int32_t source = motionEvent->getSource();
+    if (source & AINPUT_SOURCE_CLASS_POINTER) {
         return waitForTouchEventTargets(motionEvent, policyFlags, injectorPid, injectorUid,
                 outTargets);
-
-    default:
-        assert(false);
-        return INPUT_EVENT_INJECTION_FAILED;
+    } else {
+        return waitForNonTouchEventTargets(motionEvent, policyFlags, injectorPid, injectorUid,
+                outTargets);
     }
 }
 
-int32_t NativeInputManager::waitForTrackballEventTargets(MotionEvent* motionEvent,
+int32_t NativeInputManager::waitForNonTouchEventTargets(MotionEvent* motionEvent,
         uint32_t policyFlags, int32_t injectorPid, int32_t injectorUid,
         Vector<InputTarget>& outTargets) {
 #if DEBUG_INPUT_DISPATCHER_POLICY
-    LOGD("waitForTrackballEventTargets - policyFlags=%d, injectorPid=%d, injectorUid=%d",
+    LOGD("waitForNonTouchEventTargets - policyFlags=%d, injectorPid=%d, injectorUid=%d",
             policyFlags, injectorPid, injectorUid);
 #endif
 
@@ -1622,10 +1644,10 @@
 
     int32_t eventType;
     switch (motionEvent->getAction()) {
-    case MOTION_EVENT_ACTION_DOWN:
+    case AMOTION_EVENT_ACTION_DOWN:
         eventType = POWER_MANAGER_TOUCH_EVENT;
         break;
-    case MOTION_EVENT_ACTION_UP:
+    case AMOTION_EVENT_ACTION_UP:
         eventType = POWER_MANAGER_TOUCH_UP_EVENT;
         break;
     default:
@@ -1852,7 +1874,7 @@
 static jint android_server_InputManager_nativeGetScanCodeState(JNIEnv* env, jclass clazz,
         jint deviceId, jint deviceClasses, jint scanCode) {
     if (checkInputManagerUnitialized(env)) {
-        return KEY_STATE_UNKNOWN;
+        return AKEY_STATE_UNKNOWN;
     }
 
     return gNativeInputManager->getInputManager()->getScanCodeState(
@@ -1862,7 +1884,7 @@
 static jint android_server_InputManager_nativeGetKeyCodeState(JNIEnv* env, jclass clazz,
         jint deviceId, jint deviceClasses, jint keyCode) {
     if (checkInputManagerUnitialized(env)) {
-        return KEY_STATE_UNKNOWN;
+        return AKEY_STATE_UNKNOWN;
     }
 
     return gNativeInputManager->getInputManager()->getKeyCodeState(
@@ -1872,7 +1894,7 @@
 static jint android_server_InputManager_nativeGetSwitchState(JNIEnv* env, jclass clazz,
         jint deviceId, jint deviceClasses, jint sw) {
     if (checkInputManagerUnitialized(env)) {
-        return KEY_STATE_UNKNOWN;
+        return AKEY_STATE_UNKNOWN;
     }
 
     return gNativeInputManager->getInputManager()->getSwitchState(deviceId, deviceClasses, sw);
@@ -1963,28 +1985,28 @@
 }
 
 static jint android_server_InputManager_nativeInjectKeyEvent(JNIEnv* env, jclass clazz,
-        jobject keyEventObj, jint nature, jint injectorPid, jint injectorUid,
+        jobject keyEventObj, jint injectorPid, jint injectorUid,
         jboolean sync, jint timeoutMillis) {
     if (checkInputManagerUnitialized(env)) {
         return INPUT_EVENT_INJECTION_FAILED;
     }
 
     KeyEvent keyEvent;
-    android_view_KeyEvent_toNative(env, keyEventObj, nature, & keyEvent);
+    android_view_KeyEvent_toNative(env, keyEventObj, & keyEvent);
 
     return gNativeInputManager->getInputManager()->injectInputEvent(& keyEvent,
             injectorPid, injectorUid, sync, timeoutMillis);
 }
 
 static jint android_server_InputManager_nativeInjectMotionEvent(JNIEnv* env, jclass clazz,
-        jobject motionEventObj, jint nature, jint injectorPid, jint injectorUid,
+        jobject motionEventObj, jint injectorPid, jint injectorUid,
         jboolean sync, jint timeoutMillis) {
     if (checkInputManagerUnitialized(env)) {
         return INPUT_EVENT_INJECTION_FAILED;
     }
 
     MotionEvent motionEvent;
-    android_view_MotionEvent_toNative(env, motionEventObj, nature, & motionEvent);
+    android_view_MotionEvent_toNative(env, motionEventObj, & motionEvent);
 
     return gNativeInputManager->getInputManager()->injectInputEvent(& motionEvent,
             injectorPid, injectorUid, sync, timeoutMillis);
@@ -2050,9 +2072,9 @@
             (void*) android_server_InputManager_nativeRegisterInputChannel },
     { "nativeUnregisterInputChannel", "(Landroid/view/InputChannel;)V",
             (void*) android_server_InputManager_nativeUnregisterInputChannel },
-    { "nativeInjectKeyEvent", "(Landroid/view/KeyEvent;IIIZI)I",
+    { "nativeInjectKeyEvent", "(Landroid/view/KeyEvent;IIZI)I",
             (void*) android_server_InputManager_nativeInjectKeyEvent },
-    { "nativeInjectMotionEvent", "(Landroid/view/MotionEvent;IIIZI)I",
+    { "nativeInjectMotionEvent", "(Landroid/view/MotionEvent;IIZI)I",
             (void*) android_server_InputManager_nativeInjectMotionEvent },
     { "nativeSetInputWindows", "([Lcom/android/server/InputWindow;)V",
             (void*) android_server_InputManager_nativeSetInputWindows },