sensors hal fuzz test.

Test: manual
Change-Id: If3e769ffc384b4ad902470cd49218369c7fe2aca
diff --git a/testcases/hal/common/fuzz/Android.hal_fuzzer.mk b/testcases/hal/common/fuzz/Android.hal_fuzzer.mk
index d48a237..8262c29 100644
--- a/testcases/hal/common/fuzz/Android.hal_fuzzer.mk
+++ b/testcases/hal/common/fuzz/Android.hal_fuzzer.mk
@@ -14,6 +14,8 @@
 # limitations under the License.
 #
 
+include $(CLEAR_VARS)
+
 # TODO(trong): enable for mips and x86.
 ifeq (,$(findstring mips, $(TARGET_ARCH)))
 ifeq (,$(findstring x86, $(TARGET_ARCH)))
@@ -33,6 +35,7 @@
 LOCAL_SHARED_LIBRARIES := \
     $(module_shared_libraries) \
     libutils \
+    libhidl \
     libhardware \
 
 LOCAL_ARM_MODE := arm
diff --git a/testcases/hal/sensors/Android.mk b/testcases/hal/sensors/Android.mk
new file mode 100644
index 0000000..f9e3276
--- /dev/null
+++ b/testcases/hal/sensors/Android.mk
@@ -0,0 +1,19 @@
+#
+# Copyright (C) 2016 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.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+include $(call all-subdir-makefiles)
diff --git a/testcases/hal/sensors/__init__.py b/testcases/hal/sensors/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/testcases/hal/sensors/__init__.py
diff --git a/testcases/hal/sensors/fuzz/Android.mk b/testcases/hal/sensors/fuzz/Android.mk
new file mode 100644
index 0000000..379ff65
--- /dev/null
+++ b/testcases/hal/sensors/fuzz/Android.mk
@@ -0,0 +1,34 @@
+#
+# Copyright (C) 2016 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.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+module_name := ISensorsFlush_fuzzer
+module_src_files := ISensorsFlush_fuzzer.cpp
+module_shared_libraries := android.hardware.sensors@1.0
+include test/vts/testcases/hal/common/fuzz/Android.hal_fuzzer.mk
+
+include $(CLEAR_VARS)
+module_name := ISensorsPoll_fuzzer
+module_src_files := ISensorsPoll_fuzzer.cpp
+module_shared_libraries := android.hardware.sensors@1.0
+include test/vts/testcases/hal/common/fuzz/Android.hal_fuzzer.mk
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := ISensorsFuzzTest
+VTS_CONFIG_SRC_DIR := testcases/hal/sensors/fuzz
+include test/vts/tools/build/Android.host_config.mk
diff --git a/testcases/hal/sensors/fuzz/AndroidTest.xml b/testcases/hal/sensors/fuzz/AndroidTest.xml
new file mode 100644
index 0000000..ce29b2e
--- /dev/null
+++ b/testcases/hal/sensors/fuzz/AndroidTest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 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.
+-->
+<configuration description="Config for VTS Sensors HIDL HAL's target-side fuzz test cases">
+    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.VtsFilePusher">
+        <option name="push-group" value="LLVMFuzzerTest.push" />
+    </target_preparer>
+    <target_preparer class="com.android.tradefed.targetprep.VtsPythonVirtualenvPreparer" />
+    <test class="com.android.tradefed.testtype.VtsMultiDeviceTest">
+        <option name="test-module-name" value="ISensorsFuzzTest" />
+        <option name="binary-test-sources" value="
+            hal_fuzz/ISensorsFlush_fuzzer,
+            hal_fuzz/ISensorsPoll_fuzzer
+            "/>
+        <option name="binary-test-type" value="llvmfuzzer" />
+    </test>
+</configuration>
diff --git a/testcases/hal/sensors/fuzz/ISensorsFlush_fuzzer.cpp b/testcases/hal/sensors/fuzz/ISensorsFlush_fuzzer.cpp
new file mode 100644
index 0000000..fab94be
--- /dev/null
+++ b/testcases/hal/sensors/fuzz/ISensorsFlush_fuzzer.cpp
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#include <FuzzerInterface.h>
+#include <android/hardware/sensors/1.0/ISensors.h>
+
+using ::android::hardware::sensors::V1_0::ISensors;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+  static ::android::sp<ISensors> sensors_hal = ISensors::getService("sensors", true);
+  if (sensors_hal == nullptr) {
+    return 0;
+  }
+  if (size < sizeof(int32_t)) {
+    return 0;
+  }
+  int32_t sensorHandle;
+  memcpy(&sensorHandle, data, sizeof(int32_t));
+
+  sensors_hal->flush(sensorHandle);
+  return 0;
+}
diff --git a/testcases/hal/sensors/fuzz/ISensorsPoll_fuzzer.cpp b/testcases/hal/sensors/fuzz/ISensorsPoll_fuzzer.cpp
new file mode 100644
index 0000000..da3f0d7
--- /dev/null
+++ b/testcases/hal/sensors/fuzz/ISensorsPoll_fuzzer.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#include <algorithm>
+
+#include <FuzzerInterface.h>
+#include <android/hardware/sensors/1.0/ISensors.h>
+
+using ::android::hardware::sensors::V1_0::ISensors;
+
+auto _hidl_cb = [](auto x, auto y, auto z){};
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+  static ::android::sp<ISensors> sensors_hal = ISensors::getService("sensors", true);
+  if (sensors_hal == nullptr) {
+    return 0;
+  }
+  if (size < sizeof(int32_t)) {
+    return 0;
+  }
+  int32_t maxCount;
+  memcpy(&maxCount, data, sizeof(int32_t));
+
+  sensors_hal->poll(maxCount, _hidl_cb);
+  return 0;
+}
diff --git a/testcases/template/llvmfuzzer_test/llvmfuzzer_test.py b/testcases/template/llvmfuzzer_test/llvmfuzzer_test.py
index 2dafcf5..eb0e5b9 100644
--- a/testcases/template/llvmfuzzer_test/llvmfuzzer_test.py
+++ b/testcases/template/llvmfuzzer_test/llvmfuzzer_test.py
@@ -76,20 +76,19 @@
         self._dut.adb.push("%s %s" % (push_src, config.FUZZER_TEST_DIR))
         logging.info("Adb pushed: %s", testcase)
 
