Merge "Split SELinux neverallow rules test." into lmp-sprout-dev
diff --git a/build/test_host_java_library.mk b/build/test_host_java_library.mk
index baf9e75..8e071e4 100644
--- a/build/test_host_java_library.mk
+++ b/build/test_host_java_library.mk
@@ -21,14 +21,18 @@
 
 cts_library_xml := $(CTS_TESTCASES_OUT)/$(LOCAL_MODULE).xml
 
-$(cts_library_xml): PRIVATE_PATH := $(LOCAL_PATH)/src
+cts_src_dirs := $(LOCAL_PATH)/src
+cts_src_dirs += $(sort $(dir $(LOCAL_GENERATED_SOURCES)))
+cts_src_dirs := $(addprefix -s , $(cts_src_dirs))
+
+$(cts_library_xml): PRIVATE_SRC_DIRS := $(cts_src_dirs)
 $(cts_library_xml): PRIVATE_TEST_PACKAGE := $(LOCAL_CTS_TEST_PACKAGE)
 $(cts_library_xml): PRIVATE_LIBRARY := $(LOCAL_MODULE)
 $(cts_library_xml): PRIVATE_JAR_PATH := $(LOCAL_MODULE).jar
 $(cts_library_xml): $(HOST_OUT_JAVA_LIBRARIES)/$(LOCAL_MODULE).jar $(CTS_EXPECTATIONS) $(CTS_UNSUPPORTED_ABIS) $(CTS_JAVA_TEST_SCANNER_DOCLET) $(CTS_JAVA_TEST_SCANNER) $(CTS_XML_GENERATOR)
 	$(hide) echo Generating test description for host library $(PRIVATE_LIBRARY)
 	$(hide) mkdir -p $(CTS_TESTCASES_OUT)
-	$(hide) $(CTS_JAVA_TEST_SCANNER) -s $(PRIVATE_PATH) \
+	$(hide) $(CTS_JAVA_TEST_SCANNER) $(PRIVATE_SRC_DIRS) \
 						-d $(CTS_JAVA_TEST_SCANNER_DOCLET) | \
 			$(CTS_XML_GENERATOR) -t hostSideOnly \
 						-j $(PRIVATE_JAR_PATH) \
diff --git a/build/test_package.mk b/build/test_package.mk
index 353ae07..72972b2 100644
--- a/build/test_package.mk
+++ b/build/test_package.mk
@@ -28,12 +28,16 @@
 cts_package_apk := $(CTS_TESTCASES_OUT)/$(LOCAL_PACKAGE_NAME).apk
 cts_package_xml := $(CTS_TESTCASES_OUT)/$(LOCAL_PACKAGE_NAME).xml
 
+cts_src_dirs := $(LOCAL_PATH)
+cts_src_dirs += $(sort $(dir $(LOCAL_GENERATED_SOURCES)))
+cts_src_dirs := $(addprefix -s , $(cts_src_dirs))
+
 $(cts_package_apk): PRIVATE_PACKAGE := $(LOCAL_PACKAGE_NAME)
 $(cts_package_apk): $(call intermediates-dir-for,APPS,$(LOCAL_PACKAGE_NAME))/package.apk | $(ACP)
 	$(hide) mkdir -p $(CTS_TESTCASES_OUT)
 	$(hide) $(ACP) -fp $(call intermediates-dir-for,APPS,$(PRIVATE_PACKAGE))/package.apk $@
 
-$(cts_package_xml): PRIVATE_PATH := $(LOCAL_PATH)
+$(cts_package_xml): PRIVATE_SRC_DIRS := $(cts_src_dirs)
 $(cts_package_xml): PRIVATE_INSTRUMENTATION := $(LOCAL_INSTRUMENTATION_FOR)
 $(cts_package_xml): PRIVATE_PACKAGE := $(LOCAL_PACKAGE_NAME)
 ifneq ($(filter cts/suite/cts/%, $(LOCAL_PATH)),)
