Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c3b80dc7 authored by Kenneth Ford's avatar Kenneth Ford
Browse files

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
parent e8cf1f7e
Loading
Loading
Loading
Loading
+8 −28
Original line number Diff line number Diff line
@@ -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);
    }

+115 −0
Original line number Diff line number Diff line
/*
 * 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);
        }
    }
}
+4 −2
Original line number Diff line number Diff line
@@ -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
            );
        }
    }