blob: de90629d3409db54ee6b9f7ead3855ae1ec92424 [file] [log] [blame]
/*
* Copyright (C) 2014 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.security.cts;
import android.platform.test.annotations.RestrictedBuildTest;
import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
import com.android.compatibility.common.util.PropertyUtil;
import com.android.tradefed.build.IBuildInfo;
import com.android.tradefed.device.CollectingOutputReceiver;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.testtype.DeviceTestCase;
import com.android.tradefed.testtype.IBuildReceiver;
import com.android.tradefed.testtype.IDeviceTest;
import com.android.compatibility.common.util.CddTest;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileReader;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.String;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.Scanner;
import java.util.Set;
import java.util.stream.Collectors;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
/**
* Host-side SELinux tests.
*
* These tests analyze the policy file in use on the subject device directly or
* run as the shell user to evaluate aspects of the state of SELinux on the test
* device which otherwise would not be available to a normal apk.
*/
public class SELinuxHostTest extends DeviceTestCase implements IBuildReceiver, IDeviceTest {
private static final Map<ITestDevice, File> cachedDevicePolicyFiles = new HashMap<>(1);
private static final Map<ITestDevice, File> cachedDevicePlatFcFiles = new HashMap<>(1);
private static final Map<ITestDevice, File> cachedDeviceNonplatFcFiles = new HashMap<>(1);
private static final Map<ITestDevice, File> cachedDeviceVendorManifest = new HashMap<>(1);
private static final Map<ITestDevice, File> cachedDeviceSystemPolicy = new HashMap<>(1);
private File sepolicyAnalyze;
private File checkSeapp;
private File checkFc;
private File aospSeappFile;
private File aospFcFile;
private File aospPcFile;
private File aospSvcFile;
private File devicePolicyFile;
private File deviceSystemPolicyFile;
private File devicePlatSeappFile;
private File deviceNonplatSeappFile;
private File devicePlatFcFile;
private File deviceNonplatFcFile;
private File devicePcFile;
private File deviceSvcFile;
private File seappNeverAllowFile;
private File libsepolwrap;
private File libcpp;
private File sepolicyTests;
private IBuildInfo mBuild;
/**
* A reference to the device under test.
*/
private ITestDevice mDevice;
/**
* {@inheritDoc}
*/
@Override
public void setBuild(IBuildInfo build) {
mBuild = build;
}
/**
* {@inheritDoc}
*/
@Override
public void setDevice(ITestDevice device) {
super.setDevice(device);
mDevice = device;
}
public static File copyResourceToTempFile(String resName) throws IOException {
InputStream is = SELinuxHostTest.class.getResourceAsStream(resName);
File tempFile = File.createTempFile("SELinuxHostTest", ".tmp");
FileOutputStream os = new FileOutputStream(tempFile);
byte[] buf = new byte[1024];
int len;
while ((len = is.read(buf)) != -1) {
os.write(buf, 0, len);
}
os.flush();
os.close();
tempFile.deleteOnExit();
return tempFile;
}
private static void appendTo(String dest, String src) throws IOException {
try (FileInputStream is = new FileInputStream(new File(src));
FileOutputStream os = new FileOutputStream(new File(dest))) {
byte[] buf = new byte[1024];
int len;
while ((len = is.read(buf)) != -1) {
os.write(buf, 0, len);
}
}
}
@Override
protected void setUp() throws Exception {
super.setUp();
CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(mBuild);
sepolicyAnalyze = copyResourceToTempFile("/sepolicy-analyze");
sepolicyAnalyze.setExecutable(true);
devicePolicyFile = getDevicePolicyFile(mDevice);
if (isSepolicySplit(mDevice)) {
devicePlatFcFile = getDeviceFile(mDevice, cachedDevicePlatFcFiles,
"/system/etc/selinux/plat_file_contexts", "plat_file_contexts");
if (mDevice.doesFileExist("/vendor/etc/selinux/nonplat_file_contexts")){
// Old nonplat_* naming can be present if a framework-only OTA was done.
deviceNonplatFcFile = getDeviceFile(mDevice, cachedDeviceNonplatFcFiles,
"/vendor/etc/selinux/nonplat_file_contexts", "nonplat_file_contexts");
} else {
deviceNonplatFcFile = getDeviceFile(mDevice, cachedDeviceNonplatFcFiles,
"/vendor/etc/selinux/vendor_file_contexts", "vendor_file_contexts");
}
deviceSystemPolicyFile =
android.security.cts.SELinuxHostTest.getDeviceSystemPolicyFile(mDevice);
} else {
devicePlatFcFile = getDeviceFile(mDevice, cachedDevicePlatFcFiles,
"/plat_file_contexts", "plat_file_contexts");
deviceNonplatFcFile = getDeviceFile(mDevice, cachedDeviceNonplatFcFiles,
"/vendor_file_contexts", "vendor_file_contexts");
}
}
/*
* IMPLEMENTATION DETAILS: We cache some host-side policy files on per-device basis (in case
* CTS supports running against multiple devices at the same time). HashMap is used instead
* of WeakHashMap because in the grand scheme of things, keeping ITestDevice and
* corresponding File objects from being garbage-collected is not a big deal in CTS. If this
* becomes a big deal, this can be switched to WeakHashMap.
*/
private static File getDeviceFile(ITestDevice device,
Map<ITestDevice, File> cache, String deviceFilePath,
String tmpFileName) throws Exception {
if (!device.doesFileExist(deviceFilePath)){
throw new Exception();
}
File file;
synchronized (cache) {
file = cache.get(device);
}
if (file != null) {
return file;
}
file = File.createTempFile(tmpFileName, ".tmp");
file.deleteOnExit();
device.pullFile(deviceFilePath, file);
synchronized (cache) {
cache.put(device, file);
}
return file;
}
private static File buildSystemPolicy(ITestDevice device, Map<ITestDevice, File> cache,
String tmpFileName) throws Exception {
File builtPolicyFile;
synchronized (cache) {
builtPolicyFile = cache.get(device);
}
if (builtPolicyFile != null) {
return builtPolicyFile;
}
builtPolicyFile = File.createTempFile(tmpFileName, ".tmp");
builtPolicyFile.deleteOnExit();
File secilc = copyResourceToTempFile("/secilc");
secilc.setExecutable(true);
File systemSepolicyCilFile = File.createTempFile("plat_sepolicy", ".cil");
systemSepolicyCilFile.deleteOnExit();
assertTrue(device.pullFile("/system/etc/selinux/plat_sepolicy.cil", systemSepolicyCilFile));
ProcessBuilder pb = new ProcessBuilder(
secilc.getAbsolutePath(),
"-m", "-M", "true", "-c", "30",
"-o", builtPolicyFile.getAbsolutePath(),
systemSepolicyCilFile.getAbsolutePath());
pb.redirectOutput(ProcessBuilder.Redirect.PIPE);
pb.redirectErrorStream(true);
Process p = pb.start();
p.waitFor();
BufferedReader result = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line;
StringBuilder errorString = new StringBuilder();
while ((line = result.readLine()) != null) {
errorString.append(line);
errorString.append("\n");
}
assertTrue(errorString.toString(), errorString.length() == 0);
synchronized (cache) {
cache.put(device, builtPolicyFile);
}
return builtPolicyFile;
}
// NOTE: cts/tools/selinux depends on this method. Rename/change with caution.
/**
* Returns the host-side file containing the SELinux policy of the device under test.
*/
public static File getDevicePolicyFile(ITestDevice device) throws Exception {
return getDeviceFile(device, cachedDevicePolicyFiles, "/sys/fs/selinux/policy", "sepolicy");
}
// NOTE: cts/tools/selinux depends on this method. Rename/change with caution.
/**
* Returns the host-side file containing the system SELinux policy of the device under test.
*/
public static File getDeviceSystemPolicyFile(ITestDevice device) throws Exception {
return buildSystemPolicy(device, cachedDeviceSystemPolicy, "system_sepolicy");
}
// NOTE: cts/tools/selinux depends on this method. Rename/change with caution.
/**
* Returns the major number of sepolicy version of device's vendor implementation.
* TODO(b/37999212): Use VINTF object API instead of parsing vendor manifest.
*/
public static int getVendorSepolicyVersion(ITestDevice device) throws Exception {
String deviceManifestPath =
(device.doesFileExist("/vendor/etc/vintf/manifest.xml")) ?
"/vendor/etc/vintf/manifest.xml" :
"/vendor/manifest.xml";
File vendorManifestFile = getDeviceFile(device, cachedDeviceVendorManifest,
deviceManifestPath, "manifest.xml");
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.parse(vendorManifestFile);
Element root = doc.getDocumentElement();
Element sepolicy = (Element) root.getElementsByTagName("sepolicy").item(0);
Element version = (Element) sepolicy.getElementsByTagName("version").item(0);
String sepolicyVersion = version.getTextContent().split("\\.")[0];
return Integer.parseInt(sepolicyVersion);
}
/**
* Tests that the kernel is enforcing selinux policy globally.
*
* @throws Exception
*/
@CddTest(requirement="9.7")
public void testGlobalEnforcing() throws Exception {
CollectingOutputReceiver out = new CollectingOutputReceiver();
mDevice.executeShellCommand("cat /sys/fs/selinux/enforce", out);
assertEquals("SELinux policy is not being enforced!", "1", out.getOutput());
}
/**
* Tests that all domains in the running policy file are in enforcing mode
*
* @throws Exception
*/
@CddTest(requirement="9.7")
@RestrictedBuildTest
public void testAllDomainsEnforcing() throws Exception {
/* run sepolicy-analyze permissive check on policy file */
ProcessBuilder pb = new ProcessBuilder(sepolicyAnalyze.getAbsolutePath(),
devicePolicyFile.getAbsolutePath(), "permissive");
pb.redirectOutput(ProcessBuilder.Redirect.PIPE);
pb.redirectErrorStream(true);
Process p = pb.start();
p.waitFor();
BufferedReader result = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line;
StringBuilder errorString = new StringBuilder();
while ((line = result.readLine()) != null) {
errorString.append(line);
errorString.append("\n");
}
assertTrue("The following SELinux domains were found to be in permissive mode:\n"
+ errorString, errorString.length() == 0);
}
/**
* Asserts that specified type is not associated with the specified
* attribute.
*
* @param attribute
* The attribute name.
* @param type
* The type name.
*/
private void assertNotInAttribute(String attribute, String badtype) throws Exception {
Set<String> actualTypes = sepolicyAnalyzeGetTypesAssociatedWithAttribute(attribute);
if (actualTypes.contains(badtype)) {
fail("Attribute " + attribute + " includes " + badtype);
}
}
private static final byte[] readFully(InputStream in) throws IOException {
ByteArrayOutputStream result = new ByteArrayOutputStream();
byte[] buf = new byte[65536];
int chunkSize;
while ((chunkSize = in.read(buf)) != -1) {
result.write(buf, 0, chunkSize);
}
return result.toByteArray();
}
/**
* Runs sepolicy-analyze against the device's SELinux policy and returns the set of types
* associated with the provided attribute.
*/
private Set<String> sepolicyAnalyzeGetTypesAssociatedWithAttribute(
String attribute) throws Exception {
ProcessBuilder pb =
new ProcessBuilder(
sepolicyAnalyze.getAbsolutePath(),
devicePolicyFile.getAbsolutePath(),
"attribute",
attribute);
pb.redirectOutput(ProcessBuilder.Redirect.PIPE);
pb.redirectErrorStream(true);
Process p = pb.start();
int errorCode = p.waitFor();
if (errorCode != 0) {
fail("sepolicy-analyze attribute " + attribute + " failed with error code " + errorCode
+ ": " + new String(readFully(p.getInputStream())));
}
try (BufferedReader in =
new BufferedReader(new InputStreamReader(p.getInputStream()))) {
Set<String> types = new HashSet<>();
String type;
while ((type = in.readLine()) != null) {
types.add(type.trim());
}
return types;
}
}
// NOTE: cts/tools/selinux depends on this method. Rename/change with caution.
/**
* Returns {@code true} if this device is required to be a full Treble device.
*/
public static boolean isFullTrebleDevice(ITestDevice device)
throws DeviceNotAvailableException {
return PropertyUtil.getFirstApiLevel(device) > 26 &&
PropertyUtil.propertyEquals(device, "ro.treble.enabled", "true");
}
private boolean isFullTrebleDevice() throws DeviceNotAvailableException {
return isFullTrebleDevice(mDevice);
}
// NOTE: cts/tools/selinux depends on this method. Rename/change with caution.
/**
* Returns {@code true} if this device is required to enforce compatible property.
*/
public static boolean isCompatiblePropertyEnforcedDevice(ITestDevice device)
throws DeviceNotAvailableException {
return PropertyUtil.propertyEquals(
device, "ro.actionable_compatible_property.enabled", "true");
}
// NOTE: cts/tools/selinux depends on this method. Rename/change with caution.
/**
* Returns {@code true} if this device has sepolicy split across different paritions.
* This is possible even for devices launched at api level higher than 26.
*/
public static boolean isSepolicySplit(ITestDevice device)
throws DeviceNotAvailableException {
return device.doesFileExist("/system/etc/selinux/plat_file_contexts");
}
/**
* Asserts that no vendor domains are exempted from the prohibition on Binder use.
*
* <p>NOTE: binder_in_vendor_violators attribute is only there to help bring up Treble devices.
* It offers a convenient way to temporarily bypass the prohibition on Binder use in vendor
* domains. This attribute must not be used on production Treble devices.
*/
public void testNoExemptionsForBinderInVendorBan() throws Exception {
if (!isFullTrebleDevice()) {
return;
}
Set<String> types =
sepolicyAnalyzeGetTypesAssociatedWithAttribute("binder_in_vendor_violators");
if (!types.isEmpty()) {
List<String> sortedTypes = new ArrayList<>(types);
Collections.sort(sortedTypes);
fail("Policy exempts vendor domains from ban on Binder: " + sortedTypes);
}
}
/**
* Asserts that no HAL server domains are exempted from the prohibition of socket use with the
* only exceptions for the automotive device type.
*/
public void testNoExemptionsForSocketsUseWithinHalServer() throws Exception {
if (!isFullTrebleDevice()) {
return;
}
if (getDevice().hasFeature("feature:android.hardware.type.automotive")) {
return;
}
Set<String> types = sepolicyAnalyzeGetTypesAssociatedWithAttribute(
"hal_automotive_socket_exemption");
if (!types.isEmpty()) {
List<String> sortedTypes = new ArrayList<>(types);
Collections.sort(sortedTypes);
fail("Policy exempts domains from ban on socket usage from HAL servers: "
+ sortedTypes);
}
}
/**
* Asserts that no domains are exempted from the prohibition on initiating socket communications
* between core and vendor domains.
*
* <p>NOTE: socket_between_core_and_vendor_violators attribute is only there to help bring up
* Treble devices. It offers a convenient way to temporarily bypass the prohibition on
* initiating socket communications between core and vendor domains. This attribute must not be
* used on production Treble devices.
*/
public void testNoExemptionsForSocketsBetweenCoreAndVendorBan() throws Exception {
if (!isFullTrebleDevice()) {
return;
}
Set<String> types =
sepolicyAnalyzeGetTypesAssociatedWithAttribute(
"socket_between_core_and_vendor_violators");
if (!types.isEmpty()) {
List<String> sortedTypes = new ArrayList<>(types);
Collections.sort(sortedTypes);
fail("Policy exempts domains from ban on socket communications between core and"
+ " vendor: " + sortedTypes);
}
}
/**
* Asserts that no vendor domains are exempted from the prohibition on directly
* executing binaries from /system.
* */
public void testNoExemptionsForVendorExecutingCore() throws Exception {
if (!isFullTrebleDevice()) {
return;
}
Set<String> types =
sepolicyAnalyzeGetTypesAssociatedWithAttribute(
"vendor_executes_system_violators");
if (!types.isEmpty()) {
List<String> sortedTypes = new ArrayList<>(types);
Collections.sort(sortedTypes);
fail("Policy exempts vendor domains from ban on executing files in /system: "
+ sortedTypes);
}
}
/**
* Tests that mlstrustedsubject does not include untrusted_app
* and that mlstrustedobject does not include app_data_file.
* This helps prevent circumventing the per-user isolation of
* normal apps via levelFrom=user.
*
* @throws Exception
*/
@CddTest(requirement="9.7")
public void testMLSAttributes() throws Exception {
assertNotInAttribute("mlstrustedsubject", "untrusted_app");
assertNotInAttribute("mlstrustedobject", "app_data_file");
}
/**
* Tests that the seapp_contexts file on the device is valid.
*
* @throws Exception
*/
@CddTest(requirement="9.7")
public void testValidSeappContexts() throws Exception {
/* obtain seapp_contexts file from running device */
devicePlatSeappFile = File.createTempFile("plat_seapp_contexts", ".tmp");
devicePlatSeappFile.deleteOnExit();
deviceNonplatSeappFile = File.createTempFile("nonplat_seapp_contexts", ".tmp");
deviceNonplatSeappFile.deleteOnExit();
if (mDevice.pullFile("/system/etc/selinux/plat_seapp_contexts", devicePlatSeappFile)) {
mDevice.pullFile("/vendor/etc/selinux/nonplat_seapp_contexts", deviceNonplatSeappFile);
}else {
mDevice.pullFile("/plat_seapp_contexts", devicePlatSeappFile);
mDevice.pullFile("/nonplat_seapp_contexts", deviceNonplatSeappFile);
}
/* retrieve the checkseapp executable from jar */
checkSeapp = copyResourceToTempFile("/checkseapp");
checkSeapp.setExecutable(true);
/* retrieve the AOSP seapp_neverallows file from jar */
seappNeverAllowFile = copyResourceToTempFile("/plat_seapp_neverallows");
/* run checkseapp on seapp_contexts */
ProcessBuilder pb = new ProcessBuilder(checkSeapp.getAbsolutePath(),
"-p", devicePolicyFile.getAbsolutePath(),
seappNeverAllowFile.getAbsolutePath(),
devicePlatSeappFile.getAbsolutePath(),
deviceNonplatSeappFile.getAbsolutePath());
pb.redirectOutput(ProcessBuilder.Redirect.PIPE);
pb.redirectErrorStream(true);
Process p = pb.start();
p.waitFor();
BufferedReader result = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line;
StringBuilder errorString = new StringBuilder();
while ((line = result.readLine()) != null) {
errorString.append(line);
errorString.append("\n");
}
assertTrue("The seapp_contexts file was invalid:\n"
+ errorString, errorString.length() == 0);
}
/**
* Asserts that the actual file contents starts with the expected file
* contents.
*
* @param expectedFile
* The file with the expected contents.
* @param actualFile
* The actual file being checked.
*/
private void assertFileStartsWith(File expectedFile, File actualFile) throws Exception {
BufferedReader expectedReader = new BufferedReader(new FileReader(expectedFile.getAbsolutePath()));
BufferedReader actualReader = new BufferedReader(new FileReader(actualFile.getAbsolutePath()));
String expectedLine, actualLine;
while ((expectedLine = expectedReader.readLine()) != null) {
actualLine = actualReader.readLine();
assertEquals("Lines do not match:", expectedLine, actualLine);
}
}
/**
* Tests that the seapp_contexts file on the device contains
* the standard AOSP entries.
*
* @throws Exception
*/
@CddTest(requirement="9.7")
public void testAospSeappContexts() throws Exception {
/* obtain seapp_contexts file from running device */
devicePlatSeappFile = File.createTempFile("seapp_contexts", ".tmp");
devicePlatSeappFile.deleteOnExit();
if (!mDevice.pullFile("/system/etc/selinux/plat_seapp_contexts", devicePlatSeappFile)) {
mDevice.pullFile("/plat_seapp_contexts", devicePlatSeappFile);
}
/* retrieve the AOSP seapp_contexts file from jar */
aospSeappFile = copyResourceToTempFile("/plat_seapp_contexts");
assertFileStartsWith(aospSeappFile, devicePlatSeappFile);
}
/**
* Tests that the plat_file_contexts file on the device contains
* the standard AOSP entries.
*
* @throws Exception
*/
@CddTest(requirement="9.7")
public void testAospFileContexts() throws Exception {
/* retrieve the checkfc executable from jar */
checkFc = copyResourceToTempFile("/checkfc");
checkFc.setExecutable(true);
/* retrieve the AOSP file_contexts file from jar */
aospFcFile = copyResourceToTempFile("/plat_file_contexts");
/* run checkfc -c plat_file_contexts plat_file_contexts */
ProcessBuilder pb = new ProcessBuilder(checkFc.getAbsolutePath(),
"-c", aospFcFile.getAbsolutePath(),
devicePlatFcFile.getAbsolutePath());
pb.redirectOutput(ProcessBuilder.Redirect.PIPE);
pb.redirectErrorStream(true);
Process p = pb.start();
p.waitFor();
BufferedReader result = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line = result.readLine();
assertTrue("The file_contexts file did not include the AOSP entries:\n"
+ line + "\n",
line.equals("equal") || line.equals("subset"));
}
/**
* Tests that the property_contexts file on the device contains
* the standard AOSP entries.
*
* @throws Exception
*/
@CddTest(requirement="9.7")
public void testAospPropertyContexts() throws Exception {
/* obtain property_contexts file from running device */
devicePcFile = File.createTempFile("plat_property_contexts", ".tmp");
devicePcFile.deleteOnExit();
// plat_property_contexts may be either in /system/etc/sepolicy or in /
if (!mDevice.pullFile("/system/etc/selinux/plat_property_contexts", devicePcFile)) {
mDevice.pullFile("/plat_property_contexts", devicePcFile);
}
// Retrieve the AOSP property_contexts file from JAR.
// The location of this file in the JAR has nothing to do with the location of this file on
// Android devices. See build script of this CTS module.
aospPcFile = copyResourceToTempFile("/plat_property_contexts");
assertFileStartsWith(aospPcFile, devicePcFile);
}
/**
* Tests that the service_contexts file on the device contains
* the standard AOSP entries.
*
* @throws Exception
*/
@CddTest(requirement="9.7")
public void testAospServiceContexts() throws Exception {
/* obtain service_contexts file from running device */
deviceSvcFile = File.createTempFile("service_contexts", ".tmp");
deviceSvcFile.deleteOnExit();
if (!mDevice.pullFile("/system/etc/selinux/plat_service_contexts", deviceSvcFile)) {
mDevice.pullFile("/plat_service_contexts", deviceSvcFile);
}
/* retrieve the AOSP service_contexts file from jar */
aospSvcFile = copyResourceToTempFile("/plat_service_contexts");
assertFileStartsWith(aospSvcFile, deviceSvcFile);
}
/**
* Tests that the file_contexts file(s) on the device is valid.
*
* @throws Exception
*/
@CddTest(requirement="9.7")
public void testValidFileContexts() throws Exception {
/* retrieve the checkfc executable from jar */
checkFc = copyResourceToTempFile("/checkfc");
checkFc.setExecutable(true);
/* combine plat and nonplat policies for testing */
File combinedFcFile = File.createTempFile("combined_file_context", ".tmp");
combinedFcFile.deleteOnExit();
appendTo(combinedFcFile.getAbsolutePath(), devicePlatFcFile.getAbsolutePath());
appendTo(combinedFcFile.getAbsolutePath(), deviceNonplatFcFile.getAbsolutePath());
/* run checkfc sepolicy file_contexts */
ProcessBuilder pb = new ProcessBuilder(checkFc.getAbsolutePath(),
devicePolicyFile.getAbsolutePath(),
combinedFcFile.getAbsolutePath());
pb.redirectOutput(ProcessBuilder.Redirect.PIPE);
pb.redirectErrorStream(true);
Process p = pb.start();
p.waitFor();
BufferedReader result = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line;
StringBuilder errorString = new StringBuilder();
while ((line = result.readLine()) != null) {
errorString.append(line);
errorString.append("\n");
}
assertTrue("file_contexts was invalid:\n"
+ errorString, errorString.length() == 0);
}
/**
* Tests that the property_contexts file on the device is valid.
*
* @throws Exception
*/
@CddTest(requirement="9.7")
public void testValidPropertyContexts() throws Exception {
/* retrieve the checkfc executable from jar */
File propertyInfoChecker = copyResourceToTempFile("/property_info_checker");
propertyInfoChecker.setExecutable(true);
/* obtain property_contexts file from running device */
devicePcFile = File.createTempFile("property_contexts", ".tmp");
devicePcFile.deleteOnExit();
// plat_property_contexts may be either in /system/etc/sepolicy or in /
if (!mDevice.pullFile("/system/etc/selinux/plat_property_contexts", devicePcFile)) {
mDevice.pullFile("/plat_property_contexts", devicePcFile);
}
/* run property_info_checker on property_contexts */
ProcessBuilder pb = new ProcessBuilder(propertyInfoChecker.getAbsolutePath(),
devicePolicyFile.getAbsolutePath(),
devicePcFile.getAbsolutePath());
pb.redirectOutput(ProcessBuilder.Redirect.PIPE);
pb.redirectErrorStream(true);
Process p = pb.start();
p.waitFor();
BufferedReader result = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line;
StringBuilder errorString = new StringBuilder();
while ((line = result.readLine()) != null) {
errorString.append(line);
errorString.append("\n");
}
assertTrue("The property_contexts file was invalid:\n"
+ errorString, errorString.length() == 0);
}
/**
* Tests that the service_contexts file on the device is valid.
*
* @throws Exception
*/
@CddTest(requirement="9.7")
public void testValidServiceContexts() throws Exception {
/* retrieve the checkfc executable from jar */
checkFc = copyResourceToTempFile("/checkfc");
checkFc.setExecutable(true);
/* obtain service_contexts file from running device */
deviceSvcFile = File.createTempFile("service_contexts", ".tmp");
deviceSvcFile.deleteOnExit();
mDevice.pullFile("/service_contexts", deviceSvcFile);
/* run checkfc -s on service_contexts */
ProcessBuilder pb = new ProcessBuilder(checkFc.getAbsolutePath(),
"-s", devicePolicyFile.getAbsolutePath(),
deviceSvcFile.getAbsolutePath());
pb.redirectOutput(ProcessBuilder.Redirect.PIPE);
pb.redirectErrorStream(true);
Process p = pb.start();
p.waitFor();
BufferedReader result = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line;
StringBuilder errorString = new StringBuilder();
while ((line = result.readLine()) != null) {
errorString.append(line);
errorString.append("\n");
}
assertTrue("The service_contexts file was invalid:\n"
+ errorString, errorString.length() == 0);
}
public static boolean isMac() {
String os = System.getProperty("os.name").toLowerCase();
return (os.startsWith("mac") || os.startsWith("darwin"));
}
private void setupLibraries() throws Exception {
// The host side binary tests are host OS specific. Use Linux
// libraries on Linux and Mac libraries on Mac.
if (isMac()) {
libsepolwrap = copyResourceToTempFile("/libsepolwrap.dylib");
libcpp = copyResourceToTempFile("/libc++.dylib");
libcpp.renameTo(new File(System.getProperty("java.io.tmpdir") + "/libc++.dylib"));
} else {
libsepolwrap = copyResourceToTempFile("/libsepolwrap.so");
libcpp = copyResourceToTempFile("/libc++.so");
libcpp.renameTo(new File(System.getProperty("java.io.tmpdir") + "/libc++.so"));
}
libsepolwrap.deleteOnExit();
libcpp.deleteOnExit();
}
private void assertSepolicyTests(String test, String testExecutable,
boolean includeVendorSepolicy) throws Exception {
setupLibraries();
sepolicyTests = copyResourceToTempFile(testExecutable);
sepolicyTests.setExecutable(true);
List<String> args = new ArrayList<String>();
args.add(sepolicyTests.getAbsolutePath());
args.add("-l");
args.add(libsepolwrap.getAbsolutePath());
args.add("-f");
args.add(devicePlatFcFile.getAbsolutePath());
args.add("--test");
args.add(test);
if (includeVendorSepolicy) {
args.add("-f");
args.add(deviceNonplatFcFile.getAbsolutePath());
args.add("-p");
args.add(devicePolicyFile.getAbsolutePath());
} else {
args.add("-p");
args.add(deviceSystemPolicyFile.getAbsolutePath());
}
ProcessBuilder pb = new ProcessBuilder(args);
Map<String, String> env = pb.environment();
if (isMac()) {
env.put("DYLD_LIBRARY_PATH", System.getProperty("java.io.tmpdir"));
} else {
env.put("LD_LIBRARY_PATH", System.getProperty("java.io.tmpdir"));
}
pb.redirectOutput(ProcessBuilder.Redirect.PIPE);
pb.redirectErrorStream(true);
Process p = pb.start();
p.waitFor();
BufferedReader result = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line;
StringBuilder errorString = new StringBuilder();
while ((line = result.readLine()) != null) {
errorString.append(line);
errorString.append("\n");
}
assertTrue(errorString.toString(), errorString.length() == 0);
}
/**
* Tests that all types on /data have the data_file_type attribute.
*
* @throws Exception
*/
public void testDataTypeViolators() throws Exception {
assertSepolicyTests("TestDataTypeViolations", "/sepolicy_tests",
PropertyUtil.isVendorApiLevelNewerThan(mDevice, 27) /* includeVendorSepolicy */);
}
/**
* Tests that all types in /proc have the proc_type attribute.
*
* @throws Exception
*/
public void testProcTypeViolators() throws Exception {
assertSepolicyTests("TestProcTypeViolations", "/sepolicy_tests",
PropertyUtil.isVendorApiLevelNewerThan(mDevice, 27) /* includeVendorSepolicy */);
}
/**
* Tests that all types in /sys have the sysfs_type attribute.
*
* @throws Exception
*/
public void testSysfsTypeViolators() throws Exception {
assertSepolicyTests("TestSysfsTypeViolations", "/sepolicy_tests",
PropertyUtil.isVendorApiLevelNewerThan(mDevice, 27) /* includeVendorSepolicy */);
}
/**
* Tests that all types on /vendor have the vendor_file_type attribute.
*
* @throws Exception
*/
public void testVendorTypeViolators() throws Exception {
assertSepolicyTests("TestVendorTypeViolations", "/sepolicy_tests",
PropertyUtil.isVendorApiLevelNewerThan(mDevice, 27) /* includeVendorSepolicy */);
}
/**
* Tests that all domains with entrypoints on /system have the coredomain
* attribute, and that all domains with entrypoints on /vendor do not have the
* coredomain attribute.
*
* @throws Exception
*/
public void testCoredomainViolators() throws Exception {
assertSepolicyTests("CoredomainViolations", "/treble_sepolicy_tests",
PropertyUtil.isVendorApiLevelNewerThan(mDevice, 27) /* includeVendorSepolicy */);
}
/**
* Tests that the policy defines no booleans (runtime conditional policy).
*
* @throws Exception
*/
@CddTest(requirement="9.7")
public void testNoBooleans() throws Exception {
/* run sepolicy-analyze booleans check on policy file */
ProcessBuilder pb = new ProcessBuilder(sepolicyAnalyze.getAbsolutePath(),
devicePolicyFile.getAbsolutePath(), "booleans");
pb.redirectOutput(ProcessBuilder.Redirect.PIPE);
pb.redirectErrorStream(true);
Process p = pb.start();
p.waitFor();
BufferedReader result = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line;
StringBuilder errorString = new StringBuilder();
while ((line = result.readLine()) != null) {
errorString.append(line);
errorString.append("\n");
}
assertTrue("The policy contained booleans:\n"
+ errorString, errorString.length() == 0);
}
/**
* Tests that taking a bugreport does not produce any dumpstate-related
* SELinux denials.
*
* @throws Exception
*/
public void testNoBugreportDenials() throws Exception {
// Take a bugreport and get its logcat output.
mDevice.executeAdbCommand("logcat", "-c");
mDevice.getBugreport();
String log = mDevice.executeAdbCommand("logcat", "-d");
// Find all the dumpstate-related types and make a regex that will match them.
Set<String> types = sepolicyAnalyzeGetTypesAssociatedWithAttribute("hal_dumpstate_server");
types.add("dumpstate");
String typeRegex = types.stream().collect(Collectors.joining("|"));
Pattern p = Pattern.compile("avc: *denied.*scontext=u:(?:r|object_r):(?:" + typeRegex + "):s0.*");
// Fail if logcat contains such a denial.
Matcher m = p.matcher(log);
StringBuilder errorString = new StringBuilder();
while (m.find()) {
errorString.append(m.group());
errorString.append("\n");
}
assertTrue("Found illegal SELinux denial(s): " + errorString, errorString.length() == 0);
}
/**
* Tests that important domain labels are being appropriately applied.
*/
/**
* Asserts that no processes are running in a domain.
*
* @param domain
* The domain or SELinux context to check.
*/
private void assertDomainEmpty(String domain) throws DeviceNotAvailableException {
List<ProcessDetails> procs = ProcessDetails.getProcMap(mDevice).get(domain);
String msg = "Expected no processes in SELinux domain \"" + domain + "\""
+ " Found: \"" + procs + "\"";
assertNull(msg, procs);
}
/**
* Asserts that a domain exists and that only one, well defined, process is
* running in that domain.
*
* @param domain
* The domain or SELinux context to check.
* @param executable
* The path of the executable or application package name.
*/
private void assertDomainOne(String domain, String executable) throws DeviceNotAvailableException {
List<ProcessDetails> procs = ProcessDetails.getProcMap(mDevice).get(domain);
List<ProcessDetails> exeProcs = ProcessDetails.getExeMap(mDevice).get(executable);
String msg = "Expected 1 process in SELinux domain \"" + domain + "\""
+ " Found \"" + procs + "\"";
assertNotNull(msg, procs);
assertEquals(msg, 1, procs.size());
msg = "Expected executable \"" + executable + "\" in SELinux domain \"" + domain + "\""
+ "Found: \"" + procs + "\"";
assertEquals(msg, executable, procs.get(0).procTitle);
msg = "Expected 1 process with executable \"" + executable + "\""
+ " Found \"" + procs + "\"";
assertNotNull(msg, exeProcs);
assertEquals(msg, 1, exeProcs.size());
msg = "Expected executable \"" + executable + "\" in SELinux domain \"" + domain + "\""
+ "Found: \"" + procs + "\"";
assertEquals(msg, domain, exeProcs.get(0).label);
}
/**
* Asserts that a domain may exist. If a domain exists, the cardinality of
* the domain is verified to be 1 and that the correct process is running in
* that domain. If the process is running, it is running in that domain.
*
* @param domain
* The domain or SELinux context to check.
* @param executable
* The path of the executable or application package name.
*/
private void assertDomainZeroOrOne(String domain, String executable)
throws DeviceNotAvailableException {
List<ProcessDetails> procs = ProcessDetails.getProcMap(mDevice).get(domain);
List<ProcessDetails> exeProcs = ProcessDetails.getExeMap(mDevice).get(executable);
if (procs != null) {
String msg = "Expected 1 process in SELinux domain \"" + domain + "\""
+ " Found: \"" + procs + "\"";
assertEquals(msg, 1, procs.size());
msg = "Expected executable \"" + executable + "\" in SELinux domain \"" + domain + "\""
+ "Found: \"" + procs.get(0) + "\"";
assertEquals(msg, executable, procs.get(0).procTitle);
}
if (exeProcs != null) {
String msg = "Expected executable \"" + executable + "\" in SELinux domain \"" + domain + "\""
+ " Instead found it running in the domain \"" + exeProcs.get(0).label + "\"";
assertNotNull(msg, procs);
msg = "Expected 1 process with executable \"" + executable + "\""
+ " Found: \"" + procs + "\"";
assertEquals(msg, 1, exeProcs.size());
msg = "Expected executable \"" + executable + "\" in SELinux domain \"" + domain + "\""
+ "Found: \"" + procs.get(0) + "\"";
assertEquals(msg, domain, exeProcs.get(0).label);
}
}
/**
* Asserts that a domain must exist, and that the cardinality is greater
* than or equal to 1.
*
* @param domain
* The domain or SELinux context to check.
* @param executables
* The path of the allowed executables or application package names.
*/
private void assertDomainN(String domain, String... executables)
throws DeviceNotAvailableException {
List<ProcessDetails> procs = ProcessDetails.getProcMap(mDevice).get(domain);
String msg = "Expected 1 or more processes in SELinux domain but found none.";
assertNotNull(msg, procs);
Set<String> execList = new HashSet<String>(Arrays.asList(executables));
for (ProcessDetails p : procs) {
msg = "Expected one of \"" + execList + "\" in SELinux domain \"" + domain + "\""
+ " Found: \"" + p + "\"";
assertTrue(msg, execList.contains(p.procTitle));
}
for (String exe : executables) {
List<ProcessDetails> exeProcs = ProcessDetails.getExeMap(mDevice).get(exe);
if (exeProcs != null) {
for (ProcessDetails p : exeProcs) {
msg = "Expected executable \"" + exe + "\" in SELinux domain \""
+ domain + "\"" + " Found: \"" + p + "\"";
assertEquals(msg, domain, p.label);
}
}
}
}
/**
* Asserts that a domain, if it exists, is only running the listed executables.
*
* @param domain
* The domain or SELinux context to check.
* @param executables
* The path of the allowed executables or application package names.
*/
private void assertDomainHasExecutable(String domain, String... executables)
throws DeviceNotAvailableException {
List<ProcessDetails> procs = ProcessDetails.getProcMap(mDevice).get(domain);
if (procs != null) {
Set<String> execList = new HashSet<String>(Arrays.asList(executables));
for (ProcessDetails p : procs) {
String msg = "Expected one of \"" + execList + "\" in SELinux domain \""
+ domain + "\"" + " Found: \"" + p + "\"";
assertTrue(msg, execList.contains(p.procTitle));
}
}
}
/**
* Asserts that an executable exists and is only running in the listed domains.
*
* @param executable
* The path of the executable to check.
* @param domains
* The list of allowed domains.
*/
private void assertExecutableExistsAndHasDomain(String executable, String... domains)
throws DeviceNotAvailableException {
List<ProcessDetails> exeProcs = ProcessDetails.getExeMap(mDevice).get(executable);
Set<String> domainList = new HashSet<String>(Arrays.asList(domains));
String msg = "Expected 1 or more processes for executable \"" + executable + "\".";
assertNotNull(msg, exeProcs);
for (ProcessDetails p : exeProcs) {
msg = "Expected one of \"" + domainList + "\" for executable \"" + executable
+ "\"" + " Found: \"" + p.label + "\"";
assertTrue(msg, domainList.contains(p.label));
}
}
/**
* Asserts that an executable, if it exists, is only running in the listed domains.
*
* @param executable
* The path of the executable to check.
* @param domains
* The list of allowed domains.
*/
private void assertExecutableHasDomain(String executable, String... domains)
throws DeviceNotAvailableException {
List<ProcessDetails> exeProcs = ProcessDetails.getExeMap(mDevice).get(executable);
Set<String> domainList = new HashSet<String>(Arrays.asList(domains));
if (exeProcs != null) {
for (ProcessDetails p : exeProcs) {
String msg = "Expected one of \"" + domainList + "\" for executable \"" + executable
+ "\"" + " Found: \"" + p.label + "\"";
assertTrue(msg, domainList.contains(p.label));
}
}
}
/* Init is always there */
@CddTest(requirement="9.7")
public void testInitDomain() throws DeviceNotAvailableException {
assertDomainHasExecutable("u:r:init:s0", "/system/bin/init");
assertDomainHasExecutable("u:r:vendor_init:s0", "/system/bin/init");
assertExecutableExistsAndHasDomain("/system/bin/init", "u:r:init:s0", "u:r:vendor_init:s0");
}
/* Ueventd is always there */
@CddTest(requirement="9.7")
public void testUeventdDomain() throws DeviceNotAvailableException {
assertDomainOne("u:r:ueventd:s0", "/system/bin/ueventd");
}
/* healthd may or may not exist */
@CddTest(requirement="9.7")
public void testHealthdDomain() throws DeviceNotAvailableException {
assertDomainZeroOrOne("u:r:healthd:s0", "/system/bin/healthd");
}
/* Servicemanager is always there */
@CddTest(requirement="9.7")
public void testServicemanagerDomain() throws DeviceNotAvailableException {
assertDomainOne("u:r:servicemanager:s0", "/system/bin/servicemanager");
}
/* Vold is always there */
@CddTest(requirement="9.7")
public void testVoldDomain() throws DeviceNotAvailableException {
assertDomainOne("u:r:vold:s0", "/system/bin/vold");
}
/* netd is always there */
@CddTest(requirement="9.7")
public void testNetdDomain() throws DeviceNotAvailableException {
assertDomainN("u:r:netd:s0", "/system/bin/netd", "/system/bin/iptables-restore", "/system/bin/ip6tables-restore");
}
/* Surface flinger is always there */
@CddTest(requirement="9.7")
public void testSurfaceflingerDomain() throws DeviceNotAvailableException {
assertDomainOne("u:r:surfaceflinger:s0", "/system/bin/surfaceflinger");
}
/* Zygote is always running */
@CddTest(requirement="9.7")
public void testZygoteDomain() throws DeviceNotAvailableException {
assertDomainN("u:r:zygote:s0", "zygote", "zygote64", "usap32", "usap64");
}
/* Checks drmserver for devices that require it */
@CddTest(requirement="9.7")
public void testDrmServerDomain() throws DeviceNotAvailableException {
assertDomainZeroOrOne("u:r:drmserver:s0", "/system/bin/drmserver");
}
/* Installd is always running */
@CddTest(requirement="9.7")
public void testInstalldDomain() throws DeviceNotAvailableException {
assertDomainOne("u:r:installd:s0", "/system/bin/installd");
}
/* keystore is always running */
@CddTest(requirement="9.7")
public void testKeystoreDomain() throws DeviceNotAvailableException {
assertDomainOne("u:r:keystore:s0", "/system/bin/keystore");
}
/* System server better be running :-P */
@CddTest(requirement="9.7")
public void testSystemServerDomain() throws DeviceNotAvailableException {
assertDomainOne("u:r:system_server:s0", "system_server");
}
/* Watchdogd may or may not be there */
@CddTest(requirement="9.7")
public void testWatchdogdDomain() throws DeviceNotAvailableException {
assertDomainZeroOrOne("u:r:watchdogd:s0", "/system/bin/watchdogd");
}
/* logd may or may not be there */
@CddTest(requirement="9.7")
public void testLogdDomain() throws DeviceNotAvailableException {
assertDomainZeroOrOne("u:r:logd:s0", "/system/bin/logd");
}
/* lmkd may or may not be there */
@CddTest(requirement="9.7")
public void testLmkdDomain() throws DeviceNotAvailableException {
assertDomainZeroOrOne("u:r:lmkd:s0", "/system/bin/lmkd");
}
/* Wifi may be off so cardinality of 0 or 1 is ok */
@CddTest(requirement="9.7")
public void testWpaDomain() throws DeviceNotAvailableException {
assertDomainZeroOrOne("u:r:wpa:s0", "/system/bin/wpa_supplicant");
}
/* permissioncontroller, if running, always runs in permissioncontroller_app */
@CddTest(requirement="9.7")
public void testPermissionControllerDomain() throws DeviceNotAvailableException {
assertExecutableHasDomain("com.google.android.permissioncontroller", "u:r:permissioncontroller_app:s0");
assertExecutableHasDomain("com.android.permissioncontroller", "u:r:permissioncontroller_app:s0");
}
/* vzwomatrigger may or may not be running */
@CddTest(requirement="9.7")
public void testVzwOmaTriggerDomain() throws DeviceNotAvailableException {
assertDomainZeroOrOne("u:r:vzwomatrigger_app:s0", "com.android.vzwomatrigger");
}
/* gmscore, if running, always runs in gmscore_app */
@CddTest(requirement="9.7")
public void testGMSCoreDomain() throws DeviceNotAvailableException {
assertExecutableHasDomain("com.google.android.gms", "u:r:gmscore_app:s0");
assertExecutableHasDomain("com.google.android.gms.ui", "u:r:gmscore_app:s0");
assertExecutableHasDomain("com.google.android.gms.persistent", "u:r:gmscore_app:s0");
assertExecutableHasDomain("com.google.android.gms:snet", "u:r:gmscore_app:s0");
}
/*
* Nothing should be running in this domain, cardinality test is all thats
* needed
*/
@CddTest(requirement="9.7")
public void testInitShellDomain() throws DeviceNotAvailableException {
assertDomainEmpty("u:r:init_shell:s0");
}
/*
* Nothing should be running in this domain, cardinality test is all thats
* needed
*/
@CddTest(requirement="9.7")
public void testRecoveryDomain() throws DeviceNotAvailableException {
assertDomainEmpty("u:r:recovery:s0");
}
/*
* Nothing should be running in this domain, cardinality test is all thats
* needed
*/
@CddTest(requirement="9.7")
@RestrictedBuildTest
public void testSuDomain() throws DeviceNotAvailableException {
assertDomainEmpty("u:r:su:s0");
}
/*
* All kthreads should be in kernel context.
*/
@CddTest(requirement="9.7")
public void testKernelDomain() throws DeviceNotAvailableException {
String domain = "u:r:kernel:s0";
List<ProcessDetails> procs = ProcessDetails.getProcMap(mDevice).get(domain);
if (procs != null) {
for (ProcessDetails p : procs) {
assertTrue("Non Kernel thread \"" + p + "\" found!", p.isKernel());
}
}
}
private static class ProcessDetails {
public String label;
public String user;
public int pid;
public int ppid;
public String procTitle;
private static HashMap<String, ArrayList<ProcessDetails>> procMap;
private static HashMap<String, ArrayList<ProcessDetails>> exeMap;
private static int kernelParentThreadpid = -1;
ProcessDetails(String label, String user, int pid, int ppid, String procTitle) {
this.label = label;
this.user = user;
this.pid = pid;
this.ppid = ppid;
this.procTitle = procTitle;
}
@Override
public String toString() {
return "label: " + label
+ " user: " + user
+ " pid: " + pid
+ " ppid: " + ppid
+ " cmd: " + procTitle;
}
private static void createProcMap(ITestDevice tDevice) throws DeviceNotAvailableException {
/* take the output of a ps -Z to do our analysis */
CollectingOutputReceiver psOut = new CollectingOutputReceiver();
// TODO: remove "toybox" below and just run "ps"
tDevice.executeShellCommand("toybox ps -A -o label,user,pid,ppid,cmdline", psOut);
String psOutString = psOut.getOutput();
Pattern p = Pattern.compile(
"^([\\w_:,]+)\\s+([\\w_]+)\\s+(\\d+)\\s+(\\d+)\\s+(\\p{Graph}+)(\\s\\p{Graph}+)*\\s*$"
);
procMap = new HashMap<String, ArrayList<ProcessDetails>>();
exeMap = new HashMap<String, ArrayList<ProcessDetails>>();
for(String line : psOutString.split("\n")) {
Matcher m = p.matcher(line);
if(m.matches()) {
String domainLabel = m.group(1);
// clean up the domainlabel
String[] parts = domainLabel.split(":");
if (parts.length > 4) {
// we have an extra categories bit at the end consisting of cxxx,cxxx ...
// just make the domain out of the first 4 parts
domainLabel = String.join(":", parts[0], parts[1], parts[2], parts[3]);
}
String user = m.group(2);
int pid = Integer.parseInt(m.group(3));
int ppid = Integer.parseInt(m.group(4));
String procTitle = m.group(5);
ProcessDetails proc = new ProcessDetails(domainLabel, user, pid, ppid, procTitle);
if (procMap.get(domainLabel) == null) {
procMap.put(domainLabel, new ArrayList<ProcessDetails>());
}
procMap.get(domainLabel).add(proc);
if (procTitle.equals("[kthreadd]") && ppid == 0) {
kernelParentThreadpid = pid;
}
if (exeMap.get(procTitle) == null) {
exeMap.put(procTitle, new ArrayList<ProcessDetails>());
}
exeMap.get(procTitle).add(proc);
}
}
}
public static HashMap<String, ArrayList<ProcessDetails>> getProcMap(ITestDevice tDevice)
throws DeviceNotAvailableException{
if (procMap == null) {
createProcMap(tDevice);
}
return procMap;
}
public static HashMap<String, ArrayList<ProcessDetails>> getExeMap(ITestDevice tDevice)
throws DeviceNotAvailableException{
if (exeMap == null) {
createProcMap(tDevice);
}
return exeMap;
}
public boolean isKernel() {
return (pid == kernelParentThreadpid || ppid == kernelParentThreadpid);
}
}
}