@@ -48,7 +52,7 @@
 	$(hide) echo Generating test description for java package $(PRIVATE_PACKAGE)
 	$(hide) mkdir -p $(CTS_TESTCASES_OUT)
 	$(hide) $(CTS_JAVA_TEST_SCANNER) \
-						-s $(PRIVATE_PATH) \
+						$(PRIVATE_SRC_DIRS) \
 						-d $(CTS_JAVA_TEST_SCANNER_DOCLET) | \
 			$(CTS_XML_GENERATOR) \
 						-t $(PRIVATE_TEST_TYPE) \
diff --git a/build/test_uiautomator.mk b/build/test_uiautomator.mk
index 5e2f07a..085d672 100644
--- a/build/test_uiautomator.mk
+++ b/build/test_uiautomator.mk
@@ -24,12 +24,16 @@
 cts_library_xml := $(CTS_TESTCASES_OUT)/$(LOCAL_MODULE).xml 
 cts_library_jar := $(CTS_TESTCASES_OUT)/$(LOCAL_MODULE).jar
 
+cts_src_dirs := $(LOCAL_PATH)/src
+cts_src_dirs += $(sort $(dir $(LOCAL_GENERATED_SOURCES)))
+cts_src_dirs := $(addprefix -s , $(cts_src_dirs))
+
 $(cts_library_jar): PRIVATE_MODULE := $(LOCAL_MODULE)
 $(cts_library_jar): $(call intermediates-dir-for,JAVA_LIBRARIES,$(LOCAL_MODULE))/javalib.jar | $(ACP)
 	$(hide) mkdir -p $(CTS_TESTCASES_OUT)
 	$(hide) $(ACP) -fp $(call intermediates-dir-for,JAVA_LIBRARIES,$(PRIVATE_MODULE))/javalib.jar $@
 
-$(cts_library_xml): PRIVATE_PATH := $(LOCAL_PATH)/src
+$(cts_library_xml): PRIVATE_SRC_DIRS := $(cts_src_dirs)
 $(cts_library_xml): PRIVATE_TEST_APP_PACKAGE := $(LOCAL_CTS_TEST_APP_PACKAGE)
 $(cts_library_xml): PRIVATE_TEST_PACKAGE := $(LOCAL_CTS_TEST_PACKAGE)
 $(cts_library_xml): PRIVATE_TEST_APK := $(LOCAL_CTS_TEST_APK)
@@ -38,7 +42,7 @@
 $(cts_library_xml): $(call intermediates-dir-for,JAVA_LIBRARIES,$(LOCAL_MODULE))/javalib.jar $(CTS_EXPECTATIONS) $(CTS_UNSUPPORTED_ABIS) $(CTS_JAVA_TEST_SCANNER_DOCLET) $(CTS_JAVA_TEST_SCANNER) $(CTS_XML_GENERATOR)
 	$(hide) echo Generating test description for uiautomator library $(PRIVATE_LIBRARY)
 	$(hide) mkdir -p $(CTS_TESTCASES_OUT)
-	$(hide) $(CTS_JAVA_TEST_SCANNER) -s $(PRIVATE_PATH) \
+	$(hide) $(CTS_JAVA_TEST_SCANNER) $(PRIVATE_SRC_DIRS) \
 						-d $(CTS_JAVA_TEST_SCANNER_DOCLET) | \
 			$(CTS_XML_GENERATOR) -t uiAutomator \
 						-i $(PRIVATE_TEST_APK) \
diff --git a/hostsidetests/security/Android.mk b/hostsidetests/security/Android.mk
index 50e0226..0c976a3 100644
--- a/hostsidetests/security/Android.mk
+++ b/hostsidetests/security/Android.mk
@@ -23,12 +23,26 @@
 # Must match the package name in CtsTestCaseList.mk
 LOCAL_MODULE := CtsSecurityHostTestCases
 
+LOCAL_MODULE_CLASS := JAVA_LIBRARIES
+
 LOCAL_JAVA_LIBRARIES := cts-tradefed tradefed-prebuilt
 
 LOCAL_CTS_TEST_PACKAGE := android.host.security
 
 LOCAL_JAVA_RESOURCE_FILES := $(HOST_OUT_EXECUTABLES)/sepolicy-analyze
