Merge "Introduce WindowManagerState field for sandboxing" into sc-v2-dev

This commit is contained in:
Naomi Musgrave
2021-12-08 21:52:51 +00:00
committed by Android (Google) Code Review
5 changed files with 149 additions and 79 deletions

View File

@@ -1367,18 +1367,18 @@ public class ActivityInfo extends ComponentInfo implements Parcelable {
* Returns if the activity should never be sandboxed to the activity window bounds. * Returns if the activity should never be sandboxed to the activity window bounds.
* @hide * @hide
*/ */
public boolean neverSandboxDisplayApis() { public boolean neverSandboxDisplayApis(ConstrainDisplayApisConfig constrainDisplayApisConfig) {
return isChangeEnabled(NEVER_SANDBOX_DISPLAY_APIS) 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. * Returns if the activity should always be sandboxed to the activity window bounds.
* @hide * @hide
*/ */
public boolean alwaysSandboxDisplayApis() { public boolean alwaysSandboxDisplayApis(ConstrainDisplayApisConfig constrainDisplayApisConfig) {
return isChangeEnabled(ALWAYS_SANDBOX_DISPLAY_APIS) return isChangeEnabled(ALWAYS_SANDBOX_DISPLAY_APIS)
|| ConstrainDisplayApisConfig.alwaysConstrainDisplayApis(applicationInfo); || constrainDisplayApisConfig.getAlwaysConstrainDisplayApis(applicationInfo);
} }
/** @hide */ /** @hide */

View File

@@ -19,10 +19,15 @@ package android.content.pm;
import static android.provider.DeviceConfig.NAMESPACE_CONSTRAIN_DISPLAY_APIS; import static android.provider.DeviceConfig.NAMESPACE_CONSTRAIN_DISPLAY_APIS;
import android.provider.DeviceConfig; import android.provider.DeviceConfig;
import android.util.ArrayMap;
import android.util.Pair;
import android.util.Slog; import android.util.Slog;
import com.android.internal.os.BackgroundThread;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Map;
/** /**
* Class for processing flags in the Device Config namespace 'constrain_display_apis'. * 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 = private static final String FLAG_ALWAYS_CONSTRAIN_DISPLAY_APIS =
"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(<min-version-code>, <max-version-code>)'.
*/
private ArrayMap<String, Pair<Long, Long>> 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(<min-version-code>, <max-version-code>)'.
*/
private ArrayMap<String, Pair<Long, Long>> 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 * 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 * 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. * @param applicationInfo Information about the application/package.
*/ */
public static boolean neverConstrainDisplayApis(ApplicationInfo applicationInfo) { public boolean getNeverConstrainDisplayApis(ApplicationInfo applicationInfo) {
if (DeviceConfig.getBoolean(NAMESPACE_CONSTRAIN_DISPLAY_APIS, if (mNeverConstrainDisplayApisAllPackages) {
FLAG_NEVER_CONSTRAIN_DISPLAY_APIS_ALL_PACKAGES, /* defaultValue= */ false)) {
return true; 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. * @param applicationInfo Information about the application/package.
*/ */
public static boolean alwaysConstrainDisplayApis(ApplicationInfo applicationInfo) { public boolean getAlwaysConstrainDisplayApis(ApplicationInfo applicationInfo) {
return flagHasMatchingPackageEntry(FLAG_ALWAYS_CONSTRAIN_DISPLAY_APIS, 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
* '<package-name>:<min-version-code>?:<max-version-code>?'.
* @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(<min-version-code>, <max-version-code>)'.
*/
private static ArrayMap<String, Pair<Long, Long>> buildConfigMap(String configStr) {
ArrayMap<String, Pair<Long, Long>> 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<String> 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<Long, Long> 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 * Returns true if the flag with the given {@code flagName} contains a package entry that
* matches the given {@code applicationInfo}. * matches the given {@code applicationInfo}.
* *
* @param configMap the map representing the current configuration value to examine
* @param applicationInfo Information about the application/package. * @param applicationInfo Information about the application/package.
*/ */
private static boolean flagHasMatchingPackageEntry(String flagName, private static boolean flagHasMatchingPackageEntry(Map<String, Pair<Long, Long>> configMap,
ApplicationInfo applicationInfo) { ApplicationInfo applicationInfo) {
String configStr = DeviceConfig.getString(NAMESPACE_CONSTRAIN_DISPLAY_APIS, if (configMap.isEmpty()) {
flagName, /* defaultValue= */ "");
// String#split returns a non-empty array given an empty string.
if (configStr.isEmpty()) {
return false; return false;
} }
if (!configMap.containsKey(applicationInfo.packageName)) {
for (String packageEntryString : configStr.split(",")) { return false;
if (matchesApplicationInfo(packageEntryString, applicationInfo)) {
return true;
}
} }
return matchesApplicationInfo(configMap.get(applicationInfo.packageName), applicationInfo);
return false;
} }
/** /**
* Parses the given {@code packageEntryString} and returns true if {@code * Parses the given {@code minMaxVersionCodes} 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 pair.
* applicationInfo.longVersionCode} is within the version range in the config. * Returns false otherwise.
* *
* <p>Logs a warning and returns false in case the given {@code packageEntryString} is invalid. * @param minMaxVersionCodes A pair expected to be in the format
* * 'Pair(<min-version-code>, <max-version-code>)'.
* @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. * @param applicationInfo Information about the application/package.
*/ */
private static boolean matchesApplicationInfo(String packageEntryStr, private static boolean matchesApplicationInfo(Pair<Long, Long> minMaxVersionCodes,
ApplicationInfo applicationInfo) { ApplicationInfo applicationInfo) {
List<String> packageAndVersions = Arrays.asList(packageEntryStr.split(":", 3)); return applicationInfo.longVersionCode >= minMaxVersionCodes.first
if (packageAndVersions.size() != 3) { && applicationInfo.longVersionCode <= minMaxVersionCodes.second;
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;
} }
} }

View File

@@ -366,6 +366,7 @@ message ActivityRecordProto {
optional bool pip_auto_enter_enabled = 31; optional bool pip_auto_enter_enabled = 31;
optional bool in_size_compat_mode = 32; optional bool in_size_compat_mode = 32;
optional float min_aspect_ratio = 33; optional float min_aspect_ratio = 33;
optional bool provides_max_bounds = 34;
} }
/* represents WindowToken */ /* represents WindowToken */

View File

@@ -18,8 +18,7 @@ package android.content.pm;
import static android.provider.DeviceConfig.NAMESPACE_CONSTRAIN_DISPLAY_APIS; import static android.provider.DeviceConfig.NAMESPACE_CONSTRAIN_DISPLAY_APIS;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import android.annotation.Nullable; import android.annotation.Nullable;
import android.platform.test.annotations.Presubmit; import android.platform.test.annotations.Presubmit;
@@ -146,24 +145,17 @@ public final class ConstrainDisplayApisConfigTest {
private static void testNeverConstrainDisplayApis(String packageName, long version, private static void testNeverConstrainDisplayApis(String packageName, long version,
boolean expected) { boolean expected) {
boolean result = ConstrainDisplayApisConfig.neverConstrainDisplayApis( ConstrainDisplayApisConfig config = new ConstrainDisplayApisConfig();
buildApplicationInfo(packageName, version)); assertEquals(expected,
if (expected) { config.getNeverConstrainDisplayApis(buildApplicationInfo(packageName, version)));
assertTrue(result);
} else {
assertFalse(result);
}
} }
private static void testAlwaysConstrainDisplayApis(String packageName, long version, private static void testAlwaysConstrainDisplayApis(String packageName, long version,
boolean expected) { boolean expected) {
boolean result = ConstrainDisplayApisConfig.alwaysConstrainDisplayApis( ConstrainDisplayApisConfig config = new ConstrainDisplayApisConfig();
buildApplicationInfo(packageName, version));
if (expected) { assertEquals(expected,
assertTrue(result); config.getAlwaysConstrainDisplayApis(buildApplicationInfo(packageName, version)));
} else {
assertFalse(result);
}
} }
private static ApplicationInfo buildApplicationInfo(String packageName, long version) { private static ApplicationInfo buildApplicationInfo(String packageName, long version) {

View File

@@ -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.NUM_INTERESTING_WINDOWS;
import static com.android.server.wm.ActivityRecordProto.PIP_AUTO_ENTER_ENABLED; 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.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_DRAWN;
import static com.android.server.wm.ActivityRecordProto.REPORTED_VISIBLE; import static com.android.server.wm.ActivityRecordProto.REPORTED_VISIBLE;
import static com.android.server.wm.ActivityRecordProto.STARTING_DISPLAYED; import static com.android.server.wm.ActivityRecordProto.STARTING_DISPLAYED;
@@ -261,6 +262,7 @@ import android.content.Intent;
import android.content.LocusId; import android.content.LocusId;
import android.content.pm.ActivityInfo; import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo; import android.content.pm.ApplicationInfo;
import android.content.pm.ConstrainDisplayApisConfig;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.content.res.CompatibilityInfo; import android.content.res.CompatibilityInfo;
import android.content.res.Configuration; import android.content.res.Configuration;
@@ -596,6 +598,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
*/ */
private CompatDisplayInsets mCompatDisplayInsets; private CompatDisplayInsets mCompatDisplayInsets;
private static ConstrainDisplayApisConfig sConstrainDisplayApisConfig;
boolean pendingVoiceInteractionStart; // Waiting for activity-invoked voice session boolean pendingVoiceInteractionStart; // Waiting for activity-invoked voice session
IVoiceInteractionSession voiceSession; // Voice interaction session for this activity IVoiceInteractionSession voiceSession; // Voice interaction session for this activity
@@ -1162,8 +1166,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
if (info.configChanges != 0) { if (info.configChanges != 0) {
pw.println(prefix + "configChanges=0x" + Integer.toHexString(info.configChanges)); pw.println(prefix + "configChanges=0x" + Integer.toHexString(info.configChanges));
} }
pw.println(prefix + "neverSandboxDisplayApis=" + info.neverSandboxDisplayApis()); pw.println(prefix + "neverSandboxDisplayApis=" + info.neverSandboxDisplayApis(
pw.println(prefix + "alwaysSandboxDisplayApis=" + info.alwaysSandboxDisplayApis()); sConstrainDisplayApisConfig));
pw.println(prefix + "alwaysSandboxDisplayApis=" + info.alwaysSandboxDisplayApis(
sConstrainDisplayApisConfig));
} }
if (mLastParentBeforePip != null) { if (mLastParentBeforePip != null) {
pw.println(prefix + "lastParentTaskIdBeforePip=" + mLastParentBeforePip.mTaskId); pw.println(prefix + "lastParentTaskIdBeforePip=" + mLastParentBeforePip.mTaskId);
@@ -1769,6 +1775,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
info.windowLayout.windowLayoutAffinity = info.windowLayout.windowLayoutAffinity =
uid + ":" + 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; stateNotNeeded = (aInfo.flags & FLAG_STATE_NOT_NEEDED) != 0;
nonLocalizedLabel = aInfo.nonLocalizedLabel; nonLocalizedLabel = aInfo.nonLocalizedLabel;
labelRes = aInfo.labelRes; labelRes = aInfo.labelRes;
@@ -7465,8 +7475,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
+ "should create compatDisplayInsets = %s", + "should create compatDisplayInsets = %s",
getUid(), getUid(),
mTmpBounds, mTmpBounds,
info.neverSandboxDisplayApis(), info.neverSandboxDisplayApis(sConstrainDisplayApisConfig),
info.alwaysSandboxDisplayApis(), info.alwaysSandboxDisplayApis(sConstrainDisplayApisConfig),
!matchParentBounds(), !matchParentBounds(),
mCompatDisplayInsets != null, mCompatDisplayInsets != null,
shouldCreateCompatDisplayInsets()); shouldCreateCompatDisplayInsets());
@@ -8027,11 +8037,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
return false; return false;
} }
// Never apply sandboxing to an app that should be explicitly excluded from the config. // 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; return false;
} }
// Always apply sandboxing to an app that should be explicitly included from the config. // 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; return true;
} }
// Max bounds should be sandboxed when an activity should have compatDisplayInsets, and it // 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(PIP_AUTO_ENTER_ENABLED, pictureInPictureArgs.isAutoEnterEnabled());
proto.write(IN_SIZE_COMPAT_MODE, inSizeCompatMode()); proto.write(IN_SIZE_COMPAT_MODE, inSizeCompatMode());
proto.write(MIN_ASPECT_RATIO, getMinAspectRatio()); 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 @Override