cts_audio work: initial code
- volume calibration and THD test case work tuned.
- 59 unit tests pass
- local audio playback / recording works : uses tinyalsa
- host to device ptorocol in host side implemented / tested (with loopback)
- device side recording / playback works for test cases / test_io.xml
- python processing baseline added.
- spectrum algorithm implementated: calculate Transfer Function of device
  over host and check if amplitudes are within margain. Needs parameter tuning
- spectrum test needs some improvements due to the non-flat response from
  ref microphone.
- build is enabled only for linux host except the client code

Change-Id: I8453ac72b6fce7ddbfee7e2cc05207f09f2b3d88
diff --git a/.gitignore b/.gitignore
index ca65e7d..b8a343c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,6 @@
 *.pyc
-*.~*
-bin
+*.*~
+*.py~
+.project
+.cproject
+.classpath
diff --git a/suite/Android.mk b/suite/Android.mk
new file mode 100644
index 0000000..c141484
--- /dev/null
+++ b/suite/Android.mk
@@ -0,0 +1,17 @@
+#
+# Copyright (C) 2012 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 $(call all-subdir-makefiles)
diff --git a/suite/audio_quality/Android.mk b/suite/audio_quality/Android.mk
new file mode 100644
index 0000000..c12e390
--- /dev/null
+++ b/suite/audio_quality/Android.mk
@@ -0,0 +1,35 @@
+#
+# Copyright (C) 2012 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.
+#
+
+# build only for linux
+ifeq ($(HOST_OS),linux)
+
+CTS_AUDIO_TOP:= $(call my-dir)
+
+CTS_AUDIO_INSTALL_DIR := $(HOST_OUT)/cts_audio_quality
+
+cts_audio: cts_audio_quality_test cts_audio_quality CtsAudioClient $(CTS_AUDIO_TOP)/test_description
+	$(hide) mkdir -p $(CTS_AUDIO_INSTALL_DIR)
+	$(hide) mkdir -p $(CTS_AUDIO_INSTALL_DIR)/client
+	$(hide) $(ACP) -fp $(ANDROID_PRODUCT_OUT)/data/app/CtsAudioClient.apk \
+        $(CTS_AUDIO_INSTALL_DIR)/client
+	$(hide) $(ACP) -fp $(HOST_OUT)/bin/cts_audio_quality_test $(CTS_AUDIO_INSTALL_DIR)
+	$(hide) $(ACP) -fp $(HOST_OUT)/bin/cts_audio_quality $(CTS_AUDIO_INSTALL_DIR)
+	$(hide) $(ACP) -fr $(CTS_AUDIO_TOP)/test_description $(CTS_AUDIO_INSTALL_DIR)
+
+include $(call all-subdir-makefiles)
+
+endif # linux
diff --git a/suite/audio_quality/BUILD.txt b/suite/audio_quality/BUILD.txt
new file mode 100644
index 0000000..eff8074
--- /dev/null
+++ b/suite/audio_quality/BUILD.txt
@@ -0,0 +1,7 @@
+C++: No special library required in host side, but gcc/g++ and std c++ library should be present.
+Python : version 2.6.5 or higher (should be /usr/bin/python)
+Necessary Python libraries:
+	- Python SciPy, NumPy
+	sudo apt-get install python-scipy python-numpy python-matplotlib
+
+run make cts_audio frop top dir.
\ No newline at end of file
diff --git a/suite/audio_quality/client/Android.mk b/suite/audio_quality/client/Android.mk
new file mode 100644
index 0000000..090c758
--- /dev/null
+++ b/suite/audio_quality/client/Android.mk
@@ -0,0 +1,33 @@
+#
+# Copyright (C) 2012 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)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := CtsAudioClient
+
+LOCAL_PROGUARD_FLAG_FILES := proguard.cfg
+
+# intentional to keep compatibility with ICS
+LOCAL_SDK_VERSION := 15
+
+include $(BUILD_PACKAGE)
diff --git a/suite/audio_quality/client/AndroidManifest.xml b/suite/audio_quality/client/AndroidManifest.xml
new file mode 100644
index 0000000..9f1c6dd
--- /dev/null
+++ b/suite/audio_quality/client/AndroidManifest.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!-- Copyright (C) 2012 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.cts.audiotest" >
+<uses-permission android:name="android.permission.INTERNET"/>
+<uses-permission android:name="android.permission.RECORD_AUDIO"/>
+
+    <application
+        android:label="@string/app_name" >
+        <activity
+            android:name=".CtsAudioClientActivity"
+            android:label="@string/app_name" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+
+</manifest>
\ No newline at end of file
diff --git a/suite/audio_quality/client/proguard.cfg b/suite/audio_quality/client/proguard.cfg
new file mode 100644
index 0000000..3d6191f
--- /dev/null
+++ b/suite/audio_quality/client/proguard.cfg
@@ -0,0 +1,33 @@
+-optimizationpasses 5
+-dontusemixedcaseclassnames
+-dontskipnonpubliclibraryclasses
+-dontpreverify
+-verbose
+-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*
+
+-keep public class * extends android.app.Activity
+
+-keepclasseswithmembernames class * {
+    native <methods>;
+}
+
+-keepclasseswithmembers class * {
+    public <init>(android.content.Context, android.util.AttributeSet);
+}
+
+-keepclasseswithmembers class * {
+    public <init>(android.content.Context, android.util.AttributeSet, int);
+}
+
+-keepclassmembers class * extends android.app.Activity {
+   public void *(android.view.View);
+}
+
+-keepclassmembers enum * {
+    public static **[] values();
+    public static ** valueOf(java.lang.String);
+}
+
+-keep class * implements android.os.Parcelable {
+  public static final android.os.Parcelable$Creator *;
+}
diff --git a/suite/audio_quality/client/res/layout/main.xml b/suite/audio_quality/client/res/layout/main.xml
new file mode 100644
index 0000000..60ef9c8
--- /dev/null
+++ b/suite/audio_quality/client/res/layout/main.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!-- Copyright (C) 2012 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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    android:orientation="vertical" >
+</LinearLayout>
\ No newline at end of file
diff --git a/suite/audio_quality/client/res/values/strings.xml b/suite/audio_quality/client/res/values/strings.xml
new file mode 100644
index 0000000..b17566c
--- /dev/null
+++ b/suite/audio_quality/client/res/values/strings.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!-- Copyright (C) 2012 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.
+-->
+
+<resources>
+    <string name="app_name">CtsAudioClient</string>
+</resources>
\ No newline at end of file
diff --git a/suite/audio_quality/client/src/com/android/cts/audiotest/AudioProtocol.java b/suite/audio_quality/client/src/com/android/cts/audiotest/AudioProtocol.java
new file mode 100644
index 0000000..8e3ef11
--- /dev/null
+++ b/suite/audio_quality/client/src/com/android/cts/audiotest/AudioProtocol.java
@@ -0,0 +1,514 @@
+/*
+ * Copyright (C) 2012 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 com.android.cts.audiotest;
+
+import android.app.Activity;
+import  android.media.AudioFormat;
+import android.media.AudioManager;
+import android.media.AudioRecord;
+import android.media.MediaRecorder.AudioSource;
+import android.media.AudioTrack;
+import android.os.Looper;
+import android.util.Log;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.Thread;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.SocketTimeoutException;
+import java.nio.ByteBuffer;
+import java.util.HashMap;
+import java.util.concurrent.locks.ReentrantLock;
+
+
+public class AudioProtocol implements AudioTrack.OnPlaybackPositionUpdateListener {
+    private static final String TAG = "AudioProtocol";
+    private static final int PORT_NUMBER = 15001;
+
+    private Thread mThread = new Thread(new ProtocolServer());
+    private boolean mExitRequested = false;
+
+    private static final int PROTOCOL_HEADER_SIZE = 8; // id + payload length
+    private static final int MAX_NON_DATA_PAYLOAD_SIZE = 20;
+    private static final int PROTOCOL_SIMPLE_REPLY_SIZE = 12;
+    private static final int PROTOCOL_OK = 0;
+    private static final int PROTOCOL_ERROR_WRONG_PARAM = 1;
+    private static final int PROTOCOL_ERROR_GENERIC = 2;
+
+    private static final int CMD_DOWNLOAD        = 0x12340001;
+    private static final int CMD_START_PLAYBACK      = 0x12340002;
+    private static final int CMD_STOP_PLAYBACK       = 0x12340003;
+    private static final int CMD_START_RECORDING = 0x12340004;
+    private static final int CMD_STOP_RECORDING  = 0x12340005;
+
+    private ByteBuffer mHeaderBuffer = ByteBuffer.allocate(PROTOCOL_HEADER_SIZE);
+    private ByteBuffer mDataBuffer = ByteBuffer.allocate(MAX_NON_DATA_PAYLOAD_SIZE);
+    private ByteBuffer mReplyBuffer = ByteBuffer.allocate(PROTOCOL_SIMPLE_REPLY_SIZE);
+
+    // all socket access (accept / read) set this timeout to check exit periodically.
+    private static final int SOCKET_ACCESS_TIMEOUT = 2000;
+    private Socket mClient = null;
+    private InputStream mInput = null;
+    private OutputStream mOutput = null;
+    // lock to use to write to socket, I/O streams, and also change socket (create, destroy)
+    private ReentrantLock mClientLock = new ReentrantLock();
+
+    private AudioRecord mRecord = null;
+    private LoopThread mRecordThread = null;
+    private AudioTrack mPlayback = null;
+    private LoopThread mPlaybackThread = null;
+    // store recording length
+    private int mRecordingLength = 0;
+
+    // map for playback data
+    private HashMap<Integer, ByteBuffer> mDataMap = new HashMap<Integer, ByteBuffer>();
+
+    public boolean start() {
+        Log.d(TAG, "start");
+        mExitRequested = false;
+        mThread.start();
+        //Log.d(TAG, "started");
+        return true;
+    }
+
+    public void stop() throws InterruptedException {
+        Log.d(TAG, "stop");
+        mExitRequested = true;
+        try {
+            mClientLock.lock();
+            if (mClient != null) {
+                // wake up from socket read
+                mClient.shutdownInput();
+            }
+        }catch (IOException e) {
+                // ignore
+        } finally {
+            mClientLock.unlock();
+        }
+        mThread.interrupt(); // this does not bail out from socket in android
+        mThread.join();
+        reset();
+        Log.d(TAG, "stopped");
+    }
+
+    @Override
+    public void onMarkerReached(AudioTrack track) {
+        Log.d(TAG, "playback completed");
+        try {
+            sendSimpleReplyHeader(CMD_START_PLAYBACK, PROTOCOL_OK);
+        } catch (IOException e) {
+            // maybe socket already closed. don't do anything
+            Log.e(TAG, "ignore exception", e);
+        } finally {
+            track.stop();
+        }
+    }
+
+    @Override
+    public void onPeriodicNotification(AudioTrack arg0) {
+        Log.d(TAG, "track periodic notification");
+        // TODO Auto-generated method stub
+    }
+
+    /**
+     * Read given amount of data to the buffer
+     * @param in
+     * @param buffer
+     * @param len length to read
+     * @return true if header read successfully, false if exit requested
+     * @throws IOException
+     * @throws ExitRequest
+     */
+    private void read(InputStream in, ByteBuffer buffer, int len) throws IOException, ExitRequest {
+        buffer.clear();
+        int totalRead = 0;
+        while (totalRead < len) {
+            int readNow = in.read(buffer.array(), totalRead, len - totalRead);
+            if (readNow < 0) { // end-of-stream, error
+                Log.e(TAG, "read returned " + readNow);
+                throw new IOException();
+            }
+            totalRead += readNow;
+            if(mExitRequested) {
+                throw new ExitRequest();
+            }
+        }
+    }
+
+    private class ProtocolError  extends Exception {
+        public ProtocolError(String message) {
+            super(message);
+        }
+    }
+
+    private class ExitRequest extends Exception {
+        public ExitRequest() {
+            super();
+        }
+    }
+
+    private void assertProtocol(boolean cond, String message) throws ProtocolError {
+        if (!cond) {
+            throw new ProtocolError(message);
+        }
+    }
+
+    private void reset() {
+        // lock only when it is not already locked by this thread
+        if (mClientLock.getHoldCount() == 0) {
+            mClientLock.lock();
+        }
+        if (mClient != null) {
+            try {
+                mClient.close();
+            } catch (IOException e) {
+                // ignore
+            }
+            mClient = null;
+        }
+        mInput = null;
+        mOutput = null;
+        while (mClientLock.getHoldCount() > 0) {
+            mClientLock.unlock();
+        }
+        if (mRecord != null) {
+            if (mRecord.getState() != AudioRecord.STATE_UNINITIALIZED) {
+                mRecord.stop();
+            }
+            mRecord.release();
+            mRecord = null;
+        }
+        if (mRecordThread != null) {
+            mRecordThread.quitLoop();
+            mRecordThread = null;
+        }
+        if (mPlayback != null) {
+            if (mPlayback.getState() != AudioTrack.STATE_UNINITIALIZED) {
+                mPlayback.stop();
+            }
+            mPlayback.release();
+            mPlayback = null;
+        }
+        if (mPlaybackThread != null) {
+            mPlaybackThread.quitLoop();
+            mPlaybackThread = null;
+        }
+        mDataMap.clear();
+    }
+
+    private void handleDownload(int len) throws IOException, ExitRequest {
+        read(mInput, mDataBuffer, 4); // only for id
+        Integer id  = new Integer(mDataBuffer.getInt(0));
+        int dataLength = len - 4;
+        ByteBuffer data = ByteBuffer.allocate(dataLength);
+        read(mInput, data, dataLength);
+        mDataMap.put(id, data);
+        Log.d(TAG, "downloaded data id " + id + " len " + dataLength);
+        sendSimpleReplyHeader(CMD_DOWNLOAD, PROTOCOL_OK);
+    }
+
+    private void handleStartPlayback(int len) throws ProtocolError, IOException, ExitRequest {
+        // this error is too critical, so do not even send reply
+        assertProtocol(len == 20, "wrong payload len");
+        read(mInput, mDataBuffer, len);
+        final Integer id = new Integer(mDataBuffer.getInt(0));
+        final int samplingRate = mDataBuffer.getInt(1 * 4);
+        final boolean stereo = ((mDataBuffer.getInt(2 * 4) & 0x80000000) != 0);
+        final int mode = mDataBuffer.getInt(2 * 4) & 0x7fffffff;
+        final int volume = mDataBuffer.getInt(3 * 4);
+        final int repeat = mDataBuffer.getInt(4 * 4);
+        try {
+            final ByteBuffer data = mDataMap.get(id);
+            if (data == null) {
+                throw new ProtocolError("wrong id");
+            }
+            if (samplingRate != 44100) {
+                throw new ProtocolError("wrong rate");
+            }
+            //TODO repeat
+            //FIXME in MODE_STATIC, setNotificationMarkerPosition does not work with full length
+            mPlaybackThread = new LoopThread(new Runnable() {
+
+                @Override
+                public void run() {
+                    int type = (mode == 0) ? AudioManager.STREAM_VOICE_CALL :
+                        AudioManager.STREAM_MUSIC;
+                    mPlayback = new AudioTrack(type, samplingRate,
+                            stereo ? AudioFormat.CHANNEL_OUT_STEREO : AudioFormat.CHANNEL_OUT_MONO,
+                            AudioFormat.ENCODING_PCM_16BIT, data.capacity(),
+                            AudioTrack.MODE_STREAM);
+                    float minVolume = mPlayback.getMinVolume();
+                    float maxVolume = mPlayback.getMaxVolume();
+                    float newVolume = (maxVolume - minVolume) * volume / 100 + minVolume;
+                    mPlayback.setStereoVolume(newVolume, newVolume);
+                    Log.d(TAG, "setting volume " + newVolume + " max " + maxVolume +
+                            " min " + minVolume + " received " + volume);
+                    mPlayback.write(data.array(), 0, data.capacity());
+                    mPlayback.setPlaybackPositionUpdateListener(AudioProtocol.this);
+
+                    int endMarker = data.capacity()/(stereo ? 4 : 2);
+                    int res = mPlayback.setNotificationMarkerPosition(endMarker);
+                    Log.d(TAG, "start playback id " + id + " len " + data.capacity() +
+                            " set.. res " + res + " stereo? " + stereo + " end " + endMarker);
+                    mPlayback.play();
+                }
+            });
+            mPlaybackThread.start();
+            // send reply when play is completed
+        } catch (ProtocolError e) {
+            sendSimpleReplyHeader(CMD_START_PLAYBACK, PROTOCOL_ERROR_WRONG_PARAM);
+            Log.e(TAG, "wrong param", e);
+        }
+    }
+
+    private void handleStopPlayback(int len) throws ProtocolError, IOException {
+        Log.d(TAG, "stopPlayback");
+        assertProtocol(len == 0, "wrong payload len");
+        if (mPlayback != null) {
+            mPlayback.stop();
+            mPlayback.release();
+            mPlayback = null;
+        }
+        if (mPlaybackThread != null) {
+            mPlaybackThread.quitLoop();
+            mPlaybackThread = null;
+        }
+        sendSimpleReplyHeader(CMD_STOP_PLAYBACK, PROTOCOL_OK);
+    }
+
+    private void handleStartRecording(int len) throws ProtocolError, IOException, ExitRequest {
+        assertProtocol(len == 16, "wrong payload len");
+        read(mInput, mDataBuffer, len);
+        final int samplingRate = mDataBuffer.getInt(0);
+        final boolean stereo = ((mDataBuffer.getInt(1 * 4) & 0x80000000) != 0);
+        final int mode = mDataBuffer.getInt(1 * 4) & 0x7fffffff;
+        final int volume = mDataBuffer.getInt(2 * 4);
+        final int samples = mDataBuffer.getInt(3 * 4);
+        try {
+            if (samplingRate != 44100) {
+                throw new ProtocolError("wrong rate");
+            }
+            if (stereo) {
+                throw new ProtocolError("mono only");
+            }
+            //TODO volume ?
+            mRecordingLength = samples * 2;
+            mRecordThread = new LoopThread(new Runnable() {
+
+                @Override
+                public void run() {
+                    int minBufferSize = AudioRecord.getMinBufferSize(samplingRate,
+                            AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT);
+                    int type = (mode == 0) ? AudioSource.VOICE_RECOGNITION : AudioSource.DEFAULT;
+                    mRecord = new AudioRecord(type, samplingRate,
+                            AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT,
+                            (minBufferSize > mRecordingLength) ? minBufferSize : mRecordingLength);
+
+                    mRecord.startRecording();
+                    Log.d(TAG, "recording started " + " samples " + samples +
+                            " recording state " + mRecord.getRecordingState() + " len " +
+                            mRecordingLength);
+                    try {
+                        boolean recordingOk = true;
+                        byte[] data = new byte[mRecordingLength];
+                        int totalRead = 0;
+                        while (totalRead < mRecordingLength) {
+                            int lenRead = mRecord.read(data, 0, (mRecordingLength - totalRead));
+                            if (lenRead < 0) {
+                                Log.e(TAG, "reading recording failed with error code " + lenRead);
+                                recordingOk = false;
+                                break;
+                            } else if (lenRead == 0) {
+                                Log.w(TAG, "zero read");
+                            }
+                            totalRead += lenRead;
+                        }
+                        Log.d(TAG, "reading recording completed");
+                        mClientLock.lock();
+
+                        mReplyBuffer.clear();
+                        mReplyBuffer.putInt((CMD_START_RECORDING & 0xffff) | 0x43210000);
+                        mReplyBuffer.putInt(recordingOk ? PROTOCOL_OK : PROTOCOL_ERROR_GENERIC);
+                        mReplyBuffer.putInt(recordingOk ? mRecordingLength : 0);
+
+                        if (mOutput != null) {
+                            mOutput.write(mReplyBuffer.array(), 0, PROTOCOL_SIMPLE_REPLY_SIZE);
+                            if (recordingOk) {
+                                mOutput.write(data, 0, mRecordingLength);
+                            }
+                        }
+                    } catch (IOException e) {
+                        // maybe socket already closed. don't do anything
+                        Log.e(TAG, "ignore exception", e);
+                    } finally {
+                        mRecord.stop();
+                        mRecord.release();
+                        mRecord = null;
+                        mClientLock.unlock();
+                    }
+                }
+             });
+            mRecordThread.start();
+        } catch (ProtocolError e) {
+            sendSimpleReplyHeader(CMD_START_RECORDING, PROTOCOL_ERROR_WRONG_PARAM);
+            Log.e(TAG, "wrong param", e);
+        }
+    }
+
+    private void handleStopRecording(int len) throws ProtocolError, IOException {
+        Log.d(TAG, "stop recording");
+        assertProtocol(len == 0, "wrong payload len");
+        if (mRecord != null) {
+            mRecord.stop();
+            mRecord.release();
+            mRecord = null;
+        }
+        if (mRecordThread != null) {
+            mRecordThread.quitLoop();
+            mRecordThread = null;
+        }
+        sendSimpleReplyHeader(CMD_STOP_RECORDING, PROTOCOL_OK);
+    }
+
+    /**
+     * send reply without payload.
+     * This function is thread-safe.
+     * @param out
+     * @param command
+     * @param errorCode
+     * @throws IOException
+     */
+    private void sendSimpleReplyHeader(int command, int errorCode) throws IOException {
+        Log.d(TAG, "sending reply cmd " + command + " err " + errorCode);
+        try {
+            mClientLock.lock();
+            mReplyBuffer.clear();
+            mReplyBuffer.putInt((command & 0xffff) | 0x43210000);
+            mReplyBuffer.putInt(errorCode);
+            mReplyBuffer.putInt(0); // payload length
+            if (mOutput != null) {
+                mOutput.write(mReplyBuffer.array(), 0, PROTOCOL_SIMPLE_REPLY_SIZE);
+            }
+        } finally {
+            mClientLock.unlock();
+        }
+    }
+
+    private class LoopThread extends Thread {
+        private Looper mLooper;
+        LoopThread(Runnable runnable) {
+            super(runnable);
+        }
+        public void run() {
+            Looper.prepare();
+            mLooper = Looper.myLooper();
+            Log.d(TAG, "run runnable");
+            super.run();
+            Log.d(TAG, "loop");
+            Looper.loop();
+        }
+        // should be called outside this thread
+        public void quitLoop() {
+            mLooper.quit();
+            try {
+                join();
+            } catch (InterruptedException e) {
+                // ignore
+            }
+        }
+    }
+
+    private class ProtocolServer implements Runnable {
+
+        @Override
+        public void run() {
+            ServerSocket server = null;
+
+            try { // for catching exception from ServerSocket
+                Log.d(TAG, "get new server socket");
+                server = new ServerSocket(PORT_NUMBER);
+                server.setReuseAddress(true);
+                server.setSoTimeout(SOCKET_ACCESS_TIMEOUT);
+                while (!mExitRequested) {
+                    //TODO check already active recording/playback
+                    try { // for catching exception from Socket, will restart upon exception
+                        try {
+                            mClientLock.lock();
+                            //Log.d(TAG, "will accept");
+                            mClient = server.accept();
+                            mClient.setReuseAddress(true);
+                            mInput = mClient.getInputStream();
+                            mOutput = mClient.getOutputStream();
+                        } catch (SocketTimeoutException e) {
+                            // This will happen frequently if client does not connect.
+                            // just re-start
+                            continue;
+                        } finally {
+                            mClientLock.unlock();
+                        }
+                        Log.i(TAG, "new client connected");
+                        while (!mExitRequested) {
+                            read(mInput, mHeaderBuffer, PROTOCOL_HEADER_SIZE);
+                            int command = mHeaderBuffer.getInt();
+                            int len = mHeaderBuffer.getInt();
+                            Log.i(TAG, "received command " + command);
+                            switch(command) {
+                            case CMD_DOWNLOAD:
+                                handleDownload(len);
+                                break;
+                            case CMD_START_PLAYBACK:
+                                handleStartPlayback(len);
+                                break;
+                            case CMD_STOP_PLAYBACK:
+                                handleStopPlayback(len);
+                                break;
+                            case CMD_START_RECORDING:
+                                handleStartRecording(len);
+                                break;
+                            case CMD_STOP_RECORDING:
+                                handleStopRecording(len);
+                                break;
+                            }
+                        }
+                    } catch (IOException e) {
+                        Log.e(TAG, "restart from exception", e);
+                    } catch (ProtocolError e) {
+                        Log.e(TAG, "restart from exception",  e);
+                    } finally {
+                        reset();
+                    }
+                }
+            } catch (ExitRequest e) {
+                Log.e(TAG, "exit requested, will exit", e);
+            } catch (IOException e) {
+                // error in server socket, just exit the thread and let things fail.
+                Log.e(TAG, "error while init, will exit", e);
+            } finally {
+                if (server != null) {
+                    try {
+                        server.close();
+                    } catch (IOException e) {
+                        // ignore
+                    }
+                }
+                reset();
+            }
+        }
+    }
+}
diff --git a/suite/audio_quality/client/src/com/android/cts/audiotest/CtsAudioClientActivity.java b/suite/audio_quality/client/src/com/android/cts/audiotest/CtsAudioClientActivity.java
new file mode 100644
index 0000000..7f801e8
--- /dev/null
+++ b/suite/audio_quality/client/src/com/android/cts/audiotest/CtsAudioClientActivity.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2012 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 com.android.cts.audiotest;
+
+import android.app.Activity;
+import android.content.Context;
+import android.media.AudioManager;
+import android.os.Bundle;
+
+
+public class CtsAudioClientActivity extends Activity {
+    private AudioProtocol mProtocol = null;
+    int mVolumeMusic;
+    int mVolumeVoice;
+    @Override
+    protected void onPause() {
+        try {
+            mProtocol.stop();
+        } catch (InterruptedException e) {
+            e.printStackTrace();
+        }
+        mProtocol = null;
+        setVolume(AudioManager.STREAM_MUSIC, mVolumeMusic);
+        setVolume(AudioManager.STREAM_VOICE_CALL, mVolumeVoice);
+        super.onPause();
+    }
+
+    @Override
+    protected void onResume() {
+        // set volume to max
+        mVolumeMusic = setVolume(AudioManager.STREAM_MUSIC, -1);
+        mVolumeVoice = setVolume(AudioManager.STREAM_VOICE_CALL, -1);
+        mProtocol = new AudioProtocol();
+        mProtocol.start();
+        super.onResume();
+    }
+
+    /** Called when the activity is first created. */
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.main);
+    }
+
+    /**
+     * set volume to desired level
+     * @param level target level, if -1, set to max
+     * @return the original volume level
+     */
+    int setVolume(int stream, int level) {
+        AudioManager mgr = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
+        mgr.setStreamMute(stream, false);
+        int original = mgr.getStreamVolume(AudioManager.STREAM_MUSIC);
+        int targetLevel = level;
+        if (level == -1) {
+            targetLevel = mgr.getStreamMaxVolume(stream);
+        }
+        mgr.setStreamVolume(stream, targetLevel, 0);
+        return original;
+    }
+}
\ No newline at end of file
diff --git a/suite/audio_quality/executable/Android.mk b/suite/audio_quality/executable/Android.mk
new file mode 100644
index 0000000..dfb67d0
--- /dev/null
+++ b/suite/audio_quality/executable/Android.mk
@@ -0,0 +1,36 @@
+#
+# Copyright (C) 2012 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.
+#
+
+# build only for linux
+ifeq ($(HOST_OS),linux)
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+LOCAL_CPP_EXTENSION := .cpp
+LOCAL_MODULE_TAGS := optional
+LOCAL_SRC_FILES := $(patsubst ./%,%, $(shell cd $(LOCAL_PATH); \
+  find . -name "*.cpp" -and -not -name ".*"))
+#$(info $(LOCAL_SRC_FILES))
+LOCAL_C_INCLUDES += $(LOCAL_PATH)/../lib/include  external/tinyalsa/include/  libcore/include \
+	$(LOCAL_PATH)/../lib/src
+LOCAL_STATIC_LIBRARIES += libutils liblog libcutils libtinyalsa libtinyxml
+LOCAL_WHOLE_STATIC_LIBRARIES := libcts_audio_quality
+LOCAL_CFLAGS:= -g -fno-exceptions
+LOCAL_LDFLAGS:= -g -lrt -ldl -lstdc++ -lm -fno-exceptions
+LOCAL_MODULE:= cts_audio_quality
+include $(BUILD_HOST_EXECUTABLE)
+
+endif # linux
diff --git a/suite/audio_quality/executable/src/main.cpp b/suite/audio_quality/executable/src/main.cpp
new file mode 100644
index 0000000..3d72d0d
--- /dev/null
+++ b/suite/audio_quality/executable/src/main.cpp
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2012 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 <stdio.h>
+
+#include <utils/String8.h>
+
+#include <UniquePtr.h>
+
+#include "Log.h"
+#include "Report.h"
+#include "task/TaskGeneric.h"
+#include "task/ModelBuilder.h"
+
+// For flushing report and log before exiting
+class CleanupStatics {
+public:
+
+    CleanupStatics() {};
+    ~CleanupStatics() {
+        Log::Finalize();
+        Report::Finalize();
+    }
+};
+
+int main(int argc, char *argv[])
+{
+    if (argc < 2) {
+        fprintf(stderr, "%s [-llog_level] test_xml\n", argv[0]);
+        return 1;
+    }
+    int logLevel = 3;
+    int argCurrent = 1;
+    if (strncmp(argv[argCurrent], "-l", 2) == 0) {
+        logLevel = atoi(argv[argCurrent] + 2);
+        argCurrent++;
+    }
+    if (argCurrent == argc) {
+        fprintf(stderr, "wrong arguments");
+        return 1;
+    }
+    android::String8 xmlFile(argv[argCurrent]);
+
+    android::String8 dirName;
+    if (!FileUtil::prepare(dirName)) {
+        fprintf(stderr, "cannot prepare report dir");
+        return 1;
+    }
+
+    UniquePtr<CleanupStatics> staticStuffs(new CleanupStatics());
+    if (Log::Instance(dirName.string()) == NULL) {
+        fprintf(stderr, "cannot create Log");
+        return 1;
+    }
+    Log::Instance()->setLogLevel((Log::LogLevel)logLevel);
+    // Log can be used from here
+    if (Report::Instance(dirName.string()) == NULL) {
+
+        LOGE("cannot create log");
+        return 1;
+    }
+
+    ModelBuilder modelBuilder;
+    UniquePtr<TaskGeneric> topTask(modelBuilder.parseTestDescriptionXml(xmlFile));
+    if (topTask.get() == NULL) {
+        LOGE("Parsing of %x failed", xmlFile.string());
+        return 1;
+    }
+    topTask->run();
+    return 0;
+}
+
diff --git a/suite/audio_quality/lib/Android.mk b/suite/audio_quality/lib/Android.mk
new file mode 100644
index 0000000..341a4dd
--- /dev/null
+++ b/suite/audio_quality/lib/Android.mk
@@ -0,0 +1,35 @@
+#
+# Copyright (C) 2012 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.
+#
+
+# build only for linux
+ifeq ($(HOST_OS),linux)
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+LOCAL_CPP_EXTENSION := .cpp
+LOCAL_MODULE_TAGS := optional
+LOCAL_SRC_FILES := $(patsubst ./%,%, $(shell cd $(LOCAL_PATH); \
+  find . -name "*.cpp" -and -not -name ".*"))
+#$(info $(LOCAL_SRC_FILES))
+LOCAL_C_INCLUDES += $(LOCAL_PATH)/include $(LOCAL_PATH)/src /usr/include/ \
+	external/tinyalsa/include/ external/tinyxml/ libcore/include
+LOCAL_STATIC_LIBRARIES += libutils liblog libtinyalsa libcutils libtinyxml
+LOCAL_CFLAGS:= -g -fno-exceptions
+LOCAL_LDFLAGS:= -g -lrt -ldl -lm -fno-exceptions
+LOCAL_MODULE:= libcts_audio_quality
+include $(BUILD_HOST_STATIC_LIBRARY)
+
+endif # linux
diff --git a/suite/audio_quality/lib/include/BuiltinProcessing.h b/suite/audio_quality/lib/include/BuiltinProcessing.h
new file mode 100644
index 0000000..6fd1b96
--- /dev/null
+++ b/suite/audio_quality/lib/include/BuiltinProcessing.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+
+#ifndef CTSAUDIO_BUILTINPROCESSING_H
+#define CTSAUDIO_BUILTINPROCESSING_H
+
+#include "task/TaskGeneric.h"
+
+class BuiltinProcessing {
+public:
+    BuiltinProcessing();
+
+    typedef TaskGeneric::ExecutionResult \
+            (BuiltinProcessing::*BuiltinProcessingMemberFn)(void**, void**);
+    struct BuiltinInfo {
+        const char* mName;
+        BuiltinProcessingMemberFn mFunction;
+        size_t mNInput;
+        const bool* mInputTypes; // true: android::sp<Buffer>*, false: Value*
+        size_t mNOutput;
+        const bool* mOutputTypes;
+    };
+
+    static const int N_BUILTIN_FNS = 1;
+    static BuiltinInfo BUINTIN_FN_TABLE[N_BUILTIN_FNS];
+    /**
+     * calculate RMS of given data. The rms is passed to moving average filter
+     * And the averaged RMS should be within passMin to passMax to pass the test.
+     * Otherwise, it will just return EResultOK.
+     * Input: android::sp<Buffer> data, int64_t passMin, int64_t passMax
+     * Output:int64_t rms
+     */
+    TaskGeneric::ExecutionResult rms_mva(void** inputs, void** outputs);
+private:
+    static const int RMS_CONTINUOUS_PASSES = 5;
+    int mRMSPasses;
+};
+
+
+#endif // CTSAUDIO_BUILTINPROCESSING_H
diff --git a/suite/audio_quality/lib/include/ClientInterface.h b/suite/audio_quality/lib/include/ClientInterface.h
new file mode 100644
index 0000000..32de0e9
--- /dev/null
+++ b/suite/audio_quality/lib/include/ClientInterface.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+#ifndef CTSAUDIO_CLIENTINTERFACE_H
+#define CTSAUDIO_CLIENTINTERFACE_H
+
+#include <utils/String8.h>
+#include <utils/StrongPointer.h>
+
+#include "audio/RemoteAudio.h"
+#include "ClientSocket.h"
+
+class ClientInterface {
+public:
+    virtual ~ClientInterface() {};
+    /**
+     * launch client and perform initial connection
+     * @param param parameter for connection. It will be device serial number or zero length string
+     */
+    virtual bool init(const android::String8& param) = 0;
+
+    virtual ClientSocket& getSocket()  = 0;
+
+    virtual android::sp<RemoteAudio>& getAudio() = 0;
+
+};
+
+
+#endif // CTSAUDIO_CLIENTINTERFACE_H
diff --git a/suite/audio_quality/lib/include/ClientSocket.h b/suite/audio_quality/lib/include/ClientSocket.h
new file mode 100644
index 0000000..49328bd
--- /dev/null
+++ b/suite/audio_quality/lib/include/ClientSocket.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+
+#ifndef CTASUDIO_CLIENTSOCKET_H
+#define CTASUDIO_CLIENTSOCKET_H
+
+class ClientSocket {
+public:
+    ClientSocket();
+    virtual ~ClientSocket();
+    virtual bool init(const char* hostIp, int port, bool enableTimeout = false);
+    /**
+     * @param timeoutInMs 0 means no time-out
+     */
+    virtual bool readData(char* data, int len, int timeoutInMs = 0);
+    virtual bool sendData(const char* data, int len);
+    int getFD() {
+        return mSocket;
+    }
+    virtual void release();
+protected:
+    int mSocket;
+    bool mTimeoutEnabled;
+};
+
+
+#endif // CTASUDIO_CLIENTSOCKET_H
diff --git a/suite/audio_quality/lib/include/FileUtil.h b/suite/audio_quality/lib/include/FileUtil.h
new file mode 100644
index 0000000..e5fa886
--- /dev/null
+++ b/suite/audio_quality/lib/include/FileUtil.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+
+#ifndef CTSAUDIO_FILEUTIL_H
+#define CTSAUDIO_FILEUTIL_H
+
+#include <stdarg.h>
+
+#include <utils/String8.h>
+#include <utils/threads.h>
+#include <iostream>
+#include <fstream>
+
+/**
+ * Class to write to file and stdout at the same time.
+ *
+ */
+class FileUtil {
+public:
+    /**
+     * create log / report dir
+     * @param dirPath returns path of created dir
+     */
+    static bool prepare(android::String8& dirPath);
+
+protected:
+    FileUtil();
+    virtual ~FileUtil();
+
+    /**
+     * if fileName is NULL, only stdout output will be supproted
+     */
+    virtual bool init(const char* fileName);
+
+    virtual bool doPrintf(const char* fmt, ...);
+    /// fileOnly log only to file
+    /// loglevel 0 .., -1 means no log level.
+    virtual bool doVprintf(bool fileOnly, int loglevel, const char *fmt, va_list ap);
+
+private:
+    // store dirPath to prevent creating multiple times
+    static android::String8 mDirPath;
+    std::ofstream mFile;
+    static const int BUFFER_SIZE = 1024;
+    // buffer for printf. one line longer than this will be truncated.
+    char mBuffer[BUFFER_SIZE];
+    android::Mutex mWriteLock;
+};
+
+
+#endif // CTSAUDIO_FILEUTIL_H
diff --git a/suite/audio_quality/lib/include/GenericFactory.h b/suite/audio_quality/lib/include/GenericFactory.h
new file mode 100644
index 0000000..243a744
--- /dev/null
+++ b/suite/audio_quality/lib/include/GenericFactory.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+#ifndef CTSAUDIO_GENERIC_FACTORY_H
+#define CTSAUDIO_GENERIC_FACTORY_H
+
+#include "task/TaskGeneric.h"
+
+class ClientInterface;
+
+/**
+ * Factory methods for all abstract classes
+ */
+class GenericFactory {
+public:
+
+    virtual ClientInterface* createClientInterface();
+    virtual TaskGeneric* createTask(TaskGeneric::TaskType type);
+};
+
+
+#endif // CTSAUDIO_GENERIC_FACTORY_H
diff --git a/suite/audio_quality/lib/include/Log.h b/suite/audio_quality/lib/include/Log.h
new file mode 100644
index 0000000..2b75bc2
--- /dev/null
+++ b/suite/audio_quality/lib/include/Log.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+#ifndef CTSAUDIO_LOG_H
+#define CTSAUDIO_LOG_H
+
+#include <stdio.h>
+#include <iostream>
+#include <fstream>
+
+#include "FileUtil.h"
+
+class Log: public FileUtil {
+public:
+    enum LogLevel {
+        ELogV = 0,
+        ELogD = 1,
+        ELogI = 2,
+        ELogW = 3,
+        ELogE = 4
+    };
+
+    static Log* Instance(const char* dirName = NULL);
+    static void Finalize();
+
+
+    void printf(LogLevel level, const char* fmt, ...);
+    void setLogLevel(LogLevel level);
+    LogLevel getLogLevel() {
+        return mLogLevel;
+    };
+private:
+    Log();
+    virtual ~Log();
+    virtual bool init(const char* dirName);
+
+private:
+    static Log* mInstance;
+    LogLevel mLogLevel;
+};
+
+#define LOGE(x...) do { Log::Instance()->printf(Log::ELogE, x); \
+    Log::Instance()->printf(Log::ELogE, "  file %s line %d", __FILE__, __LINE__); } while(0)
+#define LOGW(x...) do { Log::Instance()->printf(Log::ELogW, x); } while(0)
+#define LOGI(x...) do { Log::Instance()->printf(Log::ELogI, x); } while(0)
+#define LOGD(x...) do { Log::Instance()->printf(Log::ELogD, x); } while(0)
+#define LOGV(x...) do { Log::Instance()->printf(Log::ELogV, x); } while(0)
+
+#define ASSERT(cond) if(!(cond)) {  Log::Instance()->printf(Log::ELogE, \
+        "assertion failed %s %d", __FILE__, __LINE__); \
+    Log::Finalize(); \
+    *(char*)0 = 0; /* this will crash */};
+
+#endif // CTSAUDIO_LOG_H
diff --git a/suite/audio_quality/lib/include/Report.h b/suite/audio_quality/lib/include/Report.h
new file mode 100644
index 0000000..67a0572
--- /dev/null
+++ b/suite/audio_quality/lib/include/Report.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+#ifndef CTSAUDIO_REPORT_H
+#define CTSAUDIO_REPORT_H
+
+#include <list>
+
+#include <utils/String8.h>
+#include "FileUtil.h"
+/**
+ * Class to generate report
+ */
+class Report: public FileUtil {
+public:
+    /**
+     * returns static instance of Report
+     * report without dir name, for the 1st call, will only print to stdout.
+     * This mode is necessary to prevent creating tons of reports during unit testing
+     */
+    static Report* Instance(const char* dirName = NULL);
+    // should be called before finishing to flush the report to file system
+    static void Finalize();
+
+    void addCasePassed(const android::String8& name);
+    void addCaseFailed(const android::String8& name);
+    void printf(const char* fmt, ...);
+
+private:
+    Report();
+    ~Report();
+    bool init(const char* dirName);
+    void writeSummary();
+
+private:
+    static Report* mInstance;
+    std::list<android::String8> mPassedCases;
+    std::list<android::String8> mFailedCases;
+};
+
+
+#endif // CTSAUDIO_REPORT_H
diff --git a/suite/audio_quality/lib/include/Semaphore.h b/suite/audio_quality/lib/include/Semaphore.h
new file mode 100644
index 0000000..3fdcc75
--- /dev/null
+++ b/suite/audio_quality/lib/include/Semaphore.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+
+#ifndef CTSAUDIO_SEMAPHORE_H
+#define CTSAUDIO_SEMAPHORE_H
+
+#include <semaphore.h>
+
+#include <Log.h>
+
+/**
+ * Simple semaphore interface for synchronization between client and server
+ */
+class Semaphore {
+public:
+    Semaphore(int count = 0);
+
+    ~Semaphore();
+
+    /// down semaphore if it is already positive.
+    void tryWait();
+
+    bool wait();
+
+    bool timedWait(int timeInMSec);
+
+    void post();
+
+private:
+    sem_t mSem;
+};
+
+
+#endif // CTSAUDIO_SEMAPHORE_H
diff --git a/suite/audio_quality/lib/include/Settings.h b/suite/audio_quality/lib/include/Settings.h
new file mode 100644
index 0000000..5cdcd7a
--- /dev/null
+++ b/suite/audio_quality/lib/include/Settings.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+
+#ifndef CTSAUDIO_SETTINGS_H
+#define CTSAUDIO_SETTINGS_H
+
+#include <utils/String8.h>
+
+class Settings {
+public:
+    static Settings* Instance();
+    static void Finalize();
+    enum SettingType {
+        EADB
+    };
+    void addSetting(SettingType type, const android::String8 setting);
+    const android::String8& getSetting(SettingType type);
+private:
+    static Settings* mInstance;
+    android::String8 mAdbSetting;
+};
+
+
+#endif // CTSAUDIO_SETTINGS_H
diff --git a/suite/audio_quality/lib/include/SignalProcessingInterface.h b/suite/audio_quality/lib/include/SignalProcessingInterface.h
new file mode 100644
index 0000000..202cdf9
--- /dev/null
+++ b/suite/audio_quality/lib/include/SignalProcessingInterface.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+#ifndef CTSAUDIO_SIGNALPROCESSINGINTERFACE_H
+#define CTSAUDIO_SIGNALPROCESSINGINTERFACE_H
+
+#include <utils/String8.h>
+
+#include "task/TaskGeneric.h"
+
+/**
+ * Interface to Signal processing module to run signal processing script and retrieve the result
+ * After construction, init() should be called before doing anything else.
+ */
+class SignalProcessingInterface {
+public:
+    virtual ~SignalProcessingInterface() {};
+
+    virtual bool init(const android::String8& script) = 0;
+    /**
+     * run the script with given input / output parameters. Note that this function does not
+     * do any type check.
+     * @param functionScript function name (python script name to run for this call)
+     * @param nInputs number of inputs. This is the length of inputTypes and inputs array
+     * @param inputTypes represent types of each input.
+     *              when true: android::sp<Buffer>*, false: Value*
+     * @param inputs pointer to input. Either android::sp<Buffer>* or Value*
+     * @param nOutputs
+     * @param outputTypes
+     * @param outputs
+     */
+    virtual TaskGeneric::ExecutionResult run(const android::String8& functionScript,
+            int nInputs, bool* inputTypes, void** inputs,
+            int nOutputs, bool* outputTypes, void** outputs) = 0;
+};
+
+#endif //CTSAUDIO_SIGNALPROCESSINGINTERFACE_H
diff --git a/suite/audio_quality/lib/include/StringUtil.h b/suite/audio_quality/lib/include/StringUtil.h
new file mode 100644
index 0000000..7519a8e
--- /dev/null
+++ b/suite/audio_quality/lib/include/StringUtil.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+
+#ifndef CTSAUDIO_STRINGUTIL_H
+#define CTSAUDIO_STRINGUTIL_H
+
+#include <utils/String8.h>
+#include <vector>
+
+/**
+ * Utility class for implementing missing features from android::String8
+ */
+class StringUtil {
+public:
+    /// split the given string with given delimiter and return the vector of string
+    /// it may return NULL if memory alloc fails.
+    /// If vector is not NULL, there will be at least one string
+    static std::vector<android::String8>* split(const android::String8& str, char delimiter);
+    /// This function will return zero length string if pos is invalid.
+    static android::String8 substr(const android::String8& str, size_t pos, size_t n);
+    static int compare(const android::String8& str, const char* other);
+    static bool endsWith(const android::String8& str, const char* other);
+};
+
+
+#endif // CTSAUDIO_STRINGUTIL_H
diff --git a/suite/audio_quality/lib/include/audio/AudioHardware.h b/suite/audio_quality/lib/include/audio/AudioHardware.h
new file mode 100644
index 0000000..c505c31
--- /dev/null
+++ b/suite/audio_quality/lib/include/audio/AudioHardware.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+#ifndef CTSAUDIO_AUDIOHARDWARE_H
+#define CTSAUDIO_AUDIOHARDWARE_H
+
+#include <utils/StrongPointer.h>
+#include <utils/RefBase.h>
+#include "Buffer.h"
+
+class TaskCase;
+/**
+ * Utility class for H/W detection
+ */
+class AudioHardware : virtual public android::RefBase {
+public:
+    /** audio length should be multiple of this */
+    static const int SAMPLES_PER_ONE_GO = 4096;
+
+    enum SamplingRate {
+        ESamplingRateInvald = 0,
+        ESampleRate_16000 = 16000,
+        ESampleRate_44100 = 44100
+    };
+    enum BytesPerSample {
+        E2BPS = 2
+    };
+    enum AudioMode {
+        EModeVoice = 0,
+        EModeMusic = 1
+    };
+
+    /**
+     * detect supported audio H/W
+     * @return card number of detected H/W. -1 if not found.
+     */
+    static int detectAudioHw();
+
+    /**
+     * Factory method
+     * options are : local or remote, playback or recording
+     * can return NULL(sp.get() == NULL) if H/W not found
+     */
+    static android::sp<AudioHardware> createAudioHw(bool local, bool playback,
+            TaskCase* testCase = NULL);
+
+    virtual ~AudioHardware();
+    /**
+     * prepare playback or recording
+     */
+    virtual bool prepare(SamplingRate samplingRate, int volume, int mode = EModeVoice) = 0;
+
+    /**
+     * Convenience API to pass buffer ID. The buffer can be either present in testCase
+     * or in remote device (when testCase is NULL)
+     */
+    virtual bool startPlaybackOrRecordById(const android::String8& id, TaskCase* testCase = NULL);
+
+    /**
+     *  Playback / Record with given buffer
+     *  @param buffer buffer to play / record
+     *  @param numberRepetition How many times to repeat playback / record for given buffer.
+     *         For record, it does not have much meaning as the last recording will always
+     *         override.
+     */
+    virtual bool startPlaybackOrRecord(android::sp<Buffer>& buffer,
+            int numberRepetition = 1) = 0;
+    /**
+     * Wait for the playback / recording to complete. return true when successfully finished.
+     * Calling waitForCompletion after calling stopPlaybackOrRecord will lead into blocking
+     * the calling thread for some time.
+     */
+    virtual bool waitForCompletion() = 0;
+    /// stops the on-going action. The active task can be canceled.
+    virtual void stopPlaybackOrRecord() = 0;
+
+protected:
+    static int mHwId;
+};
+
+
+#endif // CTSAUDIO_AUDIOHARDWARE_H
diff --git a/suite/audio_quality/lib/include/audio/AudioLocal.h b/suite/audio_quality/lib/include/audio/AudioLocal.h
new file mode 100644
index 0000000..a263528
--- /dev/null
+++ b/suite/audio_quality/lib/include/audio/AudioLocal.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+
+#ifndef CTSAUDIO_AUDIOLOCAL_H
+#define CTSAUDIO_AUDIOLOCAL_H
+
+#include <utils/StrongPointer.h>
+#include <utils/threads.h>
+
+#include <Semaphore.h>
+
+#include "AudioHardware.h"
+
+class Buffer;
+
+/**
+ * Basic API for playback and record
+ */
+class AudioLocal: public android::Thread, public AudioHardware {
+public:
+
+    virtual bool prepare(AudioHardware::SamplingRate samplingRate, int gain,
+            int mode = AudioHardware::EModeVoice);
+    virtual bool startPlaybackOrRecord(android::sp<Buffer>& buffer, int numberRepetition = 1);
+    virtual bool waitForCompletion();
+    virtual void stopPlaybackOrRecord();
+
+    virtual ~AudioLocal();
+protected:
+    AudioLocal();
+
+    virtual bool doPrepare(AudioHardware::SamplingRate, int samplesInOneGo) = 0;
+    virtual bool doPlaybackOrRecord(android::sp<Buffer>& buffer) = 0;
+    virtual void doStop() = 0;
+    virtual void releaseHw() {};
+
+private:
+
+
+    bool threadLoop();
+
+    enum AudioCommand{
+        ECmNone = 0,
+        ECmInitialize,
+        ECmRun,
+        ECmStop,
+        ECmThreadStop // terminate the thread
+    };
+
+    bool issueCommandAndWaitForCompletion(AudioCommand command);
+
+protected:
+
+private:
+    // only one command at a time.
+    // Thus, all parameters can be stored here.
+    AudioHardware::SamplingRate mSamplingRate;
+
+    android::sp<Buffer> mBuffer;
+    int mNumberRepetition;
+    int mCurrentRepeat;
+
+    enum AudioState{
+        EStNone,
+        EStCreated,
+        EStInitialized,
+        EStRunning  // playing or recording
+    };
+    volatile AudioState mState;
+    volatile AudioCommand mCurrentCommand;
+
+
+    static const int COMMAND_WAIT_TIME_MSEC = 4000;
+
+    Semaphore mClientCommandWait;
+    Semaphore mClientCompletionWait;
+    Semaphore mAudioThreadWait;
+
+    bool mCommandResult;
+    bool mCompletionResult;
+};
+
+#endif // CTSAUDIO_AUDIOLOCAL_H
diff --git a/suite/audio_quality/lib/include/audio/AudioPlaybackLocal.h b/suite/audio_quality/lib/include/audio/AudioPlaybackLocal.h
new file mode 100644
index 0000000..94422af
--- /dev/null
+++ b/suite/audio_quality/lib/include/audio/AudioPlaybackLocal.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+
+#ifndef CTSAUDIO_AUDIOPLAYBACKLOCAL_H
+#define CTSAUDIO_AUDIOPLAYBACKLOCAL_H
+
+#include <utils/String8.h>
+
+#include <tinyalsa/asoundlib.h>
+
+#include "AudioLocal.h"
+
+
+class AudioPlaybackLocal: public AudioLocal {
+public:
+    AudioPlaybackLocal(int hwId);
+    virtual ~AudioPlaybackLocal();
+protected:
+    bool doPrepare(AudioHardware::SamplingRate, int samplesInOneGo);
+    bool doPlaybackOrRecord(android::sp<Buffer>& buffer);
+    void doStop();
+    void releaseHw();
+
+private:
+    int mHwId;
+    struct pcm* mPcmHandle;
+    // unit playback samples
+    int mSamples;
+    // unit playback sizes
+    int mSizes;
+};
+
+
+#endif // CTSAUDIO_AUDIOPLAYBACKLOCAL_H
diff --git a/suite/audio_quality/lib/include/audio/AudioProtocol.h b/suite/audio_quality/lib/include/audio/AudioProtocol.h
new file mode 100644
index 0000000..83ba922
--- /dev/null
+++ b/suite/audio_quality/lib/include/audio/AudioProtocol.h
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+
+#ifndef CTSAUDIO_AUDIOPROTOCOL_H
+#define CTSAUDIO_AUDIOPROTOCOL_H
+
+#include <stdint.h>
+
+#include <utils/StrongPointer.h>
+#include "Log.h"
+#include "audio/Buffer.h"
+#include "ClientSocket.h"
+
+#define U32_ENDIAN_SWAP(x) ( ((x) & 0x000000ff)<<24 | ((x) & 0x0000ff00)<<8 | \
+        ((x) & 0x00ff0000)>>8 | ((x) & 0xff000000)>>24 )
+
+class AudioParam {
+public:
+    bool mStereo;
+    uint32_t mSamplingF;
+    uint32_t mMode;
+    uint32_t mNumberRepetition; // only for playback
+    uint32_t mVolume;
+    uint32_t mId;
+    android::sp<Buffer> mBuffer;
+};
+
+class AudioProtocol {
+public:
+    enum CommandId {
+        ECmdStart               = 0x12340001, //not actual command
+        ECmdDownload            = 0x12340001,
+        ECmdStartPlayback       = 0x12340002,
+        ECmdStopPlayback        = 0x12340003,
+        ECmdStartRecording      = 0x12340004,
+        ECmdStopRecording       = 0x12340005,
+        ECmdLast                = 0x12340006, // not actual command
+    };
+
+    static const uint32_t REPLY_HEADER_SIZE = 12;
+    // up to 5 parameters for command / reply
+    class ProtocolParam {
+    public:
+        void* param[5];
+    };
+
+    virtual ~AudioProtocol() {
+        //LOGD("~AudioProtocol %x", this);
+    };
+
+    /// default implementation, no param, no payload
+    virtual bool sendCommand(AudioParam& param);
+    /// default implementation, no param, no payload
+    virtual bool handleReply(const uint32_t* data, AudioParam* param);
+
+    /**
+     * read header of reply and returns CommandId of reply.
+     * @param socket socket to read
+     * @param data pointer to buffer to store header, it should be uint32_t[3]
+     * @param id types of reply
+     * @return true if everything OK
+     */
+    static bool handleReplyHeader(ClientSocket& socket, uint32_t* data, CommandId& id);
+
+protected:
+    AudioProtocol(ClientSocket& socket, uint32_t command)
+        : mCommand(command),
+          mSocket(socket) {};
+
+    bool sendData(const char* data, int len) {
+        return mSocket.sendData(data, len);
+    };
+
+    bool checkHeaderId(const uint32_t* data, uint32_t command);
+    bool readData(char* data, int len) {
+        return mSocket.readData(data, len);
+    };
+
+protected:
+    int mBuffer[8];
+private:
+    uint32_t mCommand;
+    ClientSocket& mSocket;
+
+};
+
+class CmdDownload: public AudioProtocol {
+public:
+    CmdDownload(ClientSocket& socket)
+        : AudioProtocol(socket, ECmdDownload) {};
+    virtual ~CmdDownload() {};
+    virtual bool sendCommand(AudioParam& param);
+};
+
+
+class CmdStartPlayback: public AudioProtocol {
+public:
+    CmdStartPlayback(ClientSocket& socket)
+        : AudioProtocol(socket, ECmdStartPlayback) {};
+    virtual ~CmdStartPlayback() {};
+    virtual bool sendCommand(AudioParam& param);
+};
+
+class CmdStopPlayback: public AudioProtocol {
+public:
+    CmdStopPlayback(ClientSocket& socket)
+        : AudioProtocol(socket, ECmdStopPlayback) {};
+    virtual ~CmdStopPlayback() {};
+};
+
+class CmdStartRecording: public AudioProtocol {
+public:
+    CmdStartRecording(ClientSocket& socket)
+        : AudioProtocol(socket, ECmdStartRecording) {};
+    virtual ~CmdStartRecording() {};
+
+    virtual bool sendCommand(AudioParam& param);
+
+    virtual bool handleReply(const uint32_t* data, AudioParam* param);
+};
+
+class CmdStopRecording: public AudioProtocol {
+public:
+    CmdStopRecording(ClientSocket& socket)
+        : AudioProtocol(socket, ECmdStopRecording) {};
+    virtual ~CmdStopRecording() {};
+};
+
+
+
+
+#endif // CTSAUDIO_AUDIOPROTOCOL_H
diff --git a/suite/audio_quality/lib/include/audio/AudioRecordingLocal.h b/suite/audio_quality/lib/include/audio/AudioRecordingLocal.h
new file mode 100644
index 0000000..614ed6d
--- /dev/null
+++ b/suite/audio_quality/lib/include/audio/AudioRecordingLocal.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+
+#ifndef CTSAUDIO_AUDIORECORDINGLOCAL_H
+#define CTSAUDIO_AUDIORECORDINGLOCAL_H
+
+#include <utils/String8.h>
+
+#include <tinyalsa/asoundlib.h>
+
+#include "AudioLocal.h"
+
+
+class AudioRecordingLocal: public AudioLocal {
+public:
+    AudioRecordingLocal(int hwId);
+    virtual ~AudioRecordingLocal();
+protected:
+    bool doPrepare(AudioHardware::SamplingRate, int samplesInOneGo);
+    bool doPlaybackOrRecord(android::sp<Buffer>& buffer);
+    void doStop();
+    void releaseHw();
+
+private:
+    int mHwId;
+    struct pcm* mPcmHandle;
+    // unit recording samples
+    int mSamples;
+    // unit recording sizes
+    int mSizes;
+    // alsa buffer size
+    int mBufferSize;
+};
+
+
+#endif // CTSAUDIO_AUDIORECORDINGLOCAL_H
diff --git a/suite/audio_quality/lib/include/audio/AudioRemote.h b/suite/audio_quality/lib/include/audio/AudioRemote.h
new file mode 100644
index 0000000..1268ee0
--- /dev/null
+++ b/suite/audio_quality/lib/include/audio/AudioRemote.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+
+#ifndef CTSAUDIO_AUDIOREMOTE_H
+#define CTSAUDIO_AUDIOREMOTE_H
+#include <utils/StrongPointer.h>
+#include "AudioHardware.h"
+// real implementation
+#include "RemoteAudio.h"
+
+
+// wrapper in AudioHardware interface
+class AudioRemote: public AudioHardware {
+public:
+    virtual bool prepare(AudioHardware::SamplingRate samplingRate, int volume,
+            int mode = AudioHardware::EModeVoice);
+
+protected:
+    AudioRemote(android::sp<RemoteAudio>& remote);
+    virtual ~AudioRemote() {};
+
+protected:
+    android::sp<RemoteAudio> mRemote;
+    AudioHardware::SamplingRate mSamplingRate;
+    int mVolume;
+    int mMode;
+};
+
+class AudioRemotePlayback: public AudioRemote {
+public:
+    AudioRemotePlayback(android::sp<RemoteAudio>& remote);
+    virtual ~AudioRemotePlayback() {};
+    virtual bool startPlaybackOrRecord(android::sp<Buffer>& buffer, int numberRepetition = 1);
+    virtual bool waitForCompletion();
+    virtual void stopPlaybackOrRecord();
+    bool startPlaybackForRemoteData(int id, bool stereo,  int numberRepetition = 1);
+};
+
+class AudioRemoteRecording: public AudioRemote {
+public:
+    AudioRemoteRecording(android::sp<RemoteAudio>& remote);
+    virtual ~AudioRemoteRecording() {};
+    virtual bool startPlaybackOrRecord(android::sp<Buffer>& buffer, int numberRepetition = 1);
+    virtual bool waitForCompletion();
+    virtual void stopPlaybackOrRecord();
+};
+
+
+#endif // CTSAUDIO_AUDIOREMOTE_H
diff --git a/suite/audio_quality/lib/include/audio/AudioSignalFactory.h b/suite/audio_quality/lib/include/audio/AudioSignalFactory.h
new file mode 100644
index 0000000..1c985cc
--- /dev/null
+++ b/suite/audio_quality/lib/include/audio/AudioSignalFactory.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+
+#ifndef CTSAUDIO_AUDIOSIGNALFACTORY_H
+#define CTSAUDIO_AUDIOSIGNALFACTORY_H
+
+#include <utils/StrongPointer.h>
+
+#include "AudioHardware.h"
+#include "Buffer.h"
+/**
+ * factory for creating various audio signals
+ */
+class AudioSignalFactory {
+public:
+    static android::sp<Buffer> generateSineWave(AudioHardware::BytesPerSample BPS,
+            int maxPositive, AudioHardware::SamplingRate samplingRate, int signalFreq, int samples,
+            bool stereo = true);
+    static android::sp<Buffer> generateWhiteNoise(AudioHardware::BytesPerSample BPS,
+            int maxPositive, int samples, bool stereo = true);
+    static android::sp<Buffer> generateZeroSound(AudioHardware::BytesPerSample BPS,
+            int samples, bool stereo = true);
+};
+
+
+#endif // CTSAUDIO_AUDIOSIGNALFACTORY_H
diff --git a/suite/audio_quality/lib/include/audio/Buffer.h b/suite/audio_quality/lib/include/audio/Buffer.h
new file mode 100644
index 0000000..343c502
--- /dev/null
+++ b/suite/audio_quality/lib/include/audio/Buffer.h
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+#ifndef CTSAUDIO_BUFFER_H
+#define CTSAUDIO_BUFFER_H
+
+#include <stdlib.h>
+#include <string.h>
+#include <utils/String8.h>
+
+#include <utils/RefBase.h>
+
+#include <Log.h>
+
+/**
+ * Buffer passed for audio playback and recording
+ * The buffer is supposed to be used with sp to guarantee that audio thread can
+ * access it even if the client thread is dead.
+ */
+class Buffer: public virtual android::RefBase {
+public:
+    Buffer(size_t capacity, size_t size = 0, bool stereo = true);
+
+    virtual ~Buffer();
+
+    inline size_t getCapacity() {
+        return mCapacity;
+    };
+
+    inline size_t getSize() {
+        return mSize;
+    };
+
+    inline size_t getSamples() {
+        return (getSize() / (isStereo() ? 4 : 2));
+    };
+
+    inline void setSize(size_t size) {
+        mSize = size;
+    };
+
+    inline void increaseSize(size_t size) {
+        mSize += size;
+    }
+    inline char* getData() {
+        return mData;
+    };
+
+    inline void setData(char* data, size_t len) {
+        ASSERT(len <= mCapacity);
+        memcpy(mData, data, len);
+        mSize = len;
+    };
+
+    inline char* getUnhanledData() {
+        return mData + mHandled;
+    };
+
+    inline bool bufferHandled() {
+        return mSize <= mHandled;
+    };
+
+    inline void restart() {
+        mHandled = 0;
+    };
+    /// size was recorded
+    inline void increaseHandled(size_t size) {
+        mHandled += size;
+    };
+
+    inline void setHandled(size_t size) {
+        mHandled = size;
+    }
+    /// amount recorded
+    inline size_t amountHandled() {
+        return mHandled;
+    };
+
+    inline size_t amountToHandle() {
+        return mSize - mHandled;
+    };
+
+    inline bool isStereo() {
+        return mStereo;
+    };
+    enum ConvertOption {
+        EKeepCh0 = 0,
+        EKeepCh1 = 1,
+        EAverage = 2
+    };
+    /// change stereo buffer to mono
+    void changeToMono(ConvertOption option);
+    /// change mono buffer to stereo. This does not increase allocated memory.
+    /// So it will fail if capacity is not big enough.
+    bool changeToStereo();
+
+    /// save the buffer to file
+    /// extension appropriate for the data type will be appended to file name
+    bool saveToFile(const android::String8& filename);
+
+    bool operator ==(const Buffer& b) const;
+
+    /// load raw data from given file.
+    /// data format is decided by extension
+    /// .r2s: 16 bps, stereo
+    /// .r2m: 16bps, mono
+    static Buffer* loadFromFile(const android::String8& filename);
+private:
+    // max data that can be hold
+    size_t mCapacity;
+    // data stored for playback / to store for recording
+    size_t mSize;
+    // how much data was handled / recorded
+    size_t mHandled;
+    // stereo or mono
+    bool mStereo;
+    // payload
+    char* mData;
+};
+
+
+
+#endif // CTSAUDIO_BUFFER_H
diff --git a/suite/audio_quality/lib/include/audio/RemoteAudio.h b/suite/audio_quality/lib/include/audio/RemoteAudio.h
new file mode 100644
index 0000000..4c587e6
--- /dev/null
+++ b/suite/audio_quality/lib/include/audio/RemoteAudio.h
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+
+#ifndef CTSAUDIO_REMOTEAUDIO_H
+#define CTSAUDIO_REMOTEAUDIO_H
+
+#include <map>
+
+#include <utils/Looper.h>
+#include <utils/StrongPointer.h>
+#include <utils/threads.h>
+
+#include "audio/Buffer.h"
+#include "AudioProtocol.h"
+#include "ClientSocket.h"
+#include "Semaphore.h"
+
+class CommandHandler;
+/**
+ * Tcp communication runs in a separate thread,
+ * and client can communicate using public APIs
+ * Assumption: only one command at a time. No other command can come
+ * while a command is pending.
+ */
+class RemoteAudio: public android::Thread {
+public:
+
+    RemoteAudio(ClientSocket& socket);
+    virtual ~RemoteAudio();
+
+    /** launch a thread, and connect to host */
+    bool init(int port);
+    bool downloadData(const android::String8 name, android::sp<Buffer>& buffer, int& id);
+    // <0 : not found
+    int getDataId(const android::String8& name);
+    bool startPlayback(bool stereo, int samplingF, int mode, int volume,
+            int id, int numberRepetition);
+    void stopPlayback();
+    bool waitForPlaybackCompletion();
+    // buffer.getSize() determines number of samples
+    bool startRecording(bool stereo, int samplingF, int mode, int volume,
+            android::sp<Buffer>& buffer);
+    bool waitForRecordingCompletion();
+    void stopRecording();
+    /** should be called before RemoteAudio is destroyed */
+    void release();
+
+private:
+    RemoteAudio(const RemoteAudio&);
+
+    bool threadLoop();
+    void wakeClient(bool result);
+    void cleanup(bool notifyClient);
+
+    bool handlePacket();
+    static int socketRxCallback(int fd, int events, void* data);
+
+    class CommandHandler;
+    void sendCommand(android::sp<android::MessageHandler>& command);
+
+    // this is just semaphore wait without any addition
+    bool waitForCompletion(android::sp<android::MessageHandler>& command, int timeInMSec);
+    // common code for waitForXXXCompletion
+    bool waitForPlaybackOrRecordingCompletion(
+            android::sp<android::MessageHandler>& commandHandler);
+    // common code for stopXXX
+    void doStop(android::sp<android::MessageHandler>& commandHandler, AudioProtocol::CommandId id);
+
+    CommandHandler* toCommandHandler(android::sp<android::MessageHandler>& command) {
+        return reinterpret_cast<CommandHandler*>(command.get());
+    };
+
+private:
+    bool mExitRequested;
+    bool mInitResult;
+    // used only for notifying successful init
+    Semaphore mInitWait;
+
+
+    enum EventId {
+        EIdSocket = 1,
+    };
+    static const int CLIENT_WAIT_TIMEOUT_MSEC = 2000;
+    int mPort;
+    ClientSocket& mSocket;
+
+
+    android::sp<android::Looper> mLooper;
+
+    friend class CommandHandler;
+
+    class CommandHandler: public android::MessageHandler {
+    public:
+        enum ClientCommands {
+            EExit = 1,
+        };
+        CommandHandler(RemoteAudio& thread, int command)
+            : mThread(thread),
+              mMessage(command),
+              mNotifyOnReply(false),
+              mActive(false) {};
+        virtual ~CommandHandler() {};
+        void handleMessage(const android::Message& message);
+        bool timedWait(int timeInMSec) {
+            return mClientWait.timedWait(timeInMSec);
+        };
+        AudioParam& getParam() {
+            return mParam;
+        };
+        android::Message& getMessage() {
+            return mMessage;
+        };
+
+    private:
+        RemoteAudio& mThread;
+        AudioParam mParam;
+        Semaphore mClientWait;
+        android::Mutex mStateLock;
+        android::Message mMessage;
+        bool mResult;
+        bool mNotifyOnReply;
+        bool mActive;
+        friend class RemoteAudio;
+    };
+    android::sp<android::MessageHandler> mDownloadHandler;
+    android::sp<android::MessageHandler> mPlaybackHandler;
+    android::sp<android::MessageHandler> mRecordingHandler;
+
+    AudioProtocol* mCmds[AudioProtocol::ECmdLast - AudioProtocol::ECmdStart];
+    int mDownloadId;
+    std::map<int, android::sp<Buffer> > mBufferList;
+    std::map<android::String8, int> mIdMap;
+};
+
+
+
+#endif // CTSAUDIO_REMOTEAUDIO_H
diff --git a/suite/audio_quality/lib/include/task/ModelBuilder.h b/suite/audio_quality/lib/include/task/ModelBuilder.h
new file mode 100644
index 0000000..c6102d7
--- /dev/null
+++ b/suite/audio_quality/lib/include/task/ModelBuilder.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+
+#ifndef CTSAUDIO_MODELBUILDER_H
+#define CTSAUDIO_MODELBUILDER_H
+
+#include <utils/String8.h>
+#include "TaskAll.h"
+
+class TiXmlElement;
+
+
+class GenericFactory;
+
+/**
+ * Class to parse Test description XML and generate test model with TestCase in top
+ */
+
+class ModelBuilder {
+public:
+    ModelBuilder();
+    ModelBuilder(GenericFactory* factory);
+    virtual ~ModelBuilder();
+
+    /**
+     * parse given xml with test case or batch. When caseOnly is true, only test case can be in.
+     */
+    virtual TaskGeneric* parseTestDescriptionXml(const android::String8& xmlFileName,
+            bool caseOnly = false);
+
+    struct ChildInfo {
+        TaskGeneric::TaskType type;
+        bool mandatory; // whether the child is mandatory or not
+    };
+
+private:
+    virtual bool parseAttributes(const TiXmlElement& elem, TaskGeneric& task);
+    virtual TaskGeneric* parseGeneric(const TiXmlElement& elem, int tableIndex);
+    virtual TaskCase* parseCase(const TiXmlElement& root);
+    virtual TaskBatch* parseBatch(const TiXmlElement& root, const android::String8& xmlFileName);
+    virtual TaskCase* parseInclude(const TiXmlElement& elem, const android::String8& path);
+
+    struct ParsingInfo {
+        const char* name; // XML element name
+        TaskGeneric::TaskType type;
+        const ChildInfo* allowedChildren;
+        int Nchildren;
+    };
+    // no table for batch, and ETaskInvalidLast is not in either (-2)
+    static const int PARSING_TABLE_SIZE = TaskGeneric::ETaskInvalidLast - 2;
+    static ParsingInfo mParsingTable[PARSING_TABLE_SIZE];
+
+    GenericFactory* mFactory;
+
+};
+
+
+#endif // CTSAUDIO_MODELBUILDER_H
diff --git a/suite/audio_quality/lib/include/task/TaskAll.h b/suite/audio_quality/lib/include/task/TaskAll.h
new file mode 100644
index 0000000..2f04fc6
--- /dev/null
+++ b/suite/audio_quality/lib/include/task/TaskAll.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+
+#ifndef CTSAUDIO_TASKALL_H
+#define CTSAUDIO_TASKALL_H
+
+/// convenience header to include all Task stuffs
+#include "TaskGeneric.h"
+#include "TaskBatch.h"
+#include "TaskCase.h"
+#include "TaskSequential.h"
+#include "TaskProcess.h"
+#include "TaskAsync.h"
+#include "TaskInput.h"
+#include "TaskOutput.h"
+#include "TaskSound.h"
+#include "TaskSave.h"
+#include "TaskMessage.h"
+
+#endif // CTSAUDIO_TASKALL_H
diff --git a/suite/audio_quality/lib/include/task/TaskAsync.h b/suite/audio_quality/lib/include/task/TaskAsync.h
new file mode 100644
index 0000000..4a8ed32
--- /dev/null
+++ b/suite/audio_quality/lib/include/task/TaskAsync.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+
+#ifndef CTSAUDIO_TASKASYNC_H
+#define CTSAUDIO_TASKASYNC_H
+
+#include "audio/AudioHardware.h"
+#include "TaskGeneric.h"
+
+class TaskSequential;
+/**
+ * Common parent class for TaskInput and TaskOutput
+ */
+class TaskAsync: public TaskGeneric {
+public:
+    TaskAsync(TaskType type);
+    virtual ~TaskAsync();
+    virtual TaskGeneric::ExecutionResult run();
+    virtual bool parseAttribute(const android::String8& name, const android::String8& value);
+    virtual TaskGeneric::ExecutionResult start() = 0;
+    virtual TaskGeneric::ExecutionResult complete() = 0;
+
+    bool isAsynchronous() {
+        return mAsynchronous;
+    }
+
+private:
+    void makeAsynchronous() {
+        mAsynchronous = true;
+    }
+    TaskSequential* getParentSequential();
+
+protected:
+    android::String8 mId;
+    int mVolume;
+
+    enum DeviceType {
+        EDeviceHost,
+        EDeviceDUT
+    };
+
+    DeviceType mDeviceType;
+
+    AudioHardware::AudioMode mMode;
+
+private:
+    bool mAsynchronous;
+
+};
+
+
+
+#endif // CTSAUDIO_TASKASYNC_H
diff --git a/suite/audio_quality/lib/include/task/TaskBatch.h b/suite/audio_quality/lib/include/task/TaskBatch.h
new file mode 100644
index 0000000..add8590
--- /dev/null
+++ b/suite/audio_quality/lib/include/task/TaskBatch.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+
+#ifndef CTSAUDIO_TASKBATCH_H
+#define CTSAUDIO_TASKBATCH_H
+
+#include "TaskGeneric.h"
+
+
+class TaskBatch: public TaskGeneric {
+public:
+    TaskBatch();
+    virtual ~TaskBatch();
+    virtual bool addChild(TaskGeneric* child);
+    virtual TaskGeneric::ExecutionResult run();
+
+};
+
+
+
+#endif // CTSAUDIO_TASKBATCH_H
diff --git a/suite/audio_quality/lib/include/task/TaskCase.h b/suite/audio_quality/lib/include/task/TaskCase.h
new file mode 100644
index 0000000..323119c
--- /dev/null
+++ b/suite/audio_quality/lib/include/task/TaskCase.h
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+
+#ifndef CTSAUDIO_TASKCASE_H
+#define CTSAUDIO_TASKCASE_H
+
+#include <stdint.h>
+#include <map>
+#include <list>
+#include <utility>
+#include <utils/String8.h>
+#include <utils/StrongPointer.h>
+#include "Log.h"
+#include "audio/Buffer.h"
+#include "TaskGeneric.h"
+
+class RemoteAudio;
+class ClientInterface;
+
+class TaskCase: public TaskGeneric {
+public:
+    TaskCase();
+    virtual ~TaskCase();
+    virtual bool addChild(TaskGeneric* child);
+    virtual TaskGeneric::ExecutionResult run();
+
+    bool getCaseName(android::String8& name);
+
+    bool registerBuffer(const android::String8& name, android::sp<Buffer>& buffer);
+    // update already existing buffer. Actually the old buffer will be deleted.
+    bool updateBuffer(const android::String8& name, android::sp<Buffer>& buffer);
+    /// find buffer with given id. sp will be NULL if not found
+    android::sp<Buffer> findBuffer(const android::String8& name);
+    typedef std::pair<android::String8, android::sp<Buffer> > BufferPair;
+    /// find all buffers with given regular expression. returns NULL if not found
+    std::list<BufferPair>*  findAllBuffers(const android::String8& re);
+
+    android::sp<RemoteAudio>& getRemoteAudio();
+
+    class Value {
+    public:
+        enum Type {
+            ETypeDouble,
+            ETypeI64
+        };
+        inline Value(): mType(ETypeDouble) {};
+        inline Value(Type type): mType(type) {};
+        inline Value(double val): mType(ETypeDouble) {
+            setDouble(val);
+        };
+        inline Value(int64_t val): mType(ETypeI64){
+            setInt64(val);
+        };
+        inline Type getType() {
+            return mType;
+        };
+        inline void setType(Type type) {
+            mType = type;
+        };
+        inline void setDouble(double val) {
+            mValue[0] = val;
+            mType = ETypeDouble;
+            //LOGD("Value set %f 0x%x", val, this);
+        };
+        inline double getDouble() {
+            //LOGD("Value get %f 0x%x", mValue[0], this);
+            return mValue[0];
+        };
+        inline void setInt64(int64_t val) {
+            int64_t* data = reinterpret_cast<int64_t*>(mValue);
+            data[0] = val;
+            mType = ETypeI64;
+            //LOGD("Value set %lld 0x%x", val, this);
+        }
+        inline int64_t getInt64() {
+            int64_t* data = reinterpret_cast<int64_t*>(mValue);
+            //LOGD("Value get %lld 0x%x", data[0], this);
+            return data[0];
+        }
+        void* getPtr() {
+            return mValue;
+        }
+        bool operator ==(const Value& b) const {
+            return ((mValue[0] == b.mValue[0]) && (mType == b.mType));
+        };
+
+    private:
+        double mValue[1];
+        Type mType;
+    };
+
+    bool registerValue(const android::String8& name, Value& val);
+    bool updateValue(const android::String8& name, Value& val);
+    bool findValue(const android::String8& name, Value& val);
+    typedef std::pair<android::String8, Value> ValuePair;
+    /// find all Values with given regular expression. returns NULL if not found
+    std::list<ValuePair>*  findAllValues(const android::String8& re);
+
+    bool registerIndex(const android::String8& name, int value = -1);
+    bool updateIndex(const android::String8& name, int value);
+    bool findIndex(const android::String8& name, int& val);
+    typedef std::pair<android::String8, int> IndexPair;
+    /// find all Indices with given regular expression. returns NULL if not found
+    std::list<IndexPair>*  findAllIndices(const android::String8& re);
+
+    /**
+     * Translate variable name like $i into index variable
+     * All xxxValue and xxxBuffer calls do translation inside.
+     */
+    bool translateVarName(const android::String8& orig, android::String8& translated);
+
+private:
+    void releaseRemoteAudio();
+
+private:
+    std::map<android::String8, android::sp<Buffer> > mBufferList;
+    std::map<android::String8, int> mIndexList;
+    std::map<android::String8, Value> mValueList;
+    ClientInterface* mClient;
+};
+
+
+#endif // CTSAUDIO_TASKCASE_H
diff --git a/suite/audio_quality/lib/include/task/TaskGeneric.h b/suite/audio_quality/lib/include/task/TaskGeneric.h
new file mode 100644
index 0000000..6dc269e
--- /dev/null
+++ b/suite/audio_quality/lib/include/task/TaskGeneric.h
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+
+#ifndef CTSAUDIO_TASKGENERIC_H
+#define CTSAUDIO_TASKGENERIC_H
+#include <string.h>
+#include <stdio.h>
+#include <list>
+#include <map>
+#include <set>
+#include <utils/String8.h>
+
+
+class TaskCase;
+
+class TaskGeneric {
+public:
+    // internal model for xml tags
+    enum TaskType {
+        ETaskInvalid        = 0,
+        ETaskBatch          = 1,
+        ETaskCase           = 2,
+        ETaskSetup          = 3,
+        ETaskAction         = 4,
+        ETaskSequential     = 5,
+        ETaskProcess        = 6,
+        ETaskInput          = 7,
+        ETaskOutput         = 8,
+        ETaskSound          = 9,
+        ETaskSave           = 10,
+        ETaskMessage        = 11,
+        ETaskInvalidLast    = 12,
+        //no ETaskInclude include does not involve any action.
+    };
+
+    TaskGeneric(TaskType type);
+
+    virtual ~TaskGeneric();
+
+    inline TaskType getType() {
+        return mType;
+    }
+
+    enum ExecutionResult {
+        EResultOK           = 0,
+        // continue in the current loop. will result in skipping all subsequent steps
+        EResultContinue     = 1,
+        // get out of the current loop
+        EResultBreakOneLoop = 2,
+        // error which cannot be continued. effect is the same as fail. stops everything.
+        EResultError        = 3,
+        // test failed. stops everything.
+        EResultFail         = 4,
+        // test passed.
+        EResultPass         = 5
+    };
+
+    /**
+     * default implementation for adding child action
+     * Ownership of the child is passed to this instance, and child will be destroyed in parent's
+     * destructor.
+     * @return false on error
+     */
+    virtual bool addChild(TaskGeneric* child);
+
+    virtual ExecutionResult run();
+
+    /// can be NULL if parent does not exist
+    TaskGeneric* getParent();
+    /// can be NULL if TestCase does not exist (unit testing?)
+    TaskCase* getTestCase();
+
+    void setParent(TaskGeneric* parent);
+
+    /**
+     * parse attribute from XML DOM. name/value pair will be passed for all attributes.
+     */
+    virtual bool parseAttribute(const android::String8& name, const android::String8& value);
+
+    bool forEachChild(bool (*runForEachChild)(TaskGeneric* child, void* data), void* data);
+
+protected:
+    /// used by child instance to register allowed attributes
+    /// keys array should end with NULL
+    void registerSupportedStringAttributes(const android::String8* keys[]);
+    bool addStringAttribute(const android::String8& key, const android::String8& value);
+    bool findStringAttribute(const android::String8& key, android::String8& value);
+    inline std::list<TaskGeneric*>& getChildren() {
+        return mChildren;
+    };
+
+private:
+    TaskType mType;
+    TaskGeneric* mParent;
+    std::list<TaskGeneric*> mChildren;
+
+    std::set<android::String8> mAllowedStringAttributes;
+    std::map<android::String8, android::String8> mStringAttributes;
+
+};
+
+
+#endif // CTSAUDIO_TASKGENERIC_H
diff --git a/suite/audio_quality/lib/include/task/TaskInput.h b/suite/audio_quality/lib/include/task/TaskInput.h
new file mode 100644
index 0000000..ddb557f
--- /dev/null
+++ b/suite/audio_quality/lib/include/task/TaskInput.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+
+#ifndef CTSAUDIO_TASKINPUT_H
+#define CTSAUDIO_TASKINPUT_H
+
+#include <utils/StrongPointer.h>
+#include "TaskAsync.h"
+#include "audio/AudioHardware.h"
+#include "audio/Buffer.h"
+
+class TaskInput: public TaskAsync {
+public:
+    TaskInput();
+    virtual ~TaskInput();
+    virtual bool parseAttribute(const android::String8& name, const android::String8& value);
+    virtual TaskGeneric::ExecutionResult start();
+    virtual TaskGeneric::ExecutionResult complete();
+private:
+    int mRecordingTimeInMs;
+    android::sp<AudioHardware> mHw;
+    android::sp<Buffer> mBuffer;
+};
+
+
+#endif // CTSAUDIO_TASKINPUT_H
diff --git a/suite/audio_quality/lib/include/task/TaskMessage.h b/suite/audio_quality/lib/include/task/TaskMessage.h
new file mode 100644
index 0000000..825bb7e
--- /dev/null
+++ b/suite/audio_quality/lib/include/task/TaskMessage.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+
+#ifndef CTSAUDIO_TASKMESSAGE_H
+#define CTSAUDIO_TASKMESSAGE_H
+
+#include "TaskGeneric.h"
+
+
+class TaskMessage: public TaskGeneric {
+public:
+    TaskMessage();
+    virtual ~TaskMessage();
+    virtual TaskGeneric::ExecutionResult run();
+    virtual bool parseAttribute(const android::String8& name, const android::String8& value);
+};
+
+
+#endif // CTSAUDIO_TASKMESSAGE_H
diff --git a/suite/audio_quality/lib/include/task/TaskOutput.h b/suite/audio_quality/lib/include/task/TaskOutput.h
new file mode 100644
index 0000000..928aae2
--- /dev/null
+++ b/suite/audio_quality/lib/include/task/TaskOutput.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+
+#ifndef CTSAUDIO_TASKOUTPUT_H
+#define CTSAUDIO_TASKOUTPUT_H
+
+#include "TaskAsync.h"
+#include "audio/AudioHardware.h"
+
+class TaskOutput: public TaskAsync {
+public:
+    TaskOutput();
+    virtual ~TaskOutput();
+    virtual bool parseAttribute(const android::String8& name, const android::String8& value);
+    virtual TaskGeneric::ExecutionResult start();
+    virtual TaskGeneric::ExecutionResult complete();
+
+private:
+    android::sp<AudioHardware> mHw;
+    bool mWaitForCompletion;
+};
+
+
+#endif // CTSAUDIO_TASKOUTPUT_H
diff --git a/suite/audio_quality/lib/include/task/TaskProcess.h b/suite/audio_quality/lib/include/task/TaskProcess.h
new file mode 100644
index 0000000..d043d98
--- /dev/null
+++ b/suite/audio_quality/lib/include/task/TaskProcess.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+
+#ifndef CTSAUDIO_TASKPROCESS_H
+#define CTSAUDIO_TASKPROCESS_H
+
+#include <vector>
+
+#include <UniquePtr.h>
+#include <utils/StrongPointer.h>
+
+#include "audio/Buffer.h"
+#include "TaskGeneric.h"
+#include "TaskCase.h"
+#include "BuiltinProcessing.h"
+#include "SignalProcessingInterface.h"
+
+class TaskProcess: public TaskGeneric {
+public:
+    TaskProcess();
+    virtual ~TaskProcess();
+    virtual TaskGeneric::ExecutionResult run();
+    virtual bool parseAttribute(const android::String8& name, const android::String8& value);
+private:
+    TaskGeneric::ExecutionResult doRun(bool builtin);
+
+    class Param;
+    bool parseParams(std::vector<Param>& list, const char* str, bool isInput);
+    //typedef necessary to prevent compiler's confusion
+    typedef void* void_ptr;
+    typedef UniquePtr<TaskCase::Value> UniqueValue;
+    typedef UniquePtr<android::sp<Buffer> > UniqueBuffer;
+    /// construct Buffers and Values for calling builtin functions.
+    /// all constructed stuffs automatically deleted by the passed UniquePtrs
+    bool prepareParams(std::vector<TaskProcess::Param>& list,
+            const bool* inputTypes,
+            UniquePtr<void_ptr, DefaultDelete<void_ptr[]> > & ptrs,
+            UniquePtr<UniqueValue, DefaultDelete<UniqueValue[]> > & values,
+            UniquePtr<UniqueBuffer, DefaultDelete<UniqueBuffer[]> > & buffers,
+            bool isInput);
+
+private:
+    enum ProcessType {
+        EBuiltin,
+        EScript
+    };
+    ProcessType mType;
+    android::String8 mName; // buit-in function or script name
+
+    enum ParamType {
+        EId,
+        EVal,
+        EConst
+    };
+    class Param {
+    public:
+        Param(ParamType type, android::String8& string);
+        Param(TaskCase::Value& val);
+        ParamType getType();
+        android::String8& getParamString();
+        TaskCase::Value& getValue();
+        TaskCase::Value* getValuePtr();
+        inline bool isIdType() {
+            return (mType == EId);
+        }
+    private:
+        ParamType mType;
+        android::String8 mString;
+        TaskCase::Value mValue;
+    };
+
+    std::vector<Param> mInput;
+    std::vector<Param> mOutput;
+    BuiltinProcessing mBuiltin;
+    UniquePtr<SignalProcessingInterface> mSp;
+};
+
+
+#endif // CTSAUDIO_TASKPROCESS_H
diff --git a/suite/audio_quality/lib/include/task/TaskSave.h b/suite/audio_quality/lib/include/task/TaskSave.h
new file mode 100644
index 0000000..bffc0b5
--- /dev/null
+++ b/suite/audio_quality/lib/include/task/TaskSave.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+
+#ifndef CTSAUDIO_TASKSAVE_H
+#define CTSAUDIO_TASKSAVE_H
+
+#include "TaskGeneric.h"
+
+
+class TaskSave: public TaskGeneric {
+public:
+    TaskSave();
+    virtual ~TaskSave();
+    virtual TaskGeneric::ExecutionResult run();
+
+private:
+    bool handleFile();
+    bool handleReport();
+};
+
+
+#endif // CTSAUDIO_TASKSAVE_H
diff --git a/suite/audio_quality/lib/include/task/TaskSequential.h b/suite/audio_quality/lib/include/task/TaskSequential.h
new file mode 100644
index 0000000..b5c3ecc
--- /dev/null
+++ b/suite/audio_quality/lib/include/task/TaskSequential.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+
+#ifndef CTSAUDIO_TASKSEQUENTIAL_H
+#define CTSAUDIO_TASKSEQUENTIAL_H
+
+#include <utils/String8.h>
+#include <list>
+#include "TaskGeneric.h"
+
+class TaskAsync;
+
+class TaskSequential: public TaskGeneric {
+public:
+    TaskSequential();
+    virtual ~TaskSequential();
+    virtual TaskGeneric::ExecutionResult run();
+    virtual bool parseAttribute(const android::String8& name, const android::String8& value);
+    /**
+     * Queue async task for asynchronous execution (= call complete later)
+     * If the task is already queued, it will not be queued again ,but will just return true.
+     */
+    bool queueAsyncTask(TaskAsync* task);
+
+private:
+    /**
+     * Run all async tasks queued (= call complete) and dequeue them.
+     * Execution will be continued even for error, and the 1st error result will be returned.
+     */
+    TaskGeneric::ExecutionResult runAsyncTasksQueued();
+
+private:
+    int mRepeatCount;
+    android::String8 mIndexName;
+    int mRepeatIndex;
+    std::list<TaskAsync*> mAsyncTasks;
+};
+
+
+#endif // CTSAUDIO_TASKSEQUENTIAL_H
diff --git a/suite/audio_quality/lib/include/task/TaskSound.h b/suite/audio_quality/lib/include/task/TaskSound.h
new file mode 100644
index 0000000..0b5ceb5
--- /dev/null
+++ b/suite/audio_quality/lib/include/task/TaskSound.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+
+#ifndef CTSAUDIO_TASKSOUND_H
+#define CTSAUDIO_TASKSOUND_H
+#include <utils/String8.h>
+#include <utils/StrongPointer.h>
+#include "audio/Buffer.h"
+#include "TaskGeneric.h"
+
+
+class TaskSound: public TaskGeneric {
+public:
+    TaskSound();
+    virtual ~TaskSound();
+    virtual TaskGeneric::ExecutionResult run();
+    virtual bool parseAttribute(const android::String8& name, const android::String8& value);
+private:
+    android::sp<Buffer> mBuffer;
+    bool mPreload;
+};
+
+
+#endif // CTSAUDIO_TASKSOUND_H
diff --git a/suite/audio_quality/lib/src/Adb.cpp b/suite/audio_quality/lib/src/Adb.cpp
new file mode 100644
index 0000000..fbfc974
--- /dev/null
+++ b/suite/audio_quality/lib/src/Adb.cpp
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2012 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 <stdlib.h>
+
+#include <StringUtil.h>
+#include "Adb.h"
+
+Adb::Adb(const android::String8& device)
+    : mDevice(device)
+{
+
+}
+
+Adb::~Adb()
+{
+
+}
+
+bool Adb::setPortForwarding(int hostPort, int devicePort)
+{
+    android::String8 command;
+    if (command.appendFormat("forward tcp:%d tcp:%d", hostPort, devicePort) != 0) {
+        return false;
+    }
+    if (executeCommand(command) != 0) {
+        return false;
+    }
+    return true;
+}
+
+bool Adb::launchClient(const android::String8& clientBinary, const android::String8& component)
+{
+    android::String8 command;
+    if (command.appendFormat("install -r %s", clientBinary.string()) != 0) {
+        return false;
+    }
+    if (executeCommand(command) != 0) {
+        return false;
+    }
+    command.clear();
+    if (command.appendFormat("shell am start -W -n %s", component.string()) != 0) {
+        return false;
+    }
+    if (executeCommand(command) != 0) {
+        return false;
+    }
+    return true;
+}
+
+/** @param command ADB command except adb -s XYZW */
+int Adb::executeCommand(const android::String8& command)
+{
+    android::String8 adbCommand;
+    if (mDevice.empty()) {
+        if (adbCommand.appendFormat("adb %s", command.string()) != 0) {
+            return -1;
+        }
+    } else {
+        if (adbCommand.appendFormat("adb -s %s %s", mDevice.string(),
+                command.string()) != 0) {
+            return -1;
+        }
+    }
+    return system(adbCommand.string());
+}
+
diff --git a/suite/audio_quality/lib/src/Adb.h b/suite/audio_quality/lib/src/Adb.h
new file mode 100644
index 0000000..59d4b9b
--- /dev/null
+++ b/suite/audio_quality/lib/src/Adb.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+#ifndef CTSAUDIO_ADB_H
+#define CTSAUDIO_ADB_H
+
+#include <utils/String8.h>
+
+/** ADB interface to set port forwarding and launch client app */
+class Adb {
+public:
+    /// device: device number typically passed in adb's -s argument.
+    /// if device string is empty, adb command will be called without -s option.
+    Adb(const android::String8& device);
+    ~Adb();
+    bool setPortForwarding(int hostPort, int devicePort);
+    /// install given clientBinary to DUT and launch given component.
+    bool launchClient(const android::String8& clientBinary, const android::String8& component);
+private:
+    int executeCommand(const android::String8& command);
+
+private:
+    android::String8 mDevice;
+};
+
+#endif // CTSAUDIO_ADB_H
+
+
+
+
diff --git a/suite/audio_quality/lib/src/BuiltinProcessing.cpp b/suite/audio_quality/lib/src/BuiltinProcessing.cpp
new file mode 100644
index 0000000..49815ce
--- /dev/null
+++ b/suite/audio_quality/lib/src/BuiltinProcessing.cpp
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2012 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 <stdint.h>
+#include <math.h>
+#include <utils/StrongPointer.h>
+#include "audio/Buffer.h"
+#include "BuiltinProcessing.h"
+#include "Log.h"
+#include "task/TaskCase.h"
+
+// Buffer, Value, Value
+static const bool RMS_MVA_INPUT_TYPE[] = {true, false, false};
+// Value
+static const bool RMS_MVA_OUTPUT_TYPE[] = {false};
+
+BuiltinProcessing::BuiltinInfo BuiltinProcessing::BUINTIN_FN_TABLE[N_BUILTIN_FNS] =
+{
+    {
+        "rms_mva", &BuiltinProcessing::rms_mva,
+        sizeof(RMS_MVA_INPUT_TYPE)/sizeof(bool), RMS_MVA_INPUT_TYPE,
+        sizeof(RMS_MVA_OUTPUT_TYPE)/sizeof(bool), RMS_MVA_OUTPUT_TYPE,
+    }
+};
+
+BuiltinProcessing::BuiltinProcessing()
+    : mRMSPasses(0)
+{
+
+}
+
+// pass for 5 consecutive passes
+TaskGeneric::ExecutionResult BuiltinProcessing::rms_mva(void** inputs, void** outputs)
+{
+    LOGD("BuiltinProcessing::rms_mva in %x %x %x out %x",
+            inputs[0], inputs[1], inputs[2], outputs[0]);
+    android::sp<Buffer>& data(*reinterpret_cast<android::sp<Buffer>*>(inputs[0]));
+
+    int64_t passMin = (reinterpret_cast<TaskCase::Value*>(inputs[1]))->getInt64();
+    int64_t passMax = (reinterpret_cast<TaskCase::Value*>(inputs[2]))->getInt64();
+
+    int64_t rms = 0;
+    size_t samples = data->getSize()/2;
+    int16_t* rawData = reinterpret_cast<int16_t*>(data->getData());
+    double energy = 0.0f;
+    for (size_t i = 0; i < samples; i++) {
+        energy += (rawData[i] * rawData[i]);
+    }
+    rms = (int64_t)sqrt(energy/samples);
+
+    TaskGeneric::ExecutionResult result = TaskGeneric::EResultOK;
+    if (rms < passMin) {
+        LOGW("Volume %lld low compared to min %lld max %lld", rms, passMin, passMax);
+        mRMSPasses = 0;
+    } else if (rms <= passMax) {
+        LOGW("Volume %lld OK compared to min %lld max %lld", rms, passMin, passMax);
+        mRMSPasses++;
+        if (mRMSPasses >= RMS_CONTINUOUS_PASSES) {
+            //mRMSPasses = 0;
+            result = TaskGeneric::EResultPass;
+        }
+    } else {
+        LOGW("Volume %lld high compared to min %lld max %lld", rms, passMin, passMax);
+        mRMSPasses = 0;
+    }
+    TaskCase::Value* rmsVal = reinterpret_cast<TaskCase::Value*>(outputs[0]);
+    rmsVal->setInt64(rms);
+
+    return result;
+}
+
+
diff --git a/suite/audio_quality/lib/src/ClientImpl.cpp b/suite/audio_quality/lib/src/ClientImpl.cpp
new file mode 100644
index 0000000..615cd2a
--- /dev/null
+++ b/suite/audio_quality/lib/src/ClientImpl.cpp
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2012 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 "Log.h"
+
+#include "Adb.h"
+#include "ClientImpl.h"
+
+
+ClientImpl::ClientImpl()
+    : mAudio(new RemoteAudio(mSocket))
+{
+
+}
+
+ClientImpl::~ClientImpl()
+{
+    mAudio->release();
+}
+
+bool ClientImpl::init(const android::String8& param)
+{
+    Adb adb(param);
+    if (!adb.setPortForwarding(HOST_TCP_PORT, CLIENT_TCP_PORT)) {
+        LOGE("adb port forwarding failed");
+        return false;
+    }
+    android::String8 clientBinary("client/CtsAudioClient.apk");
+    android::String8 componentName("com.android.cts.audiotest/.CtsAudioClientActivity");
+    if (!adb.launchClient(clientBinary, componentName)) {
+        LOGE("cannot install or launch client");
+        return false;
+    }
+    // now socket connection
+    return mAudio->init(HOST_TCP_PORT);
+}
+
+
diff --git a/suite/audio_quality/lib/src/ClientImpl.h b/suite/audio_quality/lib/src/ClientImpl.h
new file mode 100644
index 0000000..28a7bcc
--- /dev/null
+++ b/suite/audio_quality/lib/src/ClientImpl.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+
+#ifndef CTSAUDIO_CLIENTIMPL_H
+#define CTSAUDIO_CLIENTIMPL_H
+
+#include <utils/StrongPointer.h>
+#include "ClientInterface.h"
+#include "ClientSocket.h"
+#include "audio/RemoteAudio.h"
+
+class ClientImpl: public ClientInterface {
+public:
+    static const int HOST_TCP_PORT = 15001;
+    static const int CLIENT_TCP_PORT = 15001;
+    ClientImpl();
+    virtual ~ClientImpl();
+    virtual bool init(const android::String8& param);
+
+    virtual ClientSocket& getSocket() {
+        return mSocket;
+    };
+
+    virtual android::sp<RemoteAudio>& getAudio() {
+        return mAudio;
+    };
+
+private:
+    ClientSocket mSocket;
+    android::sp<RemoteAudio> mAudio;
+
+};
+
+
+#endif // CTSAUDIO_CLIENTIMPL_H
diff --git a/suite/audio_quality/lib/src/ClientSocket.cpp b/suite/audio_quality/lib/src/ClientSocket.cpp
new file mode 100644
index 0000000..1fa9090
--- /dev/null
+++ b/suite/audio_quality/lib/src/ClientSocket.cpp
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2012 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 <arpa/inet.h>
+#include <errno.h>
+#include <strings.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "Log.h"
+
+#include "ClientSocket.h"
+
+ClientSocket::ClientSocket()
+    : mSocket(-1),
+      mTimeoutEnabled(false)
+{
+
+}
+
+ClientSocket::~ClientSocket()
+{
+    release();
+}
+
+bool ClientSocket::init(const char* hostIp, int port, bool enableTimeout)
+{
+    LOGD("ClientSocket::init");
+    mSocket = socket(AF_INET, SOCK_STREAM, 0);
+    if (mSocket < 0) {
+        LOGE("cannot open socket %d", errno);
+        return false;
+    }
+    int reuse = 1;
+    if (setsockopt(mSocket, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) == -1) {
+        LOGE("setsockopt error %d", errno);
+        release();
+        return false;
+    }
+
+    struct sockaddr_in serverAddr;
+    bzero((char*)&serverAddr, sizeof(serverAddr));
+    serverAddr.sin_family = AF_INET;
+    serverAddr.sin_port = htons(port);
+    if (inet_pton(AF_INET, hostIp, &serverAddr.sin_addr) != 1) {
+        release();
+        LOGE("inet_pton failed %d", errno);
+        return false;
+    }
+    if (connect(mSocket, (struct sockaddr *)&serverAddr, sizeof(serverAddr)) < 0) {
+        release();
+        LOGE("cannot connect socket %d", errno);
+        return false;
+    }
+    mTimeoutEnabled = enableTimeout;
+    return true;
+}
+
+const int ZERO_RW_SLEEP_TIME_US = 10;
+
+// make non-blocking mode only during read. This allows supporting time-out for read
+bool ClientSocket::readData(char* data, int len, int timeoutInMs)
+{
+    bool useTimeout = (mTimeoutEnabled && (timeoutInMs > 0));
+    int flOriginal = 0;
+    int timeInSec = 0;
+    int timeInUs = 0;
+    if (useTimeout) {
+        flOriginal = fcntl(mSocket, F_GETFL,0);
+        if (flOriginal == -1) {
+            LOGE("fcntl error %d", errno);
+            return false;
+        }
+        if (fcntl(mSocket, F_SETFL, flOriginal | O_NONBLOCK) == -1) {
+            LOGE("fcntl error %d", errno);
+            return false;
+        }
+        timeInSec = timeoutInMs / 1000;
+        timeInUs = (timeoutInMs % 1000) * 1000;
+    }
+    bool result = true;
+    int read;
+    int toRead = len;
+    while (toRead > 0) {
+        if (useTimeout) {
+            fd_set rfds;
+            struct timeval tv;
+            tv.tv_sec = timeInSec;
+            tv.tv_usec = timeInUs;
+            FD_ZERO(&rfds);
+            FD_SET(mSocket, &rfds);
+            if (select(mSocket + 1, &rfds, NULL, NULL, &tv) == -1) {
+                LOGE("select failed");
+                result = false;
+                break;
+            }
+            if (!FD_ISSET(mSocket, &rfds)) {
+                LOGE("socket read timeout");
+                result = false;
+                break;
+            }
+        }
+        read = recv(mSocket, (void*)data, toRead, 0);
+        if (read > 0) {
+            toRead -= read;
+            data += read;
+        } else if (read == 0) {
+            // in blocking mode, zero read mean's peer closed.
+            // in non-blocking mode, select said that there is data. so it should not happen
+            LOGE("zero read, peer closed or what?, nonblocking: %d", useTimeout);
+            result = false;
+            break;
+        } else {
+            LOGE("recv returned %d", read);
+            result = false;
+            break;
+        }
+    }
+    if (useTimeout) {
+        fcntl(mSocket, F_SETFL, flOriginal); // now blocking again
+    }
+    return result;
+}
+
+bool ClientSocket::sendData(const char* data, int len)
+{
+    int sent;
+    int toSend = len;
+    while (toSend > 0) {
+        sent = send(mSocket, (void*)data, (size_t)toSend, 0);
+        if (sent > 0) {
+            toSend -= sent;
+            data += sent;
+        } else if (sent == 0) { // no more buffer?
+            usleep(ZERO_RW_SLEEP_TIME_US); // just wait
+        } else {
+            LOGE("send returned %d, error %d", sent, errno);
+            return false;
+        }
+    }
+    return true;
+}
+
+void ClientSocket::release()
+{
+    if (mSocket != -1) {
+        close(mSocket);
+        mSocket = -1;
+    }
+}
diff --git a/suite/audio_quality/lib/src/FileUtil.cpp b/suite/audio_quality/lib/src/FileUtil.cpp
new file mode 100644
index 0000000..e40b7e3
--- /dev/null
+++ b/suite/audio_quality/lib/src/FileUtil.cpp
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2012 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 <time.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <errno.h>
+
+#include "Log.h"
+#include "StringUtil.h"
+#include "FileUtil.h"
+
+
+// This class is used by Log. So we cannot use LOG? macros here.
+#define _LOGD_(x...) do { fprintf(stderr, x); fprintf(stderr, "\n"); } while(0)
+
+// reported generated under reports/YYYY_MM_DD_HH_MM_SS dir
+const char reportTopDir[] = "reports";
+android::String8 FileUtil::mDirPath;
+
+bool FileUtil::prepare(android::String8& dirPath)
+{
+    if (mDirPath.length() != 0) {
+        dirPath = mDirPath;
+        _LOGD_("mDirPath %s", mDirPath.string());
+        return true;
+    }
+
+    time_t timeNow = time(NULL);
+    if (timeNow == ((time_t)-1)) {
+        _LOGD_("time error");
+       return false;
+    }
+    // tm is allocated in static buffer, and should not be freed.
+    struct tm* tm = localtime(&timeNow);
+    if (tm == NULL) {
+        _LOGD_("localtime error");
+        return false;
+    }
+    int result = mkdir(reportTopDir, S_IRWXU);
+    if ((result == -1) && (errno != EEXIST)) {
+        _LOGD_("mkdir of topdir failed, error %d", errno);
+        return false;
+    }
+    android::String8 path;
+    if (path.appendFormat("%s/%04d_%02d_%02d_%02d_%02d_%02d", reportTopDir,tm->tm_year + 1900,
+            tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec) != 0) {
+        return false;
+    }
+    result = mkdir(path.string(), S_IRWXU);
+    if ((result == -1) && (errno != EEXIST)) {
+        _LOGD_("mkdir of report dir failed, error %d", errno);
+        return false;
+    }
+    mDirPath = path;
+    dirPath = path;
+
+    return true;
+}
+
+FileUtil::FileUtil()
+{
+
+}
+
+FileUtil::~FileUtil()
+{
+    if (mFile.is_open()) {
+        mFile.close();
+    }
+}
+
+bool FileUtil::init(const char* fileName)
+{
+    if (fileName == NULL) {
+        return true;
+    }
+
+    mFile.open(fileName, std::ios::out | std::ios::trunc);
+    if (!mFile.is_open()) {
+            return false;
+        }
+    return true;
+}
+
+bool FileUtil::doVprintf(bool fileOnly, int loglevel, const char *fmt, va_list ap)
+{
+    // prevent messed up log in multi-thread env. Still multi-line logs can be messed up.
+    android::Mutex::Autolock lock(mWriteLock);
+    int start = 0;
+    if (loglevel != -1) {
+        mBuffer[0] = '0' + loglevel;
+        mBuffer[1] = '>';
+        start = 2;
+    }
+    int size;
+    size = vsnprintf(mBuffer + start, BUFFER_SIZE - start - 2, fmt, ap); // 2 for \n\0
+    if (size < 0) {
+        fprintf(stderr, "FileUtil::vprintf failed");
+        return false;
+    }
+    size += start;
+    mBuffer[size] = '\n';
+    size++;
+    mBuffer[size] = 0;
+
+    if (!fileOnly) {
+        fprintf(stdout, "%s", mBuffer);
+    }
+    if (mFile.is_open()) {
+        mFile<<mBuffer;
+    }
+    return true;
+}
+
+bool FileUtil::doPrintf(const char* fmt, ...)
+{
+    va_list ap;
+    va_start(ap, fmt);
+    bool result = doVprintf(false, -1, fmt, ap);
+    va_end(ap);
+    return result;
+}
diff --git a/suite/audio_quality/lib/src/GenericFactory.cpp b/suite/audio_quality/lib/src/GenericFactory.cpp
new file mode 100644
index 0000000..f615876
--- /dev/null
+++ b/suite/audio_quality/lib/src/GenericFactory.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2012 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 "Log.h"
+#include "GenericFactory.h"
+#include "ClientImpl.h"
+#include "task/TaskAll.h"
+
+ClientInterface* GenericFactory::createClientInterface()
+{
+    return new ClientImpl();
+}
+
+TaskGeneric* GenericFactory::createTask(TaskGeneric::TaskType type)
+{
+    TaskGeneric* task;
+    switch(type) {
+    case TaskGeneric::ETaskBatch:
+        task = new TaskBatch();
+        break;
+    case TaskGeneric::ETaskCase:
+        task = new TaskCase();
+        break;
+    case TaskGeneric::ETaskSequential:
+        task = new TaskSequential();
+        break;
+    case TaskGeneric::ETaskProcess:
+        task = new TaskProcess();
+        break;
+    case TaskGeneric::ETaskInput:
+        task = new TaskInput();
+        break;
+    case TaskGeneric::ETaskOutput:
+        task = new TaskOutput();
+        break;
+    case TaskGeneric::ETaskSound:
+        task = new TaskSound();
+        break;
+    case TaskGeneric::ETaskSave:
+        task = new TaskSave();
+        break;
+    // simple elements without its own class
+    case TaskGeneric::ETaskSetup:
+    case TaskGeneric::ETaskAction:
+        task = new TaskGeneric(type);
+        break;
+    case TaskGeneric::ETaskMessage:
+        task = new TaskMessage();
+        break;
+    default:
+        LOGE("GenericFactory::createTask unsupported type %d", type);
+        return NULL;
+    }
+    LOGD("GenericFactory::createTask 0x%x, type %d", task, type);
+    return task;
+}
diff --git a/suite/audio_quality/lib/src/Log.cpp b/suite/audio_quality/lib/src/Log.cpp
new file mode 100644
index 0000000..d4cf353
--- /dev/null
+++ b/suite/audio_quality/lib/src/Log.cpp
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2012 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 <stdio.h>
+#include <stdarg.h>
+
+#include "StringUtil.h"
+#include "Log.h"
+
+Log* Log::mInstance = NULL;
+
+#define ASSERT_PLAIN(cond) if(!(cond)) { fprintf(stderr, \
+        "assertion failed %s %d", __FILE__, __LINE__); \
+    *(char*)0 = 0; /* this will crash */};
+
+Log* Log::Instance(const char* dirName)
+{
+    if (!mInstance) {
+        mInstance = new Log();
+        ASSERT_PLAIN(mInstance->init(dirName));
+    }
+    return mInstance;
+}
+void Log::Finalize()
+{
+    delete mInstance;
+    mInstance = NULL;
+}
+void Log::printf(LogLevel level, const char* fmt, ...)
+{
+    va_list ap;
+    va_start(ap, fmt);
+    FileUtil::doVprintf(level < mLogLevel, level, fmt, ap);
+    va_end(ap);
+}
+
+void Log::setLogLevel(LogLevel level)
+{
+    mLogLevel = level;
+}
+
+Log::Log()
+    : mLogLevel(ELogV)
+{
+    ::fprintf(stderr, "Log level %d\n", mLogLevel);
+}
+
+Log::~Log()
+{
+
+}
+
+bool Log::init(const char* dirName)
+{
+    if (dirName == NULL) {
+        return true;
+    }
+    android::String8 logFile;
+    if (logFile.appendFormat("%s/log.txt", dirName) != 0) {
+        return false;
+    }
+    return FileUtil::init(logFile.string());
+}
+
+
+
diff --git a/suite/audio_quality/lib/src/RWBuffer.h b/suite/audio_quality/lib/src/RWBuffer.h
new file mode 100644
index 0000000..8b9d2a0
--- /dev/null
+++ b/suite/audio_quality/lib/src/RWBuffer.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+
+#ifndef CTSAUDIO_RWBUFFER_H
+#define CTSAUDIO_RWBUFFER_H
+
+#include <stdint.h>
+#include <utils/String8.h>
+#include "Log.h"
+
+/// utility for R/W buffer
+class RWBuffer {
+public:
+    RWBuffer(int capacity)
+        : mCapacity(capacity),
+          mWrPoint(0),
+          mRdPoint(0) {
+        mBuffer = new char[capacity];
+    }
+
+    ~RWBuffer() {
+        delete[] mBuffer;
+    }
+
+    void reset() {
+        mWrPoint = 0;
+        mRdPoint = 0;
+    }
+
+    void resetWr() {
+        mWrPoint = 0;
+    }
+
+    void resetRd() {
+        mRdPoint = 0;
+    }
+
+    const char* getBuffer() {
+        return mBuffer;
+    }
+    char* getUnwrittenBuffer() {
+        return mBuffer + mWrPoint;
+    }
+
+    inline void assertWriteCapacity(int sizeToWrite) {
+        ASSERT((mWrPoint + sizeToWrite) <= mCapacity);
+    }
+    void increaseWritten(int size) {
+        assertWriteCapacity(0); // damage already done, but detect and panic if happened
+        mWrPoint += size;
+    }
+
+    int getSizeWritten() {
+        return mWrPoint;
+    }
+
+    int getSizeRead() {
+        return mRdPoint;
+    }
+
+    template <typename T> void write(T v) {
+        char* src = (char*)&v;
+        assertWriteCapacity(sizeof(T));
+        memcpy(mBuffer + mWrPoint, src, sizeof(T));
+        mWrPoint += sizeof(T);
+    }
+    void writeStr(const android::String8& str) {
+        size_t len = str.length();
+        assertWriteCapacity(len);
+        memcpy(mBuffer + mWrPoint, str.string(), len);
+        mWrPoint += len;
+    }
+    template <typename T> T read() {
+        T v;
+        ASSERT((mRdPoint + sizeof(T)) <= mWrPoint);
+        memcpy(&v, mBuffer + mRdPoint, sizeof(T));
+        mRdPoint += sizeof(T);
+    }
+
+private:
+    int mCapacity;
+    int mWrPoint;
+    int mRdPoint;
+    char* mBuffer;
+};
+
+
+#endif // CTSAUDIO_RWBUFFER_H
diff --git a/suite/audio_quality/lib/src/Report.cpp b/suite/audio_quality/lib/src/Report.cpp
new file mode 100644
index 0000000..b487836
--- /dev/null
+++ b/suite/audio_quality/lib/src/Report.cpp
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2012 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 "Log.h"
+#include "StringUtil.h"
+#include "Report.h"
+
+
+Report* Report::mInstance = NULL;
+
+Report* Report::Instance(const char* dirName)
+{
+    if (mInstance == NULL) {
+        mInstance = new Report();
+        ASSERT(mInstance->init(dirName));
+    }
+    return mInstance;
+}
+void Report::Finalize()
+{
+    delete mInstance;
+    mInstance = NULL;
+}
+
+
+Report::Report()
+{
+
+}
+
+Report::~Report()
+{
+    writeSummary();
+}
+
+bool Report::init(const char* dirName)
+{
+    if (dirName == NULL) {
+        return true;
+    }
+    android::String8 report;
+    if (report.appendFormat("%s/report.txt", dirName) != 0) {
+        return false;
+    }
+    return FileUtil::init(report.string());
+}
+
+void Report::printf(const char* fmt, ...)
+{
+    va_list ap;
+    va_start(ap, fmt);
+    FileUtil::doVprintf(false, -1, fmt, ap);
+    va_end(ap);
+}
+
+void Report::addCasePassed(const android::String8& name)
+{
+    mPassedCases.push_back(name);
+}
+
+void Report::addCaseFailed(const android::String8& name)
+{
+    mFailedCases.push_back(name);
+}
+
+void Report::writeSummary()
+{
+    printf("= Test cases executed: %d, passed: %d, failed: %d =",
+            mPassedCases.size() + mFailedCases.size(), mPassedCases.size(), mFailedCases.size());
+    printf("= Failed cases =");
+    std::list<android::String8>::iterator it;
+    for (it = mFailedCases.begin(); it != mFailedCases.end(); it++) {
+        printf("* %s", it->string());
+    }
+    printf("= Passed cases =");
+    for (it = mPassedCases.begin(); it != mPassedCases.end(); it++) {
+        printf("* %s", it->string());
+    }
+}
diff --git a/suite/audio_quality/lib/src/Semaphore.cpp b/suite/audio_quality/lib/src/Semaphore.cpp
new file mode 100644
index 0000000..b430cad
--- /dev/null
+++ b/suite/audio_quality/lib/src/Semaphore.cpp
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2012 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 "Semaphore.h"
+
+
+Semaphore::Semaphore(int count)
+{
+    if (sem_init(&mSem, 0, count) != 0) {
+        ASSERT(false);
+    }
+}
+
+Semaphore::~Semaphore()
+{
+    sem_destroy(&mSem);
+}
+
+void Semaphore::tryWait()
+{
+    sem_trywait(&mSem);
+}
+
+bool Semaphore::wait()
+{
+    if (sem_wait(&mSem) == 0) {
+        return true;
+    } else {
+        return false;
+    }
+}
+
+bool Semaphore::timedWait(int timeInMSec)
+{
+    const int ONE_SEC_IN_NANOSEC = 1000000000;
+    const int ONE_MSEC_IN_NANOSEC = 1000000;
+    const int ONE_SEC_IN_MSEC = 1000;
+    struct timespec timeOld;
+    if (clock_gettime(CLOCK_REALTIME, &timeOld) != 0) {
+            return false;
+    }
+    int secToGo = timeInMSec / ONE_SEC_IN_MSEC;
+    int msecToGo = timeInMSec - (ONE_SEC_IN_MSEC * secToGo);
+    int nanoSecToGo = ONE_MSEC_IN_NANOSEC * msecToGo;
+    struct timespec timeNew = timeOld;
+    int nanoTotal = timeOld.tv_nsec + nanoSecToGo;
+    //LOGI("secToGo %d, msecToGo %d, nanoTotal %d", secToGo, msecToGo, nanoTotal);
+    if (nanoTotal > ONE_SEC_IN_NANOSEC) {
+        nanoTotal -= ONE_SEC_IN_NANOSEC;
+        secToGo += 1;
+    }
+    timeNew.tv_sec += secToGo;
+    timeNew.tv_nsec = nanoTotal;
+    //LOGV("Semaphore::timedWait now %d-%d until %d-%d for %d msecs",
+    //        timeOld.tv_sec, timeOld.tv_nsec, timeNew.tv_sec, timeNew.tv_nsec, timeInMSec);
+    if (sem_timedwait(&mSem, &timeNew) == 0) {
+        return true;
+    } else {
+        return false;
+    }
+}
+
+void Semaphore::post()
+{
+    sem_post(&mSem);
+}
diff --git a/suite/audio_quality/lib/src/Settings.cpp b/suite/audio_quality/lib/src/Settings.cpp
new file mode 100644
index 0000000..4f78fe4
--- /dev/null
+++ b/suite/audio_quality/lib/src/Settings.cpp
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2012 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 "Settings.h"
+#include "Log.h"
+
+Settings* Settings::mInstance = NULL;
+
+Settings* Settings::Instance()
+{
+    if (mInstance == NULL) {
+        mInstance = new Settings();
+    }
+    return mInstance;
+}
+
+void Settings::Finalize()
+{
+    delete mInstance;
+    mInstance = NULL;
+}
+
+
+void Settings::addSetting(SettingType type, const android::String8 setting)
+{
+    // TODO key, string can be better if there are large number of settings
+    switch(type) {
+    case EADB:
+        mAdbSetting = setting;
+    default:
+        ASSERT(false);
+    }
+}
+const android::String8& Settings::getSetting(SettingType type)
+{
+    switch(type) {
+    case EADB:
+        return mAdbSetting;
+    default:
+        ASSERT(false);
+    }
+    return mAdbSetting; // just for removing compiler warning, will not reach here
+}
+
+
+
diff --git a/suite/audio_quality/lib/src/SignalProcessingImpl.cpp b/suite/audio_quality/lib/src/SignalProcessingImpl.cpp
new file mode 100644
index 0000000..fed9866
--- /dev/null
+++ b/suite/audio_quality/lib/src/SignalProcessingImpl.cpp
@@ -0,0 +1,253 @@
+/*
+ * Copyright (C) 2012 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 <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <utils/StrongPointer.h>
+
+#include "Log.h"
+#include "audio/Buffer.h"
+#include "StringUtil.h"
+#include "SimpleScriptExec.h"
+#include "SignalProcessingImpl.h"
+#include "task/TaskCase.h"
+
+enum ToPythonCommandType {
+    EHeader             = 0x0,
+    ETerminate          = 0x1,
+    EFunctionName       = 0x2,
+    EAudioMono          = 0x4,
+    EAudioStereo        = 0x5,
+    EValue64Int         = 0x8,
+    EValueDouble        = 0x9,
+    EExecutionResult    = 0x10
+};
+
+const android::String8 \
+    SignalProcessingImpl::MAIN_PROCESSING_SCRIPT("test_description/processing_main.py");
+
+SignalProcessingImpl::SignalProcessingImpl()
+    : mChildRunning(false),
+      mBuffer(1024)
+{
+
+}
+
+SignalProcessingImpl::~SignalProcessingImpl()
+{
+    if (mSocket.get() != NULL) {
+        int terminationCommand [] = {ETerminate, 0};
+        send((char*)terminationCommand, sizeof(terminationCommand));
+        mSocket->release();
+    }
+    if (mChildRunning) {
+        waitpid(mChildPid, NULL, 0);
+    }
+}
+
+#define CHILD_LOGE(x...) do { fprintf(stderr, x); \
+    fprintf(stderr, " %s - %d\n", __FILE__, __LINE__); } while(0)
+
+const int CHILD_WAIT_TIME_US = 100000;
+
+bool SignalProcessingImpl::init(const android::String8& script)
+{
+    pid_t pid;
+    if ((pid = fork()) < 0) {
+        LOGE("SignalProcessingImpl::init fork failed %d", errno);
+        return false;
+    } else if (pid == 0) { // child
+        if (execl(SimpleScriptExec::PYTHON_PATH, SimpleScriptExec::PYTHON_PATH,
+                script.string(), NULL) < 0) {
+            CHILD_LOGE("execl %s %s failed %d", SimpleScriptExec::PYTHON_PATH,
+                    script.string(), errno);
+            exit(EXIT_FAILURE);
+        }
+    } else { // parent
+        mChildPid = pid;
+        mChildRunning = true;
+        int result = false;
+        int retryCount = 0;
+        // not that clean, but it takes some time for python side to have socket ready
+        const int MAX_RETRY = 20;
+        while (retryCount < MAX_RETRY) {
+            usleep(CHILD_WAIT_TIME_US);
+            mSocket.reset(new ClientSocket());
+            if (mSocket.get() == NULL) {
+                result = false;
+                break;
+            }
+            if (mSocket->init("127.0.0.1", SCRIPT_PORT, true)) {
+                result = true;
+                break;
+            }
+            retryCount++;
+        }
+        if (!result) {
+            LOGE("cannot connect to child");
+            mSocket.reset(NULL);
+            return result;
+        }
+    }
+    return true;
+}
+
+
+TaskGeneric::ExecutionResult SignalProcessingImpl::run( const android::String8& functionScript,
+        int nInputs, bool* inputTypes, void** inputs,
+        int nOutputs, bool* outputTypes, void** outputs)
+{
+    mBuffer.reset();
+    mBuffer.write <int32_t>((int32_t)EHeader);
+    mBuffer.write<int32_t>(nInputs + 1);
+    mBuffer.write<int32_t>((int32_t)EFunctionName);
+    mBuffer.write<int32_t>((int32_t)functionScript.length());
+    mBuffer.writeStr(functionScript);
+    if (!send(mBuffer.getBuffer(), mBuffer.getSizeWritten())) {
+        LOGE("send failed");
+        return TaskGeneric::EResultError;
+    }
+    for (int i = 0; i < nInputs; i++) {
+        mBuffer.reset();
+        if (inputTypes[i]) { // android::sp<Buffer>*
+            android::sp<Buffer>* buffer = reinterpret_cast<android::sp<Buffer>*>(inputs[i]);
+            mBuffer.write<int32_t>((int32_t)((*buffer)->isStereo() ? EAudioStereo : EAudioMono));
+            int dataLen = (*buffer)->getSize();
+            mBuffer.write<int32_t>(dataLen);
+            if (!send(mBuffer.getBuffer(), mBuffer.getSizeWritten())) {
+                LOGE("send failed");
+                return TaskGeneric::EResultError;
+            }
+            if (!send((*buffer)->getData(), dataLen)) {
+                LOGE("send failed");
+                return TaskGeneric::EResultError;
+            }
+            LOGD("%d-th param buffer %d, stereo:%d", dataLen, (*buffer)->isStereo());
+        } else { //TaskCase::Value*
+            TaskCase::Value* val = reinterpret_cast<TaskCase::Value*>(inputs[i]);
+            bool isI64 = (val->getType() == TaskCase::Value::ETypeI64);
+            mBuffer.write<int32_t>((int32_t)(isI64 ? EValue64Int : EValueDouble));
+            if (isI64) {
+                mBuffer.write<int64_t>(val->getInt64());
+            } else  {
+                mBuffer.write<double>(val->getDouble());
+            }
+            if (!send(mBuffer.getBuffer(), mBuffer.getSizeWritten())) {
+                LOGE("send failed");
+                return TaskGeneric::EResultError;
+            }
+            LOGD("%d-th param Value", i);
+        }
+    }
+    int32_t header[4]; // id 0 - no of types - id 0x10 - ExecutionResult
+    if (!read((char*)header, sizeof(header))) {
+        LOGE("read failed");
+        return TaskGeneric::EResultError;
+    }
+    if (header[0] != 0) {
+        LOGE("wrong data");
+        return TaskGeneric::EResultError;
+    }
+    if (header[2] != EExecutionResult) {
+        LOGE("wrong data");
+        return TaskGeneric::EResultError;
+    }
+    if (header[3] == TaskGeneric::EResultError) {
+        LOGE("script returned error %d", header[3]);
+        return (TaskGeneric::ExecutionResult)header[3];
+    }
+    if ((header[1] - 1) != nOutputs) {
+        LOGE("wrong data");
+        return TaskGeneric::EResultError;
+    }
+    for (int i = 0; i < nOutputs; i++) {
+        int32_t type;
+        if (!read((char*)&type, sizeof(type))) {
+            LOGE("read failed");
+            return TaskGeneric::EResultError;
+        }
+        if (outputTypes[i]) { // android::sp<Buffer>*
+            int32_t dataLen;
+            if (!read((char*)&dataLen, sizeof(dataLen))) {
+                LOGE("read failed");
+                return TaskGeneric::EResultError;
+            }
+            android::sp<Buffer>* buffer = reinterpret_cast<android::sp<Buffer>*>(outputs[i]);
+            if (buffer->get() == NULL) { // data not allocated, this can happen for unknown-length output
+                *buffer = new Buffer(dataLen, dataLen, (type == EAudioStereo) ? true: false);
+                if (buffer->get() == NULL) {
+                    LOGE("alloc failed");
+                    return TaskGeneric::EResultError;
+                }
+            }
+            bool isStereo = (*buffer)->isStereo();
+
+            if (((type == EAudioStereo) && isStereo) || ((type == EAudioMono) && !isStereo)) {
+                // valid
+            } else {
+                LOGE("%d-th output wrong type %d stereo: %d", i, type, isStereo);
+                return TaskGeneric::EResultError;
+            }
+
+            if (dataLen > (int)(*buffer)->getSize()) {
+                LOGE("%d-th output data too long %d while buffer size %d", i, dataLen,
+                        (*buffer)->getSize());
+                return TaskGeneric::EResultError;
+            }
+            if (!read((*buffer)->getData(), dataLen)) {
+                LOGE("read failed");
+                return TaskGeneric::EResultError;
+            }
+            LOGD("received buffer %x %x", ((*buffer)->getData())[0], ((*buffer)->getData())[1]);
+            (*buffer)->setHandled(dataLen);
+            (*buffer)->setSize(dataLen);
+        } else { //TaskCase::Value*
+            TaskCase::Value* val = reinterpret_cast<TaskCase::Value*>(outputs[i]);
+            if ((type == EValue64Int) || (type == EValueDouble)) {
+                if (!read((char*)val->getPtr(), sizeof(int64_t))) {
+                    LOGE("read failed");
+                    return TaskGeneric::EResultError;
+                }
+                if (type == EValue64Int) {
+                    val->setType(TaskCase::Value::ETypeI64);
+                } else {
+                    val->setType(TaskCase::Value::ETypeDouble);
+                }
+            } else {
+                LOGE("wrong type %d", type);
+                return TaskGeneric::EResultError;
+            }
+        }
+    }
+    return (TaskGeneric::ExecutionResult)header[3];
+}
+
+bool SignalProcessingImpl::send(const char* data, int len)
+{
+    //LOGD("send %d", len);
+    return mSocket->sendData(data, len);
+}
+
+bool SignalProcessingImpl::read(char* data, int len)
+{
+    const int READ_TIMEOUT_MS = 60000 * 2; // as some calculation like calc_delay takes almost 20 secs
+    //LOGD("read %d", len);
+    return mSocket->readData(data, len, READ_TIMEOUT_MS);
+}
+
diff --git a/suite/audio_quality/lib/src/SignalProcessingImpl.h b/suite/audio_quality/lib/src/SignalProcessingImpl.h
new file mode 100644
index 0000000..29b410e
--- /dev/null
+++ b/suite/audio_quality/lib/src/SignalProcessingImpl.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+#ifndef CTSAUDIO_SIGNALPROCESSINGIMPL_H
+#define CTSAUDIO_SIGNALPROCESSINGIMPL_H
+
+#include <UniquePtr.h>
+#include <utils/String8.h>
+
+#include "SignalProcessingInterface.h"
+#include "ClientSocket.h"
+#include "RWBuffer.h"
+
+/**
+ * Implements SignalProcessingInterface
+ */
+class SignalProcessingImpl: public SignalProcessingInterface {
+public:
+    static const android::String8 MAIN_PROCESSING_SCRIPT;
+    SignalProcessingImpl();
+    virtual ~SignalProcessingImpl();
+    /**
+     * @param script main script to call function script
+     */
+    virtual bool init(const android::String8& script);
+
+    virtual TaskGeneric::ExecutionResult run(const android::String8& functionScript,
+            int nInputs, bool* inputTypes, void** inputs,
+            int nOutputs, bool* outputTypes, void** outputs);
+private:
+    bool send(const char* data, int len);
+    bool read(char* data, int len);
+
+private:
+    static const int SCRIPT_PORT = 15010;
+    UniquePtr<ClientSocket> mSocket;
+    pid_t mChildPid;
+    bool mChildRunning;
+    RWBuffer mBuffer;
+};
+
+
+#endif //CTSAUDIO_SIGNALPROCESSINGIMPL_H
diff --git a/suite/audio_quality/lib/src/SimpleScriptExec.cpp b/suite/audio_quality/lib/src/SimpleScriptExec.cpp
new file mode 100644
index 0000000..0a2045b
--- /dev/null
+++ b/suite/audio_quality/lib/src/SimpleScriptExec.cpp
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2012 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 <stdlib.h>
+#include <stdio.h>
+
+#include "Log.h"
+#include "StringUtil.h"
+
+#include "SimpleScriptExec.h"
+
+const char* SimpleScriptExec::PYTHON_PATH = "/usr/bin/python";
+const char* PASS_MAGIC_WORD = "___CTS_AUDIO_PASS___";
+
+bool SimpleScriptExec::checkPythonEnv()
+{
+    android::String8 script("test_description/conf/check_conf.py");
+    android::String8 param;
+    android::String8 result;
+    if (!runScript(script, param, result)) {
+        return false;
+    }
+
+    android::String8 rePattern;
+    return checkIfPassed(result, rePattern);
+}
+
+bool SimpleScriptExec::checkIfPassed(const android::String8& str, const android::String8& reMatch,
+        int nmatch, regmatch_t pmatch[])
+{
+    android::String8 match;
+    match.append(PASS_MAGIC_WORD);
+    match.append(reMatch);
+    LOGV("re match %s", match.string());
+    regex_t re;
+    int cflags = REG_EXTENDED;
+    if (nmatch == 0) {
+        cflags |= REG_NOSUB;
+    }
+    if (regcomp(&re, match.string(), cflags) != 0) {
+        LOGE("regcomp failed");
+        return false;
+    }
+    bool result = false;
+    if (regexec(&re, str.string(), nmatch, pmatch, 0) == 0) {
+        // match found. passed
+        result = true;
+    }
+    regfree(&re);
+    return result;
+}
+
+bool SimpleScriptExec::runScript(const android::String8& script, const android::String8& param,
+        android::String8& result)
+{
+    FILE *fpipe;
+    android::String8 command;
+    command.appendFormat("%s %s %s", PYTHON_PATH, script.string(), param.string());
+    const int READ_SIZE = 1024;
+    char buffer[READ_SIZE];
+    size_t len = 0;
+
+    if ( !(fpipe = (FILE*)popen(command.string(),"r")) ) {
+        LOGE("cannot execute python");
+        return false;
+    }
+    result.clear();
+    while((len = fread(buffer, 1, READ_SIZE, fpipe)) > 0) {
+        result.append(buffer, len);
+    }
+    pclose(fpipe);
+
+    return true;
+}
+
+
+
diff --git a/suite/audio_quality/lib/src/SimpleScriptExec.h b/suite/audio_quality/lib/src/SimpleScriptExec.h
new file mode 100644
index 0000000..28117c4
--- /dev/null
+++ b/suite/audio_quality/lib/src/SimpleScriptExec.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+#ifndef CTSAUDIO_SIMPLESCRIPTEXEC_H
+#define CTSAUDIO_SIMPLESCRIPTEXEC_H
+
+#include <sys/types.h>
+#include <regex.h>
+
+#include <utils/String8.h>
+
+/**
+ * Utility class for executing simple scripts
+ * which prints ___CTS_AUDIO_PASS___ string in output
+ */
+class SimpleScriptExec {
+public:
+    static const char* PYTHON_PATH;
+
+    static bool checkPythonEnv();
+    /**
+     * run given script
+     * @param script full path of the script
+     * @param param arguments to pass
+     * @param result
+     */
+    static bool runScript(const android::String8& script, const android::String8& param,
+            android::String8& result);
+
+    /**
+     * check if the given str include magic words for pass.
+     * @param str
+     * @param reMatch pattern to match in re besides the pass string
+     * @param nmatch number of substring pattern match elements. It should be in POSIX
+     *        extended RE syntax, no \d nor [:digit:]
+     * @param pmatch pattern match elements
+     * @return true if passed
+     */
+    static bool checkIfPassed(const android::String8& str, const android::String8& reMatch,
+            int nmatch = 0, regmatch_t pmatch[] = NULL);
+};
+
+
+#endif // CTSAUDIO_SIMPLESCRIPTEXEC_H
diff --git a/suite/audio_quality/lib/src/StringUtil.cpp b/suite/audio_quality/lib/src/StringUtil.cpp
new file mode 100644
index 0000000..f23a29a
--- /dev/null
+++ b/suite/audio_quality/lib/src/StringUtil.cpp
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2012 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 <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+
+#include "Log.h"
+#include "StringUtil.h"
+
+std::vector<android::String8>* StringUtil::split(const android::String8& str, char delimiter)
+{
+    std::vector<android::String8>* tokens = new std::vector<android::String8>();
+    unsigned int lastTokenEnd = 0;
+    for (unsigned int i = 0; i < str.length(); i++) {
+        if (str[i] == delimiter) {
+            if ((i - lastTokenEnd) > 0) {
+                tokens->push_back(substr(str, lastTokenEnd, i - lastTokenEnd));
+            }
+            lastTokenEnd = i + 1; // 1 for skipping delimiter
+        }
+    }
+    if (lastTokenEnd < str.length()) {
+        tokens->push_back(substr(str, lastTokenEnd, str.length() - lastTokenEnd));
+    }
+    return tokens;
+}
+
+android::String8 StringUtil::substr(const android::String8& str, size_t pos, size_t n)
+{
+    size_t l = str.length();
+
+    if (pos >= l) {
+        android::String8 resultDummy;
+        return resultDummy;
+    }
+    if ((pos + n) > l) {
+        n = l - pos;
+    }
+    android::String8 result(str.string() + pos, n);
+    return result;
+}
+
+int StringUtil::compare(const android::String8& str, const char* other)
+{
+    return strcmp(str.string(), other);
+}
+
+bool StringUtil::endsWith(const android::String8& str, const char* other)
+{
+    size_t l1 = str.length();
+    size_t l2 = strlen(other);
+    const char* data = str.string();
+    if (l2 > l1) {
+        return false;
+    }
+    size_t iStr = l1 - l2;
+    size_t iOther = 0;
+    for(; iStr < l1; iStr++) {
+        if (data[iStr] != other[iOther]) {
+            return false;
+        }
+        iOther++;
+    }
+    return true;
+}
diff --git a/suite/audio_quality/lib/src/audio/AudioHardware.cpp b/suite/audio_quality/lib/src/audio/AudioHardware.cpp
new file mode 100644
index 0000000..eeb70f5
--- /dev/null
+++ b/suite/audio_quality/lib/src/audio/AudioHardware.cpp
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2012 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 <sys/types.h>
+#include <regex.h>
+#include <stdlib.h>
+
+#include <tinyalsa/asoundlib.h>
+
+#include "Log.h"
+#include "StringUtil.h"
+#include "SimpleScriptExec.h"
+#include "audio/AudioHardware.h"
+#include "audio/Buffer.h"
+#include "audio/AudioPlaybackLocal.h"
+#include "audio/AudioRecordingLocal.h"
+#include "audio/AudioRemote.h"
+#include "task/TaskCase.h"
+
+int AudioHardware::mHwId = -1;
+
+int AudioHardware::detectAudioHw()
+{
+    android::String8 script("test_description/conf/detect_usb_audio.py");
+    android::String8 param("MobilePre");
+    android::String8 resultStr;
+    if (!SimpleScriptExec::runScript(script, param, resultStr)) {
+        LOGE("cannot run script");
+        return -1;
+    }
+
+    android::String8 match("[ \t]+([A-Za-z0-9_]+)[ \t]+([0-9]+)");
+    const int nmatch = 3;
+    regmatch_t pmatch[nmatch];
+    if (!SimpleScriptExec::checkIfPassed(resultStr, match, nmatch, pmatch)) {
+        LOGE("result not correct %s", resultStr.string());
+        return -1;
+    }
+    LOGV("pmatch 0: %d, %d  1:%d, %d  2:%d, %d",
+        pmatch[0].rm_so, pmatch[0].rm_eo,
+        pmatch[1].rm_so, pmatch[1].rm_eo,
+        pmatch[2].rm_so, pmatch[2].rm_eo);
+
+    if (pmatch[1].rm_so == -1) {
+        return -1;
+    }
+    if (pmatch[2].rm_so == -1) {
+        return -1;
+    }
+    android::String8 product = StringUtil::substr(resultStr, pmatch[1].rm_so,
+            pmatch[1].rm_eo - pmatch[1].rm_so);
+    LOGI("Audio device %s found", product.string());
+    android::String8 cardNumber = StringUtil::substr(resultStr, pmatch[2].rm_so,
+            pmatch[2].rm_eo - pmatch[2].rm_so);
+    int cardN = atoi(cardNumber.string());
+    LOGI("Card number : %d", cardN);
+    return cardN;
+}
+
+android::sp<AudioHardware> AudioHardware::createAudioHw(bool local, bool playback,
+        TaskCase* testCase)
+{
+    android::sp<AudioHardware> hw;
+    if (local) {
+        if (mHwId < 0) {
+            mHwId = detectAudioHw();
+        }
+        if (mHwId < 0) {
+            return NULL;
+        }
+        if (playback) {
+            hw = new AudioPlaybackLocal(mHwId);
+        } else {
+            hw = new AudioRecordingLocal(mHwId);
+        }
+    } else {
+        if (testCase != NULL) {
+            if (playback) {
+                hw = new AudioRemotePlayback(testCase->getRemoteAudio());
+            } else {
+                hw = new AudioRemoteRecording(testCase->getRemoteAudio());
+            }
+        }
+    }
+    return hw;
+}
+
+AudioHardware::~AudioHardware()
+{
+
+}
+
+bool AudioHardware::startPlaybackOrRecordById(const android::String8& id, TaskCase* testCase)
+{
+    if (testCase == NULL) { // default implementation only handles local buffer.
+        return false;
+    }
+    android::sp<Buffer> buffer = testCase->findBuffer(id);
+    if (buffer.get() == NULL) {
+        return false;
+    }
+    return startPlaybackOrRecord(buffer);
+}
diff --git a/suite/audio_quality/lib/src/audio/AudioLocal.cpp b/suite/audio_quality/lib/src/audio/AudioLocal.cpp
new file mode 100644
index 0000000..71b7afb
--- /dev/null
+++ b/suite/audio_quality/lib/src/audio/AudioLocal.cpp
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2012 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 <Log.h>
+#include "audio/Buffer.h"
+#include "audio/AudioLocal.h"
+
+bool AudioLocal::prepare(AudioHardware::SamplingRate samplingRate,  int gain, int /*mode*/)
+{
+    LOGV("prepare");
+    // gain control not necessary in MobilePre as there is no control.
+    // This means audio source itself should be adjusted to control volume
+    if (mState == EStNone) {
+        if (run() != android::NO_ERROR) {
+            LOGE("AudioLocal cannot run");
+            // cannot run thread
+            return false;
+        }
+        mState = EStCreated;
+    } else if (mState == EStRunning) {
+        // wrong usage. first stop!
+        return false;
+    }
+    mClientCompletionWait.tryWait(); // this will reset semaphore to 0 if it is 1.
+    mSamplingRate = samplingRate;
+    return issueCommandAndWaitForCompletion(ECmInitialize);
+}
+
+bool AudioLocal::startPlaybackOrRecord(android::sp<Buffer>& buffer, int numberRepetition)
+{
+    LOGV("startPlaybackOrRecord");
+    if (mState != EStInitialized) {
+        LOGE("startPlaybackOrRecord while not initialized");
+        // wrong state
+        return false;
+    }
+    mBuffer = buffer;
+    mNumberRepetition = numberRepetition;
+    mCurrentRepeat = 0;
+    return issueCommandAndWaitForCompletion(ECmRun);
+}
+
+bool AudioLocal::waitForCompletion()
+{
+    int waitTimeInMsec = mBuffer->getSamples() / (mSamplingRate/1000);
+    waitTimeInMsec += COMMAND_WAIT_TIME_MSEC;
+    LOGD("waitForCompletion will wait for %d", waitTimeInMsec);
+    if (!mClientCompletionWait.timedWait(waitTimeInMsec)) {
+        LOGE("waitForCompletion time-out");
+        return false;
+    }
+    return mCompletionResult;
+}
+
+void AudioLocal::stopPlaybackOrRecord()
+{
+    LOGV("stopPlaybackOrRecord");
+    if (mState == EStRunning) {
+        issueCommandAndWaitForCompletion(ECmStop);
+    }
+
+    if (mState != EStNone) { // thread alive
+        requestExit();
+        mCurrentCommand = ECmThreadStop;
+        mAudioThreadWait.post();
+        requestExitAndWait();
+        mState = EStNone;
+    }
+}
+
+bool AudioLocal::issueCommandAndWaitForCompletion(AudioCommand command)
+{
+    mCurrentCommand = command;
+    mAudioThreadWait.post();
+    if (!mClientCommandWait.timedWait(COMMAND_WAIT_TIME_MSEC)) {
+        LOGE("issueCommandAndWaitForCompletion timeout cmd %d", command);
+        return false;
+    }
+    return mCommandResult;
+}
+
+AudioLocal::~AudioLocal()
+{
+    LOGV("~AudioLocal");
+}
+
+AudioLocal::AudioLocal()
+    : mState(EStNone),
+      mCurrentCommand(ECmNone),
+      mClientCommandWait(0),
+      mClientCompletionWait(0),
+      mAudioThreadWait(0),
+      mCompletionResult(false)
+{
+    LOGV("AudioLocal");
+}
+
+
+bool AudioLocal::threadLoop()
+{
+    if (mCurrentCommand == ECmNone) {
+        if (mState == EStRunning) {
+            if (doPlaybackOrRecord(mBuffer)) {
+                // check exit condition
+                if (mBuffer->bufferHandled()) {
+                    mCurrentRepeat++;
+                    LOGV("repeat %d - %d", mCurrentRepeat, mNumberRepetition);
+                    if (mCurrentRepeat == mNumberRepetition) {
+                        LOGV("AudioLocal complete command");
+                        mState = EStInitialized;
+                        mCompletionResult = true;
+                        mClientCompletionWait.post();
+                    } else {
+                        mBuffer->restart();
+                    }
+                }
+            } else {
+                mState = EStInitialized;
+                //notify error
+                mCompletionResult = false;
+                mClientCompletionWait.post();
+            }
+            return true;
+        }
+        //LOGV("audio thread waiting");
+        mAudioThreadWait.wait();
+        //LOGV("audio thread waken up");
+        if (mCurrentCommand == ECmNone) {
+            return true; // continue to check exit condition
+        }
+    }
+
+    int pendingCommand = mCurrentCommand;
+    // now there is a command
+    switch (pendingCommand) {
+    case ECmInitialize:
+        mCommandResult = doPrepare(mSamplingRate, AudioHardware::SAMPLES_PER_ONE_GO);
+        if (mCommandResult) {
+            mState = EStInitialized;
+        }
+        break;
+    case ECmRun: {
+        mCommandResult = doPlaybackOrRecord(mBuffer);
+        if (mCommandResult) {
+            mState = EStRunning;
+        }
+    }
+        break;
+    case ECmStop:
+        doStop();
+        mState = EStCreated;
+        mCommandResult = true;
+        break;
+    case ECmThreadStop:
+        return false;
+        break;
+    default:
+        // this should not happen
+        ASSERT(false);
+        break;
+    }
+
+    mCurrentCommand = ECmNone;
+    mClientCommandWait.post();
+
+    return true;
+}
+
+
diff --git a/suite/audio_quality/lib/src/audio/AudioPlaybackLocal.cpp b/suite/audio_quality/lib/src/audio/AudioPlaybackLocal.cpp
new file mode 100644
index 0000000..fe91cb3
--- /dev/null
+++ b/suite/audio_quality/lib/src/audio/AudioPlaybackLocal.cpp
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+// TODO remove all the headers upto asound.h after removing pcm_drain hack
+#include <errno.h>
+#include <unistd.h>
+#include <poll.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/time.h>
+#include <limits.h>
+#include <linux/ioctl.h>
+#define __force
+#define __bitwise
+#define __user
+#include <sound/asound.h>
+
+#include <string.h>
+#include <tinyalsa/asoundlib.h>
+
+#include "audio/AudioHardware.h"
+#include "audio/Buffer.h"
+#include "Log.h"
+
+#include "audio/AudioPlaybackLocal.h"
+
+
+AudioPlaybackLocal::AudioPlaybackLocal(int hwId)
+    : mHwId(hwId),
+      mPcmHandle(NULL)
+{
+    LOGV("AudioPlaybackLocal %x", (unsigned int)this);
+}
+
+AudioPlaybackLocal::~AudioPlaybackLocal()
+{
+    LOGV("~AudioPlaybackLocal %x", (unsigned int)this);
+    releaseHw();
+}
+
+bool AudioPlaybackLocal::doPrepare(AudioHardware::SamplingRate samplingRate, int samplesInOneGo)
+{
+    releaseHw();
+
+    struct pcm_config config;
+
+    memset(&config, 0, sizeof(config));
+    config.channels = 2;
+    config.rate = samplingRate;
+    config.period_size = 1024;
+    config.period_count = 64;
+    config.format = PCM_FORMAT_S16_LE;
+    config.start_threshold = 0;
+    config.stop_threshold =  0;
+    config.silence_threshold = 0;
+
+    mPcmHandle = pcm_open(mHwId, 0, PCM_OUT, &config);
+    if (!mPcmHandle || !pcm_is_ready(mPcmHandle)) {
+       LOGE("Unable to open PCM device(%d) (%s)\n", mHwId, pcm_get_error(mPcmHandle));
+       return false;
+    }
+
+    mSamples = samplesInOneGo;
+    mSizes = samplesInOneGo * 4; // stereo, 16bit
+
+    return true;
+}
+
+bool AudioPlaybackLocal::doPlaybackOrRecord(android::sp<Buffer>& buffer)
+{
+    if (buffer->amountToHandle() < (size_t)mSizes) {
+        mSizes = buffer->amountToHandle();
+    }
+    if (pcm_write(mPcmHandle, buffer->getUnhanledData(), mSizes)) {
+        LOGE("AudioPlaybackLocal error %s", pcm_get_error(mPcmHandle));
+        return false;
+    }
+    buffer->increaseHandled(mSizes);
+    LOGV("AudioPlaybackLocal::doPlaybackOrRecord %d", buffer->amountHandled());
+    return true;
+}
+
+void AudioPlaybackLocal::doStop()
+{
+    // TODO: remove when pcm_stop does pcm_drain
+    // hack to have snd_pcm_drain equivalent
+    struct pcm_ {
+        int fd;
+    };
+    pcm_* pcm = (pcm_*)mPcmHandle;
+    ioctl(pcm->fd, SNDRV_PCM_IOCTL_DRAIN);
+    pcm_stop(mPcmHandle);
+}
+
+void AudioPlaybackLocal::releaseHw()
+{
+    if (mPcmHandle != NULL) {
+        LOGV("releaseHw %x", (unsigned int)this);
+        doStop();
+        pcm_close(mPcmHandle);
+        mPcmHandle = NULL;
+    }
+}
diff --git a/suite/audio_quality/lib/src/audio/AudioProtocol.cpp b/suite/audio_quality/lib/src/audio/AudioProtocol.cpp
new file mode 100644
index 0000000..a046432
--- /dev/null
+++ b/suite/audio_quality/lib/src/audio/AudioProtocol.cpp
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2012 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 <stdint.h>
+#include <arpa/inet.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <utils/StrongPointer.h>
+
+#include "audio/Buffer.h"
+#include "Log.h"
+#include "audio/AudioProtocol.h"
+
+
+bool AudioProtocol::sendCommand(AudioParam& param)
+{
+    mBuffer[0] = htonl(mCommand);
+    mBuffer[1] = 0;
+    return sendData((char*)mBuffer, 8);
+}
+
+bool AudioProtocol::handleReply(const uint32_t* data, AudioParam* param)
+{
+    if (!checkHeaderId(data, mCommand)) {
+        return false;
+    }
+    if (data[1] != 0) { // no endian change for 0
+        LOGE("error in reply %d", ntohl(data[1]));
+        return false;
+    }
+    if (data[2] != 0) {
+        LOGE("payload length %d not zero", ntohl(data[2]));
+        return false;
+    }
+    return true;
+}
+
+bool AudioProtocol::handleReplyHeader(ClientSocket& socket, uint32_t* data, CommandId& id)
+{
+    if (!socket.readData((char*)data, REPLY_HEADER_SIZE)) {
+        LOGE("handleReplyHeader cannot read");
+        return false;
+    }
+    uint32_t command = ntohl(data[0]);
+    if ((command & 0xffff0000) != 0x43210000) {
+        LOGE("Wrong header %x %x", command, data[0]);
+        return false;
+    }
+    command = (command & 0xffff) | 0x12340000; // convert to id
+    if (command < ECmdStart) {
+        LOGE("Wrong header %x %x", command, data[0]);
+        return false;
+    }
+    if (command > (ECmdLast - 1)) {
+        LOGE("Wrong header %x %x", command, data[0]);
+        return false;
+    }
+    id = (CommandId)command;
+    LOGD("received reply with command %x", command);
+    return true;
+}
+
+bool AudioProtocol::checkHeaderId(const uint32_t* data, uint32_t command)
+{
+    if (ntohl(data[0]) != ((command & 0xffff) | 0x43210000)) {
+        LOGE("wrong reply ID 0x%x", ntohl(data[0]));
+        return false;
+    }
+    return true;
+}
+
+
+/**
+ * param0 u32 data id
+ * param1 sp<Buffer>
+ */
+bool CmdDownload::sendCommand(AudioParam& param)
+{
+    mBuffer[0] = htonl(ECmdDownload);
+    mBuffer[1] = htonl(4 + param.mBuffer->getSize());
+    mBuffer[2] = htonl(param.mId);
+    if(!sendData((char*)mBuffer, 12)) {
+        return false;
+    }
+    return sendData(param.mBuffer->getData(), param.mBuffer->getSize());
+}
+
+/**
+ * param0 u32 data id
+ * param1 u32 sampling rate
+ * param2 u32 mono / stereo(MSB) | mode
+ * param3 u32 volume
+ * param4 u32 repeat
+ */
+bool CmdStartPlayback::sendCommand(AudioParam& param)
+{
+    mBuffer[0] = htonl(ECmdStartPlayback);
+    mBuffer[1] = htonl(20);
+    mBuffer[2] = htonl(param.mId);
+    mBuffer[3] = htonl(param.mSamplingF);
+    uint32_t mode = param.mStereo ? 1<<31 : 0;
+    mode |= param.mMode;
+    mBuffer[4] = htonl(mode);
+    mBuffer[5] = htonl(param.mVolume);
+    mBuffer[6] = htonl(param.mNumberRepetition);
+
+    return sendData((char*)mBuffer, 28);
+}
+
+
+/**
+ * param0 u32 sampling rate
+ * param1 u32 mono / stereo(MSB) | mode
+ * param2 u32 volume
+ * param3 u32 samples
+ */
+bool CmdStartRecording::sendCommand(AudioParam& param)
+{
+    mBuffer[0] = htonl(ECmdStartRecording);
+    mBuffer[1] = htonl(16);
+    mBuffer[2] = htonl(param.mSamplingF);
+    uint32_t mode = param.mStereo ? 1<<31 : 0;
+    mode |= param.mMode;
+    mBuffer[3] = htonl(mode);
+    mBuffer[4] = htonl(param.mVolume);
+    uint32_t samples = param.mBuffer->getSize() / (param.mStereo ? 4 : 2);
+    mBuffer[5] = htonl(samples);
+
+    return sendData((char*)mBuffer, 24);
+}
+
+/**
+ * param0 sp<Buffer>
+ */
+bool CmdStartRecording::handleReply(const uint32_t* data, AudioParam* param)
+{
+    if (!checkHeaderId(data, ECmdStartRecording)) {
+        return false;
+    }
+    if (data[1] != 0) { // no endian change for 0
+        LOGE("error in reply %d", ntohl(data[1]));
+        return false;
+    }
+    int len = ntohl(data[2]);
+    if (len > (int)param->mBuffer->getCapacity()) {
+        LOGE("received data %d exceeding buffer capacity %d", len, param->mBuffer->getCapacity());
+        // read and throw away
+        //Buffer tempBuffer(len);
+        //readData(tempBuffer.getData(), len);
+        return false;
+    }
+    if (!readData(param->mBuffer->getData(), len)) {
+        return false;
+    }
+    LOGI("received data %d from device", len);
+    param->mBuffer->setHandled(len);
+    param->mBuffer->setSize(len);
+    return true;
+}
+
+
+
+
diff --git a/suite/audio_quality/lib/src/audio/AudioRecordingLocal.cpp b/suite/audio_quality/lib/src/audio/AudioRecordingLocal.cpp
new file mode 100644
index 0000000..1325949
--- /dev/null
+++ b/suite/audio_quality/lib/src/audio/AudioRecordingLocal.cpp
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2012 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 <stdint.h>
+#include <string.h>
+#include <tinyalsa/asoundlib.h>
+
+#include "audio/AudioHardware.h"
+#include "audio/Buffer.h"
+#include "Log.h"
+
+#include "audio/AudioRecordingLocal.h"
+
+
+AudioRecordingLocal::AudioRecordingLocal(int hwId)
+    : mHwId(hwId),
+      mPcmHandle(NULL)
+{
+    LOGV("AudioRecordingLocal %x", (unsigned int)this);
+}
+
+AudioRecordingLocal::~AudioRecordingLocal()
+{
+    LOGV("~AudioRecordingLocal %x", (unsigned int)this);
+    releaseHw();
+}
+
+bool AudioRecordingLocal::doPrepare(AudioHardware::SamplingRate samplingRate, int samplesInOneGo)
+{
+    releaseHw();
+
+    struct pcm_config config;
+
+    memset(&config, 0, sizeof(config));
+    config.channels = 2;
+    config.rate = samplingRate;
+    config.period_size = 1024;
+    config.period_count = 32;
+    config.format = PCM_FORMAT_S16_LE;
+    config.start_threshold = 0;
+    config.stop_threshold = 0;
+    config.silence_threshold = 0;
+
+    mPcmHandle = pcm_open(mHwId, 0, PCM_IN, &config);
+    if (!mPcmHandle || !pcm_is_ready(mPcmHandle)) {
+       LOGE("Unable to open PCM device(%d) (%s)\n", mHwId, pcm_get_error(mPcmHandle));
+       return false;
+    }
+
+    mSamples = samplesInOneGo;
+    mSizes = samplesInOneGo * 4; // stereo, 16bit
+
+    mBufferSize = pcm_get_buffer_size(mPcmHandle);
+    LOGD("buffer size %d, read size %d", mBufferSize, mSizes);
+    return true;
+}
+
+bool AudioRecordingLocal::doPlaybackOrRecord(android::sp<Buffer>& buffer)
+{
+    int toRead = mSizes;
+    if (buffer->amountToHandle() < (size_t)mSizes) {
+        toRead = buffer->amountToHandle();
+    }
+    LOGD("recording will read %d", toRead);
+
+    while (toRead > 0) {
+        int readSize = (toRead > mBufferSize) ? mBufferSize : toRead;
+        if (pcm_read(mPcmHandle, buffer->getUnhanledData(), readSize)) {
+            LOGE("AudioRecordingLocal error %s", pcm_get_error(mPcmHandle));
+            return false;
+        }
+        buffer->increaseHandled(readSize);
+        toRead -= readSize;
+    }
+    LOGV("AudioRecordingLocal::doPlaybackOrRecord %d", buffer->amountHandled());
+    return true;
+}
+
+void AudioRecordingLocal::doStop()
+{
+    pcm_stop(mPcmHandle);
+}
+
+void AudioRecordingLocal::releaseHw()
+{
+    if (mPcmHandle != NULL) {
+        LOGV("releaseHw %x", (unsigned int)this);
+        doStop();
+        pcm_close(mPcmHandle);
+        mPcmHandle = NULL;
+    }
+}
diff --git a/suite/audio_quality/lib/src/audio/AudioRemote.cpp b/suite/audio_quality/lib/src/audio/AudioRemote.cpp
new file mode 100644
index 0000000..444a33e
--- /dev/null
+++ b/suite/audio_quality/lib/src/audio/AudioRemote.cpp
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2012 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 "Log.h"
+#include "audio/AudioRemote.h"
+#include "audio/RemoteAudio.h"
+
+bool AudioRemote::prepare(AudioHardware::SamplingRate samplingRate, int volume, int mode)
+{
+    if (mRemote == NULL) {
+        LOGE("AudioRemote::prepare mRemote NULL");
+        return false;
+    }
+    mSamplingRate = samplingRate;
+    mVolume = volume;
+    mMode = mode;
+    return true;
+}
+
+AudioRemote::AudioRemote(android::sp<RemoteAudio>& remote)
+    : mRemote(remote)
+{
+
+}
+
+AudioRemotePlayback::AudioRemotePlayback(android::sp<RemoteAudio>& remote)
+    : AudioRemote(remote)
+{
+
+}
+
+bool AudioRemotePlayback::startPlaybackOrRecord(android::sp<Buffer>& buffer, int numberRepetition)
+{
+    //TODO not supported for the moment
+    return false;
+}
+
+bool AudioRemotePlayback::waitForCompletion()
+{
+    return mRemote->waitForPlaybackCompletion();
+}
+
+void AudioRemotePlayback::stopPlaybackOrRecord()
+{
+    mRemote->stopPlayback();
+}
+
+bool AudioRemotePlayback::startPlaybackForRemoteData(int id, bool stereo, int numberRepetition)
+{
+    return mRemote->startPlayback(stereo, mSamplingRate, mMode, mVolume, id, numberRepetition);
+}
+
+AudioRemoteRecording::AudioRemoteRecording(android::sp<RemoteAudio>& remote)
+    : AudioRemote(remote)
+{
+
+}
+
+bool AudioRemoteRecording::startPlaybackOrRecord(android::sp<Buffer>& buffer,
+        int /*numberRepetition*/)
+{
+    bool stereo = buffer->isStereo();
+    return mRemote->startRecording(stereo, mSamplingRate, mMode, mVolume, buffer);
+}
+
+bool AudioRemoteRecording::waitForCompletion()
+{
+    return mRemote->waitForRecordingCompletion();
+}
+
+void AudioRemoteRecording::stopPlaybackOrRecord()
+{
+    mRemote->stopRecording();
+}
+
+
+
diff --git a/suite/audio_quality/lib/src/audio/AudioSignalFactory.cpp b/suite/audio_quality/lib/src/audio/AudioSignalFactory.cpp
new file mode 100644
index 0000000..d2308a3
--- /dev/null
+++ b/suite/audio_quality/lib/src/audio/AudioSignalFactory.cpp
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2012 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 <math.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "Log.h"
+#include "audio/AudioSignalFactory.h"
+
+android::sp<Buffer> AudioSignalFactory::generateSineWave(AudioHardware::BytesPerSample BPS,
+        int maxPositive, AudioHardware::SamplingRate samplingRate, int signalFreq,
+        int samples,  bool stereo)
+{
+    int bufferSize = samples * (stereo? 2 : 1) * BPS;
+    android::sp<Buffer> buffer(new Buffer(bufferSize));
+    // only 16bit signed
+    ASSERT(BPS == AudioHardware::E2BPS);
+    int16_t* data = reinterpret_cast<int16_t*>(buffer->getData());
+    double multiplier = 2.0 * M_PI * (double)signalFreq / samplingRate;
+    for (int i = 0; i < samples; i++) {
+        double val = sin(multiplier * i) * maxPositive;
+        *data = (int16_t)val;
+        data++;
+        if(stereo) {
+            *data = (int16_t)val;
+            data++;
+        }
+    }
+    buffer->setSize(buffer->getCapacity());
+    return buffer;
+}
+android::sp<Buffer> AudioSignalFactory::generateWhiteNoise(AudioHardware::BytesPerSample BPS,
+        int maxPositive, int samples, bool stereo)
+{
+    int bufferSize = samples * (stereo? 2 : 1) * BPS;
+    android::sp<Buffer> buffer(new Buffer(bufferSize, bufferSize));
+    // only 16bit signed
+    ASSERT(BPS == AudioHardware::E2BPS);
+    srand(123456);
+    int16_t* data = reinterpret_cast<int16_t*>(buffer->getData());
+    int middle = RAND_MAX / 2;
+    double multiplier = (double)maxPositive / middle;
+    for (int i = 0; i < samples; i++) {
+        int val =  rand();
+        val = (int16_t)((val - middle) * maxPositive / middle);
+        *data = val;
+        data++;
+        if (stereo) {
+            *data = val;
+            data++;
+        }
+    }
+    buffer->setSize(buffer->getCapacity());
+    return buffer;
+}
+
+android::sp<Buffer> AudioSignalFactory::generateZeroSound(AudioHardware::BytesPerSample BPS,
+        int samples, bool stereo)
+{
+    int bufferSize = samples * (stereo? 2 : 1) * BPS;
+    android::sp<Buffer> buffer(new Buffer(bufferSize, bufferSize));
+    // only 16bit signed
+    ASSERT(BPS == AudioHardware::E2BPS);
+    int16_t* data = reinterpret_cast<int16_t*>(buffer->getData());
+    for (int i = 0; i < samples; i++) {
+        *data = 0;
+        data++;
+        if (stereo) {
+            *data = 0;
+            data++;
+        }
+    }
+    buffer->setSize(buffer->getCapacity());
+    return buffer;
+}
+
+
diff --git a/suite/audio_quality/lib/src/audio/Buffer.cpp b/suite/audio_quality/lib/src/audio/Buffer.cpp
new file mode 100644
index 0000000..316374a
--- /dev/null
+++ b/suite/audio_quality/lib/src/audio/Buffer.cpp
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2012 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 <stdint.h>
+#include <iostream>
+#include <fstream>
+#include <utils/String8.h>
+#include "Log.h"
+#include "StringUtil.h"
+#include "audio/Buffer.h"
+
+Buffer::Buffer(size_t capacity, size_t size, bool stereo)
+    : mCapacity(capacity),
+      mSize(size),
+      mHandled(0),
+      mStereo(stereo)
+{
+    mData = new char[capacity];
+    //LOGV("Buffer %d data %x", capacity, (unsigned int)mData);
+    // assume 4bytes alignment
+    ASSERT(((long)mData & 0x3) == 0);
+    // filling with zero just to make valgrind happy.
+    // Otherwise, valgrind will complain about uninitialized data for all captured data
+    memset(mData, capacity, 0);
+};
+
+Buffer::~Buffer()
+{
+    delete[] mData;
+    //LOGV("~Buffer %d", mCapacity);
+}
+
+void Buffer::changeToMono(ConvertOption option)
+{
+    size_t newSize = mSize/2;
+    int16_t* data = reinterpret_cast<int16_t*>(mData);
+    if (option == EKeepCh0) {
+        for (size_t i = 0; i < newSize/2; i++) { //16bpp only
+            int16_t l = data[i * 2];
+            data[i] = l;
+        }
+    } else if (option == EKeepCh1) {
+        for (size_t i = 0; i < newSize/2; i++) { //16bpp only
+            int16_t r = data[i * 2 + 1];
+            data[i] = r;
+        }
+    } else { // average
+        for (size_t i = 0; i < newSize/2; i++) { //16bpp only
+            int16_t l = data[i * 2];
+            int16_t r = data[i * 2 + 1];
+            int16_t avr = (int16_t)(((int32_t)l + (int32_t)r)/2);
+            data[i] = avr;
+        }
+    }
+    mSize = newSize;
+    mHandled /= 2;
+    mStereo = false;
+}
+
+bool Buffer::changeToStereo()
+{
+    //TODO ChangeToStereo
+    return false;
+}
+
+const char* EXTENSION_S16_STEREO = ".r2s";
+const char* EXTENSION_S16_MONO = ".r2m";
+Buffer* Buffer::loadFromFile(const android::String8& filename)
+{
+    bool stereo;
+    if (StringUtil::endsWith(filename, EXTENSION_S16_STEREO)) {
+        stereo = true;
+    } else if (StringUtil::endsWith(filename, EXTENSION_S16_MONO)) {
+        stereo = false;
+    } else {
+        LOGE("Buffer::loadFromFile specified file %s has unknown extension.", filename.string());
+        return NULL;
+    }
+    std::ifstream file(filename.string(),  std::ios::in | std::ios::binary |
+            std::ios::ate);
+    if (!file.is_open()) {
+        LOGE("Buffer::loadFromFile cannot open file %s.", filename.string());
+        return NULL;
+    }
+    size_t size = file.tellg();
+    Buffer* buffer = new Buffer(size, size, stereo);
+    if (buffer == NULL) {
+        return NULL;
+    }
+    file.seekg(0, std::ios::beg);
+    file.read(buffer->mData, size); //TODO handle read error
+    file.close();
+    return buffer;
+}
+
+bool Buffer::saveToFile(const android::String8& filename)
+{
+    android::String8 filenameWithExtension(filename);
+    if (isStereo()) {
+        filenameWithExtension.append(EXTENSION_S16_STEREO);
+    } else {
+        filenameWithExtension.append(EXTENSION_S16_MONO);
+    }
+    std::ofstream file(filenameWithExtension.string(),  std::ios::out | std::ios::binary |
+            std::ios::trunc);
+    if (!file.is_open()) {
+        LOGE("Buffer::saveToFile cannot create file %s.",
+                filenameWithExtension.string());
+        return false;
+    }
+    file.write(mData, mSize);
+    bool writeOK = true;
+    if (file.rdstate() != std::ios_base::goodbit) {
+        LOGE("Got error while writing file %s %x", filenameWithExtension.string(), file.rdstate());
+        writeOK = false;
+    }
+    file.close();
+    return writeOK;
+}
+
+bool Buffer::operator == (const Buffer& b) const
+{
+    if (mStereo != b.mStereo) {
+        LOGD("stereo mismatch %d %d", mStereo, b.mStereo);
+        return false;
+    }
+    if (mSize != b.mSize) {
+        LOGD("size mismatch %d %d", mSize, b.mSize);
+        return false;
+    }
+    for (size_t i = 0; i < mSize; i++) {
+        if (mData[i] != b.mData[i]) {
+            LOGD("%d %x vs %x", i, mData[i], b.mData[i]);
+            return false;
+        }
+    }
+    return true;
+}
diff --git a/suite/audio_quality/lib/src/audio/RemoteAudio.cpp b/suite/audio_quality/lib/src/audio/RemoteAudio.cpp
new file mode 100644
index 0000000..10ae14c
--- /dev/null
+++ b/suite/audio_quality/lib/src/audio/RemoteAudio.cpp
@@ -0,0 +1,336 @@
+/*
+ * Copyright (C) 2012 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 <arpa/inet.h>
+#include <strings.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <utils/Looper.h>
+
+#include "Log.h"
+#include "audio/AudioProtocol.h"
+#include "audio/RemoteAudio.h"
+
+
+RemoteAudio::RemoteAudio(ClientSocket& socket)
+    : mExitRequested(false),
+      mSocket(socket),
+      mDownloadHandler(new CommandHandler(*this, (int)AudioProtocol::ECmdDownload)),
+      mPlaybackHandler(new CommandHandler(*this, (int)AudioProtocol::ECmdStartPlayback)),
+      mRecordingHandler(new CommandHandler(*this, (int)AudioProtocol::ECmdStartRecording)),
+      mDownloadId(0)
+{
+    mCmds[AudioProtocol::ECmdDownload - AudioProtocol::ECmdStart] = new CmdDownload(socket);
+    mCmds[AudioProtocol::ECmdStartPlayback - AudioProtocol::ECmdStart] =
+            new CmdStartPlayback(socket);
+    mCmds[AudioProtocol::ECmdStopPlayback - AudioProtocol::ECmdStart] =
+            new CmdStopPlayback(socket);
+    mCmds[AudioProtocol::ECmdStartRecording - AudioProtocol::ECmdStart] =
+            new CmdStartRecording(socket);
+    mCmds[AudioProtocol::ECmdStopRecording - AudioProtocol::ECmdStart] =
+            new CmdStopRecording(socket);
+}
+
+RemoteAudio::~RemoteAudio()
+{
+    for (int i = 0; i < (AudioProtocol::ECmdLast - AudioProtocol::ECmdStart); i++) {
+        delete mCmds[i];
+    }
+    //mBufferList.clear();
+}
+
+bool RemoteAudio::init(int port)
+{
+    mPort = port;
+    if (run() != android::NO_ERROR) {
+        LOGE("RemoteAudio cannot run");
+        // cannot run thread
+        return false;
+    }
+
+    if (!mInitWait.timedWait(CLIENT_WAIT_TIMEOUT_MSEC)) {
+        return false;
+    }
+    return mInitResult;
+}
+
+bool RemoteAudio::threadLoop()
+{
+    // initial action until socket connection done by init
+    mLooper = new android::Looper(false);
+    if (mLooper.get() == NULL) {
+        wakeClient(false);
+        return false;
+    }
+    android::Looper::setForThread(mLooper);
+
+    if (!mSocket.init("127.0.0.1", mPort)) {
+        wakeClient(false);
+        return false;
+    }
+    LOGD("adding fd %d to polling", mSocket.getFD());
+    mLooper->addFd(mSocket.getFD(), EIdSocket, ALOOPER_EVENT_INPUT, socketRxCallback, this);
+    wakeClient(true);
+    while(!mExitRequested) {
+        mLooper->pollOnce(10000);
+    }
+    return false; // exit without requestExit()
+}
+
+void RemoteAudio::wakeClient(bool result)
+{
+    mInitResult = result;
+    mInitWait.post();
+}
+
+bool RemoteAudio::handlePacket()
+{
+    uint32_t data[AudioProtocol::REPLY_HEADER_SIZE/sizeof(uint32_t)];
+    AudioProtocol::CommandId id;
+    if (!AudioProtocol::handleReplyHeader(mSocket, data, id)) {
+        return false;
+    }
+    AudioParam* param = NULL;
+    CommandHandler* handler = NULL;
+    if (id == AudioProtocol::ECmdDownload) {
+        handler = reinterpret_cast<CommandHandler*>(mDownloadHandler.get());
+    } else if (id == AudioProtocol::ECmdStartPlayback) {
+        handler = reinterpret_cast<CommandHandler*>(mPlaybackHandler.get());
+    } else if (id == AudioProtocol::ECmdStartRecording) {
+        handler = reinterpret_cast<CommandHandler*>(mRecordingHandler.get());
+        param = &(handler->getParam());
+    }
+    bool result = mCmds[id - AudioProtocol::ECmdStart]->handleReply(data, param);
+    if (handler != NULL) {
+        LOGD("handler present. Notify client");
+        android::Mutex::Autolock lock(handler->mStateLock);
+        if (handler->mNotifyOnReply) {
+            handler->mNotifyOnReply = false;
+            handler->mResult = result;
+            handler->mClientWait.post();
+        }
+        handler->mActive = false;
+    }
+    return result;
+}
+
+int RemoteAudio::socketRxCallback(int fd, int events, void* data)
+{
+    RemoteAudio* self = reinterpret_cast<RemoteAudio*>(data);
+    if (events & ALOOPER_EVENT_INPUT) {
+        //LOGD("socketRxCallback input");
+        if (!self->handlePacket()) { //error, stop polling
+            LOGE("socketRxCallback, error in packet, stopping polling");
+            return 0;
+        }
+    }
+    return 1;
+}
+
+void RemoteAudio::sendCommand(android::sp<android::MessageHandler>& command)
+{
+    mLooper->sendMessage(command, toCommandHandler(command)->getMessage());
+}
+
+bool RemoteAudio::waitForCompletion(android::sp<android::MessageHandler>& command, int timeInMSec)
+{
+    return toCommandHandler(command)->timedWait(timeInMSec);
+}
+
+bool RemoteAudio::waitForPlaybackOrRecordingCompletion(
+        android::sp<android::MessageHandler>& commandHandler)
+{
+    CommandHandler* handler = reinterpret_cast<CommandHandler*>(commandHandler.get());
+    handler->mStateLock.lock();
+    if(!handler->mActive) {
+        handler->mStateLock.unlock();
+        return true;
+    }
+    int runTime = handler->getParam().mBuffer->getSize() /
+            (handler->getParam().mStereo ? 4 : 2) * 1000 / handler->getParam().mSamplingF;
+    handler->mNotifyOnReply = true;
+    handler->mStateLock.unlock();
+    return waitForCompletion(commandHandler, runTime + CLIENT_WAIT_TIMEOUT_MSEC);
+}
+
+void RemoteAudio::doStop(android::sp<android::MessageHandler>& commandHandler,
+        AudioProtocol::CommandId id)
+{
+    CommandHandler* handler = reinterpret_cast<CommandHandler*>(commandHandler.get());
+    handler->mStateLock.lock();
+    if (!handler->mActive) {
+        handler->mStateLock.unlock();
+       return;
+    }
+    handler->mActive = false;
+    handler->mNotifyOnReply = false;
+    handler->mStateLock.unlock();
+    android::sp<android::MessageHandler> command(new CommandHandler(*this, (int)id));
+    sendCommand(command);
+    waitForCompletion(command, CLIENT_WAIT_TIMEOUT_MSEC);
+}
+
+
+bool RemoteAudio::downloadData(const android::String8 name, android::sp<Buffer>& buffer, int& id)
+{
+    CommandHandler* handler = reinterpret_cast<CommandHandler*>(mDownloadHandler.get());
+    id = mDownloadId;
+    mDownloadId++;
+    handler->getParam().mId = id;
+    handler->getParam().mBuffer = buffer;
+    sendCommand(mDownloadHandler);
+    handler->mStateLock.lock();
+    handler->mNotifyOnReply = true;
+    handler->mStateLock.unlock();
+    // assume 1Mbps ==> 1000 bits per msec ==> 125 bytes per msec
+    int maxWaitTime = CLIENT_WAIT_TIMEOUT_MSEC + buffer->getSize() / 125;
+    // client blocked until reply comes from DUT
+    if (!waitForCompletion(mDownloadHandler, maxWaitTime)) {
+        LOGE("timeout");
+        return false;
+    }
+    mBufferList[id] = buffer;
+    mIdMap[name] = id;
+    return handler->mResult;
+}
+
+int RemoteAudio::getDataId(const android::String8& name)
+{
+    std::map<android::String8, int>::iterator it;
+    it = mIdMap.find(name);
+    if (it == mIdMap.end()) {
+        LOGE("Buffer name %s not registered", name.string());
+        return -1;
+    }
+    return it->second;
+}
+
+bool RemoteAudio::startPlayback(bool stereo, int samplingF, int mode, int volume,
+        int id, int numberRepetition)
+{
+    CommandHandler* handler = reinterpret_cast<CommandHandler*>(mPlaybackHandler.get());
+    handler->mStateLock.lock();
+    if (handler->mActive) {
+        LOGE("busy");
+        handler->mStateLock.unlock();
+        return false;
+    }
+    std::map<int, android::sp<Buffer> >::iterator it;
+    it = mBufferList.find(id);
+    if (it == mBufferList.end()) {
+        LOGE("Buffer id %d not registered", id);
+        return false;
+    }
+    handler->mActive = true;
+    handler->getParam().mStereo = stereo;
+    handler->getParam().mSamplingF = samplingF;
+    handler->getParam().mMode = mode;
+    handler->getParam().mVolume = volume;
+    handler->getParam().mId = id;
+    // for internal tracking
+    handler->getParam().mBuffer = it->second;
+    handler->getParam().mNumberRepetition = numberRepetition;
+    handler->mStateLock.unlock();
+    sendCommand(mPlaybackHandler);
+    if (!waitForCompletion(mPlaybackHandler, CLIENT_WAIT_TIMEOUT_MSEC)) {
+        LOGE("timeout");
+        return false;
+    }
+    return handler->mResult;
+}
+
+void RemoteAudio::stopPlayback()
+{
+    doStop(mPlaybackHandler, AudioProtocol::ECmdStopPlayback);
+}
+
+bool RemoteAudio::waitForPlaybackCompletion()
+{
+    return waitForPlaybackOrRecordingCompletion(mPlaybackHandler);
+}
+
+bool RemoteAudio::startRecording(bool stereo, int samplingF, int mode, int volume,
+        android::sp<Buffer>& buffer)
+{
+    CommandHandler* handler = reinterpret_cast<CommandHandler*>(mRecordingHandler.get());
+    handler->mStateLock.lock();
+    if (handler->mActive) {
+        LOGE("busy");
+        handler->mStateLock.unlock();
+        return false;
+    }
+    handler->mActive = true;
+    handler->getParam().mStereo = stereo;
+    handler->getParam().mSamplingF = samplingF;
+    handler->getParam().mMode = mode;
+    handler->getParam().mVolume = volume;
+    handler->getParam().mBuffer = buffer;
+    handler->mStateLock.unlock();
+    sendCommand(mRecordingHandler);
+    if (!waitForCompletion(mRecordingHandler, CLIENT_WAIT_TIMEOUT_MSEC)) {
+        LOGE("timeout");
+        return false;
+    }
+    return handler->mResult;
+}
+
+bool RemoteAudio::waitForRecordingCompletion()
+{
+    return waitForPlaybackOrRecordingCompletion(mRecordingHandler);
+}
+
+void RemoteAudio::stopRecording()
+{
+    doStop(mRecordingHandler, AudioProtocol::ECmdStopRecording);
+}
+
+/** should be called before RemoteAudio is destroyed */
+void RemoteAudio::release()
+{
+    android::sp<android::MessageHandler> command(new CommandHandler(*this, CommandHandler::EExit));
+    sendCommand(command);
+    join(); // wait for exit
+    mSocket.release();
+}
+
+void RemoteAudio::CommandHandler::handleMessage(const android::Message& message)
+{
+    switch(message.what) {
+    case EExit:
+        LOGD("thread exit requested, will exit ");
+        mResult = true;
+        mThread.mExitRequested = true;
+        mClientWait.post(); // client will not wait, but just do it.
+        break;
+    case AudioProtocol::ECmdDownload:
+    case AudioProtocol::ECmdStartPlayback:
+    case AudioProtocol::ECmdStopPlayback:
+    case AudioProtocol::ECmdStartRecording:
+    case AudioProtocol::ECmdStopRecording:
+    {
+        mResult = (mThread.mCmds[message.what - AudioProtocol::ECmdStart]) \
+                ->sendCommand(mParam);
+        // no post for download. Client blocked until reply comes with time-out
+        if (message.what != AudioProtocol::ECmdDownload) {
+            mClientWait.post();
+        }
+
+    }
+        break;
+
+    }
+}
diff --git a/suite/audio_quality/lib/src/task/ModelBuilder.cpp b/suite/audio_quality/lib/src/task/ModelBuilder.cpp
new file mode 100644
index 0000000..87373ca
--- /dev/null
+++ b/suite/audio_quality/lib/src/task/ModelBuilder.cpp
@@ -0,0 +1,263 @@
+/*
+ * Copyright (C) 2012 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 <tinyxml.h>
+
+#include <UniquePtr.h>
+
+#include "Log.h"
+#include "GenericFactory.h"
+#include "task/ModelBuilder.h"
+
+static const int MAX_NO_CHILDREN = 8;
+static const ModelBuilder::ChildInfo CASE_TABLE[] = {
+    { TaskGeneric::ETaskSetup, true },
+    { TaskGeneric::ETaskAction, true },
+    { TaskGeneric::ETaskSave, false }
+};
+static const ModelBuilder::ChildInfo SETUP_TABLE[] = {
+    { TaskGeneric::ETaskSound, false },
+    { TaskGeneric::ETaskProcess, false }
+};
+static const ModelBuilder::ChildInfo ACTION_TABLE[] = {
+    { TaskGeneric::ETaskSequential, true }
+};
+static const ModelBuilder::ChildInfo SEQUENTIAL_TABLE[] = {
+    { TaskGeneric::ETaskSequential, false },
+    { TaskGeneric::ETaskInput, false },
+    { TaskGeneric::ETaskOutput, false },
+    { TaskGeneric::ETaskProcess, false },
+    { TaskGeneric::ETaskMessage, false }
+};
+
+
+ModelBuilder::ParsingInfo ModelBuilder::mParsingTable[ModelBuilder::PARSING_TABLE_SIZE] = {
+    { "case", TaskGeneric::ETaskCase, CASE_TABLE,
+            sizeof(CASE_TABLE)/sizeof(ModelBuilder::ChildInfo) },
+    { "setup", TaskGeneric::ETaskSetup, SETUP_TABLE,
+            sizeof(SETUP_TABLE)/sizeof(ModelBuilder::ChildInfo) },
+    { "action", TaskGeneric::ETaskAction, ACTION_TABLE,
+            sizeof(ACTION_TABLE)/sizeof(ModelBuilder::ChildInfo) },
+    { "sequential", TaskGeneric::ETaskSequential, SEQUENTIAL_TABLE,
+                sizeof(SEQUENTIAL_TABLE)/sizeof(ModelBuilder::ChildInfo) },
+    { "process", TaskGeneric::ETaskProcess, NULL, 0 },
+    { "input", TaskGeneric::ETaskInput, NULL, 0 },
+    { "output", TaskGeneric::ETaskOutput, NULL, 0 },
+    { "sound", TaskGeneric::ETaskSound, NULL, 0 },
+    { "save", TaskGeneric::ETaskSave, NULL, 0 },
+    { "message", TaskGeneric::ETaskMessage, NULL, 0 }
+};
+
+
+ModelBuilder::ModelBuilder()
+    : mFactory(new GenericFactory())
+{
+
+}
+
+ModelBuilder::ModelBuilder(GenericFactory* factory)
+    : mFactory(factory)
+{
+
+}
+ModelBuilder::~ModelBuilder()
+{
+    delete mFactory;
+}
+
+TaskGeneric* ModelBuilder::parseTestDescriptionXml(const android::String8& xmlFileName,
+        bool caseOnly)
+{
+    TiXmlDocument doc(xmlFileName.string());
+    if (!doc.LoadFile()) {
+        LOGE("ModelBuilder::parseTestDescriptionXml cannot load file %s", xmlFileName.string());
+        return NULL;
+    }
+    const TiXmlElement* root;
+    if ((root = doc.FirstChildElement("case")) != NULL) {
+        return parseCase(*root);
+    } else if (!caseOnly && ((root = doc.FirstChildElement("batch")) != NULL)) {
+        return parseBatch(*root, xmlFileName);
+    } else {
+        LOGE("ModelBuilder::parseTestDescriptionXml wrong root element");
+        return NULL;
+    }
+}
+
+TaskGeneric* ModelBuilder::parseGeneric(const TiXmlElement& self, int tableIndex)
+{
+    TaskGeneric::TaskType typeSelf(mParsingTable[tableIndex].type);
+    int Nchildren = mParsingTable[tableIndex].Nchildren;
+    UniquePtr<TaskGeneric> taskSelf(mFactory->createTask(typeSelf));
+    if (taskSelf.get() == NULL) {
+        return NULL;
+    }
+    if (!parseAttributes(self, *taskSelf.get())) {
+        return NULL;
+    }
+    // copy mandatory flags, and will be cleared once the item is found
+    bool mandatoryAbsence[MAX_NO_CHILDREN];
+    const ModelBuilder::ChildInfo* childTable = mParsingTable[tableIndex].allowedChildren;
+    for (int i = 0; i < Nchildren; i++) {
+        mandatoryAbsence[i] = childTable[i].mandatory;
+    }
+
+    // handle children
+    const TiXmlElement* child = self.FirstChildElement();
+    while (child != NULL) {
+        TaskGeneric::TaskType childType(TaskGeneric::ETaskInvalid);
+        int i;
+        // check if type is valid
+        for (i = 0; i < PARSING_TABLE_SIZE; i++) {
+            if (strcmp(child->Value(), mParsingTable[i].name) == 0) {
+                break;
+            }
+        }
+        if (i == PARSING_TABLE_SIZE) {
+            LOGE("ModelBuilder::parseGeneric unknown element %s", child->Value());
+            return NULL;
+        }
+        childType = mParsingTable[i].type;
+        int j;
+        // check if the type is allowed as child
+        for (j = 0; j < Nchildren; j++) {
+            if (childTable[j].type == childType) {
+                if (childTable[j].mandatory) {
+                    mandatoryAbsence[j] = false;
+                }
+                break;
+            }
+        }
+        if (j == Nchildren) {
+            LOGE("ModelBuilder::parseGeneric unsupported child type %d for type %d", childType,
+                    typeSelf);
+            return NULL;
+        }
+        UniquePtr<TaskGeneric> taskChild(parseGeneric(*child, i));
+        if (taskChild.get() == NULL) {
+            LOGE("ModelBuilder::parseGeneric failed in parsing child type %d for type %d",
+                    childType, typeSelf);
+            return NULL;
+        }
+        if (!taskSelf.get()->addChild(taskChild.get())) {
+            LOGE("ModelBuilder::parseGeneric cannot add child type %d to type %d", childType,
+                    typeSelf);
+            return NULL;
+        }
+        TaskGeneric* donotuse = taskChild.release();
+
+        child = child->NextSiblingElement();
+    }
+    for (int i = 0; i < Nchildren; i++) {
+        if (mandatoryAbsence[i]) {
+            LOGE("ModelBuilder::parseGeneric mandatory child type %d not present in type %d",
+                    childTable[i].type, typeSelf);
+            return NULL;
+        }
+    }
+
+    return taskSelf.release();
+}
+
+
+TaskCase* ModelBuilder::parseCase(const TiXmlElement& root)
+{
+    // position 0 of mParsingTable should be "case"
+    return reinterpret_cast<TaskCase*>(parseGeneric(root, 0));
+}
+
+
+TaskBatch* ModelBuilder::parseBatch(const TiXmlElement& root, const android::String8& xmlFileName)
+{
+    UniquePtr<TaskBatch> batch(
+            reinterpret_cast<TaskBatch*>(mFactory->createTask(TaskGeneric::ETaskBatch)));
+    if (batch.get() == NULL) {
+        LOGE("ModelBuilder::handleBatch cannot create TaskBatch");
+        return NULL;
+    }
+    if (!parseAttributes(root, *batch.get())) {
+        return NULL;
+    }
+
+    const TiXmlElement* inc = root.FirstChildElement("include");
+    if (inc == NULL) {
+        LOGE("ModelBuilder::handleBatch no include inside batch");
+        return NULL;
+    }
+    android::String8 path = xmlFileName.getPathDir();
+
+    UniquePtr<TaskCase> testCase;
+    int i = 0;
+    while (1) {
+        if (inc == NULL) {
+            break;
+        }
+        if (strcmp(inc->Value(),"include") != 0) {
+            LOGE("ModelBuilder::handleBatch invalid element %s", inc->Value());
+        }
+        testCase.reset(parseInclude(*inc, path));
+        if (testCase.get() == NULL) {
+            LOGE("ModelBuilder::handleBatch cannot create test case from include");
+            return NULL;
+        }
+        if (!batch.get()->addChild(testCase.get())) {
+            return NULL;
+        }
+        TaskGeneric* donotuse = testCase.release(); // parent will take care of destruction.
+        inc = inc->NextSiblingElement();
+        i++;
+    }
+    if (i == 0) {
+        // at least one include should exist.
+        LOGE("ModelBuilder::handleBatch no include elements");
+        return NULL;
+    }
+
+    return batch.release();
+}
+
+TaskCase* ModelBuilder::parseInclude(const TiXmlElement& elem, const android::String8& path)
+{
+    const char* fileName = elem.Attribute("file");
+    if (fileName == NULL) {
+        LOGE("ModelBuilder::handleBatch no include elements");
+        return NULL;
+    }
+    android::String8 incFile = path;
+    incFile.appendPath(fileName);
+
+    // again no dynamic_cast intentionally
+    return reinterpret_cast<TaskCase*>(parseTestDescriptionXml(incFile, true));
+}
+
+bool ModelBuilder::parseAttributes(const TiXmlElement& elem, TaskGeneric& task)
+{
+    const TiXmlAttribute* attr = elem.FirstAttribute();
+    while (1) {
+        if (attr == NULL) {
+            break;
+        }
+        android::String8 name(attr->Name());
+        android::String8 value(attr->Value());
+        if (!task.parseAttribute(name, value)) {
+            LOGE("ModelBuilder::parseAttributes cannot parse attribute %s:%s for task type %d",
+                    attr->Name(), attr->Value(), task.getType());
+            return false;
+        }
+        attr = attr->Next();
+    }
+    return true;
+}
diff --git a/suite/audio_quality/lib/src/task/TaskAsync.cpp b/suite/audio_quality/lib/src/task/TaskAsync.cpp
new file mode 100644
index 0000000..4121a5e
--- /dev/null
+++ b/suite/audio_quality/lib/src/task/TaskAsync.cpp
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2012 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 <stdlib.h>
+#include "Log.h"
+#include "StringUtil.h"
+#include "task/TaskAll.h"
+
+TaskAsync::TaskAsync(TaskType type)
+    : TaskGeneric(type),
+      mVolume(-1),
+      mDeviceType(EDeviceHost),
+      mMode(AudioHardware::EModeVoice),
+      mAsynchronous(false)
+{
+    // nothing to do
+}
+
+TaskAsync::~TaskAsync()
+{
+
+}
+
+TaskGeneric::ExecutionResult TaskAsync::run()
+{
+    // id is mandatory
+    if (mId.length() == 0) {
+        LOGE(" TaskAsync::run no id attribute");
+        return TaskGeneric::EResultError;
+    }
+    TaskGeneric::ExecutionResult result = start();
+    if (result == TaskGeneric::EResultOK) {
+        if (!isAsynchronous()) {
+            return complete();
+        } else {
+            if (!getParentSequential()->queueAsyncTask(const_cast<TaskAsync*>(this))) {
+                LOGE("TaskAsync::run queueAsyncTask failed");
+                return TaskGeneric::EResultError;
+            }
+        }
+    }
+    return result;
+}
+
+bool TaskAsync::parseAttribute(const android::String8& name, const android::String8& value)
+{
+    bool result = true;
+    if (StringUtil::compare(name, "id") == 0) {
+        mId.append(value);
+    } else if (StringUtil::compare(name, "gain") == 0) {
+        mVolume = atoi(value.string());
+        if ((mVolume < 1) || (mVolume > 100)) {
+            LOGE("TaskGeneric::parseAttribute gain out of range %d", mVolume);
+            return false;
+        }
+    } else if (StringUtil::compare(name, "sync") == 0) {
+        if (StringUtil::compare(value, "start") == 0) { // async
+            makeAsynchronous();
+        }
+    } else if (StringUtil::compare(name, "device") == 0) {
+        if (StringUtil::compare(value, "host") == 0) {
+            mDeviceType = EDeviceHost;
+        } else if (StringUtil::compare(value, "DUT") == 0) {
+            mDeviceType = EDeviceDUT;
+        } else {
+            return false;
+        }
+    } else if (StringUtil::compare(name, "mode") == 0) {
+            if (StringUtil::compare(value, "voice") == 0) {
+                mMode = AudioHardware::EModeVoice;
+            } else if (StringUtil::compare(value, "music") == 0) {
+                mMode = AudioHardware::EModeMusic;
+            } else {
+                return false;
+            }
+    } else {
+        result = TaskGeneric::parseAttribute(name, value);
+    }
+    return result;
+}
+
+TaskSequential* TaskAsync::getParentSequential()
+{
+    ASSERT(getParent()->getType() == TaskGeneric::ETaskSequential);
+    return reinterpret_cast<TaskSequential*>(getParent());
+}
+
diff --git a/suite/audio_quality/lib/src/task/TaskBatch.cpp b/suite/audio_quality/lib/src/task/TaskBatch.cpp
new file mode 100644
index 0000000..f8c77fe
--- /dev/null
+++ b/suite/audio_quality/lib/src/task/TaskBatch.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2012 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 "Log.h"
+#include "Report.h"
+
+#include "task/TaskBatch.h"
+
+static const android::String8 STR_NAME("name");
+static const android::String8 STR_VERSION("version");
+static const android::String8 STR_DESCRIPTION("description");
+
+TaskBatch::TaskBatch()
+    :TaskGeneric(TaskGeneric::ETaskBatch)
+{
+    const android::String8* list[] = {&STR_NAME, &STR_VERSION, &STR_DESCRIPTION, NULL};
+    registerSupportedStringAttributes(list);
+}
+
+TaskBatch::~TaskBatch()
+{
+
+}
+
+bool TaskBatch::addChild(TaskGeneric* child)
+{
+    if (child->getType() != TaskGeneric::ETaskCase) {
+        LOGE("TaskBatch::addChild wrong child type %d", child->getType());
+        return false;
+    }
+    return TaskGeneric::addChild(child);
+}
+
+bool runAlways(TaskGeneric* child, void* data)
+{
+    child->run();
+    return true;
+}
+
+TaskGeneric::ExecutionResult TaskBatch::run()
+{
+    android::String8 name;
+    android::String8 version;
+
+    if (!findStringAttribute(STR_NAME, name) || !findStringAttribute(STR_VERSION, version)) {
+        LOGW("TaskBatch::run no name or version information");
+    }
+    Report::Instance()->printf("= Test batch %s version %s started. =", name.string(),
+            version.string());
+    bool result = TaskGeneric::forEachChild(runAlways, NULL);
+    Report::Instance()->printf("= Finished Test batch =");
+    return TaskGeneric::EResultOK;
+}
+
+
diff --git a/suite/audio_quality/lib/src/task/TaskCase.cpp b/suite/audio_quality/lib/src/task/TaskCase.cpp
new file mode 100644
index 0000000..9cbc6c8
--- /dev/null
+++ b/suite/audio_quality/lib/src/task/TaskCase.cpp
@@ -0,0 +1,352 @@
+/*
+ * Copyright (C) 2012 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 <sys/types.h>
+#include <regex.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "Log.h"
+#include "audio/RemoteAudio.h"
+#include "ClientImpl.h"
+#include "Report.h"
+#include "Settings.h"
+#include "StringUtil.h"
+#include "task/TaskCase.h"
+
+static const android::String8 STR_NAME("name");
+static const android::String8 STR_VERSION("version");
+static const android::String8 STR_DESCRIPTION("description");
+
+TaskCase::TaskCase()
+    : TaskGeneric(TaskGeneric::ETaskCase),
+      mClient(NULL)
+{
+    const android::String8* list[] = {&STR_NAME, &STR_VERSION, &STR_DESCRIPTION, NULL};
+    registerSupportedStringAttributes(list);
+}
+
+TaskCase::~TaskCase()
+{
+    delete mClient;
+}
+
+bool TaskCase::getCaseName(android::String8& name)
+{
+    if (!findStringAttribute(STR_NAME, name)) {
+        LOGW("TaskCase no name");
+        return false;
+    }
+    return true;
+}
+
+bool TaskCase::addChild(TaskGeneric* child)
+{
+    if ((child->getType() != TaskGeneric::ETaskSetup)
+            &&  (child->getType() != TaskGeneric::ETaskAction)
+            &&  (child->getType() != TaskGeneric::ETaskSave)) {
+        LOGE("TestCase::addChild wrong child type %d", child->getType());
+        return false;
+    }
+    return TaskGeneric::addChild(child);
+}
+
+template <typename T> bool registerGeneric(
+        typename std::map<android::String8, T>& map,
+        const android::String8& name, T& data)
+{
+    typename std::map<android::String8, T>::iterator it;
+    it = map.find(name);
+    if (it != map.end()) {
+        LOGV("registerGeneric key %s already registered", name.string());
+        return false;
+    }
+    LOGD("registerGeneric registered key %s", name.string());
+    map[name] = data;
+    return true;
+}
+
+template <typename T> bool findGeneric(typename std::map<android::String8, T>& map,
+        const android::String8& name, T& data)
+{
+    LOGD("findGeneric key %s", name.string());
+    typename std::map<android::String8, T>::iterator it;
+    it = map.find(name);
+    if (it == map.end()) {
+        return false;
+    }
+    data = it->second;
+    return true;
+}
+
+template <typename T> bool updateGeneric(typename std::map<android::String8, T>& map,
+        const android::String8& name, T& data)
+{
+    LOGD("updateGeneric key %s", name.string());
+    typename std::map<android::String8, T>::iterator it;
+    it = map.find(name);
+    if (it == map.end()) {
+        return false;
+    }
+    it->second = data;
+    return true;
+}
+
+// return all the matches for the given regular expression.
+// name string and the data itself is copied.
+template <typename T> typename std::list<std::pair<android::String8, T> >* findAllGeneric(
+        typename std::map<android::String8, T>& map, const char* re)
+{
+    regex_t regex;
+    if (regcomp(&regex, re, REG_EXTENDED | REG_NOSUB) != 0) {
+        LOGE("regcomp failed");
+        return NULL;
+    }
+    typename std::map<android::String8, T>::iterator it;
+    typename std::list<std::pair<android::String8, T> >* list = NULL;
+    for (it = map.begin(); it != map.end(); it++) {
+        if (regexec(&regex, it->first, 0, NULL, 0) == 0) {
+            if (list == NULL) { // create only when found
+                list = new std::list<std::pair<android::String8, T> >();
+                if (list == NULL) {
+                    regfree(&regex);
+                    return NULL;
+                }
+            }
+            typename std::pair<android::String8, T> match(it->first, it->second);
+            list->push_back(match);
+        }
+    }
+    regfree(&regex);
+    return list;
+}
+
+
+bool TaskCase::registerBuffer(const android::String8& orig, android::sp<Buffer>& buffer)
+{
+    android::String8 translated;
+    if (!translateVarName(orig, translated)) {
+        return false;
+    }
+    return registerGeneric<android::sp<Buffer> >(mBufferList, translated, buffer);
+}
+
+bool TaskCase::updateBuffer(const android::String8& orig, android::sp<Buffer>& buffer)
+{
+    android::String8 translated;
+    if (!translateVarName(orig, translated)) {
+        return false;
+    }
+    return updateGeneric<android::sp<Buffer> >(mBufferList, translated, buffer);
+}
+
+android::sp<Buffer> TaskCase::findBuffer(const android::String8& orig)
+{
+    android::String8 translated;
+    android::sp<Buffer> result;
+    if (!translateVarName(orig, translated)) {
+        return result;
+    }
+    findGeneric<android::sp<Buffer> >(mBufferList, translated, result);
+    return result;
+}
+
+std::list<TaskCase::BufferPair>* TaskCase::findAllBuffers(const android::String8& re)
+{
+    android::String8 translated;
+    if (!translateVarName(re, translated)) {
+        return NULL;
+    }
+    return findAllGeneric<android::sp<Buffer> >(mBufferList, translated.string());
+}
+
+
+bool TaskCase::registerValue(const android::String8& orig, Value& val)
+{
+    android::String8 translated;
+    if (!translateVarName(orig, translated)) {
+        return false;
+    }
+    LOGD("str %x", translated.string());
+    return registerGeneric<Value>(mValueList, translated, val);
+}
+
+bool TaskCase::updateValue(const android::String8& orig, Value& val)
+{
+    android::String8 translated;
+    if (!translateVarName(orig, translated)) {
+        return false;
+    }
+    return updateGeneric<Value>(mValueList, translated, val);
+}
+
+bool TaskCase::findValue(const android::String8& orig, Value& val)
+{
+    android::String8 translated;
+    if (!translateVarName(orig, translated)) {
+        return false;
+    }
+    return findGeneric<Value>(mValueList, translated, val);
+}
+
+std::list<TaskCase::ValuePair>* TaskCase::findAllValues(const android::String8& re)
+{
+    android::String8 translated;
+    if (!translateVarName(re, translated)) {
+        return NULL;
+    }
+    return findAllGeneric<Value>(mValueList, translated.string());
+}
+
+bool TaskCase::registerIndex(const android::String8& name, int value)
+{
+    return registerGeneric<int>(mIndexList, name, value);
+}
+
+bool TaskCase::updateIndex(const android::String8& name, int value)
+{
+    return updateGeneric<int>(mIndexList, name, value);
+}
+
+bool TaskCase::findIndex(const android::String8& name, int& val)
+{
+    return findGeneric<int>(mIndexList, name, val);
+}
+
+std::list<TaskCase::IndexPair>* TaskCase::findAllIndices(const android::String8& re)
+{
+    android::String8 translated;
+    if (!translateVarName(re, translated)) {
+        return NULL;
+    }
+    return findAllGeneric<int>(mIndexList, translated.string());
+}
+
+bool TaskCase::translateVarName(const android::String8& orig, android::String8& translated)
+{
+    const char* src = orig.string();
+    const int nmatch = 2;
+    regmatch_t pmatch[nmatch];
+    regex_t re;
+    size_t strStart = 0;
+
+    if (regcomp(&re, "[a-z0-9_]*[$]([a-z0-9]+)[_]*", REG_EXTENDED) != 0) {
+        LOGE("regcomp failed");
+        return false;
+    }
+    bool result = false;
+    size_t matchStart = 0;
+    size_t matchEnd = 0;
+    while (regexec(&re, src, nmatch, pmatch, 0) == 0) {
+        matchStart = strStart + pmatch[1].rm_so;
+        matchEnd = strStart + pmatch[1].rm_eo;
+        translated.append(StringUtil::substr(orig, strStart, pmatch[1].rm_so - 1)); //-1 for $
+        android::String8 indexName;
+        indexName.append(StringUtil::substr(orig, matchStart, matchEnd - matchStart));
+        int val;
+        if (!findIndex(indexName, val)) {
+            LOGE("TaskCase::translateVarName no index with name %s", indexName.string());
+            regfree(&re);
+            return false;
+        }
+        translated.appendFormat("%d", val);
+        LOGD("match found strStart %d, matchStart %d, matchEnd %d, converted str %s",
+                strStart, matchStart, matchEnd, translated.string());
+        src += pmatch[1].rm_eo;
+        strStart += pmatch[1].rm_eo;
+    }
+    if (matchEnd < orig.length()) {
+        //LOGD("%d %d", matchEnd, orig.length());
+        translated.append(StringUtil::substr(orig, matchEnd, orig.length() - matchEnd));
+    }
+    LOGD("translated str %s to %s", orig.string(), translated.string());
+    regfree(&re);
+    return true;
+}
+
+android::sp<RemoteAudio>& TaskCase::getRemoteAudio()
+{
+    if (mClient == NULL) {
+        mClient = new ClientImpl();
+        ASSERT(mClient->init(Settings::Instance()->getSetting(Settings::EADB)));
+    }
+    return mClient->getAudio();
+}
+
+void TaskCase::releaseRemoteAudio()
+{
+    delete mClient;
+    mClient = NULL;
+}
+
+TaskGeneric::ExecutionResult TaskCase::run()
+{
+    android::String8 name;
+    android::String8 version;
+    //LOGI("str %d, %d", strlen(STR_NAME), strlen(STR_VERSION));
+    if (!findStringAttribute(STR_NAME, name) || !findStringAttribute(STR_VERSION, version)) {
+        LOGW("TaskCase::run no name or version information");
+    }
+    Report::Instance()->printf("== Test case %s version %s started ==", name.string(),
+            version.string());
+    std::list<TaskGeneric*>::iterator i = getChildren().begin();
+    std::list<TaskGeneric*>::iterator end = getChildren().end();
+    TaskGeneric* setup = *i;
+    i++;
+    TaskGeneric* action = *i;
+    i++;
+    TaskGeneric* save = (i == end)? NULL : *i;
+    if (save == NULL) {
+        LOGW("No save stage in test case");
+    }
+    bool testPassed = true;
+    TaskGeneric::ExecutionResult result = setup->run();
+    TaskGeneric::ExecutionResult resultAction(TaskGeneric::EResultOK);
+    if (result != TaskGeneric::EResultOK) {
+        Report::Instance()->printf("== setup stage failed %d ==", result);
+        testPassed = false;
+    } else {
+        resultAction = action->run();
+        if (resultAction != TaskGeneric::EResultPass) {
+            Report::Instance()->printf("== action stage failed %d ==", resultAction);
+            testPassed = false;
+        }
+        // save done even for failure if possible
+        if (save != NULL) {
+            result = save->run();
+        }
+        if (result != TaskGeneric::EResultOK) {
+            Report::Instance()->printf("== save stage failed %d ==", result);
+            testPassed = false;
+        }
+    }
+    if (testPassed) {
+        result = TaskGeneric::EResultPass;
+        Report::Instance()->printf("== Case %s Passed ==", name.string());
+        Report::Instance()->addCasePassed(name);
+    } else {
+        if (resultAction != TaskGeneric::EResultOK) {
+            result = resultAction;
+        }
+        Report::Instance()->printf("== Case %s Failed ==", name.string());
+        Report::Instance()->addCaseFailed(name);
+    }
+    // release remote audio for other cases to use
+    releaseRemoteAudio();
+    return result;
+}
+
+
diff --git a/suite/audio_quality/lib/src/task/TaskGeneric.cpp b/suite/audio_quality/lib/src/task/TaskGeneric.cpp
new file mode 100644
index 0000000..7abd137
--- /dev/null
+++ b/suite/audio_quality/lib/src/task/TaskGeneric.cpp
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2012 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 "Log.h"
+
+#include "task/TaskAll.h"
+
+
+TaskGeneric::TaskGeneric(TaskType type):
+    mType(type),
+    mParent(NULL)
+{
+
+}
+
+bool deleteChildInstance(TaskGeneric* child, void* /*data*/)
+{
+    delete child;
+    return true;
+}
+
+TaskGeneric::~TaskGeneric()
+{
+    forEachChild(deleteChildInstance, NULL);
+    //mChildren.clear();
+}
+
+bool TaskGeneric::addChild(TaskGeneric* child)
+{
+    mChildren.push_back(child);
+    child->setParent(this);
+    return true;
+}
+
+bool TaskGeneric::forEachChild(bool (*runForEachChild)(TaskGeneric* child, void* data), void* data)
+{
+    std::list<TaskGeneric*>::iterator i = mChildren.begin();
+    std::list<TaskGeneric*>::iterator end = mChildren.end();
+    for (; i != end; i++) {
+        if (!(*runForEachChild)(*i, data)) {
+            return false;
+        }
+    }
+    return true;
+}
+
+TaskGeneric* TaskGeneric::getParent()
+{
+    return mParent;
+}
+
+TaskCase* TaskGeneric::getTestCase()
+{
+    TaskGeneric* task = this;
+
+    while (task != NULL) {
+        if (task->getType() == ETaskCase) {
+            // do not use dynamic_cast intentionally
+            return reinterpret_cast<TaskCase*>(task);
+        }
+        task = task->getParent();
+    }
+    LOGE("TaskGeneric::getTestCase no TaskCase found!");
+    return NULL;
+}
+
+void TaskGeneric::setParent(TaskGeneric* parent)
+{
+    LOGD("TaskGeneric::setParent self %x, parent %x", this, parent);
+    mParent = parent;
+}
+
+bool runChild(TaskGeneric* child, void* data)
+{
+    TaskGeneric::ExecutionResult* result = reinterpret_cast<TaskGeneric::ExecutionResult*>(data);
+    *result = child->run();
+    if (*result != TaskGeneric::EResultOK) {
+        LOGE("child type %d returned %d", child->getType(), *result);
+        return false;
+    }
+    return true;
+}
+
+TaskGeneric::ExecutionResult TaskGeneric::run()
+{
+    ExecutionResult result = EResultOK;
+    forEachChild(runChild, &result);
+    return result;
+}
+
+bool TaskGeneric::parseAttribute(const android::String8& name, const android::String8& value)
+{
+    // default implementation only handles registered string attributes
+    if (!addStringAttribute(name, value)) {
+        LOGE("parseAttribute unknown attribute %s %s for type %d",
+                name.string(), value.string(), getType());
+        return false;
+    }
+    return true;
+}
+
+
+void TaskGeneric::registerSupportedStringAttributes(const android::String8* keys[])
+{
+    int i = 0;
+    while (keys[i] != NULL) {
+        mAllowedStringAttributes.insert(*keys[i]);
+        i++;
+    }
+}
+
+bool TaskGeneric::addStringAttribute(const android::String8& key, const android::String8& value)
+{
+    std::set<android::String8, android::String8>::iterator it = mAllowedStringAttributes.find(key);
+    if (it == mAllowedStringAttributes.end()) {
+        return false; // not allowed
+    }
+    mStringAttributes[key] = value;
+    return true;
+}
+
+bool TaskGeneric::findStringAttribute(const android::String8& key, android::String8& value)
+{
+    std::map<android::String8, android::String8>::iterator it = mStringAttributes.find(key);
+    if (it == mStringAttributes.end()) {
+        return false; // not found
+    }
+    value = it->second;
+    return true;
+}
+
diff --git a/suite/audio_quality/lib/src/task/TaskInput.cpp b/suite/audio_quality/lib/src/task/TaskInput.cpp
new file mode 100644
index 0000000..e5b4b06
--- /dev/null
+++ b/suite/audio_quality/lib/src/task/TaskInput.cpp
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2012 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 <stdlib.h>
+
+#include "Log.h"
+#include "audio/AudioHardware.h"
+#include "task/TaskCase.h"
+#include "task/TaskInput.h"
+
+TaskInput::TaskInput()
+    : TaskAsync(TaskGeneric::ETaskInput),
+      mRecordingTimeInMs(0)
+{
+
+}
+
+TaskInput::~TaskInput()
+{
+
+}
+
+bool TaskInput::parseAttribute(const android::String8& name, const android::String8& value)
+{
+    if (strcmp(name, "time") == 0) {
+        mRecordingTimeInMs = atoi(value);
+        if (mRecordingTimeInMs < 0) {
+            LOGE("TaskInput::parseAttribute invalid recording time %d", mRecordingTimeInMs);
+            return false;
+        }
+        return true;
+    }
+    return TaskAsync::parseAttribute(name, value);
+}
+
+TaskGeneric::ExecutionResult TaskInput::start()
+{
+    bool localDevice = (mDeviceType == TaskAsync::EDeviceHost);
+    android::sp<AudioHardware> hw = AudioHardware::createAudioHw(localDevice, false,
+            getTestCase());
+    if (hw.get() == NULL) {
+        LOGE("createAudioHw failed");
+        return TaskGeneric::EResultError;
+    }
+    // TODO support stereo mode in local later
+    //     for now, local is captured in stereo, and it is stored to mono
+    //     by keeping only channel 1.
+    // local : stereo only, remote : mono only
+    size_t bufferSize = mRecordingTimeInMs * AudioHardware::ESampleRate_44100 / 1000 *
+            (localDevice ? 4 : 2);
+    android::sp<Buffer> buffer(new Buffer(bufferSize, bufferSize, localDevice));
+    if (buffer.get() == NULL) {
+        LOGE("buffer alloc failed");
+        return TaskGeneric::EResultError;
+    }
+    if (!hw->prepare(AudioHardware::ESampleRate_44100, mVolume, mMode)) {
+        LOGE("prepare failed");
+        return TaskGeneric::EResultError;
+    }
+    if (!hw->startPlaybackOrRecord(buffer)) {
+        LOGE("record failed");
+        return TaskGeneric::EResultError;
+    }
+    // now store sp
+    mHw = hw;
+    mBuffer = buffer;
+    return TaskGeneric::EResultOK;
+}
+
+TaskGeneric::ExecutionResult TaskInput::complete()
+{
+    bool result = mHw->waitForCompletion();
+    mHw->stopPlaybackOrRecord();
+    mHw.clear();
+    if (!result) {
+        LOGE("waitForComletion failed");
+        return TaskGeneric::EResultError;
+    }
+    // TODO: need to keep stereo for local if in stereo mode
+    // For now, convert to mono if it is stereo
+    if (mBuffer->isStereo()) {
+        mBuffer->changeToMono(Buffer::EKeepCh0);
+    }
+    if (!getTestCase()->registerBuffer(mId, mBuffer)) {
+        if (!getTestCase()->updateBuffer(mId, mBuffer)) {
+            LOGE("cannot register/update buffer %s", mId.string());
+            return TaskGeneric::EResultError;
+        }
+    }
+    return TaskGeneric::EResultOK;
+}
+
+
diff --git a/suite/audio_quality/lib/src/task/TaskMessage.cpp b/suite/audio_quality/lib/src/task/TaskMessage.cpp
new file mode 100644
index 0000000..4241d2e
--- /dev/null
+++ b/suite/audio_quality/lib/src/task/TaskMessage.cpp
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2012 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 "task/TaskMessage.h"
+
+
+TaskMessage::TaskMessage()
+    : TaskGeneric(TaskGeneric::ETaskMessage)
+{}
+TaskMessage::~TaskMessage()
+{
+
+}
+TaskGeneric::ExecutionResult TaskMessage::run()
+{
+    //TODO
+    return TaskGeneric::EResultError;
+}
+bool TaskMessage::parseAttribute(const android::String8& name, const android::String8& value)
+{
+    //TODO
+    return false;
+}
diff --git a/suite/audio_quality/lib/src/task/TaskOutput.cpp b/suite/audio_quality/lib/src/task/TaskOutput.cpp
new file mode 100644
index 0000000..ce3a7d8
--- /dev/null
+++ b/suite/audio_quality/lib/src/task/TaskOutput.cpp
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2012 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 "task/TaskCase.h"
+#include "StringUtil.h"
+#include "task/TaskOutput.h"
+#include "audio/AudioRemote.h"
+#include "audio/RemoteAudio.h"
+
+
+TaskOutput::TaskOutput()
+    : TaskAsync(TaskGeneric::ETaskOutput),
+      mWaitForCompletion(false)
+{
+
+}
+
+TaskOutput::~TaskOutput()
+{
+
+}
+bool TaskOutput::parseAttribute(const android::String8& name, const android::String8& value)
+{
+    if (StringUtil::compare(name, "waitforcompletion") == 0) {
+        if (StringUtil::compare(value, "1") == 0) {
+            mWaitForCompletion = true;
+        }
+        return true;
+    }
+    return TaskAsync::parseAttribute(name, value);
+}
+TaskGeneric::ExecutionResult TaskOutput::start()
+{
+    bool localDevice = (mDeviceType == TaskAsync::EDeviceHost);
+    android::sp<AudioHardware> hw = AudioHardware::createAudioHw(localDevice, true, getTestCase());
+    if (hw.get() == NULL) {
+        LOGE("cannot create Audio HW");
+        return TaskGeneric::EResultError;
+    }
+    if (!hw->prepare(AudioHardware::ESampleRate_44100, mVolume, mMode)) {
+        LOGE("prepare failed");
+        return TaskGeneric::EResultError;
+    }
+    if (localDevice) {
+        android::sp<Buffer> buffer;
+        buffer = getTestCase()->findBuffer(mId);
+        if (buffer.get() == NULL) {
+            LOGE("cannot find buffer %s", mId.string());
+            return TaskGeneric::EResultError;
+        }
+        buffer->restart(); // reset to play from beginning
+
+        if (!hw->startPlaybackOrRecord(buffer)) {
+            LOGE("play failed");
+            return TaskGeneric::EResultError;
+        }
+    } else {
+        int id = getTestCase()->getRemoteAudio()->getDataId(mId);
+        if (id < 0) {
+            return TaskGeneric::EResultError;
+        }
+        AudioRemotePlayback* remote = reinterpret_cast<AudioRemotePlayback*>(hw.get());
+        if (!remote->startPlaybackForRemoteData(id, false)) { // mono always
+            return TaskGeneric::EResultError;
+        }
+    }
+    // now store sp
+    mHw = hw;
+
+    return TaskGeneric::EResultOK;
+}
+
+TaskGeneric::ExecutionResult TaskOutput::complete()
+{
+    bool result = true;
+    if (mWaitForCompletion) {
+        result = mHw->waitForCompletion();
+    }
+    mHw->stopPlaybackOrRecord();
+    mHw.clear();
+    if (!result) {
+        LOGE("waitForCompletion failed");
+        return TaskGeneric::EResultError;
+    }
+    return TaskGeneric::EResultOK;
+}
+
+
diff --git a/suite/audio_quality/lib/src/task/TaskProcess.cpp b/suite/audio_quality/lib/src/task/TaskProcess.cpp
new file mode 100644
index 0000000..f1e47af
--- /dev/null
+++ b/suite/audio_quality/lib/src/task/TaskProcess.cpp
@@ -0,0 +1,363 @@
+/*
+ * Copyright (C) 2012 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 <stdlib.h>
+#include <string.h>
+
+#include <vector>
+
+#include "Log.h"
+#include "StringUtil.h"
+#include "task/TaskProcess.h"
+#include "SignalProcessingImpl.h"
+
+TaskProcess::TaskProcess()
+    : TaskGeneric(TaskGeneric::ETaskProcess)
+{
+
+}
+
+TaskProcess::~TaskProcess()
+{
+}
+
+TaskGeneric::ExecutionResult TaskProcess::run()
+{
+    if (mType == EBuiltin) {
+        return doRun(true);
+    } else {
+        if (mSp.get() == NULL) {
+            mSp.reset(new SignalProcessingImpl());
+            if (!mSp->init(SignalProcessingImpl::MAIN_PROCESSING_SCRIPT)) {
+                mSp.reset(NULL);
+                return TaskGeneric::EResultError;
+            }
+        }
+        return doRun(false);
+    }
+}
+
+// Allocate Buffers and Values to pass to builtin functions
+bool TaskProcess::prepareParams(std::vector<TaskProcess::Param>& list,
+        const bool* paramTypes,
+        UniquePtr<void_ptr, DefaultDelete<void_ptr[]> > & ptrs,
+        UniquePtr<UniqueValue, DefaultDelete<UniqueValue[]> > & values,
+        UniquePtr<UniqueBuffer, DefaultDelete<UniqueBuffer[]> > & buffers,
+        bool isInput)
+{
+    size_t N = list.size();
+
+    LOGD("TaskProcess::prepareParams N = %d", N);
+    ptrs.reset(new void_ptr[N]);
+    if (ptrs.get() == NULL) {
+        LOGE("alloc failed");
+        return false;
+    }
+    // set to NULL to detect illegal access
+    bzero(ptrs.get(), N * sizeof(void_ptr));
+    values.reset(new UniqueValue[N]);
+    if (values.get() == NULL) {
+        LOGE("alloc failed");
+        return false;
+    }
+    buffers.reset(new UniqueBuffer[N]);
+    if (buffers.get() == NULL) {
+        LOGE("alloc failed");
+        return false;
+    }
+
+    void_ptr* voidPtrs = ptrs.get();
+    UniqueValue* valuesPtr = values.get();
+    UniqueBuffer* buffersPtr = buffers.get();
+    for (size_t i = 0; i < N; i++) {
+        if ((paramTypes != NULL) && paramTypes[i] && (list[i].getType() != EId)) {
+            LOGE("mismatching types %d %d", paramTypes[i], list[i].getType());
+            return false;
+        }
+        if ((paramTypes != NULL) && !paramTypes[i] && (list[i].getType() == EId)) {
+            LOGE("mismatching types %d %d", paramTypes[i], list[i].getType());
+            return false;
+        }
+        switch(list[i].getType()) {
+        case EId: {
+            UniquePtr<android::sp<Buffer> > buffer(new android::sp<Buffer>());
+            if (buffer.get() == NULL) {
+                LOGE("alloc failed");
+                return false;
+            }
+            if (isInput) {
+                *(buffer.get()) = getTestCase()->findBuffer(list[i].getParamString());
+                if (buffer.get()->get() == NULL) {
+                    LOGE("find failed");
+                    return false;
+                }
+                LOGD("input buffer len %d stereo %d", (*buffer.get())->getSize(),
+                        (*buffer.get())->isStereo());
+            }
+            buffersPtr[i].reset(buffer.release());
+            voidPtrs[i] = buffersPtr[i].get();
+        }
+        break;
+        case EVal: {
+            valuesPtr[i].reset(new TaskCase::Value());
+            if (isInput) {
+                if (!getTestCase()->findValue(list[i].getParamString(), *(valuesPtr[i].get()))) {
+                    LOGE("find %s failed", list[i].getParamString().string());
+                    return false;
+                }
+            }
+            voidPtrs[i] = valuesPtr[i].get();
+        }
+        break;
+        case EConst: {
+            if (!isInput) {
+                LOGE("const for output");
+                return false;
+            }
+            voidPtrs[i] = list[i].getValuePtr();
+
+            if (list[i].getValue().getType() == TaskCase::Value::ETypeDouble) {
+                LOGD(" %f", list[i].getValue().getDouble());
+            } else {
+                LOGD(" %lld", list[i].getValue().getInt64());
+            }
+        }
+        break;
+        }
+        LOGD("TaskProcess::prepareParams %d-th, const 0x%x", i, voidPtrs[i]);
+    }
+    return true;
+}
+
+// run builtin function by searching BuiltinProcessing::BUINTIN_FN_TABLE
+TaskGeneric::ExecutionResult TaskProcess::doRun(bool builtIn)
+{
+    BuiltinProcessing::BuiltinInfo* info = NULL;
+    if (builtIn) {
+        for (int i = 0; i < BuiltinProcessing::N_BUILTIN_FNS; i++) {
+            if (StringUtil::compare(mName, BuiltinProcessing::BUINTIN_FN_TABLE[i].mName) == 0) {
+                info = &BuiltinProcessing::BUINTIN_FN_TABLE[i];
+                break;
+            }
+        }
+        if (info == NULL) {
+            LOGE("TaskProcess::runBuiltin no match for %s", mName.string());
+            return TaskGeneric::EResultError;
+        }
+        if (mInput.size() != info->mNInput) {
+            LOGE("TaskProcess::runBuiltin size mismatch %d vs %d", mInput.size(), info->mNInput);
+            return TaskGeneric::EResultError;
+        }
+        if (mOutput.size() != info->mNOutput) {
+            LOGE("TaskProcess::runBuiltin size mismatch %d vs %d", mOutput.size(), info->mNOutput);
+            return TaskGeneric::EResultError;
+        }
+    }
+    // This is for passing to builtin fns. Just void pts will be cleared in exit
+    UniquePtr<void_ptr, DefaultDelete<void_ptr[]> > inputs;
+    // This is for holding Value instances. Will be destroyed in exit
+    UniquePtr<UniqueValue, DefaultDelete<UniqueValue[]> > inputValues;
+    // This is for holding android::sp<Buffer>. Buffer itself is from the global map.
+    UniquePtr<UniqueBuffer, DefaultDelete<UniqueBuffer[]> > inputBuffers;
+
+    UniquePtr<void_ptr, DefaultDelete<void_ptr[]> > outputs;
+    // Value is created here. Builtin function just need to set it.
+    UniquePtr<UniqueValue, DefaultDelete<UniqueValue[]> > outputValues;
+    // Buffer itself should be allocated by the builtin function itself.
+    UniquePtr<UniqueBuffer, DefaultDelete<UniqueBuffer[]> > outputBuffers;
+
+    if (!prepareParams(mInput, builtIn ? info->mInputTypes : NULL, inputs, inputValues,
+            inputBuffers, true)) {
+        return TaskGeneric::EResultError;
+    }
+
+    if (!prepareParams(mOutput, builtIn ? info->mOutputTypes : NULL, outputs, outputValues,
+            outputBuffers, false)) {
+        return TaskGeneric::EResultError;
+    }
+
+    TaskGeneric::ExecutionResult result;
+    if (builtIn) {
+        result = (mBuiltin.*(info->mFunction))(inputs.get(), outputs.get());
+    } else {
+        UniquePtr<bool, DefaultDelete<bool[]> > inputTypes(new bool[mInput.size()]);
+        for (size_t i = 0; i < mInput.size(); i++) {
+            (inputTypes.get())[i] = mInput[i].isIdType();
+        }
+        UniquePtr<bool, DefaultDelete<bool[]> > outputTypes(new bool[mOutput.size()]);
+        for (size_t i = 0; i < mOutput.size(); i++) {
+            (outputTypes.get())[i] = mOutput[i].isIdType();
+        }
+        result = mSp->run( mName,
+                mInput.size(), inputTypes.get(), inputs.get(),
+                mOutput.size(), outputTypes.get(), outputs.get());
+    }
+    if ((result == TaskGeneric::EResultOK) || (result == TaskGeneric::EResultFail)
+            || (result == TaskGeneric::EResultPass)) {
+        // try to save result
+        bool saveResultFailed = false;
+        for (size_t i = 0; i < mOutput.size(); i++) {
+            if (mOutput[i].isIdType()) { // Buffer
+                android::sp<Buffer>* bufferp =
+                        reinterpret_cast<android::sp<Buffer>*>((outputs.get())[i]);
+                if (!getTestCase()->registerBuffer(mOutput[i].getParamString(), *bufferp)) {
+                    // maybe already there, try update
+                    if (!getTestCase()->updateBuffer(mOutput[i].getParamString(), *bufferp)) {
+                        LOGE("cannot register / update %d-th output Buffer for builtin fn %s",
+                                i, mName.string());
+                        saveResultFailed = true; // mark failure, but continue
+                    }
+                }
+            } else { // Value
+                TaskCase::Value* valuep =
+                        reinterpret_cast<TaskCase::Value*>((outputs.get())[i]);
+                if (!getTestCase()->registerValue(mOutput[i].getParamString(), *valuep)) {
+                    if (!getTestCase()->updateValue(mOutput[i].getParamString(), *valuep)) {
+                        LOGE("cannot register / update %d-th output Value for builtin fn %s",
+                                i, mName.string());
+                        saveResultFailed = true; // mark failure, but continue
+                    }
+                }
+            }
+        }
+        if (saveResultFailed) {
+            LOGE("TaskProcess::runBuiltin cannot save result");
+            return TaskGeneric::EResultError;
+        }
+    }
+    LOGV("TaskProcess::runBuiltin return %d", result);
+    return result;
+}
+
+bool TaskProcess::parseParams(std::vector<TaskProcess::Param>& list, const char* str, bool isInput)
+{
+    LOGV("TaskProcess::parseParams will parse %s", str);
+    android::String8 paramStr(str);
+    UniquePtr<std::vector<android::String8> > paramTokens(StringUtil::split(paramStr, ','));
+    if (paramTokens.get() == NULL) {
+        LOGE("split failed");
+        return false;
+    }
+    std::vector<android::String8>& tokens = *(paramTokens.get());
+    for (size_t i = 0; i < tokens.size(); i++) {
+        UniquePtr<std::vector<android::String8> > itemTokens(StringUtil::split(tokens[i], ':'));
+        if (itemTokens.get() == NULL) {
+            LOGE("split failed");
+            return false;
+        }
+        if (itemTokens->size() != 2) {
+            LOGE("size mismatch %d", itemTokens->size());
+            return false;
+        }
+        std::vector<android::String8>& item = *(itemTokens.get());
+        if (StringUtil::compare(item[0], "id") == 0) {
+            Param param(EId, item[1]);
+            list.push_back(param);
+            LOGD(" id %s", param.getParamString().string());
+        } else if (StringUtil::compare(item[0], "val") == 0) {
+            Param param(EVal, item[1]);
+            list.push_back(param);
+            LOGD(" val %s", param.getParamString().string());
+        } else if (isInput && (StringUtil::compare(item[0], "consti") == 0)) {
+            long long value = atoll(item[1].string());
+            TaskCase::Value v(value);
+            Param param(v);
+            list.push_back(param);
+            LOGD("consti %lld", value);
+        } else if (isInput && (StringUtil::compare(item[0], "constf") == 0)) {
+            double value = atof(item[1].string());
+            TaskCase::Value v(value);
+            Param param(v);
+            list.push_back(param);
+            LOGD("constf %f", value);
+        } else {
+            LOGE("unrecognized word %s", item[0].string());
+            return false;
+        }
+        LOGV("TaskProcess::parseParams %d-th type %d", i, list[i].getType());
+    }
+   return true;
+}
+
+bool TaskProcess::parseAttribute(const android::String8& name, const android::String8& value)
+{
+    if (StringUtil::compare(name, "method") == 0) {
+        UniquePtr<std::vector<android::String8> > tokenPtr(StringUtil::split(value, ':'));
+        std::vector<android::String8>* tokens = tokenPtr.get();
+        if (tokens == NULL) {
+            LOGE("split failed");
+            return false;
+        }
+        if (tokens->size() != 2) {
+            LOGE("cannot parse attr %s %s", name.string(), value.string());
+            return false;
+        }
+        if (StringUtil::compare(tokens->at(0), "builtin") == 0) {
+            mType = EBuiltin;
+        } else if (StringUtil::compare(tokens->at(0), "script") == 0) {
+            mType = EScript;
+        } else {
+            LOGE("cannot parse attr %s %s", name.string(), value.string());
+            return false;
+        }
+        mName.append(tokens->at(1));
+        return true;
+    } else if (StringUtil::compare(name, "input") == 0) {
+        return parseParams(mInput, value, true);
+    } else if (StringUtil::compare(name, "output") == 0) {
+        return parseParams(mOutput, value, false);
+    } else {
+        LOGE("cannot parse attr %s %s", name.string(), value.string());
+        return false;
+    }
+}
+
+TaskProcess::Param::Param(TaskProcess::ParamType type, android::String8& string)
+    : mType(type),
+      mString(string)
+{
+    ASSERT((type == TaskProcess::EId) || (type == TaskProcess::EVal));
+
+}
+
+TaskProcess::Param::Param(TaskCase::Value& val)
+    : mType(TaskProcess::EConst),
+      mValue(val)
+{
+
+}
+
+TaskProcess::ParamType TaskProcess::Param::getType()
+{
+    return mType;
+}
+
+android::String8& TaskProcess::Param::getParamString()
+{
+    ASSERT((mType == TaskProcess::EId) || (mType == TaskProcess::EVal));
+    return mString;
+}
+
+TaskCase::Value& TaskProcess::Param::getValue()
+{
+    ASSERT(mType == TaskProcess::EConst);
+    return mValue;
+}
+
+TaskCase::Value* TaskProcess::Param::getValuePtr()
+{
+    ASSERT(mType == TaskProcess::EConst);
+    return &mValue;
+}
diff --git a/suite/audio_quality/lib/src/task/TaskSave.cpp b/suite/audio_quality/lib/src/task/TaskSave.cpp
new file mode 100644
index 0000000..d62b846
--- /dev/null
+++ b/suite/audio_quality/lib/src/task/TaskSave.cpp
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2012 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 <sys/stat.h>
+#include <sys/types.h>
+#include <errno.h>
+
+#include <UniquePtr.h>
+
+#include "Log.h"
+#include "FileUtil.h"
+#include "Report.h"
+#include "StringUtil.h"
+#include "task/TaskCase.h"
+#include "task/TaskGeneric.h"
+#include "task/TaskSave.h"
+
+static const android::String8 STR_FILE("file");
+static const android::String8 STR_REPORT("report");
+
+TaskSave::TaskSave()
+    : TaskGeneric(TaskGeneric::ETaskSave)
+{
+    const android::String8* list[] = {&STR_FILE, &STR_REPORT, NULL};
+    registerSupportedStringAttributes(list);
+}
+
+TaskSave::~TaskSave()
+{
+
+}
+
+bool TaskSave::handleFile()
+{
+    android::String8 fileValue;
+    if (!findStringAttribute(STR_FILE, fileValue)) {
+        LOGI("no saving to file");
+        return true; // true as there is no need to save
+    }
+
+    UniquePtr<std::vector<android::String8> > list(StringUtil::split(fileValue, ','));
+    std::vector<android::String8>* listp = list.get();
+    if (listp == NULL) {
+        LOGE("alloc failed");
+        return false;
+    }
+
+    android::String8 dirName;
+    if (!FileUtil::prepare(dirName)) {
+        LOGE("cannot prepare report dir");
+        return false;
+    }
+    android::String8 caseName;
+    if (!getTestCase()->getCaseName(caseName)) {
+        return false;
+    }
+    dirName.appendPath(caseName);
+    int result = mkdir(dirName.string(), S_IRWXU);
+    if ((result == -1) && (errno != EEXIST)) {
+        LOGE("mkdir of save dir %s failed, error %d", dirName.string(), errno);
+        return false;
+    }
+
+    for (size_t i = 0; i < listp->size(); i++) {
+        UniquePtr<std::list<TaskCase::BufferPair> > buffers(
+                getTestCase()->findAllBuffers((*listp)[i]));
+        std::list<TaskCase::BufferPair>* buffersp = buffers.get();
+        if (buffersp == NULL) {
+            LOGE("no buffer for given pattern %s", ((*listp)[i]).string());
+            return false;
+        }
+        std::list<TaskCase::BufferPair>::iterator it = buffersp->begin();
+        std::list<TaskCase::BufferPair>::iterator end = buffersp->end();
+        for (; it != end; it++) {
+            android::String8 fileName(dirName);
+            fileName.appendPath(it->first);
+            if (!it->second->saveToFile(fileName)) {
+                LOGE("save failed");
+                return false;
+            }
+        }
+    }
+    return true;
+}
+
+bool TaskSave::handleReport()
+{
+    android::String8 reportValue;
+    if (!findStringAttribute(STR_REPORT, reportValue)) {
+        LOGI("no saving to report");
+        return true; // true as there is no need to save
+    }
+
+    UniquePtr<std::vector<android::String8> > list(StringUtil::split(reportValue, ','));
+    std::vector<android::String8>* listp = list.get();
+    if (listp == NULL) {
+        LOGE("alloc failed");
+        return false;
+    }
+    Report::Instance()->printf("=== Values stored ===");
+    for (size_t i = 0; i < listp->size(); i++) {
+        UniquePtr<std::list<TaskCase::ValuePair> > values(
+                getTestCase()->findAllValues((*listp)[i]));
+        std::list<TaskCase::ValuePair>* valuesp = values.get();
+        if (valuesp == NULL) {
+            LOGE("no value for given pattern %s", ((*listp)[i]).string());
+            return false;
+        }
+        std::list<TaskCase::ValuePair>::iterator it = values->begin();
+        std::list<TaskCase::ValuePair>::iterator end = values->end();
+        for (; it != end; it++) {
+            if (it->second.getType() == TaskCase::Value::ETypeDouble) {
+                Report::Instance()->printf("   %s: %f", it->first.string(),
+                        it->second.getDouble());
+            } else { //64bit int
+                Report::Instance()->printf("   %s: %lld", it->first.string(),
+                        it->second.getInt64());
+            }
+        }
+    }
+    return true;
+}
+
+TaskGeneric::ExecutionResult TaskSave::run()
+{
+    bool failed = false;
+    if (!handleFile()) {
+        failed = true;
+    }
+    if (!handleReport()) {
+        failed = true;
+    }
+    if (failed) {
+        return TaskGeneric::EResultError;
+    } else {
+        return TaskGeneric::EResultOK;
+    }
+}
+
+
diff --git a/suite/audio_quality/lib/src/task/TaskSequential.cpp b/suite/audio_quality/lib/src/task/TaskSequential.cpp
new file mode 100644
index 0000000..8c60cb7f
--- /dev/null
+++ b/suite/audio_quality/lib/src/task/TaskSequential.cpp
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2012 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 <stdlib.h>
+#include <algorithm>
+#include "Log.h"
+#include "StringUtil.h"
+#include "task/TaskSequential.h"
+#include "task/TaskCase.h"
+#include "task/TaskAsync.h"
+
+TaskSequential::TaskSequential()
+    : TaskGeneric(TaskGeneric::ETaskSequential),
+      mRepeatCount(1),
+      mRepeatIndex(-1)
+{
+
+}
+
+TaskSequential::~TaskSequential()
+{
+
+}
+
+
+TaskGeneric::ExecutionResult TaskSequential::run()
+{
+    mRepeatIndex = -1;
+    bool storeIndex = (mIndexName.length() == 0 ? false: true);
+    if (storeIndex && !getTestCase()->registerIndex(mIndexName, mRepeatIndex)) {
+        if (!getTestCase()->updateIndex(mIndexName, mRepeatIndex)) {
+            LOGE("register/update of index %s failed", mIndexName.string());
+            return TaskGeneric::EResultError;
+        }
+    }
+
+    TaskGeneric::ExecutionResult firstError(TaskGeneric::EResultOK);
+
+    for (mRepeatIndex = 0; mRepeatIndex < mRepeatCount; mRepeatIndex++) {
+        LOGI("  TaskSequential index %s loop %d-th", mIndexName.string(), mRepeatIndex);
+        if (storeIndex && !getTestCase()->updateIndex(mIndexName, mRepeatIndex)) {
+            return TaskGeneric::EResultError;
+        }
+        std::list<TaskGeneric*>::iterator i = getChildren().begin();
+        std::list<TaskGeneric*>::iterator end = getChildren().end();
+        for (; i != end; i++) {
+            TaskGeneric* child = *i;
+            TaskGeneric::ExecutionResult result = child->run();
+            if ((result != TaskGeneric::EResultOK) && (firstError == TaskGeneric::EResultOK)) {
+                firstError = result;
+                break;
+            }
+        }
+        TaskGeneric::ExecutionResult result = runAsyncTasksQueued();
+        if ((result != TaskGeneric::EResultOK) && (firstError == TaskGeneric::EResultOK)) {
+                    firstError = result;
+        }
+        switch (firstError) {
+        case TaskGeneric::EResultOK:
+        case TaskGeneric::EResultContinue:
+            // continue at the last index should be treated as OK
+            firstError = TaskGeneric::EResultOK;
+            break; // continue for loop
+        case TaskGeneric:: EResultBreakOneLoop:
+            return TaskGeneric::EResultOK;
+        case TaskGeneric::EResultError:
+        case TaskGeneric::EResultFail:
+        case TaskGeneric::EResultPass:
+            mRepeatIndex = mRepeatCount; //exit for loop
+            break;
+        }
+    }
+    // update to the loop exit value
+    if (storeIndex && !getTestCase()->updateIndex(mIndexName, mRepeatIndex)) {
+        return TaskGeneric::EResultError;
+    }
+    return firstError;
+}
+
+bool TaskSequential::queueAsyncTask(TaskAsync* task)
+{
+    std::list<TaskAsync*>::iterator it;
+    it = std::find(mAsyncTasks.begin(), mAsyncTasks.end(), task);
+    if (it != mAsyncTasks.end()) { // already queued
+        return true;
+    }
+    mAsyncTasks.push_back(task);
+    return true;
+}
+
+TaskGeneric::ExecutionResult TaskSequential::runAsyncTasksQueued()
+{
+    std::list<TaskAsync*>::iterator i = mAsyncTasks.begin();
+    std::list<TaskAsync*>::iterator end = mAsyncTasks.end();
+    TaskGeneric::ExecutionResult firstError(TaskGeneric::EResultOK);
+
+    for (; i != end; i++) {
+        TaskAsync* child = *i;
+        TaskGeneric::ExecutionResult result = child->complete();
+        if ((result != TaskGeneric::EResultOK) && (firstError == TaskGeneric::EResultOK)) {
+            firstError = result;
+        }
+    }
+    mAsyncTasks.clear();
+    return firstError;
+}
+
+
+bool TaskSequential::parseAttribute(const android::String8& name, const android::String8& value)
+{
+    if (StringUtil::compare(name, "repeat") == 0) {
+        mRepeatCount = atoi(value.string());
+        if (mRepeatCount <= 0) {
+            LOGE("TaskSequential::parseAttribute invalid value %s for key %s",
+                    value.string(), name.string());
+            return false;
+        }
+        return true;
+    } else if (StringUtil::compare(name, "index") == 0) {
+        mIndexName.append(value);
+        LOGD("TaskSequential::parseAttribute index %s", mIndexName.string());
+        return true;
+    } else {
+        return false;
+    }
+}
diff --git a/suite/audio_quality/lib/src/task/TaskSound.cpp b/suite/audio_quality/lib/src/task/TaskSound.cpp
new file mode 100644
index 0000000..f530203
--- /dev/null
+++ b/suite/audio_quality/lib/src/task/TaskSound.cpp
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2012 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 <UniquePtr.h>
+#include "Log.h"
+#include "audio/AudioSignalFactory.h"
+#include "audio/RemoteAudio.h"
+#include "StringUtil.h"
+#include "task/TaskCase.h"
+#include "task/TaskSound.h"
+
+static const android::String8 STR_ID("id");
+static const android::String8 STR_TYPE("type");
+
+TaskSound::TaskSound()
+    : TaskGeneric(TaskGeneric::ETaskSound),
+      mPreload(false)
+{
+    const android::String8* list[] = {&STR_ID, &STR_TYPE, NULL};
+    registerSupportedStringAttributes(list);
+}
+
+TaskSound::~TaskSound()
+{
+
+}
+
+bool TaskSound::parseAttribute(const android::String8& name, const android::String8& value)
+{
+    if (StringUtil::compare(name, "preload") == 0) {
+            if (StringUtil::compare(value, "1") == 0) {
+                mPreload = true;
+            }
+            return true;
+    }
+    return TaskGeneric::parseAttribute(name, value);
+}
+
+TaskGeneric::ExecutionResult TaskSound::run()
+{
+    //TODO : needs to support data generated from process
+    android::String8 id;
+    if (!findStringAttribute(STR_ID, id)) {
+        LOGE("TaskSound::run %s string not found", STR_ID.string());
+        return TaskGeneric::EResultError;
+    }
+    android::String8 type;
+    if (!findStringAttribute(STR_TYPE, type)) {
+        LOGE("TaskSound::run %s string not found", STR_TYPE.string());
+        return TaskGeneric::EResultError;
+    }
+    UniquePtr<std::vector<android::String8> > tokens(StringUtil::split(type, ':'));
+    if (tokens.get() == NULL) {
+        LOGE("alloc failed");
+        return TaskGeneric::EResultError;
+    }
+    android::sp<Buffer> buffer;
+    if (StringUtil::compare(tokens->at(0), "file") == 0) {
+        if (tokens->size() != 2) {
+            LOGE("Wrong number of parameters %d", tokens->size());
+        }
+        buffer = Buffer::loadFromFile(tokens->at(1));
+    } else if (StringUtil::compare(tokens->at(0), "sin") == 0) {
+        if (tokens->size() != 4) {
+            LOGE("Wrong number of parameters %d", tokens->size());
+        }
+        int amplitude = atoi(tokens->at(1).string());
+        int freq = atoi(tokens->at(2).string());
+        int time = atoi(tokens->at(3).string());
+        int samples = time * AudioHardware::ESampleRate_44100 / 1000;
+        buffer = AudioSignalFactory::generateSineWave(AudioHardware::E2BPS, amplitude,
+                AudioHardware::ESampleRate_44100, freq, samples, true);
+    } else if (StringUtil::compare(tokens->at(0), "random") == 0) {
+        if (tokens->size() != 3) {
+            LOGE("Wrong number of parameters %d", tokens->size());
+        }
+        int amplitude = atoi(tokens->at(1).string());
+        int time = atoi(tokens->at(2).string());
+        int samples = time * AudioHardware::ESampleRate_44100 / 1000;
+        buffer = AudioSignalFactory::generateWhiteNoise(AudioHardware::E2BPS, amplitude,
+                samples, true);
+    } else { // unknown word
+        LOGE("TaskSound::run unknown word in type %s", type.string());
+        // next buffer check will return
+    }
+
+    if (buffer.get() == NULL) {
+        return TaskGeneric::EResultError;
+    }
+    if (!getTestCase()->registerBuffer(id, buffer)) {
+        LOGE("TaskSound::run registering buffer %s failed", id.string());
+        return TaskGeneric::EResultError;
+    }
+    if (mPreload) {
+        int downloadId;
+        if (!getTestCase()->getRemoteAudio()->downloadData(id, buffer, downloadId)) {
+            return TaskGeneric::EResultError;
+        }
+        LOGI("Downloaded buffer %s to DUT with id %d", id.string(), downloadId);
+    }
+    return TaskGeneric::EResultOK;
+}
+
+
+
diff --git a/suite/audio_quality/test/Android.mk b/suite/audio_quality/test/Android.mk
new file mode 100644
index 0000000..9e32557
--- /dev/null
+++ b/suite/audio_quality/test/Android.mk
@@ -0,0 +1,41 @@
+#
+# Copyright (C) 2012 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.
+#
+
+# build only for linux
+ifeq ($(HOST_OS),linux)
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+LOCAL_CPP_EXTENSION := .cpp
+LOCAL_MODULE_TAGS := optional
+LOCAL_SRC_FILES := $(patsubst ./%,%, $(shell cd $(LOCAL_PATH); \
+  find . -name "*.cpp" -and -not -name ".*"))
+#LOCAL_SRC_FILES := 	AudioRecordPlayTest.cpp
+
+#$(info $(LOCAL_SRC_FILES))
+LOCAL_C_INCLUDES += $(LOCAL_PATH)/../lib/include $(LOCAL_PATH)/../lib/src external/gtest/include \
+    external/tinyalsa/include/  libcore/include
+LOCAL_STATIC_LIBRARIES := libutils libgtest_host libgtest_main_host  liblog libcutils libtinyalsa \
+    libtinyxml
+# need to keep everything in libcts_.. Otherwise, linker will drop some
+# functions and linker error happens
+LOCAL_WHOLE_STATIC_LIBRARIES := libcts_audio_quality
+LOCAL_CFLAGS:= -g -fno-exceptions
+LOCAL_LDFLAGS:= -g -lrt -ldl -lstdc++ -lm -fno-exceptions
+LOCAL_MODULE:= cts_audio_quality_test
+include $(BUILD_HOST_EXECUTABLE)
+
+endif # linux
diff --git a/suite/audio_quality/test/AudioHardwareTest.cpp b/suite/audio_quality/test/AudioHardwareTest.cpp
new file mode 100644
index 0000000..35d750b
--- /dev/null
+++ b/suite/audio_quality/test/AudioHardwareTest.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2012 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 <gtest/gtest.h>
+#include <audio/AudioHardware.h>
+#include <task/TaskAll.h>
+
+
+class AudioHardwareTest : public testing::Test {
+
+};
+
+TEST_F(AudioHardwareTest, DetectTest) {
+    int hwId = AudioHardware::detectAudioHw();
+    ASSERT_TRUE(hwId >= 0);
+}
+
+TEST_F(AudioHardwareTest, LocalFactoryTest) {
+    android::sp<AudioHardware> playback = AudioHardware::createAudioHw(true, true);
+    ASSERT_TRUE(playback.get() != NULL);
+    android::sp<AudioHardware> recording = AudioHardware::createAudioHw(true, false);
+    ASSERT_TRUE(recording.get() != NULL);
+}
+
+TEST_F(AudioHardwareTest, RemoteFactoryTest) {
+    TaskCase* testCase = new TaskCase();
+    ASSERT_TRUE(testCase != NULL);
+    android::sp<AudioHardware> playback = AudioHardware::createAudioHw(false, true, testCase);
+    ASSERT_TRUE(playback.get() != NULL);
+    android::sp<AudioHardware> recording = AudioHardware::createAudioHw(false, false, testCase);
+    ASSERT_TRUE(recording.get() != NULL);
+    delete testCase;
+}
diff --git a/suite/audio_quality/test/AudioLocalTest.cpp b/suite/audio_quality/test/AudioLocalTest.cpp
new file mode 100644
index 0000000..b38c44d
--- /dev/null
+++ b/suite/audio_quality/test/AudioLocalTest.cpp
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2012 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 <unistd.h>
+
+#include <gtest/gtest.h>
+#include <utils/threads.h>
+#include <utils/StrongPointer.h>
+
+#include <audio/AudioLocal.h>
+#include <audio/Buffer.h>
+#include <Log.h>
+
+#include "AudioPlayTestCommon.h"
+
+class AudioPlayerDummy: public AudioLocal {
+public:
+    AudioHardware::SamplingRate mSamplingRate;
+    android::sp<Buffer> mBufferPassed;
+    bool mPrepareCalled;
+    bool mdoStartPlaybackOrRecordCalled;
+    bool mdoContinuePlaybackOrRecordCalled;
+    bool mdoStopCalled;
+    int mPlaybackUnit;
+
+    AudioPlayerDummy()
+        : mSamplingRate(AudioHardware::ESamplingRateInvald),
+          mPrepareCalled(false),
+          mdoStartPlaybackOrRecordCalled(false),
+          mdoContinuePlaybackOrRecordCalled(false),
+          mdoStopCalled(false)
+    {
+
+    }
+
+    virtual bool doPrepare(AudioHardware::SamplingRate samplingRate, int samplesInOneGo) {
+        mPlaybackUnit = samplesInOneGo * 4;
+        LOGV("doPrepare");
+        return true;
+    };
+
+    virtual bool doPlaybackOrRecord(android::sp<Buffer>& buffer) {
+        buffer->increaseHandled(mPlaybackUnit);
+        return true;
+    };
+
+    virtual void doStop() {
+        LOGV("doStop");
+    };
+
+
+};
+
+class AudioLocalTest : public AudioPlayTestCommon {
+public:
+    virtual ~AudioLocalTest() {};
+
+protected:
+    android::sp<AudioHardware> createAudioHw() {
+        android::sp<AudioHardware> hw(new AudioPlayerDummy());
+        return hw;
+    }
+};
+
+TEST_F(AudioLocalTest, PlayAllTest) {
+    playAll(1);
+}
+
+TEST_F(AudioLocalTest, PlayAllRepeatTest) {
+    playAll(4);
+}
+
+TEST_F(AudioLocalTest, StartStopTest) {
+    repeatPlayStop();
+}
+
+TEST_F(AudioLocalTest, WrongUsageTest) {
+    playWrongUsage();
+}
+
diff --git a/suite/audio_quality/test/AudioPlayTestCommon.h b/suite/audio_quality/test/AudioPlayTestCommon.h
new file mode 100644
index 0000000..96f129c
--- /dev/null
+++ b/suite/audio_quality/test/AudioPlayTestCommon.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+#ifndef CTSAUDIO_AUDIOPLAYTESTCOMMON_H
+#define CTSAUDIO_AUDIOPLAYTESTCOMMON_H
+
+#include <gtest/gtest.h>
+#include <utils/threads.h>
+#include <utils/StrongPointer.h>
+
+#include <audio/AudioHardware.h>
+#include <audio/AudioPlaybackLocal.h>
+#include <audio/AudioRecordingLocal.h>
+#include <audio/AudioSignalFactory.h>
+#include <audio/AudioLocal.h>
+#include <audio/Buffer.h>
+
+
+#include <Log.h>
+
+
+class AudioPlayTestCommon : public testing::Test {
+protected:
+    android::sp<Buffer> mBuffer;
+    android::sp<AudioHardware> mAudioHw;
+
+    static const int MAX_POSITIVE_AMPLITUDE = 1000;
+    static const int SIGNAL_FREQ = 1000;
+    static const int SIGNAL_LENGTH = AudioHardware::SAMPLES_PER_ONE_GO * 2;
+    static const int DEFAULT_VOLUME = 10;
+
+protected:
+    virtual ~AudioPlayTestCommon() {
+        LOGV("~AudioPlayTestCommon");
+    }
+    virtual void SetUp() {
+        mAudioHw = createAudioHw();
+        ASSERT_TRUE(mAudioHw.get() != NULL);
+        mBuffer = AudioSignalFactory::generateSineWave(AudioHardware::E2BPS,
+                MAX_POSITIVE_AMPLITUDE, AudioHardware::ESampleRate_44100,
+                SIGNAL_FREQ, SIGNAL_LENGTH);
+        ASSERT_TRUE(mBuffer.get() != NULL);
+    }
+
+    virtual void TearDown() {
+        LOGV("AudioPlayTestCommon::TearDown");
+        mAudioHw->stopPlaybackOrRecord(); // this stops the thread
+        mAudioHw.clear();
+    }
+
+    void playAll(int numberRepetition) {
+        ASSERT_TRUE(mAudioHw->prepare(AudioHardware::ESampleRate_44100, DEFAULT_VOLUME));
+        ASSERT_TRUE(mAudioHw->startPlaybackOrRecord(mBuffer, numberRepetition));
+        ASSERT_TRUE(mAudioHw->waitForCompletion());
+        mAudioHw->stopPlaybackOrRecord();
+        LOGV("size %d, handled %d", mBuffer->getSize(), mBuffer->amountHandled());
+        ASSERT_TRUE(mBuffer->amountHandled() == mBuffer->getSize());
+    }
+
+    void repeatPlayStop() {
+        for (int i = 0; i < 2; i++) {
+            ASSERT_TRUE(mAudioHw->prepare(AudioHardware::ESampleRate_44100, DEFAULT_VOLUME));
+            mBuffer->restart();
+            ASSERT_TRUE(mAudioHw->startPlaybackOrRecord(mBuffer, 10));
+            mAudioHw->stopPlaybackOrRecord();
+        }
+    }
+
+    void playWrongUsage() {
+        ASSERT_FALSE(mAudioHw->startPlaybackOrRecord(mBuffer));
+        ASSERT_TRUE(mAudioHw->prepare(AudioHardware::ESampleRate_44100, DEFAULT_VOLUME));
+        playAll(1);
+    }
+
+    virtual android::sp<AudioHardware> createAudioHw() = 0;
+};
+
+#endif // CTSAUDIO_AUDIOPLAYTESTCOMMON_H
diff --git a/suite/audio_quality/test/AudioPlaybackLocalTest.cpp b/suite/audio_quality/test/AudioPlaybackLocalTest.cpp
new file mode 100644
index 0000000..896d01c
--- /dev/null
+++ b/suite/audio_quality/test/AudioPlaybackLocalTest.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2012 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 <unistd.h>
+
+#include <gtest/gtest.h>
+#include <utils/threads.h>
+#include <utils/StrongPointer.h>
+
+#include <audio/AudioHardware.h>
+#include <GenericFactory.h>
+#include <audio/AudioPlaybackLocal.h>
+
+#include <Log.h>
+
+#include "AudioPlayTestCommon.h"
+
+class AudioPlaybackLocalTest : public AudioPlayTestCommon {
+public:
+    virtual ~AudioPlaybackLocalTest() {};
+protected:
+
+    android::sp<AudioHardware> createAudioHw() {
+        return AudioHardware::createAudioHw(true, true);
+    }
+};
+
+
+TEST_F(AudioPlaybackLocalTest, PlayAllTest) {
+    playAll(1);
+}
+
+TEST_F(AudioPlaybackLocalTest, PlayAllRepeatTest) {
+    playAll(4);
+}
+
+TEST_F(AudioPlaybackLocalTest, StartStopTest) {
+    repeatPlayStop();
+}
+
+TEST_F(AudioPlaybackLocalTest, WrongUsageTest) {
+    playWrongUsage();
+}
+
+
diff --git a/suite/audio_quality/test/AudioRecordPlayLocalTest.cpp b/suite/audio_quality/test/AudioRecordPlayLocalTest.cpp
new file mode 100644
index 0000000..3a94d06
--- /dev/null
+++ b/suite/audio_quality/test/AudioRecordPlayLocalTest.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2012 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 "AudioRecordPlayTestCommon.h"
+
+#include <Log.h>
+
+class AudioRecordPlayLocalTest: public AudioRecordPlayTestCommon {
+public:
+    virtual ~AudioRecordPlayLocalTest() {};
+protected:
+    android::sp<AudioHardware> createRecordingHw() {
+        return AudioHardware::createAudioHw(true, false);
+    };
+
+    android::sp<AudioHardware> createPlaybackHw() {
+        return AudioHardware::createAudioHw(true, true);
+    }
+};
+
+
+
+TEST_F(AudioRecordPlayLocalTest, PlayAndRecordTest) {
+    PlayAndRecord(4);
+}
+
+
diff --git a/suite/audio_quality/test/AudioRecordPlayTestCommon.h b/suite/audio_quality/test/AudioRecordPlayTestCommon.h
new file mode 100644
index 0000000..28bc0a6
--- /dev/null
+++ b/suite/audio_quality/test/AudioRecordPlayTestCommon.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+
+#ifndef CTSAUDIO_AUDIORECORDPLAYTESTCOMMON_H
+#define CTSAUDIO_AUDIORECORDPLAYTESTCOMMON_H
+
+#include <gtest/gtest.h>
+#include <utils/threads.h>
+#include <utils/StrongPointer.h>
+
+#include <audio/AudioHardware.h>
+#include <audio/AudioPlaybackLocal.h>
+#include <audio/AudioRecordingLocal.h>
+#include <audio/AudioSignalFactory.h>
+#include <audio/AudioLocal.h>
+#include <audio/Buffer.h>
+#include <Log.h>
+
+class AudioRecordPlayTestCommon : public testing::Test {
+protected:
+    android::sp<Buffer> mBufferRecording;
+    android::sp<Buffer> mBufferPlayback;
+    android::sp<AudioHardware> mAudioRecordingHw;
+    android::sp<AudioHardware> mAudioPlaybackHw;
+
+    static const int MAX_POSITIVE_AMPLITUDE = 10000;
+    static const int SIGNAL_FREQ = 1000;
+    static const int NUMBER_SAMPLES = AudioHardware::SAMPLES_PER_ONE_GO * 4;
+    static const int DEFAULT_VOLUME = 10;
+protected:
+    virtual void SetUp() {
+        mAudioPlaybackHw = createPlaybackHw();
+        ASSERT_TRUE(mAudioPlaybackHw.get() != NULL);
+        mAudioRecordingHw = createRecordingHw();
+        ASSERT_TRUE(mAudioRecordingHw.get() != NULL);
+        mBufferPlayback = AudioSignalFactory::generateSineWave(AudioHardware::E2BPS,
+                MAX_POSITIVE_AMPLITUDE, AudioHardware::ESampleRate_44100,
+                SIGNAL_FREQ, NUMBER_SAMPLES);
+        ASSERT_TRUE(mBufferPlayback.get() != NULL);
+        mBufferRecording = new Buffer(NUMBER_SAMPLES * 4, NUMBER_SAMPLES * 4);
+        ASSERT_TRUE(mBufferRecording.get() != NULL);
+    }
+
+    virtual void TearDown() {
+        mAudioRecordingHw->stopPlaybackOrRecord();
+        mAudioPlaybackHw->stopPlaybackOrRecord();
+        mAudioRecordingHw.clear();
+        mAudioPlaybackHw.clear();
+    }
+
+    void PlayAndRecord(int numberRepetition) {
+        ASSERT_TRUE(mAudioPlaybackHw->prepare(AudioHardware::ESampleRate_44100, DEFAULT_VOLUME));
+        ASSERT_TRUE(mAudioRecordingHw->prepare(AudioHardware::ESampleRate_44100, DEFAULT_VOLUME));
+        ASSERT_TRUE(mAudioRecordingHw->startPlaybackOrRecord(mBufferRecording,
+                numberRepetition));
+        ASSERT_TRUE(mAudioPlaybackHw->startPlaybackOrRecord(mBufferPlayback,
+                numberRepetition));
+
+        ASSERT_TRUE(mAudioRecordingHw->waitForCompletion());
+        ASSERT_TRUE(mAudioPlaybackHw->waitForCompletion());
+        mAudioPlaybackHw->stopPlaybackOrRecord();
+        mAudioRecordingHw->stopPlaybackOrRecord();
+        LOGV("Audio playback buffer size %d, handled %d", mBufferPlayback->getSize(),
+                mBufferPlayback->amountHandled());
+        ASSERT_TRUE(mBufferPlayback->amountHandled() == mBufferPlayback->getSize());
+        LOGV("Audio recording buffer size %d, handled %d", mBufferRecording->getSize(),
+                mBufferRecording->amountHandled());
+        ASSERT_TRUE(mBufferRecording->amountHandled() == mBufferRecording->getSize());
+    }
+
+    virtual android::sp<AudioHardware> createRecordingHw() = 0;
+    virtual android::sp<AudioHardware> createPlaybackHw() = 0;
+};
+
+
+
+
+#endif // CTSAUDIO_AUDIORECORDPLAYTESTCOMMON_H
diff --git a/suite/audio_quality/test/AudioRecordingLocalTest.cpp b/suite/audio_quality/test/AudioRecordingLocalTest.cpp
new file mode 100644
index 0000000..bb9f8ba
--- /dev/null
+++ b/suite/audio_quality/test/AudioRecordingLocalTest.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2012 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 <unistd.h>
+
+#include <gtest/gtest.h>
+#include <utils/threads.h>
+#include <utils/StrongPointer.h>
+
+#include <audio/AudioHardware.h>
+#include <GenericFactory.h>
+#include <audio/AudioRecordingLocal.h>
+
+#include <Log.h>
+
+#include "AudioPlayTestCommon.h"
+
+class AudioRecordingLocalTest : public AudioPlayTestCommon {
+public:
+    virtual ~AudioRecordingLocalTest() {};
+protected:
+
+    android::sp<AudioHardware> createAudioHw() {
+        return AudioHardware::createAudioHw(true, false);
+    }
+};
+
+
+TEST_F(AudioRecordingLocalTest, PlayAllTest) {
+    playAll(1);
+}
+
+TEST_F(AudioRecordingLocalTest, PlayAllRepeatTest) {
+    playAll(4);
+}
+
+TEST_F(AudioRecordingLocalTest, StartStopTest) {
+    repeatPlayStop();
+}
+
+TEST_F(AudioRecordingLocalTest, WrongUsageTest) {
+    playWrongUsage();
+}
+
diff --git a/suite/audio_quality/test/AudioSignalFactoryTest.cpp b/suite/audio_quality/test/AudioSignalFactoryTest.cpp
new file mode 100644
index 0000000..62ff900
--- /dev/null
+++ b/suite/audio_quality/test/AudioSignalFactoryTest.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2012 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 <stdint.h>
+
+#include <gtest/gtest.h>
+
+#include <audio/AudioSignalFactory.h>
+
+class AudioSignalFactoryTest: public testing::Test {
+protected:
+
+    void testSignalBasic(android::sp<Buffer>& buffer, int maxPositive,
+            AudioHardware::SamplingRate samplingRate, int signalFreq, int samples) {
+        ASSERT_TRUE(buffer->getSize() == (unsigned int)(AudioHardware::E2BPS * 2 * samples));
+        int16_t* data = reinterpret_cast<int16_t*>(buffer->getData());
+        for(int i = 0; i < samples; i++) {
+            ASSERT_TRUE(*data <= maxPositive);
+            ASSERT_TRUE(*data >= -maxPositive);
+            data++;
+            ASSERT_TRUE(*data <= maxPositive);
+            ASSERT_TRUE(*data >= -maxPositive);
+            data++;
+        }
+    }
+};
+
+TEST_F(AudioSignalFactoryTest, SineTest) {
+    const int maxPositive = 1000;
+    const int signalFreq = AudioHardware::ESampleRate_44100/100;
+    const int samples = 8192 * 10;
+    android::sp<Buffer> buffer = AudioSignalFactory::generateSineWave(AudioHardware::E2BPS,
+            maxPositive, AudioHardware::ESampleRate_44100, signalFreq, samples);
+    testSignalBasic(buffer, maxPositive, AudioHardware::ESampleRate_44100, signalFreq, samples);
+}
+
+TEST_F(AudioSignalFactoryTest, WhiteNoiseTest) {
+    const int maxPositive = 1000;
+    const int signalFreq = AudioHardware::ESampleRate_44100/100;
+    const int samples = 8192 * 10;
+    android::sp<Buffer> buffer = AudioSignalFactory::generateWhiteNoise(AudioHardware::E2BPS,
+            maxPositive, samples);
+    testSignalBasic(buffer, maxPositive, AudioHardware::ESampleRate_44100, signalFreq, samples);
+}
+
diff --git a/suite/audio_quality/test/BufferTest.cpp b/suite/audio_quality/test/BufferTest.cpp
new file mode 100644
index 0000000..2c7b20e
--- /dev/null
+++ b/suite/audio_quality/test/BufferTest.cpp
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2012 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 <stdint.h>
+#include <gtest/gtest.h>
+
+#include <UniquePtr.h>
+
+#include "audio/Buffer.h"
+
+
+class BufferTest : public testing::Test {
+public:
+
+    virtual void SetUp() {
+
+    }
+
+    virtual void TearDown() {
+
+    }
+};
+
+
+TEST_F(BufferTest, saveLoadStereoTest) {
+    const int BUFFER_SIZE = 32;
+
+    UniquePtr<Buffer> buffer(new Buffer(BUFFER_SIZE, BUFFER_SIZE, true));
+    ASSERT_TRUE(buffer.get() != NULL);
+    int16_t* data = (int16_t*)buffer->getData();
+    ASSERT_TRUE(data != NULL);
+    for (int i = 0; i < BUFFER_SIZE/4; i++) {
+        data[2*i] = i;
+        data[2*i+1] = i;
+    }
+    android::String8 file("/tmp/cts_audio_temp");
+    ASSERT_TRUE(buffer->saveToFile(file));
+    file.append(".r2s");
+    UniquePtr<Buffer> bufferRead(Buffer::loadFromFile(file));
+    ASSERT_TRUE(bufferRead.get() != NULL);
+    ASSERT_TRUE(bufferRead->getSize() == (size_t)BUFFER_SIZE);
+    ASSERT_TRUE(bufferRead->isStereo());
+    int16_t* dataRead = (int16_t*)bufferRead->getData();
+    for (int i = 0; i < BUFFER_SIZE/4; i++) {
+        ASSERT_TRUE(data[2*i] == dataRead[2*i]);
+        ASSERT_TRUE(data[2*i+1] == dataRead[2*i+1]);
+    }
+}
+
+TEST_F(BufferTest, monoLTest) {
+    const int BUFFER_SIZE = 8;
+
+    UniquePtr<Buffer> buffer(new Buffer(BUFFER_SIZE, BUFFER_SIZE, true));
+    ASSERT_TRUE(buffer.get() != NULL);
+    int16_t* data = (int16_t*)buffer->getData();
+    ASSERT_TRUE(data != NULL);
+    for (int i = 0; i < BUFFER_SIZE/2; i++) {
+        data[i] = i;
+    }
+    UniquePtr<Buffer> bufferl(new Buffer(BUFFER_SIZE/2, BUFFER_SIZE/2, false));
+    ASSERT_TRUE(bufferl.get() != NULL);
+    data = (int16_t*)bufferl->getData();
+    ASSERT_TRUE(data != NULL);
+    for (int i = 0; i < BUFFER_SIZE/4; i++) {
+        data[i] = 2 * i;
+    }
+    buffer->changeToMono(Buffer::EKeepCh0);
+    ASSERT_TRUE((*buffer) == (*bufferl));
+}
diff --git a/suite/audio_quality/test/ClientInterfaceTest.cpp b/suite/audio_quality/test/ClientInterfaceTest.cpp
new file mode 100644
index 0000000..b72be9b
--- /dev/null
+++ b/suite/audio_quality/test/ClientInterfaceTest.cpp
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2012 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 <utils/String8.h>
+
+#include <gtest/gtest.h>
+
+#include <audio/AudioSignalFactory.h>
+#include <ClientInterface.h>
+#include <ClientImpl.h>
+#include <GenericFactory.h>
+#include <audio/RemoteAudio.h>
+
+
+
+class ClientInterfaceTest : public testing::Test {
+protected:
+    ClientInterface* mClient;
+
+protected:
+    virtual void SetUp() {
+        GenericFactory factory;
+        mClient = factory.createClientInterface();
+        ASSERT_TRUE(mClient != NULL);
+        android::String8 dummyParam;
+        ASSERT_TRUE(mClient->init(dummyParam));
+    }
+
+    virtual void TearDown() {
+        delete mClient;
+        mClient = NULL;
+    }
+};
+
+TEST_F(ClientInterfaceTest, InitTest) {
+    // all done in SetUp
+}
+
+TEST_F(ClientInterfaceTest, PlayTest) {
+    ClientImpl* client = reinterpret_cast<ClientImpl*>(mClient);
+    android::sp<RemoteAudio>& audio(client->getAudio());
+    const int maxPositive = 10000;
+    const int signalFreq = AudioHardware::ESampleRate_44100/100;
+    const int samples = 8192*2;
+    android::sp<Buffer> buffer = AudioSignalFactory::generateSineWave(AudioHardware::E2BPS,
+            maxPositive, AudioHardware::ESampleRate_44100, signalFreq, samples);
+    int id;
+    android::String8 name("1");
+    ASSERT_TRUE(audio->downloadData(name, buffer, id));
+    ASSERT_TRUE(audio->startPlayback(true, AudioHardware::ESampleRate_44100,
+            AudioHardware::EModeVoice, 100, id, 1));
+    ASSERT_TRUE(audio->waitForPlaybackCompletion());
+    ASSERT_TRUE(id == audio->getDataId(name));
+    android::String8 name2("2");
+    ASSERT_TRUE(audio->getDataId(name2) < 0);
+}
+
+TEST_F(ClientInterfaceTest, RecordTest) {
+    ClientImpl* client = reinterpret_cast<ClientImpl*>(mClient);
+    android::sp<RemoteAudio>& audio(client->getAudio());
+    const int maxPositive = 10000;
+    const int signalFreq = AudioHardware::ESampleRate_44100 / 100;
+    const int samples = 44100 * 4;
+    android::sp<Buffer> buffer(new Buffer(samples * 2, samples * 2, false));
+
+    ASSERT_TRUE(audio->startRecording(false, AudioHardware::ESampleRate_44100,
+            AudioHardware::EModeVoice, 100, buffer));
+    ASSERT_TRUE(audio->waitForRecordingCompletion());
+    ASSERT_TRUE(buffer->amountHandled() == (samples * 2));
+}
diff --git a/suite/audio_quality/test/FileUtilTest.cpp b/suite/audio_quality/test/FileUtilTest.cpp
new file mode 100644
index 0000000..d79b2aa
--- /dev/null
+++ b/suite/audio_quality/test/FileUtilTest.cpp
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2012 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 <stdint.h>
+#include <gtest/gtest.h>
+
+#include <UniquePtr.h>
+
+#include "Log.h"
+#include "FileUtil.h"
+
+
+class FileUtilTest : public testing::Test {
+public:
+
+};
+
+
+TEST_F(FileUtilTest, initTest) {
+    android::String8 dirPath;
+    ASSERT_TRUE(FileUtil::prepare(dirPath));
+    ASSERT_TRUE(dirPath.find("reports/") == 0);
+    LOGI("returned %s %d", dirPath.string(), dirPath.find("reports/"));
+    android::String8 dirPath2;
+    ASSERT_TRUE(FileUtil::prepare(dirPath2));
+    ASSERT_TRUE(dirPath == dirPath2);
+}
+
+
diff --git a/suite/audio_quality/test/LogTest.cpp b/suite/audio_quality/test/LogTest.cpp
new file mode 100644
index 0000000..421b904
--- /dev/null
+++ b/suite/audio_quality/test/LogTest.cpp
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2012 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 <stdint.h>
+#include <gtest/gtest.h>
+
+#include <UniquePtr.h>
+
+#include "Log.h"
+
+
+
+class LogTest : public testing::Test {
+public:
+
+};
+
+
+TEST_F(LogTest, logTest) {
+    Log::LogLevel level = Log::Instance()->getLogLevel();
+
+    // following lines should match. no automatic test yet..
+    // TODO make it automatic?
+    Log::Instance()->setLogLevel(Log::ELogV);
+    printf("printf %d %d %d %d %d %d\n", 0, 1, 2, 3, 4, 5);
+    LOGD(  "logd   %d %d %d %d %d %d", 0, 1, 2, 3, 4, 5);
+    LOGV(  "logv   %d %d %d %d %d %d", 0, 1, 2, 3, 4, 5);
+    LOGI(  "logi   %d %d %d %d %d %d", 0, 1, 2, 3, 4, 5);
+    LOGW(  "logw   %d %d %d %d %d %d", 0, 1, 2, 3, 4, 5);
+    LOGE(  "loge   %d %d %d %d %d %d", 0, 1, 2, 3, 4, 5);
+
+    int64_t a = 0;
+    int64_t b = 1;
+    int64_t c = 2;
+    int64_t d = 3;
+    int64_t e = 4;
+    int64_t f = 5;
+    printf("printf %lld %lld %lld %lld %lld %lld\n", a, b, c, d, e, f);
+    LOGD(  "logd   %lld %lld %lld %lld %lld %lld", a, b, c, d, e, f);
+    LOGV(  "logv   %lld %lld %lld %lld %lld %lld", a, b, c, d, e, f);
+    LOGI(  "logi   %lld %lld %lld %lld %lld %lld", a, b, c, d, e, f);
+    LOGW(  "logw   %lld %lld %lld %lld %lld %lld", a, b, c, d, e, f);
+    LOGE(  "loge   %lld %lld %lld %lld %lld %lld", a, b, c, d, e, f);
+
+    Log::Instance()->setLogLevel(level);
+}
+
+
+
diff --git a/suite/audio_quality/test/MixerTest.cpp b/suite/audio_quality/test/MixerTest.cpp
new file mode 100644
index 0000000..308822c
--- /dev/null
+++ b/suite/audio_quality/test/MixerTest.cpp
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2012 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 <stdint.h>
+#include <gtest/gtest.h>
+
+#include <UniquePtr.h>
+#include <tinyalsa/asoundlib.h>
+#include <Log.h>
+#include <audio/AudioHardware.h>
+
+class MixerTest : public testing::Test {
+public:
+
+    virtual void SetUp() {
+
+    }
+
+    virtual void TearDown() {
+
+    }
+};
+
+
+TEST_F(MixerTest, tryTinyAlsaTest) {
+    int hwId = AudioHardware::detectAudioHw();
+    ASSERT_TRUE(hwId >= 0);
+    struct mixer* mixerp = mixer_open(hwId);
+    ASSERT_TRUE(mixerp != NULL);
+    int num_ctls = mixer_get_num_ctls(mixerp);
+    // no mixer control for MobilePre. If this assumption fails,
+    // mixer control should be added.
+    ASSERT_TRUE(num_ctls == 0);
+    for (int i = 0; i < num_ctls; i++) {
+        struct mixer_ctl* control = mixer_get_ctl(mixerp, i);
+        ASSERT_TRUE(control != NULL);
+        LOGI("Mixer control %s type %s value %d", mixer_ctl_get_name(control),
+                mixer_ctl_get_type_string(control), mixer_ctl_get_num_values(control));
+        free(control);
+    }
+    mixer_close(mixerp);
+}
+
diff --git a/suite/audio_quality/test/ModelBuilderTest.cpp b/suite/audio_quality/test/ModelBuilderTest.cpp
new file mode 100644
index 0000000..49a0c57
--- /dev/null
+++ b/suite/audio_quality/test/ModelBuilderTest.cpp
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2012 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 <gtest/gtest.h>
+#include <task/ModelBuilder.h>
+
+
+class ModelBuilderTest : public testing::Test {
+public:
+    ModelBuilder mModelBuilder;
+};
+
+TEST_F(ModelBuilderTest, ParsingCaseNoAttribTest) {
+    android::String8 xmlFile("test_description/test/no_attrib.xml");
+    TaskGeneric* testCase = mModelBuilder.parseTestDescriptionXml(xmlFile);
+    ASSERT_TRUE(testCase != NULL);
+    //TODO verify TestCase
+    delete testCase;
+}
+
+TEST_F(ModelBuilderTest, ParsingCaseTest) {
+    android::String8 xmlFile("test_description/host_speaker_calibration.xml");
+    TaskGeneric* testCase = mModelBuilder.parseTestDescriptionXml(xmlFile);
+    ASSERT_TRUE(testCase != NULL);
+    //TODO verify TestCase
+    delete testCase;
+}
+
+TEST_F(ModelBuilderTest, ParsingBatchTest) {
+    android::String8 xmlFile("test_description/all.xml");
+    TaskGeneric* testBatch = mModelBuilder.parseTestDescriptionXml(xmlFile);
+    ASSERT_TRUE(testBatch != NULL);
+    //TODO verify TestCase
+    delete testBatch;
+}
+
+TEST_F(ModelBuilderTest, CaseOnlyTest) {
+    android::String8 xmlFile("test_description/all.xml");
+    TaskGeneric* task = mModelBuilder.parseTestDescriptionXml(xmlFile, true);
+    ASSERT_TRUE(task == NULL);
+
+    delete task;
+}
+
+TEST_F(ModelBuilderTest, MissingMandatoryTest) {
+    android::String8 xmlFile("test_description/test/missing_mandatory.xml");
+    TaskGeneric* task = mModelBuilder.parseTestDescriptionXml(xmlFile);
+    ASSERT_TRUE(task == NULL);
+    delete task;
+}
+
+TEST_F(ModelBuilderTest, UnknownElementTest) {
+    android::String8 xmlFile("test_description/test/unknown_element.xml");
+    TaskGeneric* task = mModelBuilder.parseTestDescriptionXml(xmlFile);
+    ASSERT_TRUE(task == NULL);
+    delete task;
+}
+
+TEST_F(ModelBuilderTest, WrongAttributeTest) {
+    android::String8 xmlFile("test_description/test/wrong_attrib.xml");
+    TaskGeneric* task = mModelBuilder.parseTestDescriptionXml(xmlFile);
+    ASSERT_TRUE(task == NULL);
+    delete task;
+}
+
+TEST_F(ModelBuilderTest, BuiltinRMSTest) {
+    android::String8 xmlFile("test_description/test/test_rms_vma.xml");
+    TaskGeneric* task = mModelBuilder.parseTestDescriptionXml(xmlFile);
+    ASSERT_TRUE(task != NULL);
+    TaskGeneric::ExecutionResult result = task->run();
+    ASSERT_TRUE((result == TaskGeneric::EResultOK) || (result == TaskGeneric::EResultPass));
+    delete task;
+}
+
diff --git a/suite/audio_quality/test/RemoteAudioFakeTcpTest.cpp b/suite/audio_quality/test/RemoteAudioFakeTcpTest.cpp
new file mode 100644
index 0000000..7d77a9b
--- /dev/null
+++ b/suite/audio_quality/test/RemoteAudioFakeTcpTest.cpp
@@ -0,0 +1,309 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+// test RemoteAudio with fake TCP
+
+#include <unistd.h>
+
+#include <utils/String8.h>
+
+#include <gtest/gtest.h>
+
+#include <utils/StrongPointer.h>
+
+#include <Log.h>
+#include <audio/AudioHardware.h>
+#include <audio/AudioProtocol.h>
+#include <audio/AudioSignalFactory.h>
+#include <ClientSocket.h>
+#include <audio/RemoteAudio.h>
+
+
+
+void assertTrue(bool cond) {
+    ASSERT_TRUE(cond);
+}
+void assertData(const char* data1, const char* data2, int len) {
+    for (int i = 0; i < len; i++) {
+        //LOGD("0x%x vs 0x%x", data1[i], data2[i]);
+        ASSERT_TRUE(data1[i] == data2[i]);
+    }
+}
+
+class ClientSocketForTest: public ClientSocket {
+public:
+    ClientSocketForTest()
+        : mToRead(NULL),
+          mReadLength(0) {};
+
+    virtual ~ClientSocketForTest() {
+        close(mSocket);
+        close(mPipeWrFd);
+    }
+    virtual bool init(const char* hostIp, int port, bool enableTimeout = false) {
+        LOGD("ClientSocketForTest::init");
+        // use this fd to work with poll
+        int pipefd[2];
+        if (pipe(pipefd)  == -1) {
+            LOGE("cannot create pipe");
+            return false;
+        }
+        LOGD("pipe %d %d", pipefd[0], pipefd[1]);
+        mSocket = pipefd[0];
+        mPipeWrFd = pipefd[1];
+        const char ipExpectation[] = "127.0.0.1";
+        assertTrue(memcmp(ipExpectation, hostIp, sizeof(ipExpectation)) == 0);
+        return true;
+    }
+    virtual bool readData(char* data, int len, int timeoutInMs = 0) {
+        read(mSocket, data, len);
+        return true;
+    }
+    virtual bool sendData(const char* data, int len) {
+        assertTrue((len + mSendPointer) <= mSendLength);
+        assertData(data, mToSend + mSendPointer, len);
+        mSendPointer += len;
+        if ((mToRead != NULL) && (mReadLength != 0)) {
+            LOGD("fake TCP copy reply %d", mReadLength);
+            write(mPipeWrFd, mToRead, mReadLength);
+            mToRead = NULL; // prevent writing the same data again
+        }
+        return true;
+    }
+
+    void setSendExpectation(const char* data, int len) {
+        mToSend = data;
+        mSendLength = len;
+        mSendPointer = 0;
+    }
+    void setReadExpectation(char* data, int len) {
+        mToRead = data;
+        mReadLength = len;
+    }
+public:
+    int mPipeWrFd; // for writing
+    const char* mToRead;
+    int mReadLength;
+    const char* mToSend;
+    int mSendLength;
+    int mSendPointer;
+};
+
+class RemoteAudioFakeTcpTest : public testing::Test {
+protected:
+    android::sp<RemoteAudio> mRemoteAudio;
+    ClientSocketForTest mTestSocket;
+
+protected:
+    virtual void SetUp() {
+        ASSERT_TRUE(U32_ENDIAN_SWAP(0x12345678) == 0x78563412);
+        mRemoteAudio = new RemoteAudio(mTestSocket);
+        ASSERT_TRUE(mRemoteAudio != NULL);
+        ASSERT_TRUE(mRemoteAudio->init(1234));
+    }
+
+    virtual void TearDown() {
+        mRemoteAudio->release();
+        mRemoteAudio.clear();
+    }
+
+    void doDownload() {
+        android::sp<Buffer> buffer = AudioSignalFactory::generateZeroSound(AudioHardware::E2BPS, 2,
+                false);
+        uint32_t prepareSend[] = {
+                U32_ENDIAN_SWAP(AudioProtocol::ECmdDownload),
+                U32_ENDIAN_SWAP(8),
+                U32_ENDIAN_SWAP(0), //id
+                U32_ENDIAN_SWAP(0)
+        };
+        uint32_t prepareReply[] = {
+                U32_ENDIAN_SWAP((AudioProtocol::ECmdDownload & 0xffff) | 0x43210000),
+                0,
+                0
+        };
+        LOGD("reply 0x%x", prepareReply[0]);
+
+        mTestSocket.setSendExpectation((char*)prepareSend, sizeof(prepareSend));
+        // this is reply, but set expectation for reply first as it is sent after send
+        mTestSocket.setReadExpectation((char*)prepareReply, sizeof(prepareReply));
+
+        int id = -1;
+        android::String8 name("1");
+        ASSERT_TRUE(mRemoteAudio->downloadData(name, buffer, id));
+        ASSERT_TRUE(id >= 0);
+    }
+};
+
+TEST_F(RemoteAudioFakeTcpTest, InitTest) {
+    // all done in SetUp
+}
+
+TEST_F(RemoteAudioFakeTcpTest, DownloadTest) {
+    doDownload();
+}
+
+TEST_F(RemoteAudioFakeTcpTest, PlayTest) {
+    doDownload();
+
+    bool stereo = false;
+    int id = 0;
+    int samplingF = 44100;
+    int mode = AudioHardware::EModeVoice | (stereo ? 0x80000000 : 0);
+    int volume = 0;
+    int repeat = 1;
+
+    uint32_t prepareSend[] = {
+            U32_ENDIAN_SWAP(AudioProtocol::ECmdStartPlayback),
+            U32_ENDIAN_SWAP(20),
+            U32_ENDIAN_SWAP(id), //id
+            U32_ENDIAN_SWAP(samplingF),
+            U32_ENDIAN_SWAP(mode),
+            U32_ENDIAN_SWAP(volume),
+            U32_ENDIAN_SWAP(repeat)
+    };
+    uint32_t prepareReply[] = {
+            U32_ENDIAN_SWAP((AudioProtocol::ECmdStartPlayback & 0xffff) | 0x43210000),
+            0,
+            0
+    };
+
+    mTestSocket.setSendExpectation((char*)prepareSend, sizeof(prepareSend));
+    // this is reply, but set expectation for reply first as it is sent after send
+    mTestSocket.setReadExpectation((char*)prepareReply, sizeof(prepareReply));
+
+    ASSERT_TRUE(mRemoteAudio->startPlayback(stereo, samplingF, mode, volume, id, repeat));
+    ASSERT_TRUE(mRemoteAudio->waitForPlaybackCompletion());
+}
+
+TEST_F(RemoteAudioFakeTcpTest, PlayStopTest) {
+    doDownload();
+
+    bool stereo = false;
+    int id = 0;
+    int samplingF = 44100;
+    int mode = AudioHardware::EModeVoice | (stereo ? 0x80000000 : 0);
+    int volume = 0;
+    int repeat = 1;
+
+    uint32_t startPlaybackSend[] = {
+            U32_ENDIAN_SWAP(AudioProtocol::ECmdStartPlayback),
+            U32_ENDIAN_SWAP(20),
+            U32_ENDIAN_SWAP(id),
+            U32_ENDIAN_SWAP(samplingF),
+            U32_ENDIAN_SWAP(mode),
+            U32_ENDIAN_SWAP(volume),
+            U32_ENDIAN_SWAP(repeat)
+    };
+    uint32_t startReply[] = {
+            U32_ENDIAN_SWAP((AudioProtocol::ECmdStartPlayback & 0xffff) | 0x43210000),
+            0,
+            0
+    };
+
+    uint32_t stopPlaybackSend[] = {
+            U32_ENDIAN_SWAP(AudioProtocol::ECmdStopPlayback),
+            U32_ENDIAN_SWAP(0)
+    };
+
+    uint32_t stopReply[] = {
+            U32_ENDIAN_SWAP((AudioProtocol::ECmdStopPlayback & 0xffff) | 0x43210000),
+            0,
+            0
+    };
+
+    mTestSocket.setSendExpectation((char*)startPlaybackSend, sizeof(startPlaybackSend));
+    // this is reply, but set expectation for reply first as it is sent after send
+    mTestSocket.setReadExpectation((char*)startReply, sizeof(startReply));
+
+    ASSERT_TRUE(mRemoteAudio->startPlayback(stereo, samplingF, mode, volume, id, repeat));
+    sleep(1);
+    mTestSocket.setSendExpectation((char*)stopPlaybackSend, sizeof(stopPlaybackSend));
+    // this is reply, but set expectation for reply first as it is sent after send
+    mTestSocket.setReadExpectation((char*)stopReply, sizeof(stopReply));
+    mRemoteAudio->stopPlayback();
+
+    mTestSocket.setSendExpectation((char*)startPlaybackSend, sizeof(startPlaybackSend));
+    // this is reply, but set expectation for reply first as it is sent after send
+    mTestSocket.setReadExpectation((char*)startReply, sizeof(startReply));
+    ASSERT_TRUE(mRemoteAudio->startPlayback(stereo, samplingF, mode, volume, id, repeat));
+    sleep(1);
+    mTestSocket.setSendExpectation((char*)stopPlaybackSend, sizeof(stopPlaybackSend));
+    // this is reply, but set expectation for reply first as it is sent after send
+    mTestSocket.setReadExpectation((char*)stopReply, sizeof(stopReply));
+    mRemoteAudio->stopPlayback();
+
+    mTestSocket.setSendExpectation((char*)startPlaybackSend, sizeof(startPlaybackSend));
+    // this is reply, but set expectation for reply first as it is sent after send
+    mTestSocket.setReadExpectation((char*)startReply, sizeof(startReply));
+    ASSERT_TRUE(mRemoteAudio->startPlayback(stereo, samplingF, mode, volume, id, repeat));
+    ASSERT_TRUE(mRemoteAudio->waitForPlaybackCompletion());
+}
+
+TEST_F(RemoteAudioFakeTcpTest, RecordingTest) {
+    bool stereo = false;
+    int id = 0;
+    int samplingF = 44100;
+    int mode = AudioHardware::EModeVoice | (stereo ? 0x80000000 : 0);
+    int volume = 0;
+    int noSamples = 44; // 1ms worth
+
+    android::sp<Buffer> buffer(new Buffer(100, noSamples*2, false));
+
+    uint32_t startSend[] = {
+            U32_ENDIAN_SWAP(AudioProtocol::ECmdStartRecording),
+            U32_ENDIAN_SWAP(16),
+            U32_ENDIAN_SWAP(samplingF),
+            U32_ENDIAN_SWAP(mode),
+            U32_ENDIAN_SWAP(volume),
+            U32_ENDIAN_SWAP(noSamples)
+    };
+
+    // 2bytes per sample, +2 for last samples rounded off
+    uint32_t startReply[noSamples/2 + 2 + 3];
+    memset(startReply, 0, sizeof(startReply));
+    startReply[0] = U32_ENDIAN_SWAP((AudioProtocol::ECmdStartRecording & 0xffff) | 0x43210000);
+    startReply[1] = 0;
+    startReply[2] = U32_ENDIAN_SWAP(noSamples * 2);
+
+    uint32_t stopSend[] = {
+            U32_ENDIAN_SWAP(AudioProtocol::ECmdStopRecording),
+            U32_ENDIAN_SWAP(0)
+    };
+
+    uint32_t stopReply[] = {
+            U32_ENDIAN_SWAP((AudioProtocol::ECmdStopRecording & 0xffff) | 0x43210000),
+            0,
+            0
+    };
+
+
+    mTestSocket.setSendExpectation((char*)startSend, sizeof(startSend));
+    // this is reply, but set expectation for reply first as it is sent after send
+    mTestSocket.setReadExpectation((char*)startReply, 12 + noSamples*2);
+
+    ASSERT_TRUE(mRemoteAudio->startRecording(stereo, samplingF, mode, volume, buffer));
+    ASSERT_TRUE(mRemoteAudio->waitForRecordingCompletion());
+    ASSERT_TRUE(buffer->amountHandled() == (size_t)(noSamples * 2));
+    mTestSocket.setSendExpectation((char*)startSend, sizeof(startSend));
+    // this is reply, but set expectation for reply first as it is sent after send
+    mTestSocket.setReadExpectation((char*)startReply, 12 + noSamples*2);
+    ASSERT_TRUE(mRemoteAudio->startRecording(stereo, samplingF, mode, volume, buffer));
+    sleep(1);
+    mTestSocket.setSendExpectation((char*)stopSend, sizeof(stopSend));
+    // this is reply, but set expectation for reply first as it is sent after send
+    mTestSocket.setReadExpectation((char*)stopReply, sizeof(stopReply));
+    mRemoteAudio->stopRecording();
+}
diff --git a/suite/audio_quality/test/SignalProcessingInterfaceTest.cpp b/suite/audio_quality/test/SignalProcessingInterfaceTest.cpp
new file mode 100644
index 0000000..cdffeb9
--- /dev/null
+++ b/suite/audio_quality/test/SignalProcessingInterfaceTest.cpp
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2012 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 <gtest/gtest.h>
+#include <utils/String8.h>
+
+#include <audio/AudioSignalFactory.h>
+#include <SignalProcessingInterface.h>
+#include <SignalProcessingImpl.h>
+#include <task/TaskAll.h>
+
+class SignalProcessingInterfaceTest : public testing::Test {
+protected:
+    SignalProcessingImpl* mSp;
+
+protected:
+    virtual void SetUp() {
+        mSp = new SignalProcessingImpl();
+        ASSERT_TRUE(mSp != NULL);
+        ASSERT_TRUE(mSp->init(SignalProcessingImpl::MAIN_PROCESSING_SCRIPT));
+    }
+
+    virtual void TearDown() {
+        delete mSp;
+        mSp = NULL;
+    }
+};
+
+TEST_F(SignalProcessingInterfaceTest, InitTest) {
+    // SetUp do all the work, nothing to do
+}
+
+TEST_F(SignalProcessingInterfaceTest, EchoTest) {
+    android::String8 functionName("echo");
+    int nInputs = 4;
+    int nOutputs = 4;
+    bool inputTypes[4] = { true, true, false, false };
+    bool outputTypes[4] = { true, true, false, false };
+
+    android::sp<Buffer> in0(new Buffer(160000, 160000, true));
+    char* data0 = in0->getData();
+    for (size_t i = 0; i < in0->getSize(); i++) {
+        data0[i] = i;
+    }
+    android::sp<Buffer> in1(new Buffer(8, 8, false));
+    char* data1 = in1->getData();
+    for (size_t i = 0; i < in1->getSize(); i++) {
+        data1[i] = i;
+    }
+    TaskCase::Value in2(1.0f);
+    TaskCase::Value in3((int64_t)100);
+    void* inputs[4] = { &in0, &in1, &in2, &in3 };
+
+    android::sp<Buffer> out0(new Buffer(160000, 160000, true));
+    char* outdata0 = out0->getData();
+    for (size_t i = 0; i < out0->getSize(); i++) {
+        outdata0[i] = 0xaa;
+    }
+    android::sp<Buffer> out1(new Buffer(8, 8, false));
+    char* outdata1 = out1->getData();
+    for (size_t i = 0; i < out1->getSize(); i++) {
+        outdata1[i] = 0xbb;
+    }
+    TaskCase::Value out2(-1.0f);
+    TaskCase::Value out3((int64_t)1000);
+    void *outputs[4] = { &out0, &out1, &out2, &out3 };
+
+    ASSERT_TRUE(mSp->run( functionName,
+            nInputs, inputTypes, inputs,
+            nOutputs, outputTypes, outputs) == TaskGeneric::EResultOK);
+    ASSERT_TRUE(*(in0.get()) == *(out0.get()));
+    ASSERT_TRUE(*(in1.get()) == *(out1.get()));
+    ASSERT_TRUE(in2 == out2);
+    ASSERT_TRUE(in3 == out3);
+}
+
+TEST_F(SignalProcessingInterfaceTest, intsumTest) {
+    android::String8 functionName("intsum");
+    int nInputs = 2;
+    int nOutputs = 1;
+    bool inputTypes[2] = { false, false };
+    bool outputTypes[1] = { false };
+
+    TaskCase::Value in0((int64_t)10);
+    TaskCase::Value in1((int64_t)100);
+    void* inputs[2] = { &in0, &in1 };
+
+    TaskCase::Value out0((int64_t)0);
+    void *outputs[1] = { &out0 };
+
+    ASSERT_TRUE(mSp->run( functionName,
+            nInputs, inputTypes, inputs,
+            nOutputs, outputTypes, outputs) == TaskGeneric::EResultOK);
+    ASSERT_TRUE(out0.getInt64() == (in0.getInt64() + in1.getInt64()));
+}
+
+// two instances of python processing processes should work
+TEST_F(SignalProcessingInterfaceTest, TwoInstanceTest) {
+    SignalProcessingImpl* sp2 = new SignalProcessingImpl();
+    ASSERT_TRUE(sp2 != NULL);
+    ASSERT_TRUE(sp2->init(SignalProcessingImpl::MAIN_PROCESSING_SCRIPT));
+
+    android::String8 functionName("intsum");
+    int nInputs = 2;
+    int nOutputs = 1;
+    bool inputTypes[2] = { false, false };
+    bool outputTypes[1] = { false };
+
+    TaskCase::Value in0((int64_t)10);
+    TaskCase::Value in1((int64_t)100);
+    void* inputs[2] = { &in0, &in1 };
+
+    TaskCase::Value out0((int64_t)0);
+    void *outputs[1] = { &out0 };
+
+    ASSERT_TRUE(mSp->run( functionName,
+            nInputs, inputTypes, inputs,
+            nOutputs, outputTypes, outputs) == TaskGeneric::EResultOK);
+    ASSERT_TRUE(out0.getInt64() == (in0.getInt64() + in1.getInt64()));
+    out0.setInt64(0);
+    ASSERT_TRUE(sp2->run( functionName,
+                nInputs, inputTypes, inputs,
+                nOutputs, outputTypes, outputs) == TaskGeneric::EResultOK);
+    ASSERT_TRUE(out0.getInt64() == (in0.getInt64() + in1.getInt64()));
+    delete sp2;
+}
+
+// test to run processing/example.py
+TEST_F(SignalProcessingInterfaceTest, exampleTest) {
+    android::String8 functionName("example");
+    int nInputs = 8;
+    int nOutputs = 4;
+    bool inputTypes[8] = { true, true, true, true, false, false, false, false };
+    bool outputTypes[4] = { true, true, false, false };
+
+    android::sp<Buffer> in0(new Buffer(16, 16, true));
+    char* data0 = in0->getData();
+    for (size_t i = 0; i < in0->getSize(); i++) {
+        data0[i] = i;
+    }
+    android::sp<Buffer> in1(new Buffer(16, 16, true));
+    char* data1 = in1->getData();
+    for (size_t i = 0; i < in1->getSize(); i++) {
+        data1[i] = i;
+    }
+    android::sp<Buffer> in2(new Buffer(8, 8, false));
+    char* data2 = in2->getData();
+    for (size_t i = 0; i < in2->getSize(); i++) {
+        data2[i] = i;
+    }
+    android::sp<Buffer> in3(new Buffer(8, 8, false));
+    char* data3 = in3->getData();
+    for (size_t i = 0; i < in3->getSize(); i++) {
+        data3[i] = i;
+    }
+    TaskCase::Value in4((int64_t)100);
+    TaskCase::Value in5((int64_t)100);
+    TaskCase::Value in6(1.0f);
+    TaskCase::Value in7(1.0f);
+    void* inputs[8] = { &in0, &in1, &in2, &in3, &in4, &in5, &in6, &in7 };
+
+    android::sp<Buffer> out0(new Buffer(16, 16, true));
+    char* outdata0 = out0->getData();
+    for (size_t i = 0; i < out0->getSize(); i++) {
+        outdata0[i] = 0xaa;
+    }
+    android::sp<Buffer> out1(new Buffer(8, 8, false));
+    char* outdata1 = out1->getData();
+    for (size_t i = 0; i < out1->getSize(); i++) {
+        outdata1[i] = 0xbb;
+    }
+    TaskCase::Value out2((int64_t)1000);
+    TaskCase::Value out3(-1.0f);
+    void *outputs[4] = { &out0, &out1, &out2, &out3 };
+
+    ASSERT_TRUE(mSp->run( functionName,
+            nInputs, inputTypes, inputs,
+            nOutputs, outputTypes, outputs) == TaskGeneric::EResultOK);
+    ASSERT_TRUE(*(in0.get()) == *(out0.get()));
+    ASSERT_TRUE(*(in2.get()) == *(out1.get()));
+    ASSERT_TRUE(in4 == out2);
+    ASSERT_TRUE(in6 == out3);
+}
diff --git a/suite/audio_quality/test/SimpleScriptExecTest.cpp b/suite/audio_quality/test/SimpleScriptExecTest.cpp
new file mode 100644
index 0000000..7016ef9
--- /dev/null
+++ b/suite/audio_quality/test/SimpleScriptExecTest.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2012 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 <gtest/gtest.h>
+#include <SimpleScriptExec.h>
+
+
+class ScriptExecTest : public testing::Test {
+
+};
+
+TEST_F(ScriptExecTest, PythonVersionTest) {
+    ASSERT_TRUE(SimpleScriptExec::checkPythonEnv());
+}
+
+
+TEST_F(ScriptExecTest, checkIfPassedTest) {
+    android::String8 pass1("___CTS_AUDIO_PASS___");
+    android::String8 match1;
+    ASSERT_TRUE(SimpleScriptExec::checkIfPassed(pass1, match1));
+
+    android::String8 fail1;
+    ASSERT_TRUE(!SimpleScriptExec::checkIfPassed(fail1, match1));
+}
+
diff --git a/suite/audio_quality/test/StringUtilTest.cpp b/suite/audio_quality/test/StringUtilTest.cpp
new file mode 100644
index 0000000..1dbcea0
--- /dev/null
+++ b/suite/audio_quality/test/StringUtilTest.cpp
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2012 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 <gtest/gtest.h>
+#include <StringUtil.h>
+
+
+class StringUtilTest : public testing::Test {
+
+};
+
+TEST_F(StringUtilTest, compareTest) {
+    android::String8 str("hello");
+    ASSERT_TRUE(StringUtil::compare(str, "hello") == 0);
+    ASSERT_TRUE(StringUtil::compare(str, "hi") != 0);
+}
+
+TEST_F(StringUtilTest, substrTest) {
+    android::String8 str("hello there");
+
+    android::String8 sub1 = StringUtil::substr(str, 0, 5);
+    ASSERT_TRUE(StringUtil::compare(sub1, "hello") == 0);
+
+    android::String8 sub2 = StringUtil::substr(str, 10, 5);
+    ASSERT_TRUE(StringUtil::compare(sub2, "e") == 0);
+
+    android::String8 sub3 = StringUtil::substr(str, 6, 5);
+    ASSERT_TRUE(StringUtil::compare(sub3, "there") == 0);
+
+    android::String8 sub4 = StringUtil::substr(str, 100, 5);
+    ASSERT_TRUE(sub4.length() == 0);
+}
+
+TEST_F(StringUtilTest, endsWithTest) {
+    android::String8 str("hello there");
+    ASSERT_TRUE(StringUtil::endsWith(str, "there"));
+    ASSERT_TRUE(StringUtil::endsWith(str, "hello there"));
+    ASSERT_TRUE(!StringUtil::endsWith(str, "not there"));
+}
+
+TEST_F(StringUtilTest, splitTest) {
+    android::String8 str("hello:there:break:this:");
+    std::vector<android::String8>* tokens = StringUtil::split(str, ':');
+    ASSERT_TRUE(tokens != NULL);
+    ASSERT_TRUE(tokens->size() == 4);
+    ASSERT_TRUE(StringUtil::compare(tokens->at(0), "hello") == 0);
+    ASSERT_TRUE(StringUtil::compare(tokens->at(1), "there") == 0);
+    ASSERT_TRUE(StringUtil::compare(tokens->at(2), "break") == 0);
+    ASSERT_TRUE(StringUtil::compare(tokens->at(3), "this") == 0);
+    delete tokens;
+
+    android::String8 str2("::::");
+    std::vector<android::String8>* tokens2 = StringUtil::split(str2, ':');
+    ASSERT_TRUE(tokens2 != NULL);
+    ASSERT_TRUE(tokens2->size() == 0);
+    delete tokens2;
+}
+
+
+
diff --git a/suite/audio_quality/test/TaskCaseCommon.h b/suite/audio_quality/test/TaskCaseCommon.h
new file mode 100644
index 0000000..cf8d272
--- /dev/null
+++ b/suite/audio_quality/test/TaskCaseCommon.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+
+#ifndef CTSAUDIO_TASKCASECOMMON_H
+#define CTSAUDIO_TASKCASECOMMON_H
+
+#include <gtest/gtest.h>
+
+#include <Log.h>
+#include <GenericFactory.h>
+#include <task/TaskAll.h>
+
+/**
+ * Create TaskCase with setup and action as children
+ * No need to destroy setup and action
+ */
+inline TaskCase* getTaskCase(TaskGeneric*& setup, TaskGeneric*& action)
+{
+    GenericFactory factory;
+    TaskCase* taskCase = new TaskCase();
+    setup = factory.createTask(TaskGeneric::ETaskSetup);
+    taskCase->addChild(setup);
+    action = factory.createTask(TaskGeneric::ETaskAction);
+    taskCase->addChild(action);
+    return taskCase;
+}
+
+
+#endif // CTSAUDIO_TASKCASECOMMON_H
diff --git a/suite/audio_quality/test/TaskCaseTest.cpp b/suite/audio_quality/test/TaskCaseTest.cpp
new file mode 100644
index 0000000..9cf74a7
--- /dev/null
+++ b/suite/audio_quality/test/TaskCaseTest.cpp
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2012 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 <stdint.h>
+#include <gtest/gtest.h>
+
+#include "Log.h"
+#include "StringUtil.h"
+#include "task/TaskAll.h"
+
+
+class TaskCaseTest : public testing::Test {
+public:
+    TaskCase* mTaskCase;
+    virtual void SetUp() {
+        mTaskCase = new TaskCase();
+        ASSERT_TRUE(mTaskCase != NULL);
+    }
+
+    virtual void TearDown() {
+        delete mTaskCase;
+    }
+};
+
+
+TEST_F(TaskCaseTest, DataMapTest) {
+    android::sp<Buffer> buffer1(new Buffer(4, 4, true));
+    android::sp<Buffer> buffer2(new Buffer(4, 4, true));
+    android::sp<Buffer> buffer3(new Buffer(4, 4, true));
+    android::sp<Buffer> buffer4(new Buffer(4, 4, true));
+
+    const android::String8 BUFFER1("buffer1");
+    const android::String8 BUFFER2("buffer2");
+    const android::String8 BUFFER3("buffer3");
+    const android::String8 BUFFER4("buffer4");
+    ASSERT_TRUE(mTaskCase->registerBuffer(BUFFER1, buffer1));
+    ASSERT_TRUE(mTaskCase->registerBuffer(BUFFER2, buffer2));
+    ASSERT_TRUE(mTaskCase->registerBuffer(BUFFER3, buffer3));
+    ASSERT_TRUE(mTaskCase->registerBuffer(BUFFER4, buffer4));
+
+    android::sp<Buffer> buffer1f = mTaskCase->findBuffer(BUFFER1);
+    //LOGI("buffer1 %x, buffer1f %x", &buffer1, buffer1f);
+    ASSERT_TRUE(buffer1.get() == buffer1f.get());
+    const android::String8 NO_SUCH_BUFFER("no_such_buffer");
+    buffer1f = mTaskCase->findBuffer(NO_SUCH_BUFFER);
+    ASSERT_TRUE(buffer1f.get() == NULL);
+    const android::String8 RE("buffer[1-2]");
+    std::list<TaskCase::BufferPair>* list = mTaskCase->findAllBuffers(RE);
+    ASSERT_TRUE(list != NULL);
+    ASSERT_TRUE(((list->front().second.get() == buffer1.get()) &&
+                    (list->back().second.get() == buffer2.get())) ||
+                ((list->front().second.get() == buffer2.get()) &&
+                    (list->back().second.get() == buffer1.get())));
+    delete list;
+}
+
+TEST_F(TaskCaseTest, ValueMapTest) {
+    TaskCase::Value val1(1.0f);
+    TaskCase::Value val2(2.0f);
+    TaskCase::Value val3((int64_t)1);
+    TaskCase::Value val4((int64_t)2);
+    TaskCase::Value val2_copy(2.0f);
+    ASSERT_TRUE(!(val1 == val2));
+    ASSERT_TRUE(!(val2 == val3));
+    ASSERT_TRUE(val2 == val2_copy);
+    ASSERT_TRUE(val1.getDouble() == 1.0f);
+    ASSERT_TRUE(val3.getInt64() == 1);
+    const android::String8 V1("v1");
+    const android::String8 V2("v2");
+    const android::String8 V3("v3");
+    const android::String8 V4("v4");
+    const android::String8 V5("v5");
+    ASSERT_TRUE(mTaskCase->registerValue(V1, val1));
+    ASSERT_TRUE(mTaskCase->registerValue(V2, val2));
+    ASSERT_TRUE(mTaskCase->registerValue(V3, val3));
+    ASSERT_TRUE(mTaskCase->registerValue(V4, val4));
+
+    TaskCase::Value valRead;
+    ASSERT_TRUE(mTaskCase->findValue(V4, valRead));
+    ASSERT_TRUE(valRead.getInt64() == 2);
+    TaskCase::Value val4_2((int64_t)3);
+    ASSERT_TRUE(mTaskCase->updateValue(V4, val4_2));
+    ASSERT_TRUE(mTaskCase->findValue(V4, valRead));
+    ASSERT_TRUE(valRead.getInt64() == 3);
+    ASSERT_TRUE(!mTaskCase->updateValue(V5, val4));
+    ASSERT_TRUE(!mTaskCase->findValue(V5, valRead));
+
+    const android::String8 RE("v[2-3]");
+    std::list<TaskCase::ValuePair>* list = mTaskCase->findAllValues(RE);
+    ASSERT_TRUE(list != NULL);
+    ASSERT_TRUE(((list->front().second == val2) && (list->back().second == val3)) ||
+                ((list->front().second == val3) && (list->back().second == val4)));
+    delete list;
+}
+
+TEST_F(TaskCaseTest, IndexMapTest) {
+    Buffer buffer1(4, 4, true);
+    Buffer buffer2(4, 4, true);
+    Buffer buffer3(4, 4, true);
+    Buffer buffer4(4, 4, true);
+
+    int i = 0;
+    int j = 1;
+    const android::String8 I("i");
+    const android::String8 J("j");
+    const android::String8 K("k");
+    ASSERT_TRUE(mTaskCase->registerIndex(I));
+    ASSERT_TRUE(mTaskCase->registerIndex(J));
+    ASSERT_TRUE(mTaskCase->updateIndex(I, i));
+    ASSERT_TRUE(mTaskCase->updateIndex(J, j));
+    int i_read, j_read, k_read;
+    ASSERT_TRUE(mTaskCase->findIndex(I, i_read));
+    ASSERT_TRUE(mTaskCase->findIndex(J, j_read));
+    ASSERT_TRUE(!mTaskCase->findIndex(K, k_read));
+    ASSERT_TRUE(i == i_read);
+    ASSERT_TRUE(j == j_read);
+    //TODO add findAll test
+}
+
+TEST_F(TaskCaseTest, VarTranslateTest) {
+    const android::String8 I("i");
+    const android::String8 J("j");
+    const android::String8 K("k");
+    ASSERT_TRUE(mTaskCase->registerIndex(I, 1));
+    ASSERT_TRUE(mTaskCase->registerIndex(J, 2));
+    ASSERT_TRUE(mTaskCase->registerIndex(K, 3));
+
+    android::String8 orig1("hello_$i_$j");
+    android::String8 result1;
+    ASSERT_TRUE(mTaskCase->translateVarName(orig1, result1));
+    ASSERT_TRUE(StringUtil::compare(result1, "hello_1_2") == 0);
+
+    android::String8 orig2("hello_$i_$j_$k_there");
+    android::String8 result2;
+    ASSERT_TRUE(mTaskCase->translateVarName(orig2, result2));
+    ASSERT_TRUE(StringUtil::compare(result2, "hello_1_2_3_there") == 0);
+
+    // should fail as there is no such var
+    android::String8 orig3("$n");
+    android::String8 result3;
+    ASSERT_TRUE(!mTaskCase->translateVarName(orig3, result3));
+
+    android::String8 orig4("hello_there");
+    android::String8 result4;
+    ASSERT_TRUE(mTaskCase->translateVarName(orig4, result4));
+    ASSERT_TRUE(StringUtil::compare(result4, "hello_there") == 0);
+}
diff --git a/suite/audio_quality/test/TaskProcessTest.cpp b/suite/audio_quality/test/TaskProcessTest.cpp
new file mode 100644
index 0000000..05c7f1d
--- /dev/null
+++ b/suite/audio_quality/test/TaskProcessTest.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2012 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 "TaskCaseCommon.h"
+
+class TaskProcessTest : public testing::Test {
+public:
+    TaskCase* mTestCase;
+    TaskSequential* mSequential;
+    TaskProcess* mProcess;
+
+
+    virtual void SetUp() {
+        TaskGeneric* setup = NULL;
+        TaskGeneric* action = NULL;
+        mTestCase  = getTaskCase(setup, action);
+        ASSERT_TRUE(mTestCase != NULL);
+        ASSERT_TRUE(setup != NULL);
+        ASSERT_TRUE(action != NULL);
+        mSequential = new TaskSequential();
+        const android::String8 REPEAT("repeat");
+        const android::String8 N_10("10");
+        const android::String8 INDEX("index");
+        const android::String8 I("i");
+        ASSERT_TRUE(mSequential->parseAttribute(REPEAT, N_10));
+        ASSERT_TRUE(mSequential->parseAttribute(INDEX, I));
+        ASSERT_TRUE(action->addChild(mSequential));
+        mProcess = new TaskProcess();
+        ASSERT_TRUE(mSequential->addChild(mProcess));
+
+    }
+
+    virtual void TearDown() {
+        delete mTestCase;
+    }
+};
+
+
+TEST_F(TaskProcessTest, AttributeTest) {
+
+}
+
+
diff --git a/suite/audio_quality/test/TaskSequentialTest.cpp b/suite/audio_quality/test/TaskSequentialTest.cpp
new file mode 100644
index 0000000..289dafe
--- /dev/null
+++ b/suite/audio_quality/test/TaskSequentialTest.cpp
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2012 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 "TaskCaseCommon.h"
+class TaskSequentialTest : public testing::Test {
+public:
+    TaskCase* mTestCase;
+    TaskSequential* mSequential;
+
+
+    virtual void SetUp() {
+        TaskGeneric* setup = NULL;
+        TaskGeneric* action = NULL;
+        mTestCase  = getTaskCase(setup, action);
+        ASSERT_TRUE(mTestCase != NULL);
+        ASSERT_TRUE(setup != NULL);
+        ASSERT_TRUE(action != NULL);
+        mSequential = new TaskSequential();
+        action->addChild(mSequential);
+    }
+
+    virtual void TearDown() {
+        delete mTestCase;
+    }
+};
+
+
+TEST_F(TaskSequentialTest, AttributeTest) {
+    const android::String8 REPEAT("repeat");
+    const android::String8 N_10("10");
+    const android::String8 INDEX("index");
+    const android::String8 I("i");
+    const android::String8 NO_SUCH_THING("no_such_thing");
+    const android::String8 SHOULD_FAIL("should_fail");
+    ASSERT_TRUE(mSequential->parseAttribute(REPEAT, N_10));
+    ASSERT_TRUE(mSequential->parseAttribute(INDEX, I));
+    ASSERT_TRUE(!mSequential->parseAttribute(NO_SUCH_THING, SHOULD_FAIL));
+    mSequential->run();
+    const android::String8 RE(".*");
+    std::list<TaskCase::IndexPair>* indices = mTestCase->findAllIndices(RE);
+    ASSERT_TRUE(indices != NULL);
+    ASSERT_TRUE(indices->size() == 1);
+    int index = -10;
+
+    ASSERT_TRUE(mTestCase->findIndex(I, index));
+    ASSERT_TRUE(index == 10);
+    delete indices;
+}
+
+
+
+
diff --git a/suite/audio_quality/test/TaskTest.cpp b/suite/audio_quality/test/TaskTest.cpp
new file mode 100644
index 0000000..b003d99
--- /dev/null
+++ b/suite/audio_quality/test/TaskTest.cpp
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2012 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 <gtest/gtest.h>
+#include "task/TaskAll.h"
+
+static const android::String8 AAA("aaa");
+static const android::String8 BBB("bbb");
+
+class TaskTest : public testing::Test {
+public:
+    TaskCase* mTestCase;
+    // should not delete
+    TaskGeneric* mTaskSetup;
+    TaskGeneric* mTaskAction;
+    TaskGeneric* mTaskSequential;
+    TaskGeneric* mTaskProcess;
+    TaskGeneric* mTaskInput;
+    TaskGeneric* mTaskOutput;
+    TaskGeneric* mTaskSound;
+
+    class TestTaskDummy: public TaskGeneric {
+    public:
+        static int mRunCounter;
+        static int mLiveInstanceCounter;
+
+        TestTaskDummy(TaskGeneric::TaskType type)
+            : TaskGeneric(type) {
+            mLiveInstanceCounter++;
+
+
+            const android::String8* list[] = {&AAA, &BBB, NULL};
+            registerSupportedStringAttributes(list);
+        };
+        virtual ~TestTaskDummy(){
+            mLiveInstanceCounter--;
+        };
+
+        virtual TaskGeneric::ExecutionResult run()
+        {
+            mRunCounter++;
+            return TaskGeneric::run();
+        };
+        bool addStringAttributePublic(const android::String8& key, android::String8& value){
+            return addStringAttribute(key, value);
+        }
+        bool findStringAttributePublic(const android::String8& key, android::String8& value){
+            return findStringAttribute(key, value);
+        }
+    };
+
+    virtual void SetUp() {
+        TestTaskDummy::mRunCounter = 0;
+        TestTaskDummy::mLiveInstanceCounter = 0;
+        mTestCase = new TaskCase();
+        mTaskSetup = new TestTaskDummy(TaskGeneric::ETaskSetup);
+        mTaskAction = new TestTaskDummy(TaskGeneric::ETaskAction);
+        ASSERT_TRUE(mTestCase->addChild(mTaskSetup));
+        ASSERT_TRUE(mTestCase->addChild(mTaskAction));
+        mTaskSequential = new TestTaskDummy(TaskGeneric::ETaskSequential);
+        ASSERT_TRUE(mTaskAction->addChild(mTaskSequential));
+        mTaskProcess = new TestTaskDummy(TaskGeneric::ETaskProcess);
+        mTaskInput = new TestTaskDummy(TaskGeneric::ETaskInput);
+        mTaskOutput = new TestTaskDummy(TaskGeneric::ETaskOutput);
+        ASSERT_TRUE(mTaskSequential->addChild(mTaskOutput));
+        ASSERT_TRUE(mTaskSequential->addChild(mTaskInput));
+        ASSERT_TRUE(mTaskSequential->addChild(mTaskProcess));
+        mTaskSound = new TestTaskDummy(TaskGeneric::ETaskSound);
+        ASSERT_TRUE(mTaskSetup->addChild(mTaskSound));
+        ASSERT_TRUE(TestTaskDummy::mLiveInstanceCounter == 7);
+    }
+
+    virtual void TearDown() {
+        if(mTestCase != NULL) {
+            delete mTestCase;
+        }
+        ASSERT_TRUE(TestTaskDummy::mLiveInstanceCounter == 0);
+    }
+};
+
+int TaskTest::TestTaskDummy::mRunCounter = 0;
+int TaskTest::TestTaskDummy::mLiveInstanceCounter = 0;
+
+TEST_F(TaskTest, HierarchyTest) {
+    // verify hierarchy
+    ASSERT_TRUE(mTaskSetup->getTestCase() == mTestCase);
+    ASSERT_TRUE(mTaskAction->getTestCase() == mTestCase);
+    ASSERT_TRUE(mTaskSequential->getTestCase() == mTestCase);
+    ASSERT_TRUE(mTaskProcess->getTestCase() == mTestCase);
+    ASSERT_TRUE(mTaskInput->getTestCase() == mTestCase);
+    ASSERT_TRUE(mTaskOutput->getTestCase() == mTestCase);
+    ASSERT_TRUE(mTaskSound->getTestCase() == mTestCase);
+}
+
+TEST_F(TaskTest, RunTest) {
+    ASSERT_TRUE(mTestCase->run() == TaskGeneric::EResultOK);
+    ASSERT_TRUE(TestTaskDummy::mRunCounter == 7);
+}
+
+TEST_F(TaskTest, StringAttributeTest) {
+    android::String8 aaaVal("aaa_val");
+    android::String8 bbbVal("bbb_val");
+    android::String8 read;
+    TestTaskDummy* task = reinterpret_cast<TestTaskDummy*>(mTaskSetup);
+    ASSERT_TRUE(task->addStringAttributePublic(AAA, aaaVal));
+    ASSERT_TRUE(task->addStringAttributePublic(BBB, bbbVal));
+    const android::String8 CCC("ccc");
+    ASSERT_TRUE(!task->addStringAttributePublic(CCC, bbbVal));
+    ASSERT_TRUE(task->findStringAttributePublic(AAA, read));
+    ASSERT_TRUE(read == aaaVal);
+    ASSERT_TRUE(task->findStringAttributePublic(BBB, read));
+    ASSERT_TRUE(read == bbbVal);
+    const android::String8 VERSION("version");
+    const android::String8 NAME("name");
+    ASSERT_TRUE(!task->findStringAttributePublic(VERSION, read));
+    ASSERT_TRUE(!task->findStringAttributePublic(NAME, read));
+}
+
+
diff --git a/suite/audio_quality/test_description/all.xml b/suite/audio_quality/test_description/all.xml
new file mode 100644
index 0000000..d022230
--- /dev/null
+++ b/suite/audio_quality/test_description/all.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!-- Copyright (C) 2012 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.
+-->
+
+<batch name="cts_audio_all" version="1.0" description="All tests">
+	<include file="host_speaker_calibration.xml" />
+	<include file="dut_recording_thd.xml" />
+	<include file="dut_recording_spectrum.xml" />
+</batch>
diff --git a/suite/audio_quality/test_description/conf/check_conf.py b/suite/audio_quality/test_description/conf/check_conf.py
new file mode 100644
index 0000000..d7775af
--- /dev/null
+++ b/suite/audio_quality/test_description/conf/check_conf.py
@@ -0,0 +1,26 @@
+#!/usr/bin/python
+
+# Copyright (C) 2012 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.
+
+import sys
+import numpy as np
+import scipy as sp
+from numpy import *
+if __name__=="__main__":
+    if(sys.version_info < (2,6,0)):
+        print "Wrong Python version ", sys.version_info
+        sys.exit(1)
+    a = np.array([1,2,3])
+    print "___CTS_AUDIO_PASS___"
diff --git a/suite/audio_quality/test_description/conf/detect_usb_audio.py b/suite/audio_quality/test_description/conf/detect_usb_audio.py
new file mode 100755
index 0000000..259f7e1
--- /dev/null
+++ b/suite/audio_quality/test_description/conf/detect_usb_audio.py
@@ -0,0 +1,51 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2012 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.
+#
+
+#detect USB sound card from sound card lists under sys/class/sound/soundX
+
+import os, re, sys
+
+AUDIO_CLASS_DIR = "/sys/class/sound"
+
+def main(argv):
+  if len(argv) < 2:
+    print "Usage: detect_usb_audio.py (product)+"
+    print "   ex: detect_usb_audio.py MobilePre"
+    sys.exit(1)
+  current_argv = 1
+  product_list = []
+  while current_argv < len(argv):
+    product_list.append(argv[current_argv])
+    current_argv = current_argv + 1
+  #print product_list
+  sound_dev_list = os.listdir(AUDIO_CLASS_DIR)
+  for sound_dev in sound_dev_list:
+    m = re.search("card(\d+)$", sound_dev)
+    if m != None:
+      card_full_path = os.path.realpath(AUDIO_CLASS_DIR + "/" + sound_dev)
+      if "usb" in card_full_path:
+        f = open(card_full_path + "/id")
+        line = f.readline().strip()
+        if line in product_list:
+          print "___CTS_AUDIO_PASS___ " + line + " " + m.group(1)
+          sys.exit(0)
+        f.close()
+  # card not found
+  sys.exit(1)
+
+if __name__ == '__main__':
+  main(sys.argv)
diff --git a/suite/audio_quality/test_description/dut_recording_spectrum.xml b/suite/audio_quality/test_description/dut_recording_spectrum.xml
new file mode 100644
index 0000000..0f58deb
--- /dev/null
+++ b/suite/audio_quality/test_description/dut_recording_spectrum.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!-- Copyright (C) 2012 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.
+-->
+
+<case name="dut_recording_spectrum" version="1.0" description="Check frequency spectrum for recording">
+	<setup>
+		<!-- input: peak amplitude, duration in msec, sampling rate, low frequency, high frequency, output: generated sound-->
+		<process method="script:gen_random" input="consti:10000,consti:30000,consti:44100,consti:50,consti:6000" output="id:sound1" />
+		<!-- sound id="sound1" type="random:10000:30000" / -->
+		<!--  Only for starting client app early. The data is not used -->
+		<sound id="sound2" type="sin:1:1000:2" preload="1"/>
+	</setup>
+	<action>
+		<sequential repeat="1" index="i">
+			<output device="host" id="sound1" gain="100" sync="start" waitforcompletion="0" />
+			<sequential repeat="1" index="j">
+				<input device="host" id="host_in_$j" gain="100" time="6000" sync="start" />
+				<input device="DUT" id="dut_in_$j" gain="100" time="4000" sync="start" />
+			</sequential>
+		</sequential>
+		<sequential repeat="1" index="k">
+			<!-- input: host record, device record, samping rate, low frequency in Hz, high frequency in Hz, allowed error for pass in smaller side, allowed error in bigger side%, output: min value in lower side calculated normalized to 1.0, max value in higher side, calculated TF in mannitude only between low f to high f -->
+			<process method="script:check_spectrum" input="id:host_in_$k,id:dut_in_$k,consti:44100,consti:200,consti:4000,constf:50.0,constf:100.0" output="val:min_val_$k,val:max_val_$k,id:tf_$k" />
+		</sequential>
+	</action>
+	<save file="sound1,host_in_.*,dut_in_.*,tf_.*" report="min_val_.*,max_val_.*" />
+</case>
diff --git a/suite/audio_quality/test_description/dut_recording_thd.xml b/suite/audio_quality/test_description/dut_recording_thd.xml
new file mode 100644
index 0000000..38986e3
--- /dev/null
+++ b/suite/audio_quality/test_description/dut_recording_thd.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!-- Copyright (C) 2012 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.
+-->
+
+<case name="dut_recording_thd" version="1.0" description="Check THD in DUT's recording side">
+	<setup>
+		<!-- prepare sound source id: to be used in output, sine 1000Hz, 40000ms long -->
+		<sound id="sound1" type="sin:32000:1000:40000" />
+		<!--  Only for starting client app early. The data is not used -->
+		<sound id="sound2" type="sin:1:1000:2" preload="1"/>
+	</setup>
+	<action>
+		<sequential repeat="1" index="i">
+			<output device="host" id="sound1" gain="100" sync="start" waitforcompletion="0" />
+			<sequential repeat="5" index="j">
+				<input device="host" id="host_in_$j" gain="100" time="4000" sync="start" />
+				<input device="DUT" id="dut_in_$j" gain="100" time="2000" sync="start" />
+			</sequential>
+		</sequential>
+		<sequential repeat="5" index="k">
+			<!-- input: host record, device record, signal frequency in Hz, THD for pass in percentile, output: THD calculated -->
+			<process method="script:recording_thd" input="id:host_in_$k,id:dut_in_$k,consti:1000,constf:0.1" output="val:thd_host_$k,val:thd_device_$k" />
+		</sequential>
+	</action>
+	<save file="host_in_.*,dut_in_.*" report="thd_.*" />
+</case>
diff --git a/suite/audio_quality/test_description/host_speaker_calibration.xml b/suite/audio_quality/test_description/host_speaker_calibration.xml
new file mode 100644
index 0000000..0bdd5a1
--- /dev/null
+++ b/suite/audio_quality/test_description/host_speaker_calibration.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!-- Copyright (C) 2012 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.
+-->
+
+<case name="host_speaker_calibration" version="1.0" description="Calibrate host speaker's volume.">
+	<setup> <!-- 1 setup -->
+		<!-- prepare sound source id: to be used in output, sine 1000Hz, 20000ms long -->
+		<sound id="sound1" type="sin:32000:1000:20000" />
+	</setup>
+
+	<action> <!-- 1 action -->
+		<!--  equivalent of for loop. all children will be completed before moving to the next 
+		      stage.repeat up to 100 times unless stopped by some condition -->
+		<sequential repeat="20" index="i">
+			<!--  sync start : execute only sync complete : execute + complete
+			      For sync start, complete will be called when the parent completes -->
+			<output device="host" id="sound1" gain="70" sync="start"/>
+			<sequential repeat="10" index="j">
+				<input device="host" id="host_in" gain="70" time="500" sync="complete" />
+				<!-- ------------moving average RMS        min for pass, max for pass                result calculated -->
+				<process method="builtin:rms_mva" input="id:host_in,consti:4000,consti:5000" output="val:rms_$i_$j" />
+				<!-- <message input="val:passfail" output_low="Volume Low" output_ok="Volume OK" output_high="Volume High" /> -->
+			</sequential>
+		</sequential>
+	</action>
+	<save file="host_in" report="rms_.*" />
+</case>
diff --git a/suite/audio_quality/test_description/host_speaker_calibration_no_pass.xml b/suite/audio_quality/test_description/host_speaker_calibration_no_pass.xml
new file mode 100644
index 0000000..c2853af
--- /dev/null
+++ b/suite/audio_quality/test_description/host_speaker_calibration_no_pass.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!-- Copyright (C) 2012 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.
+-->
+
+<case name="host_speaker_calibration" version="1.0" description="Calibrate host speaker's volume.">
+	<setup> <!-- 1 setup -->
+		<!-- prepare sound source id: to be used in output, sine 1000Hz, 20000ms long -->
+		<sound id="sound1" type="sin:32000:1000:20000" />
+	</setup>
+
+	<action> <!-- 1 action -->
+		<!--  equivalent of for loop. all children will be completed before moving to the next 
+		      stage.repeat up to 100 times unless stopped by some condition -->
+		<sequential repeat="20" index="i">
+			<!--  sync start : execute only sync complete : execute + complete
+			      For sync start, complete will be called when the parent completes -->
+			<output device="host" id="sound1" gain="70" sync="start"/>
+			<sequential repeat="10" index="j">
+				<input device="host" id="host_in" gain="70" time="500" sync="complete" />
+				<!-- ------------moving average RMS        min for pass, max for pass                result calculated -->
+				<process method="builtin:rms_mva" input="id:host_in,consti:2,consti:1" output="val:rms_$i_$j" />
+				<!-- <message input="val:passfail" output_low="Volume Low" output_ok="Volume OK" output_high="Volume High" /> -->
+			</sequential>
+		</sequential>
+	</action>
+	<save file="host_in" report="rms_.*" />
+</case>
diff --git a/suite/audio_quality/test_description/processing/calc_delay.py b/suite/audio_quality/test_description/processing/calc_delay.py
new file mode 100644
index 0000000..6e63d28
--- /dev/null
+++ b/suite/audio_quality/test_description/processing/calc_delay.py
@@ -0,0 +1,77 @@
+#!/usr/bin/python
+
+# Copyright (C) 2012 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.
+
+import numpy as np
+import numpy.linalg
+import scipy as sp
+import scipy.fftpack
+import scipy.signal
+import math
+import sys
+from multiprocessing import Pool
+
+def convolution(data0, data1reversed, n):
+    """calculate convolution part of data0 with data1 from pos n"""
+    N = len(data1reversed)
+    return np.dot(data0[n:N+n], data1reversed)
+
+
+def convolutionstar(args):
+    return convolution(*args)
+
+def calc_delay(data0, data1):
+    """Calcuate delay between two data. data0 is assumed to be recorded first,
+       and will have longer length than data1
+       returns delay between data0 and data1 in number of samples in data0's point of view"""
+
+    len0 = len(data0)
+    len1 = len(data1)
+    if len1 > len0:
+        print "data1 longer than data0"
+        return -1
+    searchLen = len0 - len1
+    data1reverse = data1[::-1]
+
+    # This is faster than signal.correlate as there is no need to process
+    # full data, but still it is slow. about 18 secs for data0 of 4 secs with data1 of 1 secs
+    print "***Caluclating delay, may take some time***"
+    gData0 = data0
+    gData1 = data1reverse
+    pool = Pool(processes = 4)
+    TASK = [(data0, data1reverse, i) for i in range(searchLen)]
+    result = pool.map(convolutionstar, TASK)
+
+    return np.argmax(result)
+
+
+# test code
+if __name__=="__main__":
+    samplingRate = 44100
+    durationInSec = 0.001
+    if len(sys.argv) > 1:
+        durationInSec = float(sys.argv[1])
+    signalFrequency = 1000
+    samples = float(samplingRate) * float(durationInSec)
+    index = np.linspace(0.0, samples, num=samples, endpoint=False)
+    time = index / samplingRate
+    multiplier = 2.0 * np.pi * signalFrequency / float(samplingRate)
+    data0 = np.sin(index * multiplier)
+    DELAY = durationInSec / 2.0 * samplingRate
+    data1 = data0[DELAY:]
+    delay = calc_delay(data0, data1)
+    print "calc_delay returned", delay, " while expecting ", DELAY
+
+
diff --git a/suite/audio_quality/test_description/processing/calc_thd.py b/suite/audio_quality/test_description/processing/calc_thd.py
new file mode 100644
index 0000000..7c96c77
--- /dev/null
+++ b/suite/audio_quality/test_description/processing/calc_thd.py
@@ -0,0 +1,59 @@
+#!/usr/bin/python
+
+# Copyright (C) 2012 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.
+
+import numpy as np
+import scipy as sp
+import scipy.fftpack as fft
+import scipy.linalg as la
+import math
+
+def calc_thd(data, signalFrequency, samplingRate, frequencyMargin):
+    # only care about magnitude
+    fftData = abs(fft.fft(data * np.hanning(len(data))))
+    fftData[0] = 0 # ignore DC
+    fftLen = len(fftData)/2
+    baseI = fftLen * signalFrequency * 2 / samplingRate
+    iMargain = baseI * frequencyMargin
+    baseSignalLoc = baseI - iMargain / 2 + \
+        np.argmax(fftData[baseI - iMargain /2: baseI + iMargain/2])
+    peakLoc = np.argmax(fftData[:fftLen])
+    if peakLoc != baseSignalLoc:
+        print "**ERROR Wrong peak signal", peakLoc, baseSignalLoc
+        return 1.0
+    print baseI, baseSignalLoc
+    P0 = math.pow(la.norm(fftData[baseSignalLoc - iMargain/2: baseSignalLoc + iMargain/2]), 2)
+    i = baseSignalLoc * 2
+    Pothers = 0.0
+    while i < fftLen:
+        Pothers += math.pow(la.norm(fftData[i - iMargain/2: i + iMargain/2]), 2)
+        i += baseSignalLoc
+    print "P0", P0, "Pothers", Pothers
+
+    return Pothers / P0
+
+# test code
+if __name__=="__main__":
+    samplingRate = 44100
+    durationInSec = 10
+    signalFrequency = 1000
+    samples = float(samplingRate) * float(durationInSec)
+    index = np.linspace(0.0, samples, num=samples, endpoint=False)
+    time = index / samplingRate
+    multiplier = 2.0 * np.pi * signalFrequency / float(samplingRate)
+    data = np.sin(index * multiplier)
+    thd = calc_thd(data, signalFrequency, samplingRate, 0.02)
+    print "THD", thd
+
diff --git a/suite/audio_quality/test_description/processing/check_spectrum.py b/suite/audio_quality/test_description/processing/check_spectrum.py
new file mode 100644
index 0000000..31b5a5e
--- /dev/null
+++ b/suite/audio_quality/test_description/processing/check_spectrum.py
@@ -0,0 +1,134 @@
+#!/usr/bin/python
+
+# Copyright (C) 2012 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.
+
+from consts import *
+import numpy as np
+import scipy as sp
+import scipy.fftpack as fft
+import matplotlib.pyplot as plt
+import sys
+sys.path.append(sys.path[0])
+import calc_delay
+
+# check if Transfer Function of DUT / Host signal
+#  lies in the given error boundary
+# input: host record
+#        device record,
+#        sampling rate
+#        low frequency in Hz,
+#        high frequency in Hz,
+#        allowed error in negative side for pass in %,
+#        allowed error ih positive side for pass
+# output: min value in negative side, normalized to 1.0
+#         max value in positive side
+#         calculated TF in magnitude (DUT / Host)
+
+def do_check_spectrum(hostData, DUTData, samplingRate, fLow, fHigh, margainLow, margainHigh):
+    # reduce FFT resolution to have averaging effects
+    N = 512 if (len(hostData) > 512) else len(hostData)
+    iLow = N * fLow / samplingRate
+    if iLow > (N / 2 - 1):
+        iLow = (N / 2 - 1)
+    iHigh = N * fHigh / samplingRate
+    if iHigh > (N / 2):
+        iHigh = N / 2
+    print fLow, iLow, fHigh, iHigh, samplingRate
+    hostFFT = abs(fft.fft(hostData, n = N))[iLow:iHigh]
+    dutFFT = abs(fft.fft(DUTData, n = N))[iLow:iHigh]
+    TF = dutFFT / hostFFT
+    TFmean = sum(TF) / len(TF)
+    TF = TF / TFmean # TF normalized to 1
+    positiveMax = abs(max(TF))
+    negativeMin = abs(min(TF))
+    passFail = True if (positiveMax < (margainHigh / 100.0 + 1.0)) and\
+        ((1.0 - negativeMin) < margainLow / 100.0) else False
+    TFResult = np.zeros(len(TF), dtype=np.int16)
+    for i in range(len(TF)):
+        TFResult[i] = TF[i] * 256 # make fixed point
+    #freq = np.linspace(0.0, fHigh, num=iHigh, endpoint=False)
+    #plt.plot(freq, abs(fft.fft(hostData, n = N))[:iHigh], freq, abs(fft.fft(DUTData, n = N))[:iHigh])
+    #plt.show()
+    print "positiveMax", positiveMax, "negativeMin", negativeMin
+    return (passFail, negativeMin, positiveMax, TFResult)
+
+def check_spectrum(inputData, inputTypes):
+    output = []
+    outputData = []
+    outputTypes = []
+    # basic sanity check
+    inputError = False
+    if (inputTypes[0] != TYPE_MONO):
+        inputError = True
+    if (inputTypes[1] != TYPE_MONO):
+        inputError = True
+    if (inputTypes[2] != TYPE_I64):
+        inputError = True
+    if (inputTypes[3] != TYPE_I64):
+        inputError = True
+    if (inputTypes[4] != TYPE_I64):
+        inputError = True
+    if (inputTypes[5] != TYPE_DOUBLE):
+        inputError = True
+    if (inputTypes[6] != TYPE_DOUBLE):
+        inputError = True
+    if inputError:
+        output.append(RESULT_ERROR)
+        output.append(outputData)
+        output.append(outputTypes)
+        return output
+    hostData = inputData[0]
+    dutData = inputData[1]
+    samplingRate = inputData[2]
+    fLow = inputData[3]
+    fHigh = inputData[4]
+    margainLow = inputData[5]
+    margainHigh = inputData[6]
+    delay = calc_delay.calc_delay(hostData, dutData)
+    N = len(dutData)
+    print "delay ", delay, "deviceRecording samples ", N
+    (passFail, minError, maxError, TF) = do_check_spectrum(hostData[delay:delay+N], dutData,\
+        samplingRate, fLow, fHigh, margainLow, margainHigh)
+
+    if passFail:
+        output.append(RESULT_PASS)
+    else:
+        output.append(RESULT_OK)
+    outputData.append(minError)
+    outputTypes.append(TYPE_DOUBLE)
+    outputData.append(maxError)
+    outputTypes.append(TYPE_DOUBLE)
+    outputData.append(TF)
+    outputTypes.append(TYPE_MONO)
+    output.append(outputData)
+    output.append(outputTypes)
+    return output
+
+# test code
+if __name__=="__main__":
+    sys.path.append(sys.path[0])
+    mod = __import__("gen_random")
+    peakAmpl = 10000
+    durationInMSec = 1000
+    samplingRate = 44100
+    fLow = 500
+    fHigh = 15000
+    data = getattr(mod, "do_gen_random")(peakAmpl, durationInMSec, samplingRate, fLow, fHigh,\
+                                         stereo=False)
+    print len(data)
+    (passFail, minVal, maxVal, TF) = do_check_spectrum(data, data, samplingRate, fLow, fHigh,\
+                                                           1.0, 1.0)
+    plt.plot(TF)
+    plt.show()
diff --git a/suite/audio_quality/test_description/processing/consts.py b/suite/audio_quality/test_description/processing/consts.py
new file mode 100644
index 0000000..3b643a9
--- /dev/null
+++ b/suite/audio_quality/test_description/processing/consts.py
@@ -0,0 +1,31 @@
+#!/usr/bin/python
+
+# Copyright (C) 2012 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.
+
+# consts to be used in signal processing functions
+
+# types of data for input / output
+TYPE_I64    = 0
+TYPE_DOUBLE = 1
+TYPE_MONO   = 2
+TYPE_STEREO = 3
+
+# result for the processing
+RESULT_OK = 0
+RESULT_CONTINUE = 1
+RESULT_BREAKONELOOP = 2
+RESULT_ERROR = 3
+RESULT_FAIL = 4
+RESULT_PASS = 5
diff --git a/suite/audio_quality/test_description/processing/example.py b/suite/audio_quality/test_description/processing/example.py
new file mode 100644
index 0000000..cce697d
--- /dev/null
+++ b/suite/audio_quality/test_description/processing/example.py
@@ -0,0 +1,64 @@
+#!/usr/bin/python
+
+# Copyright (C) 2012 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.
+
+from consts import *
+import numpy as np
+import scipy as sp
+
+# Example python script for signal processing in CTS audio
+# There should be a function with the same name as the script
+# Here, function example in example.py
+
+# inputData : list of inputs with different types like int64, double,
+#             mono or stereo audio data
+# inputTypes: list of types for each input. Types are defined as TYPE_XXX
+#             consts from consts.py
+# return value: 3 elements list
+#     element 0 : execution result value as defined as RESULT_XXX in consts.py
+#     element 1 : outputData
+#     element 2 : outputTypes
+#
+# This example takes 2 stereo data, 2 mono data, 2 i64, and 2 doubles
+# and returns average as 1 stereo data, 1 mono data, 1 i64, and 1 double
+# inputTypes for this function is expected to be
+#   [ TYPE_STEREO, TYPE_STEREO, TYPE_MONO, TYPE_MONO, TYPE_I64, TYPE_I64,
+#     TYPE_DOUBLE, TYPE_DOUBLE ]
+# outputTypes will be [ TYPE_STEREO, TYPE_MONO, TYPE_I64, TYPE_DOUBLE ]
+def example(inputData, inputTypes):
+    output = []
+    outputData = []
+    outputTypes = []
+    stereoInt = (inputData[0].astype(int) + inputData[1].astype(int))/2
+    stereo = stereoInt.astype(np.int16)
+    #print len(inputData[0]), len(inputData[1]), len(stereoInt), len(stereo)
+    monoInt = (inputData[2].astype(int) + inputData[3].astype(int))/2
+    mono = monoInt.astype(np.int16)
+    #print len(inputData[2]), len(inputData[3]), len(monoInt), len(mono)
+    i64Val = (inputData[4] + inputData[5])/2
+    doubleVal = (inputData[6] + inputData[7])/2
+    outputData.append(stereo)
+    outputTypes.append(TYPE_STEREO)
+    outputData.append(mono)
+    outputTypes.append(TYPE_MONO)
+    outputData.append(i64Val)
+    outputTypes.append(TYPE_I64)
+    outputData.append(doubleVal)
+    outputTypes.append(TYPE_DOUBLE)
+    output.append(RESULT_OK)
+    output.append(outputData)
+    output.append(outputTypes)
+
+    return output
diff --git a/suite/audio_quality/test_description/processing/gen_random.py b/suite/audio_quality/test_description/processing/gen_random.py
new file mode 100644
index 0000000..541c60a
--- /dev/null
+++ b/suite/audio_quality/test_description/processing/gen_random.py
@@ -0,0 +1,110 @@
+#!/usr/bin/python
+
+# Copyright (C) 2012 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.
+
+from consts import *
+import numpy as np
+import scipy as sp
+import scipy.fftpack as fft
+import matplotlib.pyplot as plt
+
+# generate random signal
+# Input: peak amplitude,
+#        duration in msec,
+#        sampling rate HZ
+#        low frequency,
+#        high frequency,
+# Output: generated sound (stereo)
+
+def do_gen_random(peakAmpl, durationInMSec, samplingRate, fLow, fHigh, stereo=True):
+    samples = durationInMSec * samplingRate / 1000
+    result = np.zeros(samples * 2 if stereo else samples, dtype=np.int16)
+    #randomSignal = np.random.random_integers(-peakAmpl, peakAmpl, samples)
+    randomSignal = np.random.normal(scale=peakAmpl *2 / 3, size=samples)
+    fftData = fft.rfft(randomSignal)
+    freqSamples = samples/2
+    iLow = 0 #freqSamples * fLow * 2/ samplingRate + 1
+    #if iLow > freqSamples - 1:
+    #    iLow = freqSamples - 1
+    iHigh = freqSamples * fHigh * 2 / samplingRate + 1
+    #print len(randomSignal), len(fftData), fLow, iLow, fHigh, iHigh
+    if iHigh > freqSamples - 1:
+        iHigh = freqSamples - 1
+    fftData[0] = 0 # DC
+    #for i in range(iLow - 1):
+    #    fftData[2 * i + 1 ] = 0 # Re
+    #    fftData[2 * i + 2 ] = 0 # Imag
+    for i in range(iHigh, freqSamples - 1):
+        fftData[ 2 * i + 1 ] = 0
+        fftData[ 2 * i + 2 ] = 0
+    if (samples - 2 *freqSamples) != 0:
+        fftData[samples - 1] = 0
+
+    filteredData = fft.irfft(fftData)
+    #freq = np.linspace(0.0, samplingRate, num=len(fftData), endpoint=False)
+    #plt.plot(freq, abs(fft.fft(filteredData)))
+    #plt.plot(filteredData)
+    #plt.show()
+    if stereo:
+        for i in range(len(filteredData)):
+            result[2 * i] = filteredData[i]
+            result[2 * i + 1] = filteredData[i]
+    else:
+        for i in range(len(filteredData)):
+            result[i] = filteredData[i]
+    return result
+
+
+def gen_random(inputData, inputTypes):
+    output = []
+    outputData = []
+    outputTypes = []
+    # basic sanity check
+    inputError = False
+    if (inputTypes[0] != TYPE_I64):
+        inputError = True
+    if (inputTypes[1] != TYPE_I64):
+        inputError = True
+    if (inputTypes[2] != TYPE_I64):
+        inputError = True
+    if (inputTypes[3] != TYPE_I64):
+        inputError = True
+    if (inputTypes[4] != TYPE_I64):
+        inputError = True
+    if inputError:
+        output.append(RESULT_ERROR)
+        output.append(outputData)
+        output.append(outputTypes)
+        return output
+
+    result = do_gen_random(inputData[0], inputData[1], inputData[2], inputData[3], inputData[4])
+
+    output.append(RESULT_OK)
+    outputData.append(result)
+    outputTypes.append(TYPE_STEREO)
+    output.append(outputData)
+    output.append(outputTypes)
+    return output
+
+# test code
+if __name__=="__main__":
+    peakAmplitude = 10000
+    samplingRate = 44100
+    durationInMSec = 10000
+    fLow = 500
+    fHigh = 15000
+    result = do_gen_random(peakAmplitude, durationInMSec, samplingRate, fLow, fHigh)
+    plt.plot(result)
+    plt.show()
diff --git a/suite/audio_quality/test_description/processing/recording_thd.py b/suite/audio_quality/test_description/processing/recording_thd.py
new file mode 100644
index 0000000..760fac0
--- /dev/null
+++ b/suite/audio_quality/test_description/processing/recording_thd.py
@@ -0,0 +1,78 @@
+#!/usr/bin/python
+
+# Copyright (C) 2012 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.
+
+from consts import *
+import numpy as np
+import scipy as sp
+from calc_thd import *
+import calc_delay
+
+# calculate THD for dut_recording_thd case
+# Input: host recording (mono), device recording (mono),
+#        frequency of sine in Hz (i64)
+#        THD pass level in percentile (double)
+# Output:THD host (double), THD device (double) in percentile
+# host recording will be longer than device recording
+# the function works in following steps:
+# 1. match the start of device recording with host recording
+#    As the host recording starts eariler and longer than device recording,
+#    matching process is required.
+# 2. calculate THD of host recording and client recording
+# 3. check pass/fail
+
+def recording_thd(inputData, inputTypes):
+    output = []
+    outputData = []
+    outputTypes = []
+    # basic sanity check
+    inputError = False
+    if (inputTypes[0] != TYPE_MONO):
+        inputError = True
+    if (inputTypes[1] != TYPE_MONO):
+        inputError = True
+    if (inputTypes[2] != TYPE_I64):
+        inputError = True
+    if (inputTypes[3] != TYPE_DOUBLE):
+        inputError = True
+    if inputError:
+        output.append(RESULT_ERROR)
+        output.append(outputData)
+        output.append(outputTypes)
+        return output
+
+    hostRecording = inputData[0]
+    deviceRecording = inputData[1]
+    signalFrequency = inputData[2]
+    thdPassPercentile = inputData[3]
+    samplingRate = 44100
+
+    delay = calc_delay.calc_delay(hostRecording, deviceRecording)
+    N = len(deviceRecording)
+    print "delay ", delay, "deviceRecording samples ", N
+    thdHost = calc_thd(hostRecording[delay:delay+N], signalFrequency, samplingRate, 0.02) * 100
+    thdDevice = calc_thd(deviceRecording, signalFrequency, samplingRate, 0.02) * 100
+    print "THD Host %", thdHost, "THD device %", thdDevice, "Margain % ", thdPassPercentile
+    if (thdDevice < (thdHost + thdPassPercentile)) and (thdDevice < 10.0):
+        output.append(RESULT_PASS)
+    else:
+        output.append(RESULT_OK)
+    outputData.append(thdHost)
+    outputTypes.append(TYPE_DOUBLE)
+    outputData.append(thdDevice)
+    outputTypes.append(TYPE_DOUBLE)
+    output.append(outputData)
+    output.append(outputTypes)
+    return output
diff --git a/suite/audio_quality/test_description/processing_main.py b/suite/audio_quality/test_description/processing_main.py
new file mode 100644
index 0000000..b5075fd
--- /dev/null
+++ b/suite/audio_quality/test_description/processing_main.py
@@ -0,0 +1,219 @@
+#!/usr/bin/python
+
+# Copyright (C) 2012 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.
+
+import sys
+import numpy as np
+import scipy as sp
+import socket
+import struct
+sys.path.append(sys.path[0] + "/processing")
+from consts import *
+
+builtinFunctions = [
+    "echo", # send back whatever is received
+    "intsum", # returns int64 + int64
+]
+
+CMD_HEADER    = 0x0
+CMD_TERMINATE = 0x1
+CMD_FUNCTION  = 0x2
+CMD_AUDIO_MONO = 0x4
+CMD_AUDIO_STEREO = 0x5
+CMD_INT64 = 0x8
+CMD_DOUBLE = 0x9
+CMD_RESULT = 0x10
+
+def echo(inputData, inputTypes):
+    output = []
+    print "echo received ", inputData
+    output.append(RESULT_OK)
+    output.append(inputData)
+    output.append(inputTypes)
+    return output
+
+def intsum(inputData, inputTypes):
+    output = []
+    output.append(RESULT_OK)
+    sum = inputData[0] + inputData[1]
+    print "intsum sum is ", sum
+    outputData = []
+    outputData.append(sum)
+    outputTypes = []
+    outputTypes.append(TYPE_I64)
+    output.append(outputData)
+    output.append(outputTypes)
+    return output
+
+
+class CommandHandler(object):
+
+    def __init__(self, conn):
+        self.conn = conn
+    def __del__(self):
+        self.conn.close()
+    def run(self):
+        header = self.readI32()
+        if header == CMD_TERMINATE:
+            print "terminate cmd, will exit"
+            sys.exit(0)
+        nParam = 0
+        if header == CMD_HEADER:
+            nParam = self.readI32()
+            if nParam < 1:
+                protocolError("wrong number of params")
+            cmdFunction = self.readI32()
+            if cmdFunction != CMD_FUNCTION:
+                protocolError("not function")
+            nameLen = self.readI32()
+            self.functionName = self.readRaw(nameLen)
+            print "Processing function:", self.functionName
+            inputData = []
+            inputTypes = []
+            for i in range(nParam - 1):
+                cmd = self.readI32()
+                if (cmd == CMD_AUDIO_STEREO) or (cmd == CMD_AUDIO_MONO):
+                    dataLen = self.readI32()
+                    data = self.readI16Array(dataLen / 2)
+                    inputData.append(data)
+                    if (cmd == CMD_AUDIO_STEREO):
+                        inputTypes.append(TYPE_STEREO)
+                    else:
+                        inputTypes.append(TYPE_MONO)
+                    print i, "-th input received audio data ", dataLen, cmd
+                elif cmd == CMD_INT64:
+                    i64 = self.readI64()
+                    inputData.append(i64)
+                    inputTypes.append(TYPE_I64)
+                elif cmd == CMD_DOUBLE:
+                    val = self.readDouble()
+                    inputData.append(val)
+                    inputTypes.append(TYPE_DOUBLE)
+                else:
+                    self.protocolError("unknown command " + str(cmd))
+            print "inputTypes ", inputTypes
+            # length 3 list
+            # output[0]: int, execution result, RESULT_XXX values
+            # output[1]: output data list
+            # output[2]: output type list
+            output = []
+            if not self.functionName in builtinFunctions:
+                mod = __import__(self.functionName)
+                output = getattr(mod, self.functionName)(inputData, inputTypes)
+            else:
+                output = globals()[self.functionName](inputData, inputTypes)
+            nOutputParams = len(output[1])
+            self.sendI32(CMD_HEADER)
+            self.sendI32(nOutputParams + 1) # 1 for result
+            self.sendI32(CMD_RESULT)
+            self.sendI32(output[0])
+            outputData = output[1]
+            outputTypes = output[2]
+            print "outputTypes ", outputTypes
+            for i in range(nOutputParams):
+                if (outputTypes[i] == TYPE_I64):
+                    self.sendI32(CMD_INT64)
+                    self.sendI64(outputData[i])
+                elif (outputTypes[i] == TYPE_DOUBLE):
+                    self.sendI32(CMD_DOUBLE)
+                    self.sendDouble(outputData[i])
+                elif (outputTypes[i] == TYPE_STEREO):
+                    self.sendI32(CMD_AUDIO_STEREO)
+                    self.sendI32(len(outputData[i]) * 2)
+                    self.sendI16Array(outputData[i])
+                elif (outputTypes[i] == TYPE_MONO):
+                    self.sendI32(CMD_AUDIO_MONO)
+                    self.sendI32(len(outputData[i]) * 2)
+                    self.sendI16Array(outputData[i])
+                else:
+                    print "unknown type ", outputTypes[i], \
+                        " returned from funcion ", self.functionName
+                    sys.exit(1)
+
+    def readRaw(self, length):
+        result = []
+        totalRead = 0
+        while totalRead < length:
+            raw = self.conn.recv(length - totalRead)
+            justRead = len(raw)
+            if justRead == 0: # socket closed
+                sys.exit(1)
+            totalRead += justRead
+            result.append(raw)
+        return ''.join(result)
+
+    def readI32(self):
+        raw = self.readRaw(4)
+        i32 = struct.unpack("<i", raw)
+        return i32[0]
+
+    def readI64(self):
+        raw = self.readRaw(8)
+        i64 = struct.unpack("<q", raw)
+        return i64[0]
+
+    def readDouble(self):
+        raw = self.readRaw(8)
+        val = struct.unpack("<d", raw)
+        return val[0]
+
+    def readI16Array(self, length):
+        raw = self.readRaw(length * 2)
+        data = np.fromstring(raw, dtype=np.int16)
+        return data
+
+    def sendI32(self, i32):
+        raw = struct.pack("<i", i32)
+        self.sendRaw(raw)
+
+    def sendI64(self, i64):
+        raw = struct.pack("<q", i64)
+        self.sendRaw(raw)
+
+    def sendDouble(self, val):
+        raw = struct.pack("<d", val)
+        self.sendRaw(raw)
+
+    def sendI16Array(self, arry):
+        raw = arry.tostring()
+        self.sendRaw(raw)
+
+    def sendRaw(self, rawString):
+        totalSent = 0
+        stringLen = len(rawString)
+        while totalSent < stringLen:
+            sent = self.conn.send(rawString[totalSent:])
+            totalSent += sent
+
+    def protocolError(self, message):
+        print message
+        sys.exit(1)
+
+
+if __name__=="__main__":
+    HOST = "localhost"
+    PORT = 15010
+    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+    s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+    s.bind((HOST, PORT))
+    s.listen(1)
+
+    conn, addr = s.accept()
+    print "client connected"
+    # close the server socket to allow other instance to run
+    s.close()
+    handler = CommandHandler(conn)
+    while 1:
+        handler.run()
diff --git a/suite/audio_quality/test_description/test/missing_mandatory.xml b/suite/audio_quality/test_description/test/missing_mandatory.xml
new file mode 100644
index 0000000..d245a2f
--- /dev/null
+++ b/suite/audio_quality/test_description/test/missing_mandatory.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!-- Copyright (C) 2012 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.
+-->
+
+<case name="host_speaker_calibration" version="1.0" description="Calibrate host speaker's volume.">
+	<action>
+		<sequential repeat="100" index="i">
+			<output device="host" id="sound1" gain="70" sync="start"/>
+			<sequential repeat="20" index=j>
+				<input device="host" id="host_in" gain="70" time="1000" sync="complete" />
+				<process method="builtin:rms_passfail" input="id:host_in" output="val:rms_$i_$j,val:passfail" />
+				<message input="val:passfail" output_low="Volume Low" output_ok="Volume OK" output_high="Volume High" />
+			</sequential>
+		</sequential>
+	</action>
+	<save file="host_in" report="rms_.*" />
+</case>
\ No newline at end of file
diff --git a/suite/audio_quality/test_description/test/no_attrib.xml b/suite/audio_quality/test_description/test/no_attrib.xml
new file mode 100644
index 0000000..a5aba05
--- /dev/null
+++ b/suite/audio_quality/test_description/test/no_attrib.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!-- Copyright (C) 2012 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.
+-->
+
+<case>
+	<setup>
+		<sound />
+	</setup>
+
+	<action>
+		<sequential>
+			<output />
+			<sequential >
+				<input  />
+				<process  />
+				<message  />
+			</sequential>
+		</sequential>
+	</action>
+	<save  />
+</case>
\ No newline at end of file
diff --git a/suite/audio_quality/test_description/test/short1.r2s b/suite/audio_quality/test_description/test/short1.r2s
new file mode 100644
index 0000000..78a68a8
--- /dev/null
+++ b/suite/audio_quality/test_description/test/short1.r2s
Binary files differ
diff --git a/suite/audio_quality/test_description/test/test_io.xml b/suite/audio_quality/test_description/test/test_io.xml
new file mode 100644
index 0000000..60aac3f
--- /dev/null
+++ b/suite/audio_quality/test_description/test/test_io.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!-- Copyright (C) 2012 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.
+-->
+
+<case name="test_io" version="1.0" description="Test Input / Output">
+	<setup> <!-- 1 setup -->
+		<!-- prepare sound source id: to be used in output, sine 1000Hz, 1000ms long -->
+		<sound id="sound1" type="sin:10000:1000:1000" preload="1" />
+	</setup>
+
+	<action> <!-- 1 action -->
+		<!--  equivalent of for loop. all children will be completed before moving to the next 
+		      stage.repeat up to 100 times unless stopped by some condition -->
+		<sequential repeat="1" index="i">
+			<output device="host" id="sound1" gain="100" sync="start"/>
+			<output device="DUT" id="sound1" gain="100" sync="start"/>
+			<input device="host" id="host1" gain="100" time="500" sync="start"/>
+			<input device="DUT" id="device1" gain="100" time="500" sync="start"/>
+		</sequential>
+		<sequential repeat="1" index="i2">
+		    <process method="builtin:rms_mva" input="id:host1,consti:0,consti:0" output="val:rms_host" />
+		    <process method="builtin:rms_mva" input="id:device1,consti:0,consti:20000" output="val:rms_device" />
+		</sequential>
+	</action>
+	<save  file="host1,device1" report="rms_.*"/>
+</case>
diff --git a/suite/audio_quality/test_description/test/test_rms_vma.xml b/suite/audio_quality/test_description/test/test_rms_vma.xml
new file mode 100644
index 0000000..163ddad
--- /dev/null
+++ b/suite/audio_quality/test_description/test/test_rms_vma.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!-- Copyright (C) 2012 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.
+-->
+
+<case name="test_rms_vma" version="1.0" description="Test built-in rms_vma">
+	<setup> <!-- 1 setup -->
+		<!-- prepare sound source id: to be used in output, sine 1000Hz, 20000ms long -->
+		<sound id="sound1" type="sin:100:1000:20000" />
+	</setup>
+
+	<action> <!-- 1 action -->
+		<!--  equivalent of for loop. all children will be completed before moving to the next 
+		      stage.repeat up to 100 times unless stopped by some condition -->
+		<sequential repeat="10" index="i">		      
+			<sequential repeat="10" index="j">
+				<process method="builtin:rms_mva" input="id:sound1,consti:1000,consti:2000" output="val:rms_$i_$j" />
+			</sequential>
+		</sequential>
+	</action>
+	<save  file="sound1" report="rms_.*"/>
+</case>
\ No newline at end of file
diff --git a/suite/audio_quality/test_description/test/unknown_element.xml b/suite/audio_quality/test_description/test/unknown_element.xml
new file mode 100644
index 0000000..ae5eaa1
--- /dev/null
+++ b/suite/audio_quality/test_description/test/unknown_element.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!-- Copyright (C) 2012 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.
+-->
+
+<case name="host_speaker_calibration" version="1.0" description="Calibrate host speaker's volume.">
+	<setup>
+		<sound id="sound1" type="sin:100:1000:20000" unsupprotedattrib="should fail" />
+	</setup>
+
+	<action>
+		<sequential repeat="100" index="i">
+		</sequential>
+	</action>
+	<nosuchelement />
+</case>
\ No newline at end of file
diff --git a/suite/audio_quality/test_description/test/wrong_attrib.xml b/suite/audio_quality/test_description/test/wrong_attrib.xml
new file mode 100644
index 0000000..0c7c95c
--- /dev/null
+++ b/suite/audio_quality/test_description/test/wrong_attrib.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!-- Copyright (C) 2012 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.
+-->
+
+<case name="host_speaker_calibration" version="1.0" description="Calibrate host speaker's volume." unsupprotedattrib="should fail">
+	<setup unsupprotedattrib="should fail">
+		<sound id="sound1" type="sin:100:1000:20000" unsupprotedattrib="should fail" />
+	</setup>
+
+	<action unsupprotedattrib="should fail">
+		<sequential repeat="100" index="i" unsupprotedattrib="should fail">
+		</sequential>
+	</action>
+	<save file="host_in" report="rms_.*"  unsupprotedattrib="should fail"/>
+</case>
\ No newline at end of file