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.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 final List<SettingsItem> items = selectWhereTarget(targetPackageName, userId); 182 return CollectionUtils.map(items, SettingsItem::getOverlayInfo); 183 } 184 getOverlaysForUser(final int userId)185 ArrayMap<String, List<OverlayInfo>> getOverlaysForUser(final int userId) { 186 final List<SettingsItem> items = selectWhereUser(userId); 187 188 final ArrayMap<String, List<OverlayInfo>> targetInfos = new ArrayMap<>(); 189 for (int i = 0, n = items.size(); i < n; i++) { 190 final SettingsItem item = items.get(i); 191 targetInfos.computeIfAbsent(item.mTargetPackageName, (String) -> new ArrayList<>()) 192 .add(item.getOverlayInfo()); 193 } 194 return targetInfos; 195 } 196 getAllBaseCodePaths()197 Set<String> getAllBaseCodePaths() { 198 final Set<String> paths = new ArraySet<>(); 199 mItems.forEach(item -> paths.add(item.mBaseCodePath)); 200 return paths; 201 } 202 getAllIdentifiersAndBaseCodePaths()203 Set<Pair<OverlayIdentifier, String>> getAllIdentifiersAndBaseCodePaths() { 204 final Set<Pair<OverlayIdentifier, String>> set = new ArraySet<>(); 205 mItems.forEach(item -> set.add(new Pair(item.mOverlay, item.mBaseCodePath))); 206 return set; 207 } 208 209 @NonNull removeIf(@onNull final Predicate<OverlayInfo> predicate, final int userId)210 List<OverlayInfo> removeIf(@NonNull final Predicate<OverlayInfo> predicate, final int userId) { 211 return removeIf(info -> (predicate.test(info) && info.userId == userId)); 212 } 213 214 @NonNull removeIf(final @NonNull Predicate<OverlayInfo> predicate)215 List<OverlayInfo> removeIf(final @NonNull Predicate<OverlayInfo> predicate) { 216 List<OverlayInfo> removed = null; 217 for (int i = mItems.size() - 1; i >= 0; i--) { 218 final OverlayInfo info = mItems.get(i).getOverlayInfo(); 219 if (predicate.test(info)) { 220 mItems.remove(i); 221 removed = CollectionUtils.add(removed, info); 222 } 223 } 224 return CollectionUtils.emptyIfNull(removed); 225 } 226 getUsers()227 int[] getUsers() { 228 return mItems.stream().mapToInt(SettingsItem::getUserId).distinct().toArray(); 229 } 230 231 /** 232 * Returns true if the settings were modified, false if they remain the same. 233 */ removeUser(final int userId)234 boolean removeUser(final int userId) { 235 return mItems.removeIf(item -> { 236 if (item.getUserId() == userId) { 237 if (DEBUG) { 238 Slog.d(TAG, "Removing overlay " + item.mOverlay + " for user " + userId 239 + " from settings because user was removed"); 240 } 241 return true; 242 } 243 return false; 244 }); 245 } 246 247 /** 248 * Reassigns the priority of an overlay maintaining the values of the overlays other settings. 249 */ 250 void setPriority(@NonNull final OverlayIdentifier overlay, final int userId, 251 final int priority) throws BadKeyException { 252 final int moveIdx = select(overlay, userId); 253 if (moveIdx < 0) { 254 throw new BadKeyException(overlay, userId); 255 } 256 257 final SettingsItem itemToMove = mItems.get(moveIdx); 258 mItems.remove(moveIdx); 259 itemToMove.setPriority(priority); 260 insert(itemToMove); 261 } 262 263 /** 264 * Returns true if the settings were modified, false if they remain the same. 265 */ 266 boolean setPriority(@NonNull final OverlayIdentifier overlay, 267 @NonNull final OverlayIdentifier newOverlay, final int userId) { 268 if (overlay.equals(newOverlay)) { 269 return false; 270 } 271 final int moveIdx = select(overlay, userId); 272 if (moveIdx < 0) { 273 return false; 274 } 275 276 final int parentIdx = select(newOverlay, userId); 277 if (parentIdx < 0) { 278 return false; 279 } 280 281 final SettingsItem itemToMove = mItems.get(moveIdx); 282 283 // Make sure both packages are targeting the same package. 284 if (!itemToMove.getTargetPackageName().equals( 285 mItems.get(parentIdx).getTargetPackageName())) { 286 return false; 287 } 288 289 mItems.remove(moveIdx); 290 final int newParentIdx = select(newOverlay, userId) + 1; 291 mItems.add(newParentIdx, itemToMove); 292 return moveIdx != newParentIdx; 293 } 294 295 /** 296 * Returns true if the settings were modified, false if they remain the same. 297 */ 298 boolean setLowestPriority(@NonNull final OverlayIdentifier overlay, final int userId) { 299 final int idx = select(overlay, userId); 300 if (idx <= 0) { 301 // If the item doesn't exist or is already the lowest, don't change anything. 302 return false; 303 } 304 305 final SettingsItem item = mItems.get(idx); 306 mItems.remove(item); 307 mItems.add(0, item); 308 return true; 309 } 310 311 /** 312 * Returns true if the settings were modified, false if they remain the same. 313 */ 314 boolean setHighestPriority(@NonNull final OverlayIdentifier overlay, final int userId) { 315 final int idx = select(overlay, userId); 316 317 // If the item doesn't exist or is already the highest, don't change anything. 318 if (idx < 0 || idx == mItems.size() - 1) { 319 return false; 320 } 321 322 final SettingsItem item = mItems.get(idx); 323 mItems.remove(idx); 324 mItems.add(item); 325 return true; 326 } 327 328 /** 329 * Inserts the item into the list of settings items. 330 */ 331 private void insert(@NonNull SettingsItem item) { 332 int i; 333 for (i = mItems.size() - 1; i >= 0; i--) { 334 SettingsItem parentItem = mItems.get(i); 335 if (parentItem.mPriority <= item.getPriority()) { 336 break; 337 } 338 } 339 mItems.add(i + 1, item); 340 } 341 342 void dump(@NonNull final PrintWriter p, @NonNull DumpState dumpState) { 343 // select items to display 344 Stream<SettingsItem> items = mItems.stream(); 345 if (dumpState.getUserId() != UserHandle.USER_ALL) { 346 items = items.filter(item -> item.mUserId == dumpState.getUserId()); 347 } 348 if (dumpState.getPackageName() != null) { 349 items = items.filter(item -> item.mOverlay.getPackageName() 350 .equals(dumpState.getPackageName())); 351 } 352 if (dumpState.getOverlayName() != null) { 353 items = items.filter(item -> item.mOverlay.getOverlayName() 354 .equals(dumpState.getOverlayName())); 355 } 356 357 // display items 358 final IndentingPrintWriter pw = new IndentingPrintWriter(p, " "); 359 if (dumpState.getField() != null) { 360 items.forEach(item -> dumpSettingsItemField(pw, item, dumpState.getField())); 361 } else { 362 items.forEach(item -> dumpSettingsItem(pw, item)); 363 } 364 } 365 366 private void dumpSettingsItem(@NonNull final IndentingPrintWriter pw, 367 @NonNull final SettingsItem item) { 368 pw.println(item.mOverlay + ":" + item.getUserId() + " {"); 369 pw.increaseIndent(); 370 371 pw.println("mPackageName...........: " + item.mOverlay.getPackageName()); 372 pw.println("mOverlayName...........: " + item.mOverlay.getOverlayName()); 373 pw.println("mUserId................: " + item.getUserId()); 374 pw.println("mTargetPackageName.....: " + item.getTargetPackageName()); 375 pw.println("mTargetOverlayableName.: " + item.getTargetOverlayableName()); 376 pw.println("mBaseCodePath..........: " + item.getBaseCodePath()); 377 pw.println("mState.................: " + OverlayInfo.stateToString(item.getState())); 378 pw.println("mIsEnabled.............: " + item.isEnabled()); 379 pw.println("mIsMutable.............: " + item.isMutable()); 380 pw.println("mPriority..............: " + item.mPriority); 381 pw.println("mCategory..............: " + item.mCategory); 382 pw.println("mIsFabricated..........: " + item.mIsFabricated); 383 384 pw.decreaseIndent(); 385 pw.println("}"); 386 } 387 388 private void dumpSettingsItemField(@NonNull final IndentingPrintWriter pw, 389 @NonNull final SettingsItem item, @NonNull final String field) { 390 switch (field) { 391 case "packagename": 392 pw.println(item.mOverlay.getPackageName()); 393 break; 394 case "overlayname": 395 pw.println(item.mOverlay.getOverlayName()); 396 break; 397 case "userid": 398 pw.println(item.mUserId); 399 break; 400 case "targetpackagename": 401 pw.println(item.mTargetPackageName); 402 break; 403 case "targetoverlayablename": 404 pw.println(item.mTargetOverlayableName); 405 break; 406 case "basecodepath": 407 pw.println(item.mBaseCodePath); 408 break; 409 case "state": 410 pw.println(OverlayInfo.stateToString(item.mState)); 411 break; 412 case "isenabled": 413 pw.println(item.mIsEnabled); 414 break; 415 case "ismutable": 416 pw.println(item.mIsMutable); 417 break; 418 case "priority": 419 pw.println(item.mPriority); 420 break; 421 case "category": 422 pw.println(item.mCategory); 423 break; 424 } 425 } 426 427 void restore(@NonNull final InputStream is) throws IOException, XmlPullParserException { 428 Serializer.restore(mItems, is); 429 } 430 431 void persist(@NonNull final OutputStream os) throws IOException, XmlPullParserException { 432 Serializer.persist(mItems, os); 433 } 434 435 @VisibleForTesting 436 static final class Serializer { 437 private static final String TAG_OVERLAYS = "overlays"; 438 private static final String TAG_ITEM = "item"; 439 440 private static final String ATTR_BASE_CODE_PATH = "baseCodePath"; 441 private static final String ATTR_IS_ENABLED = "isEnabled"; 442 private static final String ATTR_PACKAGE_NAME = "packageName"; 443 private static final String ATTR_OVERLAY_NAME = "overlayName"; 444 private static final String ATTR_STATE = "state"; 445 private static final String ATTR_TARGET_PACKAGE_NAME = "targetPackageName"; 446 private static final String ATTR_TARGET_OVERLAYABLE_NAME = "targetOverlayableName"; 447 private static final String ATTR_IS_STATIC = "isStatic"; 448 private static final String ATTR_PRIORITY = "priority"; 449 private static final String ATTR_CATEGORY = "category"; 450 private static final String ATTR_USER_ID = "userId"; 451 private static final String ATTR_VERSION = "version"; 452 private static final String ATTR_IS_FABRICATED = "fabricated"; 453 454 @VisibleForTesting 455 static final int CURRENT_VERSION = 4; 456 457 public static void restore(@NonNull final ArrayList<SettingsItem> table, 458 @NonNull final InputStream is) throws IOException, XmlPullParserException { 459 table.clear(); 460 final TypedXmlPullParser parser = Xml.resolvePullParser(is); 461 XmlUtils.beginDocument(parser, TAG_OVERLAYS); 462 final int version = parser.getAttributeInt(null, ATTR_VERSION); 463 if (version != CURRENT_VERSION) { 464 upgrade(version); 465 } 466 467 final int depth = parser.getDepth(); 468 while (XmlUtils.nextElementWithin(parser, depth)) { 469 if (TAG_ITEM.equals(parser.getName())) { 470 final SettingsItem item = restoreRow(parser, depth + 1); 471 table.add(item); 472 } 473 } 474 } 475 476 private static void upgrade(int oldVersion) throws XmlPullParserException { 477 switch (oldVersion) { 478 case 0: 479 case 1: 480 case 2: 481 // Throw an exception which will cause the overlay file to be ignored 482 // and overwritten. 483 throw new XmlPullParserException("old version " + oldVersion + "; ignoring"); 484 case 3: 485 // Upgrading from version 3 to 4 is not a breaking change so do not ignore the 486 // overlay file. 487 return; 488 default: 489 throw new XmlPullParserException("unrecognized version " + oldVersion); 490 } 491 } 492 493 private static SettingsItem restoreRow(@NonNull final TypedXmlPullParser parser, 494 final int depth) throws IOException, XmlPullParserException { 495 final OverlayIdentifier overlay = new OverlayIdentifier( 496 XmlUtils.readStringAttribute(parser, ATTR_PACKAGE_NAME), 497 XmlUtils.readStringAttribute(parser, ATTR_OVERLAY_NAME)); 498 final int userId = parser.getAttributeInt(null, ATTR_USER_ID); 499 final String targetPackageName = XmlUtils.readStringAttribute(parser, 500 ATTR_TARGET_PACKAGE_NAME); 501 final String targetOverlayableName = XmlUtils.readStringAttribute(parser, 502 ATTR_TARGET_OVERLAYABLE_NAME); 503 final String baseCodePath = XmlUtils.readStringAttribute(parser, ATTR_BASE_CODE_PATH); 504 final int state = parser.getAttributeInt(null, ATTR_STATE); 505 final boolean isEnabled = parser.getAttributeBoolean(null, ATTR_IS_ENABLED, false); 506 final boolean isStatic = parser.getAttributeBoolean(null, ATTR_IS_STATIC, false); 507 final int priority = parser.getAttributeInt(null, ATTR_PRIORITY); 508 final String category = XmlUtils.readStringAttribute(parser, ATTR_CATEGORY); 509 final boolean isFabricated = parser.getAttributeBoolean(null, ATTR_IS_FABRICATED, 510 false); 511 512 return new SettingsItem(overlay, userId, targetPackageName, targetOverlayableName, 513 baseCodePath, state, isEnabled, !isStatic, priority, category, isFabricated); 514 } 515 516 public static void persist(@NonNull final ArrayList<SettingsItem> table, 517 @NonNull final OutputStream os) throws IOException, XmlPullParserException { 518 final TypedXmlSerializer xml = Xml.resolveSerializer(os); 519 xml.startDocument(null, true); 520 xml.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); 521 xml.startTag(null, TAG_OVERLAYS); 522 xml.attributeInt(null, ATTR_VERSION, CURRENT_VERSION); 523 524 final int n = table.size(); 525 for (int i = 0; i < n; i++) { 526 final SettingsItem item = table.get(i); 527 persistRow(xml, item); 528 } 529 xml.endTag(null, TAG_OVERLAYS); 530 xml.endDocument(); 531 } 532 533 private static void persistRow(@NonNull final TypedXmlSerializer xml, 534 @NonNull final SettingsItem item) throws IOException { 535 xml.startTag(null, TAG_ITEM); 536 XmlUtils.writeStringAttribute(xml, ATTR_PACKAGE_NAME, item.mOverlay.getPackageName()); 537 XmlUtils.writeStringAttribute(xml, ATTR_OVERLAY_NAME, item.mOverlay.getOverlayName()); 538 xml.attributeInt(null, ATTR_USER_ID, item.mUserId); 539 XmlUtils.writeStringAttribute(xml, ATTR_TARGET_PACKAGE_NAME, item.mTargetPackageName); 540 XmlUtils.writeStringAttribute(xml, ATTR_TARGET_OVERLAYABLE_NAME, 541 item.mTargetOverlayableName); 542 XmlUtils.writeStringAttribute(xml, ATTR_BASE_CODE_PATH, item.mBaseCodePath); 543 xml.attributeInt(null, ATTR_STATE, item.mState); 544 XmlUtils.writeBooleanAttribute(xml, ATTR_IS_ENABLED, item.mIsEnabled); 545 XmlUtils.writeBooleanAttribute(xml, ATTR_IS_STATIC, !item.mIsMutable); 546 xml.attributeInt(null, ATTR_PRIORITY, item.mPriority); 547 XmlUtils.writeStringAttribute(xml, ATTR_CATEGORY, item.mCategory); 548 XmlUtils.writeBooleanAttribute(xml, ATTR_IS_FABRICATED, item.mIsFabricated); 549 xml.endTag(null, TAG_ITEM); 550 } 551 } 552 553 private static final class SettingsItem { 554 private final int mUserId; 555 private final OverlayIdentifier mOverlay; 556 private final String mTargetPackageName; 557 private final String mTargetOverlayableName; 558 private String mBaseCodePath; 559 private @OverlayInfo.State int mState; 560 private boolean mIsEnabled; 561 private OverlayInfo mCache; 562 private boolean mIsMutable; 563 private int mPriority; 564 private String mCategory; 565 private boolean mIsFabricated; 566 567 SettingsItem(@NonNull final OverlayIdentifier overlay, final int userId, 568 @NonNull final String targetPackageName, 569 @Nullable final String targetOverlayableName, @NonNull final String baseCodePath, 570 final @OverlayInfo.State int state, final boolean isEnabled, 571 final boolean isMutable, final int priority, @Nullable String category, 572 final boolean isFabricated) { 573 mOverlay = overlay; 574 mUserId = userId; 575 mTargetPackageName = targetPackageName; 576 mTargetOverlayableName = targetOverlayableName; 577 mBaseCodePath = baseCodePath; 578 mState = state; 579 mIsEnabled = isEnabled; 580 mCategory = category; 581 mCache = null; 582 mIsMutable = isMutable; 583 mPriority = priority; 584 mIsFabricated = isFabricated; 585 } 586 587 private String getTargetPackageName() { 588 return mTargetPackageName; 589 } 590 591 private String getTargetOverlayableName() { 592 return mTargetOverlayableName; 593 } 594 595 private int getUserId() { 596 return mUserId; 597 } 598 599 private String getBaseCodePath() { 600 return mBaseCodePath; 601 } 602 603 private boolean setBaseCodePath(@NonNull final String path) { 604 if (!mBaseCodePath.equals(path)) { 605 mBaseCodePath = path; 606 invalidateCache(); 607 return true; 608 } 609 return false; 610 } 611 612 private @OverlayInfo.State int getState() { 613 return mState; 614 } 615 616 private boolean setState(final @OverlayInfo.State int state) { 617 if (mState != state) { 618 mState = state; 619 invalidateCache(); 620 return true; 621 } 622 return false; 623 } 624 625 private boolean isEnabled() { 626 return mIsEnabled; 627 } 628 629 private boolean setEnabled(boolean enable) { 630 if (!mIsMutable) { 631 return false; 632 } 633 634 if (mIsEnabled != enable) { 635 mIsEnabled = enable; 636 invalidateCache(); 637 return true; 638 } 639 return false; 640 } 641 642 private boolean setCategory(String category) { 643 if (!Objects.equals(mCategory, category)) { 644 mCategory = (category == null) ? null : category.intern(); 645 invalidateCache(); 646 return true; 647 } 648 return false; 649 } 650 651 private OverlayInfo getOverlayInfo() { 652 if (mCache == null) { 653 mCache = new OverlayInfo(mOverlay.getPackageName(), mOverlay.getOverlayName(), 654 mTargetPackageName, mTargetOverlayableName, mCategory, mBaseCodePath, 655 mState, mUserId, mPriority, mIsMutable, mIsFabricated); 656 } 657 return mCache; 658 } 659 660 private void setPriority(int priority) { 661 mPriority = priority; 662 invalidateCache(); 663 } 664 665 private void invalidateCache() { 666 mCache = null; 667 } 668 669 private boolean isMutable() { 670 return mIsMutable; 671 } 672 673 private int getPriority() { 674 return mPriority; 675 } 676 } 677 678 private int select(@NonNull final OverlayIdentifier overlay, final int userId) { 679 final int n = mItems.size(); 680 for (int i = 0; i < n; i++) { 681 final SettingsItem item = mItems.get(i); 682 if (item.mUserId == userId && item.mOverlay.equals(overlay)) { 683 return i; 684 } 685 } 686 return -1; 687 } 688 689 private List<SettingsItem> selectWhereUser(final int userId) { 690 final List<SettingsItem> selectedItems = new ArrayList<>(); 691 CollectionUtils.addIf(mItems, selectedItems, i -> i.mUserId == userId); 692 return selectedItems; 693 } 694 695 private List<SettingsItem> selectWhereOverlay(@NonNull final String packageName, 696 final int userId) { 697 final List<SettingsItem> items = selectWhereUser(userId); 698 items.removeIf(i -> !i.mOverlay.getPackageName().equals(packageName)); 699 return items; 700 } 701 702 private List<SettingsItem> selectWhereTarget(@NonNull final String targetPackageName, 703 final int userId) { 704 final List<SettingsItem> items = selectWhereUser(userId); 705 items.removeIf(i -> !i.getTargetPackageName().equals(targetPackageName)); 706 return items; 707 } 708 709 static final class BadKeyException extends Exception { 710 BadKeyException(@NonNull final OverlayIdentifier overlay, final int userId) { 711 super("Bad key '" + overlay + "' for user " + userId ); 712 } 713 } 714 } 715