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 static android.content.om.OverlayInfo.STATE_DISABLED; 20 import static android.content.om.OverlayInfo.STATE_ENABLED; 21 22 import static org.junit.Assert.assertArrayEquals; 23 import static org.junit.Assert.assertEquals; 24 import static org.junit.Assert.assertFalse; 25 import static org.junit.Assert.assertNotNull; 26 import static org.junit.Assert.assertTrue; 27 import static org.junit.Assert.fail; 28 29 import android.content.om.OverlayIdentifier; 30 import android.content.om.OverlayInfo; 31 import android.text.TextUtils; 32 import android.util.Xml; 33 34 import androidx.annotation.NonNull; 35 import androidx.test.runner.AndroidJUnit4; 36 37 import com.android.modules.utils.TypedXmlPullParser; 38 39 import org.junit.Before; 40 import org.junit.Test; 41 import org.junit.runner.RunWith; 42 import org.xmlpull.v1.XmlPullParser; 43 44 import java.io.ByteArrayInputStream; 45 import java.io.ByteArrayOutputStream; 46 import java.io.InputStream; 47 import java.util.ArrayList; 48 import java.util.List; 49 import java.util.Map; 50 import java.util.Set; 51 import java.util.stream.IntStream; 52 53 import javax.annotation.Nullable; 54 55 @RunWith(AndroidJUnit4.class) 56 public class OverlayManagerSettingsTests { 57 private OverlayManagerSettings mSettings; 58 private static int USER_0 = 0; 59 private static int USER_1 = 1; 60 61 private static OverlayIdentifier OVERLAY_A = new OverlayIdentifier("com.test.overlay_a", 62 null /* overlayName */); 63 private static OverlayIdentifier OVERLAY_B = new OverlayIdentifier("com.test.overlay_b", 64 null /* overlayName */); 65 private static OverlayIdentifier OVERLAY_C = new OverlayIdentifier("com.test.overlay_c", 66 null /* overlayName */); 67 68 private static final OverlayInfo OVERLAY_A_USER0 = createInfo(OVERLAY_A, USER_0); 69 private static final OverlayInfo OVERLAY_B_USER0 = createInfo(OVERLAY_B, USER_0); 70 private static final OverlayInfo OVERLAY_C_USER0 = createInfo(OVERLAY_C, USER_0); 71 72 private static final OverlayInfo OVERLAY_A_USER1 = createInfo(OVERLAY_A, USER_1); 73 private static final OverlayInfo OVERLAY_B_USER1 = createInfo(OVERLAY_B, USER_1); 74 75 private static final String TARGET_PACKAGE = "com.test.target"; 76 77 @Before setUp()78 public void setUp() throws Exception { 79 mSettings = new OverlayManagerSettings(); 80 } 81 82 // tests: generic functionality 83 84 @Test testSettingsInitiallyEmpty()85 public void testSettingsInitiallyEmpty() throws Exception { 86 final Map<String, List<OverlayInfo>> map = mSettings.getOverlaysForUser(0 /* userId */); 87 assertEquals(0, map.size()); 88 } 89 90 @Test testBasicSetAndGet()91 public void testBasicSetAndGet() throws Exception { 92 assertDoesNotContain(mSettings, OVERLAY_A_USER0); 93 94 insertSetting(OVERLAY_A_USER0); 95 assertContains(mSettings, OVERLAY_A_USER0); 96 final OverlayInfo oi = mSettings.getOverlayInfo(OVERLAY_A, USER_0); 97 assertEquals(OVERLAY_A_USER0, oi); 98 99 assertTrue(mSettings.remove(OVERLAY_A, USER_0)); 100 assertDoesNotContain(mSettings, OVERLAY_A, USER_0); 101 } 102 103 @Test testGetUsers()104 public void testGetUsers() throws Exception { 105 assertArrayEquals(new int[]{}, mSettings.getUsers()); 106 107 insertSetting(OVERLAY_A_USER0); 108 assertArrayEquals(new int[]{USER_0}, mSettings.getUsers()); 109 110 insertSetting(OVERLAY_A_USER1); 111 insertSetting(OVERLAY_B_USER1); 112 assertArrayEquals(new int[]{USER_0, USER_1}, mSettings.getUsers()); 113 } 114 115 @Test testGetOverlaysForUser()116 public void testGetOverlaysForUser() throws Exception { 117 insertSetting(OVERLAY_A_USER0); 118 insertSetting(OVERLAY_B_USER0); 119 insertSetting(OVERLAY_A_USER1); 120 insertSetting(OVERLAY_B_USER0); 121 122 final Map<String, List<OverlayInfo>> map = mSettings.getOverlaysForUser(USER_0); 123 assertEquals(Set.of(TARGET_PACKAGE), map.keySet()); 124 125 // Two overlays in user 0 target the same package 126 final List<OverlayInfo> list = map.get(TARGET_PACKAGE); 127 assertListsAreEqual(List.of(OVERLAY_A_USER0, OVERLAY_B_USER0), list); 128 129 // No users installed for user 3 130 assertEquals(Map.<String, List<OverlayInfo>>of(), mSettings.getOverlaysForUser(3)); 131 } 132 133 @Test testRemoveUser()134 public void testRemoveUser() throws Exception { 135 insertSetting(OVERLAY_A_USER0); 136 insertSetting(OVERLAY_B_USER0); 137 insertSetting(OVERLAY_A_USER1); 138 139 assertContains(mSettings, OVERLAY_A_USER0); 140 assertContains(mSettings, OVERLAY_B_USER0); 141 assertContains(mSettings, OVERLAY_A_USER1); 142 143 mSettings.removeUser(USER_0); 144 145 assertDoesNotContain(mSettings, OVERLAY_A_USER0); 146 assertDoesNotContain(mSettings, OVERLAY_B_USER0); 147 assertContains(mSettings, OVERLAY_A_USER1); 148 } 149 150 @Test testOrderOfNewlyAddedItems()151 public void testOrderOfNewlyAddedItems() throws Exception { 152 // new items are appended to the list 153 insertSetting(OVERLAY_A_USER0); 154 insertSetting(OVERLAY_B_USER0); 155 insertSetting(OVERLAY_C_USER0); 156 157 assertListsAreEqual(List.of(OVERLAY_A_USER0, OVERLAY_B_USER0, OVERLAY_C_USER0), 158 mSettings.getOverlaysForTarget(TARGET_PACKAGE, USER_0)); 159 160 // overlays keep their positions when updated 161 mSettings.setState(OVERLAY_B, USER_0, STATE_ENABLED); 162 final OverlayInfo oi = mSettings.getOverlayInfo(OVERLAY_B, USER_0); 163 assertNotNull(oi); 164 165 assertListsAreEqual(List.of(OVERLAY_A_USER0, oi, OVERLAY_C_USER0), 166 mSettings.getOverlaysForTarget(TARGET_PACKAGE, USER_0)); 167 } 168 169 @Test testSetPriority()170 public void testSetPriority() throws Exception { 171 insertSetting(OVERLAY_A_USER0); 172 insertSetting(OVERLAY_B_USER0); 173 insertSetting(OVERLAY_C_USER0); 174 175 assertListsAreEqual(List.of(OVERLAY_A_USER0, OVERLAY_B_USER0, OVERLAY_C_USER0), 176 mSettings.getOverlaysForTarget(TARGET_PACKAGE, USER_0)); 177 178 assertTrue(mSettings.setPriority(OVERLAY_B, OVERLAY_C, USER_0)); 179 assertListsAreEqual(List.of(OVERLAY_A_USER0, OVERLAY_C_USER0, OVERLAY_B_USER0), 180 mSettings.getOverlaysForTarget(TARGET_PACKAGE, USER_0)); 181 182 // Nothing happens if the parent package cannot be found 183 assertFalse(mSettings.setPriority(OVERLAY_B, new OverlayIdentifier("does.not.exist"), 184 USER_0)); 185 assertListsAreEqual(List.of(OVERLAY_A_USER0, OVERLAY_C_USER0, OVERLAY_B_USER0), 186 mSettings.getOverlaysForTarget(TARGET_PACKAGE, USER_0)); 187 188 // An overlay should not affect the priority of overlays targeting a different package 189 final OverlayInfo otherTarget = new OverlayInfo( 190 "com.test.overlay_other", 191 null, 192 "com.test.some.other.target", 193 null, 194 "some-category", 195 "/data/app/com.test.overlay_other-1/base.apk", 196 STATE_DISABLED, 197 0, 198 0, 199 true, 200 false); 201 insertSetting(otherTarget); 202 assertFalse(mSettings.setPriority(OVERLAY_A, otherTarget.getOverlayIdentifier(), USER_0)); 203 } 204 205 @Test testSetLowestPriority()206 public void testSetLowestPriority() throws Exception { 207 insertSetting(OVERLAY_A_USER0); 208 insertSetting(OVERLAY_B_USER0); 209 insertSetting(OVERLAY_C_USER0); 210 assertListsAreEqual(List.of(OVERLAY_A_USER0, OVERLAY_B_USER0, OVERLAY_C_USER0), 211 mSettings.getOverlaysForTarget(TARGET_PACKAGE, USER_0)); 212 213 assertTrue(mSettings.setLowestPriority(OVERLAY_B, USER_0)); 214 assertListsAreEqual(List.of(OVERLAY_B_USER0, OVERLAY_A_USER0, OVERLAY_C_USER0), 215 mSettings.getOverlaysForTarget(TARGET_PACKAGE, USER_0)); 216 } 217 218 @Test testSetHighestPriority()219 public void testSetHighestPriority() throws Exception { 220 insertSetting(OVERLAY_A_USER0); 221 insertSetting(OVERLAY_B_USER0); 222 insertSetting(OVERLAY_C_USER0); 223 assertListsAreEqual(List.of(OVERLAY_A_USER0, OVERLAY_B_USER0, OVERLAY_C_USER0), 224 mSettings.getOverlaysForTarget(TARGET_PACKAGE, USER_0)); 225 226 assertTrue(mSettings.setHighestPriority(OVERLAY_B, USER_0)); 227 assertListsAreEqual(List.of(OVERLAY_A_USER0, OVERLAY_C_USER0, OVERLAY_B_USER0), 228 mSettings.getOverlaysForTarget(OVERLAY_A_USER0.targetPackageName, USER_0)); 229 } 230 231 // tests: persist and restore 232 233 @Test testPersistEmpty()234 public void testPersistEmpty() throws Exception { 235 ByteArrayOutputStream os = new ByteArrayOutputStream(); 236 mSettings.persist(os); 237 ByteArrayInputStream xml = new ByteArrayInputStream(os.toByteArray()); 238 239 assertEquals(1, countXmlTags(xml, "overlays")); 240 assertEquals(0, countXmlTags(xml, "item")); 241 } 242 243 @Test testPersistDifferentOverlaysSameUser()244 public void testPersistDifferentOverlaysSameUser() throws Exception { 245 insertSetting(OVERLAY_A_USER0); 246 insertSetting(OVERLAY_B_USER0); 247 248 ByteArrayOutputStream os = new ByteArrayOutputStream(); 249 mSettings.persist(os); 250 ByteArrayInputStream xml = new ByteArrayInputStream(os.toByteArray()); 251 252 assertEquals(1, countXmlTags(xml, "overlays")); 253 assertEquals(2, countXmlTags(xml, "item")); 254 assertEquals(1, countXmlAttributesWhere(xml, "item", "packageName", 255 OVERLAY_A.getPackageName())); 256 assertEquals(1, countXmlAttributesWhere(xml, "item", "packageName", 257 OVERLAY_B.getPackageName())); 258 assertEquals(2, countXmlAttributesWhere(xml, "item", "userId", 259 Integer.toString(USER_0))); 260 } 261 262 @Test testPersistSameOverlayDifferentUsers()263 public void testPersistSameOverlayDifferentUsers() throws Exception { 264 insertSetting(OVERLAY_A_USER0); 265 insertSetting(OVERLAY_A_USER1); 266 267 ByteArrayOutputStream os = new ByteArrayOutputStream(); 268 mSettings.persist(os); 269 ByteArrayInputStream xml = new ByteArrayInputStream(os.toByteArray()); 270 271 assertEquals(1, countXmlTags(xml, "overlays")); 272 assertEquals(2, countXmlTags(xml, "item")); 273 assertEquals(2, countXmlAttributesWhere(xml, "item", "packageName", 274 OVERLAY_A.getPackageName())); 275 assertEquals(1, countXmlAttributesWhere(xml, "item", "userId", 276 Integer.toString(USER_0))); 277 assertEquals(1, countXmlAttributesWhere(xml, "item", "userId", 278 Integer.toString(USER_1))); 279 } 280 281 @Test testPersistEnabled()282 public void testPersistEnabled() throws Exception { 283 insertSetting(OVERLAY_A_USER0); 284 mSettings.setEnabled(OVERLAY_A, USER_0, true); 285 286 ByteArrayOutputStream os = new ByteArrayOutputStream(); 287 mSettings.persist(os); 288 ByteArrayInputStream xml = new ByteArrayInputStream(os.toByteArray()); 289 290 assertEquals(1, countXmlAttributesWhere(xml, "item", "isEnabled", "true")); 291 } 292 293 @Test testRestoreEmpty()294 public void testRestoreEmpty() throws Exception { 295 final int version = OverlayManagerSettings.Serializer.CURRENT_VERSION; 296 final String xml = 297 "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n" 298 + "<overlays version=\"" + version + "\" />\n"; 299 ByteArrayInputStream is = new ByteArrayInputStream(xml.getBytes("utf-8")); 300 301 mSettings.restore(is); 302 assertDoesNotContain(mSettings, new OverlayIdentifier("com.test.overlay"), 0); 303 } 304 305 @Test testRestoreSingleUserSingleOverlay()306 public void testRestoreSingleUserSingleOverlay() throws Exception { 307 final int version = OverlayManagerSettings.Serializer.CURRENT_VERSION; 308 final String xml = 309 "<?xml version='1.0' encoding='utf-8' standalone='yes'?>\n" 310 + "<overlays version='" + version + "'>\n" 311 + "<item packageName='com.test.overlay'\n" 312 + " overlayName='test'\n" 313 + " userId='1234'\n" 314 + " targetPackageName='com.test.target'\n" 315 + " baseCodePath='/data/app/com.test.overlay-1/base.apk'\n" 316 + " state='" + STATE_DISABLED + "'\n" 317 + " isEnabled='false'\n" 318 + " category='test-category'\n" 319 + " isStatic='false'\n" 320 + " priority='0' />\n" 321 + "</overlays>\n"; 322 ByteArrayInputStream is = new ByteArrayInputStream(xml.getBytes("utf-8")); 323 324 mSettings.restore(is); 325 final OverlayIdentifier identifier = new OverlayIdentifier("com.test.overlay", "test"); 326 OverlayInfo oi = mSettings.getOverlayInfo(identifier, 1234); 327 assertNotNull(oi); 328 assertEquals("com.test.overlay", oi.packageName); 329 assertEquals("test", oi.overlayName); 330 assertEquals("com.test.target", oi.targetPackageName); 331 assertEquals("/data/app/com.test.overlay-1/base.apk", oi.baseCodePath); 332 assertEquals(1234, oi.userId); 333 assertEquals(STATE_DISABLED, oi.state); 334 assertFalse(mSettings.getEnabled(identifier, 1234)); 335 } 336 337 @Test testPersistAndRestore()338 public void testPersistAndRestore() throws Exception { 339 insertSetting(OVERLAY_A_USER0); 340 insertSetting(OVERLAY_B_USER1); 341 342 ByteArrayOutputStream os = new ByteArrayOutputStream(); 343 mSettings.persist(os); 344 ByteArrayInputStream is = new ByteArrayInputStream(os.toByteArray()); 345 OverlayManagerSettings newSettings = new OverlayManagerSettings(); 346 newSettings.restore(is); 347 348 OverlayInfo a = newSettings.getOverlayInfo(OVERLAY_A, USER_0); 349 assertEquals(OVERLAY_A_USER0, a); 350 351 OverlayInfo b = newSettings.getOverlayInfo(OVERLAY_B, USER_1); 352 assertEquals(OVERLAY_B_USER1, b); 353 } 354 countXmlTags(InputStream in, String tagToLookFor)355 private int countXmlTags(InputStream in, String tagToLookFor) throws Exception { 356 in.reset(); 357 int count = 0; 358 TypedXmlPullParser parser = Xml.resolvePullParser(in); 359 int event = parser.getEventType(); 360 while (event != XmlPullParser.END_DOCUMENT) { 361 if (event == XmlPullParser.START_TAG && tagToLookFor.equals(parser.getName())) { 362 count++; 363 } 364 event = parser.next(); 365 } 366 return count; 367 } 368 countXmlAttributesWhere(InputStream in, String tag, String attr, String value)369 private int countXmlAttributesWhere(InputStream in, String tag, String attr, String value) 370 throws Exception { 371 in.reset(); 372 int count = 0; 373 TypedXmlPullParser parser = Xml.resolvePullParser(in); 374 int event = parser.getEventType(); 375 while (event != XmlPullParser.END_DOCUMENT) { 376 if (event == XmlPullParser.START_TAG && tag.equals(parser.getName())) { 377 String v = parser.getAttributeValue(null, attr); 378 if (value.equals(v)) { 379 count++; 380 } 381 } 382 event = parser.next(); 383 } 384 return count; 385 } 386 insertSetting(OverlayInfo oi)387 private void insertSetting(OverlayInfo oi) throws Exception { 388 mSettings.init(oi.getOverlayIdentifier(), oi.userId, oi.targetPackageName, null, 389 oi.baseCodePath, true, false,0, oi.category, oi.isFabricated); 390 mSettings.setState(oi.getOverlayIdentifier(), oi.userId, oi.state); 391 mSettings.setEnabled(oi.getOverlayIdentifier(), oi.userId, false); 392 } 393 assertContains(final OverlayManagerSettings settings, final OverlayInfo oi)394 private static void assertContains(final OverlayManagerSettings settings, 395 final OverlayInfo oi) { 396 try { 397 settings.getOverlayInfo(oi.getOverlayIdentifier(), oi.userId); 398 } catch (OverlayManagerSettings.BadKeyException e) { 399 fail(String.format("settings does not contain overlay=%s userId=%d", 400 oi.getOverlayIdentifier(), oi.userId)); 401 } 402 } 403 assertDoesNotContain(final OverlayManagerSettings settings, final OverlayInfo oi)404 private static void assertDoesNotContain(final OverlayManagerSettings settings, 405 final OverlayInfo oi) { 406 assertDoesNotContain(settings, oi.getOverlayIdentifier(), oi.userId); 407 } 408 assertDoesNotContain(final OverlayManagerSettings settings, final OverlayIdentifier overlay, int userId)409 private static void assertDoesNotContain(final OverlayManagerSettings settings, 410 final OverlayIdentifier overlay, int userId) { 411 try { 412 settings.getOverlayInfo(overlay, userId); 413 fail(String.format("settings contains overlay=%s userId=%d", overlay, userId)); 414 } catch (OverlayManagerSettings.BadKeyException e) { 415 // do nothing: we expect to end up here 416 } 417 } 418 createInfo(@onNull OverlayIdentifier identifier, int userId)419 private static OverlayInfo createInfo(@NonNull OverlayIdentifier identifier, int userId) { 420 return new OverlayInfo( 421 identifier.getPackageName(), 422 identifier.getOverlayName(), 423 "com.test.target", 424 null, 425 "some-category", 426 "/data/app/" + identifier + "/base.apk", 427 STATE_DISABLED, 428 userId, 429 0, 430 true, 431 false); 432 } 433 assertContains(int[] haystack, int needle)434 private static void assertContains(int[] haystack, int needle) { 435 List<Integer> list = IntStream.of(haystack) 436 .boxed() 437 .collect(ArrayList::new, ArrayList::add, ArrayList::addAll); 438 if (!list.contains(needle)) { 439 fail(String.format("integer array [%s] does not contain value %s", 440 TextUtils.join(",", list), needle)); 441 } 442 } 443 assertDoesNotContain(int[] haystack, int needle)444 private static void assertDoesNotContain(int[] haystack, int needle) { 445 List<Integer> list = IntStream.of(haystack) 446 .boxed() 447 .collect(ArrayList::new, ArrayList::add, ArrayList::addAll); 448 if (list.contains(needle)) { 449 fail(String.format("integer array [%s] contains value %s", 450 TextUtils.join(",", list), needle)); 451 } 452 } 453 assertListsAreEqual( @onNull List<OverlayInfo> expected, @Nullable List<OverlayInfo> actual)454 private static void assertListsAreEqual( 455 @NonNull List<OverlayInfo> expected, @Nullable List<OverlayInfo> actual) { 456 if (!expected.equals(actual)) { 457 fail(String.format("lists [%s] and [%s] differ", 458 TextUtils.join(",", expected), TextUtils.join(",", actual))); 459 } 460 } 461 } 462