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