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