Merge "Fix potential exception while pressing volume up and volume down"
diff --git a/services/core/java/com/android/server/policy/KeyCombinationManager.java b/services/core/java/com/android/server/policy/KeyCombinationManager.java
index 268de3e..68e078c 100644
--- a/services/core/java/com/android/server/policy/KeyCombinationManager.java
+++ b/services/core/java/com/android/server/policy/KeyCombinationManager.java
@@ -17,11 +17,13 @@
 
 import static android.view.KeyEvent.KEYCODE_POWER;
 
+import android.os.Handler;
 import android.os.SystemClock;
 import android.util.Log;
 import android.util.SparseLongArray;
 import android.view.KeyEvent;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.ToBooleanFunction;
 
 import java.io.PrintWriter;
@@ -35,13 +37,18 @@
     private static final String TAG = "KeyCombinationManager";
 
     // Store the received down time of keycode.
+    @GuardedBy("mLock")
     private final SparseLongArray mDownTimes = new SparseLongArray(2);
     private final ArrayList<TwoKeysCombinationRule> mRules = new ArrayList();
 
     // Selected rules according to current key down.
+    private final Object mLock = new Object();
+    @GuardedBy("mLock")
     private final ArrayList<TwoKeysCombinationRule> mActiveRules = new ArrayList();
     // The rule has been triggered by current keys.
+    @GuardedBy("mLock")
     private TwoKeysCombinationRule mTriggeredRule;
+    private final Handler mHandler = new Handler();
 
     // Keys in a key combination must be pressed within this interval of each other.
     private static final long COMBINE_KEY_DELAY_MILLIS = 150;
@@ -109,6 +116,12 @@
      * Return true if any active rule could be triggered by the key event, otherwise false.
      */
     boolean interceptKey(KeyEvent event, boolean interactive) {
+        synchronized (mLock) {
+            return interceptKeyLocked(event, interactive);
+        }
+    }
+
+    private boolean interceptKeyLocked(KeyEvent event, boolean interactive) {
         final boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
         final int keyCode = event.getKeyCode();
         final int count = mActiveRules.size();
@@ -154,7 +167,7 @@
                         return false;
                     }
                     Log.v(TAG, "Performing combination rule : " + rule);
-                    rule.execute();
+                    mHandler.post(rule::execute);
                     mTriggeredRule = rule;
                     return true;
                 });
@@ -169,7 +182,7 @@
             for (int index = count - 1; index >= 0; index--) {
                 final TwoKeysCombinationRule rule = mActiveRules.get(index);
                 if (rule.shouldInterceptKey(keyCode)) {
-                    rule.cancel();
+                    mHandler.post(rule::cancel);
                     mActiveRules.remove(index);
                 }
             }
@@ -181,31 +194,37 @@
      * Return the interceptTimeout to tell InputDispatcher when is ready to deliver to window.
      */
     long getKeyInterceptTimeout(int keyCode) {
-        if (forAllActiveRules((rule) -> rule.shouldInterceptKey(keyCode))) {
-            return mDownTimes.get(keyCode) + COMBINE_KEY_DELAY_MILLIS;
+        synchronized (mLock) {
+            if (forAllActiveRules((rule) -> rule.shouldInterceptKey(keyCode))) {
+                return mDownTimes.get(keyCode) + COMBINE_KEY_DELAY_MILLIS;
+            }
+            return 0;
         }
-        return 0;
     }
 
     /**
      * True if the key event had been handled.
      */
     boolean isKeyConsumed(KeyEvent event) {
-        if ((event.getFlags() & KeyEvent.FLAG_FALLBACK) != 0) {
-            return false;
+        synchronized (mLock) {
+            if ((event.getFlags() & KeyEvent.FLAG_FALLBACK) != 0) {
+                return false;
+            }
+            return mTriggeredRule != null && mTriggeredRule.shouldInterceptKey(event.getKeyCode());
         }
-        return mTriggeredRule != null && mTriggeredRule.shouldInterceptKey(event.getKeyCode());
     }
 
     /**
      * True if power key is the candidate.
      */
     boolean isPowerKeyIntercepted() {
-        if (forAllActiveRules((rule) -> rule.shouldInterceptKey(KEYCODE_POWER))) {
-            // return false if only if power key pressed.
-            return mDownTimes.size() > 1 || mDownTimes.get(KEYCODE_POWER) == 0;
+        synchronized (mLock) {
+            if (forAllActiveRules((rule) -> rule.shouldInterceptKey(KEYCODE_POWER))) {
+                // return false if only if power key pressed.
+                return mDownTimes.size() > 1 || mDownTimes.get(KEYCODE_POWER) == 0;
+            }
+            return false;
         }
-        return false;
     }
 
     /**