1 /* <lambda>null2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.systemui.cts.tv 18 19 import android.Manifest.permission.READ_DREAM_STATE 20 import android.Manifest.permission.WRITE_DREAM_STATE 21 import android.app.WindowConfiguration 22 import android.content.pm.PackageManager 23 import android.os.ServiceManager 24 import android.platform.test.annotations.AppModeFull 25 import android.platform.test.annotations.Postsubmit 26 import android.server.wm.Condition 27 import android.server.wm.WindowManagerState 28 import android.server.wm.annotation.Group2 29 import android.service.dreams.DreamService 30 import android.service.dreams.IDreamManager 31 import android.systemui.tv.cts.Components.PIP_ACTIVITY 32 import android.systemui.tv.cts.Components.windowName 33 import android.systemui.tv.cts.PipActivity 34 import android.systemui.tv.cts.PipActivity.ACTION_ENTER_PIP 35 import androidx.annotation.CallSuper 36 import androidx.test.ext.junit.runners.AndroidJUnit4 37 import com.android.compatibility.common.util.SystemUtil 38 import com.android.compatibility.common.util.ThrowingSupplier 39 import org.junit.Assume 40 import org.junit.Ignore 41 import org.junit.Test 42 import org.junit.runner.RunWith 43 import kotlin.test.assertTrue 44 45 /** 46 * Tests most basic picture in picture (PiP) behavior. 47 * 48 * Build/Install/Run: 49 * atest CtsSystemUiTestCases:BasicPipTests 50 */ 51 @AppModeFull(reason = "Checks window manager state") 52 @Postsubmit 53 @Group2 54 @RunWith(AndroidJUnit4::class) 55 class BasicPipTests : TvTestBase() { 56 private val isPipSupported: Boolean 57 get() = packageManager.hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE) 58 59 @CallSuper 60 override fun onSetUp() { 61 Assume.assumeTrue(isPipSupported) 62 } 63 64 override fun onTearDown() { 65 stopPackage(PIP_ACTIVITY.packageName) 66 } 67 68 /** Open an app in pip mode and ensure it has a window but is not focused. */ 69 @Test 70 fun openPip_launchedNotFocused() { 71 launchActivity(PIP_ACTIVITY, ACTION_ENTER_PIP) 72 waitForEnterPip() 73 74 assertLaunchedNotFocused() 75 } 76 77 /** Ensure an app can be launched into pip mode from the screensaver state. */ 78 @Test 79 @Ignore("b/163116693") 80 fun openPip_afterScreenSaver() { 81 runWithDreamManager { dreamManager -> 82 dreamManager.dream() 83 dreamManager.waitForDream() 84 } 85 86 // Launch pip activity that is supposed to wake up the device 87 launchActivity( 88 activity = PIP_ACTIVITY, 89 action = ACTION_ENTER_PIP, 90 boolExtras = mapOf(PipActivity.EXTRA_TURN_ON_SCREEN to true) 91 ) 92 waitForEnterPip() 93 94 assertLaunchedNotFocused() 95 assertTrue("Device must be awake") { 96 runWithDreamManager { dreamManager -> 97 !dreamManager.isDreaming 98 } 99 } 100 } 101 102 /** Ensure an app in pip mode remains open throughout the device dreaming and waking. */ 103 @Test 104 fun pipApp_remainsOpen_afterScreensaver() { 105 launchActivity(PIP_ACTIVITY, ACTION_ENTER_PIP) 106 waitForEnterPip() 107 108 runWithDreamManager { dreamManager -> 109 dreamManager.dream() 110 dreamManager.waitForDream() 111 dreamManager.awaken() 112 dreamManager.waitForAwake() 113 } 114 115 assertLaunchedNotFocused() 116 } 117 118 private fun assertLaunchedNotFocused() { 119 wmState.assertActivityDisplayed(PIP_ACTIVITY) 120 wmState.assertNotFocusedWindow("PiP Window must not be focused!", 121 PIP_ACTIVITY.windowName()) 122 } 123 124 /** Run the given actions on a dream manager, acquiring appropriate permissions. */ 125 private fun <T> runWithDreamManager(actions: (IDreamManager) -> T): T { 126 val dreamManager: IDreamManager = IDreamManager.Stub.asInterface( 127 ServiceManager.getServiceOrThrow(DreamService.DREAM_SERVICE)) 128 129 return SystemUtil.runWithShellPermissionIdentity(ThrowingSupplier { 130 actions(dreamManager) 131 }, READ_DREAM_STATE, WRITE_DREAM_STATE) 132 } 133 134 /** Wait for the device to enter dream state. Throw on timeout. */ 135 private fun IDreamManager.waitForDream() { 136 val message = "Device must be dreaming!" 137 Condition.waitFor(message) { 138 isDreaming 139 } || error(message) 140 } 141 142 /** Wait for the device to awaken. Throw on timeout. */ 143 private fun IDreamManager.waitForAwake() { 144 val message = "Device must be awake!" 145 Condition.waitFor(message) { 146 !isDreaming 147 } || error(message) 148 } 149 150 /** Waits until the pip animation has finished and the app is fully in pip mode. */ 151 private fun waitForEnterPip() { 152 wmState.waitForWithAmState("checking task windowing mode") { state: WindowManagerState -> 153 state.getTaskByActivity(PIP_ACTIVITY)?.let { task -> 154 task.windowingMode == WindowConfiguration.WINDOWING_MODE_PINNED 155 } ?: false 156 } || error("Task ${PIP_ACTIVITY.flattenToShortString()} is not found or not pinned!") 157 158 wmState.waitForWithAmState("checking activity windowing mode") { 159 state: WindowManagerState -> 160 state.getTaskByActivity(PIP_ACTIVITY)?.getActivity(PIP_ACTIVITY)?.let { activity -> 161 activity.windowingMode == WindowConfiguration.WINDOWING_MODE_PINNED && 162 activity.state == WindowManagerState.STATE_PAUSED 163 } ?: false 164 } || error("Activity ${PIP_ACTIVITY.flattenToShortString()} is not found," + 165 " not pinned or not paused!") 166 } 167 } 168