1 /* 2 * Copyright (C) 2016 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 com.android.server.om.OverlayManagerService.DEBUG; 20 import static com.android.server.om.OverlayManagerService.TAG; 21 22 import android.annotation.NonNull; 23 import android.annotation.Nullable; 24 import android.content.om.OverlayIdentifier; 25 import android.content.om.OverlayInfo; 26 import android.os.UserHandle; 27 import android.util.ArrayMap; 28 import android.util.ArraySet; 29 import android.util.Pair; 30 import android.util.Slog; 31 import android.util.TypedXmlPullParser; 32 import android.util.TypedXmlSerializer; 33 import android.util.Xml; 34 35 import com.android.internal.annotations.VisibleForTesting; 36 import com.android.internal.util.CollectionUtils; 37 import com.android.internal.util.IndentingPrintWriter; 38 import com.android.internal.util.XmlUtils; 39 40 import org.xmlpull.v1.XmlPullParserException; 41 42 import java.io.IOException; 43 import java.io.InputStream; 44 import java.io.OutputStream; 45 import java.io.PrintWriter; 46 import java.util.ArrayList; 47 import java.util.List; 48 import java.util.Objects; 49 import java.util.Set; 50 import java.util.function.Predicate; 51 import java.util.stream.Stream; 52 53 /** 54 * Data structure representing the current state of all overlay packages in the 55 * system. 56 * 57 * Modifications to the data are signaled by returning true from any state mutating method. 58 * 59 * @see OverlayManagerService 60 */ 61 final class OverlayManagerSettings { 62 /** 63 * All overlay data for all users and target packages is stored in this list. 64 * This keeps memory down, while increasing the cost of running queries or mutating the 65 * data. This is ok, since changing of overlays is very rare and has larger costs associated 66 * with it. 67 * 68 * The order of the items in the list is important, those with a lower index having a lower 69 * priority. 70 */ 71 private final ArrayList<SettingsItem> mItems = new ArrayList<>(); 72 73 @NonNull init(@onNull final OverlayIdentifier overlay, final int userId, @NonNull final String targetPackageName, @Nullable final String targetOverlayableName, @NonNull final String baseCodePath, boolean isMutable, boolean isEnabled, int priority, @Nullable String overlayCategory, boolean isFabricated)74 OverlayInfo init(@NonNull final OverlayIdentifier overlay, final int userId, 75 @NonNull final String targetPackageName, @Nullable final String targetOverlayableName, 76 @NonNull final String baseCodePath, boolean isMutable, boolean isEnabled, int priority, 77 @Nullable String overlayCategory, boolean isFabricated) { 78 remove(overlay, userId); 79 final SettingsItem item = new SettingsItem(overlay, userId, targetPackageName, 80 targetOverlayableName, baseCodePath, OverlayInfo.STATE_UNKNOWN, isEnabled, 81 isMutable, priority, overlayCategory, isFabricated); 82 insert(item); 83 return item.getOverlayInfo(); 84 } 85 86 /** 87 * Returns true if the settings were modified, false if they remain the same. 88 */ remove(@onNull final OverlayIdentifier overlay, final int userId)89 boolean remove(@NonNull final OverlayIdentifier overlay, final int userId) { 90 final int idx = select(overlay, userId); 91 if (idx < 0) { 92 return false; 93 } 94 mItems.remove(idx); 95 return true; 96 } 97 getOverlayInfo(@onNull final OverlayIdentifier overlay, final int userId)98 @NonNull OverlayInfo getOverlayInfo(@NonNull final OverlayIdentifier overlay, final int userId) 99 throws BadKeyException { 100 final int idx = select(overlay, userId); 101 if (idx < 0) { 102 throw new BadKeyException(overlay, userId); 103 } 104 return mItems.get(idx).getOverlayInfo(); 105 } 106 107 @Nullable getNullableOverlayInfo(@onNull final OverlayIdentifier overlay, final int userId)108 OverlayInfo getNullableOverlayInfo(@NonNull final OverlayIdentifier overlay, final int userId) { 109 final int idx = select(overlay, userId); 110 if (idx < 0) { 111 return null; 112 } 113 return mItems.get(idx).getOverlayInfo(); 114 } 115 116 /** 117 * Returns true if the settings were modified, false if they remain the same. 118 */ setBaseCodePath(@onNull final OverlayIdentifier overlay, final int userId, @NonNull final String path)119 boolean setBaseCodePath(@NonNull final OverlayIdentifier overlay, final int userId, 120 @NonNull final String path) throws BadKeyException { 121 final int idx = select(overlay, userId); 122 if (idx < 0) { 123 throw new BadKeyException(overlay, userId); 124 } 125 return mItems.get(idx).setBaseCodePath(path); 126 } 127 setCategory(@onNull final OverlayIdentifier overlay, final int userId, @Nullable String category)128 boolean setCategory(@NonNull final OverlayIdentifier overlay, final int userId, 129 @Nullable String category) throws BadKeyException { 130 final int idx = select(overlay, userId); 131 if (idx < 0) { 132 throw new BadKeyException(overlay, userId); 133 } 134 return mItems.get(idx).setCategory(category); 135 } 136 getEnabled(@onNull final OverlayIdentifier overlay, final int userId)137 boolean getEnabled(@NonNull final OverlayIdentifier overlay, final int userId) 138 throws BadKeyException { 139 final int idx = select(overlay, userId); 140 if (idx < 0) { 141 throw new BadKeyException(overlay, userId); 142 } 143 return mItems.get(idx).isEnabled(); 144 } 145 146 /** 147 * Returns true if the settings were modified, false if they remain the same. 148 */ setEnabled(@onNull final OverlayIdentifier overlay, final int userId, final boolean enable)149 boolean setEnabled(@NonNull final OverlayIdentifier overlay, final int userId, 150 final boolean enable) throws BadKeyException { 151 final int idx = select(overlay, userId); 152 if (idx < 0) { 153 throw new BadKeyException(overlay, userId); 154 } 155 return mItems.get(idx).setEnabled(enable); 156 } 157 getState(@onNull final OverlayIdentifier overlay, final int userId)158 @OverlayInfo.State int getState(@NonNull final OverlayIdentifier overlay, final int userId) 159 throws BadKeyException { 160 final int idx = select(overlay, userId); 161 if (idx < 0) { 162 throw new BadKeyException(overlay, userId); 163 } 164 return mItems.get(idx).getState(); 165 } 166 167 /** 168 * Returns true if the settings were modified, false if they remain the same. 169 */ setState(@onNull final OverlayIdentifier overlay, final int userId, final @OverlayInfo.State int state)170 boolean setState(@NonNull final OverlayIdentifier overlay, final int userId, 171 final @OverlayInfo.State int state) throws BadKeyException { 172 final int idx = select(overlay, userId); 173 if (idx < 0) { 174 throw new BadKeyException(overlay, userId); 175 } 176 return mItems.get(idx).setState(state); 177 } 178 getOverlaysForTarget(@onNull final String targetPackageName, final int userId)179 List<OverlayInfo> getOverlaysForTarget(@NonNull final String targetPackageName, 180 final int userId) { 181 // Immutable RROs targeting "android" are loaded from AssetManager, and so they should be 182 // ignored in OverlayManagerService. 183 final List<SettingsItem> items = selectWhereTarget(targetPackageName, userId); 184 items.removeIf(OverlayManagerSettings::isImmutableFrameworkOverlay); 185 return CollectionUtils.map(items, SettingsItem::getOverlayInfo); 186 } 187 getOverlaysForUser(final int userId)188 ArrayMap<String, List<OverlayInfo>> getOverlaysForUser(final int userId) { 189 // Immutable RROs targeting "android" are loaded from AssetManager, and so they should be 190 // ignored in OverlayManagerService. 191 final List<SettingsItem> items = selectWhereUser(userId); 192 items.removeIf(OverlayManagerSettings::isImmutableFrameworkOverlay); 193 194 final ArrayMap<String, List<OverlayInfo>> targetInfos = new ArrayMap<>(); 195 for (int i = 0, n = items.size(); i < n; i++) { 196 final SettingsItem item = items.get(i); 197 targetInfos.computeIfAbsent(item.mTargetPackageName, (String) -> new ArrayList<>()) 198 .add(item.getOverlayInfo()); 199 } 200 return targetInfos; 201 } 202 getAllBaseCodePaths()203 Set<String> getAllBaseCodePaths() { 204 final Set<String> paths = new ArraySet<>(); 205 mItems.forEach(item -> paths.add(item.mBaseCodePath)); 206 return paths; 207 } 208 getAllIdentifiersAndBaseCodePaths()209 Set<Pair<OverlayIdentifier, String>> getAllIdentifiersAndBaseCodePaths() { 210 final Set<Pair<OverlayIdentifier, String>> set = new ArraySet<>(); 211 mItems.forEach(item -> set.add(new Pair(item.mOverlay, item.mBaseCodePath))); 212 return set; 213 } 214 215 @NonNull removeIf(@onNull final Predicate<OverlayInfo> predicate, final int userId)216 List<OverlayInfo> removeIf(@NonNull final Predicate<OverlayInfo> predicate, final int userId) { 217 return removeIf(info -> (predicate.test(info) && info.userId == userId)); 218 } 219 220 @NonNull removeIf(final @NonNull Predicate<OverlayInfo> predicate)221 List<OverlayInfo> removeIf(final @NonNull Predicate<OverlayInfo> predicate) { 222 List<OverlayInfo> removed = null; 223 for (int i = mItems.size() - 1; i >= 0; i--) { 224 final OverlayInfo info = mItems.get(i).getOverlayInfo(); 225 if (predicate.test(info)) { 226 mItems.remove(i); 227 removed = CollectionUtils.add(removed, info); 228 } 229 } 230 return CollectionUtils.emptyIfNull(removed); 231 } 232 getUsers()233 int[] getUsers() { 234 return mItems.stream().mapToInt(SettingsItem::getUserId).distinct().toArray(); 235 } 236 isImmutableFrameworkOverlay(@onNull SettingsItem item)237 private static boolean isImmutableFrameworkOverlay(@NonNull SettingsItem item) { 238 return !item.isMutable() && "android".equals(item.getTargetPackageName()); 239 } 240 241 /** 242 * Returns true if the settings were modified, false if they remain the same. 243 */ removeUser(final int userId)244 boolean removeUser(final int userId) { 245 return mItems.removeIf(item -> { 246 if (item.getUserId() == userId) { 247 if (DEBUG) { 248 Slog.d(TAG, "Removing overlay " + item.mOverlay + " for user " + userId 249 + " from settings because user was removed"); 250 } 251 return true; 252 } 253 return false; 254 }); 255 } 256 257 /** 258 * Reassigns the priority of an overlay maintaining the values of the overlays other settings. 259 */ 260 void setPriority(@NonNull final OverlayIdentifier overlay, final int userId, 261 final int priority) throws BadKeyException { 262 final int moveIdx = select(overlay, userId); 263 if (moveIdx < 0) { 264 throw new BadKeyException(overlay, userId); 265 } 266 267 final SettingsItem itemToMove = mItems.get(moveIdx); 268 mItems.remove(moveIdx); 269 itemToMove.setPriority(priority); 270 insert(itemToMove); 271 } 272 273 /** 274 * Returns true if the settings were modified, false if they remain the same. 275 */ 276 boolean setPriority(@NonNull final OverlayIdentifier overlay, 277 @NonNull final OverlayIdentifier newOverlay, final int userId) { 278 if (overlay.equals(newOverlay)) { 279 return false; 280 } 281 final int moveIdx = select(overlay, userId); 282 if (moveIdx < 0) { 283 return false; 284 } 285 286 final int parentIdx = select(newOverlay, userId); 287 if (parentIdx < 0) { 288 return false; 289 } 290 291 final SettingsItem itemToMove = mItems.get(moveIdx); 292 293 // Make sure both packages are targeting the same package. 294 if (!itemToMove.getTargetPackageName().equals( 295 mItems.get(parentIdx).getTargetPackageName())) { 296 return false; 297 } 298 299 mItems.remove(moveIdx); 300 final int newParentIdx = select(newOverlay, userId) + 1; 301 mItems.add(newParentIdx, itemToMove); 302 return moveIdx != newParentIdx; 303 } 304 305 /** 306 * Returns true if the settings were modified, false if they remain the same. 307 */ 308 boolean setLowestPriority(@NonNull final OverlayIdentifier overlay, final int userId) { 309 final int idx = select(overlay, userId); 310 if (idx <= 0) { 311 // If the item doesn't exist or is already the lowest, don't change anything. 312 return false; 313 } 314 315 final SettingsItem item = mItems.get(idx); 316 mItems.remove(item); 317 mItems.add(0, item); 318 return true; 319 } 320 321 /** 322 * Returns true if the settings were modified, false if they remain the same. 323 */ 324 boolean setHighestPriority(@NonNull final OverlayIdentifier overlay, final int userId) { 325 final int idx = select(overlay, userId); 326 327 // If the item doesn't exist or is already the highest, don't change anything. 328 if (idx < 0 || idx == mItems.size() - 1) { 329 return false; 330 } 331 332 final SettingsItem item = mItems.get(idx); 333 mItems.remove(idx); 334 mItems.add(item); 335 return true; 336 } 337 338 /** 339 * Inserts the item into the list of settings items. 340 */ 341 private void insert(@NonNull SettingsItem item) { 342 int i; 343 for (i = mItems.size() - 1; i >= 0; i--) { 344 SettingsItem parentItem = mItems.get(i); 345 if (parentItem.mPriority <= item.getPriority()) { 346 break; 347 } 348 } 349 mItems.add(i + 1, item); 350 } 351 352 void dump(@NonNull final PrintWriter p, @NonNull DumpState dumpState) { 353 // select items to display 354 Stream<SettingsItem> items = mItems.stream(); 355 if (dumpState.getUserId() != UserHandle.USER_ALL) { 356 items = items.filter(item -> item.mUserId == dumpState.getUserId()); 357 } 358 if (dumpState.getPackageName() != null) { 359 items = items.filter(item -> item.mOverlay.getPackageName() 360 .equals(dumpState.getPackageName())); 361 } 362 if (dumpState.getOverlayName() != null) { 363 items = items.filter(item -> item.mOverlay.getOverlayName() 364 .equals(dumpState.getOverlayName())); 365 } 366 367 // display items 368 final IndentingPrintWriter pw = new IndentingPrintWriter(p, " "); 369 if (dumpState.getField() != null) { 370 items.forEach(item -> dumpSettingsItemField(pw, item, dumpState.getField())); 371 } else { 372 items.forEach(item -> dumpSettingsItem(pw, item)); 373 } 374 } 375 376 private void dumpSettingsItem(@NonNull final IndentingPrintWriter pw, 377 @NonNull final SettingsItem item) { 378 pw.println(item.mOverlay + ":" + item.getUserId() + " {"); 379 pw.increaseIndent(); 380 381 pw.println("mPackageName...........: " + item.mOverlay.getPackageName()); 382 pw.println("mOverlayName...........: " + item.mOverlay.getOverlayName()); 383 pw.println("mUserId................: " + item.getUserId()); 384 pw.println("mTargetPackageName.....: " + item.getTargetPackageName()); 385 pw.println("mTargetOverlayableName.: " + item.getTargetOverlayableName()); 386 pw.println("mBaseCodePath..........: " + item.getBaseCodePath()); 387 pw.println("mState.................: " + OverlayInfo.stateToString(item.getState())); 388 pw.println("mIsEnabled.............: " + item.isEnabled()); 389 pw.println("mIsMutable.............: " + item.isMutable()); 390 pw.println("mPriority..............: " + item.mPriority); 391 pw.println("mCategory..............: " + item.mCategory); 392 pw.println("mIsFabricated..........: " + item.mIsFabricated); 393 394 pw.decreaseIndent(); 395 pw.println("}"); 396 } 397 398 private void dumpSettingsItemField(@NonNull final IndentingPrintWriter pw, 399 @NonNull final SettingsItem item, @NonNull final String field) { 400 switch (field) { 401 case "packagename": 402 pw.println(item.mOverlay.getPackageName()); 403 break; 404 case "overlayname": 405 pw.println(item.mOverlay.getOverlayName()); 406 break; 407 case "userid": 408 pw.println(item.mUserId); 409 break; 410 case "targetpackagename": 411 pw.println(item.mTargetPackageName); 412 break; 413 case "targetoverlayablename": 414 pw.println(item.mTargetOverlayableName); 415 break; 416 case "basecodepath": 417 pw.println(item.mBaseCodePath); 418 break; 419 case "state": 420 pw.println(OverlayInfo.stateToString(item.mState)); 421 break; 422 case "isenabled": 423 pw.println(item.mIsEnabled); 424 break; 425 case "ismutable": 426 pw.println(item.mIsMutable); 427 break; 428 case "priority": 429 pw.println(item.mPriority); 430 break; 431 case "category": 432 pw.println(item.mCategory); 433 break; 434 } 435 } 436 437 void restore(@NonNull final InputStream is) throws IOException, XmlPullParserException { 438 Serializer.restore(mItems, is); 439 } 440 441 void persist(@NonNull final OutputStream os) throws IOException, XmlPullParserException { 442 Serializer.persist(mItems, os); 443 } 444 445 @VisibleForTesting 446 static final class Serializer { 447 private static final String TAG_OVERLAYS = "overlays"; 448 private static final String TAG_ITEM = "item"; 449 450 private static final String ATTR_BASE_CODE_PATH = "baseCodePath"; 451 private static final String ATTR_IS_ENABLED = "isEnabled"; 452 private static final String ATTR_PACKAGE_NAME = "packageName"; 453 private static final String ATTR_OVERLAY_NAME = "overlayName"; 454 private static final String ATTR_STATE = "state"; 455 private static final String ATTR_TARGET_PACKAGE_NAME = "targetPackageName"; 456 private static final String ATTR_TARGET_OVERLAYABLE_NAME = "targetOverlayableName"; 457 private static final String ATTR_IS_STATIC = "isStatic"; 458 private static final String ATTR_PRIORITY = "priority"; 459 private static final String ATTR_CATEGORY = "category"; 460 private static final String ATTR_USER_ID = "userId"; 461 private static final String ATTR_VERSION = "version"; 462 private static final String ATTR_IS_FABRICATED = "fabricated"; 463 464 @VisibleForTesting 465 static final int CURRENT_VERSION = 4; 466 467 public static void restore(@NonNull final ArrayList<SettingsItem> table, 468 @NonNull final InputStream is) throws IOException, XmlPullParserException { 469 table.clear(); 470 final TypedXmlPullParser parser = Xml.resolvePullParser(is); 471 XmlUtils.beginDocument(parser, TAG_OVERLAYS); 472 final int version = parser.getAttributeInt(null, ATTR_VERSION); 473 if (version != CURRENT_VERSION) { 474 upgrade(version); 475 } 476 477 final int depth = parser.getDepth(); 478 while (XmlUtils.nextElementWithin(parser, depth)) { 479 if (TAG_ITEM.equals(parser.getName())) { 480 final SettingsItem item = restoreRow(parser, depth + 1); 481 table.add(item); 482 } 483 } 484 } 485 486 private static void upgrade(int oldVersion) throws XmlPullParserException { 487 switch (oldVersion) { 488 case 0: 489 case 1: 490 case 2: 491 // Throw an exception which will cause the overlay file to be ignored 492 // and overwritten. 493 throw new XmlPullParserException("old version " + oldVersion + "; ignoring"); 494 case 3: 495 // Upgrading from version 3 to 4 is not a breaking change so do not ignore the 496 // overlay file. 497 return; 498 default: 499 throw new XmlPullParserException("unrecognized version " + oldVersion); 500 } 501 } 502 503 private static SettingsItem restoreRow(@NonNull final TypedXmlPullParser parser, 504 final int depth) throws IOException, XmlPullParserException { 505 final OverlayIdentifier overlay = new OverlayIdentifier( 506 XmlUtils.readStringAttribute(parser, ATTR_PACKAGE_NAME), 507 XmlUtils.readStringAttribute(parser, ATTR_OVERLAY_NAME)); 508 final int userId = parser.getAttributeInt(null, ATTR_USER_ID); 509 final String targetPackageName = XmlUtils.readStringAttribute(parser, 510 ATTR_TARGET_PACKAGE_NAME); 511 final String targetOverlayableName = XmlUtils.readStringAttribute(parser, 512 ATTR_TARGET_OVERLAYABLE_NAME); 513 final String baseCodePath = XmlUtils.readStringAttribute(parser, ATTR_BASE_CODE_PATH); 514 final int state = parser.getAttributeInt(null, ATTR_STATE); 515 final boolean isEnabled = parser.getAttributeBoolean(null, ATTR_IS_ENABLED, false); 516 final boolean isStatic = parser.getAttributeBoolean(null, ATTR_IS_STATIC, false); 517 final int priority = parser.getAttributeInt(null, ATTR_PRIORITY); 518 final String category = XmlUtils.readStringAttribute(parser, ATTR_CATEGORY); 519 final boolean isFabricated = parser.getAttributeBoolean(null, ATTR_IS_FABRICATED, 520 false); 521 522 return new SettingsItem(overlay, userId, targetPackageName, targetOverlayableName, 523 baseCodePath, state, isEnabled, !isStatic, priority, category, isFabricated); 524 } 525 526 public static void persist(@NonNull final ArrayList<SettingsItem> table, 527 @NonNull final OutputStream os) throws IOException, XmlPullParserException { 528 final TypedXmlSerializer xml = Xml.resolveSerializer(os); 529 xml.startDocument(null, true); 530 xml.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); 531 xml.startTag(null, TAG_OVERLAYS); 532 xml.attributeInt(null, ATTR_VERSION, CURRENT_VERSION); 533 534 final int n = table.size(); 535 for (int i = 0; i < n; i++) { 536 final SettingsItem item = table.get(i); 537 persistRow(xml, item); 538 } 539 xml.endTag(null, TAG_OVERLAYS); 540 xml.endDocument(); 541 } 542 543 private static void persistRow(@NonNull final TypedXmlSerializer xml, 544 @NonNull final SettingsItem item) throws IOException { 545 xml.startTag(null, TAG_ITEM); 546 XmlUtils.writeStringAttribute(xml, ATTR_PACKAGE_NAME, item.mOverlay.getPackageName()); 547 XmlUtils.writeStringAttribute(xml, ATTR_OVERLAY_NAME, item.mOverlay.getOverlayName()); 548 xml.attributeInt(null, ATTR_USER_ID, item.mUserId); 549 XmlUtils.writeStringAttribute(xml, ATTR_TARGET_PACKAGE_NAME, item.mTargetPackageName); 550 XmlUtils.writeStringAttribute(xml, ATTR_TARGET_OVERLAYABLE_NAME, 551 item.mTargetOverlayableName); 552 XmlUtils.writeStringAttribute(xml, ATTR_BASE_CODE_PATH, item.mBaseCodePath); 553 xml.attributeInt(null, ATTR_STATE, item.mState); 554 XmlUtils.writeBooleanAttribute(xml, ATTR_IS_ENABLED, item.mIsEnabled); 555 XmlUtils.writeBooleanAttribute(xml, ATTR_IS_STATIC, !item.mIsMutable); 556 xml.attributeInt(null, ATTR_PRIORITY, item.mPriority); 557 XmlUtils.writeStringAttribute(xml, ATTR_CATEGORY, item.mCategory); 558 XmlUtils.writeBooleanAttribute(xml, ATTR_IS_FABRICATED, item.mIsFabricated); 559 xml.endTag(null, TAG_ITEM); 560 } 561 } 562 563 private static final class SettingsItem { 564 private final int mUserId; 565 private final OverlayIdentifier mOverlay; 566 private final String mTargetPackageName; 567 private final String mTargetOverlayableName; 568 private String mBaseCodePath; 569 private @OverlayInfo.State int mState; 570 private boolean mIsEnabled; 571 private OverlayInfo mCache; 572 private boolean mIsMutable; 573 private int mPriority; 574 private String mCategory; 575 private boolean mIsFabricated; 576 577 SettingsItem(@NonNull final OverlayIdentifier overlay, final int userId, 578 @NonNull final String targetPackageName, 579 @Nullable final String targetOverlayableName, @NonNull final String baseCodePath, 580 final @OverlayInfo.State int state, final boolean isEnabled, 581 final boolean isMutable, final int priority, @Nullable String category, 582 final boolean isFabricated) { 583 mOverlay = overlay; 584 mUserId = userId; 585 mTargetPackageName = targetPackageName; 586 mTargetOverlayableName = targetOverlayableName; 587 mBaseCodePath = baseCodePath; 588 mState = state; 589 mIsEnabled = isEnabled; 590 mCategory = category; 591 mCache = null; 592 mIsMutable = isMutable; 593 mPriority = priority; 594 mIsFabricated = isFabricated; 595 } 596 597 private String getTargetPackageName() { 598 return mTargetPackageName; 599 } 600 601 private String getTargetOverlayableName() { 602 return mTargetOverlayableName; 603 } 604 605 private int getUserId() { 606 return mUserId; 607 } 608 609 private String getBaseCodePath() { 610 return mBaseCodePath; 611 } 612 613 private boolean setBaseCodePath(@NonNull final String path) { 614 if (!mBaseCodePath.equals(path)) { 615 mBaseCodePath = path; 616 invalidateCache(); 617 return true; 618 } 619 return false; 620 } 621 622 private @OverlayInfo.State int getState() { 623 return mState; 624 } 625 626 private boolean setState(final @OverlayInfo.State int state) { 627 if (mState != state) { 628 mState = state; 629 invalidateCache(); 630 return true; 631 } 632 return false; 633 } 634 635 private boolean isEnabled() { 636 return mIsEnabled; 637 } 638 639 private boolean setEnabled(boolean enable) { 640 if (!mIsMutable) { 641 return false; 642 } 643 644 if (mIsEnabled != enable) { 645 mIsEnabled = enable; 646 invalidateCache(); 647 return true; 648 } 649 return false; 650 } 651 652 private boolean setCategory(String category) { 653 if (!Objects.equals(mCategory, category)) { 654 mCategory = (category == null) ? null : category.intern(); 655 invalidateCache(); 656 return true; 657 } 658 return false; 659 } 660 661 private OverlayInfo getOverlayInfo() { 662 if (mCache == null) { 663 mCache = new OverlayInfo(mOverlay.getPackageName(), mOverlay.getOverlayName(), 664 mTargetPackageName, mTargetOverlayableName, mCategory, mBaseCodePath, 665 mState, mUserId, mPriority, mIsMutable, mIsFabricated); 666 } 667 return mCache; 668 } 669 670 private void setPriority(int priority) { 671 mPriority = priority; 672 invalidateCache(); 673 } 674 675 private void invalidateCache() { 676 mCache = null; 677 } 678 679 private boolean isMutable() { 680 return mIsMutable; 681 } 682 683 private int getPriority() { 684 return mPriority; 685 } 686 } 687 688 private int select(@NonNull final OverlayIdentifier overlay, final int userId) { 689 final int n = mItems.size(); 690 for (int i = 0; i < n; i++) { 691 final SettingsItem item = mItems.get(i); 692 if (item.mUserId == userId && item.mOverlay.equals(overlay)) { 693 return i; 694 } 695 } 696 return -1; 697 } 698 699 private List<SettingsItem> selectWhereUser(final int userId) { 700 final List<SettingsItem> selectedItems = new ArrayList<>(); 701 CollectionUtils.addIf(mItems, selectedItems, i -> i.mUserId == userId); 702 return selectedItems; 703 } 704 705 private List<SettingsItem> selectWhereOverlay(@NonNull final String packageName, 706 final int userId) { 707 final List<SettingsItem> items = selectWhereUser(userId); 708 items.removeIf(i -> !i.mOverlay.getPackageName().equals(packageName)); 709 return items; 710 } 711 712 private List<SettingsItem> selectWhereTarget(@NonNull final String targetPackageName, 713 final int userId) { 714 final List<SettingsItem> items = selectWhereUser(userId); 715 items.removeIf(i -> !i.getTargetPackageName().equals(targetPackageName)); 716 return items; 717 } 718 719 static final class BadKeyException extends Exception { 720 BadKeyException(@NonNull final OverlayIdentifier overlay, final int userId) { 721 super("Bad key '" + overlay + "' for user " + userId ); 722 } 723 } 724 } 725