| /* |
| * 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.server.companion.virtual; |
| |
| import static android.content.pm.ActivityInfo.FLAG_CAN_DISPLAY_ON_REMOTE_DEVICES; |
| import static android.view.WindowManager.LayoutParams.FLAG_SECURE; |
| import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS; |
| |
| import static com.google.common.truth.Truth.assertThat; |
| |
| import static org.mockito.ArgumentMatchers.any; |
| import static org.mockito.ArgumentMatchers.anyInt; |
| import static org.mockito.ArgumentMatchers.anyString; |
| import static org.mockito.ArgumentMatchers.eq; |
| import static org.mockito.Mockito.doNothing; |
| import static org.mockito.Mockito.doReturn; |
| import static org.mockito.Mockito.mock; |
| import static org.mockito.Mockito.never; |
| import static org.mockito.Mockito.verify; |
| import static org.mockito.Mockito.when; |
| |
| import android.app.WindowConfiguration; |
| import android.companion.virtual.IVirtualDeviceIntentInterceptor; |
| import android.companion.virtual.VirtualDeviceManager; |
| import android.content.ComponentName; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.content.pm.ActivityInfo; |
| import android.content.pm.ApplicationInfo; |
| import android.net.Uri; |
| import android.os.RemoteException; |
| import android.os.UserHandle; |
| import android.platform.test.annotations.Presubmit; |
| import android.platform.test.flag.junit.SetFlagsRule; |
| import android.util.ArraySet; |
| import android.view.Display; |
| |
| import androidx.test.ext.junit.runners.AndroidJUnit4; |
| import androidx.test.platform.app.InstrumentationRegistry; |
| |
| import com.android.internal.app.BlockedAppStreamingActivity; |
| |
| import org.junit.Before; |
| import org.junit.Rule; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| import org.mockito.Mock; |
| import org.mockito.MockitoAnnotations; |
| |
| import java.util.Arrays; |
| import java.util.Collections; |
| import java.util.HashSet; |
| import java.util.Set; |
| |
| @Presubmit |
| @RunWith(AndroidJUnit4.class) |
| public class GenericWindowPolicyControllerTest { |
| |
| private static final int DISPLAY_ID = Display.DEFAULT_DISPLAY + 1; |
| private static final int TEST_UID = 1234567; |
| private static final String DISPLAY_CATEGORY = "com.display.category"; |
| private static final String NONBLOCKED_APP_PACKAGE_NAME = "com.someapp"; |
| private static final String BLOCKED_PACKAGE_NAME = "com.blockedapp"; |
| private static final int FLAG_CANNOT_DISPLAY_ON_REMOTE_DEVICES = 0x00000; |
| private static final String TEST_SITE = "http://test"; |
| private static final ComponentName BLOCKED_APP_STREAMING_COMPONENT = |
| new ComponentName("android", BlockedAppStreamingActivity.class.getName()); |
| private static final ComponentName BLOCKED_COMPONENT = new ComponentName(BLOCKED_PACKAGE_NAME, |
| BLOCKED_PACKAGE_NAME); |
| private static final ComponentName NONBLOCKED_COMPONENT = new ComponentName( |
| NONBLOCKED_APP_PACKAGE_NAME, NONBLOCKED_APP_PACKAGE_NAME); |
| |
| @Rule |
| public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); |
| |
| @Mock |
| private GenericWindowPolicyController.PipBlockedCallback mPipBlockedCallback; |
| @Mock |
| private VirtualDeviceManager.ActivityListener mActivityListener; |
| @Mock |
| private GenericWindowPolicyController.IntentListenerCallback mIntentListenerCallback; |
| @Mock |
| private GenericWindowPolicyController.ActivityBlockedCallback mActivityBlockedCallback; |
| @Mock |
| private GenericWindowPolicyController.RunningAppsChangedListener mRunningAppsChangedListener; |
| @Mock |
| private GenericWindowPolicyController.SecureWindowCallback mSecureWindowCallback; |
| |
| @Before |
| public void setUp() throws Exception { |
| MockitoAnnotations.initMocks(this); |
| } |
| |
| @Test |
| public void showTasksInHostDeviceRecents() { |
| GenericWindowPolicyController gwpc = createGwpc(); |
| |
| gwpc.setShowInHostDeviceRecents(true); |
| assertThat(gwpc.canShowTasksInHostDeviceRecents()).isTrue(); |
| |
| gwpc.setShowInHostDeviceRecents(false); |
| assertThat(gwpc.canShowTasksInHostDeviceRecents()).isFalse(); |
| } |
| |
| @Test |
| public void containsUid() { |
| GenericWindowPolicyController gwpc = createGwpc(); |
| |
| assertThat(gwpc.containsUid(TEST_UID)).isFalse(); |
| |
| gwpc.onRunningAppsChanged(new ArraySet<>(Arrays.asList(TEST_UID))); |
| assertThat(gwpc.containsUid(TEST_UID)).isTrue(); |
| |
| gwpc.onRunningAppsChanged(new ArraySet<>()); |
| assertThat(gwpc.containsUid(TEST_UID)).isFalse(); |
| } |
| |
| @Test |
| public void isEnteringPipAllowed_falseByDefault() { |
| GenericWindowPolicyController gwpc = createGwpc(); |
| |
| assertThat(gwpc.isEnteringPipAllowed(TEST_UID)).isFalse(); |
| verify(mPipBlockedCallback).onEnteringPipBlocked(TEST_UID); |
| } |
| |
| @Test |
| public void isEnteringPipAllowed_dpcSupportsPinned_allowed() { |
| GenericWindowPolicyController gwpc = createGwpc(); |
| gwpc.setSupportedWindowingModes(new HashSet<>( |
| Arrays.asList(WindowConfiguration.WINDOWING_MODE_FULLSCREEN, |
| WindowConfiguration.WINDOWING_MODE_PINNED))); |
| assertThat(gwpc.isEnteringPipAllowed(TEST_UID)).isTrue(); |
| verify(mPipBlockedCallback, never()).onEnteringPipBlocked(TEST_UID); |
| } |
| |
| @Test |
| public void openNonBlockedAppOnVirtualDisplay_isNotBlocked() { |
| GenericWindowPolicyController gwpc = createGwpc(); |
| gwpc.setDisplayId(DISPLAY_ID, /* isMirrorDisplay= */ false); |
| |
| ActivityInfo activityInfo = getActivityInfo( |
| NONBLOCKED_APP_PACKAGE_NAME, |
| NONBLOCKED_APP_PACKAGE_NAME, |
| /* displayOnRemoteDevices */ true, |
| /* targetDisplayCategory */ null); |
| assertActivityCanBeLaunched(gwpc, activityInfo); |
| } |
| |
| @Test |
| public void activityDoesNotSupportDisplayOnRemoteDevices_isBlocked() { |
| GenericWindowPolicyController gwpc = createGwpc(); |
| gwpc.setDisplayId(DISPLAY_ID, /* isMirrorDisplay= */ false); |
| |
| ActivityInfo activityInfo = getActivityInfo( |
| NONBLOCKED_APP_PACKAGE_NAME, |
| NONBLOCKED_APP_PACKAGE_NAME, |
| /* displayOnRemoteDevices */ false, |
| /* targetDisplayCategory */ null); |
| assertActivityIsBlocked(gwpc, activityInfo); |
| } |
| |
| @Test |
| public void openBlockedComponentOnVirtualDisplay_isBlocked() { |
| GenericWindowPolicyController gwpc = createGwpcWithBlockedComponent(BLOCKED_COMPONENT); |
| gwpc.setDisplayId(DISPLAY_ID, /* isMirrorDisplay= */ false); |
| |
| ActivityInfo activityInfo = getActivityInfo( |
| BLOCKED_PACKAGE_NAME, |
| BLOCKED_PACKAGE_NAME, |
| /* displayOnRemoteDevices */ true, |
| /* targetDisplayCategory */ null); |
| assertActivityIsBlocked(gwpc, activityInfo); |
| } |
| |
| @Test |
| public void addActivityPolicyExemption_openBlockedOnVirtualDisplay_isBlocked() { |
| GenericWindowPolicyController gwpc = createGwpc(); |
| gwpc.setDisplayId(DISPLAY_ID, /* isMirrorDisplay= */ false); |
| gwpc.setActivityLaunchDefaultAllowed(true); |
| gwpc.addActivityPolicyExemption(BLOCKED_COMPONENT); |
| |
| ActivityInfo activityInfo = getActivityInfo( |
| BLOCKED_PACKAGE_NAME, |
| BLOCKED_PACKAGE_NAME, |
| /* displayOnRemoteDevices */ true, |
| /* targetDisplayCategory */ null); |
| assertActivityIsBlocked(gwpc, activityInfo); |
| } |
| |
| @Test |
| public void openNotAllowedComponentOnBlocklistVirtualDisplay_isBlocked() { |
| GenericWindowPolicyController gwpc = createGwpcWithAllowedComponent(NONBLOCKED_COMPONENT); |
| gwpc.setDisplayId(DISPLAY_ID, /* isMirrorDisplay= */ false); |
| |
| ActivityInfo activityInfo = getActivityInfo( |
| BLOCKED_PACKAGE_NAME, |
| BLOCKED_PACKAGE_NAME, |
| /* displayOnRemoteDevices */ true, |
| /* targetDisplayCategory */ null); |
| assertActivityIsBlocked(gwpc, activityInfo); |
| } |
| |
| @Test |
| public void addActivityPolicyExemption_openNotAllowedOnVirtualDisplay_isBlocked() { |
| GenericWindowPolicyController gwpc = createGwpc(); |
| gwpc.setDisplayId(DISPLAY_ID, /* isMirrorDisplay= */ false); |
| gwpc.setActivityLaunchDefaultAllowed(false); |
| gwpc.addActivityPolicyExemption(NONBLOCKED_COMPONENT); |
| |
| ActivityInfo activityInfo = getActivityInfo( |
| BLOCKED_PACKAGE_NAME, |
| BLOCKED_PACKAGE_NAME, |
| /* displayOnRemoteDevices */ true, |
| /* targetDisplayCategory */ null); |
| assertActivityIsBlocked(gwpc, activityInfo); |
| } |
| |
| @Test |
| public void openAllowedComponentOnBlocklistVirtualDisplay_startsActivity() { |
| GenericWindowPolicyController gwpc = createGwpcWithAllowedComponent(NONBLOCKED_COMPONENT); |
| gwpc.setDisplayId(DISPLAY_ID, /* isMirrorDisplay= */ false); |
| |
| ActivityInfo activityInfo = getActivityInfo( |
| NONBLOCKED_APP_PACKAGE_NAME, |
| NONBLOCKED_APP_PACKAGE_NAME, |
| /* displayOnRemoteDevices */ true, |
| /* targetDisplayCategory */ null); |
| assertActivityCanBeLaunched(gwpc, activityInfo); |
| } |
| |
| @Test |
| public void addActivityPolicyExemption_openAllowedOnVirtualDisplay_startsActivity() { |
| GenericWindowPolicyController gwpc = createGwpc(); |
| gwpc.setDisplayId(DISPLAY_ID, /* isMirrorDisplay= */ false); |
| gwpc.setActivityLaunchDefaultAllowed(false); |
| gwpc.addActivityPolicyExemption(NONBLOCKED_COMPONENT); |
| |
| ActivityInfo activityInfo = getActivityInfo( |
| NONBLOCKED_APP_PACKAGE_NAME, |
| NONBLOCKED_APP_PACKAGE_NAME, |
| /* displayOnRemoteDevices */ true, |
| /* targetDisplayCategory */ null); |
| assertActivityCanBeLaunched(gwpc, activityInfo); |
| } |
| |
| @Test |
| public void openNonBlockedAppOnMirrorVirtualDisplay_isBlocked() { |
| GenericWindowPolicyController gwpc = createGwpc(); |
| gwpc.setDisplayId(DISPLAY_ID, /* isMirrorDisplay= */ true); |
| |
| ActivityInfo activityInfo = getActivityInfo( |
| NONBLOCKED_APP_PACKAGE_NAME, |
| NONBLOCKED_APP_PACKAGE_NAME, |
| /* displayOnRemoteDevices */ true, |
| /* targetDisplayCategory */ null); |
| assertNoActivityLaunched(gwpc, DISPLAY_ID, activityInfo); |
| } |
| |
| @Test |
| public void canActivityBeLaunched_mismatchingUserHandle_isBlocked() { |
| GenericWindowPolicyController gwpc = createGwpc(); |
| gwpc.setDisplayId(DISPLAY_ID, /* isMirrorDisplay= */ false); |
| |
| ActivityInfo activityInfo = getActivityInfo( |
| NONBLOCKED_APP_PACKAGE_NAME, |
| NONBLOCKED_APP_PACKAGE_NAME, |
| /* displayOnRemoteDevices */ true, |
| /* targetDisplayCategory */ null, |
| /* uid */ UserHandle.PER_USER_RANGE + 1); |
| assertActivityIsBlocked(gwpc, activityInfo); |
| } |
| |
| @Test |
| public void canActivityBeLaunched_blockedAppStreamingComponent_isNeverBlocked() { |
| GenericWindowPolicyController gwpc = createGwpc(); |
| gwpc.setDisplayId(DISPLAY_ID, /* isMirrorDisplay= */ false); |
| |
| ActivityInfo activityInfo = getActivityInfo( |
| BLOCKED_APP_STREAMING_COMPONENT.getPackageName(), |
| BLOCKED_APP_STREAMING_COMPONENT.getClassName(), |
| /* displayOnRemoteDevices */ true, |
| /* targetDisplayCategory */ null); |
| assertActivityCanBeLaunched(gwpc, activityInfo); |
| } |
| |
| @Test |
| public void canActivityBeLaunched_blockedAppStreamingComponentExplicitlyBlocked_isNeverBlocked() { |
| GenericWindowPolicyController gwpc = createGwpcWithBlockedComponent( |
| BLOCKED_APP_STREAMING_COMPONENT); |
| gwpc.setDisplayId(DISPLAY_ID, /* isMirrorDisplay= */ false); |
| |
| ActivityInfo activityInfo = getActivityInfo( |
| BLOCKED_APP_STREAMING_COMPONENT.getPackageName(), |
| BLOCKED_APP_STREAMING_COMPONENT.getClassName(), |
| /* displayOnRemoteDevices */ true, |
| /* targetDisplayCategory */ null); |
| |
| assertActivityCanBeLaunched(gwpc, activityInfo); |
| } |
| |
| @Test |
| public void canActivityBeLaunched_blockedAppStreamingComponentExemptFromStreaming_isNeverBlocked() { |
| GenericWindowPolicyController gwpc = createGwpc(); |
| gwpc.setDisplayId(DISPLAY_ID, /* isMirrorDisplay= */ false); |
| gwpc.setActivityLaunchDefaultAllowed(true); |
| gwpc.addActivityPolicyExemption(BLOCKED_APP_STREAMING_COMPONENT); |
| |
| ActivityInfo activityInfo = getActivityInfo( |
| BLOCKED_APP_STREAMING_COMPONENT.getPackageName(), |
| BLOCKED_APP_STREAMING_COMPONENT.getClassName(), |
| /* displayOnRemoteDevices */ true, |
| /* targetDisplayCategory */ null); |
| |
| assertActivityCanBeLaunched(gwpc, activityInfo); |
| } |
| |
| @Test |
| public void canActivityBeLaunched_blockedAppStreamingComponentNotAllowlisted_isNeverBlocked() { |
| GenericWindowPolicyController gwpc = createGwpcWithAllowedComponent(NONBLOCKED_COMPONENT); |
| gwpc.setDisplayId(DISPLAY_ID, /* isMirrorDisplay= */ false); |
| |
| ActivityInfo activityInfo = getActivityInfo( |
| BLOCKED_APP_STREAMING_COMPONENT.getPackageName(), |
| BLOCKED_APP_STREAMING_COMPONENT.getClassName(), |
| /* displayOnRemoteDevices */ true, |
| /* targetDisplayCategory */ null); |
| |
| assertActivityCanBeLaunched(gwpc, activityInfo); |
| } |
| |
| @Test |
| public void canActivityBeLaunched_blockedAppStreamingComponentNotExemptFromBlocklist_isNeverBlocked() { |
| GenericWindowPolicyController gwpc = createGwpc(); |
| gwpc.setDisplayId(DISPLAY_ID, /* isMirrorDisplay= */ false); |
| gwpc.setActivityLaunchDefaultAllowed(false); |
| gwpc.addActivityPolicyExemption(NONBLOCKED_COMPONENT); |
| |
| ActivityInfo activityInfo = getActivityInfo( |
| BLOCKED_APP_STREAMING_COMPONENT.getPackageName(), |
| BLOCKED_APP_STREAMING_COMPONENT.getClassName(), |
| /* displayOnRemoteDevices */ true, |
| /* targetDisplayCategory */ null); |
| |
| assertActivityCanBeLaunched(gwpc, activityInfo); |
| } |
| |
| @Test |
| public void canActivityBeLaunched_customDisplayCategoryMatches_isNotBlocked() { |
| GenericWindowPolicyController gwpc = createGwpcWithDisplayCategory(DISPLAY_CATEGORY); |
| gwpc.setDisplayId(DISPLAY_ID, /* isMirrorDisplay= */ false); |
| |
| ActivityInfo activityInfo = getActivityInfo( |
| NONBLOCKED_APP_PACKAGE_NAME, |
| NONBLOCKED_APP_PACKAGE_NAME, |
| /* displayOnRemoteDevices */ true, |
| /* targetDisplayCategory */ DISPLAY_CATEGORY); |
| |
| assertActivityCanBeLaunched(gwpc, activityInfo); |
| } |
| |
| @Test |
| public void canActivityBeLaunched_customDisplayCategoryDoesNotMatch_isBlocked() { |
| GenericWindowPolicyController gwpc = createGwpcWithDisplayCategory(DISPLAY_CATEGORY); |
| gwpc.setDisplayId(DISPLAY_ID, /* isMirrorDisplay= */ false); |
| |
| ActivityInfo activityInfo = getActivityInfo( |
| NONBLOCKED_APP_PACKAGE_NAME, |
| NONBLOCKED_APP_PACKAGE_NAME, |
| /* displayOnRemoteDevices */ true, |
| /* targetDisplayCategory */ "some.random.category"); |
| assertActivityIsBlocked(gwpc, activityInfo); |
| } |
| |
| @Test |
| public void canActivityBeLaunched_crossTaskLaunch_fromDefaultDisplay_isNotBlocked() { |
| GenericWindowPolicyController gwpc = createGwpc(); |
| gwpc.setDisplayId(DISPLAY_ID, /* isMirrorDisplay= */ false); |
| |
| ActivityInfo activityInfo = getActivityInfo( |
| NONBLOCKED_APP_PACKAGE_NAME, |
| NONBLOCKED_APP_PACKAGE_NAME, |
| /* displayOnRemoteDevices */ true, |
| /* targetDisplayCategory */ null); |
| assertActivityCanBeLaunched(gwpc, Display.DEFAULT_DISPLAY, true, |
| WindowConfiguration.WINDOWING_MODE_FULLSCREEN, activityInfo); |
| } |
| |
| @Test |
| public void canActivityBeLaunched_crossTaskLaunchFromVirtualDisplay_notExplicitlyBlocked_isNotBlocked() { |
| GenericWindowPolicyController gwpc = createGwpcWithCrossTaskNavigationBlockedFor( |
| BLOCKED_COMPONENT); |
| gwpc.setDisplayId(DISPLAY_ID, /* isMirrorDisplay= */ false); |
| |
| ActivityInfo activityInfo = getActivityInfo( |
| NONBLOCKED_APP_PACKAGE_NAME, |
| NONBLOCKED_APP_PACKAGE_NAME, |
| /* displayOnRemoteDevices */ true, |
| /* targetDisplayCategory */ null); |
| |
| assertActivityCanBeLaunched(gwpc, DISPLAY_ID, true, |
| WindowConfiguration.WINDOWING_MODE_FULLSCREEN, activityInfo); |
| } |
| |
| @Test |
| public void canActivityBeLaunched_crossTaskLaunchFromVirtualDisplay_explicitlyBlocked_isBlocked() { |
| GenericWindowPolicyController gwpc = createGwpcWithCrossTaskNavigationBlockedFor( |
| BLOCKED_COMPONENT); |
| gwpc.setDisplayId(DISPLAY_ID, /* isMirrorDisplay= */ false); |
| |
| ActivityInfo activityInfo = getActivityInfo( |
| BLOCKED_PACKAGE_NAME, |
| BLOCKED_PACKAGE_NAME, |
| /* displayOnRemoteDevices */ true, |
| /* targetDisplayCategory */ null); |
| assertActivityIsBlocked(gwpc, DISPLAY_ID, true, |
| WindowConfiguration.WINDOWING_MODE_FULLSCREEN, activityInfo); |
| } |
| |
| @Test |
| public void canActivityBeLaunched_crossTaskLaunchFromVirtualDisplay_notAllowed_isBlocked() { |
| GenericWindowPolicyController gwpc = createGwpcWithCrossTaskNavigationAllowed( |
| NONBLOCKED_COMPONENT); |
| gwpc.setDisplayId(DISPLAY_ID, /* isMirrorDisplay= */ false); |
| |
| ActivityInfo activityInfo = getActivityInfo( |
| BLOCKED_PACKAGE_NAME, |
| BLOCKED_PACKAGE_NAME, |
| /* displayOnRemoteDevices */ true, |
| /* targetDisplayCategory */ null); |
| assertActivityIsBlocked(gwpc, DISPLAY_ID, true, |
| WindowConfiguration.WINDOWING_MODE_FULLSCREEN, activityInfo); |
| } |
| |
| @Test |
| public void canActivityBeLaunched_crossTaskLaunchFromVirtualDisplay_allowed_isNotBlocked() { |
| GenericWindowPolicyController gwpc = createGwpcWithCrossTaskNavigationAllowed( |
| NONBLOCKED_COMPONENT); |
| gwpc.setDisplayId(DISPLAY_ID, /* isMirrorDisplay= */ false); |
| |
| ActivityInfo activityInfo = getActivityInfo( |
| NONBLOCKED_APP_PACKAGE_NAME, |
| NONBLOCKED_APP_PACKAGE_NAME, |
| /* displayOnRemoteDevices */ true, |
| /* targetDisplayCategory */ null); |
| assertActivityCanBeLaunched(gwpc, DISPLAY_ID, true, |
| WindowConfiguration.WINDOWING_MODE_FULLSCREEN, activityInfo); |
| } |
| |
| @Test |
| public void canActivityBeLaunched_unsupportedWindowingMode_isBlocked() { |
| GenericWindowPolicyController gwpc = createGwpc(); |
| gwpc.setDisplayId(DISPLAY_ID, /* isMirrorDisplay= */ false); |
| |
| ActivityInfo activityInfo = getActivityInfo( |
| NONBLOCKED_APP_PACKAGE_NAME, |
| NONBLOCKED_APP_PACKAGE_NAME, |
| /* displayOnRemoteDevices */ true, |
| /* targetDisplayCategory */ null); |
| assertActivityIsBlocked(gwpc, DISPLAY_ID, true, WindowConfiguration.WINDOWING_MODE_PINNED, |
| activityInfo); |
| } |
| |
| @Test |
| public void canActivityBeLaunched_permissionComponent_isBlocked() { |
| GenericWindowPolicyController gwpc = createGwpcWithPermissionComponent(BLOCKED_COMPONENT); |
| gwpc.setDisplayId(DISPLAY_ID, /* isMirrorDisplay= */ false); |
| |
| ActivityInfo activityInfo = getActivityInfo( |
| BLOCKED_PACKAGE_NAME, |
| BLOCKED_PACKAGE_NAME, |
| /* displayOnRemoteDevices */ true, |
| /* targetDisplayCategory */ null); |
| assertActivityIsBlocked(gwpc, activityInfo); |
| } |
| |
| @Test |
| public void registerRunningAppsChangedListener_onRunningAppsChanged_listenersNotified() { |
| ArraySet<Integer> uids = new ArraySet<>(Arrays.asList(TEST_UID)); |
| GenericWindowPolicyController gwpc = createGwpc(); |
| |
| gwpc.registerRunningAppsChangedListener(mRunningAppsChangedListener); |
| gwpc.onRunningAppsChanged(uids); |
| |
| assertThat(gwpc.getRunningAppsChangedListenersSizeForTesting()).isEqualTo(1); |
| verify(mRunningAppsChangedListener).onRunningAppsChanged(uids); |
| } |
| |
| @Test |
| public void onRunningAppsChanged_empty_onDisplayEmpty() { |
| ArraySet<Integer> uids = new ArraySet<>(); |
| GenericWindowPolicyController gwpc = createGwpc(); |
| gwpc.setDisplayId(DISPLAY_ID, /* isMirrorDisplay= */ false); |
| |
| gwpc.onRunningAppsChanged(uids); |
| |
| assertThat(gwpc.getRunningAppsChangedListenersSizeForTesting()).isEqualTo(0); |
| verify(mActivityListener).onDisplayEmpty(DISPLAY_ID); |
| } |
| |
| @Test |
| public void noRunningAppsChangedListener_onRunningAppsChanged_doesNotThrowException() { |
| ArraySet<Integer> uids = new ArraySet<>(Arrays.asList(TEST_UID)); |
| GenericWindowPolicyController gwpc = createGwpc(); |
| |
| gwpc.onRunningAppsChanged(uids); |
| |
| assertThat(gwpc.getRunningAppsChangedListenersSizeForTesting()).isEqualTo(0); |
| verify(mRunningAppsChangedListener, never()).onRunningAppsChanged(uids); |
| } |
| |
| @Test |
| public void registerUnregisterRunningAppsChangedListener_onRunningAppsChanged_doesNotThrowException() { |
| ArraySet<Integer> uids = new ArraySet<>(Arrays.asList(TEST_UID)); |
| GenericWindowPolicyController gwpc = createGwpc(); |
| |
| gwpc.registerRunningAppsChangedListener(mRunningAppsChangedListener); |
| gwpc.unregisterRunningAppsChangedListener(mRunningAppsChangedListener); |
| gwpc.onRunningAppsChanged(uids); |
| |
| assertThat(gwpc.getRunningAppsChangedListenersSizeForTesting()).isEqualTo(0); |
| verify(mRunningAppsChangedListener, never()).onRunningAppsChanged(uids); |
| } |
| |
| @Test |
| public void canActivityBeLaunched_intentInterceptedWhenRegistered_activityNoLaunch() |
| throws RemoteException { |
| Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(TEST_SITE)); |
| |
| IVirtualDeviceIntentInterceptor.Stub interceptor = |
| mock(IVirtualDeviceIntentInterceptor.Stub.class); |
| doNothing().when(interceptor).onIntentIntercepted(any()); |
| doReturn(interceptor).when(interceptor).asBinder(); |
| doReturn(interceptor).when(interceptor).queryLocalInterface(anyString()); |
| |
| GenericWindowPolicyController gwpc = createGwpc(); |
| ActivityInfo activityInfo = getActivityInfo( |
| NONBLOCKED_APP_PACKAGE_NAME, |
| NONBLOCKED_APP_PACKAGE_NAME, |
| /* displayOnRemoteDevices */ true, |
| /* targetDisplayCategory */ null); |
| |
| // register interceptor and intercept intent |
| when(mIntentListenerCallback.shouldInterceptIntent(any(Intent.class))).thenReturn(true); |
| assertThat(gwpc.canActivityBeLaunched(activityInfo, intent, |
| WindowConfiguration.WINDOWING_MODE_FULLSCREEN, DISPLAY_ID, /*isNewTask=*/false)) |
| .isFalse(); |
| |
| // unregister interceptor and launch activity |
| when(mIntentListenerCallback.shouldInterceptIntent(any(Intent.class))).thenReturn(false); |
| assertThat(gwpc.canActivityBeLaunched(activityInfo, intent, |
| WindowConfiguration.WINDOWING_MODE_FULLSCREEN, DISPLAY_ID, /*isNewTask=*/false)) |
| .isTrue(); |
| } |
| |
| @Test |
| public void canActivityBeLaunched_noMatchIntentFilter_activityLaunches() { |
| Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("testing")); |
| |
| GenericWindowPolicyController gwpc = createGwpc(); |
| ActivityInfo activityInfo = getActivityInfo( |
| NONBLOCKED_APP_PACKAGE_NAME, |
| NONBLOCKED_APP_PACKAGE_NAME, |
| /* displayOnRemoteDevices */ true, |
| /* targetDisplayCategory */ null); |
| |
| // register interceptor with different filter |
| when(mIntentListenerCallback.shouldInterceptIntent(any(Intent.class))).thenReturn(false); |
| assertThat(gwpc.canActivityBeLaunched(activityInfo, intent, |
| WindowConfiguration.WINDOWING_MODE_FULLSCREEN, DISPLAY_ID, /*isNewTask=*/false)) |
| .isTrue(); |
| verify(mIntentListenerCallback).shouldInterceptIntent(any(Intent.class)); |
| } |
| |
| @Test |
| public void onTopActivitychanged_null_noCallback() { |
| GenericWindowPolicyController gwpc = createGwpc(); |
| |
| gwpc.onTopActivityChanged(null, 0, 0); |
| verify(mActivityListener, never()) |
| .onTopActivityChanged(anyInt(), any(ComponentName.class), anyInt()); |
| } |
| |
| @Test |
| public void onTopActivitychanged_activityListenerCallbackObserved() { |
| int userId = 1000; |
| GenericWindowPolicyController gwpc = createGwpc(); |
| gwpc.setDisplayId(DISPLAY_ID, /* isMirrorDisplay= */ false); |
| |
| gwpc.onTopActivityChanged(BLOCKED_COMPONENT, 0, userId); |
| verify(mActivityListener) |
| .onTopActivityChanged(eq(DISPLAY_ID), eq(BLOCKED_COMPONENT), eq(userId)); |
| } |
| |
| @Test |
| public void keepActivityOnWindowFlagsChanged_noChange() { |
| GenericWindowPolicyController gwpc = createGwpc(); |
| gwpc.setDisplayId(DISPLAY_ID, /* isMirrorDisplay= */ false); |
| |
| ActivityInfo activityInfo = getActivityInfo( |
| NONBLOCKED_APP_PACKAGE_NAME, |
| NONBLOCKED_APP_PACKAGE_NAME, |
| /* displayOnRemoteDevices */ true, |
| /* targetDisplayCategory */ null); |
| |
| assertThat(gwpc.keepActivityOnWindowFlagsChanged(activityInfo, 0, 0)).isTrue(); |
| |
| verify(mSecureWindowCallback, never()).onSecureWindowShown(DISPLAY_ID, |
| activityInfo.applicationInfo.uid); |
| verify(mActivityBlockedCallback, never()).onActivityBlocked(DISPLAY_ID, activityInfo); |
| } |
| |
| @Test |
| public void keepActivityOnWindowFlagsChanged_flagSecure_isAllowedAfterTM() { |
| GenericWindowPolicyController gwpc = createGwpc(); |
| gwpc.setDisplayId(DISPLAY_ID, /* isMirrorDisplay= */ false); |
| |
| ActivityInfo activityInfo = getActivityInfo( |
| NONBLOCKED_APP_PACKAGE_NAME, |
| NONBLOCKED_APP_PACKAGE_NAME, |
| /* displayOnRemoteDevices */ true, |
| /* targetDisplayCategory */ null); |
| |
| assertThat(gwpc.keepActivityOnWindowFlagsChanged(activityInfo, FLAG_SECURE, 0)).isTrue(); |
| |
| verify(mSecureWindowCallback).onSecureWindowShown(DISPLAY_ID, |
| activityInfo.applicationInfo.uid); |
| verify(mActivityBlockedCallback, never()).onActivityBlocked(DISPLAY_ID, activityInfo); |
| } |
| |
| @Test |
| public void keepActivityOnWindowFlagsChanged_systemFlagHideNonSystemOverlayWindows_isAllowedAfterTM() { |
| GenericWindowPolicyController gwpc = createGwpc(); |
| gwpc.setDisplayId(DISPLAY_ID, /* isMirrorDisplay= */ false); |
| |
| ActivityInfo activityInfo = getActivityInfo( |
| NONBLOCKED_APP_PACKAGE_NAME, |
| NONBLOCKED_APP_PACKAGE_NAME, |
| /* displayOnRemoteDevices */ true, |
| /* targetDisplayCategory */ null); |
| |
| assertThat(gwpc.keepActivityOnWindowFlagsChanged(activityInfo, 0, |
| SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS)).isTrue(); |
| |
| verify(mSecureWindowCallback, never()).onSecureWindowShown(DISPLAY_ID, |
| activityInfo.applicationInfo.uid); |
| verify(mActivityBlockedCallback, never()).onActivityBlocked(DISPLAY_ID, activityInfo); |
| } |
| |
| @Test |
| public void getCustomHomeComponent_noneSet() { |
| GenericWindowPolicyController gwpc = createGwpc(); |
| |
| assertThat(gwpc.getCustomHomeComponent()).isNull(); |
| } |
| |
| @Test |
| public void getCustomHomeComponent_returnsHomeComponent() { |
| GenericWindowPolicyController gwpc = createGwpcWithCustomHomeComponent( |
| NONBLOCKED_COMPONENT); |
| |
| assertThat(gwpc.getCustomHomeComponent()).isEqualTo(NONBLOCKED_COMPONENT); |
| } |
| |
| private GenericWindowPolicyController createGwpc() { |
| return new GenericWindowPolicyController( |
| 0, |
| 0, |
| /* allowedUsers= */ new ArraySet<>(getCurrentUserId()), |
| /* activityLaunchAllowedByDefault= */ true, |
| /* activityPolicyExemptions= */ new ArraySet<>(), |
| /* crossTaskNavigationAllowedByDefault= */ true, |
| /* crossTaskNavigationExemptions= */ new ArraySet<>(), |
| /* permissionDialogComponent= */ null, |
| /* activityListener= */ mActivityListener, |
| /* pipBlockedCallback= */ mPipBlockedCallback, |
| /* activityBlockedCallback= */ mActivityBlockedCallback, |
| /* secureWindowCallback= */ mSecureWindowCallback, |
| /* intentListenerCallback= */ mIntentListenerCallback, |
| /* displayCategories= */ new ArraySet<>(), |
| /* showTasksInHostDeviceRecents= */ true, |
| /* customHomeComponent= */ null); |
| } |
| |
| private GenericWindowPolicyController createGwpcWithCustomHomeComponent( |
| ComponentName homeComponent) { |
| return new GenericWindowPolicyController( |
| 0, |
| 0, |
| /* allowedUsers= */ new ArraySet<>(getCurrentUserId()), |
| /* activityLaunchAllowedByDefault= */ true, |
| /* activityPolicyExemptions= */ new ArraySet<>(), |
| /* crossTaskNavigationAllowedByDefault= */ true, |
| /* crossTaskNavigationExemptions= */ new ArraySet<>(), |
| /* permissionDialogComponent= */ null, |
| /* activityListener= */ mActivityListener, |
| /* pipBlockedCallback= */ mPipBlockedCallback, |
| /* activityBlockedCallback= */ mActivityBlockedCallback, |
| /* secureWindowCallback= */ null, |
| /* intentListenerCallback= */ mIntentListenerCallback, |
| /* displayCategories= */ new ArraySet<>(), |
| /* showTasksInHostDeviceRecents= */ true, |
| /* customHomeComponent= */ homeComponent); |
| } |
| |
| private GenericWindowPolicyController createGwpcWithBlockedComponent( |
| ComponentName blockedComponent) { |
| return new GenericWindowPolicyController( |
| 0, |
| 0, |
| /* allowedUsers= */ new ArraySet<>(getCurrentUserId()), |
| /* activityLaunchAllowedByDefault= */ true, |
| /* activityPolicyExemptions= */ Collections.singleton(blockedComponent), |
| /* crossTaskNavigationAllowedByDefault= */ true, |
| /* crossTaskNavigationExemptions= */ new ArraySet<>(), |
| /* permissionDialogComponent= */ null, |
| /* activityListener= */ mActivityListener, |
| /* pipBlockedCallback= */ mPipBlockedCallback, |
| /* activityBlockedCallback= */ mActivityBlockedCallback, |
| /* secureWindowCallback= */ null, |
| /* intentListenerCallback= */ mIntentListenerCallback, |
| /* displayCategories= */ new ArraySet<>(), |
| /* showTasksInHostDeviceRecents= */ true, |
| /* customHomeComponent= */ null); |
| } |
| |
| private GenericWindowPolicyController createGwpcWithAllowedComponent( |
| ComponentName allowedComponent) { |
| return new GenericWindowPolicyController( |
| 0, |
| 0, |
| /* allowedUsers= */ new ArraySet<>(getCurrentUserId()), |
| /* activityLaunchAllowedByDefault= */ false, |
| /* activityPolicyExemptions= */ Collections.singleton(allowedComponent), |
| /* crossTaskNavigationAllowedByDefault= */ true, |
| /* crossTaskNavigationExemptions= */ new ArraySet<>(), |
| /* permissionDialogComponent= */ null, |
| /* activityListener= */ mActivityListener, |
| /* pipBlockedCallback= */ mPipBlockedCallback, |
| /* activityBlockedCallback= */ mActivityBlockedCallback, |
| /* secureWindowCallback= */ null, |
| /* intentListenerCallback= */ mIntentListenerCallback, |
| /* displayCategories= */ new ArraySet<>(), |
| /* showTasksInHostDeviceRecents= */ true, |
| /* customHomeComponent= */ null); |
| } |
| |
| private GenericWindowPolicyController createGwpcWithDisplayCategory( |
| String displayCategory) { |
| return new GenericWindowPolicyController( |
| 0, |
| 0, |
| /* allowedUsers= */ new ArraySet<>(getCurrentUserId()), |
| /* activityLaunchAllowedByDefault= */ true, |
| /* activityPolicyExemptions= */ new ArraySet<>(), |
| /* crossTaskNavigationAllowedByDefault= */ true, |
| /* crossTaskNavigationExemptions= */ new ArraySet<>(), |
| /* permissionDialogComponent= */ null, |
| /* activityListener= */ mActivityListener, |
| /* pipBlockedCallback= */ mPipBlockedCallback, |
| /* activityBlockedCallback= */ mActivityBlockedCallback, |
| /* secureWindowCallback= */ null, |
| /* intentListenerCallback= */ mIntentListenerCallback, |
| /* displayCategories= */ Collections.singleton(displayCategory), |
| /* showTasksInHostDeviceRecents= */ true, |
| /* customHomeComponent= */ null); |
| } |
| |
| private GenericWindowPolicyController createGwpcWithCrossTaskNavigationBlockedFor( |
| ComponentName blockedComponent) { |
| return new GenericWindowPolicyController( |
| 0, |
| 0, |
| /* allowedUsers= */ new ArraySet<>(getCurrentUserId()), |
| /* activityLaunchAllowedByDefault= */ true, |
| /* activityPolicyExemptions= */ new ArraySet<>(), |
| /* crossTaskNavigationAllowedByDefault= */ true, |
| /* crossTaskNavigationExemptions= */ Collections.singleton(blockedComponent), |
| /* permissionDialogComponent= */ null, |
| /* activityListener= */ mActivityListener, |
| /* pipBlockedCallback= */ mPipBlockedCallback, |
| /* activityBlockedCallback= */ mActivityBlockedCallback, |
| /* secureWindowCallback= */ null, |
| /* intentListenerCallback= */ mIntentListenerCallback, |
| /* displayCategories= */ new ArraySet<>(), |
| /* showTasksInHostDeviceRecents= */ true, |
| /* customHomeComponent= */ null); |
| } |
| |
| private GenericWindowPolicyController createGwpcWithCrossTaskNavigationAllowed( |
| ComponentName allowedComponent) { |
| return new GenericWindowPolicyController( |
| 0, |
| 0, |
| /* allowedUsers= */ new ArraySet<>(getCurrentUserId()), |
| /* activityLaunchAllowedByDefault= */ true, |
| /* activityPolicyExemptions= */ new ArraySet<>(), |
| /* crossTaskNavigationAllowedByDefault= */ false, |
| /* crossTaskNavigationExemptions= */ Collections.singleton(allowedComponent), |
| /* permissionDialogComponent= */ null, |
| /* activityListener= */ mActivityListener, |
| /* pipBlockedCallback= */ mPipBlockedCallback, |
| /* activityBlockedCallback= */ mActivityBlockedCallback, |
| /* secureWindowCallback= */ null, |
| /* intentListenerCallback= */ mIntentListenerCallback, |
| /* displayCategories= */ new ArraySet<>(), |
| /* showTasksInHostDeviceRecents= */ true, |
| /* customHomeComponent= */ null); |
| } |
| |
| private GenericWindowPolicyController createGwpcWithPermissionComponent( |
| ComponentName permissionComponent) { |
| //TODO instert the component |
| return new GenericWindowPolicyController( |
| 0, |
| 0, |
| /* allowedUsers= */ new ArraySet<>(getCurrentUserId()), |
| /* activityLaunchAllowedByDefault= */ true, |
| /* activityPolicyExemptions= */ new ArraySet<>(), |
| /* crossTaskNavigationAllowedByDefault= */ false, |
| /* crossTaskNavigationExemptions= */ new ArraySet<>(), |
| /* permissionDialogComponent= */ permissionComponent, |
| /* activityListener= */ mActivityListener, |
| /* pipBlockedCallback= */ mPipBlockedCallback, |
| /* activityBlockedCallback= */ mActivityBlockedCallback, |
| /* secureWindowCallback= */ null, |
| /* intentListenerCallback= */ mIntentListenerCallback, |
| /* displayCategories= */ new ArraySet<>(), |
| /* showTasksInHostDeviceRecents= */ true, |
| /* customHomeComponent= */ null); |
| } |
| |
| private Set<UserHandle> getCurrentUserId() { |
| Context context = InstrumentationRegistry.getInstrumentation().getTargetContext(); |
| return new ArraySet<>(Arrays.asList(context.getUser())); |
| } |
| |
| private ActivityInfo getActivityInfo( |
| String packageName, String name, boolean displayOnRemoteDevices, |
| String requiredDisplayCategory) { |
| return getActivityInfo(packageName, name, displayOnRemoteDevices, requiredDisplayCategory, |
| 0); |
| } |
| |
| private ActivityInfo getActivityInfo( |
| String packageName, String name, boolean displayOnRemoteDevices, |
| String requiredDisplayCategory, int uid) { |
| ApplicationInfo applicationInfo = new ApplicationInfo(); |
| applicationInfo.uid = uid; |
| |
| ActivityInfo activityInfo = new ActivityInfo(); |
| activityInfo.packageName = packageName; |
| activityInfo.name = name; |
| activityInfo.flags = displayOnRemoteDevices |
| ? FLAG_CAN_DISPLAY_ON_REMOTE_DEVICES : FLAG_CANNOT_DISPLAY_ON_REMOTE_DEVICES; |
| activityInfo.applicationInfo = applicationInfo; |
| activityInfo.requiredDisplayCategory = requiredDisplayCategory; |
| return activityInfo; |
| } |
| |
| private void assertActivityCanBeLaunched(GenericWindowPolicyController gwpc, |
| ActivityInfo activityInfo) { |
| assertActivityCanBeLaunched(gwpc, DISPLAY_ID, false, |
| WindowConfiguration.WINDOWING_MODE_FULLSCREEN, activityInfo); |
| } |
| |
| private void assertActivityCanBeLaunched(GenericWindowPolicyController gwpc, int fromDisplay, |
| boolean isNewTask, int windowingMode, ActivityInfo activityInfo) { |
| assertThat(gwpc.canActivityBeLaunched(activityInfo, null, windowingMode, fromDisplay, |
| isNewTask)).isTrue(); |
| |
| verify(mActivityBlockedCallback, never()).onActivityBlocked(fromDisplay, activityInfo); |
| verify(mIntentListenerCallback, never()).shouldInterceptIntent(any(Intent.class)); |
| } |
| |
| private void assertActivityIsBlocked(GenericWindowPolicyController gwpc, |
| ActivityInfo activityInfo) { |
| assertActivityIsBlocked(gwpc, DISPLAY_ID, false, |
| WindowConfiguration.WINDOWING_MODE_FULLSCREEN, activityInfo); |
| } |
| |
| private void assertActivityIsBlocked(GenericWindowPolicyController gwpc, int fromDisplay, |
| boolean isNewTask, int windowingMode, ActivityInfo activityInfo) { |
| assertThat(gwpc.canActivityBeLaunched(activityInfo, null, windowingMode, fromDisplay, |
| isNewTask)).isFalse(); |
| |
| verify(mActivityBlockedCallback).onActivityBlocked(fromDisplay, activityInfo); |
| verify(mIntentListenerCallback, never()).shouldInterceptIntent(any(Intent.class)); |
| } |
| |
| private void assertNoActivityLaunched(GenericWindowPolicyController gwpc, int fromDisplay, |
| ActivityInfo activityInfo) { |
| assertThat(gwpc.canActivityBeLaunched(activityInfo, null, |
| WindowConfiguration.WINDOWING_MODE_FULLSCREEN, DISPLAY_ID, true)) |
| .isFalse(); |
| |
| verify(mActivityBlockedCallback, never()).onActivityBlocked(fromDisplay, activityInfo); |
| verify(mIntentListenerCallback, never()).shouldInterceptIntent(any(Intent.class)); |
| } |
| } |