-LOCAL_JAVA_RESOURCE_FILES += $(call intermediates-dir-for,ETC,general_sepolicy.conf)/general_sepolicy.conf
+
+selinux_general_policy := $(call intermediates-dir-for,ETC,general_sepolicy.conf)/general_sepolicy.conf
+
+selinux_neverallow_gen := cts/tools/selinux/SELinuxNeverallowTestGen.py
+
+selinux_neverallow_gen_data := cts/tools/selinux/SELinuxNeverallowTestFrame.py
+
+LOCAL_GENERATED_SOURCES := $(call local-generated-sources-dir)/android/cts/security/SELinuxNeverallowRulesTest.java
+
+$(LOCAL_GENERATED_SOURCES) : PRIVATE_SELINUX_GENERAL_POLICY := $(selinux_general_policy)
+$(LOCAL_GENERATED_SOURCES) : $(selinux_neverallow_gen) $(selinux_general_policy) $(selinux_neverallow_gen_data)
+	mkdir -p $(dir $@)
+	$< $(PRIVATE_SELINUX_GENERAL_POLICY) $@
 
 include $(BUILD_CTS_HOST_JAVA_LIBRARY)
 
diff --git a/hostsidetests/security/src/android/cts/security/SELinuxHostTest.java b/hostsidetests/security/src/android/cts/security/SELinuxHostTest.java
index 1a88ea0..96845b1 100644
--- a/hostsidetests/security/src/android/cts/security/SELinuxHostTest.java
+++ b/hostsidetests/security/src/android/cts/security/SELinuxHostTest.java
@@ -105,32 +105,4 @@
         assertTrue("The following SELinux domains were found to be in permissive mode:\n"
                    + errorString, errorString.length() == 0);
     }
-
-    /**
-     * Checks the policy running on-device against a set of neverallow rules
-     *
-     * @throws Exception
-     */
-    public void testNeverallowRules() throws Exception {
-
-        File neverallowRules = copyResourceToTempFile("/general_sepolicy.conf");
-
-        /* run sepolicy-analyze neverallow check on policy file using given neverallow rules */
-        ProcessBuilder pb = new ProcessBuilder(sepolicyAnalyze.getAbsolutePath(),
-                    devicePolicyFile.getAbsolutePath(), "neverallow", "-f",
-                    neverallowRules.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 following errors were encountered when validating the SELinux"
-                   + "neverallow rules:\n" + errorString, errorString.length() == 0);
-    }
 }
diff --git a/tools/cts-java-scanner/src/com/android/cts/javascanner/CtsJavaScanner.java b/tools/cts-java-scanner/src/com/android/cts/javascanner/CtsJavaScanner.java
index a843fc6..fc774e9 100644
--- a/tools/cts-java-scanner/src/com/android/cts/javascanner/CtsJavaScanner.java
+++ b/tools/cts-java-scanner/src/com/android/cts/javascanner/CtsJavaScanner.java
@@ -16,7 +16,9 @@
 package com.android.cts.javascanner;
 
 import java.io.File;
