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 android.systemui.cts.tv 18 19 import android.Manifest.permission.FORCE_STOP_PACKAGES 20 import android.app.ActivityManager 21 import android.app.IActivityManager 22 import android.app.IProcessObserver 23 import android.app.Instrumentation 24 import android.content.ComponentName 25 import android.content.Context 26 import android.content.pm.PackageManager 27 import android.content.pm.PackageManager.FEATURE_LEANBACK 28 import android.content.pm.PackageManager.FEATURE_LEANBACK_ONLY 29 import android.os.SystemClock 30 import android.server.wm.UiDeviceUtils 31 import android.server.wm.WindowManagerStateHelper 32 import android.systemui.tv.cts.ResourceNames.SYSTEM_UI_PACKAGE 33 import android.util.Log 34 import androidx.test.platform.app.InstrumentationRegistry 35 import androidx.test.uiautomator.UiDevice 36 import com.android.compatibility.common.util.SystemUtil 37 import org.junit.After 38 import org.junit.Assert.assertFalse 39 import org.junit.Assume.assumeTrue 40 import org.junit.Before 41 import java.io.IOException 42 43 abstract class TvTestBase { 44 companion object { 45 private const val TAG = "TvTestBase" 46 private const val AFTER_TEST_PROCESS_CHECK_DELAY = 1_000L // 1 sec 47 } 48 49 protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() 50 protected val uiDevice: UiDevice = UiDevice.getInstance(instrumentation) 51 protected val context: Context = instrumentation.context 52 protected val packageManager: PackageManager = context.packageManager 53 ?: error("Could not get a PackageManager") 54 protected val activityManager: ActivityManager = 55 context.getSystemService(ActivityManager::class.java) 56 ?: error("Could not get a ActivityManager") 57 protected val wmState: WindowManagerStateHelper = WindowManagerStateHelper() 58 private val isTelevision: Boolean <lambda>null59 get() = packageManager.run { 60 hasSystemFeature(FEATURE_LEANBACK) || hasSystemFeature(FEATURE_LEANBACK_ONLY) 61 } 62 private val systemUiProcessObserver = SystemUiProcessObserver() 63 64 @Before setUpnull65 fun setUp() { 66 assumeTrue(isTelevision) 67 68 systemUiProcessObserver.start() 69 70 UiDeviceUtils.pressWakeupButton() 71 UiDeviceUtils.pressUnlockButton() 72 73 onSetUp() 74 } 75 76 @After tearDownnull77 fun tearDown() { 78 if (!isTelevision) return 79 80 onTearDown() 81 82 SystemClock.sleep(AFTER_TEST_PROCESS_CHECK_DELAY) 83 systemUiProcessObserver.stop() 84 assertFalse("SystemUI has died during test execution", systemUiProcessObserver.hasDied) 85 } 86 onSetUpnull87 abstract fun onSetUp() 88 89 abstract fun onTearDown() 90 91 protected fun launchActivity( 92 activity: ComponentName? = null, 93 action: String? = null, 94 flags: Set<Int> = setOf(), 95 boolExtras: Map<String, Boolean> = mapOf(), 96 intExtras: Map<String, Int> = mapOf(), 97 stringExtras: Map<String, String> = mapOf() 98 ) { 99 require(activity != null || !action.isNullOrBlank()) { 100 "Cannot launch an activity with neither activity name nor action!" 101 } 102 val command = composeAmShellCommand( 103 "start", activity, action, flags, boolExtras, intExtras, stringExtras) 104 executeShellCommand(command) 105 } 106 startForegroundServicenull107 protected fun startForegroundService( 108 service: ComponentName, 109 action: String? = null 110 ) { 111 val command = composeAmShellCommand("start-foreground-service", service, action) 112 executeShellCommand(command) 113 } 114 sendBroadcastnull115 protected fun sendBroadcast( 116 action: String, 117 flags: Set<Int> = setOf(), 118 boolExtras: Map<String, Boolean> = mapOf(), 119 intExtras: Map<String, Int> = mapOf(), 120 stringExtras: Map<String, String> = mapOf() 121 ) { 122 val command = composeAmShellCommand( 123 "broadcast", null, action, flags, boolExtras, intExtras, stringExtras) 124 executeShellCommand(command) 125 } 126 stopPackagenull127 protected fun stopPackage(packageName: String) { 128 SystemUtil.runWithShellPermissionIdentity({ 129 activityManager.forceStopPackage(packageName) 130 }, FORCE_STOP_PACKAGES) 131 } 132 composeAmShellCommandnull133 private fun composeAmShellCommand( 134 command: String, 135 component: ComponentName?, 136 action: String? = null, 137 flags: Set<Int> = setOf(), 138 boolExtras: Map<String, Boolean> = mapOf(), 139 intExtras: Map<String, Int> = mapOf(), 140 stringExtras: Map<String, String> = mapOf() 141 ): String = buildString { 142 append("am ") 143 append(command) 144 component?.let { 145 append(" -n ") 146 append(it.flattenToShortString()) 147 } 148 action?.let { 149 append(" -a ") 150 append(it) 151 } 152 flags.forEach { 153 append(" -f ") 154 append(it) 155 } 156 boolExtras.forEach { 157 append(it.withFlag("ez")) 158 } 159 intExtras.forEach { 160 append(it.withFlag("ei")) 161 } 162 stringExtras.forEach { 163 append(it.withFlag("es")) 164 } 165 } 166 withFlagnull167 private fun Map.Entry<String, *>.withFlag(flag: String): String = " --$flag $key $value" 168 169 protected fun executeShellCommand(cmd: String): String { 170 try { 171 return SystemUtil.runShellCommand(instrumentation, cmd) 172 } catch (e: IOException) { 173 Log.e(TAG, "Error running shell command: $cmd") 174 throw e 175 } 176 } 177 178 inner class SystemUiProcessObserver : IProcessObserver.Stub() { 179 private val activityManager: IActivityManager = ActivityManager.getService() 180 private val uiAutomation = instrumentation.uiAutomation 181 private val systemUiUid = packageManager.getPackageUid(SYSTEM_UI_PACKAGE, 0) 182 var hasDied: Boolean = false 183 startnull184 fun start() { 185 hasDied = false 186 uiAutomation.adoptShellPermissionIdentity( 187 android.Manifest.permission.SET_ACTIVITY_WATCHER) 188 activityManager.registerProcessObserver(this) 189 } 190 stopnull191 fun stop() { 192 activityManager.unregisterProcessObserver(this) 193 uiAutomation.dropShellPermissionIdentity() 194 } 195 onForegroundActivitiesChangednull196 override fun onForegroundActivitiesChanged(pid: Int, uid: Int, foreground: Boolean) {} 197 onForegroundServicesChangednull198 override fun onForegroundServicesChanged(pid: Int, uid: Int, serviceTypes: Int) {} 199 onProcessDiednull200 override fun onProcessDied(pid: Int, uid: Int) { 201 if (uid == systemUiUid) hasDied = true 202 } 203 } 204 }