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