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