1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.cts.verifier.audio; 18 19 import java.io.IOException; 20 21 import android.media.midi.MidiDevice; 22 import android.media.midi.MidiDeviceInfo; 23 import android.media.midi.MidiManager; 24 import android.media.midi.MidiReceiver; 25 import android.os.Bundle; 26 import android.util.Log; 27 28 import com.android.cts.verifier.audio.midilib.MidiIODevice; 29 import com.android.cts.verifier.audio.midilib.NativeMidiManager; 30 import com.android.cts.verifier.R; 31 32 /* 33 * A note about the USB MIDI device. 34 * Any USB MIDI peripheral with standard female DIN jacks can be used. A standard MIDI cable 35 * plugged into both input and output is required for the USB Loopback Test. A Bluetooth MIDI 36 * device like the Yamaha MD-BT01 plugged into both input and output is required for the 37 * Bluetooth Loopback test. 38 */ 39 40 /* 41 * A note about the "virtual MIDI" device... 42 * See file MidiEchoService for implementation of the echo server itself. 43 * This service is started by the main manifest file (AndroidManifest.xml). 44 */ 45 46 /* 47 * A note about Bluetooth MIDI devices... 48 * Any Bluetooth MIDI device needs to be paired with the DUT with the "MIDI+BTLE" application 49 * available in the Play Store: 50 * (https://play.google.com/store/apps/details?id=com.mobileer.example.midibtlepairing). 51 */ 52 53 /** 54 * CTS Verifier Activity for MIDI test 55 */ 56 public class MidiNativeTestActivity extends MidiTestActivityBase { 57 private static final String TAG = "MidiNativeTestActivity"; 58 private static final boolean DEBUG = false; 59 MidiNativeTestActivity()60 public MidiNativeTestActivity() { 61 super(); 62 initTestModules(new NativeMidiTestModule(MidiDeviceInfo.TYPE_USB), 63 new NativeMidiTestModule(MidiDeviceInfo.TYPE_VIRTUAL), 64 new BTMidiTestModule()); 65 66 NativeMidiManager.loadNativeAPI(); 67 NativeMidiManager.initN(); 68 } 69 70 @Override onCreate(Bundle savedInstanceState)71 protected void onCreate(Bundle savedInstanceState) { 72 if (DEBUG) { 73 Log.i(TAG, "---- onCreate()"); 74 } 75 76 setContentView(R.layout.ndk_midi_activity); 77 78 super.onCreate(savedInstanceState); 79 80 startMidiEchoServer(); 81 scanMidiDevices(); 82 83 connectDeviceListener(); 84 } 85 86 @Override onPause()87 protected void onPause () { 88 super.onPause(); 89 if (DEBUG) { 90 Log.i(TAG, "---- onPause()"); 91 } 92 93 boolean isFound = stopService(mMidiServiceIntent); 94 if (DEBUG) { 95 Log.i(TAG, "---- Stop Service: " + isFound); 96 } 97 } 98 99 /** 100 * A class to control and represent the state of a given test. 101 * It hold the data needed for IO, and the logic for sending, receiving and matching 102 * the MIDI data stream. 103 */ 104 public class NativeMidiTestModule extends MidiTestModule { 105 private static final String TAG = "NativeMidiTestModule"; 106 private static final boolean DEBUG = true; 107 108 private NativeMidiManager mNativeMidiManager; 109 NativeMidiTestModule(int deviceType)110 public NativeMidiTestModule(int deviceType) { 111 super(deviceType); 112 mNativeMidiManager = new NativeMidiManager(); 113 114 // this call is just to keep the build from stripping out "endTest", because 115 // it is only called from JNI. 116 endTest(TESTSTATUS_NOTRUN); 117 } 118 closePorts()119 protected void closePorts() { 120 // NOP 121 } 122 123 @Override startLoopbackTest(int testID)124 void startLoopbackTest(int testID) { 125 synchronized (mTestLock) { 126 mTestRunning = true; 127 enableTestButtons(false); 128 } 129 130 if (DEBUG) { 131 Log.i(TAG, "---- startLoopbackTest()"); 132 } 133 134 mRunningTestID = testID; 135 136 synchronized (mTestLock) { 137 mTestStatus = TESTSTATUS_NOTRUN; 138 } 139 140 if (mIODevice.mSendDevInfo != null) { 141 mMidiManager.openDevice(mIODevice.mSendDevInfo, new TestModuleOpenListener(), null); 142 } 143 144 startTimeoutHandler(); 145 } 146 147 @Override hasTestPassed()148 boolean hasTestPassed() { 149 int status; 150 synchronized (mTestLock) { 151 status = mTestStatus; 152 } 153 return status == TESTSTATUS_PASSED; 154 } 155 endTest(int endCode)156 public void endTest(int endCode) { 157 synchronized (mTestLock) { 158 mTestRunning = false; 159 mTestStatus = endCode; 160 } 161 if (endCode != TESTSTATUS_NOTRUN) { 162 updateTestStateUI(); 163 enableTestButtons(true); 164 } 165 166 closePorts(); 167 } 168 169 /** 170 * Listens for MIDI device opens. Opens I/O ports and sends out the apriori 171 * setup messages. 172 */ 173 class TestModuleOpenListener implements MidiManager.OnDeviceOpenedListener { 174 // 175 // This is where the logical part of the test starts 176 // 177 @Override onDeviceOpened(MidiDevice device)178 public void onDeviceOpened(MidiDevice device) { 179 if (DEBUG) { 180 Log.i(TAG, "---- onDeviceOpened()"); 181 } 182 mNativeMidiManager.startTest(NativeMidiTestModule.this, device, 183 mRunningTestID == TESTID_BTLOOPBACK); 184 } 185 } 186 } /* class NativeMidiTestModule */ 187 188 /** 189 * Test Module for Bluetooth Loopback. 190 * This is a specialization of NativeMidiTestModule (which has the connections for the BL device 191 * itself) with and added MidiIODevice object for the USB audio device which does the 192 * "looping back". 193 */ 194 private class BTMidiTestModule extends NativeMidiTestModule { 195 private static final String TAG = "BTMidiTestModule"; 196 private MidiIODevice mUSBLoopbackDevice = new MidiIODevice(MidiDeviceInfo.TYPE_USB); 197 BTMidiTestModule()198 public BTMidiTestModule() { 199 super(MidiDeviceInfo.TYPE_BLUETOOTH); 200 } 201 202 @Override scanDevices(MidiDeviceInfo[] devInfos)203 public void scanDevices(MidiDeviceInfo[] devInfos) { 204 // (normal) Scan for BT MIDI device 205 super.scanDevices(devInfos); 206 // Find a USB Loopback Device 207 mUSBLoopbackDevice.scanDevices(devInfos); 208 } 209 closePorts()210 protected void closePorts() { 211 super.closePorts(); 212 if (mUSBLoopbackDevice != null) { 213 mUSBLoopbackDevice.closePorts(); 214 } 215 } 216 217 @Override startLoopbackTest(int testID)218 void startLoopbackTest(int testID) { 219 if (DEBUG) { 220 Log.i(TAG, "---- startLoopbackTest()"); 221 } 222 // Setup the USB Loopback Device 223 mUSBLoopbackDevice.closePorts(); 224 225 if (mIODevice.mSendDevInfo != null) { 226 mMidiManager.openDevice( 227 mUSBLoopbackDevice.mSendDevInfo, new USBLoopbackOpenListener(), null); 228 } 229 230 // Now start the test as usual 231 super.startLoopbackTest(testID); 232 } 233 234 /** 235 * We need this OnDeviceOpenedListener to open the USB-Loopback device 236 */ 237 private class USBLoopbackOpenListener implements MidiManager.OnDeviceOpenedListener { 238 @Override onDeviceOpened(MidiDevice device)239 public void onDeviceOpened(MidiDevice device) { 240 if (DEBUG) { 241 Log.i("USBLoopbackOpenListener", "---- onDeviceOpened()"); 242 } 243 mUSBLoopbackDevice.openPorts(device, new USBMidiEchoReceiver()); 244 } 245 } /* class USBLoopbackOpenListener */ 246 247 /** 248 * MidiReceiver subclass for BlueTooth Loopback Test 249 * 250 * This class receives bytes from the USB Interface (presumably coming from the 251 * Bluetooth MIDI peripheral) and echoes them back out (presumably to the Bluetooth 252 * MIDI peripheral). 253 */ 254 //TODO - This could be pulled out into a separate class and shared with the identical 255 // code in MidiJavaTestActivity is we pass in the send port 256 private class USBMidiEchoReceiver extends MidiReceiver { 257 private int mTotalBytesEchoed; 258 259 @Override onSend(byte[] msg, int offset, int count, long timestamp)260 public void onSend(byte[] msg, int offset, int count, long timestamp) throws IOException { 261 mTotalBytesEchoed += count; 262 if (DEBUG) { 263 Log.i(TAG, "---- USBMidiEchoReceiver.onSend() count:" + count + 264 " total:" + mTotalBytesEchoed); 265 } 266 if (mUSBLoopbackDevice.mSendPort == null) { 267 Log.e(TAG, "(native) mUSBLoopbackDevice.mSendPort is null"); 268 } else { 269 mUSBLoopbackDevice.mSendPort.onSend(msg, offset, count, timestamp); 270 } 271 } 272 } /* class USBMidiEchoReceiver */ 273 } /* class BTMidiTestModule */ 274 } /* class MidiNativeTestActivity */ 275