am fe91f772: am 28a108a9: Merge "CTSVerifier tests for audio (re)routing callbacks." into mnc-dev
* commit 'fe91f7724a39b640108cf1c653d8c6488d1640d6':
CTSVerifier tests for audio (re)routing callbacks.
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index 6c515fd..98e8acd 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -1495,6 +1495,18 @@
-->
</activity>
+ <activity android:name=".audio.AudioRoutingNotificationsActivity"
+ android:label="@string/audio_routingnotifications_test">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.cts.intent.category.MANUAL_TEST" />
+ </intent-filter>
+ <meta-data android:name="test_category" android:value="@string/test_category_audio" />
+ <!--
+ <meta-data android:name="test_required_features" android:value="android.hardware.microphone" />
+ -->
+ </activity>
+
<service android:name=".tv.MockTvInputService"
android:permission="android.permission.BIND_TV_INPUT">
<intent-filter>
diff --git a/apps/CtsVerifier/res/layout/audio_routingnotifications_test.xml b/apps/CtsVerifier/res/layout/audio_routingnotifications_test.xml
new file mode 100644
index 0000000..cef30d6
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/audio_routingnotifications_test.xml
@@ -0,0 +1,99 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:padding="10dip"
+ android:orientation="vertical">
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:scrollbars="vertical"
+ android:gravity="bottom"
+ android:id="@+id/info_text"
+ android:text="@string/audio_dev_routingnotification_instructions" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:id="@+id/audioTrackRoutingLayout">
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/audio_routingnotification_playHeader"/>
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/audio_routingnotification_audioTrack_change"/>
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/audio_routingnotification_playBtn"
+ android:text="@string/audio_routingnotification_playBtn"/>
+
+ <Button
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/audio_routingnotification_playStopBtn"
+ android:text="@string/audio_routingnotification_playStopBtn"/>
+ </LinearLayout>
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:id="@+id/audioRecordRoutingLayout">
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/audio_routingnotification_recHeader"/>
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/audio_routingnotification_audioRecord_change"/>
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/audio_routingnotification_recordBtn"
+ android:text="@string/audio_routingnotification_recBtn"/>
+
+ <Button
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/audio_routingnotification_recordStopBtn"
+ android:text="@string/audio_routingnotification_recStopBtn"/>
+ </LinearLayout>
+ </LinearLayout>
+
+ <include layout="@layout/pass_fail_buttons" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index 1ccd6b7..d22f5ec 100644
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -1615,4 +1615,23 @@
<string name="audio_dev_notification_clearmsgs">Clear Messages</string>
<string name="audio_dev_notification_connectMsg">CONNECT DETECTED</string>
<string name="audio_dev_notification_disconnectMsg">DISCONNECT DETECTED</string>
+
+ <!-- Audio Routing Notifcations Test -->
+ <string name="audio_routingnotifications_test">Audio Routing Notifications Test</string>
+ <string name="audio_dev_routingnotification_instructions">
+ Click on the "Play" button in the AudioTrack Routing Notifictions section below to
+ start (silent) playback. Insert a wired headset. Observe a message acknowledging the
+ rerouting event below. Remove the wired headset and observe the new routing message.
+ Click on the "Stop" button to stop playback.\n
+ Repeat the process with "Record" and "Stop" button in the AudioRecord Routing
+ Notifications section below.
+ </string>
+ <string name="audio_routingnotification_playBtn">Play</string>
+ <string name="audio_routingnotification_playStopBtn">Stop</string>
+ <string name="audio_routingnotification_recBtn">Record</string>
+ <string name="audio_routingnotification_recStopBtn">Stop</string>
+ <string name="audio_routingnotification_playHeader">AudioTrack Routing Notifications</string>
+ <string name="audio_routingnotification_recHeader">AudioRecord Routing Notifications</string>
+ <string name="audio_routingnotification_trackRoutingMsg">AudioTrack rerouting</string>
+ <string name="audio_routingnotification_recordRoutingMsg">AudioRecord rerouting</string>
</resources>
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioRoutingNotificationsActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioRoutingNotificationsActivity.java
new file mode 100644
index 0000000..b6a4255
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioRoutingNotificationsActivity.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.audio;
+
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+
+import android.content.Context;
+
+import android.media.AudioDeviceCallback;
+import android.media.AudioDeviceInfo;
+import android.media.AudioManager;
+import android.media.AudioRecord;
+import android.media.AudioTrack;
+
+import android.os.Bundle;
+import android.os.Handler;
+
+import android.util.Log;
+
+import android.view.View;
+import android.view.View.OnClickListener;
+
+import android.widget.Button;
+import android.widget.TextView;
+
+/**
+ * Tests AudioTrack and AudioRecord (re)Routing messages.
+ */
+public class AudioRoutingNotificationsActivity extends PassFailButtons.Activity {
+ private static final String TAG = "AudioRoutingNotificationsActivity";
+
+ Context mContext;
+
+ OnBtnClickListener mBtnClickListener = new OnBtnClickListener();
+
+ int mNumTrackNotifications = 0;
+ int mNumRecordNotifications = 0;
+
+ TrivialPlayer mAudioPlayer = new TrivialPlayer();
+ TrivialRecorder mAudioRecorder = new TrivialRecorder();
+
+ private class OnBtnClickListener implements OnClickListener {
+ @Override
+ public void onClick(View v) {
+ switch (v.getId()) {
+ case R.id.audio_routingnotification_playBtn:
+ Log.i(TAG, "audio_routingnotification_playBtn");
+ mAudioPlayer.start();
+ break;
+
+ case R.id.audio_routingnotification_playStopBtn:
+ Log.i(TAG, "audio_routingnotification_playStopBtn");
+ mAudioPlayer.stop();
+ break;
+
+ case R.id.audio_routingnotification_recordBtn:
+ break;
+
+ case R.id.audio_routingnotification_recordStopBtn:
+ break;
+ }
+ }
+ }
+
+ private class AudioTrackRoutingChangeListener implements AudioTrack.OnRoutingChangedListener {
+ public void onRoutingChanged(AudioTrack audioTrack) {
+ mNumTrackNotifications++;
+ TextView textView =
+ (TextView)findViewById(R.id.audio_routingnotification_audioTrack_change);
+ String msg = mContext.getResources().getString(
+ R.string.audio_routingnotification_trackRoutingMsg);
+ AudioDeviceInfo routedDevice = audioTrack.getRoutedDevice();
+ CharSequence deviceName = routedDevice != null ? routedDevice.getProductName() : "none";
+ int deviceType = routedDevice != null ? routedDevice.getType() : -1;
+ textView.setText(msg + " - " +
+ deviceName + " [0x" + Integer.toHexString(deviceType) + "]" +
+ " - " + mNumTrackNotifications);
+ }
+ }
+
+ private class AudioRecordRoutingChangeListener implements AudioRecord.OnRoutingChangedListener {
+ public void onRoutingChanged(AudioRecord audioRecord) {
+ mNumRecordNotifications++;
+ TextView textView =
+ (TextView)findViewById(R.id.audio_routingnotification_audioRecord_change);
+ String msg = mContext.getResources().getString(
+ R.string.audio_routingnotification_recordRoutingMsg);
+ AudioDeviceInfo routedDevice = audioRecord.getRoutedDevice();
+ CharSequence deviceName = routedDevice != null ? routedDevice.getProductName() : "none";
+ int deviceType = routedDevice != null ? routedDevice.getType() : -1;
+ textView.setText(msg + " - " +
+ deviceName + " [0x" + Integer.toHexString(deviceType) + "]" +
+ " - " + mNumRecordNotifications);
+ }
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.audio_routingnotifications_test);
+
+ Button btn;
+ btn = (Button)findViewById(R.id.audio_routingnotification_playBtn);
+ btn.setOnClickListener(mBtnClickListener);
+ btn = (Button)findViewById(R.id.audio_routingnotification_playStopBtn);
+ btn.setOnClickListener(mBtnClickListener);
+ btn = (Button)findViewById(R.id.audio_routingnotification_recordBtn);
+ btn.setOnClickListener(mBtnClickListener);
+ btn = (Button)findViewById(R.id.audio_routingnotification_recordStopBtn);
+ btn.setOnClickListener(mBtnClickListener);
+
+ mContext = this;
+
+ AudioTrack audioTrack = mAudioPlayer.getAudioTrack();
+ audioTrack.addOnRoutingChangedListener(
+ new AudioTrackRoutingChangeListener(), new Handler());
+
+ AudioRecord audioRecord = mAudioRecorder.getAudioRecord();
+ audioRecord.addOnRoutingChangedListener(
+ new AudioRecordRoutingChangeListener(), new Handler());
+
+ setPassFailButtonClickListeners();
+ }
+
+ @Override
+ public void onBackPressed () {
+ mAudioPlayer.shutDown();
+ mAudioRecorder.shutDown();
+ super.onBackPressed();
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/TrivialPlayer.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/TrivialPlayer.java
new file mode 100644
index 0000000..af09504
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/TrivialPlayer.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.audio;
+
+import android.media.AudioFormat;
+import android.media.AudioManager;
+import android.media.AudioTrack;
+
+import java.lang.InterruptedException;
+import java.lang.Math;
+import java.lang.Runnable;
+
+public class TrivialPlayer implements Runnable {
+ AudioTrack mAudioTrack;
+ int mBufferSize;
+
+ boolean mPlay;
+ boolean mIsPlaying;
+
+ short[] mAudioData;
+
+ Thread mFillerThread = null;
+
+ public TrivialPlayer() {
+ mBufferSize =
+ AudioTrack.getMinBufferSize(
+ 41000,
+ AudioFormat.CHANNEL_OUT_STEREO,
+ AudioFormat.ENCODING_PCM_16BIT);
+ mAudioTrack =
+ new AudioTrack(
+ AudioManager.STREAM_MUSIC,
+ 41000,
+ AudioFormat.CHANNEL_OUT_STEREO,
+ AudioFormat.ENCODING_PCM_16BIT,
+ mBufferSize,
+ AudioTrack.MODE_STREAM);
+
+ mPlay = false;
+ mIsPlaying = false;
+
+ // setup audio data (silence will suffice)
+ mAudioData = new short[mBufferSize];
+ for (int index = 0; index < mBufferSize; index++) {
+ // mAudioData[index] = 0;
+ // keep this code since one might want to hear the playnig audio
+ // for debugging/verification.
+ mAudioData[index] =
+ (short)(((Math.random() * 2.0) - 1.0) * (double)Short.MAX_VALUE/2.0);
+ }
+ }
+
+ public AudioTrack getAudioTrack() { return mAudioTrack; }
+
+ public boolean isPlaying() {
+ synchronized (this) {
+ return mIsPlaying;
+ }
+ }
+
+ public void start() {
+ mPlay = true;
+ mFillerThread = new Thread(this);
+ mFillerThread.start();
+ }
+
+ public void stop() {
+ mPlay = false;
+ mFillerThread = null;
+ }
+
+ public void shutDown() {
+ stop();
+ while (isPlaying()) {
+ try {
+ Thread.sleep(10);
+ } catch (InterruptedException ex) {
+ }
+ }
+ mAudioTrack.release();
+ }
+
+ @Override
+ public void run() {
+ mAudioTrack.play();
+ synchronized (this) {
+ mIsPlaying = true;
+ }
+ while (mAudioTrack != null && mPlay) {
+ mAudioTrack.write(mAudioData, 0, mBufferSize);
+ }
+ synchronized (this) {
+ mIsPlaying = false;
+ }
+ mAudioTrack.stop();
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/TrivialRecorder.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/TrivialRecorder.java
new file mode 100644
index 0000000..f684681
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/TrivialRecorder.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.audio;
+
+import android.media.AudioFormat;
+import android.media.AudioManager;
+import android.media.AudioRecord;
+
+import android.media.MediaRecorder;
+
+import java.lang.InterruptedException;
+import java.lang.Runnable;
+
+public class TrivialRecorder implements Runnable {
+ AudioRecord mAudioRecord;
+ int mBufferSize;
+
+ boolean mRecord;
+ boolean mIsRecording;
+
+ short[] mAudioData;
+
+ Thread mReaderThread = null;
+
+ public TrivialRecorder() {
+ mBufferSize =
+ AudioRecord.getMinBufferSize(
+ 41000,
+ AudioFormat.CHANNEL_IN_MONO,
+ AudioFormat.ENCODING_PCM_16BIT);
+ mAudioRecord =
+ new AudioRecord(
+ MediaRecorder.AudioSource.DEFAULT,
+ 41000,
+ AudioFormat.CHANNEL_IN_MONO,
+ AudioFormat.ENCODING_PCM_16BIT,
+ mBufferSize);
+
+ mRecord = false;
+ mIsRecording = false;
+
+ // setup audio data (silence will suffice)
+ mAudioData = new short[mBufferSize];
+ }
+
+ public AudioRecord getAudioRecord() { return mAudioRecord; }
+
+ public boolean mIsRecording() {
+ synchronized (this) {
+ return mIsRecording;
+ }
+ }
+
+ public void start() {
+ mRecord = true;
+ mReaderThread = new Thread(this);
+ mReaderThread.start();
+ }
+
+ public void stop() {
+ mRecord = false;
+ mReaderThread = null;
+ }
+
+ public void shutDown() {
+ stop();
+ while (mIsRecording()) {
+ try {
+ Thread.sleep(10);
+ } catch (InterruptedException ex) {
+ }
+ }
+ mAudioRecord.release();
+ }
+
+ @Override
+ public void run() {
+ mAudioRecord.startRecording();
+ synchronized (this) {
+ mIsRecording = true;
+ }
+ while (mRecord) {
+ mAudioRecord.read(mAudioData, 0, mBufferSize);
+ }
+ mAudioRecord.stop();
+ synchronized (this) {
+ mIsRecording = false;
+ }
+ }
+}