Loading packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java +27 −1 Original line number Diff line number Diff line Loading @@ -116,6 +116,27 @@ public class QuickStepContract { public static final int SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE = 1 << 26; // Device dreaming state public static final int SYSUI_STATE_DEVICE_DREAMING = 1 << 27; // Whether the screen is currently on. Note that the screen is considered on while turning on, // but not while turning off. public static final int SYSUI_STATE_SCREEN_ON = 1 << 28; // Whether the screen is currently transitioning into the state indicated by // SYSUI_STATE_SCREEN_ON. public static final int SYSUI_STATE_SCREEN_TRANSITION = 1 << 29; // Mask for SystemUiStateFlags to isolate SYSUI_STATE_SCREEN_ON and // SYSUI_STATE_SCREEN_TRANSITION, to match SCREEN_STATE_* public static final int SYSUI_STATE_SCREEN_STATE_MASK = SYSUI_STATE_SCREEN_ON | SYSUI_STATE_SCREEN_TRANSITION; // Screen is off. public static final int SCREEN_STATE_OFF = 0; // Screen is on. public static final int SCREEN_STATE_ON = SYSUI_STATE_SCREEN_ON; // Screen is still on, but transitioning to turn off. public static final int SCREEN_STATE_TURNING_OFF = SYSUI_STATE_SCREEN_TRANSITION; // Screen was off and is now turning on. public static final int SCREEN_STATE_TURNING_ON = SYSUI_STATE_SCREEN_TRANSITION | SYSUI_STATE_SCREEN_ON; // Whether the back gesture is allowed (or ignored) by the Shade public static final boolean ALLOW_BACK_GESTURE_IN_SHADE = SystemProperties.getBoolean( "persist.wm.debug.shade_allow_back_gesture", false); Loading Loading @@ -148,7 +169,9 @@ public class QuickStepContract { SYSUI_STATE_IMMERSIVE_MODE, SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING, SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE, SYSUI_STATE_DEVICE_DREAMING SYSUI_STATE_DEVICE_DREAMING, SYSUI_STATE_SCREEN_ON, SYSUI_STATE_SCREEN_TRANSITION, }) public @interface SystemUiStateFlags {} Loading Loading @@ -187,6 +210,9 @@ public class QuickStepContract { str.add((flags & SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE) != 0 ? "freeform_active_in_desktop_mode" : ""); str.add((flags & SYSUI_STATE_DEVICE_DREAMING) != 0 ? "device_dreaming" : ""); str.add("screen_" + ((flags & SYSUI_STATE_SCREEN_TRANSITION) != 0 ? "turning_" : "") + ((flags & SYSUI_STATE_SCREEN_ON) != 0 ? "on" : "off")); return str.toString(); } Loading packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java +49 −5 Original line number Diff line number Diff line Loading @@ -31,6 +31,8 @@ import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_WIN import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BOUNCER_SHOWING; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_DEVICE_DOZING; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_DEVICE_DREAMING; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_ON; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_TRANSITION; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_TRACING_ENABLED; Loading Loading @@ -121,7 +123,8 @@ import dagger.Lazy; public class OverviewProxyService implements CallbackController<OverviewProxyListener>, NavigationModeController.ModeChangedListener, Dumpable { private static final String ACTION_QUICKSTEP = "android.intent.action.QUICKSTEP_SERVICE"; @VisibleForTesting static final String ACTION_QUICKSTEP = "android.intent.action.QUICKSTEP_SERVICE"; public static final String TAG_OPS = "OverviewProxyService"; private static final long BACKOFF_MILLIS = 1000; Loading Loading @@ -548,6 +551,7 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis mUiEventLogger = uiEventLogger; mDisplayTracker = displayTracker; mUnfoldTransitionProgressForwarder = unfoldTransitionProgressForwarder; mSysuiUnlockAnimationController = sysuiUnlockAnimationController; dumpManager.registerDumpable(getClass().getSimpleName(), this); Loading Loading @@ -596,7 +600,6 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis // Connect to the service updateEnabledState(); startConnectionToCurrentUser(); mSysuiUnlockAnimationController = sysuiUnlockAnimationController; // Listen for assistant changes assistUtils.registerVoiceInteractionSessionListener(mVoiceInteractionSessionListener); Loading Loading @@ -726,10 +729,8 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis return; } mHandler.removeCallbacks(mConnectionRunnable); Intent launcherServiceIntent = new Intent(ACTION_QUICKSTEP) .setPackage(mRecentsComponentName.getPackageName()); try { mBound = mContext.bindServiceAsUser(launcherServiceIntent, mBound = mContext.bindServiceAsUser(mQuickStepIntent, mOverviewServiceConnection, Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE, UserHandle.of(mUserTracker.getUserId())); Loading Loading @@ -862,6 +863,11 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis */ @Override public void onScreenTurnedOn() { mSysUiState .setFlag(SYSUI_STATE_SCREEN_ON, true) .setFlag(SYSUI_STATE_SCREEN_TRANSITION, false) .commitUpdate(mContext.getDisplayId()); try { if (mOverviewProxy != null) { mOverviewProxy.onScreenTurnedOn(); Loading @@ -873,11 +879,27 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis } } /** * Notifies the Launcher that screen turned off. */ @Override public void onScreenTurnedOff() { mSysUiState .setFlag(SYSUI_STATE_SCREEN_ON, false) .setFlag(SYSUI_STATE_SCREEN_TRANSITION, false) .commitUpdate(mContext.getDisplayId()); } /** * Notifies the Launcher that screen is starting to turn on. */ @Override public void onScreenTurningOff() { mSysUiState .setFlag(SYSUI_STATE_SCREEN_ON, false) .setFlag(SYSUI_STATE_SCREEN_TRANSITION, true) .commitUpdate(mContext.getDisplayId()); try { if (mOverviewProxy != null) { mOverviewProxy.onScreenTurningOff(); Loading @@ -894,6 +916,11 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis */ @Override public void onScreenTurningOn() { mSysUiState .setFlag(SYSUI_STATE_SCREEN_ON, true) .setFlag(SYSUI_STATE_SCREEN_TRANSITION, true) .commitUpdate(mContext.getDisplayId()); try { if (mOverviewProxy != null) { mOverviewProxy.onScreenTurningOn(); Loading Loading @@ -1005,4 +1032,21 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis default void onAssistantGestureCompletion(float velocity) {} default void startAssistant(Bundle bundle) {} } /** * Shuts down this service at the end of a testcase. * <p> * The in-production service is never shuts down, and it was not designed with testing in mind. * This unregisters the mechanisms by which the service will be revived after a testcase. * <p> * NOTE: This is a stop-gap introduced when first added some tests to this class. It should * probably be replaced by proper lifecycle management on this class. */ @VisibleForTesting() void shutdownForTest() { mContext.unregisterReceiver(mLauncherStateChangedReceiver); mIsEnabled = false; mHandler.removeCallbacks(mConnectionRunnable); disconnectFromLauncherService(); } } packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt 0 → 100644 +186 −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 com.android.systemui.recents import android.content.ComponentName import android.content.pm.PackageManager import android.content.pm.ResolveInfo import android.testing.AndroidTestingRunner import android.testing.TestableContext import android.testing.TestableLooper import androidx.test.filters.SmallTest import com.android.internal.app.AssistUtils import com.android.internal.logging.UiEventLogger import com.android.systemui.SysuiTestCase import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.dump.DumpManager import com.android.systemui.keyguard.KeyguardUnlockAnimationController import com.android.systemui.keyguard.ScreenLifecycle import com.android.systemui.model.SysUiState import com.android.systemui.navigationbar.NavigationBarController import com.android.systemui.navigationbar.NavigationModeController import com.android.systemui.recents.OverviewProxyService.ACTION_QUICKSTEP import com.android.systemui.settings.FakeDisplayTracker import com.android.systemui.settings.UserTracker import com.android.systemui.shared.recents.IOverviewProxy import com.android.systemui.shared.system.QuickStepContract.SCREEN_STATE_OFF import com.android.systemui.shared.system.QuickStepContract.SCREEN_STATE_ON import com.android.systemui.shared.system.QuickStepContract.SCREEN_STATE_TURNING_OFF import com.android.systemui.shared.system.QuickStepContract.SCREEN_STATE_TURNING_ON import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_STATE_MASK import com.android.systemui.statusbar.CommandQueue import com.android.systemui.statusbar.NotificationShadeWindowController import com.android.systemui.statusbar.phone.CentralSurfaces import com.android.systemui.unfold.progress.UnfoldTransitionProgressForwarder import com.android.systemui.util.mockito.whenever import com.android.wm.shell.sysui.ShellInterface import com.google.common.util.concurrent.MoreExecutors import dagger.Lazy import java.util.Optional import java.util.concurrent.Executor import org.junit.After import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentMatchers import org.mockito.Mock import org.mockito.Mockito.any import org.mockito.Mockito.anyInt import org.mockito.Mockito.intThat import org.mockito.Mockito.mock import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @SmallTest @RunWith(AndroidTestingRunner::class) @TestableLooper.RunWithLooper(setAsMainLooper = true) class OverviewProxyServiceTest : SysuiTestCase() { @Main private val executor: Executor = MoreExecutors.directExecutor() private lateinit var subject: OverviewProxyService private val dumpManager = DumpManager() private val displayTracker = FakeDisplayTracker(mContext) private val sysUiState = SysUiState(displayTracker) private val screenLifecycle = ScreenLifecycle(dumpManager) @Mock private lateinit var overviewProxy: IOverviewProxy.Stub @Mock private lateinit var packageManager: PackageManager // The following mocks belong to not-yet-tested parts of OverviewProxyService. @Mock private lateinit var commandQueue: CommandQueue @Mock private lateinit var shellInterface: ShellInterface @Mock private lateinit var navBarController: NavigationBarController @Mock private lateinit var centralSurfaces: CentralSurfaces @Mock private lateinit var navModeController: NavigationModeController @Mock private lateinit var statusBarWinController: NotificationShadeWindowController @Mock private lateinit var userTracker: UserTracker @Mock private lateinit var uiEventLogger: UiEventLogger @Mock private lateinit var sysuiUnlockAnimationController: KeyguardUnlockAnimationController @Mock private lateinit var assistUtils: AssistUtils @Mock private lateinit var unfoldTransitionProgressForwarder: Optional<UnfoldTransitionProgressForwarder> @Before fun setUp() { MockitoAnnotations.initMocks(this) val serviceComponent = ComponentName("test_package", "service_provider") context.addMockService(serviceComponent, overviewProxy) context.addMockServiceResolver( TestableContext.MockServiceResolver { if (it.action == ACTION_QUICKSTEP) serviceComponent else null } ) whenever(overviewProxy.queryLocalInterface(ArgumentMatchers.anyString())) .thenReturn(overviewProxy) whenever(overviewProxy.asBinder()).thenReturn(overviewProxy) // packageManager.resolveServiceAsUser has to return non-null for // OverviewProxyService#isEnabled to become true. context.setMockPackageManager(packageManager) whenever(packageManager.resolveServiceAsUser(any(), anyInt(), anyInt())) .thenReturn(mock(ResolveInfo::class.java)) subject = OverviewProxyService( context, executor, commandQueue, shellInterface, Lazy { navBarController }, Lazy { Optional.of(centralSurfaces) }, navModeController, statusBarWinController, sysUiState, userTracker, screenLifecycle, uiEventLogger, displayTracker, sysuiUnlockAnimationController, assistUtils, dumpManager, unfoldTransitionProgressForwarder ) } @After fun tearDown() { subject.shutdownForTest() } @Test fun `ScreenLifecycle - screenTurnedOn triggers SysUI state flag changes `() { screenLifecycle.dispatchScreenTurnedOn() verify(overviewProxy) .onSystemUiStateChanged( intThat { it and SYSUI_STATE_SCREEN_STATE_MASK == SCREEN_STATE_ON } ) } @Test fun `ScreenLifecycle - screenTurningOn triggers SysUI state flag changes `() { screenLifecycle.dispatchScreenTurningOn() verify(overviewProxy) .onSystemUiStateChanged( intThat { it and SYSUI_STATE_SCREEN_STATE_MASK == SCREEN_STATE_TURNING_ON } ) } @Test fun `ScreenLifecycle - screenTurnedOff triggers SysUI state flag changes `() { screenLifecycle.dispatchScreenTurnedOff() verify(overviewProxy) .onSystemUiStateChanged( intThat { it and SYSUI_STATE_SCREEN_STATE_MASK == SCREEN_STATE_OFF } ) } @Test fun `ScreenLifecycle - screenTurningOff triggers SysUI state flag changes `() { screenLifecycle.dispatchScreenTurningOff() verify(overviewProxy) .onSystemUiStateChanged( intThat { it and SYSUI_STATE_SCREEN_STATE_MASK == SCREEN_STATE_TURNING_OFF } ) } } tests/testables/src/android/testing/TestableContext.java +67 −15 Original line number Diff line number Diff line Loading @@ -33,11 +33,15 @@ import android.provider.Settings; import android.util.ArrayMap; import android.view.LayoutInflater; import androidx.annotation.Nullable; import org.junit.rules.TestRule; import org.junit.rules.TestWatcher; import org.junit.runner.Description; import org.junit.runners.model.Statement; import java.util.ArrayList; /** * A ContextWrapper with utilities specifically designed to make Testing easier. * Loading @@ -61,6 +65,7 @@ public class TestableContext extends ContextWrapper implements TestRule { private final TestableContentResolver mTestableContentResolver; private final TestableSettingsProvider mSettingsProvider; private ArrayList<MockServiceResolver> mMockServiceResolvers; private ArrayMap<String, Object> mMockSystemServices; private ArrayMap<ComponentName, IBinder> mMockServices; private ArrayMap<ServiceConnection, ComponentName> mActiveServices; Loading Loading @@ -220,19 +225,48 @@ public class TestableContext extends ContextWrapper implements TestRule { * with the specified service, and will call {@link ServiceConnection#onServiceDisconnected} * when the service is unbound. * </p> * * @see #addMockServiceResolver(MockServiceResolver) for custom resolution of service Intents to * ComponentNames */ public void addMockService(ComponentName component, IBinder service) { if (mMockServices == null) mMockServices = new ArrayMap<>(); mMockServices.put(component, service); } /** * Strategy to resolve a service {@link Intent} to a mock service {@link ComponentName}. */ public interface MockServiceResolver { @Nullable ComponentName resolve(Intent service); } /** * Registers a strategy to resolve service intents to registered mock services. * <p> * The result of the first {@link MockServiceResolver} to return a non-null * {@link ComponentName} is used to look up a mock service. The mock service must be registered * via {@link #addMockService(ComponentName, IBinder)} separately, using the same component * name. * * If none of the resolvers return a non-null value, or the first returned component name * does not link to a registered mock service, the bind requests are passed to the base context * * The resolvers are queried in order of registration. */ public void addMockServiceResolver(MockServiceResolver resolver) { if (mMockServiceResolvers == null) mMockServiceResolvers = new ArrayList<>(); mMockServiceResolvers.add(resolver); } /** * @see #addMockService(ComponentName, IBinder) */ @Override public boolean bindService(Intent service, ServiceConnection conn, int flags) { if (mService != null) mService.getLeakInfo(conn).addAllocation(new Throwable()); if (checkMocks(service.getComponent(), conn)) return true; if (checkMocks(service, conn)) return true; return super.bindService(service, conn, flags); } Loading @@ -243,7 +277,7 @@ public class TestableContext extends ContextWrapper implements TestRule { public boolean bindServiceAsUser(Intent service, ServiceConnection conn, int flags, Handler handler, UserHandle user) { if (mService != null) mService.getLeakInfo(conn).addAllocation(new Throwable()); if (checkMocks(service.getComponent(), conn)) return true; if (checkMocks(service, conn)) return true; return super.bindServiceAsUser(service, conn, flags, handler, user); } Loading @@ -254,18 +288,36 @@ public class TestableContext extends ContextWrapper implements TestRule { public boolean bindServiceAsUser(Intent service, ServiceConnection conn, int flags, UserHandle user) { if (mService != null) mService.getLeakInfo(conn).addAllocation(new Throwable()); if (checkMocks(service.getComponent(), conn)) return true; if (checkMocks(service, conn)) return true; return super.bindServiceAsUser(service, conn, flags, user); } private boolean checkMocks(ComponentName component, ServiceConnection conn) { if (mMockServices != null && component != null && mMockServices.containsKey(component)) { private boolean checkMocks(Intent service, ServiceConnection conn) { if (mMockServices == null) return false; ComponentName serviceComponent = resolveMockServiceComponent(service); if (serviceComponent == null) return false; IBinder serviceImpl = mMockServices.get(serviceComponent); if (serviceImpl == null) return false; if (mActiveServices == null) mActiveServices = new ArrayMap<>(); mActiveServices.put(conn, component); conn.onServiceConnected(component, mMockServices.get(component)); mActiveServices.put(conn, serviceComponent); conn.onServiceConnected(serviceComponent, serviceImpl); return true; } return false; private ComponentName resolveMockServiceComponent(Intent service) { ComponentName specifiedComponentName = service.getComponent(); if (specifiedComponentName != null) return specifiedComponentName; if (mMockServiceResolvers == null) return null; for (MockServiceResolver resolver : mMockServiceResolvers) { ComponentName resolvedComponent = resolver.resolve(service); if (resolvedComponent != null) return resolvedComponent; } return null; } /** Loading Loading
packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java +27 −1 Original line number Diff line number Diff line Loading @@ -116,6 +116,27 @@ public class QuickStepContract { public static final int SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE = 1 << 26; // Device dreaming state public static final int SYSUI_STATE_DEVICE_DREAMING = 1 << 27; // Whether the screen is currently on. Note that the screen is considered on while turning on, // but not while turning off. public static final int SYSUI_STATE_SCREEN_ON = 1 << 28; // Whether the screen is currently transitioning into the state indicated by // SYSUI_STATE_SCREEN_ON. public static final int SYSUI_STATE_SCREEN_TRANSITION = 1 << 29; // Mask for SystemUiStateFlags to isolate SYSUI_STATE_SCREEN_ON and // SYSUI_STATE_SCREEN_TRANSITION, to match SCREEN_STATE_* public static final int SYSUI_STATE_SCREEN_STATE_MASK = SYSUI_STATE_SCREEN_ON | SYSUI_STATE_SCREEN_TRANSITION; // Screen is off. public static final int SCREEN_STATE_OFF = 0; // Screen is on. public static final int SCREEN_STATE_ON = SYSUI_STATE_SCREEN_ON; // Screen is still on, but transitioning to turn off. public static final int SCREEN_STATE_TURNING_OFF = SYSUI_STATE_SCREEN_TRANSITION; // Screen was off and is now turning on. public static final int SCREEN_STATE_TURNING_ON = SYSUI_STATE_SCREEN_TRANSITION | SYSUI_STATE_SCREEN_ON; // Whether the back gesture is allowed (or ignored) by the Shade public static final boolean ALLOW_BACK_GESTURE_IN_SHADE = SystemProperties.getBoolean( "persist.wm.debug.shade_allow_back_gesture", false); Loading Loading @@ -148,7 +169,9 @@ public class QuickStepContract { SYSUI_STATE_IMMERSIVE_MODE, SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING, SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE, SYSUI_STATE_DEVICE_DREAMING SYSUI_STATE_DEVICE_DREAMING, SYSUI_STATE_SCREEN_ON, SYSUI_STATE_SCREEN_TRANSITION, }) public @interface SystemUiStateFlags {} Loading Loading @@ -187,6 +210,9 @@ public class QuickStepContract { str.add((flags & SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE) != 0 ? "freeform_active_in_desktop_mode" : ""); str.add((flags & SYSUI_STATE_DEVICE_DREAMING) != 0 ? "device_dreaming" : ""); str.add("screen_" + ((flags & SYSUI_STATE_SCREEN_TRANSITION) != 0 ? "turning_" : "") + ((flags & SYSUI_STATE_SCREEN_ON) != 0 ? "on" : "off")); return str.toString(); } Loading
packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java +49 −5 Original line number Diff line number Diff line Loading @@ -31,6 +31,8 @@ import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_WIN import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BOUNCER_SHOWING; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_DEVICE_DOZING; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_DEVICE_DREAMING; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_ON; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_TRANSITION; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_TRACING_ENABLED; Loading Loading @@ -121,7 +123,8 @@ import dagger.Lazy; public class OverviewProxyService implements CallbackController<OverviewProxyListener>, NavigationModeController.ModeChangedListener, Dumpable { private static final String ACTION_QUICKSTEP = "android.intent.action.QUICKSTEP_SERVICE"; @VisibleForTesting static final String ACTION_QUICKSTEP = "android.intent.action.QUICKSTEP_SERVICE"; public static final String TAG_OPS = "OverviewProxyService"; private static final long BACKOFF_MILLIS = 1000; Loading Loading @@ -548,6 +551,7 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis mUiEventLogger = uiEventLogger; mDisplayTracker = displayTracker; mUnfoldTransitionProgressForwarder = unfoldTransitionProgressForwarder; mSysuiUnlockAnimationController = sysuiUnlockAnimationController; dumpManager.registerDumpable(getClass().getSimpleName(), this); Loading Loading @@ -596,7 +600,6 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis // Connect to the service updateEnabledState(); startConnectionToCurrentUser(); mSysuiUnlockAnimationController = sysuiUnlockAnimationController; // Listen for assistant changes assistUtils.registerVoiceInteractionSessionListener(mVoiceInteractionSessionListener); Loading Loading @@ -726,10 +729,8 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis return; } mHandler.removeCallbacks(mConnectionRunnable); Intent launcherServiceIntent = new Intent(ACTION_QUICKSTEP) .setPackage(mRecentsComponentName.getPackageName()); try { mBound = mContext.bindServiceAsUser(launcherServiceIntent, mBound = mContext.bindServiceAsUser(mQuickStepIntent, mOverviewServiceConnection, Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE, UserHandle.of(mUserTracker.getUserId())); Loading Loading @@ -862,6 +863,11 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis */ @Override public void onScreenTurnedOn() { mSysUiState .setFlag(SYSUI_STATE_SCREEN_ON, true) .setFlag(SYSUI_STATE_SCREEN_TRANSITION, false) .commitUpdate(mContext.getDisplayId()); try { if (mOverviewProxy != null) { mOverviewProxy.onScreenTurnedOn(); Loading @@ -873,11 +879,27 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis } } /** * Notifies the Launcher that screen turned off. */ @Override public void onScreenTurnedOff() { mSysUiState .setFlag(SYSUI_STATE_SCREEN_ON, false) .setFlag(SYSUI_STATE_SCREEN_TRANSITION, false) .commitUpdate(mContext.getDisplayId()); } /** * Notifies the Launcher that screen is starting to turn on. */ @Override public void onScreenTurningOff() { mSysUiState .setFlag(SYSUI_STATE_SCREEN_ON, false) .setFlag(SYSUI_STATE_SCREEN_TRANSITION, true) .commitUpdate(mContext.getDisplayId()); try { if (mOverviewProxy != null) { mOverviewProxy.onScreenTurningOff(); Loading @@ -894,6 +916,11 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis */ @Override public void onScreenTurningOn() { mSysUiState .setFlag(SYSUI_STATE_SCREEN_ON, true) .setFlag(SYSUI_STATE_SCREEN_TRANSITION, true) .commitUpdate(mContext.getDisplayId()); try { if (mOverviewProxy != null) { mOverviewProxy.onScreenTurningOn(); Loading Loading @@ -1005,4 +1032,21 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis default void onAssistantGestureCompletion(float velocity) {} default void startAssistant(Bundle bundle) {} } /** * Shuts down this service at the end of a testcase. * <p> * The in-production service is never shuts down, and it was not designed with testing in mind. * This unregisters the mechanisms by which the service will be revived after a testcase. * <p> * NOTE: This is a stop-gap introduced when first added some tests to this class. It should * probably be replaced by proper lifecycle management on this class. */ @VisibleForTesting() void shutdownForTest() { mContext.unregisterReceiver(mLauncherStateChangedReceiver); mIsEnabled = false; mHandler.removeCallbacks(mConnectionRunnable); disconnectFromLauncherService(); } }
packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt 0 → 100644 +186 −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 com.android.systemui.recents import android.content.ComponentName import android.content.pm.PackageManager import android.content.pm.ResolveInfo import android.testing.AndroidTestingRunner import android.testing.TestableContext import android.testing.TestableLooper import androidx.test.filters.SmallTest import com.android.internal.app.AssistUtils import com.android.internal.logging.UiEventLogger import com.android.systemui.SysuiTestCase import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.dump.DumpManager import com.android.systemui.keyguard.KeyguardUnlockAnimationController import com.android.systemui.keyguard.ScreenLifecycle import com.android.systemui.model.SysUiState import com.android.systemui.navigationbar.NavigationBarController import com.android.systemui.navigationbar.NavigationModeController import com.android.systemui.recents.OverviewProxyService.ACTION_QUICKSTEP import com.android.systemui.settings.FakeDisplayTracker import com.android.systemui.settings.UserTracker import com.android.systemui.shared.recents.IOverviewProxy import com.android.systemui.shared.system.QuickStepContract.SCREEN_STATE_OFF import com.android.systemui.shared.system.QuickStepContract.SCREEN_STATE_ON import com.android.systemui.shared.system.QuickStepContract.SCREEN_STATE_TURNING_OFF import com.android.systemui.shared.system.QuickStepContract.SCREEN_STATE_TURNING_ON import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_STATE_MASK import com.android.systemui.statusbar.CommandQueue import com.android.systemui.statusbar.NotificationShadeWindowController import com.android.systemui.statusbar.phone.CentralSurfaces import com.android.systemui.unfold.progress.UnfoldTransitionProgressForwarder import com.android.systemui.util.mockito.whenever import com.android.wm.shell.sysui.ShellInterface import com.google.common.util.concurrent.MoreExecutors import dagger.Lazy import java.util.Optional import java.util.concurrent.Executor import org.junit.After import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentMatchers import org.mockito.Mock import org.mockito.Mockito.any import org.mockito.Mockito.anyInt import org.mockito.Mockito.intThat import org.mockito.Mockito.mock import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @SmallTest @RunWith(AndroidTestingRunner::class) @TestableLooper.RunWithLooper(setAsMainLooper = true) class OverviewProxyServiceTest : SysuiTestCase() { @Main private val executor: Executor = MoreExecutors.directExecutor() private lateinit var subject: OverviewProxyService private val dumpManager = DumpManager() private val displayTracker = FakeDisplayTracker(mContext) private val sysUiState = SysUiState(displayTracker) private val screenLifecycle = ScreenLifecycle(dumpManager) @Mock private lateinit var overviewProxy: IOverviewProxy.Stub @Mock private lateinit var packageManager: PackageManager // The following mocks belong to not-yet-tested parts of OverviewProxyService. @Mock private lateinit var commandQueue: CommandQueue @Mock private lateinit var shellInterface: ShellInterface @Mock private lateinit var navBarController: NavigationBarController @Mock private lateinit var centralSurfaces: CentralSurfaces @Mock private lateinit var navModeController: NavigationModeController @Mock private lateinit var statusBarWinController: NotificationShadeWindowController @Mock private lateinit var userTracker: UserTracker @Mock private lateinit var uiEventLogger: UiEventLogger @Mock private lateinit var sysuiUnlockAnimationController: KeyguardUnlockAnimationController @Mock private lateinit var assistUtils: AssistUtils @Mock private lateinit var unfoldTransitionProgressForwarder: Optional<UnfoldTransitionProgressForwarder> @Before fun setUp() { MockitoAnnotations.initMocks(this) val serviceComponent = ComponentName("test_package", "service_provider") context.addMockService(serviceComponent, overviewProxy) context.addMockServiceResolver( TestableContext.MockServiceResolver { if (it.action == ACTION_QUICKSTEP) serviceComponent else null } ) whenever(overviewProxy.queryLocalInterface(ArgumentMatchers.anyString())) .thenReturn(overviewProxy) whenever(overviewProxy.asBinder()).thenReturn(overviewProxy) // packageManager.resolveServiceAsUser has to return non-null for // OverviewProxyService#isEnabled to become true. context.setMockPackageManager(packageManager) whenever(packageManager.resolveServiceAsUser(any(), anyInt(), anyInt())) .thenReturn(mock(ResolveInfo::class.java)) subject = OverviewProxyService( context, executor, commandQueue, shellInterface, Lazy { navBarController }, Lazy { Optional.of(centralSurfaces) }, navModeController, statusBarWinController, sysUiState, userTracker, screenLifecycle, uiEventLogger, displayTracker, sysuiUnlockAnimationController, assistUtils, dumpManager, unfoldTransitionProgressForwarder ) } @After fun tearDown() { subject.shutdownForTest() } @Test fun `ScreenLifecycle - screenTurnedOn triggers SysUI state flag changes `() { screenLifecycle.dispatchScreenTurnedOn() verify(overviewProxy) .onSystemUiStateChanged( intThat { it and SYSUI_STATE_SCREEN_STATE_MASK == SCREEN_STATE_ON } ) } @Test fun `ScreenLifecycle - screenTurningOn triggers SysUI state flag changes `() { screenLifecycle.dispatchScreenTurningOn() verify(overviewProxy) .onSystemUiStateChanged( intThat { it and SYSUI_STATE_SCREEN_STATE_MASK == SCREEN_STATE_TURNING_ON } ) } @Test fun `ScreenLifecycle - screenTurnedOff triggers SysUI state flag changes `() { screenLifecycle.dispatchScreenTurnedOff() verify(overviewProxy) .onSystemUiStateChanged( intThat { it and SYSUI_STATE_SCREEN_STATE_MASK == SCREEN_STATE_OFF } ) } @Test fun `ScreenLifecycle - screenTurningOff triggers SysUI state flag changes `() { screenLifecycle.dispatchScreenTurningOff() verify(overviewProxy) .onSystemUiStateChanged( intThat { it and SYSUI_STATE_SCREEN_STATE_MASK == SCREEN_STATE_TURNING_OFF } ) } }
tests/testables/src/android/testing/TestableContext.java +67 −15 Original line number Diff line number Diff line Loading @@ -33,11 +33,15 @@ import android.provider.Settings; import android.util.ArrayMap; import android.view.LayoutInflater; import androidx.annotation.Nullable; import org.junit.rules.TestRule; import org.junit.rules.TestWatcher; import org.junit.runner.Description; import org.junit.runners.model.Statement; import java.util.ArrayList; /** * A ContextWrapper with utilities specifically designed to make Testing easier. * Loading @@ -61,6 +65,7 @@ public class TestableContext extends ContextWrapper implements TestRule { private final TestableContentResolver mTestableContentResolver; private final TestableSettingsProvider mSettingsProvider; private ArrayList<MockServiceResolver> mMockServiceResolvers; private ArrayMap<String, Object> mMockSystemServices; private ArrayMap<ComponentName, IBinder> mMockServices; private ArrayMap<ServiceConnection, ComponentName> mActiveServices; Loading Loading @@ -220,19 +225,48 @@ public class TestableContext extends ContextWrapper implements TestRule { * with the specified service, and will call {@link ServiceConnection#onServiceDisconnected} * when the service is unbound. * </p> * * @see #addMockServiceResolver(MockServiceResolver) for custom resolution of service Intents to * ComponentNames */ public void addMockService(ComponentName component, IBinder service) { if (mMockServices == null) mMockServices = new ArrayMap<>(); mMockServices.put(component, service); } /** * Strategy to resolve a service {@link Intent} to a mock service {@link ComponentName}. */ public interface MockServiceResolver { @Nullable ComponentName resolve(Intent service); } /** * Registers a strategy to resolve service intents to registered mock services. * <p> * The result of the first {@link MockServiceResolver} to return a non-null * {@link ComponentName} is used to look up a mock service. The mock service must be registered * via {@link #addMockService(ComponentName, IBinder)} separately, using the same component * name. * * If none of the resolvers return a non-null value, or the first returned component name * does not link to a registered mock service, the bind requests are passed to the base context * * The resolvers are queried in order of registration. */ public void addMockServiceResolver(MockServiceResolver resolver) { if (mMockServiceResolvers == null) mMockServiceResolvers = new ArrayList<>(); mMockServiceResolvers.add(resolver); } /** * @see #addMockService(ComponentName, IBinder) */ @Override public boolean bindService(Intent service, ServiceConnection conn, int flags) { if (mService != null) mService.getLeakInfo(conn).addAllocation(new Throwable()); if (checkMocks(service.getComponent(), conn)) return true; if (checkMocks(service, conn)) return true; return super.bindService(service, conn, flags); } Loading @@ -243,7 +277,7 @@ public class TestableContext extends ContextWrapper implements TestRule { public boolean bindServiceAsUser(Intent service, ServiceConnection conn, int flags, Handler handler, UserHandle user) { if (mService != null) mService.getLeakInfo(conn).addAllocation(new Throwable()); if (checkMocks(service.getComponent(), conn)) return true; if (checkMocks(service, conn)) return true; return super.bindServiceAsUser(service, conn, flags, handler, user); } Loading @@ -254,18 +288,36 @@ public class TestableContext extends ContextWrapper implements TestRule { public boolean bindServiceAsUser(Intent service, ServiceConnection conn, int flags, UserHandle user) { if (mService != null) mService.getLeakInfo(conn).addAllocation(new Throwable()); if (checkMocks(service.getComponent(), conn)) return true; if (checkMocks(service, conn)) return true; return super.bindServiceAsUser(service, conn, flags, user); } private boolean checkMocks(ComponentName component, ServiceConnection conn) { if (mMockServices != null && component != null && mMockServices.containsKey(component)) { private boolean checkMocks(Intent service, ServiceConnection conn) { if (mMockServices == null) return false; ComponentName serviceComponent = resolveMockServiceComponent(service); if (serviceComponent == null) return false; IBinder serviceImpl = mMockServices.get(serviceComponent); if (serviceImpl == null) return false; if (mActiveServices == null) mActiveServices = new ArrayMap<>(); mActiveServices.put(conn, component); conn.onServiceConnected(component, mMockServices.get(component)); mActiveServices.put(conn, serviceComponent); conn.onServiceConnected(serviceComponent, serviceImpl); return true; } return false; private ComponentName resolveMockServiceComponent(Intent service) { ComponentName specifiedComponentName = service.getComponent(); if (specifiedComponentName != null) return specifiedComponentName; if (mMockServiceResolvers == null) return null; for (MockServiceResolver resolver : mMockServiceResolvers) { ComponentName resolvedComponent = resolver.resolve(service); if (resolvedComponent != null) return resolvedComponent; } return null; } /** Loading