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