• 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.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