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