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/InputManager.java b/services/java/com/android/server/InputManager.java
index 5c2048b..718d4fc 100644
--- a/services/java/com/android/server/InputManager.java
+++ b/services/java/com/android/server/InputManager.java
@@ -423,7 +423,7 @@
         
         @SuppressWarnings("unused")
         public void notifyConfigurationChanged(long whenNanos) {
-            mWindowManagerService.sendNewConfiguration();
+            mWindowManagerService.mInputMonitor.notifyConfigurationChanged();
         }
         
         @SuppressWarnings("unused")
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 95534e3..6f4b4c5 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -481,14 +481,11 @@
         // we are in safe mode.
         final boolean safeMode = wm.detectSafeMode();
         if (safeMode) {
-            try {
-                ActivityManagerNative.getDefault().enterSafeMode();
-                // Post the safe mode state in the Zygote class
-                Zygote.systemInSafeMode = true;
-                // Disable the JIT for the system_server process
-                VMRuntime.getRuntime().disableJitCompilation();
-            } catch (RemoteException e) {
-            }
+            ActivityManagerService.self().enterSafeMode();
+            // Post the safe mode state in the Zygote class
+            Zygote.systemInSafeMode = true;
+            // Disable the JIT for the system_server process
+            VMRuntime.getRuntime().disableJitCompilation();
         } else {
             // Enable the JIT for the system_server process
             VMRuntime.getRuntime().startJitCompilation();
@@ -506,6 +503,10 @@
 
         wm.systemReady();
 
+        if (safeMode) {
+            ActivityManagerService.self().showSafeModeOverlay();
+        }
+
         // Update the configuration for this context by hand, because we're going
         // to start using it before the config change done in wm.systemReady() will
         // propagate to it.
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;
     }
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 761dcd1..c7c3fb2 100755
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -6074,23 +6074,25 @@
                     AppGlobals.getPackageManager().enterSafeMode();
                 } catch (RemoteException e) {
                 }
-
-                View v = LayoutInflater.from(mContext).inflate(
-                        com.android.internal.R.layout.safe_mode, null);
-                WindowManager.LayoutParams lp = new WindowManager.LayoutParams();
-                lp.type = WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY;
-                lp.width = WindowManager.LayoutParams.WRAP_CONTENT;
-                lp.height = WindowManager.LayoutParams.WRAP_CONTENT;
-                lp.gravity = Gravity.BOTTOM | Gravity.LEFT;
-                lp.format = v.getBackground().getOpacity();
-                lp.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
-                        | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
-                ((WindowManager)mContext.getSystemService(
-                        Context.WINDOW_SERVICE)).addView(v, lp);
             }
         }
     }
 
+    public final void showSafeModeOverlay() {
+        View v = LayoutInflater.from(mContext).inflate(
+                com.android.internal.R.layout.safe_mode, null);
+        WindowManager.LayoutParams lp = new WindowManager.LayoutParams();
+        lp.type = WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY;
+        lp.width = WindowManager.LayoutParams.WRAP_CONTENT;
+        lp.height = WindowManager.LayoutParams.WRAP_CONTENT;
+        lp.gravity = Gravity.BOTTOM | Gravity.LEFT;
+        lp.format = v.getBackground().getOpacity();
+        lp.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+                | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
+        ((WindowManager)mContext.getSystemService(
+                Context.WINDOW_SERVICE)).addView(v, lp);
+    }
+
     public void noteWakeupAlarm(IIntentSender sender) {
         if (!(sender instanceof PendingIntentRecord)) {
             return;