1 /* 2 * Copyright (C) 2019 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.om 18 19 import android.net.Uri 20 import com.android.server.pm.pkg.AndroidPackage 21 import com.android.server.testutils.mockThrowOnUnmocked 22 import com.android.server.testutils.whenever 23 import com.google.common.truth.Truth.assertThat 24 import org.junit.Before 25 import org.junit.Test 26 import org.junit.runner.RunWith 27 import org.junit.runners.Parameterized 28 29 @RunWith(Parameterized::class) 30 class OverlayReferenceMapperTests { 31 32 companion object { 33 private const val TARGET_PACKAGE_NAME = "com.test.target" 34 private const val OVERLAY_PACKAGE_NAME = "com.test.overlay" 35 private const val ACTOR_PACKAGE_NAME = "com.test.actor" 36 private const val ACTOR_NAME = "overlay://test/actorName" 37 38 @JvmStatic 39 @Parameterized.Parameters(name = "deferRebuild {0}") 40 fun parameters() = arrayOf(/*true, */false) 41 } 42 43 private lateinit var mapper: OverlayReferenceMapper 44 45 @JvmField 46 @Parameterized.Parameter(0) 47 var deferRebuild = false 48 49 @Before 50 fun initMapper() { 51 mapper = mapper() 52 } 53 54 @Test 55 fun targetWithOverlay() { 56 val target = mockTarget() 57 val overlay = mockOverlay() 58 val existing = mapper.addInOrder(overlay) { 59 assertThat(it).isEmpty() 60 } 61 assertEmpty() 62 mapper.addInOrder(target, existing = existing) { 63 assertThat(it).containsExactly(ACTOR_PACKAGE_NAME) 64 } 65 assertMapping(ACTOR_PACKAGE_NAME to setOf(target, overlay)) 66 mapper.remove(target) { 67 assertThat(it).containsExactly(ACTOR_PACKAGE_NAME) 68 } 69 assertEmpty() 70 } 71 72 @Test 73 fun targetWithMultipleOverlays() { 74 val target = mockTarget() 75 val overlay0 = mockOverlay(0) 76 val overlay1 = mockOverlay(1) 77 mapper = mapper( 78 overlayToTargetToOverlayables = mapOf( 79 overlay0.packageName to mapOf( 80 target.packageName to target.overlayables.keys 81 ), 82 overlay1.packageName to mapOf( 83 target.packageName to target.overlayables.keys 84 ) 85 ) 86 ) 87 val existing = mapper.addInOrder(overlay0, overlay1) { 88 assertThat(it).isEmpty() 89 } 90 assertEmpty() 91 mapper.addInOrder(target, existing = existing) { 92 assertThat(it).containsExactly(ACTOR_PACKAGE_NAME) 93 } 94 assertMapping(ACTOR_PACKAGE_NAME to setOf(target, overlay0, overlay1)) 95 mapper.remove(overlay0) { 96 assertThat(it).containsExactly(ACTOR_PACKAGE_NAME) 97 } 98 assertMapping(ACTOR_PACKAGE_NAME to setOf(target, overlay1)) 99 mapper.remove(target) { 100 assertThat(it).containsExactly(ACTOR_PACKAGE_NAME) 101 } 102 assertEmpty() 103 } 104 105 @Test 106 fun targetWithoutOverlay() { 107 val target = mockTarget() 108 mapper.addInOrder(target) { 109 assertThat(it).containsExactly(ACTOR_PACKAGE_NAME) 110 } 111 assertMapping(ACTOR_PACKAGE_NAME to setOf(target)) 112 mapper.remove(target) { 113 assertThat(it).containsExactly(ACTOR_PACKAGE_NAME) 114 } 115 assertEmpty() 116 } 117 118 @Test 119 fun overlayWithTarget() { 120 val target = mockTarget() 121 val overlay = mockOverlay() 122 val existing = mapper.addInOrder(target) { 123 assertThat(it).containsExactly(ACTOR_PACKAGE_NAME) 124 } 125 assertMapping(ACTOR_PACKAGE_NAME to setOf(target)) 126 mapper.addInOrder(overlay, existing = existing) { 127 assertThat(it).containsExactly(ACTOR_PACKAGE_NAME) 128 } 129 assertMapping(ACTOR_PACKAGE_NAME to setOf(target, overlay)) 130 mapper.remove(overlay) { 131 assertThat(it).containsExactly(ACTOR_PACKAGE_NAME) 132 } 133 assertMapping(ACTOR_PACKAGE_NAME to setOf(target)) 134 } 135 136 @Test 137 fun overlayWithMultipleTargets() { 138 val target0 = mockTarget(0) 139 val target1 = mockTarget(1) 140 val overlay = mockOverlay() 141 mapper = mapper( 142 overlayToTargetToOverlayables = mapOf( 143 overlay.packageName to mapOf( 144 target0.packageName to target0.overlayables.keys, 145 target1.packageName to target1.overlayables.keys 146 ) 147 ) 148 ) 149 mapper.addInOrder(target0, target1, overlay) { 150 assertThat(it).containsExactly(ACTOR_PACKAGE_NAME) 151 } 152 assertMapping(ACTOR_PACKAGE_NAME to setOf(target0, target1, overlay)) 153 mapper.remove(target0) { 154 assertThat(it).containsExactly(ACTOR_PACKAGE_NAME) 155 } 156 assertMapping(ACTOR_PACKAGE_NAME to setOf(target1, overlay)) 157 mapper.remove(target1) { 158 assertThat(it).containsExactly(ACTOR_PACKAGE_NAME) 159 } 160 assertEmpty() 161 } 162 163 @Test 164 fun overlayWithoutTarget() { 165 val overlay = mockOverlay() 166 mapper.addInOrder(overlay) { 167 assertThat(it).isEmpty() 168 } 169 // An overlay can only have visibility exposed through its target 170 assertEmpty() 171 mapper.remove(overlay) { 172 assertThat(it).isEmpty() 173 } 174 assertEmpty() 175 } 176 177 private fun OverlayReferenceMapper.addInOrder( 178 vararg pkgs: AndroidPackage, 179 existing: MutableMap<String, AndroidPackage> = mutableMapOf(), 180 assertion: (changedPackages: Set<String>) -> Unit 181 ): MutableMap<String, AndroidPackage> { 182 val changedPackages = mutableSetOf<String>() 183 pkgs.forEach { 184 changedPackages += addPkg(it, existing) 185 existing[it.packageName] = it 186 } 187 assertion(changedPackages) 188 return existing 189 } 190 191 private fun OverlayReferenceMapper.remove( 192 pkg: AndroidPackage, 193 assertion: (changedPackages: Set<String>) -> Unit 194 ) = assertion(removePkg(pkg.packageName)) 195 196 private fun assertMapping(vararg pairs: Pair<String, Set<AndroidPackage>>) { 197 val expected = pairs.associate { it } 198 .mapValues { pair -> pair.value.map { it.packageName }.toSet() } 199 200 // This validates the API exposed for querying the relationships 201 expected.forEach { (actorPkgName, expectedPkgNames) -> 202 expectedPkgNames.forEach { expectedPkgName -> 203 if (deferRebuild) { 204 mapper.rebuildIfDeferred() 205 deferRebuild = false 206 } 207 208 assertThat(mapper.isValidActor(expectedPkgName, actorPkgName)).isTrue() 209 } 210 } 211 212 // This asserts no other relationships are defined besides those tested above 213 assertThat(mapper.actorPkgToPkgs).containsExactlyEntriesIn(expected) 214 } 215 216 private fun assertEmpty() = assertMapping() 217 218 private fun mapper( 219 namedActors: Map<String, Map<String, String>> = Uri.parse(ACTOR_NAME).run { 220 mapOf(authority!! to mapOf(pathSegments.first() to ACTOR_PACKAGE_NAME)) 221 }, 222 overlayToTargetToOverlayables: Map<String, Map<String, Set<String>>> = mapOf( 223 mockOverlay().packageName to mapOf( 224 mockTarget().run { packageName to overlayables.keys } 225 ) 226 ) 227 ) = OverlayReferenceMapper(deferRebuild, object : OverlayReferenceMapper.Provider { 228 override fun getActorPkg(actor: String) = 229 OverlayActorEnforcer.getPackageNameForActor(actor, namedActors).first 230 231 override fun getTargetToOverlayables(pkg: AndroidPackage) = 232 overlayToTargetToOverlayables[pkg.packageName] ?: emptyMap() 233 }) 234 235 private fun mockTarget(increment: Int = 0) = mockThrowOnUnmocked<AndroidPackage> { 236 whenever(packageName) { "$TARGET_PACKAGE_NAME$increment" } 237 whenever(overlayables) { mapOf("overlayableName$increment" to ACTOR_NAME) } 238 whenever(toString()) { "Package{$packageName}" } 239 whenever(isResourceOverlay) { false } 240 } 241 242 private fun mockOverlay(increment: Int = 0) = mockThrowOnUnmocked<AndroidPackage> { 243 whenever(packageName) { "$OVERLAY_PACKAGE_NAME$increment" } 244 whenever(overlayables) { emptyMap<String, String>() } 245 whenever(toString()) { "Package{$packageName}" } 246 whenever(isResourceOverlay) { true } 247 } 248 } 249