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.compat; 18 19 import static android.content.pm.PackageManager.MATCH_ANY_USER; 20 21 import android.annotation.Nullable; 22 import android.app.compat.ChangeIdStateCache; 23 import android.app.compat.PackageOverride; 24 import android.compat.Compatibility.ChangeConfig; 25 import android.content.Context; 26 import android.content.pm.ApplicationInfo; 27 import android.content.pm.PackageManager; 28 import android.os.Environment; 29 import android.text.TextUtils; 30 import android.util.LongArray; 31 import android.util.Slog; 32 33 import com.android.internal.annotations.GuardedBy; 34 import com.android.internal.annotations.VisibleForTesting; 35 import com.android.internal.compat.AndroidBuildClassifier; 36 import com.android.internal.compat.CompatibilityChangeConfig; 37 import com.android.internal.compat.CompatibilityChangeInfo; 38 import com.android.internal.compat.CompatibilityOverrideConfig; 39 import com.android.internal.compat.CompatibilityOverridesToRemoveConfig; 40 import com.android.internal.compat.IOverrideValidator; 41 import com.android.internal.compat.OverrideAllowedState; 42 import com.android.server.compat.config.Change; 43 import com.android.server.compat.config.Config; 44 import com.android.server.compat.overrides.ChangeOverrides; 45 import com.android.server.compat.overrides.Overrides; 46 import com.android.server.compat.overrides.XmlWriter; 47 import com.android.server.pm.ApexManager; 48 49 import org.xmlpull.v1.XmlPullParserException; 50 51 import java.io.BufferedInputStream; 52 import java.io.File; 53 import java.io.FileInputStream; 54 import java.io.IOException; 55 import java.io.InputStream; 56 import java.io.PrintWriter; 57 import java.util.Arrays; 58 import java.util.HashSet; 59 import java.util.List; 60 import java.util.Set; 61 import java.util.concurrent.ConcurrentHashMap; 62 import java.util.concurrent.atomic.AtomicBoolean; 63 64 import javax.xml.datatype.DatatypeConfigurationException; 65 66 /** 67 * CompatConfig maintains state related to the platform compatibility changes. 68 * 69 * <p>It stores the default configuration for each change, and any per-package overrides that have 70 * been configured. 71 */ 72 final class CompatConfig { 73 74 private static final String TAG = "CompatConfig"; 75 private static final String APP_COMPAT_DATA_DIR = "/data/misc/appcompat"; 76 private static final String STATIC_OVERRIDES_PRODUCT_DIR = "/product/etc/appcompat"; 77 private static final String OVERRIDES_FILE = "compat_framework_overrides.xml"; 78 79 private final ConcurrentHashMap<Long, CompatChange> mChanges = new ConcurrentHashMap<>(); 80 81 private final OverrideValidatorImpl mOverrideValidator; 82 private final AndroidBuildClassifier mAndroidBuildClassifier; 83 private Context mContext; 84 @GuardedBy("mOverridesFile") 85 private File mOverridesFile; 86 87 @VisibleForTesting CompatConfig(AndroidBuildClassifier androidBuildClassifier, Context context)88 CompatConfig(AndroidBuildClassifier androidBuildClassifier, Context context) { 89 mOverrideValidator = new OverrideValidatorImpl(androidBuildClassifier, context, this); 90 mAndroidBuildClassifier = androidBuildClassifier; 91 mContext = context; 92 } 93 create(AndroidBuildClassifier androidBuildClassifier, Context context)94 static CompatConfig create(AndroidBuildClassifier androidBuildClassifier, Context context) { 95 CompatConfig config = new CompatConfig(androidBuildClassifier, context); 96 config.initConfigFromLib(Environment.buildPath( 97 Environment.getRootDirectory(), "etc", "compatconfig")); 98 config.initConfigFromLib(Environment.buildPath( 99 Environment.getRootDirectory(), "system_ext", "etc", "compatconfig")); 100 101 List<ApexManager.ActiveApexInfo> apexes = ApexManager.getInstance().getActiveApexInfos(); 102 for (ApexManager.ActiveApexInfo apex : apexes) { 103 config.initConfigFromLib(Environment.buildPath( 104 apex.apexDirectory, "etc", "compatconfig")); 105 } 106 config.initOverrides(); 107 config.invalidateCache(); 108 return config; 109 } 110 111 /** 112 * Adds a change. 113 * 114 * <p>This is intended to be used by unit tests only. 115 * 116 * @param change the change to add 117 */ 118 @VisibleForTesting addChange(CompatChange change)119 void addChange(CompatChange change) { 120 mChanges.put(change.getId(), change); 121 } 122 123 /** 124 * Retrieves the set of disabled changes for a given app. 125 * 126 * <p>Any change ID not in the returned array is by default enabled for the app. 127 * 128 * <p>We use a primitive array to minimize memory footprint: every app process will store this 129 * array statically so we aim to reduce overhead as much as possible. 130 * 131 * @param app the app in question 132 * @return a sorted long array of change IDs 133 */ getDisabledChanges(ApplicationInfo app)134 long[] getDisabledChanges(ApplicationInfo app) { 135 LongArray disabled = new LongArray(); 136 for (CompatChange c : mChanges.values()) { 137 if (!c.isEnabled(app, mAndroidBuildClassifier)) { 138 disabled.add(c.getId()); 139 } 140 } 141 final long[] sortedChanges = disabled.toArray(); 142 Arrays.sort(sortedChanges); 143 return sortedChanges; 144 } 145 146 /** 147 * Looks up a change ID by name. 148 * 149 * @param name name of the change to look up 150 * @return the change ID, or {@code -1} if no change with that name exists 151 */ lookupChangeId(String name)152 long lookupChangeId(String name) { 153 for (CompatChange c : mChanges.values()) { 154 if (TextUtils.equals(c.getName(), name)) { 155 return c.getId(); 156 } 157 } 158 return -1; 159 } 160 161 /** 162 * Checks if a given change is enabled for a given application. 163 * 164 * @param changeId the ID of the change in question 165 * @param app app to check for 166 * @return {@code true} if the change is enabled for this app. Also returns {@code true} if the 167 * change ID is not known, as unknown changes are enabled by default. 168 */ isChangeEnabled(long changeId, ApplicationInfo app)169 boolean isChangeEnabled(long changeId, ApplicationInfo app) { 170 CompatChange c = mChanges.get(changeId); 171 if (c == null) { 172 // we know nothing about this change: default behaviour is enabled. 173 return true; 174 } 175 return c.isEnabled(app, mAndroidBuildClassifier); 176 } 177 178 /** 179 * Checks if a given change will be enabled for a given package name after the installation. 180 * 181 * @param changeId the ID of the change in question 182 * @param packageName package name to check for 183 * @return {@code true} if the change would be enabled for this package name. Also returns 184 * {@code true} if the change ID is not known, as unknown changes are enabled by default. 185 */ willChangeBeEnabled(long changeId, String packageName)186 boolean willChangeBeEnabled(long changeId, String packageName) { 187 CompatChange c = mChanges.get(changeId); 188 if (c == null) { 189 // we know nothing about this change: default behaviour is enabled. 190 return true; 191 } 192 return c.willBeEnabled(packageName); 193 } 194 195 /** 196 * Overrides the enabled state for a given change and app. 197 * 198 * <p>This method is intended to be used *only* for debugging purposes, ultimately invoked 199 * either by an adb command, or from some developer settings UI. 200 * 201 * <p>Note: package overrides are not persistent and will be lost on system or runtime restart. 202 * 203 * @param changeId the ID of the change to be overridden. Note, this call will succeed even 204 * if this change is not known; it will only have any effect if any code in 205 * the platform is gated on the ID given. 206 * @param packageName the app package name to override the change for 207 * @param enabled if the change should be enabled or disabled 208 * @return {@code true} if the change existed before adding the override 209 * @throws IllegalStateException if overriding is not allowed 210 */ addOverride(long changeId, String packageName, boolean enabled)211 synchronized boolean addOverride(long changeId, String packageName, boolean enabled) { 212 boolean alreadyKnown = addOverrideUnsafe(changeId, packageName, 213 new PackageOverride.Builder().setEnabled(enabled).build()); 214 saveOverrides(); 215 invalidateCache(); 216 return alreadyKnown; 217 } 218 219 /** 220 * Overrides the enabled state for a given change and app. 221 * 222 * 223 * @param overrides list of overrides to default changes config. 224 * @param packageName app for which the overrides will be applied. 225 * @param skipUnknownChangeIds whether to skip unknown change IDs in {@code overrides}. 226 */ addPackageOverrides(CompatibilityOverrideConfig overrides, String packageName, boolean skipUnknownChangeIds)227 synchronized void addPackageOverrides(CompatibilityOverrideConfig overrides, 228 String packageName, boolean skipUnknownChangeIds) { 229 for (Long changeId : overrides.overrides.keySet()) { 230 if (skipUnknownChangeIds && !isKnownChangeId(changeId)) { 231 Slog.w(TAG, "Trying to add overrides for unknown Change ID " + changeId + ". " 232 + "Skipping Change ID."); 233 continue; 234 } 235 addOverrideUnsafe(changeId, packageName, overrides.overrides.get(changeId)); 236 } 237 saveOverrides(); 238 invalidateCache(); 239 } 240 addOverrideUnsafe(long changeId, String packageName, PackageOverride overrides)241 private boolean addOverrideUnsafe(long changeId, String packageName, 242 PackageOverride overrides) { 243 final AtomicBoolean alreadyKnown = new AtomicBoolean(true); 244 OverrideAllowedState allowedState = 245 mOverrideValidator.getOverrideAllowedState(changeId, packageName); 246 allowedState.enforce(changeId, packageName); 247 Long versionCode = getVersionCodeOrNull(packageName); 248 249 final CompatChange c = mChanges.computeIfAbsent(changeId, (key) -> { 250 alreadyKnown.set(false); 251 return new CompatChange(changeId); 252 }); 253 c.addPackageOverride(packageName, overrides, allowedState, versionCode); 254 invalidateCache(); 255 return alreadyKnown.get(); 256 } 257 258 /** Checks whether the change is known to the compat config. */ isKnownChangeId(long changeId)259 boolean isKnownChangeId(long changeId) { 260 return mChanges.containsKey(changeId); 261 } 262 263 /** 264 * Returns the maximum SDK version for which this change can be opted in (or -1 if it is not 265 * target SDK gated). 266 */ maxTargetSdkForChangeIdOptIn(long changeId)267 int maxTargetSdkForChangeIdOptIn(long changeId) { 268 CompatChange c = mChanges.get(changeId); 269 if (c != null && c.getEnableSinceTargetSdk() != -1) { 270 return c.getEnableSinceTargetSdk() - 1; 271 } 272 return -1; 273 } 274 275 /** 276 * Returns whether the change is marked as logging only. 277 */ isLoggingOnly(long changeId)278 boolean isLoggingOnly(long changeId) { 279 CompatChange c = mChanges.get(changeId); 280 return c != null && c.getLoggingOnly(); 281 } 282 283 /** 284 * Returns whether the change is marked as disabled. 285 */ isDisabled(long changeId)286 boolean isDisabled(long changeId) { 287 CompatChange c = mChanges.get(changeId); 288 return c != null && c.getDisabled(); 289 } 290 291 /** 292 * Returns whether the change is overridable. 293 */ isOverridable(long changeId)294 boolean isOverridable(long changeId) { 295 CompatChange c = mChanges.get(changeId); 296 return c != null && c.getOverridable(); 297 } 298 299 /** 300 * Removes an override previously added via {@link #addOverride(long, String, boolean)}. 301 * 302 * <p>This restores the default behaviour for the given change and app, once any app processes 303 * have been restarted. 304 * 305 * @param changeId the ID of the change that was overridden 306 * @param packageName the app package name that was overridden 307 * @return {@code true} if an override existed; 308 */ removeOverride(long changeId, String packageName)309 synchronized boolean removeOverride(long changeId, String packageName) { 310 boolean overrideExists = removeOverrideUnsafe(changeId, packageName); 311 if (overrideExists) { 312 saveOverrides(); 313 invalidateCache(); 314 } 315 return overrideExists; 316 } 317 318 /** 319 * Unsafe version of {@link #removeOverride(long, String)}. 320 * It does not save the overrides. 321 */ removeOverrideUnsafe(long changeId, String packageName)322 private boolean removeOverrideUnsafe(long changeId, String packageName) { 323 Long versionCode = getVersionCodeOrNull(packageName); 324 CompatChange c = mChanges.get(changeId); 325 if (c != null) { 326 return removeOverrideUnsafe(c, packageName, versionCode); 327 } 328 return false; 329 } 330 331 /** 332 * Similar to {@link #removeOverrideUnsafe(long, String)} except this method receives a {@link 333 * CompatChange} directly as well as the package's version code. 334 */ removeOverrideUnsafe(CompatChange change, String packageName, @Nullable Long versionCode)335 private boolean removeOverrideUnsafe(CompatChange change, String packageName, 336 @Nullable Long versionCode) { 337 long changeId = change.getId(); 338 OverrideAllowedState allowedState = 339 mOverrideValidator.getOverrideAllowedState(changeId, packageName); 340 return change.removePackageOverride(packageName, allowedState, versionCode); 341 } 342 343 /** 344 * Removes all overrides previously added via {@link #addOverride(long, String, boolean)} or 345 * {@link #addPackageOverrides(CompatibilityOverrideConfig, String, boolean)} for a certain 346 * package. 347 * 348 * <p>This restores the default behaviour for the given app. 349 * 350 * @param packageName the package for which the overrides should be purged 351 */ removePackageOverrides(String packageName)352 synchronized void removePackageOverrides(String packageName) { 353 Long versionCode = getVersionCodeOrNull(packageName); 354 boolean shouldInvalidateCache = false; 355 for (CompatChange change : mChanges.values()) { 356 shouldInvalidateCache |= removeOverrideUnsafe(change, packageName, versionCode); 357 } 358 if (shouldInvalidateCache) { 359 saveOverrides(); 360 invalidateCache(); 361 } 362 } 363 364 /** 365 * Removes overrides whose change ID is specified in {@code overridesToRemove} that were 366 * previously added via {@link #addOverride(long, String, boolean)} or 367 * {@link #addPackageOverrides(CompatibilityOverrideConfig, String, boolean)} for a certain 368 * package. 369 * 370 * <p>This restores the default behaviour for the given change IDs and app. 371 * 372 * @param overridesToRemove list of change IDs for which to restore the default behaviour. 373 * @param packageName the package for which the overrides should be purged 374 */ removePackageOverrides(CompatibilityOverridesToRemoveConfig overridesToRemove, String packageName)375 synchronized void removePackageOverrides(CompatibilityOverridesToRemoveConfig overridesToRemove, 376 String packageName) { 377 boolean shouldInvalidateCache = false; 378 for (Long changeId : overridesToRemove.changeIds) { 379 if (!isKnownChangeId(changeId)) { 380 Slog.w(TAG, "Trying to remove overrides for unknown Change ID " + changeId + ". " 381 + "Skipping Change ID."); 382 continue; 383 } 384 shouldInvalidateCache |= removeOverrideUnsafe(changeId, packageName); 385 } 386 if (shouldInvalidateCache) { 387 saveOverrides(); 388 invalidateCache(); 389 } 390 } 391 getAllowedChangesSinceTargetSdkForPackage(String packageName, int targetSdkVersion)392 private long[] getAllowedChangesSinceTargetSdkForPackage(String packageName, 393 int targetSdkVersion) { 394 LongArray allowed = new LongArray(); 395 for (CompatChange change : mChanges.values()) { 396 if (change.getEnableSinceTargetSdk() != targetSdkVersion) { 397 continue; 398 } 399 OverrideAllowedState allowedState = 400 mOverrideValidator.getOverrideAllowedState(change.getId(), 401 packageName); 402 if (allowedState.state == OverrideAllowedState.ALLOWED) { 403 allowed.add(change.getId()); 404 } 405 } 406 return allowed.toArray(); 407 } 408 409 /** 410 * Enables all changes with enabledSinceTargetSdk == {@param targetSdkVersion} for 411 * {@param packageName}. 412 * 413 * @return the number of changes that were toggled 414 */ enableTargetSdkChangesForPackage(String packageName, int targetSdkVersion)415 int enableTargetSdkChangesForPackage(String packageName, int targetSdkVersion) { 416 long[] changes = getAllowedChangesSinceTargetSdkForPackage(packageName, targetSdkVersion); 417 boolean shouldInvalidateCache = false; 418 for (long changeId : changes) { 419 shouldInvalidateCache |= addOverrideUnsafe(changeId, packageName, 420 new PackageOverride.Builder().setEnabled(true).build()); 421 } 422 if (shouldInvalidateCache) { 423 saveOverrides(); 424 invalidateCache(); 425 } 426 return changes.length; 427 } 428 429 /** 430 * Disables all changes with enabledSinceTargetSdk == {@param targetSdkVersion} for 431 * {@param packageName}. 432 * 433 * @return the number of changes that were toggled 434 */ disableTargetSdkChangesForPackage(String packageName, int targetSdkVersion)435 int disableTargetSdkChangesForPackage(String packageName, int targetSdkVersion) { 436 long[] changes = getAllowedChangesSinceTargetSdkForPackage(packageName, targetSdkVersion); 437 boolean shouldInvalidateCache = false; 438 for (long changeId : changes) { 439 shouldInvalidateCache |= addOverrideUnsafe(changeId, packageName, 440 new PackageOverride.Builder().setEnabled(false).build()); 441 } 442 if (shouldInvalidateCache) { 443 saveOverrides(); 444 invalidateCache(); 445 } 446 return changes.length; 447 } 448 registerListener(long changeId, CompatChange.ChangeListener listener)449 boolean registerListener(long changeId, CompatChange.ChangeListener listener) { 450 final AtomicBoolean alreadyKnown = new AtomicBoolean(true); 451 final CompatChange c = mChanges.computeIfAbsent(changeId, (key) -> { 452 alreadyKnown.set(false); 453 invalidateCache(); 454 return new CompatChange(changeId); 455 }); 456 c.registerListener(listener); 457 return alreadyKnown.get(); 458 } 459 defaultChangeIdValue(long changeId)460 boolean defaultChangeIdValue(long changeId) { 461 CompatChange c = mChanges.get(changeId); 462 if (c == null) { 463 return true; 464 } 465 return c.defaultValue(); 466 } 467 468 @VisibleForTesting forceNonDebuggableFinalForTest(boolean value)469 void forceNonDebuggableFinalForTest(boolean value) { 470 mOverrideValidator.forceNonDebuggableFinalForTest(value); 471 } 472 473 @VisibleForTesting clearChanges()474 void clearChanges() { 475 mChanges.clear(); 476 } 477 478 /** 479 * Dumps the current list of compatibility config information. 480 * 481 * @param pw {@link PrintWriter} instance to which the information will be dumped 482 */ dumpConfig(PrintWriter pw)483 void dumpConfig(PrintWriter pw) { 484 if (mChanges.size() == 0) { 485 pw.println("No compat overrides."); 486 return; 487 } 488 for (CompatChange c : mChanges.values()) { 489 pw.println(c.toString()); 490 } 491 } 492 493 /** 494 * Returns config for a given app. 495 * 496 * @param applicationInfo the {@link ApplicationInfo} for which the info should be dumped 497 */ getAppConfig(ApplicationInfo applicationInfo)498 CompatibilityChangeConfig getAppConfig(ApplicationInfo applicationInfo) { 499 Set<Long> enabled = new HashSet<>(); 500 Set<Long> disabled = new HashSet<>(); 501 for (CompatChange c : mChanges.values()) { 502 if (c.isEnabled(applicationInfo, mAndroidBuildClassifier)) { 503 enabled.add(c.getId()); 504 } else { 505 disabled.add(c.getId()); 506 } 507 } 508 return new CompatibilityChangeConfig(new ChangeConfig(enabled, disabled)); 509 } 510 511 /** 512 * Dumps all the compatibility change information. 513 * 514 * @return an array of {@link CompatibilityChangeInfo} with the current changes 515 */ dumpChanges()516 CompatibilityChangeInfo[] dumpChanges() { 517 CompatibilityChangeInfo[] changeInfos = new CompatibilityChangeInfo[mChanges.size()]; 518 int i = 0; 519 for (CompatChange change : mChanges.values()) { 520 changeInfos[i++] = new CompatibilityChangeInfo(change); 521 } 522 return changeInfos; 523 } 524 initConfigFromLib(File libraryDir)525 void initConfigFromLib(File libraryDir) { 526 if (!libraryDir.exists() || !libraryDir.isDirectory()) { 527 Slog.d(TAG, "No directory " + libraryDir + ", skipping"); 528 return; 529 } 530 for (File f : libraryDir.listFiles()) { 531 Slog.d(TAG, "Found a config file: " + f.getPath()); 532 //TODO(b/138222363): Handle duplicate ids across config files. 533 readConfig(f); 534 } 535 } 536 readConfig(File configFile)537 private void readConfig(File configFile) { 538 try (InputStream in = new BufferedInputStream(new FileInputStream(configFile))) { 539 Config config = com.android.server.compat.config.XmlParser.read(in); 540 for (Change change : config.getCompatChange()) { 541 Slog.d(TAG, "Adding: " + change.toString()); 542 mChanges.put(change.getId(), new CompatChange(change)); 543 } 544 } catch (IOException | DatatypeConfigurationException | XmlPullParserException e) { 545 Slog.e(TAG, "Encountered an error while reading/parsing compat config file", e); 546 } finally { 547 invalidateCache(); 548 } 549 } 550 initOverrides()551 private void initOverrides() { 552 initOverrides(new File(APP_COMPAT_DATA_DIR, OVERRIDES_FILE), 553 new File(STATIC_OVERRIDES_PRODUCT_DIR, OVERRIDES_FILE)); 554 } 555 556 @VisibleForTesting initOverrides(File dynamicOverridesFile, File staticOverridesFile)557 void initOverrides(File dynamicOverridesFile, File staticOverridesFile) { 558 // Clear overrides from all changes before loading. 559 560 for (CompatChange c : mChanges.values()) { 561 c.clearOverrides(); 562 } 563 564 565 loadOverrides(staticOverridesFile); 566 567 mOverridesFile = dynamicOverridesFile; 568 loadOverrides(dynamicOverridesFile); 569 570 if (staticOverridesFile.exists()) { 571 // Only save overrides if there is a static overrides file. 572 saveOverrides(); 573 } 574 } 575 loadOverrides(File overridesFile)576 private void loadOverrides(File overridesFile) { 577 if (!overridesFile.exists()) { 578 // Overrides file doesn't exist. 579 return; 580 } 581 582 try (InputStream in = new BufferedInputStream(new FileInputStream(overridesFile))) { 583 Overrides overrides = com.android.server.compat.overrides.XmlParser.read(in); 584 if (overrides == null) { 585 Slog.w(TAG, "Parsing " + overridesFile.getPath() + " failed"); 586 return; 587 } 588 for (ChangeOverrides changeOverrides : overrides.getChangeOverrides()) { 589 long changeId = changeOverrides.getChangeId(); 590 CompatChange compatChange = mChanges.get(changeId); 591 if (compatChange == null) { 592 Slog.w(TAG, "Change ID " + changeId + " not found. " 593 + "Skipping overrides for it."); 594 continue; 595 } 596 compatChange.loadOverrides(changeOverrides); 597 } 598 } catch (IOException | DatatypeConfigurationException | XmlPullParserException e) { 599 Slog.w(TAG, "Error processing " + overridesFile + " " + e.toString()); 600 return; 601 } 602 } 603 604 /** 605 * Persist compat framework overrides to /data/misc/appcompat/compat_framework_overrides.xml 606 */ saveOverrides()607 void saveOverrides() { 608 if (mOverridesFile == null) { 609 return; 610 } 611 synchronized (mOverridesFile) { 612 Overrides overrides = new Overrides(); 613 List<ChangeOverrides> changeOverridesList = overrides.getChangeOverrides(); 614 for (CompatChange c : mChanges.values()) { 615 ChangeOverrides changeOverrides = c.saveOverrides(); 616 if (changeOverrides != null) { 617 changeOverridesList.add(changeOverrides); 618 } 619 } 620 // Create the file if it doesn't already exist 621 try { 622 mOverridesFile.createNewFile(); 623 } catch (IOException e) { 624 Slog.e(TAG, "Could not create override config file: " + e.toString()); 625 return; 626 } 627 try (PrintWriter out = new PrintWriter(mOverridesFile)) { 628 XmlWriter writer = new XmlWriter(out); 629 XmlWriter.write(writer, overrides); 630 } catch (IOException e) { 631 Slog.e(TAG, e.toString()); 632 } 633 } 634 } 635 getOverrideValidator()636 IOverrideValidator getOverrideValidator() { 637 return mOverrideValidator; 638 } 639 invalidateCache()640 private void invalidateCache() { 641 ChangeIdStateCache.invalidate(); 642 } 643 644 /** 645 * Rechecks all the existing overrides for a package. 646 */ recheckOverrides(String packageName)647 void recheckOverrides(String packageName) { 648 Long versionCode = getVersionCodeOrNull(packageName); 649 boolean shouldInvalidateCache = false; 650 for (CompatChange c : mChanges.values()) { 651 OverrideAllowedState allowedState = 652 mOverrideValidator.getOverrideAllowedStateForRecheck(c.getId(), 653 packageName); 654 shouldInvalidateCache |= c.recheckOverride(packageName, allowedState, versionCode); 655 } 656 if (shouldInvalidateCache) { 657 invalidateCache(); 658 } 659 } 660 661 @Nullable getVersionCodeOrNull(String packageName)662 private Long getVersionCodeOrNull(String packageName) { 663 try { 664 ApplicationInfo applicationInfo = mContext.getPackageManager().getApplicationInfo( 665 packageName, MATCH_ANY_USER); 666 return applicationInfo.longVersionCode; 667 } catch (PackageManager.NameNotFoundException e) { 668 return null; 669 } 670 } 671 registerContentObserver()672 void registerContentObserver() { 673 mOverrideValidator.registerContentObserver(); 674 } 675 } 676