diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-08-11 16:50:37 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-08-11 16:50:37 +0000 |
commit | 344069beabc939d6ca3652fa8ea1e07b3fc8420a (patch) | |
tree | e06a3e930c3204f42e531b824bcf9063fff2c762 | |
parent | dcd5a135e87a90d0ebaec7cb3a8e5a8e669b5427 (diff) | |
parent | a045d26415d15737628b6f6ed54a60961d7ea253 (diff) | |
download | platform_testing-344069beabc939d6ca3652fa8ea1e07b3fc8420a.tar.gz |
Snap for 8935351 from a045d26415d15737628b6f6ed54a60961d7ea253 to mainline-uwb-releaseaml_uwb_331015040
Change-Id: I146399419aac694bb7cc8c65badc9f555782f3c4
6 files changed, 628 insertions, 3 deletions
diff --git a/libraries/collectors-helper/memory/src/com/android/helpers/MemLeaksHelper.java b/libraries/collectors-helper/memory/src/com/android/helpers/MemLeaksHelper.java index a5025017c..cdaeaecb5 100644 --- a/libraries/collectors-helper/memory/src/com/android/helpers/MemLeaksHelper.java +++ b/libraries/collectors-helper/memory/src/com/android/helpers/MemLeaksHelper.java @@ -82,9 +82,11 @@ public class MemLeaksHelper implements ICollectorHelper<Long> { if (mDiffOnFlag) { for (String processName : current.keySet()) { - results.put( - processName, - current.get(processName) - mPrevious.getOrDefault(processName, 0L)); + if (mPrevious.containsKey(processName)) { + results.put(processName, current.get(processName) - mPrevious.get(processName)); + } else { + results.put(processName, current.get(processName)); + } } } else { return current; diff --git a/libraries/health/rules/src/android/platform/test/rule/AddAppToHomescreenRule.java b/libraries/health/rules/src/android/platform/test/rule/AddAppToHomescreenRule.java new file mode 100644 index 000000000..764cbdcc9 --- /dev/null +++ b/libraries/health/rules/src/android/platform/test/rule/AddAppToHomescreenRule.java @@ -0,0 +1,53 @@ +/* + * 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 android.platform.test.rule; + +import android.util.Log; + +import com.android.launcher3.tapl.LauncherInstrumentation; +import com.android.launcher3.tapl.Workspace; + +import org.junit.runner.Description; + +/** Drags an app to the homescreen for quick access. */ +public class AddAppToHomescreenRule extends TestWatcher { + private final String LOG_TAG = AddAppToHomescreenRule.class.getSimpleName(); + + private final LauncherInstrumentation mLauncher = new LauncherInstrumentation(); + + private final String mAppName; + + public AddAppToHomescreenRule(String appName) { + mAppName = appName; + } + + @Override + protected void starting(Description description) { + mLauncher.pressHome(); + Workspace workspace = mLauncher.getWorkspace(); + if (workspace.tryGetWorkspaceAppIcon(mAppName) != null) { + Log.d(LOG_TAG, "App icon is already on the homescreen."); + } else { + Log.d(LOG_TAG, "Adding app icon to home screen."); + mLauncher + .getWorkspace() + .switchToAllApps() + .getAppIcon(mAppName) + .dragToWorkspace(/* startsActivity */ false, /* isWidgetShortcut */ false); + } + } +} diff --git a/libraries/sts-common-util/host-side/Android.bp b/libraries/sts-common-util/host-side/Android.bp index 7a07ef4b8..59121b05a 100644 --- a/libraries/sts-common-util/host-side/Android.bp +++ b/libraries/sts-common-util/host-side/Android.bp @@ -28,6 +28,7 @@ java_library_host { ], libs: [ + "compatibility-host-util", "compatibility-tradefed", "guava", "tradefed", diff --git a/libraries/sts-common-util/host-side/src/com/android/sts/common/tradefed/testtype/NonRootSecurityTestCase.java b/libraries/sts-common-util/host-side/src/com/android/sts/common/tradefed/testtype/NonRootSecurityTestCase.java new file mode 100644 index 000000000..7ee3afdce --- /dev/null +++ b/libraries/sts-common-util/host-side/src/com/android/sts/common/tradefed/testtype/NonRootSecurityTestCase.java @@ -0,0 +1,37 @@ +/* + * 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.tradefed.testtype; + +import static org.junit.Assume.assumeTrue; + +import com.android.tradefed.device.DeviceNotAvailableException; + +import org.junit.Before; + +/** + * Class of tests that runs on devices that don't have and should not have adb root. + * + * <p>To optimize performance, all non-root tests should be grouped into the same module. + */ +public class NonRootSecurityTestCase extends SecurityTestCase { + + /** Make sure adb root is not enable on device after SecurityTestCase's setUp. */ + @Before + public void setUpUnroot() throws DeviceNotAvailableException { + assumeTrue("Could not disable adb root on device.", getDevice().disableAdbRoot()); + } +} diff --git a/libraries/sts-common-util/host-side/src/com/android/sts/common/tradefed/testtype/RootSecurityTestCase.java b/libraries/sts-common-util/host-side/src/com/android/sts/common/tradefed/testtype/RootSecurityTestCase.java new file mode 100644 index 000000000..d299e7198 --- /dev/null +++ b/libraries/sts-common-util/host-side/src/com/android/sts/common/tradefed/testtype/RootSecurityTestCase.java @@ -0,0 +1,58 @@ +/* + * 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.tradefed.testtype; + +import static org.junit.Assume.assumeTrue; + +import com.android.tradefed.device.DeviceNotAvailableException; +import com.android.tradefed.device.ITestDevice; +import com.android.tradefed.log.LogUtil.CLog; + +import org.junit.Before; + +/** + * Class of tests that need root on device to run. + * + * <p>To optimize performance, all tests that need root to run should be grouped into the same + * module. + */ +public class RootSecurityTestCase extends SecurityTestCase { + + /** Enable root after SecurityTestCase's setUp */ + @Before + public void setUpRoot() throws DeviceNotAvailableException { + assumeTrue("Could not enable adb root on device.", getDevice().enableAdbRoot()); + } + + /** + * Try to enable adb root on device. + * + * <p>Use {@link NativeDevice#enableAdbRoot()} internally. The test methods calling this + * function should run even if enableAdbRoot fails, which is why the return value is ignored. + * However, we may want to act on that data point in the future. + */ + protected static boolean enableAdbRoot(ITestDevice device) throws DeviceNotAvailableException { + if (device.enableAdbRoot()) { + return true; + } else { + CLog.e( + "\"enable-root\" set to false! " + + "Root is required to check if device is vulnerable."); + return false; + } + } +} diff --git a/libraries/sts-common-util/host-side/src/com/android/sts/common/tradefed/testtype/SecurityTestCase.java b/libraries/sts-common-util/host-side/src/com/android/sts/common/tradefed/testtype/SecurityTestCase.java new file mode 100644 index 000000000..b88787188 --- /dev/null +++ b/libraries/sts-common-util/host-side/src/com/android/sts/common/tradefed/testtype/SecurityTestCase.java @@ -0,0 +1,474 @@ +/* + * Copyright (C) 2019 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 static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertFalse; +import static org.junit.Assume.assumeFalse; +import static org.junit.Assume.assumeTrue; + +import com.android.compatibility.common.util.MetricsReportLog; +import com.android.compatibility.common.util.ResultType; +import com.android.compatibility.common.util.ResultUnit; +import com.android.ddmlib.Log.LogLevel; +import com.android.sts.common.HostsideMainlineModuleDetector; +import com.android.sts.common.PocPusher; +import com.android.sts.common.RegexUtils; +import com.android.tradefed.build.IBuildInfo; +import com.android.tradefed.config.Option; +import com.android.tradefed.device.DeviceNotAvailableException; +import com.android.tradefed.device.ITestDevice; +import com.android.tradefed.log.LogUtil.CLog; +import com.android.tradefed.testtype.IAbi; + +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.rules.TestName; + +import java.math.BigInteger; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.Callable; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class SecurityTestCase extends StsExtraBusinessLogicHostTestBase { + + private static final String LOG_TAG = "SecurityTestCase"; + private static final int RADIX_HEX = 16; + + protected static final int TIMEOUT_DEFAULT = 60; + // account for the poc timer of 5 minutes (+15 seconds for safety) + public static final int TIMEOUT_NONDETERMINISTIC = 315; + + private long kernelStartTime = -1; + + private HostsideMainlineModuleDetector mainlineModuleDetector = + new HostsideMainlineModuleDetector(this); + + @Rule public TestName testName = new TestName(); + @Rule public PocPusher pocPusher = new PocPusher(); + + private static Map<ITestDevice, IBuildInfo> sBuildInfo = new HashMap<>(); + private static Map<ITestDevice, IAbi> sAbi = new HashMap<>(); + private static Map<ITestDevice, String> sTestName = new HashMap<>(); + private static Map<ITestDevice, PocPusher> sPocPusher = new HashMap<>(); + + @Option( + name = "set-kptr_restrict", + description = "If kptr_restrict should be set to 2 after every reboot") + private boolean setKptr_restrict = false; + + private boolean ignoreKernelAddress = false; + + /** Waits for device to be online, marks the most recent boottime of the device */ + @Before + public void setUp() throws Exception { + getDevice().waitForDeviceAvailable(); + updateKernelStartTime(); + // TODO:(badash@): Watch for other things to track. + // Specifically time when app framework starts + + sBuildInfo.put(getDevice(), getBuild()); + sAbi.put(getDevice(), getAbi()); + sTestName.put(getDevice(), testName.getMethodName()); + + pocPusher.setDevice(getDevice()).setBuild(getBuild()).setAbi(getAbi()); + sPocPusher.put(getDevice(), pocPusher); + + if (setKptr_restrict) { + boolean wasRoot = getDevice().isAdbRoot(); + + if (wasRoot || getDevice().enableAdbRoot()) { + CLog.i("setting kptr_restrict to 2"); + getDevice().executeShellCommand("echo 2 > /proc/sys/kernel/kptr_restrict"); + if (!wasRoot) { + getDevice().disableAdbRoot(); + } + } else { + CLog.i("Not a rootable device - could not set kptr_restrict to 2"); + ignoreKernelAddress = true; + } + } + } + + /** + * Makes sure the phone is online, and the ensure the current boottime is within 2 seconds (due + * to rounding) of the previous boottime to check if The phone has crashed. + */ + @After + public void tearDown() throws Exception { + try { + getDevice().waitForDeviceAvailable(90 * 1000); + } catch (DeviceNotAvailableException e) { + // Force a disconnection of all existing sessions to see if that unsticks adbd. + getDevice().executeAdbCommand("reconnect"); + getDevice().waitForDeviceAvailable(30 * 1000); + } + + if (kernelStartTime != -1) { + // only fail when the kernel start time is valid + long deviceTime = getDeviceUptime() + kernelStartTime; + long hostTime = System.currentTimeMillis() / 1000; + assertTrue("Phone has had a hard reset", (hostTime - deviceTime) < 2); + kernelStartTime = -1; + } + + logAndTerminateTestProcesses(); + + // TODO(badash@): add ability to catch runtime restart + } + + public static IBuildInfo getBuildInfo(ITestDevice device) { + return sBuildInfo.get(device); + } + + public static IAbi getAbi(ITestDevice device) { + return sAbi.get(device); + } + + public static String getTestName(ITestDevice device) { + return sTestName.get(device); + } + + public static PocPusher getPocPusher(ITestDevice device) { + return sPocPusher.get(device); + } + + // TODO convert existing assertMatches*() to RegexUtils.assertMatches*() + // b/123237827 + @Deprecated + public void assertMatches(String pattern, String input) throws Exception { + RegexUtils.assertContains(pattern, input); + } + + @Deprecated + public void assertMatchesMultiLine(String pattern, String input) throws Exception { + RegexUtils.assertContainsMultiline(pattern, input); + } + + @Deprecated + public void assertNotMatches(String pattern, String input) throws Exception { + RegexUtils.assertNotContains(pattern, input); + } + + @Deprecated + public void assertNotMatchesMultiLine(String pattern, String input) throws Exception { + RegexUtils.assertNotContainsMultiline(pattern, input); + } + + /** + * Runs a provided function that collects a String to test against kernel pointer leaks. The + * getPtrFunction function implementation must return a String that starts with the pointer. + * i.e. "01234567". Trailing characters are allowed except for [0-9a-fA-F]. In the event that + * the pointer appears to be vulnerable, a JUnit assert is thrown. Since kernel pointers can be + * hashed, there is a possibility the hashed pointer overlaps into the normal kernel space. The + * test re-runs to make false positives statistically insignificant. When kernel pointers won't + * change without a reboot, provide a device to reboot. + * + * @param getPtrFunction a function that returns a string that starts with a pointer + * @param deviceToReboot device to reboot when kernel pointers won't change + */ + public void assertNotKernelPointer(Callable<String> getPtrFunction, ITestDevice deviceToReboot) + throws Exception { + assumeFalse("Could not set kptr_restrict to 2, ignoring kptr test.", ignoreKernelAddress); + + MetricsReportLog reportLog = buildMetricsReportLog(getDevice()); + + int kptrRestrict; + try { + kptrRestrict = + Integer.parseInt( + getDevice() + .executeShellV2Command("cat /proc/sys/kernel/kptr_restrict") + .getStdout() + .trim()); + } catch (NumberFormatException e) { + kptrRestrict = -1; + } + reportLog.addValue("kptr_restrict", kptrRestrict, ResultType.NEUTRAL, ResultUnit.NONE); + + boolean isKernelPointer = true; + String ptr = null; + for (int i = 0; i < 4; i++) { // ~0.4% chance of false positive + ptr = getPtrFunction.call(); + if (ptr == null) { + isKernelPointer = false; + break; + } + reportLog.addValue("address" + i, ptr, ResultType.NEUTRAL, ResultUnit.NONE); + + if (!isKptr(ptr)) { + // quit early because the ptr is likely hashed or zeroed. + isKernelPointer = false; + break; + } + if (deviceToReboot != null) { + deviceToReboot.nonBlockingReboot(); + deviceToReboot.waitForDeviceAvailable(); + updateKernelStartTime(); + } + } + reportLog.addValue( + "is_kernel_pointer", isKernelPointer, ResultType.NEUTRAL, ResultUnit.NONE); + reportLog.submit(); + assertFalse( + String.format( + "\"%s\" is an exposed kernel pointer. The device kptr_restrict is" + + " \"%d\".", + ptr, kptrRestrict) + + "Please check the help center FAQ#2 at " + + "https://support.google.com/androidpartners_security/answer/9144408?hl=en&ref_topic=7534918", + isKernelPointer); + } + + private boolean isKptr(String ptr) { + Matcher m = Pattern.compile("[0-9a-fA-F]*").matcher(ptr); + if (!m.find() || m.start() != 0) { + // ptr string is malformed + return false; + } + int length = m.end(); + + if (length == 8) { + // 32-bit pointer + BigInteger address = new BigInteger(ptr.substring(0, length), RADIX_HEX); + // 32-bit kernel memory range: 0xC0000000 -> 0xffffffff + // 0x3fffffff bytes = 1GB / 0xffffffff = 4 GB + // 1 in 4 collision for hashed pointers + return address.compareTo(new BigInteger("C0000000", RADIX_HEX)) >= 0; + } else if (length == 16) { + // 64-bit pointer + BigInteger address = new BigInteger(ptr.substring(0, length), RADIX_HEX); + // 64-bit kernel memory range: 0x8000000000000000 -> 0xffffffffffffffff + // 48-bit implementation: 0xffff800000000000; 1 in 131,072 collision + // 56-bit implementation: 0xff80000000000000; 1 in 512 collision + // 64-bit implementation: 0x8000000000000000; 1 in 2 collision + return address.compareTo(new BigInteger("ff80000000000000", RADIX_HEX)) >= 0; + } + + return false; + } + + /** Check if a driver is present and readable. */ + protected boolean containsDriver(ITestDevice device, String driver) throws Exception { + return containsDriver(device, driver, true); + } + + /** Check if a driver is present on a machine. */ + protected boolean containsDriver(ITestDevice device, String driver, boolean checkReadable) + throws Exception { + boolean containsDriver = false; + if (driver.contains("*")) { + // -A list all files but . and .. + // -d directory, not contents + // -1 list one file per line + // -f unsorted + String ls = "ls -A -d -1 -f " + driver; + if (device.executeShellV2Command(ls).getExitCode().intValue() == 0) { + String[] expanded = device.executeShellCommand(ls).split("\\R"); + for (String expandedDriver : expanded) { + containsDriver |= containsDriver(device, expandedDriver, checkReadable); + } + } + } else { + if (checkReadable) { + containsDriver = + device.executeShellV2Command("test -r " + driver).getExitCode().intValue() + == 0; + } else { + containsDriver = + device.executeShellV2Command("test -e " + driver).getExitCode().intValue() + == 0; + } + } + + MetricsReportLog reportLog = buildMetricsReportLog(getDevice()); + reportLog.addValue("path", driver, ResultType.NEUTRAL, ResultUnit.NONE); + reportLog.addValue("exists", containsDriver, ResultType.NEUTRAL, ResultUnit.NONE); + reportLog.submit(); + + return containsDriver; + } + + public static MetricsReportLog buildMetricsReportLog(ITestDevice device) { + IBuildInfo buildInfo = getBuildInfo(device); + IAbi abi = getAbi(device); + String testName = getTestName(device); + + StackTraceElement[] stacktraces = Thread.currentThread().getStackTrace(); + int stackDepth = 2; // 0: getStackTrace(), 1: buildMetricsReportLog, 2: caller + String className = stacktraces[stackDepth].getClassName(); + String methodName = stacktraces[stackDepth].getMethodName(); + String classMethodName = String.format("%s#%s", className, methodName); + + // The stream name must be snake_case or else json formatting breaks + String streamName = methodName.replaceAll("(\\p{Upper})", "_$1").toLowerCase(); + + MetricsReportLog reportLog = + new MetricsReportLog( + buildInfo, + abi.getName(), + classMethodName, + "StsHostTestCases", + streamName, + true); + reportLog.addValue("test_name", testName, ResultType.NEUTRAL, ResultUnit.NONE); + return reportLog; + } + + private long getDeviceUptime() throws DeviceNotAvailableException { + String uptime = null; + int attempts = 5; + do { + if (attempts-- <= 0) { + throw new RuntimeException("could not get device uptime"); + } + getDevice().waitForDeviceAvailable(); + uptime = getDevice().executeShellCommand("cat /proc/uptime").trim(); + } while (uptime.isEmpty()); + return Long.parseLong(uptime.substring(0, uptime.indexOf('.'))); + } + + public void safeReboot() throws DeviceNotAvailableException { + getDevice().nonBlockingReboot(); + getDevice().waitForDeviceAvailable(); + updateKernelStartTime(); + } + + /** Allows a test to pass if called after a planned reboot. */ + public void updateKernelStartTime() throws DeviceNotAvailableException { + long uptime = getDeviceUptime(); + kernelStartTime = (System.currentTimeMillis() / 1000) - uptime; + } + + /** + * Queries the device for any test binaries which are still running. Those found will be dumped + * to stdout, then killed. + */ + private void logAndTerminateTestProcesses() { + ITestDevice device = getDevice(); + + Set<String> danglingPgids = new HashSet<String>(); + + try { + // Get all pid, command pairs. + String rawProcessList = device.executeShellCommand("ps -Ao pid,name,pgid"); + String[] processLines = rawProcessList.split("\n"); + + // Extract all PIDs and commands. Format of line is "PID COMMAND" + for (int i = 0; i < processLines.length; ++i) { + String[] tokens = processLines[i].trim().split("\\s+"); + if (3 != tokens.length) { + CLog.i( + "process entry doesn't tokenize as expected, skipping: " + + processLines[i]); + continue; + } + String pid = tokens[0]; + // Strip any brackets from the process name + String name = tokens[1].replaceAll("\\[|\\]", ""); + String pgid = tokens[2]; + // All STS poc binaries are stored in /data/local/tmp + if (name.startsWith("Bug") || name.startsWith("CVE")) { + danglingPgids.add(pgid); + CLog.w("Found dangling test process %s with PID %s, PGID %s", name, pid, pgid); + } + } + } catch (DeviceNotAvailableException e) { + CLog.logAndDisplay( + LogLevel.ERROR, + "DeviceNotAvailableException encountered while querying device for " + + "dangling test processes."); + return; + } + + try { + if (danglingPgids.size() > 0) { + CLog.logAndDisplay( + LogLevel.WARN, + "Found " + + danglingPgids.size() + + " dangling test process group(s). Terminating..."); + + for (String pgid : danglingPgids) { + if (Long.parseLong(pgid) <= 1) { + CLog.e("PGID %s allegedly a dangling STS group, ignoring.", pgid); + continue; + } + String killCommand = "kill -9 -" + pgid; + CLog.i(killCommand); + String killOutput = device.executeShellCommand(killCommand); + CLog.i(killOutput); + } + } + } catch (DeviceNotAvailableException e) { + CLog.logAndDisplay( + LogLevel.ERROR, + "DeviceNotAvailableException encountered while attempting to terminate " + + "dangling test processes."); + } + } + + /** + * Return true if a module is play managed. + * + * <p>Example of skipping a test based on mainline modules: + * + * <pre> + * @Test + * public void testPocCVE_1234_5678() throws Exception { + * // This will skip the test if MODULE_METADATA mainline module is play managed. + * assumeFalse(moduleIsPlayManaged("com.google.android.captiveportallogin")); + * // Do testing... + * } + * * </pre> + */ + public boolean moduleIsPlayManaged(String modulePackageName) throws Exception { + return mainlineModuleDetector.getPlayManagedModules().contains(modulePackageName); + } + + public void assumeIsSupportedNfcDevice(ITestDevice device) throws Exception { + String supportedDrivers[] = { + "/dev/nq-nci*", "/dev/pn54*", "/dev/pn551*", "/dev/pn553*", + "/dev/pn557*", "/dev/pn65*", "/dev/pn66*", "/dev/pn67*", + "/dev/pn80*", "/dev/pn81*", "/dev/sn100*", "/dev/sn220*", + "/dev/st54j*", "/dev/st21nfc*" + }; + boolean isDriverFound = false; + for (String supportedDriver : supportedDrivers) { + if (containsDriver(device, supportedDriver, false)) { + isDriverFound = true; + break; + } + } + String[] output = device.executeShellCommand("ls -la /dev | grep nfc").split("\\n"); + String nfcDevice = null; + for (String line : output) { + if (line.contains("nfc")) { + String text[] = line.split("\\s+"); + nfcDevice = text[text.length - 1]; + } + } + assumeTrue( + "NFC device " + nfcDevice + " is not supported. Hence skipping the test", + isDriverFound); + } +} |