1 /* 2 * Copyright (C) 2021 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.app.cts 18 19 import android.Manifest.permission.STATUS_BAR 20 import android.app.Activity 21 import android.app.ActivityManager 22 import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND 23 import android.app.Instrumentation 24 import android.app.StatusBarManager 25 import android.app.stubs.MockActivity 26 import android.app.stubs.NotExportedTestTileService 27 import android.app.stubs.TestTileService 28 import android.content.ComponentName 29 import android.content.Context 30 import android.content.Intent 31 import android.content.pm.PackageManager 32 import android.graphics.drawable.Icon 33 import android.permission.cts.PermissionUtils 34 import android.service.quicksettings.TileService 35 import androidx.test.InstrumentationRegistry 36 import androidx.test.ext.junit.runners.AndroidJUnit4 37 import com.android.compatibility.common.util.SystemUtil 38 import com.google.common.truth.Truth.assertThat 39 import com.google.common.util.concurrent.MoreExecutors 40 import org.junit.Assume 41 import org.junit.Before 42 import org.junit.Test 43 import org.junit.runner.RunWith 44 import java.util.concurrent.CountDownLatch 45 import java.util.concurrent.TimeUnit 46 import java.util.function.Consumer 47 48 /** 49 * Test that the request fails in all the expected ways. 50 * 51 * These tests are for [StatusBarManager.requestAddTileService]. 52 */ 53 @RunWith(AndroidJUnit4::class) 54 class RequestTileServiceAddTest { 55 56 companion object { 57 private const val LABEL = "label" 58 private const val TIME_OUT_SECONDS = 5L 59 } 60 61 private lateinit var statusBarService: StatusBarManager 62 private lateinit var context: Context 63 private lateinit var icon: Icon 64 private lateinit var consumer: StoreIntConsumer 65 private lateinit var instrumentation: Instrumentation 66 private val executor = MoreExecutors.directExecutor() 67 private lateinit var latch: CountDownLatch 68 69 @Before setUpnull70 fun setUp() { 71 Assume.assumeTrue(TileService.isQuickSettingsSupported()) 72 73 instrumentation = InstrumentationRegistry.getInstrumentation() 74 context = instrumentation.getTargetContext() 75 statusBarService = context.getSystemService(StatusBarManager::class.java)!! 76 77 icon = Icon.createWithResource(context, R.drawable.ic_android) 78 latch = CountDownLatch(1) 79 consumer = StoreIntConsumer(latch) 80 } 81 82 @Test testRequestBadPackageFailsnull83 fun testRequestBadPackageFails() { 84 val componentName = ComponentName("test_pkg", "test_cls") 85 86 statusBarService.requestAddTileService( 87 componentName, 88 LABEL, 89 icon, 90 executor, 91 consumer 92 ) 93 94 latch.await(TIME_OUT_SECONDS, TimeUnit.SECONDS) 95 96 assertThat(consumer.result) 97 .isEqualTo(StatusBarManager.TILE_ADD_REQUEST_ERROR_MISMATCHED_PACKAGE) 98 } 99 100 @Test testRequestBadComponentNamenull101 fun testRequestBadComponentName() { 102 val componentName = ComponentName(context, "test_cls") 103 statusBarService.requestAddTileService( 104 componentName, 105 LABEL, 106 icon, 107 executor, 108 consumer 109 ) 110 111 latch.await(TIME_OUT_SECONDS, TimeUnit.SECONDS) 112 113 assertThat(consumer.result).isEqualTo(StatusBarManager.TILE_ADD_REQUEST_ERROR_BAD_COMPONENT) 114 } 115 116 @Test testDisabledComponentnull117 fun testDisabledComponent() { 118 val componentName = TestTileService.getComponentName() 119 context.packageManager.setComponentEnabledSetting( 120 componentName, 121 PackageManager.COMPONENT_ENABLED_STATE_DISABLED, 122 PackageManager.SYNCHRONOUS or PackageManager.DONT_KILL_APP 123 ) 124 125 statusBarService.requestAddTileService( 126 componentName, 127 LABEL, 128 icon, 129 executor, 130 consumer 131 ) 132 133 latch.await(TIME_OUT_SECONDS, TimeUnit.SECONDS) 134 135 assertThat(consumer.result).isEqualTo(StatusBarManager.TILE_ADD_REQUEST_ERROR_BAD_COMPONENT) 136 137 // Cleanup 138 context.packageManager.setComponentEnabledSetting( 139 componentName, 140 PackageManager.COMPONENT_ENABLED_STATE_DEFAULT, 141 PackageManager.SYNCHRONOUS or PackageManager.DONT_KILL_APP 142 ) 143 } 144 145 @Test testNotExportedComponentnull146 fun testNotExportedComponent() { 147 val componentName = NotExportedTestTileService.getComponentName() 148 149 statusBarService.requestAddTileService( 150 componentName, 151 LABEL, 152 icon, 153 executor, 154 consumer 155 ) 156 157 latch.await(TIME_OUT_SECONDS, TimeUnit.SECONDS) 158 159 assertThat(consumer.result).isEqualTo(StatusBarManager.TILE_ADD_REQUEST_ERROR_BAD_COMPONENT) 160 } 161 162 @Test testAppNotInForegroundnull163 fun testAppNotInForeground() { 164 // This test is never run in foreground, so it's a good candidate for testing this 165 val componentName = TestTileService.getComponentName() 166 val activityManager = context.getSystemService(ActivityManager::class.java)!! 167 withPermission(context.packageName, android.Manifest.permission.PACKAGE_USAGE_STATS) { 168 assertThat(activityManager.getPackageImportance(context.packageName)) 169 .isNotEqualTo(IMPORTANCE_FOREGROUND) 170 } 171 172 statusBarService.requestAddTileService( 173 componentName, 174 LABEL, 175 icon, 176 executor, 177 consumer 178 ) 179 180 latch.await(TIME_OUT_SECONDS, TimeUnit.SECONDS) 181 182 assertThat(consumer.result) 183 .isEqualTo(StatusBarManager.TILE_ADD_REQUEST_ERROR_APP_NOT_IN_FOREGROUND) 184 } 185 186 @Test testTwoSimultaneousRequestsnull187 fun testTwoSimultaneousRequests() { 188 // We need an activity in the foreground for the first request to not be denied 189 val activity = setUpForActivity() 190 val componentName = TestTileService.getComponentName() 191 192 statusBarService.requestAddTileService( 193 componentName, 194 LABEL, 195 icon, 196 executor, 197 {} 198 ) 199 200 Thread.sleep(TimeUnit.SECONDS.toMillis(TIME_OUT_SECONDS)) 201 202 statusBarService.requestAddTileService( 203 componentName, 204 LABEL, 205 icon, 206 executor, 207 consumer 208 ) 209 210 latch.await(TIME_OUT_SECONDS, TimeUnit.SECONDS) 211 assertThat(consumer.result) 212 .isEqualTo(StatusBarManager.TILE_ADD_REQUEST_ERROR_REQUEST_IN_PROGRESS) 213 214 SystemUtil.callWithShellPermissionIdentity( 215 { statusBarService.cancelRequestAddTile(componentName.packageName) }, 216 STATUS_BAR 217 ) 218 219 activity.finish() 220 } 221 withPermissionnull222 private inline fun withPermission(packageName: String, permission: String, block: () -> Unit) { 223 val isPermissionGranted = PermissionUtils.isPermissionGranted(packageName, permission) 224 try { 225 PermissionUtils.grantPermission(packageName, permission) 226 block() 227 } finally { 228 if (!isPermissionGranted) { 229 PermissionUtils.revokePermission(packageName, permission) 230 } 231 } 232 } 233 setUpForActivitynull234 private fun setUpForActivity(): Activity { 235 val intent = Intent(context, MockActivity::class.java) 236 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) 237 val activity = instrumentation.startActivitySync(intent) 238 instrumentation.waitForIdleSync() 239 return activity 240 } 241 242 private class StoreIntConsumer(private val latch: CountDownLatch) : Consumer<Int> { 243 var result: Int? = null 244 acceptnull245 override fun accept(t: Int) { 246 result = t 247 latch.countDown() 248 } 249 } 250 }