+import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.List;
 
 /**
  * Class that searches a source directory for native gTests and outputs a
@@ -31,12 +33,12 @@
     }
 
     public static void main(String[] args) throws Exception {
-        File sourceDir = null;
+        List<File> sourceDirs = new ArrayList<File>();
         File docletPath = null;
 
         for (int i = 0; i < args.length; i++) {
             if ("-s".equals(args[i])) {
-                sourceDir = new File(getArg(args, ++i, "Missing value for source directory"));
+                sourceDirs.add(new File(getArg(args, ++i, "Missing value for source directory")));
             } else if ("-d".equals(args[i])) {
                 docletPath = new File(getArg(args, ++i, "Missing value for docletPath"));
             } else {
@@ -45,7 +47,7 @@
             }
         }
 
-        if (sourceDir == null) {
+        if (sourceDirs.isEmpty()) {
             System.err.println("Source directory is required");
             usage(args);
         }
@@ -55,7 +57,7 @@
             usage(args);
         }
 
-        DocletRunner runner = new DocletRunner(sourceDir, docletPath);
+        DocletRunner runner = new DocletRunner(sourceDirs, docletPath);
         System.exit(runner.runJavaDoc());
     }
 
diff --git a/tools/cts-java-scanner/src/com/android/cts/javascanner/DocletRunner.java b/tools/cts-java-scanner/src/com/android/cts/javascanner/DocletRunner.java
index 06951b9..94761fb 100644
--- a/tools/cts-java-scanner/src/com/android/cts/javascanner/DocletRunner.java
+++ b/tools/cts-java-scanner/src/com/android/cts/javascanner/DocletRunner.java
@@ -25,11 +25,11 @@
 
 class DocletRunner {
 
-    private final File mSourceDir;
+    private final List<File> mSourceDirs;
     private final File mDocletPath;
 
-    DocletRunner(File sourceDir, File docletPath) {
-        mSourceDir = sourceDir;
+    DocletRunner(List<File> sourceDirs, File docletPath) {
+        mSourceDirs = sourceDirs;
         mDocletPath = docletPath;
     }
 
@@ -41,10 +41,12 @@
         args.add("-docletpath");
         args.add(mDocletPath.toString());
         args.add("-sourcepath");
-        args.add(getSourcePath(mSourceDir));
+        args.add(getSourcePath(mSourceDirs));
         args.add("-classpath");
         args.add(getClassPath());
-        args.addAll(getSourceFiles(mSourceDir));
+        for (File sourceDir : mSourceDirs) {
+            args.addAll(getSourceFiles(sourceDir));
+        }
 
 
         // NOTE: We redirect the error stream to make sure the child process
@@ -67,7 +69,7 @@
         return process.waitFor();
     }
 
-    private String getSourcePath(File sourceDir) {
+    private String getSourcePath(List<File> sourceDirs) {
         List<String> sourcePath = new ArrayList<String>();
         sourcePath.add("./frameworks/base/core/java");
         sourcePath.add("./frameworks/base/test-runner/src");
@@ -77,7 +79,9 @@
         sourcePath.add("./cts/tests/src");
         sourcePath.add("./cts/libs/commonutil/src");
         sourcePath.add("./cts/libs/deviceutil/src");
-        sourcePath.add(sourceDir.toString());
+        for (File sourceDir : sourceDirs) {
+            sourcePath.add(sourceDir.toString());
+        }
         return join(sourcePath, ":");
     }
 
diff --git a/tools/selinux/SELinuxNeverallowTestFrame.py b/tools/selinux/SELinuxNeverallowTestFrame.py
new file mode 100644
index 0000000..932014a
--- /dev/null
+++ b/tools/selinux/SELinuxNeverallowTestFrame.py
@@ -0,0 +1,106 @@
+#!/usr/bin/python
+
+src_header = """/*
+ * 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.cts.security;
+
+import com.android.cts.tradefed.build.CtsBuildHelper;
+import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.testtype.DeviceTestCase;
+import com.android.tradefed.testtype.IBuildReceiver;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.FileOutputStream;
+import java.lang.String;
+import java.net.URL;
+import java.util.Scanner;
+
+/**
+ * Neverallow Rules SELinux tests.
+ */
+public class SELinuxNeverallowRulesTest extends DeviceTestCase {
+    private File sepolicyAnalyze;
+    private File devicePolicyFile;
+
+    /**
+     * A reference to the device under test.
+     */
+    private ITestDevice mDevice;
+
+    private File copyResourceToTempFile(String resName) throws IOException {
+        InputStream is = this.getClass().getResourceAsStream(resName);
+        File tempFile = File.createTempFile("SELinuxHostTest", ".tmp");
+        FileOutputStream os = new FileOutputStream(tempFile);
+        int rByte = 0;
+        while ((rByte = is.read()) != -1) {
+            os.write(rByte);
+        }
+        os.flush();
+        os.close();
+        tempFile.deleteOnExit();
+        return tempFile;
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mDevice = getDevice();
+
+        /* retrieve the sepolicy-analyze executable from jar */
+        sepolicyAnalyze = copyResourceToTempFile("/sepolicy-analyze");
+        sepolicyAnalyze.setExecutable(true);
+
+        /* obtain sepolicy file from running device */
+        devicePolicyFile = File.createTempFile("sepolicy", ".tmp");
+        devicePolicyFile.deleteOnExit();
+        mDevice.executeAdbCommand("pull", "/sys/fs/selinux/policy",
+                devicePolicyFile.getAbsolutePath());
+    }
+"""
+src_body = ""
+src_footer = """}
+"""
+
+src_method = """
+    public void testNeverallowRules() throws Exception {
+        String neverallowRule = "$NEVERALLOW_RULE_HERE$";
+
+        /* run sepolicy-analyze neverallow check on policy file using given neverallow rules */
+        ProcessBuilder pb = new ProcessBuilder(sepolicyAnalyze.getAbsolutePath(),
+                devicePolicyFile.getAbsolutePath(), "neverallow", "-n",
+                neverallowRule);
+        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 errors were encountered when validating the SELinux"
+                   + "neverallow rule:\\n" + neverallowRule + "\\n" + errorString,
+                   errorString.length() == 0);
+    }
+"""
diff --git a/tools/selinux/SELinuxNeverallowTestGen.py b/tools/selinux/SELinuxNeverallowTestGen.py
new file mode 100755
index 0000000..9cb1e24
--- /dev/null
+++ b/tools/selinux/SELinuxNeverallowTestGen.py
@@ -0,0 +1,53 @@
+#!/usr/bin/python
+
+import re
+import sys
+import SELinuxNeverallowTestFrame
+
+usage = "Usage: ./gen_SELinux_CTS_neverallows.py <input policy file> <output cts java source>"
+
+# extract_neverallow_rules - takes an intermediate policy file and pulls out the
+# neverallow rules by taking all of the non-commented text between the 'neverallow'
+# keyword and a terminating ';'
+# returns: a list of strings representing these rules
+def extract_neverallow_rules(policy_file):
+    with open(policy_file, 'r') as in_file:
+        policy_str = in_file.read()
+        # remove comments
+        no_comments = re.sub(r'#.+?$', r'', policy_str, flags = re.M)
+        # match neverallow rules
+        return re.findall(r'(^neverallow\s.+?;)', no_comments, flags = re.M |re.S);
+
+# neverallow_rule_to_test - takes a neverallow statement and transforms it into
+# the output necessary to form a cts unit test in a java source file.
+# returns: a string representing a generic test method based on this rule.
+def neverallow_rule_to_test(neverallow_rule, test_num):
+    squashed_neverallow = neverallow_rule.replace("\n", " ")
+    method  = SELinuxNeverallowTestFrame.src_method
+    method = method.replace("testNeverallowRules()",
+        "testNeverallowRules" + str(test_num) + "()")
+    return method.replace("$NEVERALLOW_RULE_HERE$", squashed_neverallow)
+
+if __name__ == "__main__":
+    # check usage
+    if len(sys.argv) != 3:
+        print usage
+        exit()
+    input_file = sys.argv[1]
+    output_file = sys.argv[2]
+
+    src_header = SELinuxNeverallowTestFrame.src_header
+    src_body = SELinuxNeverallowTestFrame.src_body
+    src_footer = SELinuxNeverallowTestFrame.src_footer
+
+    # grab the neverallow rules from the policy file and transform into tests
+    neverallow_rules = extract_neverallow_rules(input_file)
+    i = 0
+    for rule in neverallow_rules:
+        src_body += neverallow_rule_to_test(rule, i)
+        i += 1
+
+    with open(output_file, 'w') as out_file:
+        out_file.write(src_header)
+        out_file.write(src_body)
+        out_file.write(src_footer)