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.Manifest.permission.LOG_COMPAT_CHANGE; 20 import static android.Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG; 21 import static android.Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD; 22 import static android.Manifest.permission.READ_COMPAT_CHANGE_CONFIG; 23 24 import android.annotation.EnforcePermission; 25 import android.annotation.RequiresNoPermission; 26 import android.annotation.UserIdInt; 27 import android.app.ActivityManager; 28 import android.app.IActivityManager; 29 import android.app.compat.PackageOverride; 30 import android.content.BroadcastReceiver; 31 import android.content.Context; 32 import android.content.Intent; 33 import android.content.IntentFilter; 34 import android.content.pm.ApplicationInfo; 35 import android.content.pm.PackageManagerInternal; 36 import android.net.Uri; 37 import android.os.Binder; 38 import android.os.Build; 39 import android.os.Process; 40 import android.os.RemoteException; 41 import android.os.UserHandle; 42 import android.util.Slog; 43 44 import com.android.internal.annotations.VisibleForTesting; 45 import com.android.internal.compat.AndroidBuildClassifier; 46 import com.android.internal.compat.ChangeReporter; 47 import com.android.internal.compat.CompatibilityChangeConfig; 48 import com.android.internal.compat.CompatibilityChangeInfo; 49 import com.android.internal.compat.CompatibilityOverrideConfig; 50 import com.android.internal.compat.CompatibilityOverridesByPackageConfig; 51 import com.android.internal.compat.CompatibilityOverridesToRemoveByPackageConfig; 52 import com.android.internal.compat.CompatibilityOverridesToRemoveConfig; 53 import com.android.internal.compat.IOverrideValidator; 54 import com.android.internal.compat.IPlatformCompat; 55 import com.android.internal.util.DumpUtils; 56 import com.android.server.LocalServices; 57 58 import java.io.FileDescriptor; 59 import java.io.PrintWriter; 60 import java.util.Arrays; 61 import java.util.Collection; 62 import java.util.HashMap; 63 import java.util.Map; 64 65 /** 66 * System server internal API for gating and reporting compatibility changes. 67 */ 68 public class PlatformCompat extends IPlatformCompat.Stub { 69 70 private static final String TAG = "Compatibility"; 71 72 private final Context mContext; 73 private final ChangeReporter mChangeReporter; 74 private final CompatConfig mCompatConfig; 75 private final AndroidBuildClassifier mBuildClassifier; 76 PlatformCompat(Context context)77 public PlatformCompat(Context context) { 78 mContext = context; 79 mChangeReporter = new ChangeReporter(ChangeReporter.SOURCE_SYSTEM_SERVER); 80 mBuildClassifier = new AndroidBuildClassifier(); 81 mCompatConfig = CompatConfig.create(mBuildClassifier, mContext); 82 } 83 84 @VisibleForTesting PlatformCompat(Context context, CompatConfig compatConfig, AndroidBuildClassifier buildClassifier)85 PlatformCompat(Context context, CompatConfig compatConfig, 86 AndroidBuildClassifier buildClassifier) { 87 mContext = context; 88 mChangeReporter = new ChangeReporter(ChangeReporter.SOURCE_SYSTEM_SERVER); 89 mCompatConfig = compatConfig; 90 mBuildClassifier = buildClassifier; 91 92 registerPackageReceiver(context); 93 } 94 95 @Override 96 @EnforcePermission(LOG_COMPAT_CHANGE) reportChange(long changeId, ApplicationInfo appInfo)97 public void reportChange(long changeId, ApplicationInfo appInfo) { 98 super.reportChange_enforcePermission(); 99 100 reportChangeInternal(changeId, appInfo.uid, ChangeReporter.STATE_LOGGED); 101 } 102 103 @Override 104 @EnforcePermission(LOG_COMPAT_CHANGE) reportChangeByPackageName(long changeId, String packageName, @UserIdInt int userId)105 public void reportChangeByPackageName(long changeId, String packageName, 106 @UserIdInt int userId) { 107 super.reportChangeByPackageName_enforcePermission(); 108 109 ApplicationInfo appInfo = getApplicationInfo(packageName, userId); 110 if (appInfo != null) { 111 reportChangeInternal(changeId, appInfo.uid, ChangeReporter.STATE_LOGGED); 112 } 113 } 114 115 @Override 116 @EnforcePermission(LOG_COMPAT_CHANGE) reportChangeByUid(long changeId, int uid)117 public void reportChangeByUid(long changeId, int uid) { 118 super.reportChangeByUid_enforcePermission(); 119 120 reportChangeInternal(changeId, uid, ChangeReporter.STATE_LOGGED); 121 } 122 123 /** 124 * Report the change, but skip over the sdk target version check. This can be used to force the 125 * debug logs. 126 * 127 * @param changeId of the change to report 128 * @param uid of the user 129 * @param state of the change - enabled/disabled/logged 130 */ reportChangeInternal(long changeId, int uid, int state)131 private void reportChangeInternal(long changeId, int uid, int state) { 132 mChangeReporter.reportChange(uid, changeId, state, true); 133 } 134 135 @Override 136 @EnforcePermission(allOf = {LOG_COMPAT_CHANGE, READ_COMPAT_CHANGE_CONFIG}) isChangeEnabled(long changeId, ApplicationInfo appInfo)137 public boolean isChangeEnabled(long changeId, ApplicationInfo appInfo) { 138 super.isChangeEnabled_enforcePermission(); 139 140 return isChangeEnabledInternal(changeId, appInfo); 141 } 142 143 @Override 144 @EnforcePermission(allOf = {LOG_COMPAT_CHANGE, READ_COMPAT_CHANGE_CONFIG}) isChangeEnabledByPackageName(long changeId, String packageName, @UserIdInt int userId)145 public boolean isChangeEnabledByPackageName(long changeId, String packageName, 146 @UserIdInt int userId) { 147 super.isChangeEnabledByPackageName_enforcePermission(); 148 149 ApplicationInfo appInfo = getApplicationInfo(packageName, userId); 150 if (appInfo == null) { 151 return mCompatConfig.willChangeBeEnabled(changeId, packageName); 152 } 153 return isChangeEnabledInternal(changeId, appInfo); 154 } 155 156 @Override 157 @EnforcePermission(allOf = {LOG_COMPAT_CHANGE, READ_COMPAT_CHANGE_CONFIG}) isChangeEnabledByUid(long changeId, int uid)158 public boolean isChangeEnabledByUid(long changeId, int uid) { 159 super.isChangeEnabledByUid_enforcePermission(); 160 161 return isChangeEnabledByUidInternal(changeId, uid); 162 } 163 164 /** 165 * Internal version of the above method, without logging. 166 * 167 * <p>Does not perform costly permission check. 168 * TODO(b/167551701): Remove this method and add 'loggability' as a changeid property. 169 */ isChangeEnabledInternalNoLogging(long changeId, ApplicationInfo appInfo)170 public boolean isChangeEnabledInternalNoLogging(long changeId, ApplicationInfo appInfo) { 171 return mCompatConfig.isChangeEnabled(changeId, appInfo); 172 } 173 174 /** 175 * Internal version of {@link #isChangeEnabled(long, ApplicationInfo)}. If the provided appInfo 176 * is not null, also reports the change. 177 * 178 * @param changeId of the change to report 179 * @param appInfo the app to check 180 * 181 * <p>Does not perform costly permission check. 182 */ isChangeEnabledInternal(long changeId, ApplicationInfo appInfo)183 public boolean isChangeEnabledInternal(long changeId, ApplicationInfo appInfo) { 184 // Fetch the CompatChange. This is done here instead of in mCompatConfig to avoid multiple 185 // fetches. 186 CompatChange c = mCompatConfig.getCompatChange(changeId); 187 188 boolean enabled = mCompatConfig.isChangeEnabled(c, appInfo); 189 int state = enabled ? ChangeReporter.STATE_ENABLED : ChangeReporter.STATE_DISABLED; 190 if (appInfo != null) { 191 boolean isTargetingLatestSdk = 192 mCompatConfig.isChangeTargetingLatestSdk(c, appInfo.targetSdkVersion); 193 mChangeReporter.reportChange(appInfo.uid, changeId, state, isTargetingLatestSdk); 194 } 195 return enabled; 196 } 197 198 /** 199 * Called by the package manager to check if a given change is enabled for a given package name 200 * and the target sdk version while the package is in the parsing state. 201 * 202 * <p>Does not perform costly permission check. 203 * 204 * @param changeId the ID of the change in question 205 * @param packageName package name to check for 206 * @param targetSdkVersion target sdk version to check for 207 * @return {@code true} if the change would be enabled for this package name. 208 */ isChangeEnabledInternal(long changeId, String packageName, int targetSdkVersion)209 public boolean isChangeEnabledInternal(long changeId, String packageName, 210 int targetSdkVersion) { 211 if (mCompatConfig.willChangeBeEnabled(changeId, packageName)) { 212 final ApplicationInfo appInfo = new ApplicationInfo(); 213 appInfo.packageName = packageName; 214 appInfo.targetSdkVersion = targetSdkVersion; 215 return isChangeEnabledInternalNoLogging(changeId, appInfo); 216 } 217 return false; 218 } 219 220 /** 221 * Internal version of {@link #isChangeEnabledByUid(long, int)}. 222 * 223 * <p>Does not perform costly permission check. 224 */ isChangeEnabledByUidInternal(long changeId, int uid)225 public boolean isChangeEnabledByUidInternal(long changeId, int uid) { 226 String[] packages = mContext.getPackageManager().getPackagesForUid(uid); 227 if (packages == null || packages.length == 0) { 228 return mCompatConfig.defaultChangeIdValue(changeId); 229 } 230 boolean enabled = true; 231 final int userId = UserHandle.getUserId(uid); 232 for (String packageName : packages) { 233 final var appInfo = getApplicationInfo(packageName, userId); 234 enabled &= isChangeEnabledInternal(changeId, appInfo); 235 } 236 return enabled; 237 } 238 239 /** 240 * Internal version of {@link #isChangeEnabledByUid(long, int)}. 241 * 242 * <p>Does not perform costly permission check and logging. 243 */ isChangeEnabledByUidInternalNoLogging(long changeId, int uid)244 public boolean isChangeEnabledByUidInternalNoLogging(long changeId, int uid) { 245 String[] packages = mContext.getPackageManager().getPackagesForUid(uid); 246 if (packages == null || packages.length == 0) { 247 return mCompatConfig.defaultChangeIdValue(changeId); 248 } 249 boolean enabled = true; 250 final int userId = UserHandle.getUserId(uid); 251 for (String packageName : packages) { 252 final var appInfo = getApplicationInfo(packageName, userId); 253 enabled &= isChangeEnabledInternalNoLogging(changeId, appInfo); 254 } 255 return enabled; 256 } 257 258 @Override 259 @EnforcePermission(OVERRIDE_COMPAT_CHANGE_CONFIG) setOverrides(CompatibilityChangeConfig overrides, String packageName)260 public void setOverrides(CompatibilityChangeConfig overrides, String packageName) { 261 super.setOverrides_enforcePermission(); 262 263 Map<Long, PackageOverride> overridesMap = new HashMap<>(); 264 for (long change : overrides.enabledChanges()) { 265 overridesMap.put(change, new PackageOverride.Builder().setEnabled(true).build()); 266 } 267 for (long change : overrides.disabledChanges()) { 268 overridesMap.put(change, new PackageOverride.Builder().setEnabled(false) 269 .build()); 270 } 271 mCompatConfig.addPackageOverrides(new CompatibilityOverrideConfig(overridesMap), 272 packageName, /* skipUnknownChangeIds */ false); 273 killPackage(packageName); 274 } 275 276 @Override 277 @EnforcePermission(OVERRIDE_COMPAT_CHANGE_CONFIG) setOverridesForTest(CompatibilityChangeConfig overrides, String packageName)278 public void setOverridesForTest(CompatibilityChangeConfig overrides, String packageName) { 279 super.setOverridesForTest_enforcePermission(); 280 281 Map<Long, PackageOverride> overridesMap = new HashMap<>(); 282 for (long change : overrides.enabledChanges()) { 283 overridesMap.put(change, new PackageOverride.Builder().setEnabled(true).build()); 284 } 285 for (long change : overrides.disabledChanges()) { 286 overridesMap.put(change, new PackageOverride.Builder().setEnabled(false) 287 .build()); 288 } 289 mCompatConfig.addPackageOverrides(new CompatibilityOverrideConfig(overridesMap), 290 packageName, /* skipUnknownChangeIds */ false); 291 } 292 293 @Override 294 @EnforcePermission(OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD) putAllOverridesOnReleaseBuilds( CompatibilityOverridesByPackageConfig overridesByPackage)295 public void putAllOverridesOnReleaseBuilds( 296 CompatibilityOverridesByPackageConfig overridesByPackage) { 297 super.putAllOverridesOnReleaseBuilds_enforcePermission(); 298 299 for (CompatibilityOverrideConfig overrides : 300 overridesByPackage.packageNameToOverrides.values()) { 301 checkAllCompatOverridesAreOverridable(overrides.overrides.keySet()); 302 } 303 mCompatConfig.addAllPackageOverrides(overridesByPackage, /* skipUnknownChangeIds= */ true); 304 } 305 306 @Override 307 @EnforcePermission(OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD) putOverridesOnReleaseBuilds(CompatibilityOverrideConfig overrides, String packageName)308 public void putOverridesOnReleaseBuilds(CompatibilityOverrideConfig overrides, 309 String packageName) { 310 super.putOverridesOnReleaseBuilds_enforcePermission(); 311 312 checkAllCompatOverridesAreOverridable(overrides.overrides.keySet()); 313 mCompatConfig.addPackageOverrides(overrides, packageName, /* skipUnknownChangeIds= */ true); 314 } 315 316 @Override 317 @EnforcePermission(OVERRIDE_COMPAT_CHANGE_CONFIG) enableTargetSdkChanges(String packageName, int targetSdkVersion)318 public int enableTargetSdkChanges(String packageName, int targetSdkVersion) { 319 super.enableTargetSdkChanges_enforcePermission(); 320 321 int numChanges = 322 mCompatConfig.enableTargetSdkChangesForPackage(packageName, targetSdkVersion); 323 killPackage(packageName); 324 return numChanges; 325 } 326 327 @Override 328 @EnforcePermission(OVERRIDE_COMPAT_CHANGE_CONFIG) disableTargetSdkChanges(String packageName, int targetSdkVersion)329 public int disableTargetSdkChanges(String packageName, int targetSdkVersion) { 330 super.disableTargetSdkChanges_enforcePermission(); 331 332 int numChanges = 333 mCompatConfig.disableTargetSdkChangesForPackage(packageName, targetSdkVersion); 334 killPackage(packageName); 335 return numChanges; 336 } 337 338 @Override 339 @EnforcePermission(OVERRIDE_COMPAT_CHANGE_CONFIG) clearOverrides(String packageName)340 public void clearOverrides(String packageName) { 341 super.clearOverrides_enforcePermission(); 342 343 mCompatConfig.removePackageOverrides(packageName); 344 killPackage(packageName); 345 } 346 347 @Override 348 @EnforcePermission(OVERRIDE_COMPAT_CHANGE_CONFIG) clearOverridesForTest(String packageName)349 public void clearOverridesForTest(String packageName) { 350 super.clearOverridesForTest_enforcePermission(); 351 352 mCompatConfig.removePackageOverrides(packageName); 353 } 354 355 @Override 356 @EnforcePermission(OVERRIDE_COMPAT_CHANGE_CONFIG) clearOverride(long changeId, String packageName)357 public boolean clearOverride(long changeId, String packageName) { 358 super.clearOverride_enforcePermission(); 359 360 boolean existed = mCompatConfig.removeOverride(changeId, packageName); 361 killPackage(packageName); 362 return existed; 363 } 364 365 @Override 366 @EnforcePermission(OVERRIDE_COMPAT_CHANGE_CONFIG) clearOverrideForTest(long changeId, String packageName)367 public boolean clearOverrideForTest(long changeId, String packageName) { 368 super.clearOverrideForTest_enforcePermission(); 369 370 return mCompatConfig.removeOverride(changeId, packageName); 371 } 372 373 @Override 374 @EnforcePermission(OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD) removeAllOverridesOnReleaseBuilds( CompatibilityOverridesToRemoveByPackageConfig overridesToRemoveByPackage)375 public void removeAllOverridesOnReleaseBuilds( 376 CompatibilityOverridesToRemoveByPackageConfig overridesToRemoveByPackage) { 377 super.removeAllOverridesOnReleaseBuilds_enforcePermission(); 378 379 for (CompatibilityOverridesToRemoveConfig overridesToRemove : 380 overridesToRemoveByPackage.packageNameToOverridesToRemove.values()) { 381 checkAllCompatOverridesAreOverridable(overridesToRemove.changeIds); 382 } 383 mCompatConfig.removeAllPackageOverrides(overridesToRemoveByPackage); 384 } 385 386 @Override 387 @EnforcePermission(OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD) removeOverridesOnReleaseBuilds( CompatibilityOverridesToRemoveConfig overridesToRemove, String packageName)388 public void removeOverridesOnReleaseBuilds( 389 CompatibilityOverridesToRemoveConfig overridesToRemove, 390 String packageName) { 391 super.removeOverridesOnReleaseBuilds_enforcePermission(); 392 393 checkAllCompatOverridesAreOverridable(overridesToRemove.changeIds); 394 mCompatConfig.removePackageOverrides(overridesToRemove, packageName); 395 } 396 397 @Override 398 @EnforcePermission(allOf = {LOG_COMPAT_CHANGE, READ_COMPAT_CHANGE_CONFIG}) getAppConfig(ApplicationInfo appInfo)399 public CompatibilityChangeConfig getAppConfig(ApplicationInfo appInfo) { 400 super.getAppConfig_enforcePermission(); 401 402 return mCompatConfig.getAppConfig(appInfo); 403 } 404 405 @Override 406 @EnforcePermission(READ_COMPAT_CHANGE_CONFIG) listAllChanges()407 public CompatibilityChangeInfo[] listAllChanges() { 408 super.listAllChanges_enforcePermission(); 409 410 return mCompatConfig.dumpChanges(); 411 } 412 413 @Override 414 @RequiresNoPermission listUIChanges()415 public CompatibilityChangeInfo[] listUIChanges() { 416 return Arrays.stream(listAllChanges()).filter(this::isShownInUI).toArray( 417 CompatibilityChangeInfo[]::new); 418 } 419 420 /** Checks whether the change is known to the compat config. */ isKnownChangeId(long changeId)421 public boolean isKnownChangeId(long changeId) { 422 return mCompatConfig.isKnownChangeId(changeId); 423 } 424 425 /** 426 * Retrieves the set of disabled changes for a given app. Any change ID not in the returned 427 * array is by default enabled for the app. 428 * 429 * @param appInfo The app in question 430 * @return A sorted long array of change IDs. We use a primitive array to minimize memory 431 * footprint: Every app process will store this array statically so we aim to reduce 432 * overhead as much as possible. 433 */ getDisabledChanges(ApplicationInfo appInfo)434 public long[] getDisabledChanges(ApplicationInfo appInfo) { 435 return mCompatConfig.getDisabledChanges(appInfo); 436 } 437 438 /** 439 * Retrieves the set of changes that should be logged for a given app. Any change ID not in the 440 * returned array is ignored for logging purposes. 441 * 442 * @param appInfo The app in question 443 * @return A sorted long array of change IDs. We use a primitive array to minimize memory 444 * footprint: Every app process will store this array statically so we aim to reduce 445 * overhead as much as possible. 446 */ getLoggableChanges(ApplicationInfo appInfo)447 public long[] getLoggableChanges(ApplicationInfo appInfo) { 448 return mCompatConfig.getLoggableChanges(appInfo); 449 } 450 451 /** 452 * Look up a change ID by name. 453 * 454 * @param name Name of the change to look up 455 * @return The change ID, or {@code -1} if no change with that name exists. 456 */ lookupChangeId(String name)457 public long lookupChangeId(String name) { 458 return mCompatConfig.lookupChangeId(name); 459 } 460 461 @Override dump(FileDescriptor fd, PrintWriter pw, String[] args)462 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 463 if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, "platform_compat", pw)) { 464 return; 465 } 466 mContext.enforceCallingOrSelfPermission( 467 READ_COMPAT_CHANGE_CONFIG, "Cannot read compat change"); 468 mContext.enforceCallingOrSelfPermission( 469 LOG_COMPAT_CHANGE, "Cannot read log compat change usage"); 470 mCompatConfig.dumpConfig(pw); 471 } 472 473 @Override 474 @RequiresNoPermission getOverrideValidator()475 public IOverrideValidator getOverrideValidator() { 476 return mCompatConfig.getOverrideValidator(); 477 } 478 479 /** 480 * Clears information stored about events reported on behalf of an app. 481 * 482 * <p>To be called once upon app start or end. A second call would be a no-op. 483 * 484 * @param appInfo the app to reset 485 */ resetReporting(ApplicationInfo appInfo)486 public void resetReporting(ApplicationInfo appInfo) { 487 mChangeReporter.resetReportedChanges(appInfo.uid); 488 } 489 getApplicationInfo(String packageName, int userId)490 private ApplicationInfo getApplicationInfo(String packageName, int userId) { 491 return LocalServices.getService(PackageManagerInternal.class).getApplicationInfo( 492 packageName, 0, Process.myUid(), userId); 493 } 494 killPackage(String packageName)495 private void killPackage(String packageName) { 496 int uid = LocalServices.getService(PackageManagerInternal.class).getPackageUid(packageName, 497 0, UserHandle.myUserId()); 498 499 if (uid < 0) { 500 Slog.w(TAG, "Didn't find package " + packageName + " on device."); 501 return; 502 } 503 504 Slog.d(TAG, "Killing package " + packageName + " (UID " + uid + ")."); 505 killUid(UserHandle.getAppId(uid)); 506 } 507 killUid(int appId)508 private void killUid(int appId) { 509 final long identity = Binder.clearCallingIdentity(); 510 try { 511 IActivityManager am = ActivityManager.getService(); 512 if (am != null) { 513 am.killUid(appId, UserHandle.USER_ALL, "PlatformCompat overrides"); 514 } 515 } catch (RemoteException e) { 516 /* ignore - same process */ 517 } finally { 518 Binder.restoreCallingIdentity(identity); 519 } 520 } 521 checkAllCompatOverridesAreOverridable(Collection<Long> changeIds)522 private void checkAllCompatOverridesAreOverridable(Collection<Long> changeIds) { 523 for (Long changeId : changeIds) { 524 if (isKnownChangeId(changeId) && !mCompatConfig.isOverridable(changeId)) { 525 throw new SecurityException("Only change ids marked as Overridable can be " 526 + "overridden."); 527 } 528 } 529 } 530 isShownInUI(CompatibilityChangeInfo change)531 private boolean isShownInUI(CompatibilityChangeInfo change) { 532 if (change.getLoggingOnly()) { 533 return false; 534 } 535 if (change.getId() == CompatChange.CTS_SYSTEM_API_CHANGEID) { 536 return false; 537 } 538 if (change.getEnableSinceTargetSdk() > 0) { 539 return change.getEnableSinceTargetSdk() >= Build.VERSION_CODES.Q 540 && change.getEnableSinceTargetSdk() <= mBuildClassifier.platformTargetSdk(); 541 } 542 return true; 543 } 544 545 /** 546 * Registers a listener for change state overrides. 547 * 548 * <p>Only one listener per change is allowed. 549 * 550 * <p>{@code listener.onCompatChange(String)} method is guaranteed to be called with 551 * packageName before the app is killed upon an override change. The state of a change is not 552 * guaranteed to change when {@code listener.onCompatChange(String)} is called. 553 * 554 * @param changeId to get updates for 555 * @param listener the listener that will be called upon a potential change for package 556 * @return {@code true} if a change with changeId was already known, or (@code false} 557 * otherwise 558 * @throws IllegalStateException if a listener was already registered for changeId 559 */ registerListener(long changeId, CompatChange.ChangeListener listener)560 public boolean registerListener(long changeId, CompatChange.ChangeListener listener) { 561 return mCompatConfig.registerListener(changeId, listener); 562 } 563 564 /** 565 * Registers a broadcast receiver that listens for package install, replace or remove. 566 * 567 * @param context the context where the receiver should be registered 568 */ registerPackageReceiver(Context context)569 public void registerPackageReceiver(Context context) { 570 final BroadcastReceiver receiver = new BroadcastReceiver() { 571 @Override 572 public void onReceive(Context context, Intent intent) { 573 if (intent == null) { 574 return; 575 } 576 final Uri packageData = intent.getData(); 577 if (packageData == null) { 578 return; 579 } 580 final String packageName = packageData.getSchemeSpecificPart(); 581 if (packageName == null) { 582 return; 583 } 584 mCompatConfig.recheckOverrides(packageName); 585 } 586 }; 587 IntentFilter filter = new IntentFilter(); 588 filter.addAction(Intent.ACTION_PACKAGE_ADDED); 589 filter.addAction(Intent.ACTION_PACKAGE_REPLACED); 590 filter.addAction(Intent.ACTION_PACKAGE_REMOVED); 591 filter.addDataScheme("package"); 592 context.registerReceiverForAllUsers(receiver, filter, /* broadcastPermission= */ 593 null, /* scheduler= */ null); 594 } 595 596 /** 597 * Registers the observer for 598 * {@link android.provider.Settings.Global#FORCE_NON_DEBUGGABLE_FINAL_BUILD_FOR_COMPAT}. 599 */ registerContentObserver()600 public void registerContentObserver() { 601 mCompatConfig.registerContentObserver(); 602 } 603 } 604