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 com.android.server.pm.test.override 18 19 import android.app.PropertyInvalidatedCache 20 import android.content.ComponentName 21 import android.content.Context 22 import android.content.pm.ApplicationInfo 23 import android.content.pm.PackageManager 24 import com.android.server.pm.pkg.component.ParsedActivity 25 import android.os.Binder 26 import android.os.UserHandle 27 import android.util.ArrayMap 28 import com.android.server.pm.* 29 import com.android.server.pm.parsing.pkg.AndroidPackage 30 import com.android.server.pm.parsing.pkg.PackageImpl 31 import com.android.server.pm.parsing.pkg.ParsedPackage 32 import com.android.server.pm.resolution.ComponentResolver 33 import com.android.server.pm.snapshot.PackageDataSnapshot 34 import com.android.server.pm.test.override.PackageManagerComponentLabelIconOverrideTest.Companion.Params.AppType 35 import com.android.server.testutils.TestHandler 36 import com.android.server.testutils.mock 37 import com.android.server.testutils.mockThrowOnUnmocked 38 import com.android.server.testutils.whenever 39 import com.android.server.wm.ActivityTaskManagerInternal 40 import com.google.common.truth.Truth.assertThat 41 import org.junit.After 42 import org.junit.Before 43 import org.junit.BeforeClass 44 import org.junit.Test 45 import org.junit.runner.RunWith 46 import org.junit.runners.Parameterized 47 import org.mockito.Mockito.any 48 import org.mockito.Mockito.anyInt 49 import org.mockito.Mockito.doReturn 50 import org.mockito.Mockito.intThat 51 import org.mockito.Mockito.same 52 import org.testng.Assert.assertThrows 53 import java.io.File 54 import java.util.UUID 55 56 @RunWith(Parameterized::class) 57 class PackageManagerComponentLabelIconOverrideTest { 58 59 companion object { 60 private const val VALID_PKG = "com.android.server.pm.test.override" 61 private const val SHARED_PKG = "com.android.server.pm.test.override.shared" 62 private const val INVALID_PKG = "com.android.server.pm.test.override.invalid" 63 private const val NON_EXISTENT_PKG = "com.android.server.pm.test.override.nonexistent" 64 65 private const val SEND_PENDING_BROADCAST = 1 // PackageManagerService.SEND_PENDING_BROADCAST 66 67 private const val DEFAULT_LABEL = "DefaultLabel" 68 private const val TEST_LABEL = "TestLabel" 69 70 private const val DEFAULT_ICON = R.drawable.black16x16 71 private const val TEST_ICON = R.drawable.white16x16 72 73 private const val COMPONENT_CLASS_NAME = ".TestComponent" 74 75 sealed class Result { 76 // Component label/icon changed, message sent to send broadcast 77 object Changed : Result() 78 79 // Component label/icon changed, message was pending, not re-sent 80 object ChangedWithoutNotify : Result() 81 82 // Component label/icon did not changed, was already equivalent 83 object NotChanged : Result() 84 85 // Updating label/icon encountered a specific exception 86 data class Exception(val type: Class<out java.lang.Exception>) : Result() 87 } 88 89 @Parameterized.Parameters(name = "{0}") 90 @JvmStatic 91 fun parameters() = arrayOf( 92 // Start with an array of the simplest known inputs and expected outputs 93 Params(VALID_PKG, AppType.SYSTEM_APP, Result.Changed), 94 Params(SHARED_PKG, AppType.SYSTEM_APP, Result.Changed), 95 Params(INVALID_PKG, AppType.SYSTEM_APP, SecurityException::class.java), 96 Params(NON_EXISTENT_PKG, AppType.SYSTEM_APP, SecurityException::class.java) 97 ) 98 .flatMap { param -> 99 mutableListOf(param).apply { 100 if (param.result is Result.Changed) { 101 // For each param that would've succeeded, also verify that if a change 102 // happened, but a message was pending, another is not re-queued/reset 103 this += param.copy(result = Result.ChangedWithoutNotify) 104 // Also verify that when the component is already configured, no change 105 // is propagated 106 this += param.copy(result = Result.NotChanged) 107 } 108 // For all params, verify that an invalid component will cause an 109 // IllegalArgumentException, instead of result initially specified 110 this += param.copy(componentName = null, 111 result = Result.Exception(IllegalArgumentException::class.java)) 112 // Also verify an updated system app variant, which should have the same 113 // result as a vanilla system app 114 this += param.copy(appType = AppType.UPDATED_SYSTEM_APP) 115 // Also verify a non-system app will cause a failure, since normal apps 116 // are not allowed to edit their label/icon 117 this += param.copy(appType = AppType.NORMAL_APP, 118 result = Result.Exception(SecurityException::class.java)) 119 } 120 } 121 122 @BeforeClass 123 @JvmStatic 124 fun disablePropertyInvalidatedCache() { 125 // Disable binder caches in this process. 126 PropertyInvalidatedCache.disableForTestMode() 127 } 128 129 data class Params( 130 val pkgName: String, 131 private val appType: AppType, 132 val result: Result, 133 val componentName: ComponentName? = ComponentName(pkgName, COMPONENT_CLASS_NAME) 134 ) { 135 constructor(pkgName: String, appType: AppType, exception: Class<out Exception>) 136 : this(pkgName, appType, Result.Exception(exception)) 137 138 val expectedLabel = when (result) { 139 Result.Changed, Result.ChangedWithoutNotify, Result.NotChanged -> TEST_LABEL 140 is Result.Exception -> DEFAULT_LABEL 141 } 142 143 val expectedIcon = when (result) { 144 Result.Changed, Result.ChangedWithoutNotify, Result.NotChanged -> TEST_ICON 145 is Result.Exception -> DEFAULT_ICON 146 } 147 148 val isUpdatedSystemApp = appType == AppType.UPDATED_SYSTEM_APP 149 val isSystem = appType == AppType.SYSTEM_APP || isUpdatedSystemApp 150 151 override fun toString(): String { 152 val resultString = when (result) { 153 Result.Changed -> "Changed" 154 Result.ChangedWithoutNotify -> "ChangedWithoutNotify" 155 Result.NotChanged -> "NotChanged" 156 is Result.Exception -> result.type.simpleName 157 } 158 159 // Nicer formatting for the test method suffix 160 return "pkg=$pkgName, type=$appType, component=$componentName, result=$resultString" 161 } 162 163 enum class AppType { SYSTEM_APP, UPDATED_SYSTEM_APP, NORMAL_APP } 164 } 165 } 166 167 @Parameterized.Parameter(0) 168 lateinit var params: Params 169 170 private lateinit var mockPendingBroadcasts: PendingPackageBroadcasts 171 private lateinit var mockPkg: AndroidPackage 172 private lateinit var mockPkgSetting: PackageSetting 173 private lateinit var service: PackageManagerService 174 175 private val testHandler = TestHandler(null) 176 private val userId = UserHandle.getCallingUserId() 177 private val userIdDifferent = userId + 1 178 179 @Before 180 fun setUpMocks() { 181 makeTestData() 182 183 mockPendingBroadcasts = PendingPackageBroadcasts() 184 service = mockService() 185 186 testHandler.clear() 187 188 if (params.result is Result.ChangedWithoutNotify) { 189 // Case where the handler already has a message and so another should not be sent. 190 // This case will verify that only 1 message exists, which is the one added here. 191 testHandler.sendEmptyMessage(SEND_PENDING_BROADCAST) 192 } 193 } 194 195 @Test 196 fun updateComponentLabelIcon() { 197 fun runUpdate() { 198 service.updateComponentLabelIcon(params.componentName, TEST_LABEL, TEST_ICON, userId) 199 } 200 201 when (val result = params.result) { 202 Result.Changed, Result.ChangedWithoutNotify, Result.NotChanged -> { 203 runUpdate() 204 mockPkgSetting.getUserStateOrDefault(userId) 205 .getOverrideLabelIconForComponent(params.componentName!!) 206 .let { 207 assertThat(it?.first).isEqualTo(TEST_LABEL) 208 assertThat(it?.second).isEqualTo(TEST_ICON) 209 } 210 } 211 is Result.Exception -> { 212 assertThrows(result.type) { runUpdate() } 213 } 214 } 215 } 216 217 @After 218 fun verifyExpectedResult() { 219 assertServiceInitialized() ?: return 220 if (params.componentName != null && params.result !is Result.Exception) { 221 // Suppress so that failures in @After don't override the actual test failure 222 @Suppress("UNNECESSARY_SAFE_CALL") 223 service?.let { 224 val activityInfo = it.snapshotComputer() 225 .getActivityInfo(params.componentName, 0, userId) 226 assertThat(activityInfo?.nonLocalizedLabel).isEqualTo(params.expectedLabel) 227 assertThat(activityInfo?.icon).isEqualTo(params.expectedIcon) 228 } 229 } 230 } 231 232 @After 233 fun verifyDifferentUserUnchanged() { 234 assertServiceInitialized() ?: return 235 when (params.result) { 236 Result.Changed, Result.ChangedWithoutNotify -> { 237 // Suppress so that failures in @After don't override the actual test failure 238 @Suppress("UNNECESSARY_SAFE_CALL") 239 service?.let { 240 val activityInfo = it.snapshotComputer() 241 ?.getActivityInfo(params.componentName, 0, userIdDifferent) 242 assertThat(activityInfo?.nonLocalizedLabel).isEqualTo(DEFAULT_LABEL) 243 assertThat(activityInfo?.icon).isEqualTo(DEFAULT_ICON) 244 } 245 } 246 Result.NotChanged, is Result.Exception -> {} 247 }.run { /*exhaust*/ } 248 } 249 250 @After 251 fun verifyHandlerHasMessage() { 252 assertServiceInitialized() ?: return 253 when (params.result) { 254 is Result.Changed, is Result.ChangedWithoutNotify -> { 255 assertThat(testHandler.pendingMessages).hasSize(1) 256 assertThat(testHandler.pendingMessages.first().message.what) 257 .isEqualTo(SEND_PENDING_BROADCAST) 258 } 259 is Result.NotChanged, is Result.Exception -> { 260 assertThat(testHandler.pendingMessages).hasSize(0) 261 } 262 }.run { /*exhaust*/ } 263 } 264 265 @After 266 fun verifyPendingBroadcast() { 267 assertServiceInitialized() ?: return 268 when (params.result) { 269 is Result.Changed, Result.ChangedWithoutNotify -> { 270 assertThat(mockPendingBroadcasts.copiedMap()?.get(userId)?.get(params.pkgName) 271 ?: emptyList<String>()) 272 .containsExactly(params.componentName!!.className) 273 .inOrder() 274 } 275 is Result.NotChanged, is Result.Exception -> { 276 assertThat(mockPendingBroadcasts.copiedMap()?.get(userId)?.get(params.pkgName)) 277 .isNull() 278 } 279 }.run { /*exhaust*/ } 280 } 281 282 private fun makePkg(pkgName: String, block: ParsedPackage.() -> Unit = {}) = 283 PackageImpl.forTesting(pkgName) 284 .setEnabled(true) 285 .let { it.hideAsParsed() as ParsedPackage } 286 .setSystem(params.isSystem) 287 .apply(block) 288 .hideAsFinal() 289 290 private fun makePkgSetting(pkgName: String, pkg: AndroidPackage) = 291 PackageSetting( 292 pkgName, null, File("/test"), 293 null, null, null, null, 0, 0, 0, 0, null, null, null, null, null, 294 UUID.fromString("3f9d52b7-d7b4-406a-a1da-d9f19984c72c") 295 ).apply { 296 if (params.isSystem) { 297 this.flags = this.flags or ApplicationInfo.FLAG_SYSTEM 298 } 299 this.pkgState.isUpdatedSystemApp = params.isUpdatedSystemApp 300 this.pkg = pkg 301 } 302 303 private fun makeTestData() { 304 mockPkg = makePkg(params.pkgName) 305 mockPkgSetting = makePkgSetting(params.pkgName, mockPkg) 306 307 if (params.result is Result.NotChanged) { 308 // If verifying no-op behavior, set the current setting to the test values 309 mockPkgSetting.overrideNonLocalizedLabelAndIcon(params.componentName!!, TEST_LABEL, 310 TEST_ICON, userId) 311 } 312 } 313 314 private fun mockService(): PackageManagerService { 315 val mockedPkgs = mapOf( 316 // Must use the test app's UID so that PMS can match them when querying, since 317 // the static Binder.getCallingUid can't mocked as it's marked final 318 VALID_PKG to makePkg(VALID_PKG) { uid = Binder.getCallingUid() }, 319 SHARED_PKG to makePkg(SHARED_PKG) { uid = Binder.getCallingUid() }, 320 INVALID_PKG to makePkg(INVALID_PKG) { uid = Binder.getCallingUid() + 1 } 321 ) 322 val mockedPkgSettings = mutableMapOf( 323 VALID_PKG to makePkgSetting(VALID_PKG, mockedPkgs[VALID_PKG]!!), 324 SHARED_PKG to makePkgSetting(SHARED_PKG, mockedPkgs[SHARED_PKG]!!), 325 INVALID_PKG to makePkgSetting(INVALID_PKG, mockedPkgs[INVALID_PKG]!!) 326 ) 327 328 var mockActivity: ParsedActivity? = null 329 if (mockedPkgSettings.containsKey(params.pkgName)) { 330 // Add pkgSetting under test so its attributes override the defaults added above 331 mockedPkgSettings.put(params.pkgName, mockPkgSetting) 332 333 mockActivity = mock<ParsedActivity> { 334 whenever(this.packageName) { params.pkgName } 335 whenever(this.nonLocalizedLabel) { DEFAULT_LABEL } 336 whenever(this.icon) { DEFAULT_ICON } 337 whenever(this.componentName) { params.componentName } 338 whenever(this.name) { params.componentName?.className } 339 whenever(this.isEnabled) { true } 340 whenever(this.isDirectBootAware) { params.isSystem } 341 } 342 } 343 344 val mockSettings = Settings(mockedPkgSettings) 345 val mockComponentResolver: ComponentResolver = mockThrowOnUnmocked { 346 params.componentName?.let { 347 doReturn(mockActivity != null).`when`(this).componentExists(same(it)) 348 doReturn(mockActivity).`when`(this).getActivity(same(it)) 349 } 350 whenever(this.snapshot()) { this@mockThrowOnUnmocked } 351 whenever(registerObserver(any())).thenCallRealMethod() 352 } 353 val mockUserManagerService: UserManagerService = mockThrowOnUnmocked { 354 val matcher: (Int) -> Boolean = { it == userId || it == userIdDifferent } 355 whenever(this.exists(intThat(matcher))) { true } 356 } 357 val mockUserManagerInternal: UserManagerInternal = mockThrowOnUnmocked { 358 val matcher: (Int) -> Boolean = { it == userId || it == userIdDifferent } 359 whenever(this.isUserUnlockingOrUnlocked(intThat(matcher))) { true } 360 } 361 val mockActivityTaskManager: ActivityTaskManagerInternal = mockThrowOnUnmocked { 362 whenever(this.isCallerRecents(anyInt())) { false } 363 } 364 val mockAppsFilter: AppsFilterImpl = mockThrowOnUnmocked { 365 whenever(this.shouldFilterApplication(any<PackageDataSnapshot>(), anyInt(), 366 any<PackageSetting>(), any<PackageSetting>(), anyInt())) { false } 367 whenever(this.snapshot()) { this@mockThrowOnUnmocked } 368 whenever(registerObserver(any())).thenCallRealMethod() 369 } 370 val mockContext: Context = mockThrowOnUnmocked { 371 whenever(this.getString( 372 com.android.internal.R.string.config_overrideComponentUiPackage)) { VALID_PKG } 373 whenever(this.checkCallingOrSelfPermission( 374 android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)) { 375 PackageManager.PERMISSION_GRANTED 376 } 377 } 378 val mockSharedLibrariesImpl: SharedLibrariesImpl = mock { 379 whenever(this.snapshot()) { this@mock } 380 } 381 val mockInjector: PackageManagerServiceInjector = mock { 382 whenever(this.lock) { PackageManagerTracedLock() } 383 whenever(this.componentResolver) { mockComponentResolver } 384 whenever(this.userManagerService) { mockUserManagerService } 385 whenever(this.userManagerInternal) { mockUserManagerInternal } 386 whenever(this.settings) { mockSettings } 387 whenever(this.getLocalService(ActivityTaskManagerInternal::class.java)) { 388 mockActivityTaskManager 389 } 390 whenever(this.appsFilter) { mockAppsFilter } 391 whenever(this.context) { mockContext } 392 whenever(this.handler) { testHandler } 393 whenever(this.sharedLibrariesImpl) { mockSharedLibrariesImpl } 394 } 395 val testParams = PackageManagerServiceTestParams().apply { 396 this.pendingPackageBroadcasts = mockPendingBroadcasts 397 this.resolveComponentName = ComponentName("android", ".Test") 398 this.packages = ArrayMap<String, AndroidPackage>().apply { putAll(mockedPkgs) } 399 this.instantAppRegistry = mock() 400 } 401 402 return PackageManagerService(mockInjector, testParams) 403 } 404 405 // If service isn't initialized, then test setup failed and @Afters should be skipped 406 private fun assertServiceInitialized() = Unit.takeIf { ::service.isInitialized } 407 } 408