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

Commit e18cccba authored by Caitlin Shkuratov's avatar Caitlin Shkuratov
Browse files

[Status Bar] Disable the keyguard status bar when we get the

DISABLE_SYSTEM_INFO or DISABLE2_SYSTEM_ICONS flags.

Fixes: 240590557
Test: manual: CTS Verifier tests > Device Owner Tests > LockTasks UI >
Enable keyguard. Turn screen off then turn screen on and verify the
status bar in keyguard doesn't display.
Test: manual: Verify keyguard status bar typically displays when not in
the CTS verifier test case
Test: DisableStateTrackerTest
Test: KeyguardStatusBarViewControllerTest

Change-Id: I4d19a168c295abee1ddfdf61452343dafa54cfc2
parent 0b136ce1
Loading
Loading
Loading
Loading
+72 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 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.systemui.statusbar.disableflags

import com.android.systemui.statusbar.CommandQueue

/**
 * Tracks the relevant DISABLE_* flags provided in [mask1] and [mask2] and sets [isDisabled] based
 * on those masks. [callback] will be notified whenever [isDisabled] changes.
 *
 * Users are responsible for adding and removing this tracker from [CommandQueue] callbacks.
 */
class DisableStateTracker(
    private val mask1: Int,
    private val mask2: Int,
    private val callback: Callback,
) : CommandQueue.Callbacks {
    /**
     * True if any of the bits in [mask1] or [mask2] are on for the current disable flags, and false
     * otherwise.
     */
    var isDisabled = false
        private set(value) {
            if (field == value) return
            field = value
            callback.onDisabledChanged()
        }

    private var displayId: Int? = null

    /** Start tracking the disable flags and updating [isDisabled] accordingly. */
    fun startTracking(commandQueue: CommandQueue, displayId: Int) {
        // A view will only have its displayId once it's attached to a window, so we can only
        // provide the displayId when we start tracking.
        this.displayId = displayId
        commandQueue.addCallback(this)
    }

    /**
     * Stop tracking the disable flags.
     *
     * [isDisabled] will stay at the same value until we start tracking again.
     */
    fun stopTracking(commandQueue: CommandQueue) {
        this.displayId = null
        commandQueue.removeCallback(this)
    }

    override fun disable(displayId: Int, state1: Int, state2: Int, animate: Boolean) {
        if (this.displayId == null || displayId != this.displayId) {
            return
        }
        isDisabled = state1 and mask1 != 0 || state2 and mask2 != 0
    }

    /** Callback triggered whenever the value of [isDisabled] changes. */
    fun interface Callback {
        fun onDisabledChanged()
    }
}
+31 −1
Original line number Diff line number Diff line
@@ -16,6 +16,9 @@

package com.android.systemui.statusbar.phone;

import static android.app.StatusBarManager.DISABLE2_SYSTEM_ICONS;
import static android.app.StatusBarManager.DISABLE_SYSTEM_INFO;

import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;

import android.animation.Animator;
@@ -43,8 +46,10 @@ import com.android.systemui.battery.BatteryMeterViewController;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shade.NotificationPanelViewController;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.disableflags.DisableStateTracker;
import com.android.systemui.statusbar.events.SystemStatusAnimationCallback;
import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
import com.android.systemui.statusbar.notification.AnimatableProperty;
@@ -108,6 +113,7 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat
    private final StatusBarUserSwitcherController mUserSwitcherController;
    private final StatusBarUserInfoTracker mStatusBarUserInfoTracker;
    private final SecureSettings mSecureSettings;
    private final CommandQueue mCommandQueue;
    private final Executor mMainExecutor;
    private final Object mLock = new Object();

@@ -218,6 +224,9 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat
                }
            };


    private final DisableStateTracker mDisableStateTracker;

    private final List<String> mBlockedIcons = new ArrayList<>();
    private final int mNotificationsHeaderCollideDistance;

