Home | History | Annotate | Download | only in cts
      1 /*
      2  * Copyright (C) 2014 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.security.cts;
     18 
     19 import android.platform.test.annotations.RestrictedBuildTest;
     20 
     21 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
     22 import com.android.compatibility.common.util.PropertyUtil;
     23 import com.android.tradefed.build.IBuildInfo;
     24 import com.android.tradefed.device.CollectingOutputReceiver;
     25 import com.android.tradefed.device.DeviceNotAvailableException;
     26 import com.android.tradefed.device.ITestDevice;
     27 import com.android.tradefed.testtype.DeviceTestCase;
     28 import com.android.tradefed.testtype.IBuildReceiver;
     29 import com.android.tradefed.testtype.IDeviceTest;
     30 
     31 import com.android.compatibility.common.util.CddTest;
     32 
     33 import java.io.BufferedReader;
     34 import java.io.ByteArrayOutputStream;
     35 import java.io.File;
     36 import java.io.FileReader;
     37 import java.io.FileInputStream;
     38 import java.io.FileOutputStream;
     39 import java.io.IOException;
     40 import java.io.InputStream;
     41 import java.io.InputStreamReader;
     42 import java.lang.String;
     43 import java.util.ArrayList;
     44 import java.util.Arrays;
     45 import java.util.Collections;
     46 import java.util.HashMap;
     47 import java.util.HashSet;
     48 import java.util.List;
     49 import java.util.Map;
     50 import java.util.regex.Matcher;
     51 import java.util.regex.Pattern;
     52 import java.util.Scanner;
     53 import java.util.Set;
     54 
     55 import javax.xml.parsers.DocumentBuilder;
     56 import javax.xml.parsers.DocumentBuilderFactory;
     57 import org.w3c.dom.Document;
     58 import org.w3c.dom.Element;
     59 
     60 /**
     61  * Host-side SELinux tests.
     62  *
     63  * These tests analyze the policy file in use on the subject device directly or
     64  * run as the shell user to evaluate aspects of the state of SELinux on the test
     65  * device which otherwise would not be available to a normal apk.
     66  */
     67 public class SELinuxHostTest extends DeviceTestCase implements IBuildReceiver, IDeviceTest {
     68 
     69     private static final Map<ITestDevice, File> cachedDevicePolicyFiles = new HashMap<>(1);
     70     private static final Map<ITestDevice, File> cachedDevicePlatFcFiles = new HashMap<>(1);
     71     private static final Map<ITestDevice, File> cachedDeviceNonplatFcFiles = new HashMap<>(1);
     72     private static final Map<ITestDevice, File> cachedDeviceVendorManifest = new HashMap<>(1);
     73     private static final Map<ITestDevice, File> cachedDeviceSystemPolicy = new HashMap<>(1);
     74 
     75     private File sepolicyAnalyze;
     76     private File checkSeapp;
     77     private File checkFc;
     78     private File aospSeappFile;
     79     private File aospFcFile;
     80     private File aospPcFile;
     81     private File aospSvcFile;
     82     private File devicePolicyFile;
     83     private File devicePlatSeappFile;
     84     private File deviceNonplatSeappFile;
     85     private File devicePlatFcFile;
     86     private File deviceNonplatFcFile;
     87     private File devicePcFile;
     88     private File deviceSvcFile;
     89     private File seappNeverAllowFile;
     90     private File libsepolwrap;
     91     private File libcpp;
     92     private File sepolicyTests;
     93 
     94     private IBuildInfo mBuild;
     95 
     96     /**
     97      * A reference to the device under test.
     98      */
     99     private ITestDevice mDevice;
    100 
    101     /**
    102      * {@inheritDoc}
    103      */
    104     @Override
    105     public void setBuild(IBuildInfo build) {
    106         mBuild = build;
    107     }
    108 
    109     /**
    110      * {@inheritDoc}
    111      */
    112     @Override
    113     public void setDevice(ITestDevice device) {
    114         super.setDevice(device);
    115         mDevice = device;
    116     }
    117 
    118     public static File copyResourceToTempFile(String resName) throws IOException {
    119         InputStream is = SELinuxHostTest.class.getResourceAsStream(resName);
    120         File tempFile = File.createTempFile("SELinuxHostTest", ".tmp");
    121         FileOutputStream os = new FileOutputStream(tempFile);
    122         byte[] buf = new byte[1024];
    123         int len;
    124 
    125         while ((len = is.read(buf)) != -1) {
    126             os.write(buf, 0, len);
    127         }
    128         os.flush();
    129         os.close();
    130         tempFile.deleteOnExit();
    131         return tempFile;
    132     }
    133 
    134     private static void appendTo(String dest, String src) throws IOException {
    135         try (FileInputStream is = new FileInputStream(new File(src));
    136              FileOutputStream os = new FileOutputStream(new File(dest))) {
    137             byte[] buf = new byte[1024];
    138             int len;
    139 
    140             while ((len = is.read(buf)) != -1) {
    141                 os.write(buf, 0, len);
    142             }
    143         }
    144     }
    145 
    146     @Override
    147     protected void setUp() throws Exception {
    148         super.setUp();
    149         CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(mBuild);
    150         sepolicyAnalyze = buildHelper.getTestFile("sepolicy-analyze");
    151         sepolicyAnalyze.setExecutable(true);
    152 
    153         devicePolicyFile = getDevicePolicyFile(mDevice);
    154         if (isSepolicySplit(mDevice)) {
    155             devicePlatFcFile = getDeviceFile(mDevice, cachedDevicePlatFcFiles,
    156                     "/system/etc/selinux/plat_file_contexts", "plat_file_contexts");
    157             if (mDevice.doesFileExist("/vendor/etc/selinux/nonplat_file_contexts")){
    158                 // Old nonplat_* naming can be present if a framework-only OTA was done.
    159                 deviceNonplatFcFile = getDeviceFile(mDevice, cachedDeviceNonplatFcFiles,
    160                         "/vendor/etc/selinux/nonplat_file_contexts", "nonplat_file_contexts");
    161             } else {
    162                 deviceNonplatFcFile = getDeviceFile(mDevice, cachedDeviceNonplatFcFiles,
    163                         "/vendor/etc/selinux/vendor_file_contexts", "vendor_file_contexts");
    164             }
    165         } else {
    166             devicePlatFcFile = getDeviceFile(mDevice, cachedDevicePlatFcFiles,
    167                     "/plat_file_contexts", "plat_file_contexts");
    168             deviceNonplatFcFile = getDeviceFile(mDevice, cachedDeviceNonplatFcFiles,
    169                     "/vendor_file_contexts", "vendor_file_contexts");
    170         }
    171     }
    172 
    173     /*
    174      * IMPLEMENTATION DETAILS: We cache some host-side policy files on per-device basis (in case
    175      * CTS supports running against multiple devices at the same time). HashMap is used instead
    176      * of WeakHashMap because in the grand scheme of things, keeping ITestDevice and
    177      * corresponding File objects from being garbage-collected is not a big deal in CTS. If this
    178      * becomes a big deal, this can be switched to WeakHashMap.
    179      */
    180     private static File getDeviceFile(ITestDevice device,
    181             Map<ITestDevice, File> cache, String deviceFilePath,
    182             String tmpFileName) throws Exception {
    183         if (!device.doesFileExist(deviceFilePath)){
    184             throw new Exception();
    185         }
    186         File file;
    187         synchronized (cache) {
    188             file = cache.get(device);
    189         }
    190         if (file != null) {
    191             return file;
    192         }
    193         file = File.createTempFile(tmpFileName, ".tmp");
    194         file.deleteOnExit();
    195         device.pullFile(deviceFilePath, file);
    196         synchronized (cache) {
    197             cache.put(device, file);
    198         }
    199         return file;
    200     }
    201 
    202     private static File buildSystemPolicy(ITestDevice device, Map<ITestDevice, File> cache,
    203             String tmpFileName) throws Exception {
    204         File builtPolicyFile;
    205         synchronized (cache) {
    206             builtPolicyFile = cache.get(device);
    207         }
    208         if (builtPolicyFile != null) {
    209             return builtPolicyFile;
    210         }
    211 
    212 
    213         builtPolicyFile = File.createTempFile(tmpFileName, ".tmp");
    214         builtPolicyFile.deleteOnExit();
    215 
    216         File secilc = copyResourceToTempFile("/secilc");
    217         secilc.setExecutable(true);
    218 
    219         File systemSepolicyCilFile = File.createTempFile("plat_sepolicy", ".cil");
    220         systemSepolicyCilFile.deleteOnExit();
    221 
    222         assertTrue(device.pullFile("/system/etc/selinux/plat_sepolicy.cil", systemSepolicyCilFile));
    223 
    224         ProcessBuilder pb = new ProcessBuilder(
    225             secilc.getAbsolutePath(),
    226             "-m", "-M", "true", "-c", "30",
    227             "-o", builtPolicyFile.getAbsolutePath(),
    228             systemSepolicyCilFile.getAbsolutePath());
    229         pb.redirectOutput(ProcessBuilder.Redirect.PIPE);
    230         pb.redirectErrorStream(true);
    231         Process p = pb.start();
    232         p.waitFor();
    233         BufferedReader result = new BufferedReader(new InputStreamReader(p.getInputStream()));
    234         String line;
    235         StringBuilder errorString = new StringBuilder();
    236         while ((line = result.readLine()) != null) {
    237             errorString.append(line);
    238             errorString.append("\n");
    239         }
    240         assertTrue(errorString.toString(), errorString.length() == 0);
    241 
    242         synchronized (cache) {
    243             cache.put(device, builtPolicyFile);
    244         }
    245         return builtPolicyFile;
    246     }
    247 
    248     // NOTE: cts/tools/selinux depends on this method. Rename/change with caution.
    249     /**
    250      * Returns the host-side file containing the SELinux policy of the device under test.
    251      */
    252     public static File getDevicePolicyFile(ITestDevice device) throws Exception {
    253         return getDeviceFile(device, cachedDevicePolicyFiles, "/sys/fs/selinux/policy", "sepolicy");
    254     }
    255 
    256     // NOTE: cts/tools/selinux depends on this method. Rename/change with caution.
    257     /**
    258      * Returns the host-side file containing the system SELinux policy of the device under test.
    259      */
    260     public static File getDeviceSystemPolicyFile(ITestDevice device) throws Exception {
    261         return buildSystemPolicy(device, cachedDeviceSystemPolicy, "system_sepolicy");
    262     }
    263 
    264     // NOTE: cts/tools/selinux depends on this method. Rename/change with caution.
    265     /**
    266      * Returns the major number of sepolicy version of device's vendor implementation.
    267      * TODO(b/37999212): Use VINTF object API instead of parsing vendor manifest.
    268      */
    269     public static int getVendorSepolicyVersion(ITestDevice device) throws Exception {
    270         String deviceManifestPath =
    271                 (device.doesFileExist("/vendor/etc/vintf/manifest.xml")) ?
    272                 "/vendor/etc/vintf/manifest.xml" :
    273                 "/vendor/manifest.xml";
    274         File vendorManifestFile = getDeviceFile(device, cachedDeviceVendorManifest,
    275                 deviceManifestPath, "manifest.xml");
    276 
    277         DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
    278         DocumentBuilder db = dbf.newDocumentBuilder();
    279         Document doc = db.parse(vendorManifestFile);
    280         Element root = doc.getDocumentElement();
    281         Element sepolicy = (Element) root.getElementsByTagName("sepolicy").item(0);
    282         Element version = (Element) sepolicy.getElementsByTagName("version").item(0);
    283         String sepolicyVersion = version.getTextContent().split("\\.")[0];
    284         return Integer.parseInt(sepolicyVersion);
    285     }
    286 
    287     /**
    288      * Tests that the kernel is enforcing selinux policy globally.
    289      *
    290      * @throws Exception
    291      */
    292     @CddTest(requirement="9.7")
    293     public void testGlobalEnforcing() throws Exception {
    294         CollectingOutputReceiver out = new CollectingOutputReceiver();
    295         mDevice.executeShellCommand("cat /sys/fs/selinux/enforce", out);
    296         assertEquals("SELinux policy is not being enforced!", "1", out.getOutput());
    297     }
    298 
    299     /**
    300      * Tests that all domains in the running policy file are in enforcing mode
    301      *
    302      * @throws Exception
    303      */
    304     @CddTest(requirement="9.7")
    305     @RestrictedBuildTest
    306     public void testAllDomainsEnforcing() throws Exception {
    307 
    308         /* run sepolicy-analyze permissive check on policy file */
    309         ProcessBuilder pb = new ProcessBuilder(sepolicyAnalyze.getAbsolutePath(),
    310                 devicePolicyFile.getAbsolutePath(), "permissive");
    311         pb.redirectOutput(ProcessBuilder.Redirect.PIPE);
    312         pb.redirectErrorStream(true);
    313         Process p = pb.start();
    314         p.waitFor();
    315         BufferedReader result = new BufferedReader(new InputStreamReader(p.getInputStream()));
    316         String line;
    317         StringBuilder errorString = new StringBuilder();
    318         while ((line = result.readLine()) != null) {
    319             errorString.append(line);
    320             errorString.append("\n");
    321         }
    322         assertTrue("The following SELinux domains were found to be in permissive mode:\n"
    323                    + errorString, errorString.length() == 0);
    324     }
    325 
    326     /**
    327      * Asserts that specified type is not associated with the specified
    328      * attribute.
    329      *
    330      * @param attribute
    331      *  The attribute name.
    332      * @param type
    333      *  The type name.
    334      */
    335     private void assertNotInAttribute(String attribute, String badtype) throws Exception {
    336         Set<String> actualTypes = sepolicyAnalyzeGetTypesAssociatedWithAttribute(attribute);
    337         if (actualTypes.contains(badtype)) {
    338             fail("Attribute " + attribute + " includes " + badtype);
    339         }
    340     }
    341 
    342     private static final byte[] readFully(InputStream in) throws IOException {
    343         ByteArrayOutputStream result = new ByteArrayOutputStream();
    344         byte[] buf = new byte[65536];
    345         int chunkSize;
    346         while ((chunkSize = in.read(buf)) != -1) {
    347             result.write(buf, 0, chunkSize);
    348         }
    349         return result.toByteArray();
    350     }
    351 
    352     /**
    353      * Runs sepolicy-analyze against the device's SELinux policy and returns the set of types
    354      * associated with the provided attribute.
    355      */
    356     private Set<String> sepolicyAnalyzeGetTypesAssociatedWithAttribute(
    357             String attribute) throws Exception {
    358         ProcessBuilder pb =
    359                 new ProcessBuilder(
    360                         sepolicyAnalyze.getAbsolutePath(),
    361                         devicePolicyFile.getAbsolutePath(),
    362                         "attribute",
    363                         attribute);
    364         pb.redirectOutput(ProcessBuilder.Redirect.PIPE);
    365         pb.redirectErrorStream(true);
    366         Process p = pb.start();
    367         int errorCode = p.waitFor();
    368         if (errorCode != 0) {
    369             fail("sepolicy-analyze attribute " + attribute + " failed with error code " + errorCode
    370                     + ": " + new String(readFully(p.getInputStream())));
    371         }
    372         try (BufferedReader in =
    373                 new BufferedReader(new InputStreamReader(p.getInputStream()))) {
    374             Set<String> types = new HashSet<>();
    375             String type;
    376             while ((type = in.readLine()) != null) {
    377                 types.add(type.trim());
    378             }
    379             return types;
    380         }
    381     }
    382 
    383     // NOTE: cts/tools/selinux depends on this method. Rename/change with caution.
    384     /**
    385      * Returns {@code true} if this device is required to be a full Treble device.
    386      */
    387     public static boolean isFullTrebleDevice(ITestDevice device)
    388             throws DeviceNotAvailableException {
    389         return PropertyUtil.getFirstApiLevel(device) > 26;
    390     }
    391 
    392     private boolean isFullTrebleDevice() throws DeviceNotAvailableException {
    393         return isFullTrebleDevice(mDevice);
    394     }
    395 
    396     // NOTE: cts/tools/selinux depends on this method. Rename/change with caution.
    397     /**
    398      * Returns {@code true} if this device is required to enforce compatible property.
    399      */
    400     public static boolean isCompatiblePropertyEnforcedDevice(ITestDevice device)
    401             throws DeviceNotAvailableException {
    402         return PropertyUtil.getFirstApiLevel(device) > 27;
    403     }
    404 
    405     // NOTE: cts/tools/selinux depends on this method. Rename/change with caution.
    406     /**
    407      * Returns {@code true} if this device has sepolicy split across different paritions.
    408      * This is possible even for devices launched at api level higher than 26.
    409      */
    410     public static boolean isSepolicySplit(ITestDevice device)
    411             throws DeviceNotAvailableException {
    412         return device.doesFileExist("/system/etc/selinux/plat_file_contexts");
    413     }
    414 
    415     /**
    416      * Asserts that no vendor domains are exempted from the prohibition on Binder use.
    417      *
    418      * <p>NOTE: binder_in_vendor_violators attribute is only there to help bring up Treble devices.
    419      * It offers a convenient way to temporarily bypass the prohibition on Binder use in vendor
    420      * domains. This attribute must not be used on production Treble devices.
    421      */
    422     public void testNoExemptionsForBinderInVendorBan() throws Exception {
    423         if (!isFullTrebleDevice()) {
    424             return;
    425         }
    426 
    427         Set<String> types =
    428             sepolicyAnalyzeGetTypesAssociatedWithAttribute("binder_in_vendor_violators");
    429         if (!types.isEmpty()) {
    430             List<String> sortedTypes = new ArrayList<>(types);
    431             Collections.sort(sortedTypes);
    432             fail("Policy exempts vendor domains from ban on Binder: " + sortedTypes);
    433         }
    434     }
    435 
    436     /**
    437      * Asserts that no HAL server domains are exempted from the prohibition of socket use with the
    438      * only exceptions for the automotive device type.
    439      */
    440     public void testNoExemptionsForSocketsUseWithinHalServer() throws Exception {
    441         if (!isFullTrebleDevice()) {
    442             return;
    443         }
    444 
    445         if (getDevice().hasFeature("android.hardware.type.automotive")) {
    446             return;
    447         }
    448 
    449         Set<String> types = sepolicyAnalyzeGetTypesAssociatedWithAttribute(
    450                 "hal_automotive_socket_exemption");
    451         if (!types.isEmpty()) {
    452             List<String> sortedTypes = new ArrayList<>(types);
    453             Collections.sort(sortedTypes);
    454             fail("Policy exempts domains from ban on socket usage from HAL servers: "
    455                     + sortedTypes);
    456         }
    457     }
    458 
    459     /**
    460      * Asserts that no domains are exempted from the prohibition on initiating socket communications
    461      * between core and vendor domains.
    462      *
    463      * <p>NOTE: socket_between_core_and_vendor_violators attribute is only there to help bring up
    464      * Treble devices. It offers a convenient way to temporarily bypass the prohibition on
    465      * initiating socket communications between core and vendor domains. This attribute must not be
    466      * used on production Treble devices.
    467      */
    468     public void testNoExemptionsForSocketsBetweenCoreAndVendorBan() throws Exception {
    469         if (!isFullTrebleDevice()) {
    470             return;
    471         }
    472 
    473         Set<String> types =
    474                 sepolicyAnalyzeGetTypesAssociatedWithAttribute(
    475                         "socket_between_core_and_vendor_violators");
    476         if (!types.isEmpty()) {
    477             List<String> sortedTypes = new ArrayList<>(types);
    478             Collections.sort(sortedTypes);
    479             fail("Policy exempts domains from ban on socket communications between core and"
    480                     + " vendor: " + sortedTypes);
    481         }
    482     }
    483 
    484     /**
    485      * Asserts that no vendor domains are exempted from the prohibition on directly
    486      * executing binaries from /system.
    487      * */
    488     public void testNoExemptionsForVendorExecutingCore() throws Exception {
    489         if (!isFullTrebleDevice()) {
    490             return;
    491         }
    492 
    493         Set<String> types =
    494                 sepolicyAnalyzeGetTypesAssociatedWithAttribute(
    495                         "vendor_executes_system_violators");
    496         if (!types.isEmpty()) {
    497             List<String> sortedTypes = new ArrayList<>(types);
    498             Collections.sort(sortedTypes);
    499             fail("Policy exempts vendor domains from ban on executing files in /system: "
    500                     + sortedTypes);
    501         }
    502     }
    503 
    504     /**
    505      * Tests that mlstrustedsubject does not include untrusted_app
    506      * and that mlstrustedobject does not include app_data_file.
    507      * This helps prevent circumventing the per-user isolation of
    508      * normal apps via levelFrom=user.
    509      *
    510      * @throws Exception
    511      */
    512     @CddTest(requirement="9.7")
    513     public void testMLSAttributes() throws Exception {
    514         assertNotInAttribute("mlstrustedsubject", "untrusted_app");
    515         assertNotInAttribute("mlstrustedobject", "app_data_file");
    516     }
    517 
    518     /**
    519      * Tests that the seapp_contexts file on the device is valid.
    520      *
    521      * @throws Exception
    522      */
    523     @CddTest(requirement="9.7")
    524     public void testValidSeappContexts() throws Exception {
    525 
    526         /* obtain seapp_contexts file from running device */
    527         devicePlatSeappFile = File.createTempFile("plat_seapp_contexts", ".tmp");
    528         devicePlatSeappFile.deleteOnExit();
    529         deviceNonplatSeappFile = File.createTempFile("nonplat_seapp_contexts", ".tmp");
    530         deviceNonplatSeappFile.deleteOnExit();
    531         if (mDevice.pullFile("/system/etc/selinux/plat_seapp_contexts", devicePlatSeappFile)) {
    532             mDevice.pullFile("/vendor/etc/selinux/nonplat_seapp_contexts", deviceNonplatSeappFile);
    533         }else {
    534             mDevice.pullFile("/plat_seapp_contexts", devicePlatSeappFile);
    535             mDevice.pullFile("/nonplat_seapp_contexts", deviceNonplatSeappFile);
    536 	}
    537 
    538         /* retrieve the checkseapp executable from jar */
    539         checkSeapp = copyResourceToTempFile("/checkseapp");
    540         checkSeapp.setExecutable(true);
    541 
    542         /* retrieve the AOSP seapp_neverallows file from jar */
    543         seappNeverAllowFile = copyResourceToTempFile("/plat_seapp_neverallows");
    544 
    545         /* run checkseapp on seapp_contexts */
    546         ProcessBuilder pb = new ProcessBuilder(checkSeapp.getAbsolutePath(),
    547                 "-p", devicePolicyFile.getAbsolutePath(),
    548                 seappNeverAllowFile.getAbsolutePath(),
    549                 devicePlatSeappFile.getAbsolutePath(),
    550                 deviceNonplatSeappFile.getAbsolutePath());
    551         pb.redirectOutput(ProcessBuilder.Redirect.PIPE);
    552         pb.redirectErrorStream(true);
    553         Process p = pb.start();
    554         p.waitFor();
    555         BufferedReader result = new BufferedReader(new InputStreamReader(p.getInputStream()));
    556         String line;
    557         StringBuilder errorString = new StringBuilder();
    558         while ((line = result.readLine()) != null) {
    559             errorString.append(line);
    560             errorString.append("\n");
    561         }
    562         assertTrue("The seapp_contexts file was invalid:\n"
    563                    + errorString, errorString.length() == 0);
    564     }
    565 
    566     /**
    567      * Asserts that the actual file contents starts with the expected file
    568      * contents.
    569      *
    570      * @param expectedFile
    571      *  The file with the expected contents.
    572      * @param actualFile
    573      *  The actual file being checked.
    574      */
    575     private void assertFileStartsWith(File expectedFile, File actualFile) throws Exception {
    576         BufferedReader expectedReader = new BufferedReader(new FileReader(expectedFile.getAbsolutePath()));
    577         BufferedReader actualReader = new BufferedReader(new FileReader(actualFile.getAbsolutePath()));
    578         String expectedLine, actualLine;
    579         while ((expectedLine = expectedReader.readLine()) != null) {
    580             actualLine = actualReader.readLine();
    581             assertEquals("Lines do not match:", expectedLine, actualLine);
    582         }
    583     }
    584 
    585     /**
    586      * Tests that the seapp_contexts file on the device contains
    587      * the standard AOSP entries.
    588      *
    589      * @throws Exception
    590      */
    591     @CddTest(requirement="9.7")
    592     public void testAospSeappContexts() throws Exception {
    593 
    594         /* obtain seapp_contexts file from running device */
    595         devicePlatSeappFile = File.createTempFile("seapp_contexts", ".tmp");
    596         devicePlatSeappFile.deleteOnExit();
    597         if (!mDevice.pullFile("/system/etc/selinux/plat_seapp_contexts", devicePlatSeappFile)) {
    598             mDevice.pullFile("/plat_seapp_contexts", devicePlatSeappFile);
    599         }
    600         /* retrieve the AOSP seapp_contexts file from jar */
    601         aospSeappFile = copyResourceToTempFile("/plat_seapp_contexts");
    602 
    603         assertFileStartsWith(aospSeappFile, devicePlatSeappFile);
    604     }
    605 
    606     /**
    607      * Tests that the file_contexts.bin file on the device contains
    608      * the standard AOSP entries.
    609      *
    610      * @throws Exception
    611      */
    612     @CddTest(requirement="9.7")
    613     public void testAospFileContexts() throws Exception {
    614 
    615         /* retrieve the checkfc executable from jar */
    616         checkFc = copyResourceToTempFile("/checkfc");
    617         checkFc.setExecutable(true);
    618 
    619         /* retrieve the AOSP file_contexts file from jar */
    620         aospFcFile = copyResourceToTempFile("/plat_file_contexts");
    621 
    622         /* run checkfc -c plat_file_contexts file_contexts.bin */
    623         ProcessBuilder pb = new ProcessBuilder(checkFc.getAbsolutePath(),
    624                 "-c", aospFcFile.getAbsolutePath(),
    625                 devicePlatFcFile.getAbsolutePath());
    626         pb.redirectOutput(ProcessBuilder.Redirect.PIPE);
    627         pb.redirectErrorStream(true);
    628         Process p = pb.start();
    629         p.waitFor();
    630         BufferedReader result = new BufferedReader(new InputStreamReader(p.getInputStream()));
    631         String line = result.readLine();
    632         assertTrue("The file_contexts file did not include the AOSP entries:\n"
    633                    + line + "\n",
    634                    line.equals("equal") || line.equals("subset"));
    635     }
    636 
    637     /**
    638      * Tests that the property_contexts file on the device contains
    639      * the standard AOSP entries.
    640      *
    641      * @throws Exception
    642      */
    643     @CddTest(requirement="9.7")
    644     public void testAospPropertyContexts() throws Exception {
    645 
    646         /* obtain property_contexts file from running device */
    647         devicePcFile = File.createTempFile("plat_property_contexts", ".tmp");
    648         devicePcFile.deleteOnExit();
    649         // plat_property_contexts may be either in /system/etc/sepolicy or in /
    650         if (!mDevice.pullFile("/system/etc/selinux/plat_property_contexts", devicePcFile)) {
    651             mDevice.pullFile("/plat_property_contexts", devicePcFile);
    652         }
    653 
    654         // Retrieve the AOSP property_contexts file from JAR.
    655         // The location of this file in the JAR has nothing to do with the location of this file on
    656         // Android devices. See build script of this CTS module.
    657         aospPcFile = copyResourceToTempFile("/plat_property_contexts");
    658 
    659         assertFileStartsWith(aospPcFile, devicePcFile);
    660     }
    661 
    662     /**
    663      * Tests that the service_contexts file on the device contains
    664      * the standard AOSP entries.
    665      *
    666      * @throws Exception
    667      */
    668     @CddTest(requirement="9.7")
    669     public void testAospServiceContexts() throws Exception {
    670 
    671         /* obtain service_contexts file from running device */
    672         deviceSvcFile = File.createTempFile("service_contexts", ".tmp");
    673         deviceSvcFile.deleteOnExit();
    674         if (!mDevice.pullFile("/system/etc/selinux/plat_service_contexts", deviceSvcFile)) {
    675             mDevice.pullFile("/plat_service_contexts", deviceSvcFile);
    676         }
    677 
    678         /* retrieve the AOSP service_contexts file from jar */
    679         aospSvcFile = copyResourceToTempFile("/plat_service_contexts");
    680 
    681         assertFileStartsWith(aospSvcFile, deviceSvcFile);
    682     }
    683 
    684     /**
    685      * Tests that the file_contexts file(s) on the device is valid.
    686      *
    687      * @throws Exception
    688      */
    689     @CddTest(requirement="9.7")
    690     public void testValidFileContexts() throws Exception {
    691 
    692         /* retrieve the checkfc executable from jar */
    693         checkFc = copyResourceToTempFile("/checkfc");
    694         checkFc.setExecutable(true);
    695 
    696         /* combine plat and nonplat policies for testing */
    697         File combinedFcFile = File.createTempFile("combined_file_context", ".tmp");
    698         combinedFcFile.deleteOnExit();
    699         appendTo(combinedFcFile.getAbsolutePath(), devicePlatFcFile.getAbsolutePath());
    700         appendTo(combinedFcFile.getAbsolutePath(), deviceNonplatFcFile.getAbsolutePath());
    701 
    702         /* run checkfc sepolicy file_contexts */
    703         ProcessBuilder pb = new ProcessBuilder(checkFc.getAbsolutePath(),
    704                 devicePolicyFile.getAbsolutePath(),
    705                 combinedFcFile.getAbsolutePath());
    706         pb.redirectOutput(ProcessBuilder.Redirect.PIPE);
    707         pb.redirectErrorStream(true);
    708         Process p = pb.start();
    709         p.waitFor();
    710         BufferedReader result = new BufferedReader(new InputStreamReader(p.getInputStream()));
    711         String line;
    712         StringBuilder errorString = new StringBuilder();
    713         while ((line = result.readLine()) != null) {
    714             errorString.append(line);
    715             errorString.append("\n");
    716         }
    717         assertTrue("file_contexts was invalid:\n"
    718                    + errorString, errorString.length() == 0);
    719     }
    720 
    721     /**
    722      * Tests that the property_contexts file on the device is valid.
    723      *
    724      * @throws Exception
    725      */
    726     @CddTest(requirement="9.7")
    727     public void testValidPropertyContexts() throws Exception {
    728 
    729         /* retrieve the checkfc executable from jar */
    730         File propertyInfoChecker = copyResourceToTempFile("/property_info_checker");
    731         propertyInfoChecker.setExecutable(true);
    732 
    733         /* obtain property_contexts file from running device */
    734         devicePcFile = File.createTempFile("property_contexts", ".tmp");
    735         devicePcFile.deleteOnExit();
    736         // plat_property_contexts may be either in /system/etc/sepolicy or in /
    737         if (!mDevice.pullFile("/system/etc/selinux/plat_property_contexts", devicePcFile)) {
    738             mDevice.pullFile("/plat_property_contexts", devicePcFile);
    739         }
    740 
    741         /* run property_info_checker on property_contexts */
    742         ProcessBuilder pb = new ProcessBuilder(propertyInfoChecker.getAbsolutePath(),
    743                 devicePolicyFile.getAbsolutePath(),
    744                 devicePcFile.getAbsolutePath());
    745         pb.redirectOutput(ProcessBuilder.Redirect.PIPE);
    746         pb.redirectErrorStream(true);
    747         Process p = pb.start();
    748         p.waitFor();
    749         BufferedReader result = new BufferedReader(new InputStreamReader(p.getInputStream()));
    750         String line;
    751         StringBuilder errorString = new StringBuilder();
    752         while ((line = result.readLine()) != null) {
    753             errorString.append(line);
    754             errorString.append("\n");
    755         }
    756         assertTrue("The property_contexts file was invalid:\n"
    757                    + errorString, errorString.length() == 0);
    758     }
    759 
    760     /**
    761      * Tests that the service_contexts file on the device is valid.
    762      *
    763      * @throws Exception
    764      */
    765     @CddTest(requirement="9.7")
    766     public void testValidServiceContexts() throws Exception {
    767 
    768         /* retrieve the checkfc executable from jar */
    769         checkFc = copyResourceToTempFile("/checkfc");
    770         checkFc.setExecutable(true);
    771 
    772         /* obtain service_contexts file from running device */
    773         deviceSvcFile = File.createTempFile("service_contexts", ".tmp");
    774         deviceSvcFile.deleteOnExit();
    775         mDevice.pullFile("/service_contexts", deviceSvcFile);
    776 
    777         /* run checkfc -s on service_contexts */
    778         ProcessBuilder pb = new ProcessBuilder(checkFc.getAbsolutePath(),
    779                 "-s", devicePolicyFile.getAbsolutePath(),
    780                 deviceSvcFile.getAbsolutePath());
    781         pb.redirectOutput(ProcessBuilder.Redirect.PIPE);
    782         pb.redirectErrorStream(true);
    783         Process p = pb.start();
    784         p.waitFor();
    785         BufferedReader result = new BufferedReader(new InputStreamReader(p.getInputStream()));
    786         String line;
    787         StringBuilder errorString = new StringBuilder();
    788         while ((line = result.readLine()) != null) {
    789             errorString.append(line);
    790             errorString.append("\n");
    791         }
    792         assertTrue("The service_contexts file was invalid:\n"
    793                    + errorString, errorString.length() == 0);
    794     }
    795 
    796     public static boolean isMac() {
    797         String os = System.getProperty("os.name").toLowerCase();
    798         return (os.startsWith("mac") || os.startsWith("darwin"));
    799     }
    800 
    801     private void setupLibraries() throws Exception {
    802         // The host side binary tests are host OS specific. Use Linux
    803         // libraries on Linux and Mac libraries on Mac.
    804         if (isMac()) {
    805             libsepolwrap = copyResourceToTempFile("/libsepolwrap.dylib");
    806             libcpp = copyResourceToTempFile("/libc++.dylib");
    807             libcpp.renameTo(new File(System.getProperty("java.io.tmpdir") + "/libc++.dylib"));
    808         } else {
    809             libsepolwrap = copyResourceToTempFile("/libsepolwrap.so");
    810             libcpp = copyResourceToTempFile("/libc++.so");
    811             libcpp.renameTo(new File(System.getProperty("java.io.tmpdir") + "/libc++.so"));
    812         }
    813         libsepolwrap.deleteOnExit();
    814         libcpp.deleteOnExit();
    815     }
    816 
    817     private void assertSepolicyTests(String test, String testExecutable) throws Exception {
    818         setupLibraries();
    819         sepolicyTests = copyResourceToTempFile(testExecutable);
    820         sepolicyTests.setExecutable(true);
    821         ProcessBuilder pb = new ProcessBuilder(
    822                 sepolicyTests.getAbsolutePath(),
    823                 "-l", libsepolwrap.getAbsolutePath(),
    824                 "-f", devicePlatFcFile.getAbsolutePath(),
    825                 "-f", deviceNonplatFcFile.getAbsolutePath(),
    826                 "-p", devicePolicyFile.getAbsolutePath(),
    827                 "--test", test);
    828         Map<String, String> env = pb.environment();
    829         if (isMac()) {
    830             env.put("DYLD_LIBRARY_PATH", System.getProperty("java.io.tmpdir"));
    831         } else {
    832             env.put("LD_LIBRARY_PATH", System.getProperty("java.io.tmpdir"));
    833         }
    834         pb.redirectOutput(ProcessBuilder.Redirect.PIPE);
    835         pb.redirectErrorStream(true);
    836         Process p = pb.start();
    837         p.waitFor();
    838         BufferedReader result = new BufferedReader(new InputStreamReader(p.getInputStream()));
    839         String line;
    840         StringBuilder errorString = new StringBuilder();
    841         while ((line = result.readLine()) != null) {
    842             errorString.append(line);
    843             errorString.append("\n");
    844         }
    845         assertTrue(errorString.toString(), errorString.length() == 0);
    846     }
    847 
    848     /**
    849      * Tests that all types on /data have the data_file_type attribute.
    850      *
    851      * @throws Exception
    852      */
    853     public void testDataTypeViolators() throws Exception {
    854         assertSepolicyTests("TestDataTypeViolations", "/sepolicy_tests");
    855     }
    856 
    857     /**
    858      * Tests that all types in /proc have the proc_type attribute.
    859      *
    860      * @throws Exception
    861      */
    862     public void testProcTypeViolators() throws Exception {
    863         assertSepolicyTests("TestProcTypeViolations", "/sepolicy_tests");
    864     }
    865 
    866     /**
    867      * Tests that all types in /sys have the sysfs_type attribute.
    868      *
    869      * @throws Exception
    870      */
    871     public void testSysfsTypeViolators() throws Exception {
    872         assertSepolicyTests("TestSysfsTypeViolations", "/sepolicy_tests");
    873     }
    874 
    875     /**
    876      * Tests that all types on /vendor have the vendor_file_type attribute.
    877      *
    878      * @throws Exception
    879      */
    880     public void testVendorTypeViolators() throws Exception {
    881         assertSepolicyTests("TestVendorTypeViolations", "/sepolicy_tests");
    882     }
    883 
    884     /**
    885      * Tests that all domains with entrypoints on /system have the coredomain
    886      * attribute, and that all domains with entrypoints on /vendor do not have the
    887      * coredomain attribute.
    888      *
    889      * @throws Exception
    890      */
    891     public void testCoredomainViolators() throws Exception {
    892         assertSepolicyTests("CoredomainViolations", "/treble_sepolicy_tests");
    893     }
    894 
    895    /**
    896      * Tests that the policy defines no booleans (runtime conditional policy).
    897      *
    898      * @throws Exception
    899      */
    900     @CddTest(requirement="9.7")
    901     public void testNoBooleans() throws Exception {
    902 
    903         /* run sepolicy-analyze booleans check on policy file */
    904         ProcessBuilder pb = new ProcessBuilder(sepolicyAnalyze.getAbsolutePath(),
    905                 devicePolicyFile.getAbsolutePath(), "booleans");
    906         pb.redirectOutput(ProcessBuilder.Redirect.PIPE);
    907         pb.redirectErrorStream(true);
    908         Process p = pb.start();
    909         p.waitFor();
    910         BufferedReader result = new BufferedReader(new InputStreamReader(p.getInputStream()));
    911         String line;
    912         StringBuilder errorString = new StringBuilder();
    913         while ((line = result.readLine()) != null) {
    914             errorString.append(line);
    915             errorString.append("\n");
    916         }
    917         assertTrue("The policy contained booleans:\n"
    918                    + errorString, errorString.length() == 0);
    919     }
    920 
    921     /**
    922      * Tests that important domain labels are being appropriately applied.
    923      */
    924 
    925     /**
    926      * Asserts that no processes are running in a domain.
    927      *
    928      * @param domain
    929      *  The domain or SELinux context to check.
    930      */
    931     private void assertDomainEmpty(String domain) throws DeviceNotAvailableException {
    932         List<ProcessDetails> procs = ProcessDetails.getProcMap(mDevice).get(domain);
    933         String msg = "Expected no processes in SELinux domain \"" + domain + "\""
    934             + " Found: \"" + procs + "\"";
    935         assertNull(msg, procs);
    936     }
    937 
    938     /**
    939      * Asserts that a domain exists and that only one, well defined, process is
    940      * running in that domain.
    941      *
    942      * @param domain
    943      *  The domain or SELinux context to check.
    944      * @param executable
    945      *  The path of the executable or application package name.
    946      */
    947     private void assertDomainOne(String domain, String executable) throws DeviceNotAvailableException {
    948         List<ProcessDetails> procs = ProcessDetails.getProcMap(mDevice).get(domain);
    949         List<ProcessDetails> exeProcs = ProcessDetails.getExeMap(mDevice).get(executable);
    950         String msg = "Expected 1 process in SELinux domain \"" + domain + "\""
    951             + " Found \"" + procs + "\"";
    952         assertNotNull(msg, procs);
    953         assertEquals(msg, 1, procs.size());
    954 
    955         msg = "Expected executable \"" + executable + "\" in SELinux domain \"" + domain + "\""
    956             + "Found: \"" + procs + "\"";
    957         assertEquals(msg, executable, procs.get(0).procTitle);
    958 
    959         msg = "Expected 1 process with executable \"" + executable + "\""
    960             + " Found \"" + procs + "\"";
    961         assertNotNull(msg, exeProcs);
    962         assertEquals(msg, 1, exeProcs.size());
    963 
    964         msg = "Expected executable \"" + executable + "\" in SELinux domain \"" + domain + "\""
    965             + "Found: \"" + procs + "\"";
    966         assertEquals(msg, domain, exeProcs.get(0).label);
    967     }
    968 
    969     /**
    970      * Asserts that a domain may exist. If a domain exists, the cardinality of
    971      * the domain is verified to be 1 and that the correct process is running in
    972      * that domain.
    973      *
    974      * @param domain
    975      *  The domain or SELinux context to check.
    976      * @param executable
    977      *  The path of the executable or application package name.
    978      */
    979     private void assertDomainZeroOrOne(String domain, String executable)
    980         throws DeviceNotAvailableException {
    981         List<ProcessDetails> procs = ProcessDetails.getProcMap(mDevice).get(domain);
    982         List<ProcessDetails> exeProcs = ProcessDetails.getExeMap(mDevice).get(executable);
    983 
    984         if (procs != null) {
    985             String msg = "Expected 1 process in SELinux domain \"" + domain + "\""
    986             + " Found: \"" + procs + "\"";
    987             assertEquals(msg, 1, procs.size());
    988 
    989             msg = "Expected executable \"" + executable + "\" in SELinux domain \"" + domain + "\""
    990                 + "Found: \"" + procs.get(0) + "\"";
    991             assertEquals(msg, executable, procs.get(0).procTitle);
    992         }
    993 
    994         if (exeProcs != null) {
    995             String msg = "Expected 1 process with executable \"" + executable + "\""
    996             + " Found: \"" + procs + "\"";
    997             assertEquals(msg, 1, exeProcs.size());
    998 
    999             msg = "Expected executable \"" + executable + "\" in SELinux domain \"" + domain + "\""
   1000                 + "Found: \"" + procs.get(0) + "\"";
   1001             assertEquals(msg, domain, exeProcs.get(0).label);
   1002         }
   1003     }
   1004 
   1005     /**
   1006      * Asserts that a domain must exist, and that the cardinality is greater
   1007      * than or equal to 1.
   1008      *
   1009      * @param domain
   1010      *  The domain or SELinux context to check.
   1011      * @param executables
   1012      *  The path of the allowed executables or application package names.
   1013      */
   1014     private void assertDomainN(String domain, String... executables)
   1015         throws DeviceNotAvailableException {
   1016         List<ProcessDetails> procs = ProcessDetails.getProcMap(mDevice).get(domain);
   1017         String msg = "Expected 1 or more processes in SELinux domain but found none.";
   1018         assertNotNull(msg, procs);
   1019 
   1020         Set<String> execList = new HashSet<String>(Arrays.asList(executables));
   1021 
   1022         for (ProcessDetails p : procs) {
   1023             msg = "Expected one of \"" + execList + "\" in SELinux domain \"" + domain + "\""
   1024                 + " Found: \"" + p + "\"";
   1025             assertTrue(msg, execList.contains(p.procTitle));
   1026         }
   1027 
   1028         for (String exe : executables) {
   1029             List<ProcessDetails> exeProcs = ProcessDetails.getExeMap(mDevice).get(exe);
   1030 
   1031             if (exeProcs != null) {
   1032                 for (ProcessDetails p : exeProcs) {
   1033                     msg = "Expected executable \"" + exe + "\" in SELinux domain \""
   1034                         + domain + "\"" + " Found: \"" + p + "\"";
   1035                     assertEquals(msg, domain, p.label);
   1036                 }
   1037             }
   1038         }
   1039     }
   1040 
   1041     /**
   1042      * Asserts that a domain, if it exists, is only running the listed executables.
   1043      *
   1044      * @param domain
   1045      *  The domain or SELinux context to check.
   1046      * @param executables
   1047      *  The path of the allowed executables or application package names.
   1048      */
   1049     private void assertDomainHasExecutable(String domain, String... executables)
   1050         throws DeviceNotAvailableException {
   1051         List<ProcessDetails> procs = ProcessDetails.getProcMap(mDevice).get(domain);
   1052 
   1053         if (procs != null) {
   1054             Set<String> execList = new HashSet<String>(Arrays.asList(executables));
   1055 
   1056             for (ProcessDetails p : procs) {
   1057                 String msg = "Expected one of \"" + execList + "\" in SELinux domain \""
   1058                     + domain + "\"" + " Found: \"" + p + "\"";
   1059                 assertTrue(msg, execList.contains(p.procTitle));
   1060             }
   1061         }
   1062     }
   1063 
   1064     /**
   1065      * Asserts that an executable exists and is only running in the listed domains.
   1066      *
   1067      * @param executable
   1068      *  The path of the executable to check.
   1069      * @param domains
   1070      *  The list of allowed domains.
   1071      */
   1072     private void assertExecutableHasDomain(String executable, String... domains)
   1073         throws DeviceNotAvailableException {
   1074         List<ProcessDetails> exeProcs = ProcessDetails.getExeMap(mDevice).get(executable);
   1075         Set<String> domainList = new HashSet<String>(Arrays.asList(domains));
   1076 
   1077         String msg = "Expected 1 or more processes for executable \"" + executable + "\".";
   1078         assertNotNull(msg, exeProcs);
   1079 
   1080         for (ProcessDetails p : exeProcs) {
   1081             msg = "Expected one of  \"" + domainList + "\" for executable \"" + executable
   1082                     + "\"" + " Found: \"" + p.label + "\"";
   1083             assertTrue(msg, domainList.contains(p.label));
   1084         }
   1085     }
   1086 
   1087     /* Init is always there */
   1088     @CddTest(requirement="9.7")
   1089     public void testInitDomain() throws DeviceNotAvailableException {
   1090         assertDomainHasExecutable("u:r:init:s0", "/init");
   1091         assertDomainHasExecutable("u:r:vendor_init:s0", "/init");
   1092         assertExecutableHasDomain("/init", "u:r:init:s0", "u:r:vendor_init:s0");
   1093     }
   1094 
   1095     /* Ueventd is always there */
   1096     @CddTest(requirement="9.7")
   1097     public void testUeventdDomain() throws DeviceNotAvailableException {
   1098         assertDomainOne("u:r:ueventd:s0", "/sbin/ueventd");
   1099     }
   1100 
   1101     /* healthd may or may not exist */
   1102     @CddTest(requirement="9.7")
   1103     public void testHealthdDomain() throws DeviceNotAvailableException {
   1104         assertDomainZeroOrOne("u:r:healthd:s0", "/system/bin/healthd");
   1105     }
   1106 
   1107     /* Servicemanager is always there */
   1108     @CddTest(requirement="9.7")
   1109     public void testServicemanagerDomain() throws DeviceNotAvailableException {
   1110         assertDomainOne("u:r:servicemanager:s0", "/system/bin/servicemanager");
   1111     }
   1112 
   1113     /* Vold is always there */
   1114     @CddTest(requirement="9.7")
   1115     public void testVoldDomain() throws DeviceNotAvailableException {
   1116         assertDomainOne("u:r:vold:s0", "/system/bin/vold");
   1117     }
   1118 
   1119     /* netd is always there */
   1120     @CddTest(requirement="9.7")
   1121     public void testNetdDomain() throws DeviceNotAvailableException {
   1122         assertDomainN("u:r:netd:s0", "/system/bin/netd", "/system/bin/iptables-restore", "/system/bin/ip6tables-restore");
   1123     }
   1124 
   1125     /* Surface flinger is always there */
   1126     @CddTest(requirement="9.7")
   1127     public void testSurfaceflingerDomain() throws DeviceNotAvailableException {
   1128         assertDomainOne("u:r:surfaceflinger:s0", "/system/bin/surfaceflinger");
   1129     }
   1130 
   1131     /* Zygote is always running */
   1132     @CddTest(requirement="9.7")
   1133     public void testZygoteDomain() throws DeviceNotAvailableException {
   1134         assertDomainN("u:r:zygote:s0", "zygote", "zygote64");
   1135     }
   1136 
   1137     /* Checks drmserver for devices that require it */
   1138     @CddTest(requirement="9.7")
   1139     public void testDrmServerDomain() throws DeviceNotAvailableException {
   1140         assertDomainZeroOrOne("u:r:drmserver:s0", "/system/bin/drmserver");
   1141     }
   1142 
   1143     /* Installd is always running */
   1144     @CddTest(requirement="9.7")
   1145     public void testInstalldDomain() throws DeviceNotAvailableException {
   1146         assertDomainOne("u:r:installd:s0", "/system/bin/installd");
   1147     }
   1148 
   1149     /* keystore is always running */
   1150     @CddTest(requirement="9.7")
   1151     public void testKeystoreDomain() throws DeviceNotAvailableException {
   1152         assertDomainOne("u:r:keystore:s0", "/system/bin/keystore");
   1153     }
   1154 
   1155     /* System server better be running :-P */
   1156     @CddTest(requirement="9.7")
   1157     public void testSystemServerDomain() throws DeviceNotAvailableException {
   1158         assertDomainOne("u:r:system_server:s0", "system_server");
   1159     }
   1160 
   1161     /* Watchdogd may or may not be there */
   1162     @CddTest(requirement="9.7")
   1163     public void testWatchdogdDomain() throws DeviceNotAvailableException {
   1164         assertDomainZeroOrOne("u:r:watchdogd:s0", "/sbin/watchdogd");
   1165     }
   1166 
   1167     /* logd may or may not be there */
   1168     @CddTest(requirement="9.7")
   1169     public void testLogdDomain() throws DeviceNotAvailableException {
   1170         assertDomainZeroOrOne("u:r:logd:s0", "/system/bin/logd");
   1171     }
   1172 
   1173     /* lmkd may or may not be there */
   1174     @CddTest(requirement="9.7")
   1175     public void testLmkdDomain() throws DeviceNotAvailableException {
   1176         assertDomainZeroOrOne("u:r:lmkd:s0", "/system/bin/lmkd");
   1177     }
   1178 
   1179     /* Wifi may be off so cardinality of 0 or 1 is ok */
   1180     @CddTest(requirement="9.7")
   1181     public void testWpaDomain() throws DeviceNotAvailableException {
   1182         assertDomainZeroOrOne("u:r:wpa:s0", "/system/bin/wpa_supplicant");
   1183     }
   1184 
   1185     /*
   1186      * Nothing should be running in this domain, cardinality test is all thats
   1187      * needed
   1188      */
   1189     @CddTest(requirement="9.7")
   1190     public void testInitShellDomain() throws DeviceNotAvailableException {
   1191         assertDomainEmpty("u:r:init_shell:s0");
   1192     }
   1193 
   1194     /*
   1195      * Nothing should be running in this domain, cardinality test is all thats
   1196      * needed
   1197      */
   1198     @CddTest(requirement="9.7")
   1199     public void testRecoveryDomain() throws DeviceNotAvailableException {
   1200         assertDomainEmpty("u:r:recovery:s0");
   1201     }
   1202 
   1203     /*
   1204      * Nothing should be running in this domain, cardinality test is all thats
   1205      * needed
   1206      */
   1207     @CddTest(requirement="9.7")
   1208     @RestrictedBuildTest
   1209     public void testSuDomain() throws DeviceNotAvailableException {
   1210         assertDomainEmpty("u:r:su:s0");
   1211     }
   1212 
   1213     /*
   1214      * All kthreads should be in kernel context.
   1215      */
   1216     @CddTest(requirement="9.7")
   1217     public void testKernelDomain() throws DeviceNotAvailableException {
   1218         String domain = "u:r:kernel:s0";
   1219         List<ProcessDetails> procs = ProcessDetails.getProcMap(mDevice).get(domain);
   1220         if (procs != null) {
   1221             for (ProcessDetails p : procs) {
   1222                 assertTrue("Non Kernel thread \"" + p + "\" found!", p.isKernel());
   1223             }
   1224         }
   1225     }
   1226 
   1227     private static class ProcessDetails {
   1228         public String label;
   1229         public String user;
   1230         public int pid;
   1231         public int ppid;
   1232         public String procTitle;
   1233 
   1234         private static HashMap<String, ArrayList<ProcessDetails>> procMap;
   1235         private static HashMap<String, ArrayList<ProcessDetails>> exeMap;
   1236         private static int kernelParentThreadpid = -1;
   1237 
   1238         ProcessDetails(String label, String user, int pid, int ppid, String procTitle) {
   1239             this.label = label;
   1240             this.user = user;
   1241             this.pid = pid;
   1242             this.ppid = ppid;
   1243             this.procTitle = procTitle;
   1244         }
   1245 
   1246         @Override
   1247         public String toString() {
   1248             return "label: " + label
   1249                     + " user: " + user
   1250                     + " pid: " + pid
   1251                     + " ppid: " + ppid
   1252                     + " cmd: " + procTitle;
   1253         }
   1254 
   1255 
   1256         private static void createProcMap(ITestDevice tDevice) throws DeviceNotAvailableException {
   1257 
   1258             /* take the output of a ps -Z to do our analysis */
   1259             CollectingOutputReceiver psOut = new CollectingOutputReceiver();
   1260             // TODO: remove "toybox" below and just run "ps"
   1261             tDevice.executeShellCommand("toybox ps -A -o label,user,pid,ppid,cmdline", psOut);
   1262             String psOutString = psOut.getOutput();
   1263             Pattern p = Pattern.compile(
   1264                     "^([\\w_:]+)\\s+([\\w_]+)\\s+(\\d+)\\s+(\\d+)\\s+(\\p{Graph}+)(\\s\\p{Graph}+)*\\s*$",
   1265                     Pattern.MULTILINE);
   1266             Matcher m = p.matcher(psOutString);
   1267             procMap = new HashMap<String, ArrayList<ProcessDetails>>();
   1268             exeMap = new HashMap<String, ArrayList<ProcessDetails>>();
   1269             while(m.find()) {
   1270                 String domainLabel = m.group(1);
   1271                 String user = m.group(2);
   1272                 int pid = Integer.parseInt(m.group(3));
   1273                 int ppid = Integer.parseInt(m.group(4));
   1274                 String procTitle = m.group(5);
   1275                 ProcessDetails proc = new ProcessDetails(domainLabel, user, pid, ppid, procTitle);
   1276                 if (procMap.get(domainLabel) == null) {
   1277                     procMap.put(domainLabel, new ArrayList<ProcessDetails>());
   1278                 }
   1279                 procMap.get(domainLabel).add(proc);
   1280                 if (procTitle.equals("[kthreadd]") && ppid == 0) {
   1281                     kernelParentThreadpid = pid;
   1282                 }
   1283                 if (exeMap.get(procTitle) == null) {
   1284                     exeMap.put(procTitle, new ArrayList<ProcessDetails>());
   1285                 }
   1286                 exeMap.get(procTitle).add(proc);
   1287             }
   1288         }
   1289 
   1290         public static HashMap<String, ArrayList<ProcessDetails>> getProcMap(ITestDevice tDevice)
   1291                 throws DeviceNotAvailableException{
   1292             if (procMap == null) {
   1293                 createProcMap(tDevice);
   1294             }
   1295             return procMap;
   1296         }
   1297 
   1298         public static HashMap<String, ArrayList<ProcessDetails>> getExeMap(ITestDevice tDevice)
   1299                 throws DeviceNotAvailableException{
   1300             if (exeMap == null) {
   1301                 createProcMap(tDevice);
   1302             }
   1303             return exeMap;
   1304         }
   1305 
   1306         public boolean isKernel() {
   1307             return (pid == kernelParentThreadpid || ppid == kernelParentThreadpid);
   1308         }
   1309     }
   1310 }
   1311