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 return compareSdk >= getEnableSinceTargetSdk(); 256 } 257 return true; 258 } 259 260 /** 261 * Find if this change will be enabled for the given package after installation. 262 * 263 * @param packageName The package name in question 264 * @return {@code true} if the change should be enabled for the package. 265 */ willBeEnabled(String packageName)266 boolean willBeEnabled(String packageName) { 267 if (packageName == null) { 268 return defaultValue(); 269 } 270 final PackageOverride override = mRawOverrides.get(packageName); 271 if (override != null) { 272 switch (override.evaluateForAllVersions()) { 273 case VALUE_ENABLED: 274 return true; 275 case VALUE_DISABLED: 276 return false; 277 case VALUE_UNDEFINED: 278 return defaultValue(); 279 } 280 } 281 return defaultValue(); 282 } 283 284 /** 285 * Returns the default value for the change id, assuming there are no overrides. 286 * 287 * @return {@code false} if it's a default disabled change, {@code true} otherwise. 288 */ defaultValue()289 boolean defaultValue() { 290 return !getDisabled(); 291 } 292 clearOverrides()293 synchronized void clearOverrides() { 294 mRawOverrides.clear(); 295 mEvaluatedOverrides.clear(); 296 } 297 loadOverrides(ChangeOverrides changeOverrides)298 synchronized void loadOverrides(ChangeOverrides changeOverrides) { 299 // Load deferred overrides for backwards compatibility 300 if (changeOverrides.getDeferred() != null) { 301 for (OverrideValue override : changeOverrides.getDeferred().getOverrideValue()) { 302 mRawOverrides.put(override.getPackageName(), 303 new PackageOverride.Builder().setEnabled( 304 override.getEnabled()).build()); 305 } 306 } 307 308 // Load validated overrides. For backwards compatibility, we also add them to raw overrides. 309 if (changeOverrides.getValidated() != null) { 310 for (OverrideValue override : changeOverrides.getValidated().getOverrideValue()) { 311 mEvaluatedOverrides.put(override.getPackageName(), override.getEnabled()); 312 mRawOverrides.put(override.getPackageName(), 313 new PackageOverride.Builder().setEnabled( 314 override.getEnabled()).build()); 315 } 316 } 317 318 // Load raw overrides 319 if (changeOverrides.getRaw() != null) { 320 for (RawOverrideValue override : changeOverrides.getRaw().getRawOverrideValue()) { 321 PackageOverride packageOverride = new PackageOverride.Builder() 322 .setMinVersionCode(override.getMinVersionCode()) 323 .setMaxVersionCode(override.getMaxVersionCode()) 324 .setEnabled(override.getEnabled()) 325 .build(); 326 mRawOverrides.put(override.getPackageName(), packageOverride); 327 } 328 } 329 } 330 saveOverrides()331 synchronized ChangeOverrides saveOverrides() { 332 if (mRawOverrides.isEmpty()) { 333 return null; 334 } 335 ChangeOverrides changeOverrides = new ChangeOverrides(); 336 changeOverrides.setChangeId(getId()); 337 ChangeOverrides.Raw rawOverrides = new ChangeOverrides.Raw(); 338 List<RawOverrideValue> rawList = rawOverrides.getRawOverrideValue(); 339 for (Map.Entry<String, PackageOverride> entry : mRawOverrides.entrySet()) { 340 RawOverrideValue override = new RawOverrideValue(); 341 override.setPackageName(entry.getKey()); 342 override.setMinVersionCode(entry.getValue().getMinVersionCode()); 343 override.setMaxVersionCode(entry.getValue().getMaxVersionCode()); 344 override.setEnabled(entry.getValue().isEnabled()); 345 rawList.add(override); 346 } 347 changeOverrides.setRaw(rawOverrides); 348 349 ChangeOverrides.Validated validatedOverrides = new ChangeOverrides.Validated(); 350 List<OverrideValue> validatedList = validatedOverrides.getOverrideValue(); 351 for (Map.Entry<String, Boolean> entry : mEvaluatedOverrides.entrySet()) { 352 OverrideValue override = new OverrideValue(); 353 override.setPackageName(entry.getKey()); 354 override.setEnabled(entry.getValue()); 355 validatedList.add(override); 356 } 357 changeOverrides.setValidated(validatedOverrides); 358 return changeOverrides; 359 } 360 361 @Override toString()362 public String toString() { 363 StringBuilder sb = new StringBuilder("ChangeId(") 364 .append(getId()); 365 if (getName() != null) { 366 sb.append("; name=").append(getName()); 367 } 368 if (getEnableSinceTargetSdk() != -1) { 369 sb.append("; enableSinceTargetSdk=").append(getEnableSinceTargetSdk()); 370 } 371 if (getDisabled()) { 372 sb.append("; disabled"); 373 } 374 if (getLoggingOnly()) { 375 sb.append("; loggingOnly"); 376 } 377 if (!mEvaluatedOverrides.isEmpty()) { 378 sb.append("; packageOverrides=").append(mEvaluatedOverrides); 379 } 380 if (!mRawOverrides.isEmpty()) { 381 sb.append("; rawOverrides=").append(mRawOverrides); 382 } 383 if (getOverridable()) { 384 sb.append("; overridable"); 385 } 386 return sb.append(")").toString(); 387 } 388 notifyListener(String packageName)389 private synchronized void notifyListener(String packageName) { 390 if (mListener != null) { 391 mListener.onCompatChange(packageName); 392 } 393 } 394 } 395