Merge "Exit persistent VR mode on edge swipe."
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index d250827..0d6cd80 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -236,6 +236,7 @@
 import com.android.server.statusbar.StatusBarManagerInternal;
 import com.android.server.wm.AppTransition;
 import com.android.server.vr.VrManagerInternal;
+import com.android.server.vr.PersistentVrStateListener;
 
 import java.io.File;
 import java.io.FileReader;
@@ -416,6 +417,9 @@
     AppOpsManager mAppOpsManager;
     private boolean mHasFeatureWatch;
 
+    // Assigned on main thread, accessed on UI thread
+    volatile VrManagerInternal mVrManagerInternal;
+
     // Vibrator pattern for haptic feedback of a long press.
     long[] mLongPressVibePattern;
 
@@ -503,6 +507,8 @@
     volatile boolean mGoingToSleep;
     volatile boolean mRecentsVisible;
     volatile boolean mTvPictureInPictureVisible;
+    // Written by vr manager thread, only read in this class
+    volatile boolean mPersistentVrModeEnabled;
 
     // Used to hold the last user key used to wake the device.  This helps us prevent up events
     // from being passed to the foregrounded app without a corresponding down event
@@ -982,6 +988,14 @@
     }
     MyOrientationListener mOrientationListener;
 
+    final PersistentVrStateListener mPersistentVrModeListener =
+            new PersistentVrStateListener() {
+        @Override
+        public void onPersistentVrStateChanged(boolean enabled) {
+            mPersistentVrModeEnabled = enabled;
+        }
+    };
+
     private final StatusBarController mStatusBarController = new StatusBarController();
 
     private final BarController mNavigationBarController = new BarController("NavigationBar",
@@ -1914,24 +1928,36 @@
                         if (mStatusBar != null) {
                             requestTransientBars(mStatusBar);
                         }
+                        if (mPersistentVrModeEnabled) {
+                            exitPersistentVrMode();
+                        }
                     }
                     @Override
                     public void onSwipeFromBottom() {
                         if (mNavigationBar != null && mNavigationBarPosition == NAV_BAR_BOTTOM) {
                             requestTransientBars(mNavigationBar);
                         }
+                        if (mPersistentVrModeEnabled) {
+                            exitPersistentVrMode();
+                        }
                     }
                     @Override
                     public void onSwipeFromRight() {
                         if (mNavigationBar != null && mNavigationBarPosition == NAV_BAR_RIGHT) {
                             requestTransientBars(mNavigationBar);
                         }
+                        if (mPersistentVrModeEnabled) {
+                            exitPersistentVrMode();
+                        }
                     }
                     @Override
                     public void onSwipeFromLeft() {
                         if (mNavigationBar != null && mNavigationBarPosition == NAV_BAR_LEFT) {
                             requestTransientBars(mNavigationBar);
                         }
+                        if (mPersistentVrModeEnabled) {
+                            exitPersistentVrMode();
+                        }
                     }
                     @Override
                     public void onFling(int duration) {
@@ -6489,11 +6515,17 @@
     }
 
     private void reportScreenStateToVrManager(boolean isScreenOn) {
-        VrManagerInternal vrService = LocalServices.getService(VrManagerInternal.class);
-        if (vrService == null) {
+        if (mVrManagerInternal == null) {
             return;
         }
-        vrService.onScreenStateChanged(isScreenOn);
+        mVrManagerInternal.onScreenStateChanged(isScreenOn);
+    }
+
+    private void exitPersistentVrMode() {
+        if (mVrManagerInternal == null) {
+            return;
+        }
+        mVrManagerInternal.setPersistentVrModeEnabled(false);
     }
 
     private void finishWindowsDrawn() {
@@ -6982,6 +7014,11 @@
                 });
         mKeyguardDelegate.onSystemReady();
 
+        mVrManagerInternal = LocalServices.getService(VrManagerInternal.class);
+        if (mVrManagerInternal != null) {
+            mVrManagerInternal.addPersistentVrModeStateListener(mPersistentVrModeListener);
+        }
+
         readCameraLensCoverState();
         updateUiMode();
         boolean bindKeyguardNow;
diff --git a/services/core/java/com/android/server/vr/PersistentVrStateListener.java b/services/core/java/com/android/server/vr/PersistentVrStateListener.java
new file mode 100644
index 0000000..bccd5f1
--- /dev/null
+++ b/services/core/java/com/android/server/vr/PersistentVrStateListener.java
@@ -0,0 +1,31 @@
+/**
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.vr;
+
+/**
+ * Listener for state changes to persistent VR mode.
+ *
+ * @hide Only for use within system server.
+ */
+public abstract class PersistentVrStateListener {
+
+  /**
+   * Called when the Persistent VR mode state changes.
+   *
+   * @param enabled {@code true} if persistent VR mode is enabled.
+   */
+    public abstract void onPersistentVrStateChanged(boolean enabled);
+}
diff --git a/services/core/java/com/android/server/vr/VrManagerInternal.java b/services/core/java/com/android/server/vr/VrManagerInternal.java
index 45b7baf..58e4bdc 100644
--- a/services/core/java/com/android/server/vr/VrManagerInternal.java
+++ b/services/core/java/com/android/server/vr/VrManagerInternal.java
@@ -88,4 +88,9 @@
      * @param enabled true if the device should be placed in persistent VR mode.
      */
     public abstract void setPersistentVrModeEnabled(boolean enabled);
