blob: 18960401d970676ba414dd545e7e9d0584e5547c [file] [log] [blame]
/*
* Copyright (C) 2020 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.wm.flicker.helpers
import android.app.ActivityManager
import android.app.Instrumentation
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.platform.helpers.AbstractStandardAppHelper
import androidx.test.uiautomator.By
import androidx.test.uiautomator.BySelector
import androidx.test.uiautomator.UiDevice
import androidx.test.uiautomator.Until
import com.android.launcher3.tapl.LauncherInstrumentation
import com.android.server.wm.traces.common.Condition
import com.android.server.wm.traces.common.DeviceStateDump
import com.android.server.wm.traces.common.WindowManagerConditionsFactory
import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher
import com.android.server.wm.traces.common.component.matchers.IComponentMatcher
import com.android.server.wm.traces.common.component.matchers.IComponentNameMatcher
import com.android.server.wm.traces.common.windowmanager.WindowManagerState
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import com.android.server.wm.traces.parser.withPerfettoTrace
/**
* Class to take advantage of {@code IAppHelper} interface so the same test can be run against first
* party and third party apps.
*/
open class StandardAppHelper(
instr: Instrumentation,
val appName: String,
val componentMatcher: ComponentNameMatcher
) : AbstractStandardAppHelper(instr), IComponentNameMatcher by componentMatcher {
constructor(
instr: Instrumentation,
appName: String,
packageName: String,
activity: String
) : this(instr, appName, ComponentNameMatcher(packageName, ".$activity"))
protected val pkgManager: PackageManager = instr.context.packageManager
protected val tapl: LauncherInstrumentation = LauncherInstrumentation()
private val activityManager: ActivityManager?
get() = mInstrumentation.context.getSystemService(ActivityManager::class.java)
protected val context: Context
get() = mInstrumentation.context
override val packageName: String = componentMatcher.packageName
override val className: String = componentMatcher.className
protected val uiDevice: UiDevice = UiDevice.getInstance(mInstrumentation)
private fun getAppSelector(expectedPackageName: String): BySelector {
val expected = expectedPackageName.ifEmpty { packageName }
return By.pkg(expected).depth(0)
}
override fun open() {
open(`package`)
}
protected fun open(expectedPackageName: String) {
tapl.goHome().switchToAllApps().getAppIcon(launcherName).launch(expectedPackageName)
}
/** {@inheritDoc} */
override fun getPackage(): String {
return packageName
}
/** {@inheritDoc} */
override fun getOpenAppIntent(): Intent {
val intent = Intent()
intent.addCategory(Intent.CATEGORY_LAUNCHER)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
intent.component = ComponentName(packageName, className)
return intent
}
/** {@inheritDoc} */
override fun getLauncherName(): String {
return appName
}
/** {@inheritDoc} */
override fun dismissInitialDialogs() {}
/** {@inheritDoc} */
override fun exit() {
withPerfettoTrace("exit") {
// Ensure all testing components end up being closed.
activityManager?.forceStopPackage(packageName)
}
}
/** Exits the activity and wait for activity destroyed */
fun exit(wmHelper: WindowManagerStateHelper) {
withPerfettoTrace("${this::class.simpleName}#exitAndWait") {
exit()
waitForActivityDestroyed(wmHelper)
}
}
/** Waits the activity until state change to {link WindowManagerState.STATE_DESTROYED} */
private fun waitForActivityDestroyed(wmHelper: WindowManagerStateHelper) {
val waitMsg =
"state of ${componentMatcher.toActivityIdentifier()} to be " +
WindowManagerState.STATE_DESTROYED
wmHelper
.StateSyncBuilder()
.add(waitMsg) {
!it.wmState.containsActivity(componentMatcher) ||
it.wmState.hasActivityState(
componentMatcher,
WindowManagerState.STATE_DESTROYED
)
}
.withAppTransitionIdle()
.waitForAndVerify()
}
private fun launchAppViaIntent(
action: String? = null,
stringExtras: Map<String, String> = mapOf()
) {
withPerfettoTrace("${this::class.simpleName}#launchAppViaIntent") {
val intent = openAppIntent
intent.action = action
stringExtras.forEach { intent.putExtra(it.key, it.value) }
context.startActivity(intent)
}
}
/**
* Launches the app through an intent instead of interacting with the launcher.
*
* Uses UiAutomation to detect when the app is open
*/
@JvmOverloads
open fun launchViaIntent(
expectedPackageName: String = "",
action: String? = null,
stringExtras: Map<String, String> = mapOf()
) {
launchAppViaIntent(action, stringExtras)
val appSelector = getAppSelector(expectedPackageName)
uiDevice.wait(Until.hasObject(appSelector), APP_LAUNCH_WAIT_TIME_MS)
}
/**
* Launches the app through an intent instead of interacting with the launcher and waits until
* the app window is visible
*/
@JvmOverloads
open fun launchViaIntent(
wmHelper: WindowManagerStateHelper,
launchedAppComponentMatcherOverride: IComponentMatcher? = null,
action: String? = null,
stringExtras: Map<String, String> = mapOf(),
waitConditions: Array<Condition<DeviceStateDump>> = emptyArray()
) =
launchViaIntentAndWaitShown(
wmHelper,
launchedAppComponentMatcherOverride,
action,
stringExtras,
waitConditions
)
/**
* Launches the app through an intent instead of interacting with the launcher and waits until
* the app window is visible
*/
protected fun launchViaIntentAndWaitShown(
wmHelper: WindowManagerStateHelper,
launchedAppComponentMatcherOverride: IComponentMatcher? = null,
action: String? = null,
stringExtras: Map<String, String> = mapOf(),
waitConditions: Array<Condition<DeviceStateDump>> = emptyArray()
) {
launchAppViaIntent(action, stringExtras)
doWaitShown(wmHelper, launchedAppComponentMatcherOverride, waitConditions)
}
private fun doWaitShown(
wmHelper: WindowManagerStateHelper,
launchedAppComponentMatcherOverride: IComponentMatcher? = null,
waitConditions: Array<Condition<DeviceStateDump>> = emptyArray()
) {
withPerfettoTrace("${this::class.simpleName}#doWaitShown") {
val expectedWindow = launchedAppComponentMatcherOverride ?: componentMatcher
val builder =
wmHelper
.StateSyncBuilder()
.add(WindowManagerConditionsFactory.isWMStateComplete())
.withAppTransitionIdle()
.withWindowSurfaceAppeared(expectedWindow)
waitConditions.forEach { builder.add(it) }
builder.waitForAndVerify()
// During seamless rotation the app window is shown
val currWmState = wmHelper.currentState.wmState
if (currWmState.visibleWindows.none { it.isFullscreen }) {
wmHelper
.StateSyncBuilder()
.withNavOrTaskBarVisible()
.withStatusBarVisible()
.waitForAndVerify()
}
}
}
fun isAvailable(): Boolean {
return try {
pkgManager.getPackageInfo(packageName, PackageManager.PackageInfoFlags.of(0))
true
} catch (e: PackageManager.NameNotFoundException) {
false
}
}
companion object {
private const val APP_LAUNCH_WAIT_TIME_MS = 10000L
}
}