-    # TODO(trong): save crash-causing inputs to the fuzzer.
     def RunTestcase(self, testcase):
         """Runs the given testcase and asserts the result.
 
         Args:
-            testcase: string, path to executable fuzzer.
+            testcase: string, path to fuzzer executable.
         """
         self.PushFiles(testcase)
-        testcase = testcase.split("/")[-1]
+        fuzzer = testcase.split("/")[-1]
 
-        chmod_cmd = "chmod -R 755 %s" % os.path.join(config.FUZZER_TEST_DIR, testcase)
-        cd_cmd = "cd %s" % os.path.join(config.FUZZER_TEST_DIR)
-        test_cmd = "./%s" % testcase
+        chmod_cmd = "chmod -R 755 %s" % os.path.join(config.FUZZER_TEST_DIR, fuzzer)
+        cd_cmd = "cd %s" % config.FUZZER_TEST_DIR
         ld_path = "LD_LIBRARY_PATH=/data/local/tmp/32:/data/local/tmp/64:$LD_LIBRARY_PATH"
+        test_cmd = "./%s" % fuzzer
 
         cmd = [
             chmod_cmd,
@@ -98,10 +97,35 @@
         logging.info("Executing: %s", cmd)
 
         result = self._shell.Execute(cmd)
-        self.AssertTestResult(result)
+        self.AssertTestResult(fuzzer, result)
+
+    def LogCrashReport(self, fuzzer):
+        """Logs crash-causing fuzzer input.
+
+        Reads the crash report file and logs the contents in format:
+        "\x01\x23\x45\x67\x89\xab\xcd\xef"
+
+        Args:
+            fuzzer: string, name of fuzzer executable.
+        """
+        cmd = "xxd -p %s" % config.FUZZER_TEST_CRASH_REPORT
+
+        # output is string of a hexdump from crash report file.
+        # From the example above, output would be "0123456789abcdef".
+        output = self._shell.Execute(cmd)[const.STDOUT][0]
+        remove_chars = ["\r", "\t", "\n", " "]
+        for char in remove_chars:
+            output = output.replace(char, "")
+
+        crash_report = ""
+        # output is guaranteed to be even in length since its a hexdump.
+        for offset in xrange(0, len(output), 2):
+            crash_report += "\\x%s" % output[offset:offset + 2]
+
+        logging.info("FUZZER_TEST_CRASH_REPORT for %s: '%s'", fuzzer, crash_report)
 
     # TODO(trong): differentiate between crashes and sanitizer rule violations.
-    def AssertTestResult(self, result):
+    def AssertTestResult(self, fuzzer, result):
         """Asserts that testcase finished as expected.
 
         Checks that device is in responsive state. If not, waits for boot
@@ -109,23 +133,25 @@
         returned exit code 0.
 
         Args:
+            fuzzer: string, name of fuzzer executable.
             result: dict([str],[str],[int]), command results from shell.
         """
-        if self._dut.hasBooted():
-            exit_codes = result[const.EXIT_CODE]
-            logging.info("EXIT_CODE: %s", exit_codes)
-            asserts.assertFalse(
-                any(exit_codes), "Test case failed.")
-        else:
+        if not self._dut.hasBooted():
             self._dut.waitForBootCompletion()
-            asserts.fail("Test case left the device in unresponsive state.")
+            asserts.fail("%s left the device in unresponsive state." % fuzzer)
+
+        # Last exit code is the exit code of the fuzzer executable.
+        exit_code = result[const.EXIT_CODE][-1]
+        if exit_code == config.ExitCode.FUZZER_TEST_FAIL:
+            self.LogCrashReport(fuzzer)
+            asserts.fail("%s failed" % fuzzer)
 
     def generateFuzzerTests(self):
         """Runs fuzzer tests."""
         self.runGeneratedTests(
             test_func=self.RunTestcase,
             settings=self._testcases,
-            name_func=lambda x: x.replace('/','_'))
+            name_func=lambda x: x.split("/")[-1])
 
 
 if __name__ == "__main__":
diff --git a/testcases/template/llvmfuzzer_test/llvmfuzzer_test_config.py b/testcases/template/llvmfuzzer_test/llvmfuzzer_test_config.py
index d4757d3..1407f76 100644
--- a/testcases/template/llvmfuzzer_test/llvmfuzzer_test_config.py
+++ b/testcases/template/llvmfuzzer_test/llvmfuzzer_test_config.py
@@ -15,13 +15,21 @@
 # limitations under the License.
 #
 
+class ExitCode(object):
+    """Exit codes for test binaries."""
+    FUZZER_TEST_PASS = 0
+    FUZZER_TEST_FAIL = 77
 
 # Directory on the target where the tests are copied.
 FUZZER_TEST_DIR = "/data/local/tmp/llvmfuzzer_test"
 
+# File used to save crash-causing fuzzer input.
+FUZZER_TEST_CRASH_REPORT = FUZZER_TEST_DIR + "/crash_report"
+
 # Default parameters that will be passed to fuzzer executable.
 FUZZER_PARAMS = {
     "max_len": 64,
-    "max_total_time": 10
+    "max_total_time": 10,
+    "exact_artifact_path": FUZZER_TEST_CRASH_REPORT
 }