+
+    /**
+     * Adds listener that reports state changes to persistent VR mode.
+     */
+    public abstract void addPersistentVrModeStateListener(PersistentVrStateListener listener);
 }
diff --git a/services/core/java/com/android/server/vr/VrManagerService.java b/services/core/java/com/android/server/vr/VrManagerService.java
index f0ea527..21a4f74 100644
--- a/services/core/java/com/android/server/vr/VrManagerService.java
+++ b/services/core/java/com/android/server/vr/VrManagerService.java
@@ -122,6 +122,8 @@
     private boolean mGuard;
     private final RemoteCallbackList<IVrStateCallbacks> mRemoteCallbacks =
             new RemoteCallbackList<>();
+    private final ArrayList<PersistentVrStateListener> mPersistentVrStateListeners =
+            new ArrayList<>();
     private int mPreviousCoarseLocationMode = INVALID_APPOPS_MODE;
     private int mPreviousManageOverlayMode = INVALID_APPOPS_MODE;
     private VrState mPendingState;
@@ -132,6 +134,7 @@
 
     private static final int MSG_VR_STATE_CHANGE = 0;
     private static final int MSG_PENDING_VR_STATE_CHANGE = 1;
+    private static final int MSG_PERSISTENT_VR_MODE_STATE_CHANGE = 2;
 
     /**
      * Set whether VR mode may be enabled.
@@ -151,7 +154,7 @@
             } else {
                 // Disable persistent mode when VR mode isn't allowed, allows an escape hatch to
                 // exit persistent VR mode when screen is turned off.
-                mPersistentVrModeEnabled = false;
+                setPersistentModeAndNotifyListenersLocked(false);
 
                 // Set pending state to current state.
                 mPendingState = (mVrModeEnabled && mCurrentVrService != null)
@@ -213,6 +216,13 @@
                         }
                     }
                 } break;
+                case MSG_PERSISTENT_VR_MODE_STATE_CHANGE : {
+                    boolean state = (msg.arg1 == 1);
+                    for (int i = 0; i < mPersistentVrStateListeners.size(); i++) {
+                        mPersistentVrStateListeners.get(i).onPersistentVrStateChanged(
+                                state);
+                    }
+                } break;
                 default :
                     throw new IllegalStateException("Unknown message type: " + msg.what);
             }
@@ -424,6 +434,16 @@
                     pw.println(n.flattenToString());
                 }
             }
+            pw.println("Attached persistent mode listeners:");
+            if (mPersistentVrStateListeners == null ||
+                    mPersistentVrStateListeners.size() == 0) {
+                pw.println("None");
+            } else {
+                for (PersistentVrStateListener l : mPersistentVrStateListeners) {
+                    pw.print(tab);
+                    pw.println("listener: " + l);
+                }
+            }
             pw.println("\n");
             pw.println("********* End of VrManagerService Dump *********");
         }
@@ -471,6 +491,11 @@
         public void setPersistentVrModeEnabled(boolean enabled) {
             VrManagerService.this.setPersistentVrModeEnabled(enabled);
         }
+
+        @Override
+        public void addPersistentVrModeStateListener(PersistentVrStateListener listener) {
+            VrManagerService.this.addPersistentVrModeStateListener(listener);
+        }
     }
 
     public VrManagerService(Context context) {
@@ -1013,9 +1038,8 @@
     }
 
     private void setPersistentVrModeEnabled(boolean enabled) {
-        synchronized (mLock) {
-            mPersistentVrModeEnabled = enabled;
-
+        synchronized(mLock) {
+            setPersistentModeAndNotifyListenersLocked(enabled);
             // Disabling persistent mode when not showing a VR should disable the overall vr mode.
             if (!enabled && mCurrentVrModeComponent == null) {
                 setVrMode(false, null, 0, null);
@@ -1023,6 +1047,22 @@
         }
     }
 
+    private void setPersistentModeAndNotifyListenersLocked(boolean enabled) {
+        if (mPersistentVrModeEnabled == enabled) {
+            return;
+        }
+        mPersistentVrModeEnabled = enabled;
+
+        mHandler.sendMessage(mHandler.obtainMessage(MSG_PERSISTENT_VR_MODE_STATE_CHANGE,
+                (mPersistentVrModeEnabled) ? 1 : 0, 0));
+    }
+
+    private void addPersistentVrModeStateListener(PersistentVrStateListener listener) {
+        synchronized (mLock) {
+            mPersistentVrStateListeners.add(listener);
+        }
+    }
+
     private int hasVrPackage(@NonNull ComponentName targetPackageName, int userId) {
         synchronized (mLock) {
             return mComponentObserver.isValid(targetPackageName, userId);