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.app.compat.PackageOverride.VALUE_DISABLED; 20 import static android.app.compat.PackageOverride.VALUE_ENABLED; 21 import static android.app.compat.PackageOverride.VALUE_UNDEFINED; 22 23 import android.annotation.Nullable; 24 import android.app.compat.PackageOverride; 25 import android.compat.annotation.ChangeId; 26 import android.compat.annotation.Disabled; 27 import android.compat.annotation.EnabledSince; 28 import android.compat.annotation.Overridable; 29 import android.content.pm.ApplicationInfo; 30 31 import com.android.internal.compat.AndroidBuildClassifier; 32 import com.android.internal.compat.CompatibilityChangeInfo; 33 import com.android.internal.compat.OverrideAllowedState; 34 import com.android.server.compat.config.Change; 35 import com.android.server.compat.overrides.ChangeOverrides; 36 import com.android.server.compat.overrides.OverrideValue; 37 import com.android.server.compat.overrides.RawOverrideValue; 38 39 import java.util.HashMap; 40 import java.util.List; 41 import java.util.Map; 42 43 /** 44 * Represents the state of a single compatibility change. 45 * 46 * <p>A compatibility change has a default setting, determined by the {@code enableAfterTargetSdk} 47 * and {@code disabled} constructor parameters. If a change is {@code disabled}, this overrides any 48 * target SDK criteria set. These settings can be overridden for a specific package using 49 * {@link #addPackageOverrideInternal(String, boolean)}. 50 * 51 * <p>Note, this class is not thread safe so callers must ensure thread safety. 52 */ 53 public final class CompatChange extends CompatibilityChangeInfo { 54 55 /** 56 * A change ID to be used only in the CTS test for this SystemApi 57 */ 58 @ChangeId 59 @EnabledSince(targetSdkVersion = 31) // Needs to be > test APK targetSdkVersion. 60 static final long CTS_SYSTEM_API_CHANGEID = 149391281; // This is a bug id. 61 62 /** 63 * An overridable change ID to be used only in the CTS test for this SystemApi 64 */ 65 @ChangeId 66 @Disabled 67 @Overridable 68 static final long CTS_SYSTEM_API_OVERRIDABLE_CHANGEID = 174043039; // This is a bug id. 69 70 71 /** 72 * Callback listener for when compat changes are updated for a package. 73 * See {@link #registerListener(ChangeListener)} for more details. 74 */ 75 public interface ChangeListener { 76 /** 77 * Called upon an override change for packageName and the change this listener is 78 * registered for. Called before the app is killed. 79 */ onCompatChange(String packageName)80 void onCompatChange(String packageName); 81 } 82 83 ChangeListener mListener = null; 84 85 private Map<String, Boolean> mEvaluatedOverrides; 86 private Map<String, PackageOverride> mRawOverrides; 87 CompatChange(long changeId)88 public CompatChange(long changeId) { 89 this(changeId, null, -1, -1, false, false, null, false); 90 } 91 92 /** 93 * @param change an object generated by services/core/xsd/platform-compat-config.xsd 94 */ CompatChange(Change change)95 public CompatChange(Change change) { 96 this(change.getId(), change.getName(), change.getEnableAfterTargetSdk(), 97 change.getEnableSinceTargetSdk(), change.getDisabled(), change.getLoggingOnly(), 98 change.getDescription(), change.getOverridable()); 99 } 100 101 /** 102 * @param changeId Unique ID for the change. See {@link android.compat.Compatibility}. 103 * @param name Short descriptive name. 104 * @param enableAfterTargetSdk {@code targetSdkVersion} restriction. See {@link EnabledAfter}; 105 * -1 if the change is always enabled. 106 * @param enableSinceTargetSdk {@code targetSdkVersion} restriction. See {@link EnabledSince}; 107 * -1 if the change is always enabled. 108 * @param disabled If {@code true}, overrides any {@code enableAfterTargetSdk} set. 109 */ CompatChange(long changeId, @Nullable String name, int enableAfterTargetSdk, int enableSinceTargetSdk, boolean disabled, boolean loggingOnly, String description, boolean overridable)110 public CompatChange(long changeId, @Nullable String name, int enableAfterTargetSdk, 111 int enableSinceTargetSdk, boolean disabled, boolean loggingOnly, String description, 112 boolean overridable) { 113 super(changeId, name, enableAfterTargetSdk, enableSinceTargetSdk, disabled, loggingOnly, 114 description, overridable); 115 116 // Initialize override maps. 117 mEvaluatedOverrides = new HashMap<>(); 118 mRawOverrides = new HashMap<>(); 119 } 120 registerListener(ChangeListener listener)121 void registerListener(ChangeListener listener) { 122 if (mListener != null) { 123 throw new IllegalStateException( 124 "Listener for change " + toString() + " already registered."); 125 } 126 mListener = listener; 127 } 128 129 130 /** 131 * Force the enabled state of this change for a given package name. The change will only take 132 * effect after that packages process is killed and restarted. 133 * 134 * <p>Note, this method is not thread safe so callers must ensure thread safety. 135 * 136 * @param pname Package name to enable the change for. 137 * @param enabled Whether or not to enable the change. 138 */ addPackageOverrideInternal(String pname, boolean enabled)139 private void addPackageOverrideInternal(String pname, boolean enabled) { 140 if (getLoggingOnly()) { 141 throw new IllegalArgumentException( 142 "Can't add overrides for a logging only change " + toString()); 143 } 144 mEvaluatedOverrides.put(pname, enabled); 145 notifyListener(pname); 146 } 147 removePackageOverrideInternal(String pname)148 private void removePackageOverrideInternal(String pname) { 149 if (mEvaluatedOverrides.remove(pname) != null) { 150 notifyListener(pname); 151 } 152 } 153 154 /** 155 * Tentatively set the state of this change for a given package name. 156 * The override will only take effect after that package is installed, if applicable. 157 * 158 * <p>Note, this method is not thread safe so callers must ensure thread safety. 159 * 160 * @param packageName Package name to tentatively enable the change for. 161 * @param override The package override to be set 162 * @param allowedState Whether the override is allowed. 163 * @param versionCode The version code of the package. 164 */ addPackageOverride(String packageName, PackageOverride override, OverrideAllowedState allowedState, @Nullable Long versionCode)165 void addPackageOverride(String packageName, PackageOverride override, 166 OverrideAllowedState allowedState, @Nullable Long versionCode) { 167 if (getLoggingOnly()) { 168 throw new IllegalArgumentException( 169 "Can't add overrides for a logging only change " + toString()); 170 } 171 mRawOverrides.put(packageName, override); 172 recheckOverride(packageName, allowedState, versionCode); 173 } 174 175 /** 176 * Rechecks an existing (and possibly deferred) override. 177 * 178 * <p>For deferred overrides, check if they can be promoted to a regular override. For regular 179 * overrides, check if they need to be demoted to deferred.</p> 180 * 181 * @param packageName Package name to apply deferred overrides for. 182 * @param allowedState Whether the override is allowed. 183 * @param versionCode The version code of the package. 184 * 185 * @return {@code true} if the recheck yielded a result that requires invalidating caches 186 * (a deferred override was consolidated or a regular override was removed). 187 */ recheckOverride(String packageName, OverrideAllowedState allowedState, @Nullable Long versionCode)188 boolean recheckOverride(String packageName, OverrideAllowedState allowedState, 189 @Nullable Long versionCode) { 190 boolean allowed = (allowedState.state == OverrideAllowedState.ALLOWED); 191 192 // If the app is not installed or no longer has raw overrides, evaluate to false 193 if (versionCode == null || !hasRawOverride(packageName) || !allowed) { 194 removePackageOverrideInternal(packageName); 195 return false; 196 } 197 198 // Evaluate the override based on its version 199 int overrideValue = mRawOverrides.get(packageName).evaluate(versionCode); 200 switch (overrideValue) { 201 case VALUE_UNDEFINED: 202 removePackageOverrideInternal(packageName); 203 break; 204 case VALUE_ENABLED: 205 addPackageOverrideInternal(packageName, true); 206 break; 207 case VALUE_DISABLED: 208 addPackageOverrideInternal(packageName, false); 209 break; 210 } 211 return true; 212 } 213 hasPackageOverride(String pname)214 boolean hasPackageOverride(String pname) { 215 return mRawOverrides.containsKey(pname); 216 } 217 218 /** 219 * Remove any package override for the given package name, restoring the default behaviour. 220 * 221 * <p>Note, this method is not thread safe so callers must ensure thread safety. 222 * 223 * @param pname Package name to reset to defaults for. 224 * @param allowedState Whether the override is allowed. 225 * @param versionCode The version code of the package. 226 */ removePackageOverride(String pname, OverrideAllowedState allowedState, @Nullable Long versionCode)227 boolean removePackageOverride(String pname, OverrideAllowedState allowedState, 228 @Nullable Long versionCode) { 229 if (mRawOverrides.remove(pname) != null) { 230 recheckOverride(pname, allowedState, versionCode); 231 return true; 232 } 233 return false; 234 } 235 236 /** 237 * Find if this change is enabled for the given package, taking into account any overrides that 238 * exist. 239 * 240 * @param app Info about the app in question 241 * @return {@code true} if the change should be enabled for the package. 242 */ isEnabled(ApplicationInfo app, AndroidBuildClassifier buildClassifier)243 boolean isEnabled(ApplicationInfo app, AndroidBuildClassifier buildClassifier) { 244 if (app == null) { 245 return defaultValue(); 246 } 247 if (mEvaluatedOverrides.containsKey(app.packageName)) { 248 return mEvaluatedOverrides.get(app.packageName); 249 } 250 if (getDisabled()) { 251 return false; 252 } 253 if (getEnableSinceTargetSdk() != -1) { 254 // If the change is gated by a platform version newer than the one currently installed 255 // on the device, disregard the app's target sdk version. 256 int compareSdk = Math.min(app.targetSdkVersion, buildClassifier.platformTargetSdk()); 257 if (compareSdk != app.targetSdkVersion) { 258 compareSdk = app.targetSdkVersion; 259 } 260 return compareSdk >= getEnableSinceTargetSdk(); 261 } 262 return true; 263 } 264 265 /** 266 * Find if this change will be enabled for the given package after installation. 267 * 268 * @param packageName The package name in question 269 * @return {@code true} if the change should be enabled for the package. 270 */ willBeEnabled(String packageName)271 boolean willBeEnabled(String packageName) { 272 if (hasRawOverride(packageName)) { 273 int eval = mRawOverrides.get(packageName).evaluateForAllVersions(); 274 switch (eval) { 275 case VALUE_ENABLED: 276 return true; 277 case VALUE_DISABLED: 278 return false; 279 case VALUE_UNDEFINED: 280 return defaultValue(); 281 } 282 } 283 return defaultValue(); 284 } 285 286 /** 287 * Returns the default value for the change id, assuming there are no overrides. 288 * 289 * @return {@code false} if it's a default disabled change, {@code true} otherwise. 290 */ defaultValue()291 boolean defaultValue() { 292 return !getDisabled(); 293 } 294 295 /** 296 * Checks whether a change has an override for a package. 297 * @param packageName name of the package 298 * @return true if there is such override 299 */ hasOverride(String packageName)300 private boolean hasOverride(String packageName) { 301 return mEvaluatedOverrides.containsKey(packageName); 302 } 303 304 /** 305 * Checks whether a change has a deferred override for a package. 306 * @param packageName name of the package 307 * @return true if there is such a deferred override 308 */ hasRawOverride(String packageName)309 private boolean hasRawOverride(String packageName) { 310 return mRawOverrides.containsKey(packageName); 311 } 312 clearOverrides()313 void clearOverrides() { 314 mRawOverrides.clear(); 315 mEvaluatedOverrides.clear(); 316 } 317 loadOverrides(ChangeOverrides changeOverrides)318 void loadOverrides(ChangeOverrides changeOverrides) { 319 // Load deferred overrides for backwards compatibility 320 if (changeOverrides.getDeferred() != null) { 321 for (OverrideValue override : changeOverrides.getDeferred().getOverrideValue()) { 322 mRawOverrides.put(override.getPackageName(), 323 new PackageOverride.Builder().setEnabled( 324 override.getEnabled()).build()); 325 } 326 } 327 328 // Load validated overrides. For backwards compatibility, we also add them to raw overrides. 329 if (changeOverrides.getValidated() != null) { 330 for (OverrideValue override : changeOverrides.getValidated().getOverrideValue()) { 331 mEvaluatedOverrides.put(override.getPackageName(), override.getEnabled()); 332 mRawOverrides.put(override.getPackageName(), 333 new PackageOverride.Builder().setEnabled( 334 override.getEnabled()).build()); 335 } 336 } 337 338 // Load raw overrides 339 if (changeOverrides.getRaw() != null) { 340 for (RawOverrideValue override : changeOverrides.getRaw().getRawOverrideValue()) { 341 PackageOverride packageOverride = new PackageOverride.Builder() 342 .setMinVersionCode(override.getMinVersionCode()) 343 .setMaxVersionCode(override.getMaxVersionCode()) 344 .setEnabled(override.getEnabled()) 345 .build(); 346 mRawOverrides.put(override.getPackageName(), packageOverride); 347 } 348 } 349 } 350 saveOverrides()351 ChangeOverrides saveOverrides() { 352 if (mRawOverrides.isEmpty()) { 353 return null; 354 } 355 ChangeOverrides changeOverrides = new ChangeOverrides(); 356 changeOverrides.setChangeId(getId()); 357 ChangeOverrides.Raw rawOverrides = new ChangeOverrides.Raw(); 358 List<RawOverrideValue> rawList = rawOverrides.getRawOverrideValue(); 359 for (Map.Entry<String, PackageOverride> entry : mRawOverrides.entrySet()) { 360 RawOverrideValue override = new RawOverrideValue(); 361 override.setPackageName(entry.getKey()); 362 override.setMinVersionCode(entry.getValue().getMinVersionCode()); 363 override.setMaxVersionCode(entry.getValue().getMaxVersionCode()); 364 override.setEnabled(entry.getValue().isEnabled()); 365 rawList.add(override); 366 } 367 changeOverrides.setRaw(rawOverrides); 368 369 ChangeOverrides.Validated validatedOverrides = new ChangeOverrides.Validated(); 370 List<OverrideValue> validatedList = validatedOverrides.getOverrideValue(); 371 for (Map.Entry<String, Boolean> entry : mEvaluatedOverrides.entrySet()) { 372 OverrideValue override = new OverrideValue(); 373 override.setPackageName(entry.getKey()); 374 override.setEnabled(entry.getValue()); 375 validatedList.add(override); 376 } 377 changeOverrides.setValidated(validatedOverrides); 378 return changeOverrides; 379 } 380 381 @Override toString()382 public String toString() { 383 StringBuilder sb = new StringBuilder("ChangeId(") 384 .append(getId()); 385 if (getName() != null) { 386 sb.append("; name=").append(getName()); 387 } 388 if (getEnableSinceTargetSdk() != -1) { 389 sb.append("; enableSinceTargetSdk=").append(getEnableSinceTargetSdk()); 390 } 391 if (getDisabled()) { 392 sb.append("; disabled"); 393 } 394 if (getLoggingOnly()) { 395 sb.append("; loggingOnly"); 396 } 397 if (!mEvaluatedOverrides.isEmpty()) { 398 sb.append("; packageOverrides=").append(mEvaluatedOverrides); 399 } 400 if (!mRawOverrides.isEmpty()) { 401 sb.append("; rawOverrides=").append(mRawOverrides); 402 } 403 if (getOverridable()) { 404 sb.append("; overridable"); 405 } 406 return sb.append(")").toString(); 407 } 408 notifyListener(String packageName)409 private void notifyListener(String packageName) { 410 if (mListener != null) { 411 mListener.onCompatChange(packageName); 412 } 413 } 414 } 415