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

Commit 81a7cf11 authored by Robin Lee's avatar Robin Lee
Browse files

Add transition assertions for keyguard/aod API

Bug: 364930619
Bug: 396134691
Bug: 361438779
Test: atest 'DisplayContentTests#testKeyguardGoingAwayWhileAodShown'
Test: atest 'DisplayContentTests#testKeyguardGoingAwayCanceledWhileAodShown'
Flag: com.android.window.flags.ensure_keyguard_does_transition_starting
Flag: com.android.window.flags.aod_transition
Change-Id: I2f57ca5d10e49783bfd3568c0b99103dc47704df
parent 793ae050
Loading
Loading
Loading
Loading
+9 −1
Original line number Diff line number Diff line
@@ -624,6 +624,12 @@ public interface WindowManager extends ViewManager {
     */
    int TRANSIT_FLAG_PHYSICAL_DISPLAY_SWITCH = (1 << 14); // 0x4000

    /**
     * Transition flag: Indicates that aod is showing hidden by entering doze
     * @hide
     */
    int TRANSIT_FLAG_AOD_APPEARING = (1 << 15); // 0x8000

    /**
     * @hide
     */
@@ -643,6 +649,7 @@ public interface WindowManager extends ViewManager {
            TRANSIT_FLAG_KEYGUARD_OCCLUDING,
            TRANSIT_FLAG_KEYGUARD_UNOCCLUDING,
            TRANSIT_FLAG_PHYSICAL_DISPLAY_SWITCH,
            TRANSIT_FLAG_AOD_APPEARING,
    })
    @Retention(RetentionPolicy.SOURCE)
    @interface TransitionFlags {}
@@ -659,7 +666,8 @@ public interface WindowManager extends ViewManager {
            (TRANSIT_FLAG_KEYGUARD_GOING_AWAY
            | TRANSIT_FLAG_KEYGUARD_APPEARING
            | TRANSIT_FLAG_KEYGUARD_OCCLUDING
            | TRANSIT_FLAG_KEYGUARD_UNOCCLUDING);
            | TRANSIT_FLAG_KEYGUARD_UNOCCLUDING
            | TRANSIT_FLAG_AOD_APPEARING);

    /**
     * Remove content mode: Indicates remove content mode is currently not defined.
+79 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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.wm;

import android.annotation.Nullable;

import com.google.common.truth.FailureMetadata;
import com.google.common.truth.IterableSubject;
import com.google.common.truth.Subject;
import com.google.common.truth.Truth;

import java.util.ArrayList;
import java.util.List;

public class TransitionSubject extends Subject {

    @Nullable
    private final Transition actual;

    /**
     * Internal constructor.
     *
     * @see TransitionSubject#assertThat(Transition)
     */
    private TransitionSubject(FailureMetadata metadata, @Nullable Transition actual) {
        super(metadata, actual);
        this.actual = actual;
    }

    /**
     * In a fluent assertion chain, the argument to the "custom" overload of {@link
     * StandardSubjectBuilder#about(CustomSubjectBuilder.Factory) about}, the method that specifies
     * what kind of {@link Subject} to create.
     */
    public static Factory<TransitionSubject, Transition> transitions() {
        return TransitionSubject::new;
    }

    /**
     * Typical entry point for making assertions about Transitions.
     *
     * @see @Truth#assertThat(Object)
     */
    public static TransitionSubject assertThat(Transition transition) {
        return Truth.assertAbout(transitions()).that(transition);
    }

    /**
     * Converts to a {@link IterableSubject} containing {@link Transition#getFlags()} separated into
     * a list of individual flags for assertions such as {@code flags().contains(TRANSIT_FLAG_XYZ)}.
     *
     * <p>If the subject is null, this will fail instead of returning a null subject.
     */
    public IterableSubject flags() {
        isNotNull();

        final List<Integer> sortedFlags = new ArrayList<>();
        for (int i = 0; i < 32; i++) {
            if ((actual.getFlags() & (1 << i)) != 0) {
                sortedFlags.add((1 << i));
            }
        }
        return com.google.common.truth.Truth.assertThat(sortedFlags);
    }
}
+48 −0
Original line number Diff line number Diff line
@@ -63,6 +63,9 @@ import static android.view.WindowManager.LayoutParams.TYPE_SCREENSHOT;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import static android.view.WindowManager.TRANSIT_FLAG_AOD_APPEARING;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_APPEARING;
import static android.window.DisplayAreaOrganizer.FEATURE_WINDOWED_MAGNIFICATION;

import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
@@ -80,6 +83,7 @@ import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_TOKEN_TRANSFO
import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
import static com.android.server.wm.TransitionSubject.assertThat;
import static com.android.window.flags.Flags.FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING;
import static com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE;
import static com.android.server.display.feature.flags.Flags.FLAG_ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT;
@@ -146,6 +150,7 @@ import com.android.internal.logging.nano.MetricsProto;
import com.android.server.LocalServices;
import com.android.server.policy.WindowManagerPolicy;
import com.android.server.wm.utils.WmDisplayCutout;
import com.android.window.flags.Flags;

