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