diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java index 8daf9f0450c46..d1ef5917ba47f 100644 --- a/core/java/android/content/pm/ActivityInfo.java +++ b/core/java/android/content/pm/ActivityInfo.java @@ -1367,18 +1367,18 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { * Returns if the activity should never be sandboxed to the activity window bounds. * @hide */ - public boolean neverSandboxDisplayApis() { + public boolean neverSandboxDisplayApis(ConstrainDisplayApisConfig constrainDisplayApisConfig) { return isChangeEnabled(NEVER_SANDBOX_DISPLAY_APIS) - || ConstrainDisplayApisConfig.neverConstrainDisplayApis(applicationInfo); + || constrainDisplayApisConfig.getNeverConstrainDisplayApis(applicationInfo); } /** * Returns if the activity should always be sandboxed to the activity window bounds. * @hide */ - public boolean alwaysSandboxDisplayApis() { + public boolean alwaysSandboxDisplayApis(ConstrainDisplayApisConfig constrainDisplayApisConfig) { return isChangeEnabled(ALWAYS_SANDBOX_DISPLAY_APIS) - || ConstrainDisplayApisConfig.alwaysConstrainDisplayApis(applicationInfo); + || constrainDisplayApisConfig.getAlwaysConstrainDisplayApis(applicationInfo); } /** @hide */ diff --git a/core/java/android/content/pm/ConstrainDisplayApisConfig.java b/core/java/android/content/pm/ConstrainDisplayApisConfig.java index 11ba3d4ba9a29..98b73aa8860a3 100644 --- a/core/java/android/content/pm/ConstrainDisplayApisConfig.java +++ b/core/java/android/content/pm/ConstrainDisplayApisConfig.java @@ -19,10 +19,15 @@ package android.content.pm; import static android.provider.DeviceConfig.NAMESPACE_CONSTRAIN_DISPLAY_APIS; import android.provider.DeviceConfig; +import android.util.ArrayMap; +import android.util.Pair; import android.util.Slog; +import com.android.internal.os.BackgroundThread; + import java.util.Arrays; import java.util.List; +import java.util.Map; /** * Class for processing flags in the Device Config namespace 'constrain_display_apis'. @@ -54,6 +59,33 @@ public final class ConstrainDisplayApisConfig { private static final String FLAG_ALWAYS_CONSTRAIN_DISPLAY_APIS = "always_constrain_display_apis"; + /** + * Indicates that display APIs should never be constrained to the activity window bounds for all + * packages. + */ + private boolean mNeverConstrainDisplayApisAllPackages; + + /** + * Indicates that display APIs should never be constrained to the activity window bounds for + * a set of defined packages. Map keys are package names, and entries are a + * 'Pair(, )'. + */ + private ArrayMap> mNeverConstrainConfigMap; + + /** + * Indicates that display APIs should always be constrained to the activity window bounds for + * a set of defined packages. Map keys are package names, and entries are a + * 'Pair(, )'. + */ + private ArrayMap> mAlwaysConstrainConfigMap; + + public ConstrainDisplayApisConfig() { + updateCache(); + + DeviceConfig.addOnPropertiesChangedListener(NAMESPACE_CONSTRAIN_DISPLAY_APIS, + BackgroundThread.getExecutor(), properties -> updateCache()); + } + /** * 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 @@ -61,13 +93,12 @@ public final class ConstrainDisplayApisConfig { * * @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)) { + public boolean getNeverConstrainDisplayApis(ApplicationInfo applicationInfo) { + if (mNeverConstrainDisplayApisAllPackages) { return true; } - return flagHasMatchingPackageEntry(FLAG_NEVER_CONSTRAIN_DISPLAY_APIS, applicationInfo); + return flagHasMatchingPackageEntry(mNeverConstrainConfigMap, applicationInfo); } /** @@ -76,73 +107,106 @@ public final class ConstrainDisplayApisConfig { * * @param applicationInfo Information about the application/package. */ - public static boolean alwaysConstrainDisplayApis(ApplicationInfo applicationInfo) { - return flagHasMatchingPackageEntry(FLAG_ALWAYS_CONSTRAIN_DISPLAY_APIS, applicationInfo); + public boolean getAlwaysConstrainDisplayApis(ApplicationInfo applicationInfo) { + return flagHasMatchingPackageEntry(mAlwaysConstrainConfigMap, applicationInfo); + } + + + /** + * Updates {@link #mNeverConstrainDisplayApisAllPackages}, {@link #mNeverConstrainConfigMap}, + * and {@link #mAlwaysConstrainConfigMap} from the {@link DeviceConfig}. + */ + private void updateCache() { + mNeverConstrainDisplayApisAllPackages = DeviceConfig.getBoolean( + NAMESPACE_CONSTRAIN_DISPLAY_APIS, + FLAG_NEVER_CONSTRAIN_DISPLAY_APIS_ALL_PACKAGES, /* defaultValue= */ false); + + final String neverConstrainConfigStr = DeviceConfig.getString( + NAMESPACE_CONSTRAIN_DISPLAY_APIS, + FLAG_NEVER_CONSTRAIN_DISPLAY_APIS, /* defaultValue= */ ""); + mNeverConstrainConfigMap = buildConfigMap(neverConstrainConfigStr); + + final String alwaysConstrainConfigStr = DeviceConfig.getString( + NAMESPACE_CONSTRAIN_DISPLAY_APIS, + FLAG_ALWAYS_CONSTRAIN_DISPLAY_APIS, /* defaultValue= */ ""); + mAlwaysConstrainConfigMap = buildConfigMap(alwaysConstrainConfigStr); + } + + /** + * Processes the configuration string into a map of version codes, for the given + * configuration to be applied to the specified packages. If the given package + * entry string is invalid, then the map will not contain an entry for the package. + * + * @param configStr A configuration string expected to be in the format of a list of package + * entries separated by ','. A package entry expected to be in the format + * ':?:?'. + * @return a map of configuration entries, where each key is a package name. Each value is + * a pair of version codes, in the format 'Pair(, )'. + */ + private static ArrayMap> buildConfigMap(String configStr) { + ArrayMap> configMap = new ArrayMap<>(); + // String#split returns a non-empty array given an empty string. + if (configStr.isEmpty()) { + return configMap; + } + for (String packageEntryString : configStr.split(",")) { + List packageAndVersions = Arrays.asList(packageEntryString.split(":", 3)); + if (packageAndVersions.size() != 3) { + Slog.w(TAG, "Invalid package entry in flag 'never/always_constrain_display_apis': " + + packageEntryString); + // Skip this entry. + continue; + } + String packageName = packageAndVersions.get(0); + String minVersionCodeStr = packageAndVersions.get(1); + String maxVersionCodeStr = packageAndVersions.get(2); + try { + final long minVersion = + minVersionCodeStr.isEmpty() ? Long.MIN_VALUE : Long.parseLong( + minVersionCodeStr); + final long maxVersion = + maxVersionCodeStr.isEmpty() ? Long.MAX_VALUE : Long.parseLong( + maxVersionCodeStr); + Pair minMaxVersionCodes = new Pair<>(minVersion, maxVersion); + configMap.put(packageName, minMaxVersionCodes); + } catch (NumberFormatException e) { + Slog.w(TAG, "Invalid APK version code in package entry: " + packageEntryString); + // Skip this entry. + } + } + return configMap; } /** * Returns true if the flag with the given {@code flagName} contains a package entry that * matches the given {@code applicationInfo}. * + * @param configMap the map representing the current configuration value to examine * @param applicationInfo Information about the application/package. */ - private static boolean flagHasMatchingPackageEntry(String flagName, + private static boolean flagHasMatchingPackageEntry(Map> configMap, 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()) { + if (configMap.isEmpty()) { return false; } - - for (String packageEntryString : configStr.split(",")) { - if (matchesApplicationInfo(packageEntryString, applicationInfo)) { - return true; - } + if (!configMap.containsKey(applicationInfo.packageName)) { + return false; } - - return false; + return matchesApplicationInfo(configMap.get(applicationInfo.packageName), applicationInfo); } /** - * 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. + * Parses the given {@code minMaxVersionCodes} and returns true if {@code + * applicationInfo.longVersionCode} is within the version range in the pair. + * Returns false otherwise. * - *

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 - * ':?:?'. + * @param minMaxVersionCodes A pair expected to be in the format + * 'Pair(, )'. * @param applicationInfo Information about the application/package. */ - private static boolean matchesApplicationInfo(String packageEntryStr, + private static boolean matchesApplicationInfo(Pair minMaxVersionCodes, ApplicationInfo applicationInfo) { - List 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; + return applicationInfo.longVersionCode >= minMaxVersionCodes.first + && applicationInfo.longVersionCode <= minMaxVersionCodes.second; } } diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto index c750eadf845ac..6faa04690473e 100644 --- a/core/proto/android/server/windowmanagerservice.proto +++ b/core/proto/android/server/windowmanagerservice.proto @@ -366,6 +366,7 @@ message ActivityRecordProto { optional bool pip_auto_enter_enabled = 31; optional bool in_size_compat_mode = 32; optional float min_aspect_ratio = 33; + optional bool provides_max_bounds = 34; } /* represents WindowToken */ diff --git a/core/tests/coretests/src/android/content/pm/ConstrainDisplayApisConfigTest.java b/core/tests/coretests/src/android/content/pm/ConstrainDisplayApisConfigTest.java index 0456029f45a04..98485c024a59d 100644 --- a/core/tests/coretests/src/android/content/pm/ConstrainDisplayApisConfigTest.java +++ b/core/tests/coretests/src/android/content/pm/ConstrainDisplayApisConfigTest.java @@ -18,8 +18,7 @@ package android.content.pm; import static android.provider.DeviceConfig.NAMESPACE_CONSTRAIN_DISPLAY_APIS; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertEquals; import android.annotation.Nullable; import android.platform.test.annotations.Presubmit; @@ -146,24 +145,17 @@ public final class ConstrainDisplayApisConfigTest { private static void testNeverConstrainDisplayApis(String packageName, long version, boolean expected) { - boolean result = ConstrainDisplayApisConfig.neverConstrainDisplayApis( - buildApplicationInfo(packageName, version)); - if (expected) { - assertTrue(result); - } else { - assertFalse(result); - } + ConstrainDisplayApisConfig config = new ConstrainDisplayApisConfig(); + assertEquals(expected, + config.getNeverConstrainDisplayApis(buildApplicationInfo(packageName, version))); } private static void testAlwaysConstrainDisplayApis(String packageName, long version, boolean expected) { - boolean result = ConstrainDisplayApisConfig.alwaysConstrainDisplayApis( - buildApplicationInfo(packageName, version)); - if (expected) { - assertTrue(result); - } else { - assertFalse(result); - } + ConstrainDisplayApisConfig config = new ConstrainDisplayApisConfig(); + + assertEquals(expected, + config.getAlwaysConstrainDisplayApis(buildApplicationInfo(packageName, version))); } private static ApplicationInfo buildApplicationInfo(String packageName, long version) { diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index b469a80a3a2f4..393d101e98303 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -160,6 +160,7 @@ import static com.android.server.wm.ActivityRecordProto.NUM_DRAWN_WINDOWS; import static com.android.server.wm.ActivityRecordProto.NUM_INTERESTING_WINDOWS; import static com.android.server.wm.ActivityRecordProto.PIP_AUTO_ENTER_ENABLED; import static com.android.server.wm.ActivityRecordProto.PROC_ID; +import static com.android.server.wm.ActivityRecordProto.PROVIDES_MAX_BOUNDS; import static com.android.server.wm.ActivityRecordProto.REPORTED_DRAWN; import static com.android.server.wm.ActivityRecordProto.REPORTED_VISIBLE; import static com.android.server.wm.ActivityRecordProto.STARTING_DISPLAYED; @@ -261,6 +262,7 @@ import android.content.Intent; import android.content.LocusId; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; +import android.content.pm.ConstrainDisplayApisConfig; import android.content.pm.PackageManager; import android.content.res.CompatibilityInfo; import android.content.res.Configuration; @@ -596,6 +598,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A */ private CompatDisplayInsets mCompatDisplayInsets; + private static ConstrainDisplayApisConfig sConstrainDisplayApisConfig; + boolean pendingVoiceInteractionStart; // Waiting for activity-invoked voice session IVoiceInteractionSession voiceSession; // Voice interaction session for this activity @@ -1162,8 +1166,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A if (info.configChanges != 0) { pw.println(prefix + "configChanges=0x" + Integer.toHexString(info.configChanges)); } - pw.println(prefix + "neverSandboxDisplayApis=" + info.neverSandboxDisplayApis()); - pw.println(prefix + "alwaysSandboxDisplayApis=" + info.alwaysSandboxDisplayApis()); + pw.println(prefix + "neverSandboxDisplayApis=" + info.neverSandboxDisplayApis( + sConstrainDisplayApisConfig)); + pw.println(prefix + "alwaysSandboxDisplayApis=" + info.alwaysSandboxDisplayApis( + sConstrainDisplayApisConfig)); } if (mLastParentBeforePip != null) { pw.println(prefix + "lastParentTaskIdBeforePip=" + mLastParentBeforePip.mTaskId); @@ -1769,6 +1775,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A info.windowLayout.windowLayoutAffinity = uid + ":" + info.windowLayout.windowLayoutAffinity; } + // Initialize once, when we know all system services are available. + if (sConstrainDisplayApisConfig == null) { + sConstrainDisplayApisConfig = new ConstrainDisplayApisConfig(); + } stateNotNeeded = (aInfo.flags & FLAG_STATE_NOT_NEEDED) != 0; nonLocalizedLabel = aInfo.nonLocalizedLabel; labelRes = aInfo.labelRes; @@ -7465,8 +7475,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A + "should create compatDisplayInsets = %s", getUid(), mTmpBounds, - info.neverSandboxDisplayApis(), - info.alwaysSandboxDisplayApis(), + info.neverSandboxDisplayApis(sConstrainDisplayApisConfig), + info.alwaysSandboxDisplayApis(sConstrainDisplayApisConfig), !matchParentBounds(), mCompatDisplayInsets != null, shouldCreateCompatDisplayInsets()); @@ -8027,11 +8037,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A return false; } // Never apply sandboxing to an app that should be explicitly excluded from the config. - if (info != null && info.neverSandboxDisplayApis()) { + if (info.neverSandboxDisplayApis(sConstrainDisplayApisConfig)) { return false; } // Always apply sandboxing to an app that should be explicitly included from the config. - if (info != null && info.alwaysSandboxDisplayApis()) { + if (info.alwaysSandboxDisplayApis(sConstrainDisplayApisConfig)) { return true; } // Max bounds should be sandboxed when an activity should have compatDisplayInsets, and it @@ -9047,6 +9057,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A proto.write(PIP_AUTO_ENTER_ENABLED, pictureInPictureArgs.isAutoEnterEnabled()); proto.write(IN_SIZE_COMPAT_MODE, inSizeCompatMode()); proto.write(MIN_ASPECT_RATIO, getMinAspectRatio()); + // Only record if max bounds sandboxing is applied, if the caller has the necessary + // permission to access the device configs. + proto.write(PROVIDES_MAX_BOUNDS, providesMaxBounds()); } @Override