Add volume down as a safe mode key for Stingray.

Fixed a race condition during startup.  We need to wait for the input
devices to actually be ready before trying to detect safe mode.

Fixed a problem during safe mode activation where we would try to add
the overlay window but the display was not initialized.  Now we do it
after the system is ready.

Bug: 3134825
Change-Id: I4c043c142ae6bf0d865c79d266d36154eaf00709
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index 6ce6d3bf..3c6802f 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -212,6 +212,10 @@
     // Maximum number of milliseconds to wait for input event injection.
     // FIXME is this value reasonable?
     private static final int INJECTION_TIMEOUT_MILLIS = 30 * 1000;
+
+    // Maximum number of milliseconds to wait for input devices to be enumerated before
+    // proceding with safe mode detection.
+    private static final int INPUT_DEVICES_READY_FOR_SAFE_MODE_DETECTION_TIMEOUT_MILLIS = 1000;
     
     // Default input dispatching timeout in nanoseconds.
     private static final long DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS = 5000 * 1000000L;
@@ -5782,6 +5786,11 @@
         // Temporary input application object to provide to the input dispatcher.
         private InputApplication mTempInputApplication = new InputApplication();
         
+        // Set to true when the first input device configuration change notification
+        // is received to indicate that the input devices are ready.
+        private final Object mInputDevicesReadyMonitor = new Object();
+        private boolean mInputDevicesReady;
+
         /* Notifies the window manager about a broken input channel.
          * 
          * Called by the InputManager.
@@ -5977,7 +5986,32 @@
             // Also avoids keeping InputChannel objects referenced unnecessarily.
             mTempInputWindows.clear();
         }
-        
+
+        /* Notifies that the input device configuration has changed. */
+        public void notifyConfigurationChanged() {
+            sendNewConfiguration();
+
+            synchronized (mInputDevicesReadyMonitor) {
+                if (!mInputDevicesReady) {
+                    mInputDevicesReady = true;
+                    mInputDevicesReadyMonitor.notifyAll();
+                }
+            }
+        }
+
+        /* Waits until the built-in input devices have been configured. */
+        public boolean waitForInputDevicesReady(long timeoutMillis) {
+            synchronized (mInputDevicesReadyMonitor) {
+                if (!mInputDevicesReady) {
+                    try {
+                        mInputDevicesReadyMonitor.wait(timeoutMillis);
+                    } catch (InterruptedException ex) {
+                    }
+                }
+                return mInputDevicesReady;
+            }
+        }
+
         /* Notifies that the lid switch changed state. */
         public void notifyLidSwitchChanged(long whenNanos, boolean lidOpen) {
             mPolicy.notifyLidSwitchChanged(whenNanos, lidOpen);
@@ -6299,6 +6333,13 @@
     }
 
     public boolean detectSafeMode() {
+        if (!mInputMonitor.waitForInputDevicesReady(
+                INPUT_DEVICES_READY_FOR_SAFE_MODE_DETECTION_TIMEOUT_MILLIS)) {
+            Slog.w(TAG, "Devices still not ready after waiting "
+                    + INPUT_DEVICES_READY_FOR_SAFE_MODE_DETECTION_TIMEOUT_MILLIS
+                    + " milliseconds before attempting to detect safe mode.");
+        }
+
         mSafeMode = mPolicy.detectSafeMode();
         return mSafeMode;
     }