@@ -269,6 +278,7 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat
            StatusBarUserSwitcherController userSwitcherController,
            StatusBarUserInfoTracker statusBarUserInfoTracker,
            SecureSettings secureSettings,
            CommandQueue commandQueue,
            @Main Executor mainExecutor
    ) {
        super(view);
@@ -292,6 +302,7 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat
        mUserSwitcherController = userSwitcherController;
        mStatusBarUserInfoTracker = statusBarUserInfoTracker;
        mSecureSettings = secureSettings;
        mCommandQueue = commandQueue;
        mMainExecutor = mainExecutor;

        mFirstBypassAttempt = mKeyguardBypassController.getBypassEnabled();
@@ -316,6 +327,12 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat
                !mFeatureController.isStatusBarUserSwitcherFeatureEnabled());
        mFeatureController.addCallback(enabled -> mView.setKeyguardUserAvatarEnabled(!enabled));
        mSystemEventAnimator = new StatusBarSystemEventAnimator(mView, r);

        mDisableStateTracker = new DisableStateTracker(
                /* mask1= */ DISABLE_SYSTEM_INFO,
                /* mask2= */ DISABLE2_SYSTEM_ICONS,
                this::updateViewState
        );
    }

    @Override
@@ -333,6 +350,7 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat
        mUserInfoController.addCallback(mOnUserInfoChangedListener);
        mStatusBarStateController.addCallback(mStatusBarStateListener);
        mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback);
        mDisableStateTracker.startTracking(mCommandQueue, mView.getDisplay().getDisplayId());
        if (mTintedIconManager == null) {
            mTintedIconManager =
                    mTintedIconManagerFactory.create(mView.findViewById(R.id.statusIcons));
@@ -357,6 +375,7 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat
        mUserInfoController.removeCallback(mOnUserInfoChangedListener);
        mStatusBarStateController.removeCallback(mStatusBarStateListener);
        mKeyguardUpdateMonitor.removeCallback(mKeyguardUpdateMonitorCallback);
        mDisableStateTracker.stopTracking(mCommandQueue);
        mSecureSettings.unregisterContentObserver(mVolumeSettingObserver);
        if (mTintedIconManager != null) {
            mStatusBarIconController.removeIconGroup(mTintedIconManager);
@@ -411,6 +430,10 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat

    /** Animate the keyguard status bar in. */
    public void animateKeyguardStatusBarIn() {
        if (mDisableStateTracker.isDisabled()) {
            // If our view is disabled, don't allow us to animate in.
            return;
        }
        mView.setVisibility(View.VISIBLE);
        mView.setAlpha(0f);
        ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
@@ -463,7 +486,11 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat
        boolean hideForBypass =
                mFirstBypassAttempt && mKeyguardUpdateMonitor.shouldListenForFace()
                        || mDelayShowingKeyguardStatusBar;
        int newVisibility = newAlpha != 0f && !mDozing && !hideForBypass
        int newVisibility =
                newAlpha != 0f
                        && !mDozing
                        && !hideForBypass
                        && !mDisableStateTracker.isDisabled()
                ? View.VISIBLE : View.INVISIBLE;

        updateViewState(newAlpha, newVisibility);
@@ -473,6 +500,9 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat
     * Updates the {@link KeyguardStatusBarView} state based on the provided values.
     */
    public void updateViewState(float alpha, int visibility) {
        if (mDisableStateTracker.isDisabled()) {
            visibility = View.INVISIBLE;
        }
        mView.setAlpha(alpha);
        mView.setVisibility(visibility);
    }
+213 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 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.systemui.statusbar.disableflags

import android.app.StatusBarManager.DISABLE2_GLOBAL_ACTIONS
import android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS
import android.app.StatusBarManager.DISABLE2_SYSTEM_ICONS
import android.app.StatusBarManager.DISABLE_CLOCK
import android.app.StatusBarManager.DISABLE_EXPAND
import android.app.StatusBarManager.DISABLE_NAVIGATION
import android.app.StatusBarManager.DISABLE_NOTIFICATION_ICONS
import android.app.StatusBarManager.DISABLE_NOTIFICATION_TICKER
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.CommandQueue
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
import org.mockito.Mock
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations

@SmallTest
class DisableStateTrackerTest : SysuiTestCase() {

    private lateinit var underTest: DisableStateTracker

    @Mock private lateinit var commandQueue: CommandQueue

    @Before
    fun setUp() {
        MockitoAnnotations.initMocks(this)
    }

    @Test
    fun startTracking_commandQueueGetsCallback() {
        underTest = DisableStateTracker(0, 0) { }

        underTest.startTracking(commandQueue, displayId = 0)

        verify(commandQueue).addCallback(underTest)
    }

    @Test
    fun stopTracking_commandQueueLosesCallback() {
        underTest = DisableStateTracker(0, 0) { }

        underTest.stopTracking(commandQueue)

        verify(commandQueue).removeCallback(underTest)
    }

    @Test
    fun disable_hadNotStartedTracking_isDisabledFalse() {
        underTest = DisableStateTracker(DISABLE_CLOCK, 0) { }

        underTest.disable(displayId = 0, state1 = DISABLE_CLOCK, state2 = 0, animate = false)

        assertThat(underTest.isDisabled).isFalse()
    }

    @Test
    fun disable_wrongDisplayId_isDisabledFalse() {
        underTest = DisableStateTracker(DISABLE_CLOCK, 0) { }
        underTest.startTracking(commandQueue, displayId = 15)

        underTest.disable(displayId = 20, state1 = DISABLE_CLOCK, state2 = 0, animate = false)

        assertThat(underTest.isDisabled).isFalse()
    }

    @Test
    fun disable_irrelevantFlagsUpdated_isDisabledFalse() {
        underTest = DisableStateTracker(DISABLE_CLOCK, DISABLE2_GLOBAL_ACTIONS) { }
        underTest.startTracking(commandQueue, DISPLAY_ID)

        underTest.disable(
            DISPLAY_ID, state1 = DISABLE_EXPAND, state2 = DISABLE2_QUICK_SETTINGS, animate = false
        )

        assertThat(underTest.isDisabled).isFalse()
    }

    @Test
    fun disable_partOfMask1True_isDisabledTrue() {
        underTest = DisableStateTracker(
            mask1 = DISABLE_CLOCK or DISABLE_EXPAND or DISABLE_NAVIGATION,
            mask2 = DISABLE2_GLOBAL_ACTIONS
        ) { }
        underTest.startTracking(commandQueue, DISPLAY_ID)

        underTest.disable(DISPLAY_ID, state1 = DISABLE_EXPAND, state2 = 0, animate = false)

        assertThat(underTest.isDisabled).isTrue()
    }

    @Test
    fun disable_partOfMask2True_isDisabledTrue() {
        underTest = DisableStateTracker(
            mask1 = DISABLE_CLOCK,
            mask2 = DISABLE2_GLOBAL_ACTIONS or DISABLE2_SYSTEM_ICONS
        ) { }
        underTest.startTracking(commandQueue, DISPLAY_ID)

        underTest.disable(DISPLAY_ID, state1 = 0, state2 = DISABLE2_SYSTEM_ICONS, animate = false)

        assertThat(underTest.isDisabled).isTrue()
    }

    @Test
    fun disable_isDisabledChangesFromFalseToTrue_callbackNotified() {
        var callbackCalled = false

        underTest = DisableStateTracker(
            mask1 = DISABLE_CLOCK,
            mask2 = DISABLE2_GLOBAL_ACTIONS
        ) { callbackCalled = true }
        underTest.startTracking(commandQueue, DISPLAY_ID)

        underTest.disable(DISPLAY_ID, state1 = DISABLE_CLOCK, state2 = 0, animate = false)

        assertThat(callbackCalled).isTrue()
    }

    @Test
    fun disable_isDisabledChangesFromTrueToFalse_callbackNotified() {
        var callbackCalled: Boolean

        underTest = DisableStateTracker(
            mask1 = DISABLE_CLOCK,
            mask2 = DISABLE2_GLOBAL_ACTIONS
        ) { callbackCalled = true }
        underTest.startTracking(commandQueue, DISPLAY_ID)

        // First, update isDisabled to true
        underTest.disable(DISPLAY_ID, state1 = DISABLE_CLOCK, state2 = 0, animate = false)
        assertThat(underTest.isDisabled).isTrue()

        // WHEN isDisabled updates back to false
        callbackCalled = false
        underTest.disable(DISPLAY_ID, state1 = 0, state2 = 0, animate = false)

        // THEN the callback is called again
        assertThat(callbackCalled).isTrue()
    }

    @Test
    fun disable_manyUpdates_isDisabledUpdatesAppropriately() {
        underTest = DisableStateTracker(
            mask1 = DISABLE_CLOCK or DISABLE_EXPAND or DISABLE_NAVIGATION,
            mask2 = DISABLE2_GLOBAL_ACTIONS or DISABLE2_SYSTEM_ICONS
        ) { }
        underTest.startTracking(commandQueue, DISPLAY_ID)

        // All flags from mask1 -> isDisabled = true
        underTest.disable(
            DISPLAY_ID,
            state1 = DISABLE_CLOCK or DISABLE_EXPAND or DISABLE_NAVIGATION,
            state2 = 0,
            animate = false
        )
        assertThat(underTest.isDisabled).isTrue()

        // Irrelevant flags from mask1 -> isDisabled = false
        underTest.disable(
            DISPLAY_ID,
            state1 = DISABLE_NOTIFICATION_ICONS or DISABLE_NOTIFICATION_TICKER,
            state2 = 0,
            animate = false
        )
        assertThat(underTest.isDisabled).isFalse()

        // All flags from mask1 & all flags from mask2 -> isDisabled = true
        underTest.disable(
            DISPLAY_ID,
            state1 = DISABLE_CLOCK or DISABLE_EXPAND or DISABLE_NAVIGATION,
            state2 = DISABLE2_GLOBAL_ACTIONS or DISABLE2_SYSTEM_ICONS,
            animate = false
        )
        assertThat(underTest.isDisabled).isTrue()

        // No flags -> isDisabled = false
        underTest.disable(DISPLAY_ID, state1 = 0, state2 = 0, animate = false)
        assertThat(underTest.isDisabled).isFalse()

        // 1 flag from mask1 & 1 flag from mask2 -> isDisabled = true
        underTest.disable(
            DISPLAY_ID,
            state1 = DISABLE_NAVIGATION,
            state2 = DISABLE2_SYSTEM_ICONS,
            animate = false
        )
        assertThat(underTest.isDisabled).isTrue()
    }

    companion object {
        private const val DISPLAY_ID = 3
    }
}
+96 −0
Original line number Diff line number Diff line
@@ -17,6 +17,9 @@
package com.android.systemui.statusbar.phone;


import static android.app.StatusBarManager.DISABLE2_SYSTEM_ICONS;
import static android.app.StatusBarManager.DISABLE_SYSTEM_INFO;

import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
import static com.android.systemui.statusbar.StatusBarState.SHADE;

@@ -49,6 +52,7 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.battery.BatteryMeterViewController;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shade.NotificationPanelViewController;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserInfoTracker;
@@ -118,6 +122,7 @@ public class KeyguardStatusBarViewControllerTest extends SysuiTestCase {
    @Mock
    private StatusBarUserInfoTracker mStatusBarUserInfoTracker;
    @Mock private SecureSettings mSecureSettings;
    @Mock private CommandQueue mCommandQueue;

    private TestNotificationPanelViewStateProvider mNotificationPanelViewStateProvider;
    private KeyguardStatusBarView mKeyguardStatusBarView;
@@ -137,6 +142,7 @@ public class KeyguardStatusBarViewControllerTest extends SysuiTestCase {
            mKeyguardStatusBarView =
                    spy((KeyguardStatusBarView) LayoutInflater.from(mContext)
                            .inflate(R.layout.keyguard_status_bar, null));
            when(mKeyguardStatusBarView.getDisplay()).thenReturn(mContext.getDisplay());
        });

        mController = createController();
@@ -165,6 +171,7 @@ public class KeyguardStatusBarViewControllerTest extends SysuiTestCase {
                mStatusBarUserSwitcherController,
                mStatusBarUserInfoTracker,
                mSecureSettings,
                mCommandQueue,
                mFakeExecutor
        );
    }
@@ -176,6 +183,7 @@ public class KeyguardStatusBarViewControllerTest extends SysuiTestCase {
        verify(mConfigurationController).addCallback(any());
        verify(mAnimationScheduler).addCallback(any());
        verify(mUserInfoController).addCallback(any());
        verify(mCommandQueue).addCallback(any());
        verify(mStatusBarIconController).addIconGroup(any());
        verify(mUserManager).isUserSwitcherEnabled(anyBoolean());
    }
@@ -214,6 +222,7 @@ public class KeyguardStatusBarViewControllerTest extends SysuiTestCase {
        verify(mConfigurationController).removeCallback(any());
        verify(mAnimationScheduler).removeCallback(any());
        verify(mUserInfoController).removeCallback(any());
        verify(mCommandQueue).removeCallback(any());
        verify(mStatusBarIconController).removeIconGroup(any());
    }

@@ -279,6 +288,17 @@ public class KeyguardStatusBarViewControllerTest extends SysuiTestCase {
        assertThat(mKeyguardStatusBarView.getVisibility()).isEqualTo(newVisibility);
    }

    @Test
    public void updateViewState_paramVisibleButIsDisabled_viewIsInvisible() {
        mController.onViewAttached();
        setDisableSystemIcons(true);

        mController.updateViewState(1f, View.VISIBLE);

        // Since we're disabled, we stay invisible
        assertThat(mKeyguardStatusBarView.getVisibility()).isEqualTo(View.INVISIBLE);
    }

    @Test
    public void updateViewState_notKeyguardState_nothingUpdated() {
        mController.onViewAttached();
@@ -358,6 +378,50 @@ public class KeyguardStatusBarViewControllerTest extends SysuiTestCase {
        assertThat(mKeyguardStatusBarView.getVisibility()).isEqualTo(View.INVISIBLE);
    }

    @Test
    public void updateViewState_disableSystemInfoFalse_viewShown() {
        mController.onViewAttached();
        updateStateToKeyguard();
        setDisableSystemInfo(false);

        mController.updateViewState();

        assertThat(mKeyguardStatusBarView.getVisibility()).isEqualTo(View.VISIBLE);
    }

    @Test
    public void updateViewState_disableSystemInfoTrue_viewHidden() {
        mController.onViewAttached();
        updateStateToKeyguard();
        setDisableSystemInfo(true);

        mController.updateViewState();

        assertThat(mKeyguardStatusBarView.getVisibility()).isEqualTo(View.INVISIBLE);
    }

    @Test
    public void updateViewState_disableSystemIconsFalse_viewShown() {
        mController.onViewAttached();
        updateStateToKeyguard();
        setDisableSystemIcons(false);

        mController.updateViewState();

        assertThat(mKeyguardStatusBarView.getVisibility()).isEqualTo(View.VISIBLE);
    }

    @Test
    public void updateViewState_disableSystemIconsTrue_viewHidden() {
        mController.onViewAttached();
        updateStateToKeyguard();
        setDisableSystemIcons(true);

        mController.updateViewState();

        assertThat(mKeyguardStatusBarView.getVisibility()).isEqualTo(View.INVISIBLE);
    }

    @Test
    public void setAlpha_explicitAlpha_setsExplicitAlpha() {
        mController.onViewAttached();
@@ -485,6 +549,19 @@ public class KeyguardStatusBarViewControllerTest extends SysuiTestCase {
        callback.onStateChanged(state);
    }

    @Test
    public void animateKeyguardStatusBarIn_isDisabled_viewStillHidden() {
        mController.onViewAttached();
        updateStateToKeyguard();
        setDisableSystemInfo(true);
        assertThat(mKeyguardStatusBarView.getVisibility()).isEqualTo(View.INVISIBLE);

        mController.animateKeyguardStatusBarIn();

        // Since we're disabled, we don't actually animate in and stay invisible
        assertThat(mKeyguardStatusBarView.getVisibility()).isEqualTo(View.INVISIBLE);
    }

    /**
     * Calls {@link com.android.keyguard.KeyguardUpdateMonitorCallback#onFinishedGoingToSleep(int)}
     * to ensure values are updated properly.
@@ -498,6 +575,25 @@ public class KeyguardStatusBarViewControllerTest extends SysuiTestCase {
        callback.onFinishedGoingToSleep(0);
    }

    private void setDisableSystemInfo(boolean disabled) {
        CommandQueue.Callbacks callback = getCommandQueueCallback();
        int disabled1 = disabled ? DISABLE_SYSTEM_INFO : 0;
        callback.disable(mContext.getDisplayId(), disabled1, 0, false);
    }

    private void setDisableSystemIcons(boolean disabled) {
        CommandQueue.Callbacks callback = getCommandQueueCallback();
        int disabled2 = disabled ? DISABLE2_SYSTEM_ICONS : 0;
        callback.disable(mContext.getDisplayId(), 0, disabled2, false);
    }

    private CommandQueue.Callbacks getCommandQueueCallback() {
        ArgumentCaptor<CommandQueue.Callbacks> captor =
                ArgumentCaptor.forClass(CommandQueue.Callbacks.class);
        verify(mCommandQueue).addCallback(captor.capture());
        return captor.getValue();
    }

    private static class TestNotificationPanelViewStateProvider
            implements NotificationPanelViewController.NotificationPanelViewStateProvider {