• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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