[autotest] audio_facade: Makes failure in recording audio more obvious

When there is something wrong with capture device, CRAS automatically
falls back to using dummy capture device, which captures all zeros.
In autotest, we find this when checking the recorded files and see RMS
is too small.

Now we check whether the recorded files contains all zeros in
audio_facade_native, and raise the exception in audio_facade_adapter so
user can see the failure reason easily.

BUG=chromium:725550
TEST=run audio_AudioBasicExternalMicrophone test on cyan with image
9579.0 and see the error messages of recording.

Change-Id: I6695812f801924f33beeb0aa51a98cc9fc7d9db3
Reviewed-on: https://chromium-review.googlesource.com/513023
Commit-Ready: Cheng-Yi Chiang <cychiang@chromium.org>
Tested-by: Cheng-Yi Chiang <cychiang@chromium.org>
Reviewed-by: Hsu Wei-Cheng <mojahsu@chromium.org>
diff --git a/client/cros/multimedia/audio_facade_native.py b/client/cros/multimedia/audio_facade_native.py
index a894b7e..461ab4e 100644
--- a/client/cros/multimedia/audio_facade_native.py
+++ b/client/cros/multimedia/audio_facade_native.py
@@ -7,6 +7,7 @@
 import functools
 import glob
 import logging
+import numpy as np
 import os
 import tempfile
 
@@ -40,6 +41,16 @@
     return wrapper
 
 
+def file_contains_all_zeros(path):
+    """Reads a file and checks whether the file contains all zeros."""
+    with open(path) as f:
+        binary = f.read()
+        # Assume data is in 16 bit signed int format. The real format
+        # does not matter though since we only care if there is nonzero data.
+        np_array = np.fromstring(binary, dtype='<i2')
+        return not np.any(np_array)
+
+
 class AudioFacadeNative(object):
     """Facede to access the audio-related functionality.
 
@@ -230,9 +241,14 @@
         """Stops recording an audio file.
 
         @returns: The path to the recorded file.
+                  None if capture device is not functional.
 
         """
         self._recorder.stop()
+        if file_contains_all_zeros(self._recorder.file_path):
+            logging.error('Recorded file contains all zeros. '
+                          'Capture device is not functional')
+            return None
         return self._recorder.file_path
 
 
diff --git a/server/cros/multimedia/audio_facade_adapter.py b/server/cros/multimedia/audio_facade_adapter.py
index f1af2f0..1a6a75f 100644
--- a/server/cros/multimedia/audio_facade_adapter.py
+++ b/server/cros/multimedia/audio_facade_adapter.py
@@ -8,6 +8,11 @@
 import tempfile
 
 
+class AudioFacadeError(Exception):
+    """Errors in audio facade."""
+    pass
+
+
 class AudioFacadeRemoteAdapter(object):
     """AudioFacadeRemoteAdapter is an adapter to remotely control DUT audio.
 
@@ -98,8 +103,14 @@
 
         @returns: the path to the recorded file on DUT.
 
+        @raises: AudioFacadeError if recorded path is None
         """
-        return self._audio_proxy.stop_recording()
+        path = self._audio_proxy.stop_recording()
+        if not path:
+            raise AudioFacadeError(
+                    'Recording does not work on DUT. '
+                    'Suggest checking messages on DUT')
+        return path
 
 
     def get_recorded_file(self, remote_path, local_path):