Adds support for enabling and disabling rear display mode.

This adds the functionality for enabling rear display mode in
WindowAreaComponentImpl.

Bug: 207686851
Test: ExtensionWindowAreaComponentTest
Change-Id: If8ac9d23e16e7aa5e12bdedbe4688226aff32d38
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java
index 76cd17c..3adae70 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java
@@ -21,6 +21,7 @@
 import android.app.Activity;
 import android.content.Context;
 import android.hardware.devicestate.DeviceStateManager;
+import android.hardware.devicestate.DeviceStateRequest;
 import android.util.ArraySet;
 
 import androidx.annotation.NonNull;
@@ -51,11 +52,15 @@
     @GuardedBy("mLock")
     private final ArraySet<Consumer<Integer>> mRearDisplayStatusListeners = new ArraySet<>();
     private final int mRearDisplayState;
+    @WindowAreaSessionState
+    private int mRearDisplaySessionStatus = WindowAreaComponent.SESSION_STATE_INACTIVE;
 
     @GuardedBy("mLock")
     private int mCurrentDeviceState = INVALID_DEVICE_STATE;
     @GuardedBy("mLock")
     private int mCurrentDeviceBaseState = INVALID_DEVICE_STATE;
+    @GuardedBy("mLock")
+    private DeviceStateRequest mDeviceStateRequest;
 
     public WindowAreaComponentImpl(@NonNull Context context) {
         mDeviceStateManager = context.getSystemService(DeviceStateManager.class);
@@ -77,8 +82,8 @@
      * Depending on the initial state of the device, we will return either
      * {@link WindowAreaComponent#STATUS_AVAILABLE} or
      * {@link WindowAreaComponent#STATUS_UNAVAILABLE} if the feature is supported or not in that
-     * state respectively. When the RearDisplay feature is triggered, we update the status to be
-     * {@link WindowAreaComponent#STATUS_UNAVAILABLE}.
+     * state respectively. When the rear display feature is triggered, we update the status to be
+     * {@link WindowAreaComponent#STATUS_UNAVAILABLE}. TODO(b/240727590) Prefix with AREA_
      *
      * TODO(b/239833099) Add a STATUS_ACTIVE option to let apps know if a feature is currently
      * enabled.
@@ -111,7 +116,7 @@
     }
 
     /**
-     * Creates and starts a RearDisplaySession and provides updates to the
+     * Creates and starts a rear display session and provides updates to the
      * callback provided. Because this is being called from the OEM provided
      * extensions, we will post the result of the listener on the executor
      * provided by the developer at the initial call site.
@@ -129,23 +134,46 @@
      */
     public void startRearDisplaySession(@NonNull Activity activity,
             @NonNull Consumer<@WindowAreaSessionState Integer> rearDisplaySessionCallback) {
-        // Todo("Adding in chained cl")
+        synchronized (mLock) {
+            if (mDeviceStateRequest != null) {
+                // Rear display session is already active
+                throw new IllegalStateException(
+                        "Unable to start new rear display session as one is already active");
+            }
+            mDeviceStateRequest = DeviceStateRequest.newBuilder(mRearDisplayState).build();
+            mDeviceStateManager.requestState(
+                    mDeviceStateRequest,
+                    mExecutor,
+                    new DeviceStateRequestCallbackAdapter(rearDisplaySessionCallback)
+            );
+        }
     }
 
     /**
-     * Ends the current RearDisplaySession and provides updates to the
+     * Ends the current rear display session and provides updates to the
      * callback provided. Because this is being called from the OEM provided
      * extensions, we will post the result of the listener on the executor
      * provided by the developer.
      */
     public void endRearDisplaySession() {
-        // Todo("Adding in chained cl")
+        synchronized (mLock) {
+            if (mDeviceStateRequest != null || isRearDisplayActive()) {
+                mDeviceStateRequest = null;
+                mDeviceStateManager.cancelStateRequest();
+            } else {
+                throw new IllegalStateException(
+                        "Unable to cancel a rear display session as there is no active session");
+            }
+        }
     }
 
     @Override
     public void onBaseStateChanged(int state) {
         synchronized (mLock) {
             mCurrentDeviceBaseState = state;
+            if (state == mCurrentDeviceState) {
+                updateStatusConsumers(getCurrentStatus());
+            }
         }
     }
 
@@ -159,7 +187,8 @@
 
     @GuardedBy("mLock")
     private int getCurrentStatus() {
-        if (isRearDisplayActive()) {
+        if (mRearDisplaySessionStatus == WindowAreaComponent.SESSION_STATE_ACTIVE
+                || isRearDisplayActive()) {
             return WindowAreaComponent.STATUS_UNAVAILABLE;
         }
         return WindowAreaComponent.STATUS_AVAILABLE;
@@ -186,4 +215,41 @@
             }
         }
     }
+
+    /**
+     * Callback for the {@link DeviceStateRequest} to be notified of when the request has been
+     * activated or cancelled. This callback provides information to the client library
+     * on the status of the RearDisplay session through {@code mRearDisplaySessionCallback}
+     */
+    private class DeviceStateRequestCallbackAdapter implements DeviceStateRequest.Callback {
+
+        private final Consumer<Integer> mRearDisplaySessionCallback;
+
+        DeviceStateRequestCallbackAdapter(@NonNull Consumer<Integer> callback) {
+            mRearDisplaySessionCallback = callback;
+        }
+
+        @Override
+        public void onRequestActivated(@NonNull DeviceStateRequest request) {
+            synchronized (mLock) {
+                if (request.equals(mDeviceStateRequest)) {
+                    mRearDisplaySessionStatus = WindowAreaComponent.SESSION_STATE_ACTIVE;
+                    mRearDisplaySessionCallback.accept(mRearDisplaySessionStatus);
+                    updateStatusConsumers(getCurrentStatus());
+                }
+            }
+        }
+
+        @Override
+        public void onRequestCanceled(DeviceStateRequest request) {
+            synchronized (mLock) {
+                if (request.equals(mDeviceStateRequest)) {
+                    mDeviceStateRequest = null;
+                }
+                mRearDisplaySessionStatus = WindowAreaComponent.SESSION_STATE_INACTIVE;
+                mRearDisplaySessionCallback.accept(mRearDisplaySessionStatus);
+                updateStatusConsumers(getCurrentStatus());
+            }
+        }
+    }
 }