• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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 android.content.pm;
18 
19 import static android.provider.DeviceConfig.NAMESPACE_CONSTRAIN_DISPLAY_APIS;
20 
21 import android.provider.DeviceConfig;
22 import android.util.Slog;
23 
24 import java.util.Arrays;
25 import java.util.List;
26 
27 /**
28  * Class for processing flags in the Device Config namespace 'constrain_display_apis'.
29  *
30  * @hide
31  */
32 public final class ConstrainDisplayApisConfig {
33     private static final String TAG = ConstrainDisplayApisConfig.class.getSimpleName();
34 
35     /**
36      * A string flag whose value holds a comma separated list of package entries in the format
37      * '<package-name>:<min-version-code>?:<max-version-code>?' for which Display APIs should never
38      * be constrained.
39      */
40     private static final String FLAG_NEVER_CONSTRAIN_DISPLAY_APIS = "never_constrain_display_apis";
41 
42     /**
43      * A boolean flag indicating whether Display APIs should never be constrained for all
44      * packages. If true, {@link #FLAG_NEVER_CONSTRAIN_DISPLAY_APIS} is ignored.
45      */
46     private static final String FLAG_NEVER_CONSTRAIN_DISPLAY_APIS_ALL_PACKAGES =
47             "never_constrain_display_apis_all_packages";
48 
49     /**
50      * A string flag whose value holds a comma separated list of package entries in the format
51      * '<package-name>:<min-version-code>?:<max-version-code>?' for which Display APIs should
52      * always be constrained.
53      */
54     private static final String FLAG_ALWAYS_CONSTRAIN_DISPLAY_APIS =
55             "always_constrain_display_apis";
56 
57     /**
58      * Returns true if either the flag 'never_constrain_display_apis_all_packages' is true or the
59      * flag 'never_constrain_display_apis' contains a package entry that matches the given {@code
60      * applicationInfo}.
61      *
62      * @param applicationInfo Information about the application/package.
63      */
neverConstrainDisplayApis(ApplicationInfo applicationInfo)64     public static boolean neverConstrainDisplayApis(ApplicationInfo applicationInfo) {
65         if (DeviceConfig.getBoolean(NAMESPACE_CONSTRAIN_DISPLAY_APIS,
66                 FLAG_NEVER_CONSTRAIN_DISPLAY_APIS_ALL_PACKAGES, /* defaultValue= */ false)) {
67             return true;
68         }
69 
70         return flagHasMatchingPackageEntry(FLAG_NEVER_CONSTRAIN_DISPLAY_APIS, applicationInfo);
71     }
72 
73     /**
74      * Returns true if the flag 'always_constrain_display_apis' contains a package entry that
75      * matches the given {@code applicationInfo}.
76      *
77      * @param applicationInfo Information about the application/package.
78      */
alwaysConstrainDisplayApis(ApplicationInfo applicationInfo)79     public static boolean alwaysConstrainDisplayApis(ApplicationInfo applicationInfo) {
80         return flagHasMatchingPackageEntry(FLAG_ALWAYS_CONSTRAIN_DISPLAY_APIS, applicationInfo);
81     }
82 
83     /**
84      * Returns true if the flag with the given {@code flagName} contains a package entry that
85      * matches the given {@code applicationInfo}.
86      *
87      * @param applicationInfo Information about the application/package.
88      */
flagHasMatchingPackageEntry(String flagName, ApplicationInfo applicationInfo)89     private static boolean flagHasMatchingPackageEntry(String flagName,
90             ApplicationInfo applicationInfo) {
91         String configStr = DeviceConfig.getString(NAMESPACE_CONSTRAIN_DISPLAY_APIS,
92                 flagName, /* defaultValue= */ "");
93 
94         // String#split returns a non-empty array given an empty string.
95         if (configStr.isEmpty()) {
96             return false;
97         }
98 
99         for (String packageEntryString : configStr.split(",")) {
100             if (matchesApplicationInfo(packageEntryString, applicationInfo)) {
101                 return true;
102             }
103         }
104 
105         return false;
106     }
107 
108     /**
109      * Parses the given {@code packageEntryString} and returns true if {@code
110      * applicationInfo.packageName} matches the package name in the config and {@code
111      * applicationInfo.longVersionCode} is within the version range in the config.
112      *
113      * <p>Logs a warning and returns false in case the given {@code packageEntryString} is invalid.
114      *
115      * @param packageEntryStr A package entry expected to be in the format
116      *                        '<package-name>:<min-version-code>?:<max-version-code>?'.
117      * @param applicationInfo Information about the application/package.
118      */
matchesApplicationInfo(String packageEntryStr, ApplicationInfo applicationInfo)119     private static boolean matchesApplicationInfo(String packageEntryStr,
120             ApplicationInfo applicationInfo) {
121         List<String> packageAndVersions = Arrays.asList(packageEntryStr.split(":", 3));
122         if (packageAndVersions.size() != 3) {
123             Slog.w(TAG, "Invalid package entry in flag 'never_constrain_display_apis': "
124                     + packageEntryStr);
125             return false;
126         }
127         String packageName = packageAndVersions.get(0);
128         String minVersionCodeStr = packageAndVersions.get(1);
129         String maxVersionCodeStr = packageAndVersions.get(2);
130 
131         if (!packageName.equals(applicationInfo.packageName)) {
132             return false;
133         }
134         long version = applicationInfo.longVersionCode;
135         try {
136             if (!minVersionCodeStr.isEmpty() && version < Long.parseLong(minVersionCodeStr)) {
137                 return false;
138             }
139             if (!maxVersionCodeStr.isEmpty() && version > Long.parseLong(maxVersionCodeStr)) {
140                 return false;
141             }
142         } catch (NumberFormatException e) {
143             Slog.w(TAG, "Invalid APK version code in package entry: " + packageEntryStr);
144             return false;
145         }
146         return true;
147     }
148 }
149