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