From f993b6b18aaba077d666c940c35b1d5f55c36b76 Mon Sep 17 00:00:00 2001 From: Christopher Dombroski Date: Thu, 21 Oct 2021 15:38:13 -0700 Subject: Add sts-common-util to platform_testing Bug: 190537825 Test: sts-tradefed run sts-dynamic-full Change-Id: I41b0b4893fb41a06a7dea502c6c3a4a4a11eda8c --- libraries/compatibility-common-util/Android.bp | 1 + libraries/sts-common-util/OWNERS | 3 + libraries/sts-common-util/device-side/Android.bp | 32 ++++ .../common/util/StsExtraBusinessLogicTestCase.java | 107 +++++++++++ libraries/sts-common-util/host-side/Android.bp | 33 ++++ .../StsExtraBusinessLogicHostTestBase.java | 110 +++++++++++ libraries/sts-common-util/util/Android.bp | 48 +++++ .../sts/common/util/DescriptionProvider.java | 34 ++++ .../src/com/android/sts/common/util/SplUtils.java | 51 +++++ .../src/com/android/sts/common/util/StsLogic.java | 209 +++++++++++++++++++++ 10 files changed, 628 insertions(+) create mode 100644 libraries/sts-common-util/OWNERS create mode 100644 libraries/sts-common-util/device-side/Android.bp create mode 100644 libraries/sts-common-util/device-side/src/com/android/sts/common/util/StsExtraBusinessLogicTestCase.java create mode 100644 libraries/sts-common-util/host-side/Android.bp create mode 100644 libraries/sts-common-util/host-side/src/com/android/sts/common/tradefed/testtype/StsExtraBusinessLogicHostTestBase.java create mode 100644 libraries/sts-common-util/util/Android.bp create mode 100644 libraries/sts-common-util/util/src/com/android/sts/common/util/DescriptionProvider.java create mode 100644 libraries/sts-common-util/util/src/com/android/sts/common/util/SplUtils.java create mode 100644 libraries/sts-common-util/util/src/com/android/sts/common/util/StsLogic.java diff --git a/libraries/compatibility-common-util/Android.bp b/libraries/compatibility-common-util/Android.bp index 850a8bba1..ca39c0c44 100644 --- a/libraries/compatibility-common-util/Android.bp +++ b/libraries/compatibility-common-util/Android.bp @@ -35,6 +35,7 @@ java_library { visibility: [ "//test/suite_harness/common/util", "//platform_testing/libraries/compatibility-common-util/tests", + "//platform_testing/libraries/sts-common-util/util", ], srcs: ["src/**/*.java"], host_supported: true, diff --git a/libraries/sts-common-util/OWNERS b/libraries/sts-common-util/OWNERS new file mode 100644 index 000000000..69d208103 --- /dev/null +++ b/libraries/sts-common-util/OWNERS @@ -0,0 +1,3 @@ +# STS Owners +cdombroski@google.com +musashi@google.com diff --git a/libraries/sts-common-util/device-side/Android.bp b/libraries/sts-common-util/device-side/Android.bp new file mode 100644 index 000000000..1692a7c2e --- /dev/null +++ b/libraries/sts-common-util/device-side/Android.bp @@ -0,0 +1,32 @@ +// 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 { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +java_library_static { + name: "sts-device-util", + sdk_version: "test_current", + + srcs: ["src/**/*.java"], + + static_libs: [ + "sts-common-util-devicesidelib", + ], + + libs: [ + "compatibility-device-util-axt", + ], +} diff --git a/libraries/sts-common-util/device-side/src/com/android/sts/common/util/StsExtraBusinessLogicTestCase.java b/libraries/sts-common-util/device-side/src/com/android/sts/common/util/StsExtraBusinessLogicTestCase.java new file mode 100644 index 000000000..0eede1609 --- /dev/null +++ b/libraries/sts-common-util/device-side/src/com/android/sts/common/util/StsExtraBusinessLogicTestCase.java @@ -0,0 +1,107 @@ +/* + * 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 com.android.sts.common.util; + +import com.android.compatibility.common.util.ExtraBusinessLogicTestCase; + +import android.os.Build; +import android.util.Log; + +import androidx.test.InstrumentationRegistry; + +import org.junit.Rule; +import org.junit.runner.Description; + +import java.time.LocalDate; +import java.util.List; + +/** The device-side implementation of StsLogic. */ +public class StsExtraBusinessLogicTestCase extends ExtraBusinessLogicTestCase implements StsLogic { + + private LocalDate deviceSpl = null; + @Rule public DescriptionProvider descriptionProvider = new DescriptionProvider(); + + protected StsExtraBusinessLogicTestCase() { + mDependentOnBusinessLogic = false; + } + + @Override + public List getExtraBusinessLogics() { + String stsDynamicPlan = + InstrumentationRegistry.getArguments().getString("sts-dynamic-plan"); + switch (stsDynamicPlan) { + case "incremental": + return StsLogic.STS_EXTRA_BUSINESS_LOGIC_INCREMENTAL; + case "full": + return StsLogic.STS_EXTRA_BUSINESS_LOGIC_FULL; + default: + throw new RuntimeException( + "Could not find Dynamic STS plan in InstrumentationRegistry arguments"); + } + } + + @Override + public Description getTestDescription() { + return descriptionProvider.getDescription(); + } + + @Override + public LocalDate getDeviceSpl() { + if (deviceSpl == null) { + deviceSpl = SplUtils.localDateFromSplString(Build.VERSION.SECURITY_PATCH); + } + return deviceSpl; + } + + /** + * Specify the latest release bulletin. Control this from the command-line with the following: + * --test-arg + * com.android.tradefed.testtype.AndroidJUnitTest:instrumentation-arg:release-bulletin-spl:=2020-06 + */ + @Override + public LocalDate getReleaseBulletinSpl() { + String releaseBulletinSpl = + InstrumentationRegistry.getArguments().getString("release-bulletin-spl"); + if (releaseBulletinSpl == null) { + return null; + } + // bulletin is released by month; add any day - only the year and month are compared. + releaseBulletinSpl = + String.format("%s-%02d", releaseBulletinSpl, SplUtils.Type.PARTIAL.day); + return SplUtils.localDateFromSplString(releaseBulletinSpl); + } + + @Override + public void logInfo(String logTag, String format, Object... args) { + Log.i(logTag, String.format(format, args)); + } + + @Override + public void logDebug(String logTag, String format, Object... args) { + Log.d(logTag, String.format(format, args)); + } + + @Override + public void logWarn(String logTag, String format, Object... args) { + Log.w(logTag, String.format(format, args)); + } + + @Override + public void logError(String logTag, String format, Object... args) { + Log.e(logTag, String.format(format, args)); + } +} diff --git a/libraries/sts-common-util/host-side/Android.bp b/libraries/sts-common-util/host-side/Android.bp new file mode 100644 index 000000000..49f51f4d7 --- /dev/null +++ b/libraries/sts-common-util/host-side/Android.bp @@ -0,0 +1,33 @@ +// 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 { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +java_library_host { + name: "sts-host-util", + defaults: ["cts_error_prone_rules"], + + srcs: ["src/**/*.java"], + + static_libs: [ + "sts-common-util-lib", + ], + + libs: [ + "compatibility-tradefed", + "tradefed", + ], +} diff --git a/libraries/sts-common-util/host-side/src/com/android/sts/common/tradefed/testtype/StsExtraBusinessLogicHostTestBase.java b/libraries/sts-common-util/host-side/src/com/android/sts/common/tradefed/testtype/StsExtraBusinessLogicHostTestBase.java new file mode 100644 index 000000000..c7fad5b9e --- /dev/null +++ b/libraries/sts-common-util/host-side/src/com/android/sts/common/tradefed/testtype/StsExtraBusinessLogicHostTestBase.java @@ -0,0 +1,110 @@ +/* + * 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 com.android.sts.common.tradefed.testtype; + +import com.android.compatibility.common.tradefed.testtype.ExtraBusinessLogicHostTestBase; +import com.android.ddmlib.Log; +import com.android.sts.common.util.DescriptionProvider; +import com.android.sts.common.util.SplUtils; +import com.android.sts.common.util.StsLogic; +import com.android.tradefed.device.DeviceNotAvailableException; + +import org.junit.Rule; +import org.junit.runner.Description; + +import java.time.LocalDate; +import java.util.List; + +/** The host-side implementation of StsLogic. */ +public class StsExtraBusinessLogicHostTestBase extends ExtraBusinessLogicHostTestBase + implements StsLogic { + + private LocalDate deviceSpl = null; + @Rule public DescriptionProvider descriptionProvider = new DescriptionProvider(); + + public StsExtraBusinessLogicHostTestBase() { + super(); + mDependentOnBusinessLogic = false; + } + + @Override + public List getExtraBusinessLogics() { + String stsDynamicPlan = getBuild().getBuildAttributes().get("sts-dynamic-plan"); + switch (stsDynamicPlan) { + case "incremental": + return StsLogic.STS_EXTRA_BUSINESS_LOGIC_INCREMENTAL; + case "full": + return StsLogic.STS_EXTRA_BUSINESS_LOGIC_FULL; + default: + throw new RuntimeException("Could not find Dynamic STS plan in build attributes"); + } + } + + @Override + public Description getTestDescription() { + return descriptionProvider.getDescription(); + } + + @Override + public LocalDate getDeviceSpl() { + if (deviceSpl == null) { + try { + String splString = getDevice().getProperty("ro.build.version.security_patch"); + deviceSpl = SplUtils.localDateFromSplString(splString); + } catch (DeviceNotAvailableException e) { + throw new RuntimeException("couldn't get the security patch level", e); + } + } + return deviceSpl; + } + + /** + * Specify the latest release bulletin. Control this from the command-line with the following + * command line argument: --build-attribute "release-bulletin-spl=2021-06" + */ + @Override + public LocalDate getReleaseBulletinSpl() { + String releaseBulletinSpl = getBuild().getBuildAttributes().get("release-bulletin-spl"); + if (releaseBulletinSpl == null) { + return null; + } + // bulletin is released by month; add any day - only the year and month are compared. + releaseBulletinSpl = + String.format("%s-%02d", releaseBulletinSpl, SplUtils.Type.PARTIAL.day); + return SplUtils.localDateFromSplString(releaseBulletinSpl); + } + + @Override + public void logInfo(String logTag, String format, Object... args) { + Log.i(logTag, String.format(format, args)); + } + + @Override + public void logDebug(String logTag, String format, Object... args) { + Log.d(logTag, String.format(format, args)); + } + + @Override + public void logWarn(String logTag, String format, Object... args) { + Log.w(logTag, String.format(format, args)); + } + + @Override + public void logError(String logTag, String format, Object... args) { + Log.e(logTag, String.format(format, args)); + } +} diff --git a/libraries/sts-common-util/util/Android.bp b/libraries/sts-common-util/util/Android.bp new file mode 100644 index 000000000..403dcc245 --- /dev/null +++ b/libraries/sts-common-util/util/Android.bp @@ -0,0 +1,48 @@ +// Copyright (C) 2020 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 { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +// Build the common utility library for use device-side +java_library_static { + name: "sts-common-util-devicesidelib", + visibility: [ + "//platform_testing/libraries/sts-common-util/device-side", + ], + sdk_version: "current", + srcs: ["src/**/*.java"], + static_libs: [ + "junit", + "platform-test-annotations", + "compatibility-common-util-devicesidelib", + ], +} + +java_library { + name: "sts-common-util-lib", + visibility: [ + "//platform_testing/libraries/sts-common-util/host-side", + ], + host_supported: true, + srcs: ["src/**/*.java"], + static_libs: [ + "compatibility-common-util-lib", + ], + libs: [ + "junit", + "platform-test-annotations", + ], +} diff --git a/libraries/sts-common-util/util/src/com/android/sts/common/util/DescriptionProvider.java b/libraries/sts-common-util/util/src/com/android/sts/common/util/DescriptionProvider.java new file mode 100644 index 000000000..60d46ea97 --- /dev/null +++ b/libraries/sts-common-util/util/src/com/android/sts/common/util/DescriptionProvider.java @@ -0,0 +1,34 @@ +/* + * 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 com.android.sts.common.util; + +import org.junit.rules.TestWatcher; +import org.junit.runner.Description; + +/** Provide a way for tests to get their description while running. */ +public class DescriptionProvider extends TestWatcher { + private volatile Description description; + + @Override + protected void starting(Description description) { + this.description = description; + } + + public Description getDescription() { + return description; + } +} diff --git a/libraries/sts-common-util/util/src/com/android/sts/common/util/SplUtils.java b/libraries/sts-common-util/util/src/com/android/sts/common/util/SplUtils.java new file mode 100644 index 000000000..afb7b12af --- /dev/null +++ b/libraries/sts-common-util/util/src/com/android/sts/common/util/SplUtils.java @@ -0,0 +1,51 @@ +/* + * 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 com.android.sts.common.util; + +import java.time.Instant; +import java.time.LocalDate; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; + +/** Tools for Security Patch Levels and LocalDates representing them. */ +public final class SplUtils { + private static final ZoneId UTC_ZONE_ID = ZoneId.of("UTC"); + private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); + + public enum Type { + PARTIAL(1), // platform + COMPLETE(5); // device-specific (kernel, soc, etc) + + public final int day; + + Type(int day) { + this.day = day; + } + } + + public static LocalDate localDateFromMillis(long millis) { + return Instant.ofEpochMilli(millis).atZone(UTC_ZONE_ID).toLocalDate(); + } + + public static LocalDate localDateFromSplString(String spl) { + return LocalDate.parse(spl, formatter); + } + + public static String format(LocalDate date) { + return date.format(formatter); + } +} diff --git a/libraries/sts-common-util/util/src/com/android/sts/common/util/StsLogic.java b/libraries/sts-common-util/util/src/com/android/sts/common/util/StsLogic.java new file mode 100644 index 000000000..648199c20 --- /dev/null +++ b/libraries/sts-common-util/util/src/com/android/sts/common/util/StsLogic.java @@ -0,0 +1,209 @@ +/* + * 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 com.android.sts.common.util; + +import static org.junit.Assume.*; +import static org.junit.Assert.*; + +import android.platform.test.annotations.AsbSecurityTest; + +import com.android.compatibility.common.util.BusinessLogicMapStore; + +import org.junit.runner.Description; + +import java.time.LocalDate; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +/** Common STS extra business logic for host-side and device-side to implement. */ +public interface StsLogic { + + static final String LOG_TAG = StsLogic.class.getSimpleName(); + + // keep in sync with google3: + // //wireless/android/partner/apbs/*/config/xtsbgusinesslogic/sts_business_logic.gcl + List STS_EXTRA_BUSINESS_LOGIC_FULL = Arrays.asList(new String[]{ + "uploadSpl", + "uploadModificationTime", + "declaredSpl", + }); + List STS_EXTRA_BUSINESS_LOGIC_INCREMENTAL = Arrays.asList(new String[]{ + "uploadSpl", + "uploadModificationTime", + "declaredSpl", + "incremental", + }); + + Description getTestDescription(); + + LocalDate getDeviceSpl(); + + LocalDate getReleaseBulletinSpl(); + + default long[] getCveBugIds() { + AsbSecurityTest annotation = getTestDescription().getAnnotation(AsbSecurityTest.class); + if (annotation == null) { + return null; + } + return annotation.cveBugId(); + } + + default boolean isBugSplDataKnownMissing() { + long[] bugIds = getCveBugIds(); + if (bugIds == null) { + // no spl data, don't complain + return true; + } + // true if the bug id is older than ~ June 2020 + return Arrays.stream(bugIds).min().getAsLong() < 157905780; + } + + default LocalDate getMinTestSpl() { + Map map = BusinessLogicMapStore.getMap("security_bulletins"); + if (map == null) { + throw new IllegalArgumentException("Could not find the security bulletin map"); + } + LocalDate minSpl = null; + for (long cveBugId : getCveBugIds()) { + String splString = map.get(Long.toString(cveBugId)); + if (splString == null) { + // This bug id wasn't found in the map. + // This is a new test or the bug was removed from the bulletin and this is an old + // binary. Neither is a critical issue and the test will run in these cases. + // New test: developer should be able to write the test without getting blocked. + // Removed bug + old binary: test will run. + logWarn(LOG_TAG, "could not find the CVE bug %d in the spl map", cveBugId); + continue; + } + LocalDate spl = SplUtils.localDateFromSplString(splString); + if (minSpl == null) { + minSpl = spl; + } else if (spl.isBefore(minSpl)) { + minSpl = spl; + } + } + return minSpl; + } + + default LocalDate getMinModificationDate() { + Map map = BusinessLogicMapStore.getMap("sts_modification_times"); + if (map == null) { + throw new IllegalArgumentException("Could not find the modification date map"); + } + LocalDate minModificationDate = null; + for (long cveBugId : getCveBugIds()) { + String modificationMillisString = map.get(Long.toString(cveBugId)); + if (modificationMillisString == null) { + logInfo(LOG_TAG, + "Could not find the CVE bug %d in the modification date map", cveBugId); + continue; + } + LocalDate modificationDate = + SplUtils.localDateFromMillis(Long.parseLong(modificationMillisString)); + if (minModificationDate == null) { + minModificationDate = modificationDate; + } else if (modificationDate.isBefore(minModificationDate)) { + minModificationDate = modificationDate; + } + } + return minModificationDate; + } + + default boolean shouldSkipIncremental() { + logDebug(LOG_TAG, "filtering by incremental"); + + long[] bugIds = getCveBugIds(); + if (bugIds == null) { + // There were no @AsbSecurityTest annotations + logInfo(LOG_TAG, "not an ASB test"); + return false; + } + + // check if test spl is older than the past 6 months from the device spl + LocalDate deviceSpl = getDeviceSpl(); + LocalDate incrementalCutoffSpl = deviceSpl.plusMonths(-6); + + LocalDate minTestModifiedDate = getMinModificationDate(); + if (minTestModifiedDate == null) { + // could not get the modification date - run the test + if (isBugSplDataKnownMissing()) { + logDebug(LOG_TAG, "no data for this old test"); + return true; + } + return false; + } + if (minTestModifiedDate.isAfter(incrementalCutoffSpl)) { + logDebug(LOG_TAG, "the test was recently modified"); + return false; + } + + LocalDate minTestSpl = getMinTestSpl(); + if (minTestSpl == null) { + // could not get the test spl - run the test + logWarn(LOG_TAG, "could not get the test SPL"); + return false; + } + if (minTestSpl.isAfter(incrementalCutoffSpl)) { + logDebug(LOG_TAG, "the test has a recent SPL"); + return false; + } + + logDebug(LOG_TAG, "test should skip"); + return true; + } + + default boolean shouldSkipDeclaredSpl() { + if (getCveBugIds() == null) { + // There were no @AsbSecurityTest annotations + logInfo(LOG_TAG, "not an ASB test"); + return false; + } + + LocalDate releaseBulletinSpl = getReleaseBulletinSpl(); + LocalDate minTestSpl = getMinTestSpl(); + if (releaseBulletinSpl != null && !isBugSplDataKnownMissing()) { + // assert that the test has a known SPL when we expect the data to be fresh + assertNotNull("Unknown SPL for new CVE", minTestSpl); + + // set the days to be the same so we only compare year-month + releaseBulletinSpl = releaseBulletinSpl.withDayOfMonth(minTestSpl.getDayOfMonth()); + // the test SPL can't be equal to or after the release bulletin SPL + assertFalse("Newer SPL than release bulletin", releaseBulletinSpl.isBefore(minTestSpl)); + } + if (minTestSpl == null) { + // no SPL for this test; run normally + return false; + } + + // skip if the test is newer than the device SPL + LocalDate deviceSpl = getDeviceSpl(); + return minTestSpl.isAfter(deviceSpl); + } + + default void skip(String message) { + assumeTrue(message, false); + } + + public void logInfo(String logTag, String format, Object... args); + + public void logDebug(String logTag, String format, Object... args); + + public void logWarn(String logTag, String format, Object... args); + + public void logError(String logTag, String format, Object... args); +} -- cgit v1.2.3 From ad3c2d997d84ca2b1529e4864dfe1c8611d054c9 Mon Sep 17 00:00:00 2001 From: Christopher Dombroski Date: Thu, 21 Oct 2021 15:38:13 -0700 Subject: Add sts-common-util to platform_testing Bug: 190537825 Test: sts-tradefed run sts-dynamic-full Change-Id: I41b0b4893fb41a06a7dea502c6c3a4a4a11eda8c Merged-In: I41b0b4893fb41a06a7dea502c6c3a4a4a11eda8c --- libraries/sts-common-util/OWNERS | 3 + libraries/sts-common-util/device-side/Android.bp | 32 ++++ .../common/util/StsExtraBusinessLogicTestCase.java | 107 +++++++++++ libraries/sts-common-util/host-side/Android.bp | 33 ++++ .../StsExtraBusinessLogicHostTestBase.java | 110 +++++++++++ libraries/sts-common-util/util/Android.bp | 47 +++++ .../sts/common/util/DescriptionProvider.java | 34 ++++ .../src/com/android/sts/common/util/SplUtils.java | 51 +++++ .../src/com/android/sts/common/util/StsLogic.java | 209 +++++++++++++++++++++ 9 files changed, 626 insertions(+) create mode 100644 libraries/sts-common-util/OWNERS create mode 100644 libraries/sts-common-util/device-side/Android.bp create mode 100644 libraries/sts-common-util/device-side/src/com/android/sts/common/util/StsExtraBusinessLogicTestCase.java create mode 100644 libraries/sts-common-util/host-side/Android.bp create mode 100644 libraries/sts-common-util/host-side/src/com/android/sts/common/tradefed/testtype/StsExtraBusinessLogicHostTestBase.java create mode 100644 libraries/sts-common-util/util/Android.bp create mode 100644 libraries/sts-common-util/util/src/com/android/sts/common/util/DescriptionProvider.java create mode 100644 libraries/sts-common-util/util/src/com/android/sts/common/util/SplUtils.java create mode 100644 libraries/sts-common-util/util/src/com/android/sts/common/util/StsLogic.java diff --git a/libraries/sts-common-util/OWNERS b/libraries/sts-common-util/OWNERS new file mode 100644 index 000000000..69d208103 --- /dev/null +++ b/libraries/sts-common-util/OWNERS @@ -0,0 +1,3 @@ +# STS Owners +cdombroski@google.com +musashi@google.com diff --git a/libraries/sts-common-util/device-side/Android.bp b/libraries/sts-common-util/device-side/Android.bp new file mode 100644 index 000000000..1692a7c2e --- /dev/null +++ b/libraries/sts-common-util/device-side/Android.bp @@ -0,0 +1,32 @@ +// 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 { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +java_library_static { + name: "sts-device-util", + sdk_version: "test_current", + + srcs: ["src/**/*.java"], + + static_libs: [ + "sts-common-util-devicesidelib", + ], + + libs: [ + "compatibility-device-util-axt", + ], +} diff --git a/libraries/sts-common-util/device-side/src/com/android/sts/common/util/StsExtraBusinessLogicTestCase.java b/libraries/sts-common-util/device-side/src/com/android/sts/common/util/StsExtraBusinessLogicTestCase.java new file mode 100644 index 000000000..0eede1609 --- /dev/null +++ b/libraries/sts-common-util/device-side/src/com/android/sts/common/util/StsExtraBusinessLogicTestCase.java @@ -0,0 +1,107 @@ +/* + * 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 com.android.sts.common.util; + +import com.android.compatibility.common.util.ExtraBusinessLogicTestCase; + +import android.os.Build; +import android.util.Log; + +import androidx.test.InstrumentationRegistry; + +import org.junit.Rule; +import org.junit.runner.Description; + +import java.time.LocalDate; +import java.util.List; + +/** The device-side implementation of StsLogic. */ +public class StsExtraBusinessLogicTestCase extends ExtraBusinessLogicTestCase implements StsLogic { + + private LocalDate deviceSpl = null; + @Rule public DescriptionProvider descriptionProvider = new DescriptionProvider(); + + protected StsExtraBusinessLogicTestCase() { + mDependentOnBusinessLogic = false; + } + + @Override + public List getExtraBusinessLogics() { + String stsDynamicPlan = + InstrumentationRegistry.getArguments().getString("sts-dynamic-plan"); + switch (stsDynamicPlan) { + case "incremental": + return StsLogic.STS_EXTRA_BUSINESS_LOGIC_INCREMENTAL; + case "full": + return StsLogic.STS_EXTRA_BUSINESS_LOGIC_FULL; + default: + throw new RuntimeException( + "Could not find Dynamic STS plan in InstrumentationRegistry arguments"); + } + } + + @Override + public Description getTestDescription() { + return descriptionProvider.getDescription(); + } + + @Override + public LocalDate getDeviceSpl() { + if (deviceSpl == null) { + deviceSpl = SplUtils.localDateFromSplString(Build.VERSION.SECURITY_PATCH); + } + return deviceSpl; + } + + /** + * Specify the latest release bulletin. Control this from the command-line with the following: + * --test-arg + * com.android.tradefed.testtype.AndroidJUnitTest:instrumentation-arg:release-bulletin-spl:=2020-06 + */ + @Override + public LocalDate getReleaseBulletinSpl() { + String releaseBulletinSpl = + InstrumentationRegistry.getArguments().getString("release-bulletin-spl"); + if (releaseBulletinSpl == null) { + return null; + } + // bulletin is released by month; add any day - only the year and month are compared. + releaseBulletinSpl = + String.format("%s-%02d", releaseBulletinSpl, SplUtils.Type.PARTIAL.day); + return SplUtils.localDateFromSplString(releaseBulletinSpl); + } + + @Override + public void logInfo(String logTag, String format, Object... args) { + Log.i(logTag, String.format(format, args)); + } + + @Override + public void logDebug(String logTag, String format, Object... args) { + Log.d(logTag, String.format(format, args)); + } + + @Override + public void logWarn(String logTag, String format, Object... args) { + Log.w(logTag, String.format(format, args)); + } + + @Override + public void logError(String logTag, String format, Object... args) { + Log.e(logTag, String.format(format, args)); + } +} diff --git a/libraries/sts-common-util/host-side/Android.bp b/libraries/sts-common-util/host-side/Android.bp new file mode 100644 index 000000000..471056ffe --- /dev/null +++ b/libraries/sts-common-util/host-side/Android.bp @@ -0,0 +1,33 @@ +// 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 { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +java_library_host { + name: "sts-host-util", + defaults: ["cts_error_prone_rules"], + + srcs: ["src/**/*.java"], + + static_libs: [ + "sts-common-util-hostsidelib", + ], + + libs: [ + "compatibility-tradefed", + "tradefed", + ], +} diff --git a/libraries/sts-common-util/host-side/src/com/android/sts/common/tradefed/testtype/StsExtraBusinessLogicHostTestBase.java b/libraries/sts-common-util/host-side/src/com/android/sts/common/tradefed/testtype/StsExtraBusinessLogicHostTestBase.java new file mode 100644 index 000000000..c7fad5b9e --- /dev/null +++ b/libraries/sts-common-util/host-side/src/com/android/sts/common/tradefed/testtype/StsExtraBusinessLogicHostTestBase.java @@ -0,0 +1,110 @@ +/* + * 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 com.android.sts.common.tradefed.testtype; + +import com.android.compatibility.common.tradefed.testtype.ExtraBusinessLogicHostTestBase; +import com.android.ddmlib.Log; +import com.android.sts.common.util.DescriptionProvider; +import com.android.sts.common.util.SplUtils; +import com.android.sts.common.util.StsLogic; +import com.android.tradefed.device.DeviceNotAvailableException; + +import org.junit.Rule; +import org.junit.runner.Description; + +import java.time.LocalDate; +import java.util.List; + +/** The host-side implementation of StsLogic. */ +public class StsExtraBusinessLogicHostTestBase extends ExtraBusinessLogicHostTestBase + implements StsLogic { + + private LocalDate deviceSpl = null; + @Rule public DescriptionProvider descriptionProvider = new DescriptionProvider(); + + public StsExtraBusinessLogicHostTestBase() { + super(); + mDependentOnBusinessLogic = false; + } + + @Override + public List getExtraBusinessLogics() { + String stsDynamicPlan = getBuild().getBuildAttributes().get("sts-dynamic-plan"); + switch (stsDynamicPlan) { + case "incremental": + return StsLogic.STS_EXTRA_BUSINESS_LOGIC_INCREMENTAL; + case "full": + return StsLogic.STS_EXTRA_BUSINESS_LOGIC_FULL; + default: + throw new RuntimeException("Could not find Dynamic STS plan in build attributes"); + } + } + + @Override + public Description getTestDescription() { + return descriptionProvider.getDescription(); + } + + @Override + public LocalDate getDeviceSpl() { + if (deviceSpl == null) { + try { + String splString = getDevice().getProperty("ro.build.version.security_patch"); + deviceSpl = SplUtils.localDateFromSplString(splString); + } catch (DeviceNotAvailableException e) { + throw new RuntimeException("couldn't get the security patch level", e); + } + } + return deviceSpl; + } + + /** + * Specify the latest release bulletin. Control this from the command-line with the following + * command line argument: --build-attribute "release-bulletin-spl=2021-06" + */ + @Override + public LocalDate getReleaseBulletinSpl() { + String releaseBulletinSpl = getBuild().getBuildAttributes().get("release-bulletin-spl"); + if (releaseBulletinSpl == null) { + return null; + } + // bulletin is released by month; add any day - only the year and month are compared. + releaseBulletinSpl = + String.format("%s-%02d", releaseBulletinSpl, SplUtils.Type.PARTIAL.day); + return SplUtils.localDateFromSplString(releaseBulletinSpl); + } + + @Override + public void logInfo(String logTag, String format, Object... args) { + Log.i(logTag, String.format(format, args)); + } + + @Override + public void logDebug(String logTag, String format, Object... args) { + Log.d(logTag, String.format(format, args)); + } + + @Override + public void logWarn(String logTag, String format, Object... args) { + Log.w(logTag, String.format(format, args)); + } + + @Override + public void logError(String logTag, String format, Object... args) { + Log.e(logTag, String.format(format, args)); + } +} diff --git a/libraries/sts-common-util/util/Android.bp b/libraries/sts-common-util/util/Android.bp new file mode 100644 index 000000000..b94bcb0b8 --- /dev/null +++ b/libraries/sts-common-util/util/Android.bp @@ -0,0 +1,47 @@ +// Copyright (C) 2020 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 { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +// Build the common utility library for use device-side +java_library_static { + name: "sts-common-util-devicesidelib", + visibility: [ + "//platform_testing/libraries/sts-common-util/device-side", + ], + sdk_version: "current", + srcs: ["src/**/*.java"], + static_libs: [ + "junit", + "platform-test-annotations", + "compatibility-common-util-devicesidelib", + ], +} + +java_library_host { + name: "sts-common-util-hostsidelib", + visibility: [ + "//platform_testing/libraries/sts-common-util/host-side", + ], + srcs: ["src/**/*.java"], + static_libs: [ + "compatibility-common-util-hostsidelib", + ], + libs: [ + "junit", + "platform-test-annotations", + ], +} diff --git a/libraries/sts-common-util/util/src/com/android/sts/common/util/DescriptionProvider.java b/libraries/sts-common-util/util/src/com/android/sts/common/util/DescriptionProvider.java new file mode 100644 index 000000000..60d46ea97 --- /dev/null +++ b/libraries/sts-common-util/util/src/com/android/sts/common/util/DescriptionProvider.java @@ -0,0 +1,34 @@ +/* + * 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 com.android.sts.common.util; + +import org.junit.rules.TestWatcher; +import org.junit.runner.Description; + +/** Provide a way for tests to get their description while running. */ +public class DescriptionProvider extends TestWatcher { + private volatile Description description; + + @Override + protected void starting(Description description) { + this.description = description; + } + + public Description getDescription() { + return description; + } +} diff --git a/libraries/sts-common-util/util/src/com/android/sts/common/util/SplUtils.java b/libraries/sts-common-util/util/src/com/android/sts/common/util/SplUtils.java new file mode 100644 index 000000000..afb7b12af --- /dev/null +++ b/libraries/sts-common-util/util/src/com/android/sts/common/util/SplUtils.java @@ -0,0 +1,51 @@ +/* + * 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 com.android.sts.common.util; + +import java.time.Instant; +import java.time.LocalDate; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; + +/** Tools for Security Patch Levels and LocalDates representing them. */ +public final class SplUtils { + private static final ZoneId UTC_ZONE_ID = ZoneId.of("UTC"); + private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); + + public enum Type { + PARTIAL(1), // platform + COMPLETE(5); // device-specific (kernel, soc, etc) + + public final int day; + + Type(int day) { + this.day = day; + } + } + + public static LocalDate localDateFromMillis(long millis) { + return Instant.ofEpochMilli(millis).atZone(UTC_ZONE_ID).toLocalDate(); + } + + public static LocalDate localDateFromSplString(String spl) { + return LocalDate.parse(spl, formatter); + } + + public static String format(LocalDate date) { + return date.format(formatter); + } +} diff --git a/libraries/sts-common-util/util/src/com/android/sts/common/util/StsLogic.java b/libraries/sts-common-util/util/src/com/android/sts/common/util/StsLogic.java new file mode 100644 index 000000000..648199c20 --- /dev/null +++ b/libraries/sts-common-util/util/src/com/android/sts/common/util/StsLogic.java @@ -0,0 +1,209 @@ +/* + * 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 com.android.sts.common.util; + +import static org.junit.Assume.*; +import static org.junit.Assert.*; + +import android.platform.test.annotations.AsbSecurityTest; + +import com.android.compatibility.common.util.BusinessLogicMapStore; + +import org.junit.runner.Description; + +import java.time.LocalDate; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +/** Common STS extra business logic for host-side and device-side to implement. */ +public interface StsLogic { + + static final String LOG_TAG = StsLogic.class.getSimpleName(); + + // keep in sync with google3: + // //wireless/android/partner/apbs/*/config/xtsbgusinesslogic/sts_business_logic.gcl + List STS_EXTRA_BUSINESS_LOGIC_FULL = Arrays.asList(new String[]{ + "uploadSpl", + "uploadModificationTime", + "declaredSpl", + }); + List STS_EXTRA_BUSINESS_LOGIC_INCREMENTAL = Arrays.asList(new String[]{ + "uploadSpl", + "uploadModificationTime", + "declaredSpl", + "incremental", + }); + + Description getTestDescription(); + + LocalDate getDeviceSpl(); + + LocalDate getReleaseBulletinSpl(); + + default long[] getCveBugIds() { + AsbSecurityTest annotation = getTestDescription().getAnnotation(AsbSecurityTest.class); + if (annotation == null) { + return null; + } + return annotation.cveBugId(); + } + + default boolean isBugSplDataKnownMissing() { + long[] bugIds = getCveBugIds(); + if (bugIds == null) { + // no spl data, don't complain + return true; + } + // true if the bug id is older than ~ June 2020 + return Arrays.stream(bugIds).min().getAsLong() < 157905780; + } + + default LocalDate getMinTestSpl() { + Map map = BusinessLogicMapStore.getMap("security_bulletins"); + if (map == null) { + throw new IllegalArgumentException("Could not find the security bulletin map"); + } + LocalDate minSpl = null; + for (long cveBugId : getCveBugIds()) { + String splString = map.get(Long.toString(cveBugId)); + if (splString == null) { + // This bug id wasn't found in the map. + // This is a new test or the bug was removed from the bulletin and this is an old + // binary. Neither is a critical issue and the test will run in these cases. + // New test: developer should be able to write the test without getting blocked. + // Removed bug + old binary: test will run. + logWarn(LOG_TAG, "could not find the CVE bug %d in the spl map", cveBugId); + continue; + } + LocalDate spl = SplUtils.localDateFromSplString(splString); + if (minSpl == null) { + minSpl = spl; + } else if (spl.isBefore(minSpl)) { + minSpl = spl; + } + } + return minSpl; + } + + default LocalDate getMinModificationDate() { + Map map = BusinessLogicMapStore.getMap("sts_modification_times"); + if (map == null) { + throw new IllegalArgumentException("Could not find the modification date map"); + } + LocalDate minModificationDate = null; + for (long cveBugId : getCveBugIds()) { + String modificationMillisString = map.get(Long.toString(cveBugId)); + if (modificationMillisString == null) { + logInfo(LOG_TAG, + "Could not find the CVE bug %d in the modification date map", cveBugId); + continue; + } + LocalDate modificationDate = + SplUtils.localDateFromMillis(Long.parseLong(modificationMillisString)); + if (minModificationDate == null) { + minModificationDate = modificationDate; + } else if (modificationDate.isBefore(minModificationDate)) { + minModificationDate = modificationDate; + } + } + return minModificationDate; + } + + default boolean shouldSkipIncremental() { + logDebug(LOG_TAG, "filtering by incremental"); + + long[] bugIds = getCveBugIds(); + if (bugIds == null) { + // There were no @AsbSecurityTest annotations + logInfo(LOG_TAG, "not an ASB test"); + return false; + } + + // check if test spl is older than the past 6 months from the device spl + LocalDate deviceSpl = getDeviceSpl(); + LocalDate incrementalCutoffSpl = deviceSpl.plusMonths(-6); + + LocalDate minTestModifiedDate = getMinModificationDate(); + if (minTestModifiedDate == null) { + // could not get the modification date - run the test + if (isBugSplDataKnownMissing()) { + logDebug(LOG_TAG, "no data for this old test"); + return true; + } + return false; + } + if (minTestModifiedDate.isAfter(incrementalCutoffSpl)) { + logDebug(LOG_TAG, "the test was recently modified"); + return false; + } + + LocalDate minTestSpl = getMinTestSpl(); + if (minTestSpl == null) { + // could not get the test spl - run the test + logWarn(LOG_TAG, "could not get the test SPL"); + return false; + } + if (minTestSpl.isAfter(incrementalCutoffSpl)) { + logDebug(LOG_TAG, "the test has a recent SPL"); + return false; + } + + logDebug(LOG_TAG, "test should skip"); + return true; + } + + default boolean shouldSkipDeclaredSpl() { + if (getCveBugIds() == null) { + // There were no @AsbSecurityTest annotations + logInfo(LOG_TAG, "not an ASB test"); + return false; + } + + LocalDate releaseBulletinSpl = getReleaseBulletinSpl(); + LocalDate minTestSpl = getMinTestSpl(); + if (releaseBulletinSpl != null && !isBugSplDataKnownMissing()) { + // assert that the test has a known SPL when we expect the data to be fresh + assertNotNull("Unknown SPL for new CVE", minTestSpl); + + // set the days to be the same so we only compare year-month + releaseBulletinSpl = releaseBulletinSpl.withDayOfMonth(minTestSpl.getDayOfMonth()); + // the test SPL can't be equal to or after the release bulletin SPL + assertFalse("Newer SPL than release bulletin", releaseBulletinSpl.isBefore(minTestSpl)); + } + if (minTestSpl == null) { + // no SPL for this test; run normally + return false; + } + + // skip if the test is newer than the device SPL + LocalDate deviceSpl = getDeviceSpl(); + return minTestSpl.isAfter(deviceSpl); + } + + default void skip(String message) { + assumeTrue(message, false); + } + + public void logInfo(String logTag, String format, Object... args); + + public void logDebug(String logTag, String format, Object... args); + + public void logWarn(String logTag, String format, Object... args); + + public void logError(String logTag, String format, Object... args); +} -- cgit v1.2.3 From f26df263bd2ddc063422a26c3c4fdc716d8c8c81 Mon Sep 17 00:00:00 2001 From: Christopher Dombroski Date: Thu, 21 Oct 2021 15:38:13 -0700 Subject: Add sts-common-util to platform_testing Bug: 190537825 Test: sts-tradefed run sts-dynamic-full Change-Id: I41b0b4893fb41a06a7dea502c6c3a4a4a11eda8c Merged-In: I41b0b4893fb41a06a7dea502c6c3a4a4a11eda8c --- libraries/sts-common-util/OWNERS | 3 + libraries/sts-common-util/device-side/Android.bp | 32 ++++ .../common/util/StsExtraBusinessLogicTestCase.java | 107 +++++++++++ libraries/sts-common-util/host-side/Android.bp | 33 ++++ .../StsExtraBusinessLogicHostTestBase.java | 110 +++++++++++ libraries/sts-common-util/util/Android.bp | 41 ++++ .../sts/common/util/DescriptionProvider.java | 34 ++++ .../src/com/android/sts/common/util/SplUtils.java | 51 +++++ .../src/com/android/sts/common/util/StsLogic.java | 209 +++++++++++++++++++++ 9 files changed, 620 insertions(+) create mode 100644 libraries/sts-common-util/OWNERS create mode 100644 libraries/sts-common-util/device-side/Android.bp create mode 100644 libraries/sts-common-util/device-side/src/com/android/sts/common/util/StsExtraBusinessLogicTestCase.java create mode 100644 libraries/sts-common-util/host-side/Android.bp create mode 100644 libraries/sts-common-util/host-side/src/com/android/sts/common/tradefed/testtype/StsExtraBusinessLogicHostTestBase.java create mode 100644 libraries/sts-common-util/util/Android.bp create mode 100644 libraries/sts-common-util/util/src/com/android/sts/common/util/DescriptionProvider.java create mode 100644 libraries/sts-common-util/util/src/com/android/sts/common/util/SplUtils.java create mode 100644 libraries/sts-common-util/util/src/com/android/sts/common/util/StsLogic.java diff --git a/libraries/sts-common-util/OWNERS b/libraries/sts-common-util/OWNERS new file mode 100644 index 000000000..69d208103 --- /dev/null +++ b/libraries/sts-common-util/OWNERS @@ -0,0 +1,3 @@ +# STS Owners +cdombroski@google.com +musashi@google.com diff --git a/libraries/sts-common-util/device-side/Android.bp b/libraries/sts-common-util/device-side/Android.bp new file mode 100644 index 000000000..1692a7c2e --- /dev/null +++ b/libraries/sts-common-util/device-side/Android.bp @@ -0,0 +1,32 @@ +// 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 { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +java_library_static { + name: "sts-device-util", + sdk_version: "test_current", + + srcs: ["src/**/*.java"], + + static_libs: [ + "sts-common-util-devicesidelib", + ], + + libs: [ + "compatibility-device-util-axt", + ], +} diff --git a/libraries/sts-common-util/device-side/src/com/android/sts/common/util/StsExtraBusinessLogicTestCase.java b/libraries/sts-common-util/device-side/src/com/android/sts/common/util/StsExtraBusinessLogicTestCase.java new file mode 100644 index 000000000..0eede1609 --- /dev/null +++ b/libraries/sts-common-util/device-side/src/com/android/sts/common/util/StsExtraBusinessLogicTestCase.java @@ -0,0 +1,107 @@ +/* + * 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 com.android.sts.common.util; + +import com.android.compatibility.common.util.ExtraBusinessLogicTestCase; + +import android.os.Build; +import android.util.Log; + +import androidx.test.InstrumentationRegistry; + +import org.junit.Rule; +import org.junit.runner.Description; + +import java.time.LocalDate; +import java.util.List; + +/** The device-side implementation of StsLogic. */ +public class StsExtraBusinessLogicTestCase extends ExtraBusinessLogicTestCase implements StsLogic { + + private LocalDate deviceSpl = null; + @Rule public DescriptionProvider descriptionProvider = new DescriptionProvider(); + + protected StsExtraBusinessLogicTestCase() { + mDependentOnBusinessLogic = false; + } + + @Override + public List getExtraBusinessLogics() { + String stsDynamicPlan = + InstrumentationRegistry.getArguments().getString("sts-dynamic-plan"); + switch (stsDynamicPlan) { + case "incremental": + return StsLogic.STS_EXTRA_BUSINESS_LOGIC_INCREMENTAL; + case "full": + return StsLogic.STS_EXTRA_BUSINESS_LOGIC_FULL; + default: + throw new RuntimeException( + "Could not find Dynamic STS plan in InstrumentationRegistry arguments"); + } + } + + @Override + public Description getTestDescription() { + return descriptionProvider.getDescription(); + } + + @Override + public LocalDate getDeviceSpl() { + if (deviceSpl == null) { + deviceSpl = SplUtils.localDateFromSplString(Build.VERSION.SECURITY_PATCH); + } + return deviceSpl; + } + + /** + * Specify the latest release bulletin. Control this from the command-line with the following: + * --test-arg + * com.android.tradefed.testtype.AndroidJUnitTest:instrumentation-arg:release-bulletin-spl:=2020-06 + */ + @Override + public LocalDate getReleaseBulletinSpl() { + String releaseBulletinSpl = + InstrumentationRegistry.getArguments().getString("release-bulletin-spl"); + if (releaseBulletinSpl == null) { + return null; + } + // bulletin is released by month; add any day - only the year and month are compared. + releaseBulletinSpl = + String.format("%s-%02d", releaseBulletinSpl, SplUtils.Type.PARTIAL.day); + return SplUtils.localDateFromSplString(releaseBulletinSpl); + } + + @Override + public void logInfo(String logTag, String format, Object... args) { + Log.i(logTag, String.format(format, args)); + } + + @Override + public void logDebug(String logTag, String format, Object... args) { + Log.d(logTag, String.format(format, args)); + } + + @Override + public void logWarn(String logTag, String format, Object... args) { + Log.w(logTag, String.format(format, args)); + } + + @Override + public void logError(String logTag, String format, Object... args) { + Log.e(logTag, String.format(format, args)); + } +} diff --git a/libraries/sts-common-util/host-side/Android.bp b/libraries/sts-common-util/host-side/Android.bp new file mode 100644 index 000000000..471056ffe --- /dev/null +++ b/libraries/sts-common-util/host-side/Android.bp @@ -0,0 +1,33 @@ +// 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 { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +java_library_host { + name: "sts-host-util", + defaults: ["cts_error_prone_rules"], + + srcs: ["src/**/*.java"], + + static_libs: [ + "sts-common-util-hostsidelib", + ], + + libs: [ + "compatibility-tradefed", + "tradefed", + ], +} diff --git a/libraries/sts-common-util/host-side/src/com/android/sts/common/tradefed/testtype/StsExtraBusinessLogicHostTestBase.java b/libraries/sts-common-util/host-side/src/com/android/sts/common/tradefed/testtype/StsExtraBusinessLogicHostTestBase.java new file mode 100644 index 000000000..c7fad5b9e --- /dev/null +++ b/libraries/sts-common-util/host-side/src/com/android/sts/common/tradefed/testtype/StsExtraBusinessLogicHostTestBase.java @@ -0,0 +1,110 @@ +/* + * 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 com.android.sts.common.tradefed.testtype; + +import com.android.compatibility.common.tradefed.testtype.ExtraBusinessLogicHostTestBase; +import com.android.ddmlib.Log; +import com.android.sts.common.util.DescriptionProvider; +import com.android.sts.common.util.SplUtils; +import com.android.sts.common.util.StsLogic; +import com.android.tradefed.device.DeviceNotAvailableException; + +import org.junit.Rule; +import org.junit.runner.Description; + +import java.time.LocalDate; +import java.util.List; + +/** The host-side implementation of StsLogic. */ +public class StsExtraBusinessLogicHostTestBase extends ExtraBusinessLogicHostTestBase + implements StsLogic { + + private LocalDate deviceSpl = null; + @Rule public DescriptionProvider descriptionProvider = new DescriptionProvider(); + + public StsExtraBusinessLogicHostTestBase() { + super(); + mDependentOnBusinessLogic = false; + } + + @Override + public List getExtraBusinessLogics() { + String stsDynamicPlan = getBuild().getBuildAttributes().get("sts-dynamic-plan"); + switch (stsDynamicPlan) { + case "incremental": + return StsLogic.STS_EXTRA_BUSINESS_LOGIC_INCREMENTAL; + case "full": + return StsLogic.STS_EXTRA_BUSINESS_LOGIC_FULL; + default: + throw new RuntimeException("Could not find Dynamic STS plan in build attributes"); + } + } + + @Override + public Description getTestDescription() { + return descriptionProvider.getDescription(); + } + + @Override + public LocalDate getDeviceSpl() { + if (deviceSpl == null) { + try { + String splString = getDevice().getProperty("ro.build.version.security_patch"); + deviceSpl = SplUtils.localDateFromSplString(splString); + } catch (DeviceNotAvailableException e) { + throw new RuntimeException("couldn't get the security patch level", e); + } + } + return deviceSpl; + } + + /** + * Specify the latest release bulletin. Control this from the command-line with the following + * command line argument: --build-attribute "release-bulletin-spl=2021-06" + */ + @Override + public LocalDate getReleaseBulletinSpl() { + String releaseBulletinSpl = getBuild().getBuildAttributes().get("release-bulletin-spl"); + if (releaseBulletinSpl == null) { + return null; + } + // bulletin is released by month; add any day - only the year and month are compared. + releaseBulletinSpl = + String.format("%s-%02d", releaseBulletinSpl, SplUtils.Type.PARTIAL.day); + return SplUtils.localDateFromSplString(releaseBulletinSpl); + } + + @Override + public void logInfo(String logTag, String format, Object... args) { + Log.i(logTag, String.format(format, args)); + } + + @Override + public void logDebug(String logTag, String format, Object... args) { + Log.d(logTag, String.format(format, args)); + } + + @Override + public void logWarn(String logTag, String format, Object... args) { + Log.w(logTag, String.format(format, args)); + } + + @Override + public void logError(String logTag, String format, Object... args) { + Log.e(logTag, String.format(format, args)); + } +} diff --git a/libraries/sts-common-util/util/Android.bp b/libraries/sts-common-util/util/Android.bp new file mode 100644 index 000000000..613d21792 --- /dev/null +++ b/libraries/sts-common-util/util/Android.bp @@ -0,0 +1,41 @@ +// Copyright (C) 2020 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 { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +// Build the common utility library for use device-side +java_library_static { + name: "sts-common-util-devicesidelib", + sdk_version: "current", + srcs: ["src/**/*.java"], + static_libs: [ + "junit", + "platform-test-annotations", + "compatibility-common-util-devicesidelib", + ], +} + +java_library_host { + name: "sts-common-util-hostsidelib", + srcs: ["src/**/*.java"], + static_libs: [ + "compatibility-common-util-hostsidelib", + ], + libs: [ + "junit", + "platform-test-annotations", + ], +} diff --git a/libraries/sts-common-util/util/src/com/android/sts/common/util/DescriptionProvider.java b/libraries/sts-common-util/util/src/com/android/sts/common/util/DescriptionProvider.java new file mode 100644 index 000000000..60d46ea97 --- /dev/null +++ b/libraries/sts-common-util/util/src/com/android/sts/common/util/DescriptionProvider.java @@ -0,0 +1,34 @@ +/* + * 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 com.android.sts.common.util; + +import org.junit.rules.TestWatcher; +import org.junit.runner.Description; + +/** Provide a way for tests to get their description while running. */ +public class DescriptionProvider extends TestWatcher { + private volatile Description description; + + @Override + protected void starting(Description description) { + this.description = description; + } + + public Description getDescription() { + return description; + } +} diff --git a/libraries/sts-common-util/util/src/com/android/sts/common/util/SplUtils.java b/libraries/sts-common-util/util/src/com/android/sts/common/util/SplUtils.java new file mode 100644 index 000000000..afb7b12af --- /dev/null +++ b/libraries/sts-common-util/util/src/com/android/sts/common/util/SplUtils.java @@ -0,0 +1,51 @@ +/* + * 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 com.android.sts.common.util; + +import java.time.Instant; +import java.time.LocalDate; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; + +/** Tools for Security Patch Levels and LocalDates representing them. */ +public final class SplUtils { + private static final ZoneId UTC_ZONE_ID = ZoneId.of("UTC"); + private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); + + public enum Type { + PARTIAL(1), // platform + COMPLETE(5); // device-specific (kernel, soc, etc) + + public final int day; + + Type(int day) { + this.day = day; + } + } + + public static LocalDate localDateFromMillis(long millis) { + return Instant.ofEpochMilli(millis).atZone(UTC_ZONE_ID).toLocalDate(); + } + + public static LocalDate localDateFromSplString(String spl) { + return LocalDate.parse(spl, formatter); + } + + public static String format(LocalDate date) { + return date.format(formatter); + } +} diff --git a/libraries/sts-common-util/util/src/com/android/sts/common/util/StsLogic.java b/libraries/sts-common-util/util/src/com/android/sts/common/util/StsLogic.java new file mode 100644 index 000000000..648199c20 --- /dev/null +++ b/libraries/sts-common-util/util/src/com/android/sts/common/util/StsLogic.java @@ -0,0 +1,209 @@ +/* + * 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 com.android.sts.common.util; + +import static org.junit.Assume.*; +import static org.junit.Assert.*; + +import android.platform.test.annotations.AsbSecurityTest; + +import com.android.compatibility.common.util.BusinessLogicMapStore; + +import org.junit.runner.Description; + +import java.time.LocalDate; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +/** Common STS extra business logic for host-side and device-side to implement. */ +public interface StsLogic { + + static final String LOG_TAG = StsLogic.class.getSimpleName(); + + // keep in sync with google3: + // //wireless/android/partner/apbs/*/config/xtsbgusinesslogic/sts_business_logic.gcl + List STS_EXTRA_BUSINESS_LOGIC_FULL = Arrays.asList(new String[]{ + "uploadSpl", + "uploadModificationTime", + "declaredSpl", + }); + List STS_EXTRA_BUSINESS_LOGIC_INCREMENTAL = Arrays.asList(new String[]{ + "uploadSpl", + "uploadModificationTime", + "declaredSpl", + "incremental", + }); + + Description getTestDescription(); + + LocalDate getDeviceSpl(); + + LocalDate getReleaseBulletinSpl(); + + default long[] getCveBugIds() { + AsbSecurityTest annotation = getTestDescription().getAnnotation(AsbSecurityTest.class); + if (annotation == null) { + return null; + } + return annotation.cveBugId(); + } + + default boolean isBugSplDataKnownMissing() { + long[] bugIds = getCveBugIds(); + if (bugIds == null) { + // no spl data, don't complain + return true; + } + // true if the bug id is older than ~ June 2020 + return Arrays.stream(bugIds).min().getAsLong() < 157905780; + } + + default LocalDate getMinTestSpl() { + Map map = BusinessLogicMapStore.getMap("security_bulletins"); + if (map == null) { + throw new IllegalArgumentException("Could not find the security bulletin map"); + } + LocalDate minSpl = null; + for (long cveBugId : getCveBugIds()) { + String splString = map.get(Long.toString(cveBugId)); + if (splString == null) { + // This bug id wasn't found in the map. + // This is a new test or the bug was removed from the bulletin and this is an old + // binary. Neither is a critical issue and the test will run in these cases. + // New test: developer should be able to write the test without getting blocked. + // Removed bug + old binary: test will run. + logWarn(LOG_TAG, "could not find the CVE bug %d in the spl map", cveBugId); + continue; + } + LocalDate spl = SplUtils.localDateFromSplString(splString); + if (minSpl == null) { + minSpl = spl; + } else if (spl.isBefore(minSpl)) { + minSpl = spl; + } + } + return minSpl; + } + + default LocalDate getMinModificationDate() { + Map map = BusinessLogicMapStore.getMap("sts_modification_times"); + if (map == null) { + throw new IllegalArgumentException("Could not find the modification date map"); + } + LocalDate minModificationDate = null; + for (long cveBugId : getCveBugIds()) { + String modificationMillisString = map.get(Long.toString(cveBugId)); + if (modificationMillisString == null) { + logInfo(LOG_TAG, + "Could not find the CVE bug %d in the modification date map", cveBugId); + continue; + } + LocalDate modificationDate = + SplUtils.localDateFromMillis(Long.parseLong(modificationMillisString)); + if (minModificationDate == null) { + minModificationDate = modificationDate; + } else if (modificationDate.isBefore(minModificationDate)) { + minModificationDate = modificationDate; + } + } + return minModificationDate; + } + + default boolean shouldSkipIncremental() { + logDebug(LOG_TAG, "filtering by incremental"); + + long[] bugIds = getCveBugIds(); + if (bugIds == null) { + // There were no @AsbSecurityTest annotations + logInfo(LOG_TAG, "not an ASB test"); + return false; + } + + // check if test spl is older than the past 6 months from the device spl + LocalDate deviceSpl = getDeviceSpl(); + LocalDate incrementalCutoffSpl = deviceSpl.plusMonths(-6); + + LocalDate minTestModifiedDate = getMinModificationDate(); + if (minTestModifiedDate == null) { + // could not get the modification date - run the test + if (isBugSplDataKnownMissing()) { + logDebug(LOG_TAG, "no data for this old test"); + return true; + } + return false; + } + if (minTestModifiedDate.isAfter(incrementalCutoffSpl)) { + logDebug(LOG_TAG, "the test was recently modified"); + return false; + } + + LocalDate minTestSpl = getMinTestSpl(); + if (minTestSpl == null) { + // could not get the test spl - run the test + logWarn(LOG_TAG, "could not get the test SPL"); + return false; + } + if (minTestSpl.isAfter(incrementalCutoffSpl)) { + logDebug(LOG_TAG, "the test has a recent SPL"); + return false; + } + + logDebug(LOG_TAG, "test should skip"); + return true; + } + + default boolean shouldSkipDeclaredSpl() { + if (getCveBugIds() == null) { + // There were no @AsbSecurityTest annotations + logInfo(LOG_TAG, "not an ASB test"); + return false; + } + + LocalDate releaseBulletinSpl = getReleaseBulletinSpl(); + LocalDate minTestSpl = getMinTestSpl(); + if (releaseBulletinSpl != null && !isBugSplDataKnownMissing()) { + // assert that the test has a known SPL when we expect the data to be fresh + assertNotNull("Unknown SPL for new CVE", minTestSpl); + + // set the days to be the same so we only compare year-month + releaseBulletinSpl = releaseBulletinSpl.withDayOfMonth(minTestSpl.getDayOfMonth()); + // the test SPL can't be equal to or after the release bulletin SPL + assertFalse("Newer SPL than release bulletin", releaseBulletinSpl.isBefore(minTestSpl)); + } + if (minTestSpl == null) { + // no SPL for this test; run normally + return false; + } + + // skip if the test is newer than the device SPL + LocalDate deviceSpl = getDeviceSpl(); + return minTestSpl.isAfter(deviceSpl); + } + + default void skip(String message) { + assumeTrue(message, false); + } + + public void logInfo(String logTag, String format, Object... args); + + public void logDebug(String logTag, String format, Object... args); + + public void logWarn(String logTag, String format, Object... args); + + public void logError(String logTag, String format, Object... args); +} -- cgit v1.2.3 From 12f20b906e5cdc658b63ef72b08e29ef9d3d452e Mon Sep 17 00:00:00 2001 From: Christopher Dombroski Date: Thu, 20 Jan 2022 16:53:27 -0800 Subject: [RESTRICT AUTOMERGE]: Add sts-common-util to platform_testing Fix build configuration Bug: 190537825 Test: sts-tradefed run sts-dynamic-full Change-Id: I617f2ca8d9cad002ee04c2ca4089eaada5633d69 --- libraries/compatibility-common-util/Android.bp | 1 + libraries/sts-common-util/host-side/Android.bp | 2 +- libraries/sts-common-util/util/Android.bp | 4 ++-- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/libraries/compatibility-common-util/Android.bp b/libraries/compatibility-common-util/Android.bp index bc40510fc..e456fa958 100644 --- a/libraries/compatibility-common-util/Android.bp +++ b/libraries/compatibility-common-util/Android.bp @@ -33,6 +33,7 @@ java_library { name: "compatibility-common-util-lib", visibility: [ "//test/suite_harness/common/util", + "//platform_testing/libraries/sts-common-util/util", ], srcs: ["src/**/*.java"], host_supported: true, diff --git a/libraries/sts-common-util/host-side/Android.bp b/libraries/sts-common-util/host-side/Android.bp index 471056ffe..49f51f4d7 100644 --- a/libraries/sts-common-util/host-side/Android.bp +++ b/libraries/sts-common-util/host-side/Android.bp @@ -23,7 +23,7 @@ java_library_host { srcs: ["src/**/*.java"], static_libs: [ - "sts-common-util-hostsidelib", + "sts-common-util-lib", ], libs: [ diff --git a/libraries/sts-common-util/util/Android.bp b/libraries/sts-common-util/util/Android.bp index b94bcb0b8..08adbe6e3 100644 --- a/libraries/sts-common-util/util/Android.bp +++ b/libraries/sts-common-util/util/Android.bp @@ -32,13 +32,13 @@ java_library_static { } java_library_host { - name: "sts-common-util-hostsidelib", + name: "sts-common-util-lib", visibility: [ "//platform_testing/libraries/sts-common-util/host-side", ], srcs: ["src/**/*.java"], static_libs: [ - "compatibility-common-util-hostsidelib", + "compatibility-common-util-lib", ], libs: [ "junit", -- cgit v1.2.3 From e17890701925962292d9ac0f02243fc8d0b88ee2 Mon Sep 17 00:00:00 2001 From: Duy Truong Date: Tue, 25 Jan 2022 18:45:07 -0800 Subject: Add library to use OverlayFS to make read-only partitions writable. Using OverlayFsUtils class will allow writing to read-only partitions without having to disable verity and without having to reboot. Note that all modifications will be deleted upon reboot. Bug: 216394844 Test: build sts; run sts with a manually crafted test testcase. Change-Id: Ia176be66e2f45cad053e7e84e20682106f6ea95c --- .../src/com/android/sts/common/OverlayFsUtils.java | 94 ++++++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 libraries/sts-common-util/host-side/src/com/android/sts/common/OverlayFsUtils.java diff --git a/libraries/sts-common-util/host-side/src/com/android/sts/common/OverlayFsUtils.java b/libraries/sts-common-util/host-side/src/com/android/sts/common/OverlayFsUtils.java new file mode 100644 index 000000000..f523fb7a4 --- /dev/null +++ b/libraries/sts-common-util/host-side/src/com/android/sts/common/OverlayFsUtils.java @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2022 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 com.android.sts.common; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.regex.Pattern; +import java.util.regex.Matcher; + +import com.android.tradefed.device.ITestDevice; +import com.android.tradefed.device.DeviceNotAvailableException; +import com.android.tradefed.util.CommandResult; +import com.android.tradefed.util.CommandStatus; + +public class OverlayFsUtils { + // output of `stat`, e.g. "root shell 755 u:object_r:vendor_file:s0" + static final Pattern PERM_PATTERN = + Pattern.compile( + "^(?[a-zA-Z0-9_-]+) (?[a-zA-Z0-9_-]+) (?[0-7]+)" + + " (?.*)$"); + + /** + * Mounts an OverlayFS dir over the top most common dir in the list. + * + *

The directory should be writable after this returns successfully. To cleanup, reboot the + * device as unfortunately unmounting overlayfs is complicated. + * + * @param device The test device to setup overlayfs for. + * @param dir The directory to make writable. Directories with single quotes are not supported. + */ + public static void makeWritable(ITestDevice device, String dir) + throws DeviceNotAvailableException, IOException { + // TODO(duytruong): This should ideally be made into a TestRule that also handles cleanups + // However, test devices initiation is done in one of the @Before, after a rule's setup. + assertTrue("Can't acquire root for " + device.getSerialNumber(), device.enableAdbRoot()); + + String statOut = runAndCheck(device, "stat -c '%U %G %a %C' '" + dir + "'"); + Matcher m = PERM_PATTERN.matcher(statOut); + assertTrue("Bad stats output: " + statOut, m.find()); + String user = m.group("user"); + String group = m.group("group"); + String unixPerm = m.group("perm"); + String seContext = m.group("secontext"); + + Path tempdir = Paths.get("/mnt", "stsoverlayfs", dir); + String upperdir = tempdir.resolve("upper").toString(); + String workdir = tempdir.resolve("workdir").toString(); + + runAndCheck(device, String.format("mkdir -p '%s' '%s'", upperdir, workdir)); + runAndCheck(device, String.format("chown %s:%s '%s'", user, group, upperdir)); + runAndCheck(device, String.format("chcon '%s' '%s'", seContext, upperdir)); + runAndCheck(device, String.format("chmod %s '%s'", unixPerm, upperdir)); + + String mountCmd = + String.format( + "mount -t overlay overlay -o lowerdir='%s',upperdir='%s',workdir='%s' '%s'", + dir, upperdir, workdir, dir); + runAndCheck(device, mountCmd); + } + + /** + * Execute shell command on device, throws AssumptionViolatedException upon failure. + * + * @return stdout. + */ + private static String runAndCheck(ITestDevice device, String cmd) + throws DeviceNotAvailableException { + CommandResult res = device.executeShellV2Command(cmd); + String failMsg = + String.format( + "cmd failed: %s\ncode: %s\nstdout:\n%s\nstderr:\n%s", + cmd, res.getExitCode(), res.getStdout(), res.getStderr()); + assertEquals(failMsg, res.getStatus(), CommandStatus.SUCCESS); + return res.getStdout(); + } +} -- cgit v1.2.3 From f198453ca886201a4c734fe294b7bc560649b3f5 Mon Sep 17 00:00:00 2001 From: Duy Truong Date: Tue, 25 Jan 2022 18:45:07 -0800 Subject: Add library to use OverlayFS to make read-only partitions writable. Using OverlayFsUtils class will allow writing to read-only partitions without having to disable verity and without having to reboot. Note that all modifications will be deleted upon reboot. Bug: 216394844 Test: build sts; run sts with a manually crafted test testcase. Change-Id: Ia176be66e2f45cad053e7e84e20682106f6ea95c Merged-In: Ia176be66e2f45cad053e7e84e20682106f6ea95c (cherry picked from commit e17890701925962292d9ac0f02243fc8d0b88ee2) --- .../src/com/android/sts/common/OverlayFsUtils.java | 94 ++++++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 libraries/sts-common-util/host-side/src/com/android/sts/common/OverlayFsUtils.java diff --git a/libraries/sts-common-util/host-side/src/com/android/sts/common/OverlayFsUtils.java b/libraries/sts-common-util/host-side/src/com/android/sts/common/OverlayFsUtils.java new file mode 100644 index 000000000..f523fb7a4 --- /dev/null +++ b/libraries/sts-common-util/host-side/src/com/android/sts/common/OverlayFsUtils.java @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2022 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 com.android.sts.common; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.regex.Pattern; +import java.util.regex.Matcher; + +import com.android.tradefed.device.ITestDevice; +import com.android.tradefed.device.DeviceNotAvailableException; +import com.android.tradefed.util.CommandResult; +import com.android.tradefed.util.CommandStatus; + +public class OverlayFsUtils { + // output of `stat`, e.g. "root shell 755 u:object_r:vendor_file:s0" + static final Pattern PERM_PATTERN = + Pattern.compile( + "^(?[a-zA-Z0-9_-]+) (?[a-zA-Z0-9_-]+) (?[0-7]+)" + + " (?.*)$"); + + /** + * Mounts an OverlayFS dir over the top most common dir in the list. + * + *

The directory should be writable after this returns successfully. To cleanup, reboot the + * device as unfortunately unmounting overlayfs is complicated. + * + * @param device The test device to setup overlayfs for. + * @param dir The directory to make writable. Directories with single quotes are not supported. + */ + public static void makeWritable(ITestDevice device, String dir) + throws DeviceNotAvailableException, IOException { + // TODO(duytruong): This should ideally be made into a TestRule that also handles cleanups + // However, test devices initiation is done in one of the @Before, after a rule's setup. + assertTrue("Can't acquire root for " + device.getSerialNumber(), device.enableAdbRoot()); + + String statOut = runAndCheck(device, "stat -c '%U %G %a %C' '" + dir + "'"); + Matcher m = PERM_PATTERN.matcher(statOut); + assertTrue("Bad stats output: " + statOut, m.find()); + String user = m.group("user"); + String group = m.group("group"); + String unixPerm = m.group("perm"); + String seContext = m.group("secontext"); + + Path tempdir = Paths.get("/mnt", "stsoverlayfs", dir); + String upperdir = tempdir.resolve("upper").toString(); + String workdir = tempdir.resolve("workdir").toString(); + + runAndCheck(device, String.format("mkdir -p '%s' '%s'", upperdir, workdir)); + runAndCheck(device, String.format("chown %s:%s '%s'", user, group, upperdir)); + runAndCheck(device, String.format("chcon '%s' '%s'", seContext, upperdir)); + runAndCheck(device, String.format("chmod %s '%s'", unixPerm, upperdir)); + + String mountCmd = + String.format( + "mount -t overlay overlay -o lowerdir='%s',upperdir='%s',workdir='%s' '%s'", + dir, upperdir, workdir, dir); + runAndCheck(device, mountCmd); + } + + /** + * Execute shell command on device, throws AssumptionViolatedException upon failure. + * + * @return stdout. + */ + private static String runAndCheck(ITestDevice device, String cmd) + throws DeviceNotAvailableException { + CommandResult res = device.executeShellV2Command(cmd); + String failMsg = + String.format( + "cmd failed: %s\ncode: %s\nstdout:\n%s\nstderr:\n%s", + cmd, res.getExitCode(), res.getStdout(), res.getStderr()); + assertEquals(failMsg, res.getStatus(), CommandStatus.SUCCESS); + return res.getStdout(); + } +} -- cgit v1.2.3