From c3b80dc70b0da3af545301fdddfdd4f59cf02664 Mon Sep 17 00:00:00 2001 From: Kenneth Ford Date: Sat, 15 Apr 2023 00:28:46 +0000 Subject: [PATCH] Fixes concurrent display test flakiness Registers a DisplayManager listener if the rear display is not yet available when we get into the concurrent display state. This could occur if we are notified that we are now in the concurrent display state while DisplayManager is still in the process of configuring itself for this state. Fixes: 267563768 Test: ExtensionRearDisplayPresentationTest Change-Id: Ice4c111491f4096195518dfc38bee2f07d2c183f --- .../RearDisplayPresentationController.java | 36 ++---- ...earDisplayPresentationRequestCallback.java | 115 ++++++++++++++++++ .../area/WindowAreaComponentImpl.java | 6 +- 3 files changed, 127 insertions(+), 30 deletions(-) create mode 100644 libs/WindowManager/Jetpack/src/androidx/window/extensions/area/RearDisplayPresentationRequestCallback.java diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/RearDisplayPresentationController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/RearDisplayPresentationController.java index 141a6ad487719..0e9119a4332be 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/RearDisplayPresentationController.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/RearDisplayPresentationController.java @@ -20,9 +20,6 @@ import static androidx.window.extensions.area.WindowAreaComponent.SESSION_STATE_ import static androidx.window.extensions.area.WindowAreaComponent.SESSION_STATE_INACTIVE; import android.content.Context; -import android.hardware.devicestate.DeviceStateRequest; -import android.hardware.display.DisplayManager; -import android.util.Log; import android.view.Display; import androidx.annotation.NonNull; @@ -32,17 +29,12 @@ import androidx.window.extensions.core.util.function.Consumer; import java.util.Objects; /** - * Controller class that keeps track of the status of the device state request - * to enable the rear display presentation feature. This controller notifies the session callback - * when the state request is active, and notifies the callback when the request is canceled. - * - * Clients are notified via {@link Consumer} provided with - * {@link androidx.window.extensions.area.WindowAreaComponent.WindowAreaStatus} values to signify - * when the request becomes active and cancelled. + * Controller class that manages the creation of the {@link android.app.Presentation} object used + * to show content on the rear facing display. This controller notifies the session callback with + * {@link androidx.window.extensions.area.WindowAreaComponent.WindowAreaStatus} values when the + * feature is active, or when the feature has been ended. */ -class RearDisplayPresentationController implements DeviceStateRequest.Callback { - - private static final String TAG = "RearDisplayPresentationController"; +class RearDisplayPresentationController { // Original context that requested to enable rear display presentation mode @NonNull @@ -51,8 +43,6 @@ class RearDisplayPresentationController implements DeviceStateRequest.Callback { private final Consumer<@WindowAreaComponent.WindowAreaSessionState Integer> mStateConsumer; @Nullable private ExtensionWindowAreaPresentation mExtensionWindowAreaPresentation; - @NonNull - private final DisplayManager mDisplayManager; /** * Creates the RearDisplayPresentationController @@ -71,25 +61,15 @@ class RearDisplayPresentationController implements DeviceStateRequest.Callback { mContext = context; mStateConsumer = stateConsumer; - mDisplayManager = context.getSystemService(DisplayManager.class); } - @Override - public void onRequestActivated(@NonNull DeviceStateRequest request) { - Display[] rearDisplays = mDisplayManager.getDisplays(DisplayManager.DISPLAY_CATEGORY_REAR); - if (rearDisplays.length == 0) { - mStateConsumer.accept(SESSION_STATE_INACTIVE); - Log.e(TAG, "Rear display list should not be empty"); - return; - } - + public void startSession(@NonNull Display rearDisplay) { mExtensionWindowAreaPresentation = - new RearDisplayPresentation(mContext, rearDisplays[0], mStateConsumer); + new RearDisplayPresentation(mContext, rearDisplay, mStateConsumer); mStateConsumer.accept(SESSION_STATE_ACTIVE); } - @Override - public void onRequestCanceled(@NonNull DeviceStateRequest request) { + public void endSession() { mStateConsumer.accept(SESSION_STATE_INACTIVE); } diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/RearDisplayPresentationRequestCallback.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/RearDisplayPresentationRequestCallback.java new file mode 100644 index 0000000000000..ea33426d0bdf1 --- /dev/null +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/RearDisplayPresentationRequestCallback.java @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2023 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 androidx.window.extensions.area; + +import android.content.Context; +import android.hardware.devicestate.DeviceStateRequest; +import android.hardware.display.DisplayManager; +import android.view.Display; + +import androidx.annotation.NonNull; + +/** + * Callback class to be notified of updates to a {@link DeviceStateRequest} for the rear display + * presentation state. This class notifies the {@link RearDisplayPresentationController} when the + * device is ready to enable the rear display presentation feature. + */ +public class RearDisplayPresentationRequestCallback implements DeviceStateRequest.Callback { + + private static final String TAG = RearDisplayPresentationRequestCallback.class.getSimpleName(); + + @NonNull + private final DisplayManager mDisplayManager; + @NonNull + private final DisplayManager.DisplayListener mRearDisplayListener = new RearDisplayListener(); + @NonNull + private final RearDisplayPresentationController mRearDisplayPresentationController; + private boolean mWaitingForRearDisplay = false; + + public RearDisplayPresentationRequestCallback(@NonNull Context context, + @NonNull RearDisplayPresentationController rearDisplayPresentationController) { + mDisplayManager = context.getSystemService(DisplayManager.class); + mDisplayManager.registerDisplayListener(mRearDisplayListener, + context.getMainThreadHandler(), DisplayManager.EVENT_FLAG_DISPLAY_ADDED + | DisplayManager.EVENT_FLAG_DISPLAY_CHANGED); + + mRearDisplayPresentationController = rearDisplayPresentationController; + } + + @Override + public void onRequestActivated(@NonNull DeviceStateRequest request) { + Display[] rearDisplays = mDisplayManager.getDisplays(DisplayManager.DISPLAY_CATEGORY_REAR); + if (rearDisplays.length == 0) { + // No rear facing display found, marking waiting for display flag as true. + mWaitingForRearDisplay = true; + return; + } + mDisplayManager.unregisterDisplayListener(mRearDisplayListener); + mRearDisplayPresentationController.startSession(rearDisplays[0]); + } + + @Override + public void onRequestCanceled(@NonNull DeviceStateRequest request) { + mDisplayManager.unregisterDisplayListener(mRearDisplayListener); + mRearDisplayPresentationController.endSession(); + } + + /** + * {@link DisplayManager.DisplayListener} to be used if a rear facing {@link Display} isn't + * available synchronously when the device is entered into the rear display presentation state. + * A rear facing {@link Display} is a {@link Display} that is categorized as + * {@link DisplayManager#DISPLAY_CATEGORY_REAR}. This can occur if {@link DisplayManager} is + * still in the process of configuring itself for this state when + * {@link DeviceStateRequest.Callback#onRequestActivated} is called. + * + * The {@link DisplayManager.DisplayListener} removes itself when a rear facing display is + * found. + */ + private class RearDisplayListener implements DisplayManager.DisplayListener { + @Override + public void onDisplayAdded(int displayId) { + Display display = mDisplayManager.getDisplay(displayId); + if (mWaitingForRearDisplay && (display.getFlags() & Display.FLAG_REAR) != 0) { + startRearDisplayPresentation(display); + } + } + + @Override + public void onDisplayRemoved(int displayId) {} + + @Override + public void onDisplayChanged(int displayId) { + Display display = mDisplayManager.getDisplay(displayId); + if (mWaitingForRearDisplay && (display.getFlags() & Display.FLAG_REAR) != 0) { + startRearDisplayPresentation(display); + } + } + + /** + * Starts a new {@link RearDisplayPresentation} with the updated {@link Display} with a + * category of {@link DisplayManager#DISPLAY_CATEGORY_REAR}. + */ + private void startRearDisplayPresentation(Display rearDisplay) { + // We have been notified of a change to a rear display, we can unregister the + // callback and stop waiting for a display + mDisplayManager.unregisterDisplayListener(this); + mWaitingForRearDisplay = false; + + mRearDisplayPresentationController.startSession(rearDisplay); + } + } +} 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 b374ae35f57f8..658d92cc74896 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java @@ -337,13 +337,15 @@ public class WindowAreaComponentImpl implements WindowAreaComponent, consumer.accept(stateStatus); } }); - + RearDisplayPresentationRequestCallback deviceStateCallback = + new RearDisplayPresentationRequestCallback(activity, + mRearDisplayPresentationController); DeviceStateRequest concurrentDisplayStateRequest = DeviceStateRequest.newBuilder( mConcurrentDisplayState).build(); mDeviceStateManager.requestState( concurrentDisplayStateRequest, mExecutor, - mRearDisplayPresentationController + deviceStateCallback ); } }