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.List; 40 import java.util.Map; 41 import java.util.concurrent.ConcurrentHashMap; 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 ConcurrentHashMap<String, Boolean> mEvaluatedOverrides; 86 private ConcurrentHashMap<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 ConcurrentHashMap<>(); 118 mRawOverrides = new ConcurrentHashMap<>(); 119 } 120 registerListener(ChangeListener listener)121 synchronized 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 * @param pname Package name to enable the change for. 135 * @param enabled Whether or not to enable the change. 136 */ addPackageOverrideInternal(String pname, boolean enabled)137 private void addPackageOverrideInternal(String pname, boolean enabled) { 138 if (getLoggingOnly()) { 139 throw new IllegalArgumentException( 140 "Can't add overrides for a logging only change " + toString()); 141 } 142 mEvaluatedOverrides.put(pname, enabled); 143 notifyListener(pname); 144 } 145 removePackageOverrideInternal(String pname)146 private void removePackageOverrideInternal(String pname) { 147 if (mEvaluatedOverrides.remove(pname) != null) { 148 notifyListener(pname); 149 } 150 } 151 152 /** 153 * Tentatively set the state of this change for a given package name. 154 * The override will only take effect after that package is installed, if applicable. 155 * 156 * @param packageName Package name to tentatively enable the change for. 157 * @param override The package override to be set 158 * @param allowedState Whether the override is allowed. 159 * @param versionCode The version code of the package. 160 */ addPackageOverride(String packageName, PackageOverride override, OverrideAllowedState allowedState, @Nullable Long versionCode)161 synchronized void addPackageOverride(String packageName, PackageOverride override, 162 OverrideAllowedState allowedState, @Nullable Long versionCode) { 163 if (getLoggingOnly()) { 164 throw new IllegalArgumentException( 165 "Can't add overrides for a logging only change " + toString()); 166 } 167 mRawOverrides.put(packageName, override); 168 recheckOverride(packageName, allowedState, versionCode); 169 } 170 171 /** 172 * Rechecks an existing (and possibly deferred) override. 173 * 174 * <p>For deferred overrides, check if they can be promoted to a regular override. For regular 175 * overrides, check if they need to be demoted to deferred.</p> 176 * 177 * @param packageName Package name to apply deferred overrides for. 178 * @param allowedState Whether the override is allowed. 179 * @param versionCode The version code of the package. 180 * 181 * @return {@code true} if the recheck yielded a result that requires invalidating caches 182 * (a deferred override was consolidated or a regular override was removed). 183 */ recheckOverride(String packageName, OverrideAllowedState allowedState, @Nullable Long versionCode)184 synchronized boolean recheckOverride(String packageName, OverrideAllowedState allowedState, 185 @Nullable Long versionCode) { 186 if (packageName == null) { 187 return false; 188 } 189 boolean allowed = (allowedState.state == OverrideAllowedState.ALLOWED); 190 // If the app is not installed or no longer has raw overrides, evaluate to false 191 if (versionCode == null || !mRawOverrides.containsKey(packageName) || !allowed) { 192 removePackageOverrideInternal(packageName); 193 return false; 194 } 195 // Evaluate the override based on its version 196 int overrideValue = mRawOverrides.get(packageName).evaluate(versionCode); 197 switch (overrideValue) { 198 case VALUE_UNDEFINED: 199 removePackageOverrideInternal(packageName); 200 break; 201 case VALUE_ENABLED: 202 addPackageOverrideInternal(packageName, true); 203 break; 204 case VALUE_DISABLED: 205 addPackageOverrideInternal(packageName, false); 206 break; 207 } 208 return true; 209 } 210 211 /** 212 * Remove any package override for the given package name, restoring the default behaviour. 213 * 214 * <p>Note, this method is not thread safe so callers must ensure thread safety. 215 * 216 * @param pname Package name to reset to defaults for. 217 * @param allowedState Whether the override is allowed. 218 * @param versionCode The version code of the package. 219 */ removePackageOverride(String pname, OverrideAllowedState allowedState, @Nullable Long versionCode)220 synchronized boolean removePackageOverride(String pname, OverrideAllowedState allowedState, 221 @Nullable Long versionCode) { 222 if (mRawOverrides.containsKey(pname)) { 223 allowedState.enforce(getId(), pname); 224 mRawOverrides.remove(pname); 225 recheckOverride(pname, allowedState, versionCode); 226 return true; 227 } 228 return false; 229 } 230 231 /** 232 * Find if this change is enabled for the given package, taking into account any overrides that 233 * exist. 234 * 235 * @param app Info about the app in question 236 * @return {@code true} if the change should be enabled for the package. 237 */ isEnabled(ApplicationInfo app, AndroidBuildClassifier buildClassifier)238 boolean isEnabled(ApplicationInfo app, AndroidBuildClassifier buildClassifier) { 239 if (app == null) { 240 return defaultValue(); 241 } 242 if (app.packageName != null) { 243 final Boolean enabled = mEvaluatedOverrides.get(app.packageName); 244 if (enabled != null) { 245 return enabled; 246 } 247 } 248 if (getDisabled()) { 249 return false; 250 } 251 if (getEnableSinceTargetSdk() != -1) { 252 // If the change is gated by a platform version newer than the one currently installed 253 // on the device, disregard the app's target sdk version. 254 int compareSdk = Math.min(app.targetSdkVersion, buildClassifier.platformTargetSdk()); 255 if (compareSdk != app.targetSdkVersion) { 256 compareSdk = app.targetSdkVersion; 257 } 258 return compareSdk >= getEnableSinceTargetSdk(); 259 } 260 return true; 261 } 262 263 /** 264 * Find if this change will be enabled for the given package after installation. 265 * 266 * @param packageName The package name in question 267 * @return {@code true} if the change should be enabled for the package. 268 */ willBeEnabled(String packageName)269 boolean willBeEnabled(String packageName) { 270 if (packageName == null) { 271 return defaultValue(); 272 } 273 final PackageOverride override = mRawOverrides.get(packageName); 274 if (override != null) { 275 switch (override.evaluateForAllVersions()) { 276 case VALUE_ENABLED: 277 return true; 278 case VALUE_DISABLED: 279 return false; 280 case VALUE_UNDEFINED: 281 return defaultValue(); 282 } 283 } 284 return defaultValue(); 285 } 286 287 /** 288 * Returns the default value for the change id, assuming there are no overrides. 289 * 290 * @return {@code false} if it's a default disabled change, {@code true} otherwise. 291 */ defaultValue()292 boolean defaultValue() { 293 return !getDisabled(); 294 } 295 clearOverrides()296 synchronized void clearOverrides() { 297 mRawOverrides.clear(); 298 mEvaluatedOverrides.clear(); 299 } 300 loadOverrides(ChangeOverrides changeOverrides)301 synchronized void loadOverrides(ChangeOverrides changeOverrides) { 302 // Load deferred overrides for backwards compatibility 303 if (changeOverrides.getDeferred() != null) { 304 for (OverrideValue override : changeOverrides.getDeferred().getOverrideValue()) { 305 mRawOverrides.put(override.getPackageName(), 306 new PackageOverride.Builder().setEnabled( 307 override.getEnabled()).build()); 308 } 309 } 310 311 // Load validated overrides. For backwards compatibility, we also add them to raw overrides. 312 if (changeOverrides.getValidated() != null) { 313 for (OverrideValue override : changeOverrides.getValidated().getOverrideValue()) { 314 mEvaluatedOverrides.put(override.getPackageName(), override.getEnabled()); 315 mRawOverrides.put(override.getPackageName(), 316 new PackageOverride.Builder().setEnabled( 317 override.getEnabled()).build()); 318 } 319 } 320 321 // Load raw overrides 322 if (changeOverrides.getRaw() != null) { 323 for (RawOverrideValue override : changeOverrides.getRaw().getRawOverrideValue()) { 324 PackageOverride packageOverride = new PackageOverride.Builder() 325 .setMinVersionCode(override.getMinVersionCode()) 326 .setMaxVersionCode(override.getMaxVersionCode()) 327 .setEnabled(override.getEnabled()) 328 .build(); 329 mRawOverrides.put(override.getPackageName(), packageOverride); 330 } 331 } 332 } 333 saveOverrides()334 synchronized ChangeOverrides saveOverrides() { 335 if (mRawOverrides.isEmpty()) { 336 return null; 337 } 338 ChangeOverrides changeOverrides = new ChangeOverrides(); 339 changeOverrides.setChangeId(getId()); 340 ChangeOverrides.Raw rawOverrides = new ChangeOverrides.Raw(); 341 List<RawOverrideValue> rawList = rawOverrides.getRawOverrideValue(); 342 for (Map.Entry<String, PackageOverride> entry : mRawOverrides.entrySet()) { 343 RawOverrideValue override = new RawOverrideValue(); 344 override.setPackageName(entry.getKey()); 345 override.setMinVersionCode(entry.getValue().getMinVersionCode()); 346 override.setMaxVersionCode(entry.getValue().getMaxVersionCode()); 347 override.setEnabled(entry.getValue().isEnabled()); 348 rawList.add(override); 349 } 350 changeOverrides.setRaw(rawOverrides); 351 352 ChangeOverrides.Validated validatedOverrides = new ChangeOverrides.Validated(); 353 List<OverrideValue> validatedList = validatedOverrides.getOverrideValue(); 354 for (Map.Entry<String, Boolean> entry : mEvaluatedOverrides.entrySet()) { 355 OverrideValue override = new OverrideValue(); 356 override.setPackageName(entry.getKey()); 357 override.setEnabled(entry.getValue()); 358 validatedList.add(override); 359 } 360 changeOverrides.setValidated(validatedOverrides); 361 return changeOverrides; 362 } 363 364 @Override toString()365 public String toString() { 366 StringBuilder sb = new StringBuilder("ChangeId(") 367 .append(getId()); 368 if (getName() != null) { 369 sb.append("; name=").append(getName()); 370 } 371 if (getEnableSinceTargetSdk() != -1) { 372 sb.append("; enableSinceTargetSdk=").append(getEnableSinceTargetSdk()); 373 } 374 if (getDisabled()) { 375 sb.append("; disabled"); 376 } 377 if (getLoggingOnly()) { 378 sb.append("; loggingOnly"); 379 } 380 if (!mEvaluatedOverrides.isEmpty()) { 381 sb.append("; packageOverrides=").append(mEvaluatedOverrides); 382 } 383 if (!mRawOverrides.isEmpty()) { 384 sb.append("; rawOverrides=").append(mRawOverrides); 385 } 386 if (getOverridable()) { 387 sb.append("; overridable"); 388 } 389 return sb.append(")").toString(); 390 } 391 notifyListener(String packageName)392 private synchronized void notifyListener(String packageName) { 393 if (mListener != null) { 394 mListener.onCompatChange(packageName); 395 } 396 } 397 } 398