From 021a43c4be14392b69b4568723b404cdfb163851 Mon Sep 17 00:00:00 2001 From: tomnatan Date: Thu, 13 May 2021 15:09:38 +0000 Subject: [PATCH] Add support for switching off sandboxing via device config flags Bug: 187408243 Test: atest WmTests:SizeCompatTests Test: atest FrameworksCoreTests:ConstrainDisplayApisConfigTest Change-Id: I3ec02f3b2ba5c1825cdc530fd0f6f71abbe5bd10 --- .../java/android/content/pm/ActivityInfo.java | 3 +- .../pm/ConstrainDisplayApisConfig.java | 118 +++++++++++++ .../pm/ConstrainDisplayApisConfigTest.java | 156 ++++++++++++++++++ services/tests/wmtests/AndroidManifest.xml | 1 + .../android/server/wm/SizeCompatTests.java | 119 +++++++++++++ 5 files changed, 396 insertions(+), 1 deletion(-) create mode 100644 core/java/android/content/pm/ConstrainDisplayApisConfig.java create mode 100644 core/tests/coretests/src/android/content/pm/ConstrainDisplayApisConfigTest.java diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java index 03f6380e279f2..af5f9ceb14d8f 100644 --- a/core/java/android/content/pm/ActivityInfo.java +++ b/core/java/android/content/pm/ActivityInfo.java @@ -1348,7 +1348,8 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { public boolean neverSandboxDisplayApis() { return CompatChanges.isChangeEnabled(NEVER_SANDBOX_DISPLAY_APIS, applicationInfo.packageName, - UserHandle.getUserHandleForUid(applicationInfo.uid)); + UserHandle.getUserHandleForUid(applicationInfo.uid)) + || ConstrainDisplayApisConfig.neverConstrainDisplayApis(applicationInfo); } /** diff --git a/core/java/android/content/pm/ConstrainDisplayApisConfig.java b/core/java/android/content/pm/ConstrainDisplayApisConfig.java new file mode 100644 index 0000000000000..1337347cdaf02 --- /dev/null +++ b/core/java/android/content/pm/ConstrainDisplayApisConfig.java @@ -0,0 +1,118 @@ +/* + * 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 + * ':?:?' 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"; + + /** + * 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; + } + String configStr = DeviceConfig.getString(NAMESPACE_CONSTRAIN_DISPLAY_APIS, + FLAG_NEVER_CONSTRAIN_DISPLAY_APIS, /* 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. + * + *

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 applicationInfo Information about the application/package. + */ + private static boolean matchesApplicationInfo(String packageEntryStr, + 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; + } +} diff --git a/core/tests/coretests/src/android/content/pm/ConstrainDisplayApisConfigTest.java b/core/tests/coretests/src/android/content/pm/ConstrainDisplayApisConfigTest.java new file mode 100644 index 0000000000000..1dfbfcd85a736 --- /dev/null +++ b/core/tests/coretests/src/android/content/pm/ConstrainDisplayApisConfigTest.java @@ -0,0 +1,156 @@ +/* + * 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 static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import android.annotation.Nullable; +import android.platform.test.annotations.Presubmit; +import android.provider.DeviceConfig; +import android.provider.DeviceConfig.Properties; + +import androidx.test.filters.SmallTest; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +/** + * Test class for {@link ConstrainDisplayApisConfig}. + * + * Build/Install/Run: + * atest FrameworksCoreTests:ConstrainDisplayApisConfigTest + */ +@SmallTest +@Presubmit +public final class ConstrainDisplayApisConfigTest { + + private Properties mInitialConstrainDisplayApisFlags; + + @Before + public void setUp() throws Exception { + mInitialConstrainDisplayApisFlags = DeviceConfig.getProperties( + NAMESPACE_CONSTRAIN_DISPLAY_APIS); + clearConstrainDisplayApisFlags(); + } + + @After + public void tearDown() throws Exception { + DeviceConfig.setProperties(mInitialConstrainDisplayApisFlags); + } + + @Test + public void neverConstrainDisplayApis_allPackagesFlagTrue_returnsTrue() { + setNeverConstrainDisplayApisAllPackagesFlag("true"); + // Setting 'never_constrain_display_apis' as well to make sure it is ignored. + setNeverConstrainDisplayApisFlag("com.android.other:1:2,com.android.other2::"); + + testNeverConstrainDisplayApis("com.android.test", /* version= */ 5, /* expected= */ true); + testNeverConstrainDisplayApis("com.android.other", /* version= */ 0, /* expected= */ true); + testNeverConstrainDisplayApis("com.android.other", /* version= */ 3, /* expected= */ true); + } + + @Test + public void neverConstrainDisplayApis_flagsNoSet_returnsFalse() { + testNeverConstrainDisplayApis("com.android.test", /* version= */ 1, /* expected= */ false); + } + + @Test + public void neverConstrainDisplayApis_flagsHasSingleEntry_returnsTrueForPackageWithinRange() { + setNeverConstrainDisplayApisFlag("com.android.test:1:1"); + + testNeverConstrainDisplayApis("com.android.other", /* version= */ 5, /* expected= */ false); + testNeverConstrainDisplayApis("com.android.test", /* version= */ 0, /* expected= */ false); + testNeverConstrainDisplayApis("com.android.test", /* version= */ 1, /* expected= */ true); + testNeverConstrainDisplayApis("com.android.test", /* version= */ 2, /* expected= */ false); + } + + @Test + public void neverConstrainDisplayApis_flagHasEntries_returnsTrueForPackagesWithinRange() { + setNeverConstrainDisplayApisFlag("com.android.test1::,com.android.test2:1:3," + + "com.android.test3:5:,com.android.test4::8"); + + // Package 'com.android.other' + testNeverConstrainDisplayApis("com.android.other", /* version= */ 5, /* expected= */ false); + // Package 'com.android.test1' + testNeverConstrainDisplayApis("com.android.test1", /* version= */ 5, /* expected= */ true); + // Package 'com.android.test2' + testNeverConstrainDisplayApis("com.android.test2", /* version= */ 0, /* expected= */ false); + testNeverConstrainDisplayApis("com.android.test2", /* version= */ 1, /* expected= */ true); + testNeverConstrainDisplayApis("com.android.test2", /* version= */ 2, /* expected= */ true); + testNeverConstrainDisplayApis("com.android.test2", /* version= */ 3, /* expected= */ true); + testNeverConstrainDisplayApis("com.android.test2", /* version= */ 4, /* expected= */ false); + // Package 'com.android.test3' + testNeverConstrainDisplayApis("com.android.test3", /* version= */ 4, /* expected= */ false); + testNeverConstrainDisplayApis("com.android.test3", /* version= */ 5, /* expected= */ true); + testNeverConstrainDisplayApis("com.android.test3", /* version= */ 6, /* expected= */ true); + // Package 'com.android.test4' + testNeverConstrainDisplayApis("com.android.test4", /* version= */ 7, /* expected= */ true); + testNeverConstrainDisplayApis("com.android.test4", /* version= */ 8, /* expected= */ true); + testNeverConstrainDisplayApis("com.android.test4", /* version= */ 9, /* expected= */ false); + } + + @Test + public void neverConstrainDisplayApis_flagHasInvalidEntries_ignoresInvalidEntries() { + // We add a valid entry before and after the invalid ones to make sure they are applied. + setNeverConstrainDisplayApisFlag("com.android.test1::,com.android.test2:1," + + "com.android.test3:5:ten,com.android.test4:5::,com.android.test5::"); + + testNeverConstrainDisplayApis("com.android.test1", /* version= */ 5, /* expected= */ true); + testNeverConstrainDisplayApis("com.android.test2", /* version= */ 2, /* expected= */ false); + testNeverConstrainDisplayApis("com.android.test3", /* version= */ 7, /* expected= */ false); + testNeverConstrainDisplayApis("com.android.test4", /* version= */ 7, /* expected= */ false); + testNeverConstrainDisplayApis("com.android.test5", /* version= */ 5, /* expected= */ true); + } + + private static void testNeverConstrainDisplayApis(String packageName, long version, + boolean expected) { + boolean result = ConstrainDisplayApisConfig.neverConstrainDisplayApis( + buildApplicationInfo(packageName, version)); + if (expected) { + assertTrue(result); + } else { + assertFalse(result); + } + } + + private static ApplicationInfo buildApplicationInfo(String packageName, long version) { + ApplicationInfo applicationInfo = new ApplicationInfo(); + applicationInfo.packageName = packageName; + applicationInfo.longVersionCode = version; + return applicationInfo; + } + + private static void setNeverConstrainDisplayApisFlag(@Nullable String value) { + DeviceConfig.setProperty(NAMESPACE_CONSTRAIN_DISPLAY_APIS, "never_constrain_display_apis", + value, /* makeDefault= */ false); + } + + private static void setNeverConstrainDisplayApisAllPackagesFlag(@Nullable String value) { + DeviceConfig.setProperty(NAMESPACE_CONSTRAIN_DISPLAY_APIS, + "never_constrain_display_apis_all_packages", + value, /* makeDefault= */ false); + } + + private static void clearConstrainDisplayApisFlags() { + setNeverConstrainDisplayApisFlag(null); + setNeverConstrainDisplayApisAllPackagesFlag(null); + } +} diff --git a/services/tests/wmtests/AndroidManifest.xml b/services/tests/wmtests/AndroidManifest.xml index 985b2d5f24baa..c2298d0f3d206 100644 --- a/services/tests/wmtests/AndroidManifest.xml +++ b/services/tests/wmtests/AndroidManifest.xml @@ -42,6 +42,7 @@ +