• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * 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 com.android.server.wm.flicker.helpers
18 
19 import android.app.ActivityManager
20 import android.app.Instrumentation
21 import android.content.ComponentName
22 import android.content.Context
23 import android.content.Intent
24 import android.platform.helpers.AbstractStandardAppHelper
25 import android.support.test.launcherhelper.ILauncherStrategy
26 import android.support.test.launcherhelper.LauncherStrategyFactory
27 import androidx.test.uiautomator.By
28 import androidx.test.uiautomator.BySelector
29 import androidx.test.uiautomator.UiDevice
30 import androidx.test.uiautomator.Until
31 import com.android.server.wm.traces.common.Condition
32 import com.android.server.wm.traces.common.DeviceStateDump
33 import com.android.server.wm.traces.common.FlickerComponentName
34 import com.android.server.wm.traces.common.WindowManagerConditionsFactory
35 import com.android.server.wm.traces.common.layers.BaseLayerTraceEntry
36 import com.android.server.wm.traces.common.windowmanager.WindowManagerState
37 import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
38 
39 /**
40  * Class to take advantage of {@code IAppHelper} interface so the same test can be run against first
41  * party and third party apps.
42  */
43 open class StandardAppHelper @JvmOverloads constructor(
44     instr: Instrumentation,
45     @JvmField val appName: String,
46     @JvmField val component: FlickerComponentName,
47     protected val launcherStrategy: ILauncherStrategy =
48         LauncherStrategyFactory.getInstance(instr).launcherStrategy
49 ) : AbstractStandardAppHelper(instr) {
50     constructor(
51         instr: Instrumentation,
52         appName: String,
53         packageName: String,
54         activity: String,
55         launcherStrategy: ILauncherStrategy =
56             LauncherStrategyFactory.getInstance(instr).launcherStrategy
57     ): this(instr, appName,
58         FlickerComponentName(packageName, ".$activity"), launcherStrategy)
59 
60     private val activityManager: ActivityManager?
61         get() = mInstrumentation.context.getSystemService(ActivityManager::class.java)
62 
63     protected val context: Context
64         get() = mInstrumentation.context
65 
66     protected val uiDevice: UiDevice = UiDevice.getInstance(mInstrumentation)
67 
getAppSelectornull68     private fun getAppSelector(expectedPackageName: String): BySelector {
69         val expected = if (expectedPackageName.isNotEmpty()) {
70             expectedPackageName
71         } else {
72             component.packageName
73         }
74         return By.pkg(expected).depth(0)
75     }
76 
opennull77     override fun open() {
78         launcherStrategy.launch(appName, component.packageName)
79     }
80 
81     /** {@inheritDoc}  */
getPackagenull82     override fun getPackage(): String {
83         return component.packageName
84     }
85 
86     /** {@inheritDoc}  */
getOpenAppIntentnull87     override fun getOpenAppIntent(): Intent {
88         val intent = Intent()
89         intent.addCategory(Intent.CATEGORY_LAUNCHER)
90         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
91         intent.component = ComponentName(component.packageName, component.className)
92         return intent
93     }
94 
95     /** {@inheritDoc}  */
getLauncherNamenull96     override fun getLauncherName(): String {
97         return appName
98     }
99 
100     /** {@inheritDoc}  */
dismissInitialDialogsnull101     override fun dismissInitialDialogs() {}
102 
103     /** {@inheritDoc}  */
exitnull104     override fun exit() {
105         // Ensure all testing components end up being closed.
106         activityManager?.forceStopPackage(component.packageName)
107     }
108 
109     /**
110      * Exits the activity and wait for activity destroyed
111      */
exitnull112     fun exit(
113         wmHelper: WindowManagerStateHelper
114     ) {
115         exit()
116         waitForActivityDestroyed(wmHelper)
117     }
118 
119     /**
120      * Waits the activity until state change to {link WindowManagerState.STATE_DESTROYED}
121      */
waitForActivityDestroyednull122     private fun waitForActivityDestroyed(
123         wmHelper: WindowManagerStateHelper
124     ) {
125         val activityName = component.toActivityName()
126         val waitMsg = "state of $activityName to be ${WindowManagerState.STATE_DESTROYED}"
127         require(
128             wmHelper.waitFor(waitMsg) {
129                 !it.wmState.containsActivity(activityName) ||
130                     it.wmState.hasActivityState(activityName, WindowManagerState.STATE_DESTROYED)
131             }
132         ) { "App activity should have been destroyed" }
133         wmHelper.waitForAppTransitionIdle()
134         // Ensure WindowManagerService wait until all animations have completed
135         mInstrumentation.uiAutomation.syncInputTransactions()
136     }
137 
launchAppViaIntentnull138     private fun launchAppViaIntent(
139         action: String? = null,
140         stringExtras: Map<String, String> = mapOf()
141     ) {
142         val intent = openAppIntent
143         intent.action = action
144         stringExtras.forEach {
145             intent.putExtra(it.key, it.value)
146         }
147         context.startActivity(intent)
148     }
149 
150     /**
151      * Launches the app through an intent instead of interacting with the launcher.
152      *
153      * Uses UiAutomation to detect when the app is open
154      */
155     @JvmOverloads
launchViaIntentnull156     open fun launchViaIntent(
157         expectedPackageName: String = "",
158         action: String? = null,
159         stringExtras: Map<String, String> = mapOf()
160     ) {
161         launchAppViaIntent(action, stringExtras)
162         val appSelector = getAppSelector(expectedPackageName)
163         uiDevice.wait(Until.hasObject(appSelector), APP_LAUNCH_WAIT_TIME_MS)
164     }
165 
166     /**
167      * Launches the app through an intent instead of interacting with the launcher and waits
168      * until the app window is visible
169      */
170     @JvmOverloads
launchViaIntentnull171     open fun launchViaIntent(
172         wmHelper: WindowManagerStateHelper,
173         expectedWindowName: String = "",
174         action: String? = null,
175         stringExtras: Map<String, String> = mapOf()
176     ) = launchViaIntentAndWaitShown(wmHelper, expectedWindowName, action, stringExtras)
177 
178     /**
179      * Launches the app through an intent instead of interacting with the launcher and waits
180      * until the app window is visible
181      */
182     protected fun launchViaIntentAndWaitShown(
183         wmHelper: WindowManagerStateHelper,
184         expectedWindowName: String = "",
185         action: String? = null,
186         stringExtras: Map<String, String> = mapOf(),
187         waitConditions: Array<
188             Condition<DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>>> =
189             emptyArray()
190     ) {
191         launchAppViaIntent(action, stringExtras)
192 
193         val expectedWindow = if (expectedWindowName.isNotEmpty()) {
194             FlickerComponentName("", expectedWindowName)
195         } else {
196             component
197         }
198         val appShown = wmHelper.waitFor(
199             WindowManagerConditionsFactory.isWMStateComplete(),
200             WindowManagerConditionsFactory.hasLayersAnimating().negate(),
201             WindowManagerConditionsFactory.isWindowVisible(expectedWindow),
202             *waitConditions
203         )
204         require(appShown) { "App didn't launch correctly via intent" }
205 
206         wmHelper.waitForAppTransitionIdle()
207         // During seamless rotation the app window is shown
208         val currWmState = wmHelper.currentState.wmState
209         if (currWmState.visibleWindows.none { it.isFullscreen }) {
210             wmHelper.waitForNavBarStatusBarVisible()
211         }
212 
213         // Ensure WindowManagerService wait until all animations have completed
214         mInstrumentation.uiAutomation.syncInputTransactions()
215     }
216 
217     companion object {
218         private const val APP_LAUNCH_WAIT_TIME_MS = 10000L
219     }
220 }
221