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