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