import org.junit.Test;
import org.junit.runner.RunWith;
@@ -2619,6 +2624,7 @@ public class DisplayContentTests extends WindowTestsBase {
        final KeyguardController keyguard = mAtm.mKeyguardController;
        final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build();
        final int displayId = mDisplayContent.getDisplayId();
        final TestTransitionPlayer transitions = registerTestTransitionPlayer();

        final BooleanSupplier keyguardShowing = () -> keyguard.isKeyguardShowing(displayId);
        final BooleanSupplier keyguardGoingAway = () -> keyguard.isKeyguardGoingAway(displayId);
@@ -2628,21 +2634,40 @@ public class DisplayContentTests extends WindowTestsBase {
        keyguard.setKeyguardShown(displayId, true /* keyguard */, true /* aod */);
        assertFalse(keyguardGoingAway.getAsBoolean());
        assertFalse(appVisible.getAsBoolean());
        transitions.flush();

        // Start unlocking from AOD.
        keyguard.keyguardGoingAway(displayId, 0x0 /* flags */);
        assertTrue(keyguardGoingAway.getAsBoolean());
        assertTrue(appVisible.getAsBoolean());

        if (Flags.ensureKeyguardDoesTransitionStarting()) {
            assertThat(transitions.mLastTransit).isNull();
        } else {
            assertThat(transitions.mLastTransit).flags()
                    .containsExactly(TRANSIT_FLAG_KEYGUARD_GOING_AWAY);
        }
        transitions.flush();

        // Clear AOD. This does *not* clear the going-away status.
        keyguard.setKeyguardShown(displayId, true /* keyguard */, false /* aod */);
        assertTrue(keyguardGoingAway.getAsBoolean());
        assertTrue(appVisible.getAsBoolean());

        if (Flags.aodTransition()) {
            assertThat(transitions.mLastTransit).flags()
                    .containsExactly(TRANSIT_FLAG_AOD_APPEARING);
        } else {
            assertThat(transitions.mLastTransit).isNull();
        }
        transitions.flush();

        // Finish unlock
        keyguard.setKeyguardShown(displayId, false /* keyguard */, false /* aod */);
        assertFalse(keyguardGoingAway.getAsBoolean());
        assertTrue(appVisible.getAsBoolean());

        assertThat(transitions.mLastTransit).isNull();
    }

    @Test
@@ -2652,6 +2677,7 @@ public class DisplayContentTests extends WindowTestsBase {
        final KeyguardController keyguard = mAtm.mKeyguardController;
        final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build();
        final int displayId = mDisplayContent.getDisplayId();
        final TestTransitionPlayer transitions = registerTestTransitionPlayer();

        final BooleanSupplier keyguardShowing = () -> keyguard.isKeyguardShowing(displayId);
        final BooleanSupplier keyguardGoingAway = () -> keyguard.isKeyguardGoingAway(displayId);
@@ -2661,22 +2687,44 @@ public class DisplayContentTests extends WindowTestsBase {
        keyguard.setKeyguardShown(displayId, true /* keyguard */, true /* aod */);
        assertFalse(keyguardGoingAway.getAsBoolean());
        assertFalse(appVisible.getAsBoolean());
        transitions.flush();

        // Start unlocking from AOD.
        keyguard.keyguardGoingAway(displayId, 0x0 /* flags */);
        assertTrue(keyguardGoingAway.getAsBoolean());
        assertTrue(appVisible.getAsBoolean());

        if (!Flags.ensureKeyguardDoesTransitionStarting()) {
            assertThat(transitions.mLastTransit).flags()
                    .containsExactly(TRANSIT_FLAG_KEYGUARD_GOING_AWAY);
        }
        transitions.flush();

        // Clear AOD. This does *not* clear the going-away status.
        keyguard.setKeyguardShown(displayId, true /* keyguard */, false /* aod */);
        assertTrue(keyguardGoingAway.getAsBoolean());
        assertTrue(appVisible.getAsBoolean());

        if (Flags.aodTransition()) {
            assertThat(transitions.mLastTransit).flags()
                    .containsExactly(TRANSIT_FLAG_AOD_APPEARING);
        } else {
            assertThat(transitions.mLastTransit).isNull();
        }
        transitions.flush();

        // Same API call a second time cancels the unlock, because AOD isn't changing.
        keyguard.setKeyguardShown(displayId, true /* keyguard */, false /* aod */);
        assertTrue(keyguardShowing.getAsBoolean());
        assertFalse(keyguardGoingAway.getAsBoolean());
        assertFalse(appVisible.getAsBoolean());

        if (Flags.ensureKeyguardDoesTransitionStarting()) {
            assertThat(transitions.mLastTransit).isNull();
        } else {
            assertThat(transitions.mLastTransit).flags()
                    .containsExactly(TRANSIT_FLAG_KEYGUARD_APPEARING);
        }
    }

    @Test
+8 −0
Original line number Diff line number Diff line
@@ -2140,6 +2140,14 @@ public class WindowTestsBase extends SystemServiceTestsBase {
            mLastRequest = null;
        }

        void flush() {
            if (mLastTransit != null) {
                start();
                finish();
                clear();
            }
        }

        @Override
        public void onTransitionReady(IBinder transitToken, TransitionInfo transitionInfo,
                SurfaceControl.Transaction transaction, SurfaceControl.Transaction finishT)