blob: 11ba3d4ba9a2988d4b4e655898740e10a91a3a3e [file] [log] [blame]
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.content.pm;
import static android.provider.DeviceConfig.NAMESPACE_CONSTRAIN_DISPLAY_APIS;
import android.provider.DeviceConfig;
import android.util.Slog;
import java.util.Arrays;
import java.util.List;
/**
* Class for processing flags in the Device Config namespace 'constrain_display_apis'.
*
* @hide
*/
public final class ConstrainDisplayApisConfig {
private static final String TAG = ConstrainDisplayApisConfig.class.getSimpleName();
/**
* A string flag whose value holds a comma separated list of package entries in the format
* '<package-name>:<min-version-code>?:<max-version-code>?' for which Display APIs should never
* be constrained.
*/
private static final String FLAG_NEVER_CONSTRAIN_DISPLAY_APIS = "never_constrain_display_apis";
/**
* A boolean flag indicating whether Display APIs should never be constrained for all
* packages. If true, {@link #FLAG_NEVER_CONSTRAIN_DISPLAY_APIS} is ignored.
*/
private static final String FLAG_NEVER_CONSTRAIN_DISPLAY_APIS_ALL_PACKAGES =
"never_constrain_display_apis_all_packages";
/**
* A string flag whose value holds a comma separated list of package entries in the format
* '<package-name>:<min-version-code>?:<max-version-code>?' for which Display APIs should
* always be constrained.
*/
private static final String FLAG_ALWAYS_CONSTRAIN_DISPLAY_APIS =
"always_constrain_display_apis";
/**
* Returns true if either the flag 'never_constrain_display_apis_all_packages' is true or the
* flag 'never_constrain_display_apis' contains a package entry that matches the given {@code
* applicationInfo}.
*
* @param applicationInfo Information about the application/package.
*/
public static boolean neverConstrainDisplayApis(ApplicationInfo applicationInfo) {
if (DeviceConfig.getBoolean(NAMESPACE_CONSTRAIN_DISPLAY_APIS,
FLAG_NEVER_CONSTRAIN_DISPLAY_APIS_ALL_PACKAGES, /* defaultValue= */ false)) {
return true;
}
return flagHasMatchingPackageEntry(FLAG_NEVER_CONSTRAIN_DISPLAY_APIS, applicationInfo);
}
/**
* Returns true if the flag 'always_constrain_display_apis' contains a package entry that
* matches the given {@code applicationInfo}.
*
* @param applicationInfo Information about the application/package.
*/
public static boolean alwaysConstrainDisplayApis(ApplicationInfo applicationInfo) {
return flagHasMatchingPackageEntry(FLAG_ALWAYS_CONSTRAIN_DISPLAY_APIS, applicationInfo);
}
/**
* Returns true if the flag with the given {@code flagName} contains a package entry that
* matches the given {@code applicationInfo}.
*
* @param applicationInfo Information about the application/package.
*/
private static boolean flagHasMatchingPackageEntry(String flagName,
ApplicationInfo applicationInfo) {
String configStr = DeviceConfig.getString(NAMESPACE_CONSTRAIN_DISPLAY_APIS,
flagName, /* defaultValue= */ "");
// String#split returns a non-empty array given an empty string.
if (configStr.isEmpty()) {
return false;
}
for (String packageEntryString : configStr.split(",")) {
if (matchesApplicationInfo(packageEntryString, applicationInfo)) {
return true;
}
}
return false;
}
/**
* Parses the given {@code packageEntryString} and returns true if {@code
* applicationInfo.packageName} matches the package name in the config and {@code
* applicationInfo.longVersionCode} is within the version range in the config.
*
* <p>Logs a warning and returns false in case the given {@code packageEntryString} is invalid.
*
* @param packageEntryStr A package entry expected to be in the format
* '<package-name>:<min-version-code>?:<max-version-code>?'.
* @param applicationInfo Information about the application/package.
*/
private static boolean matchesApplicationInfo(String packageEntryStr,
ApplicationInfo applicationInfo) {
List<String> packageAndVersions = Arrays.asList(packageEntryStr.split(":", 3));
if (packageAndVersions.size() != 3) {
Slog.w(TAG, "Invalid package entry in flag 'never_constrain_display_apis': "
+ packageEntryStr);
return false;
}
String packageName = packageAndVersions.get(0);
String minVersionCodeStr = packageAndVersions.get(1);
String maxVersionCodeStr = packageAndVersions.get(2);
if (!packageName.equals(applicationInfo.packageName)) {
return false;
}
long version = applicationInfo.longVersionCode;
try {
if (!minVersionCodeStr.isEmpty() && version < Long.parseLong(minVersionCodeStr)) {
return false;
}
if (!maxVersionCodeStr.isEmpty() && version > Long.parseLong(maxVersionCodeStr)) {
return false;
}
} catch (NumberFormatException e) {
Slog.w(TAG, "Invalid APK version code in package entry: " + packageEntryStr);
return false;
}
return true;
}
}