Merge "Caps Lock toggle with Meta + Alt (2/2)" into nyc-dev
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index fbac58c..47440de 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -16,9 +16,7 @@
 
 package android.hardware.input;
 
-import com.android.internal.inputmethod.InputMethodSubtypeHandle;
 import com.android.internal.os.SomeArgs;
-import com.android.internal.util.ArrayUtils;
 
 import android.annotation.IntDef;
 import android.annotation.SdkConstant;
diff --git a/core/java/android/hardware/input/InputManagerInternal.java b/core/java/android/hardware/input/InputManagerInternal.java
index 014e73f..10fc8e6 100644
--- a/core/java/android/hardware/input/InputManagerInternal.java
+++ b/core/java/android/hardware/input/InputManagerInternal.java
@@ -52,4 +52,11 @@
      */
     public abstract void onInputMethodSubtypeChanged(int userId,
             @Nullable InputMethodInfo inputMethodInfo, @Nullable InputMethodSubtype subtype);
+
+    /**
+     * Toggles Caps Lock state for input device with specific id.
+     *
+     * @param deviceId The id of input device.
+     */
+    public abstract void toggleCapsLock(int deviceId);
 }
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index e0c6770..636384c 100644
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -1870,6 +1870,11 @@
         return keyCode == KeyEvent.KEYCODE_META_LEFT || keyCode == KeyEvent.KEYCODE_META_RIGHT;
     }
 
+    /** @hide */
+    public static final boolean isAltKey(int keyCode) {
+        return keyCode == KeyEvent.KEYCODE_ALT_LEFT || keyCode == KeyEvent.KEYCODE_ALT_RIGHT;
+    }
+
     /** {@inheritDoc} */
     @Override
     public final int getDeviceId() {
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index e73beaa..c7c765bb 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -200,6 +200,7 @@
     private static native int nativeInjectInputEvent(long ptr, InputEvent event, int displayId,
             int injectorPid, int injectorUid, int syncMode, int timeoutMillis,
             int policyFlags);
+    private static native void nativeToggleCapsLock(long ptr, int deviceId);
     private static native void nativeSetInputWindows(long ptr, InputWindowHandle[] windowHandles);
     private static native void nativeSetInputDispatchMode(long ptr, boolean enabled, boolean frozen);
     private static native void nativeSetSystemUiVisibility(long ptr, int visibility);
@@ -2279,5 +2280,10 @@
             mHandler.obtainMessage(MSG_INPUT_METHOD_SUBTYPE_CHANGED, userId, 0, someArgs)
                     .sendToTarget();
         }
+
+        @Override
+        public void toggleCapsLock(int deviceId) {
+            nativeToggleCapsLock(mPtr, deviceId);
+        }
     }
 }
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 97c0cf1..fb56a0c 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -69,6 +69,7 @@
 import android.hardware.hdmi.HdmiControlManager;
 import android.hardware.hdmi.HdmiPlaybackClient;
 import android.hardware.hdmi.HdmiPlaybackClient.OneTouchPlayCallback;
+import android.hardware.input.InputManagerInternal;
 import android.media.AudioAttributes;
 import android.media.AudioManager;
 import android.media.AudioSystem;
@@ -305,6 +306,7 @@
     WindowManagerInternal mWindowManagerInternal;
     PowerManager mPowerManager;
     ActivityManagerInternal mActivityManagerInternal;
+    InputManagerInternal mInputManagerInternal;
     DreamManagerInternal mDreamManagerInternal;
     PowerManagerInternal mPowerManagerInternal;
     IStatusBarService mStatusBarService;
@@ -604,6 +606,9 @@
     boolean mConsumeSearchKeyUp;
     boolean mAssistKeyLongPressed;
     boolean mPendingMetaAction;
+    boolean mPendingCapsLockToggle;
+    int mMetaState;
+    int mInitialMetaState;
     boolean mForceShowSystemBars;
 
     // support for activating the lock screen while the screen is on
@@ -1494,6 +1499,7 @@
         mWindowManagerFuncs = windowManagerFuncs;
         mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
         mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
+        mInputManagerInternal = LocalServices.getService(InputManagerInternal.class);
         mDreamManagerInternal = LocalServices.getService(DreamManagerInternal.class);
         mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
         mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
@@ -2968,6 +2974,10 @@
         if (mPendingMetaAction && !KeyEvent.isMetaKey(keyCode)) {
             mPendingMetaAction = false;
         }
+        // Any key that is not Alt or Meta cancels Caps Lock combo tracking.
+        if (mPendingCapsLockToggle && !KeyEvent.isMetaKey(keyCode) && !KeyEvent.isAltKey(keyCode)) {
+            mPendingCapsLockToggle = false;
+        }
 
         // First we always handle the home key here, so applications
         // can never break it, although if keyguard is on, we do let
@@ -3216,6 +3226,38 @@
             }
         }
 
+        // Toggle Caps Lock on META-ALT.
+        boolean actionTriggered = false;
+        if (KeyEvent.isModifierKey(keyCode)) {
+            if (!mPendingCapsLockToggle) {
+                // Start tracking meta state for combo.
+                mInitialMetaState = mMetaState;
+                mPendingCapsLockToggle = true;
+            } else if (event.getAction() == KeyEvent.ACTION_UP) {
+                int altOnMask = mMetaState & KeyEvent.META_ALT_MASK;
+                int metaOnMask = mMetaState & KeyEvent.META_META_MASK;
+
+                // Check for Caps Lock toggle
+                if ((metaOnMask != 0) && (altOnMask != 0)) {
+                    // Check if nothing else is pressed
+                    if (mInitialMetaState == (mMetaState ^ (altOnMask | metaOnMask))) {
+                        // Handle Caps Lock Toggle
+                        mInputManagerInternal.toggleCapsLock(event.getDeviceId());
+                        actionTriggered = true;
+                    }
+                }
+
+                // Always stop tracking when key goes up.
+                mPendingCapsLockToggle = false;
+            }
+        }
+        // Store current meta state to be able to evaluate it later.
+        mMetaState = metaState;
+
+        if (actionTriggered) {
+            return -1;
+        }
+
         if (KeyEvent.isMetaKey(keyCode)) {
             if (down) {
                 mPendingMetaAction = true;
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index a5237ca..cd485c5 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -1312,6 +1312,12 @@
     }
 }
 
+static void nativeToggleCapsLock(JNIEnv* env, jclass /* clazz */,
+         jlong ptr, jint deviceId) {
+    NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
+    im->getInputManager()->getReader()->toggleCapsLockState(deviceId);
+}
+
 static void nativeSetInputWindows(JNIEnv* env, jclass /* clazz */,
         jlong ptr, jobjectArray windowHandleObjArray) {
     NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
@@ -1508,6 +1514,8 @@
             (void*) nativeSetInputFilterEnabled },
     { "nativeInjectInputEvent", "(JLandroid/view/InputEvent;IIIIII)I",
             (void*) nativeInjectInputEvent },
+    { "nativeToggleCapsLock", "(JI)V",
+            (void*) nativeToggleCapsLock },
     { "nativeSetInputWindows", "(J[Lcom/android/server/input/InputWindowHandle;)V",
             (void*) nativeSetInputWindows },
     { "nativeSetFocusedApplication", "(JLcom/android/server/input/InputApplicationHandle;)V",