Add support for playing audio during bootanimation

The bootanimation daemon will play 16 bit WAV files contained in bootanimation.zip
For this to work, the bootanimation.zip must contain an audio_conf.txt file,
which contains parameters to be used for the tinyalsa pcm_open call
as well as mixer parameters to set before attempting to play the sound.

If the bootanimation finds an audio_conf.txt file, then it will look for a file named
"audio.wav" in each of the part subdirectories. If audio.wav is found, it will play that
WAV file starting at the beginning of that part.

The code for this is based on the tinyplay utility in tinyalsa.

The audio_conf.txt and must begin with the following header:

card=<ALSA card number>
device=<ALSA device number>
period_size=<period size>
period_count=<period count>

This header is followed by zero or more mixer settings, each with the format:
mixer "<name>" = <value list>
Since mixer names can contain spaces, the name must be enclosed in double quotes.
The values in the value list can be integers, booleans (represented by 0 or 1)
or strings for enum values.

Finally I should mention that this change is not the right approach.
Instead of going straight to ALSA we should be using the mediaserver instead.
But mediaserver isn't ready in time due to interactions with the system server, and there
isn't time to fix this for the current release. We need to fix that for the next one.

Bug: 17674304

Change-Id: Ic391ade61c941d0a24f4d64fe005ac9375a23fa9
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index 08923119..b2474f2 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#define LOG_NDEBUG 0
 #define LOG_TAG "BootAnimation"
 
 #include <stdint.h>
@@ -30,7 +31,6 @@
 #include <utils/Atomic.h>
 #include <utils/Errors.h>
 #include <utils/Log.h>
-#include <utils/threads.h>
 
 #include <ui/PixelFormat.h>
 #include <ui/Rect.h>
@@ -50,6 +50,7 @@
 #include <EGL/eglext.h>
 
 #include "BootAnimation.h"
+#include "AudioPlayer.h"
 
 #define OEM_BOOTANIMATION_FILE "/oem/media/bootanimation.zip"
 #define SYSTEM_BOOTANIMATION_FILE "/system/media/bootanimation.zip"
@@ -99,6 +100,9 @@
     // might be blocked on a condition variable that will never be updated.
     kill( getpid(), SIGKILL );
     requestExit();
+    if (mAudioPlayer != NULL) {
+        mAudioPlayer->requestExit();
+    }
 }
 
 status_t BootAnimation::initTexture(Texture* texture, AssetManager& assets,
@@ -394,6 +398,9 @@
     int exitnow = atoi(value);
     if (exitnow) {
         requestExit();
+        if (mAudioPlayer != NULL) {
+            mAudioPlayer->requestExit();
+        }
     }
 }
 
@@ -422,26 +429,45 @@
     return true;
 }
 
+bool BootAnimation::readFile(const char* name, String8& outString)
+{
+    ZipEntryRO entry = mZip->findEntryByName(name);
+    ALOGE_IF(!entry, "couldn't find %s", name);
+    if (!entry) {
+        return false;
+    }
+
+    FileMap* entryMap = mZip->createEntryFileMap(entry);
+    mZip->releaseEntry(entry);
+    ALOGE_IF(!entryMap, "entryMap is null");
+    if (!entryMap) {
+        return false;
+    }
+
+    outString.setTo((char const*)entryMap->getDataPtr(), entryMap->getDataLength());
+    entryMap->release();
+    return true;
+}
+
 bool BootAnimation::movie()
 {
-    ZipEntryRO desc = mZip->findEntryByName("desc.txt");
-    ALOGE_IF(!desc, "couldn't find desc.txt");
-    if (!desc) {
+    String8 desString;
+
+    if (!readFile("desc.txt", desString)) {
         return false;
     }
-
-    FileMap* descMap = mZip->createEntryFileMap(desc);
-    mZip->releaseEntry(desc);
-    ALOGE_IF(!descMap, "descMap is null");
-    if (!descMap) {
-        return false;
-    }
-
-    String8 desString((char const*)descMap->getDataPtr(),
-            descMap->getDataLength());
-    descMap->release();
     char const* s = desString.string();
 
+    // Create and initialize an AudioPlayer if we have an audio_conf.txt file
+    String8 audioConf;
+    if (readFile("audio_conf.txt", audioConf)) {
+        mAudioPlayer = new AudioPlayer;
+        if (!mAudioPlayer->init(audioConf.string())) {
+            ALOGE("mAudioPlayer.init failed");
+            mAudioPlayer = NULL;
+        }
+    }
+
     Animation animation;
 
     // Parse the description file
@@ -468,6 +494,7 @@
             part.count = count;
             part.pause = pause;
             part.path = path;
+            part.audioFile = NULL;
             if (!parseColor(color, part.backgroundColor)) {
                 ALOGE("> invalid color '#%s'", color);
                 part.backgroundColor[0] = 0.0f;
@@ -508,11 +535,16 @@
                         if (method == ZipFileRO::kCompressStored) {
                             FileMap* map = mZip->createEntryFileMap(entry);
                             if (map) {
-                                Animation::Frame frame;
-                                frame.name = leaf;
-                                frame.map = map;
                                 Animation::Part& part(animation.parts.editItemAt(j));
-                                part.frames.add(frame);
+                                if (leaf == "audio.wav") {
+                                    // a part may have at most one audio file
+                                    part.audioFile = map;
+                                } else {
+                                    Animation::Frame frame;
+                                    frame.name = leaf;
+                                    frame.map = map;
+                                    part.frames.add(frame);
+                                }
                             }
                         }
                     }
@@ -559,6 +591,11 @@
             if(exitPending() && !part.playUntilComplete)
                 break;
 
+            // only play audio file the first time we animate the part
+            if (r == 0 && mAudioPlayer != NULL && part.audioFile) {
+                mAudioPlayer->playFile(part.audioFile);
+            }
+
             glClearColor(
                     part.backgroundColor[0],
                     part.backgroundColor[1],