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 android.media.midi.MidiDevice;
20 import android.media.midi.MidiDeviceInfo;
21 import android.media.midi.MidiManager;
22 import android.media.midi.MidiReceiver;
23 import android.os.Bundle;
24 import android.util.Log;
25 
26 import com.android.compatibility.common.util.CddTest;
27 import com.android.cts.verifier.R;
28 import com.android.cts.verifier.audio.midilib.JavaMidiTestModule;
29 import com.android.cts.verifier.audio.midilib.MidiIODevice;
30 
31 import java.io.IOException;
32 import java.util.Collection;
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-4,C-1-2")
59 public class MidiJavaTestActivity extends MidiTestActivityBase {
60     private static final String TAG = "MidiJavaTestActivity";
61     private static final boolean DEBUG = true;
62 
MidiJavaTestActivity()63     public MidiJavaTestActivity() {
64         super();
65         initTestModules(new JavaMidiTestActivityModule(MidiDeviceInfo.TYPE_USB),
66                 new JavaMidiTestActivityModule(MidiDeviceInfo.TYPE_VIRTUAL),
67                 new BTMidiTestModule());
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.midi_activity);
77 
78         super.onCreate(savedInstanceState);
79 
80         startMidiEchoServer();
81         scanMidiDevices();
82 
83         connectDeviceListener();
84     }
85 
86     /**
87      * Overrides parts of JavaMidiTestModule that needs a higher scope.
88      */
89     private class JavaMidiTestActivityModule extends JavaMidiTestModule {
90         private static final String TAG = "JavaMidiTestActivityModule";
91 
JavaMidiTestActivityModule(int deviceType)92         JavaMidiTestActivityModule(int deviceType) {
93             super(deviceType);
94         }
95 
96         @Override
updateTestStateUIAbstract()97         protected void updateTestStateUIAbstract() {
98             updateTestStateUI();
99         }
100 
101         @Override
showTimeoutMessageAbstract()102         protected void showTimeoutMessageAbstract() {
103             showTimeoutMessage();
104         }
105 
106         @Override
enableTestButtonsAbstract(boolean enable)107         protected void enableTestButtonsAbstract(boolean enable) {
108             enableTestButtons(enable);
109         }
110 
111         @Override
openMidiDevice()112         protected void openMidiDevice() {
113             mMidiManager.openDevice(mIODevice.mSendDevInfo, new TestModuleOpenListener(), null);
114         }
115 
showTimeoutMessage()116         void showTimeoutMessage() {
117             runOnUiThread(new Runnable() {
118                 public void run() {
119                     synchronized (mTestLock) {
120                         if (mTestRunning) {
121                             if (DEBUG) {
122                                 Log.i(TAG, "---- Test Failed - TIMEOUT");
123                             }
124                             mTestStatus = TESTSTATUS_FAILED_TIMEOUT;
125                             updateTestStateUIAbstract();
126                         }
127                     }
128                 }
129             });
130         }
131     } /* class JavaMidiTestActivityModule */
132 
133     /**
134      * Test Module for Bluetooth Loopback.
135      * This is a specialization of JavaMidiTestModule (which has the connections for the BL device
136      * itself) with and added MidiIODevice object for the USB audio device which does the
137      * "looping back".
138      */
139     private class BTMidiTestModule extends JavaMidiTestActivityModule {
140         private static final String TAG = "BTMidiTestModule";
141         private MidiIODevice mUSBLoopbackDevice = new MidiIODevice(MidiDeviceInfo.TYPE_USB);
142 
BTMidiTestModule()143         public BTMidiTestModule() {
144             super(MidiDeviceInfo.TYPE_BLUETOOTH );
145         }
146 
147         @Override
scanDevices(Collection<MidiDeviceInfo> devInfos)148         public void scanDevices(Collection<MidiDeviceInfo> devInfos) {
149             // (normal) Scan for BT MIDI device
150             super.scanDevices(devInfos);
151             // Find a USB Loopback Device
152             mUSBLoopbackDevice.scanDevices(devInfos);
153         }
154 
openUSBEchoDevice(MidiDevice device)155         private void openUSBEchoDevice(MidiDevice device) {
156             MidiDeviceInfo deviceInfo = device.getInfo();
157             int numOutputs = deviceInfo.getOutputPortCount();
158             if (numOutputs > 0) {
159                 mUSBLoopbackDevice.mReceivePort = device.openOutputPort(0);
160                 mUSBLoopbackDevice.mReceivePort.connect(new USBMidiEchoReceiver());
161             }
162 
163             int numInputs = deviceInfo.getInputPortCount();
164             if (numInputs != 0) {
165                 mUSBLoopbackDevice.mSendPort = device.openInputPort(0);
166             }
167         }
168 
closePorts()169         protected void closePorts() {
170             super.closePorts();
171             mUSBLoopbackDevice.closePorts();
172         }
173 
174         @Override
startLoopbackTest(int testID)175         public void startLoopbackTest(int testID) {
176             if (DEBUG) {
177                 Log.i(TAG, "---- startLoopbackTest()");
178             }
179 
180             super.startLoopbackTest(testID);
181 
182             // Setup the USB Loopback Device
183             mUSBLoopbackDevice.closePorts();
184 
185             if (mIODevice.mSendDevInfo != null) {
186                 mMidiManager.openDevice(
187                         mUSBLoopbackDevice.mSendDevInfo, new USBLoopbackOpenListener(), null);
188             }
189         }
190 
191         /**
192          * We need this OnDeviceOpenedListener to open the USB-Loopback device
193          */
194         private class USBLoopbackOpenListener implements MidiManager.OnDeviceOpenedListener {
195             @Override
onDeviceOpened(MidiDevice device)196             public void onDeviceOpened(MidiDevice device) {
197                 if (DEBUG) {
198                     Log.i("USBLoopbackOpenListener", "---- onDeviceOpened()");
199                 }
200                 mUSBLoopbackDevice.openPorts(device, new USBMidiEchoReceiver());
201             }
202         } /* class USBLoopbackOpenListener */
203 
204         /**
205          * MidiReceiver subclass for BlueTooth Loopback Test
206          *
207          * This class receives bytes from the USB Interface (presumably coming from the
208          * Bluetooth MIDI peripheral) and echoes them back out (presumably to the Bluetooth
209          * MIDI peripheral).
210          */
211         //TODO - This could be pulled out into a separate class and shared with the identical
212         // code in MidiNativeTestActivity if we pass in the USB Loopback Device object rather
213         // than accessing it from the enclosing BTMidiTestModule class.
214         private class USBMidiEchoReceiver extends MidiReceiver {
215             private int mTotalBytesEchoed;
216 
217             @Override
onSend(byte[] msg, int offset, int count, long timestamp)218             public void onSend(byte[] msg, int offset, int count, long timestamp) throws IOException {
219                 mTotalBytesEchoed += count;
220                 if (DEBUG) {
221                     logByteArray("echo: ", msg, offset, count);
222                 }
223                 if (mUSBLoopbackDevice.mSendPort == null) {
224                     Log.e(TAG, "(java) mUSBLoopbackDevice.mSendPort is null");
225                 } else {
226                     mUSBLoopbackDevice.mSendPort.onSend(msg, offset, count, timestamp);
227                 }
228             }
229         } /* class USBMidiEchoReceiver */
230     } /* class BTMidiTestModule */
231 } /* class MidiActivity */
232