• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.content.ComponentName;
22 import android.content.Context;
23 import android.content.Intent;
24 import android.content.res.Resources;
25 import android.content.pm.PackageManager;
26 import android.media.midi.MidiDevice;
27 import android.media.midi.MidiDeviceInfo;
28 import android.media.midi.MidiDeviceStatus;
29 import android.media.midi.MidiInputPort;
30 import android.media.midi.MidiManager;
31 import android.media.midi.MidiOutputPort;
32 import android.media.midi.MidiReceiver;
33 import android.os.Bundle;
34 import android.os.Handler;
35 import android.util.Log;
36 import android.view.View;
37 import android.widget.Button;
38 import android.widget.TextView;
39 
40 import java.lang.InterruptedException;
41 import java.util.Timer;
42 import java.util.TimerTask;
43 
44 import com.android.cts.verifier.audio.midilib.NativeMidiManager;
45 
46 import com.android.cts.verifier.PassFailButtons;
47 
48 import com.android.cts.verifier.R;  // needed to access resource in CTSVerifier project namespace.
49 
50 import com.android.midi.MidiEchoTestService;
51 
52 /*
53  * A note about the USB MIDI device.
54  * Any USB MIDI peripheral with standard female DIN jacks can be used. A standard MIDI cable
55  * plugged into both input and output is required for the USB Loopback Test. A Bluetooth MIDI
56  * device like the Yamaha MD-BT01 plugged into both input and output is required for the
57  * Bluetooth Loopback test.
58  */
59 
60 /*
61  * A note about the "virtual MIDI" device...
62  * See file MidiEchoTestService for implementation of the echo server itself.
63  * This service is started by the main manifest file (AndroidManifest.xml).
64  */
65 
66 /*
67  * A note about Bluetooth MIDI devices...
68  * Any Bluetooth MIDI device needs to be paired with the DUT with the "MIDI+BTLE" application
69  * available in the Play Store:
70  * (https://play.google.com/store/apps/details?id=com.mobileer.example.midibtlepairing).
71  */
72 
73 /**
74  * CTS Verifier Activity for MIDI test
75  */
76 public class NDKMidiActivity extends PassFailButtons.Activity implements View.OnClickListener {
77 
78     private static final String TAG = "NDKMidiActivity";
79     private static final boolean DEBUG = false;
80 
81     private MidiManager mMidiManager;
82 
83     // Flags
84     private boolean mHasMIDI;
85 
86     // Test "Modules"
87     private NDKMidiTestModule   mUSBTestModule;
88     private NDKMidiTestModule   mVirtTestModule;
89     private BTMidiTestModule    mBTTestModule;
90 
91     // Widgets
92     private Button          mUSBTestBtn;
93     private Button          mVirtTestBtn;
94     private Button          mBTTestBtn;
95 
96     private TextView        mUSBIInputDeviceLbl;
97     private TextView        mUSBOutputDeviceLbl;
98     private TextView        mUSBTestStatusTxt;
99 
100     private TextView        mVirtInputDeviceLbl;
101     private TextView        mVirtOutputDeviceLbl;
102     private TextView        mVirtTestStatusTxt;
103 
104     private TextView        mBTInputDeviceLbl;
105     private TextView        mBTOutputDeviceLbl;
106     private TextView        mBTTestStatusTxt;
107 
108     private Intent          mMidiServiceIntent;
109     private ComponentName   mMidiService;
110 
NDKMidiActivity()111     public NDKMidiActivity() {
112         super();
113 
114         NativeMidiManager.loadNativeAPI();
115         NativeMidiManager.initN();
116     }
117 
hasMIDI()118     private boolean hasMIDI() {
119         // CDD Section C-1-4: android.software.midi
120         return getPackageManager().hasSystemFeature(PackageManager.FEATURE_MIDI);
121     }
122 
showConnectedMIDIPeripheral()123     private void showConnectedMIDIPeripheral() {
124         // USB
125         mUSBIInputDeviceLbl.setText(mUSBTestModule.getInputName());
126         mUSBOutputDeviceLbl.setText(mUSBTestModule.getOutputName());
127         mUSBTestBtn.setEnabled(mUSBTestModule.isTestReady());
128 
129         // Virtual MIDI
130         mVirtInputDeviceLbl.setText(mVirtTestModule.getInputName());
131         mVirtOutputDeviceLbl.setText(mVirtTestModule.getOutputName());
132         mVirtTestBtn.setEnabled(mVirtTestModule.isTestReady());
133 
134         // Bluetooth
135         mBTInputDeviceLbl.setText(mBTTestModule.getInputName());
136         mBTOutputDeviceLbl.setText(mBTTestModule.getOutputName());
137         mBTTestBtn.setEnabled(mBTTestModule.isTestReady());
138     }
139 
calcTestPassed()140     private boolean calcTestPassed() {
141         boolean hasPassed = false;
142         if (!mHasMIDI) {
143             // if it doesn't report MIDI support, then it doesn't have to pass the other tests.
144             hasPassed = true;
145         } else {
146             hasPassed = mUSBTestModule.hasTestPassed() &&
147                     mVirtTestModule.hasTestPassed() &&
148                     mBTTestModule.hasTestPassed();
149         }
150 
151         getPassButton().setEnabled(hasPassed);
152         return hasPassed;
153     }
154 
scanMidiDevices()155     private void scanMidiDevices() {
156         if (DEBUG) {
157             Log.i(TAG, "scanMidiDevices()....");
158         }
159 
160         MidiDeviceInfo[] devInfos = mMidiManager.getDevices();
161         mUSBTestModule.scanDevices(devInfos, MidiDeviceInfo.TYPE_USB);
162         mVirtTestModule.scanDevices(devInfos, MidiDeviceInfo.TYPE_VIRTUAL);
163         mBTTestModule.scanDevices(devInfos, MidiDeviceInfo.TYPE_BLUETOOTH);
164 
165         showConnectedMIDIPeripheral();
166     }
167 
168     //
169     // UI Updaters
170     //
enableTestButtons(boolean enable)171     public void enableTestButtons(boolean enable) {
172         if (DEBUG) {
173             Log.i(TAG, "enableTestButtons" + enable + ")");
174         }
175 
176         runOnUiThread(new Runnable() {
177             public void run() {
178                 if (enable) {
179                     // remember, a given test might not be enabled, so we can't just enable
180                     // all of the buttons
181                     showConnectedMIDIPeripheral();
182                 } else {
183                     mUSBTestBtn.setEnabled(enable);
184                     mVirtTestBtn.setEnabled(enable);
185                     mBTTestBtn.setEnabled(enable);
186                 }
187             }
188         });
189     }
190 
showUSBTestStatus()191     private void showUSBTestStatus() {
192         mUSBTestStatusTxt.setText(mUSBTestModule.getTestStatusString());
193     }
194 
showVirtTestStatus()195     private void showVirtTestStatus() {
196         mVirtTestStatusTxt.setText(mVirtTestModule.getTestStatusString());
197     }
198 
showBTTestStatus()199     private void showBTTestStatus() {
200         mBTTestStatusTxt.setText(mBTTestModule.getTestStatusString());
201     }
202 
203     // Need this to update UI from MIDI read thread
updateTestStateUI()204     public void updateTestStateUI() {
205         if (DEBUG) {
206             Log.i(TAG, "updateTestStateUI()");
207         }
208         runOnUiThread(new Runnable() {
209             public void run() {
210                 calcTestPassed();
211                 showUSBTestStatus();
212                 showVirtTestStatus();
213                 showBTTestStatus();
214             }
215         });
216     }
217 
218     @Override
onCreate(Bundle savedInstanceState)219     protected void onCreate(Bundle savedInstanceState) {
220         super.onCreate(savedInstanceState);
221 
222         if (DEBUG) {
223             Log.i(TAG, "---- onCreate()");
224         }
225 
226         // Start MIDI Echo service
227         mMidiServiceIntent = new Intent(this, MidiEchoTestService.class);
228         mMidiService = startService(mMidiServiceIntent);
229         if (DEBUG) {
230             Log.i(TAG, "---- mMidiService instantiated:" + mMidiService);
231         }
232 
233         // Setup UI
234         setContentView(R.layout.ndk_midi_activity);
235 
236         // Standard PassFailButtons.Activity initialization
237         setPassFailButtonClickListeners();
238         setInfoResources(R.string.midi_test, R.string.midi_info, -1);
239 
240         // May as well calculate this right off the bat.
241         mHasMIDI = hasMIDI();
242         ((TextView)findViewById(R.id.midiHasMIDILbl)).setText("" + mHasMIDI);
243 
244         // USB Test Widgets
245         mUSBIInputDeviceLbl = (TextView)findViewById(R.id.midiUSBInputLbl);
246         mUSBOutputDeviceLbl = (TextView)findViewById(R.id.midiUSBOutputLbl);
247         mUSBTestBtn = (Button)findViewById(R.id.midiTestUSBInterfaceBtn);
248         mUSBTestBtn.setOnClickListener(this);
249         mUSBTestStatusTxt = (TextView)findViewById(R.id.midiUSBTestStatusLbl);
250 
251         // Virtual MIDI Test Widgets
252         mVirtInputDeviceLbl = (TextView)findViewById(R.id.midiVirtInputLbl);
253         mVirtOutputDeviceLbl = (TextView)findViewById(R.id.midiVirtOutputLbl);
254         mVirtTestBtn = (Button)findViewById(R.id.midiTestVirtInterfaceBtn);
255         mVirtTestBtn.setOnClickListener(this);
256         mVirtTestStatusTxt = (TextView)findViewById(R.id.midiVirtTestStatusLbl);
257 
258         // Bluetooth MIDI Test Widgets
259         mBTInputDeviceLbl = (TextView)findViewById(R.id.midiBTInputLbl);
260         mBTOutputDeviceLbl = (TextView)findViewById(R.id.midiBTOutputLbl);
261         mBTTestBtn = (Button)findViewById(R.id.midiTestBTInterfaceBtn);
262         mBTTestBtn.setOnClickListener(this);
263         mBTTestStatusTxt = (TextView)findViewById(R.id.midiBTTestStatusLbl);
264 
265         // Init MIDI Stuff
266         mMidiManager = (MidiManager) getSystemService(Context.MIDI_SERVICE);
267 
268         // Setup Test Modules
269         mUSBTestModule = new NDKMidiTestModule(this, mMidiManager);
270         mVirtTestModule = new NDKMidiTestModule(this, mMidiManager);
271         mBTTestModule = new BTMidiTestModule(this, mMidiManager);
272 
273         // Initial MIDI Device Scan
274         scanMidiDevices();
275 
276         // Plug in device connect/disconnect callback
277         mMidiManager.registerDeviceCallback(new MidiDeviceCallback(), new Handler(getMainLooper()));
278 
279     }
280 
281     @Override
onPause()282     protected void onPause () {
283         super.onPause();
284         if (DEBUG) {
285             Log.i(TAG, "---- onPause()");
286         }
287 
288         boolean isFound = stopService(mMidiServiceIntent);
289         if (DEBUG) {
290             Log.i(TAG, "---- Stop Service: " + isFound);
291         }
292     }
293 
294     /**
295      * Callback class for MIDI device connect/disconnect.
296      */
297     private class MidiDeviceCallback extends MidiManager.DeviceCallback {
298         private static final String TAG = "MidiDeviceCallback";
299 
300         @Override
onDeviceAdded(MidiDeviceInfo device)301         public void onDeviceAdded(MidiDeviceInfo device) {
302             scanMidiDevices();
303         }
304 
305         @Override
onDeviceRemoved(MidiDeviceInfo device)306         public void onDeviceRemoved(MidiDeviceInfo device) {
307             scanMidiDevices();
308         }
309     } /* class MidiDeviceCallback */
310 
311     //
312     // View.OnClickListener Override - Handles button clicks
313     //
314     @Override
onClick(View view)315     public void onClick(View view) {
316         switch (view.getId()) {
317         case R.id.midiTestUSBInterfaceBtn:
318             mUSBTestModule.startLoopbackTest();
319             break;
320 
321         case R.id.midiTestVirtInterfaceBtn:
322             mVirtTestModule.startLoopbackTest();
323             break;
324 
325         case R.id.midiTestBTInterfaceBtn:
326             mBTTestModule.startLoopbackTest();
327             break;
328 
329         default:
330             assert false : "Unhandled button click";
331         }
332     }
333 
334     /**
335      * A class to control and represent the state of a given test.
336      * It hold the data needed for IO, and the logic for sending, receiving and matching
337      * the MIDI data stream.
338      */
339     public class NDKMidiTestModule {
340         private static final String TAG = "NDKMidiTestModule";
341         private static final boolean DEBUG = true;
342 
343         private NDKMidiActivity     mMidiActivity;
344 
345         private MidiManager         mMidiManager;
346         private NativeMidiManager   mNativeMidiManager;
347 
348         // Test State
349         private final Object        mTestLock = new Object();
350         private boolean             mTestRunning;
351 
352         // Timeout handling
353         private static final int    TEST_TIMEOUT_MS = 1000;
354         private final Timer         mTimeoutTimer = new Timer();
355 
356         // Test Peripheral
357         MidiIODevice                mIODevice = new MidiIODevice();
358 
359         // Test Status
360         protected static final int TESTSTATUS_NOTRUN = 0;
361         protected static final int TESTSTATUS_PASSED = 1;
362         protected static final int TESTSTATUS_FAILED_MISMATCH = 2;
363         protected static final int TESTSTATUS_FAILED_TIMEOUT = 3;
364         protected static final int TESTSTATUS_FAILED_OVERRUN = 4;
365         protected static final int TESTSTATUS_FAILED_DEVICE = 5;
366         protected static final int TESTSTATUS_FAILED_JNI = 6;
367         protected int mTestStatus = TESTSTATUS_NOTRUN;
368 
369         /**
370          * A class to hold the MidiDeviceInfo and ports objects associated
371          * with a MIDI I/O peripheral.
372          */
373         class MidiIODevice {
374             private static final String TAG = "MidiIODevice";
375 
376             public MidiDeviceInfo   mSendDevInfo;
377             public MidiDeviceInfo   mReceiveDevInfo;
378 
379             public MidiInputPort    mSendPort;
380             public MidiOutputPort   mReceivePort;
381 
scanDevices(MidiDeviceInfo[] devInfos, int typeID)382             public void scanDevices(MidiDeviceInfo[] devInfos, int typeID) {
383                 if (DEBUG) {
384                     Log.i(TAG, "---- scanDevices() typeID: " + typeID);
385                 }
386 
387                 mSendDevInfo = null;
388                 mReceiveDevInfo = null;
389                 mSendPort = null;
390                 mReceivePort = null;
391 
392                 for (MidiDeviceInfo devInfo : devInfos) {
393                     // Inputs?
394                     int numInPorts = devInfo.getInputPortCount();
395                     if (numInPorts <= 0) {
396                         continue; // none?
397                     }
398                     if (devInfo.getType() == typeID && mSendDevInfo == null) {
399                         mSendDevInfo = devInfo;
400                     }
401 
402                     // Outputs?
403                     int numOutPorts = devInfo.getOutputPortCount();
404                     if (numOutPorts <= 0) {
405                         continue; // none?
406                     }
407                     if (devInfo.getType() == typeID && mReceiveDevInfo == null) {
408                         mReceiveDevInfo = devInfo;
409                     }
410 
411                     if (mSendDevInfo != null && mReceiveDevInfo != null) {
412                         break;  // we have an in and out device, so we can stop scanning
413                     }
414                 }
415 
416                 if (DEBUG) {
417                     if (mSendDevInfo != null) {
418                         Log.i(TAG, "---- mSendDevInfo: " + mSendDevInfo);
419                     }
420                     if (mReceiveDevInfo != null) {
421                         Log.i(TAG, "---- mReceiveDevInfo: " + mReceiveDevInfo);
422                     }
423                 }
424             }
425 
openPorts(MidiDevice device, MidiReceiver receiver)426             protected void openPorts(MidiDevice device, MidiReceiver receiver) {
427                 if (DEBUG) {
428                     Log.i(TAG, "---- openPorts()");
429                 }
430                 MidiDeviceInfo deviceInfo = device.getInfo();
431                 int numOutputs = deviceInfo.getOutputPortCount();
432                 if (numOutputs > 0) {
433                     mReceivePort = device.openOutputPort(0);
434                     mReceivePort.connect(receiver);
435                 }
436 
437                 int numInputs = deviceInfo.getInputPortCount();
438                 if (numInputs != 0) {
439                     mSendPort = device.openInputPort(0);
440                 }
441             }
442 
closePorts()443             public void closePorts() {
444                 if (DEBUG) {
445                     Log.i(TAG, "---- closePorts()");
446                 }
447                 try {
448                     if (mSendPort != null) {
449                         mSendPort.close();
450                         mSendPort = null;
451                     }
452                     if (mReceivePort != null) {
453                         mReceivePort.close();
454                         mReceivePort = null;
455                     }
456                 } catch (IOException ex) {
457                     Log.e(TAG, "IOException Closing MIDI ports: " + ex);
458                 }
459             }
460 
getInputName()461             public String getInputName() {
462                 return mReceiveDevInfo != null
463                         ? mReceiveDevInfo.getProperties().getString(MidiDeviceInfo.PROPERTY_NAME)
464                         : "";
465             }
466 
getOutputName()467             public String getOutputName() {
468                 return mSendDevInfo != null
469                         ? mSendDevInfo.getProperties().getString(MidiDeviceInfo.PROPERTY_NAME)
470                         : "";
471             }
472         }   /* class MidiIODevice */
473 
NDKMidiTestModule(NDKMidiActivity midiActivity, MidiManager midiManager)474         public NDKMidiTestModule(NDKMidiActivity midiActivity, MidiManager midiManager) {
475             mMidiActivity = midiActivity;
476             mMidiManager = midiManager;
477             mNativeMidiManager = new NativeMidiManager();
478 
479             // this call is just to keep the build from stripping out "endTest", because
480             // it is only called from JNI.
481             endTest(TESTSTATUS_NOTRUN);
482         }
483 
484         // UI Helper
getTestStatusString()485         public String getTestStatusString() {
486             Resources appResources = mMidiActivity.getApplicationContext().getResources();
487             int status;
488             synchronized (mTestLock) {
489                 status = mTestStatus;
490             }
491             switch (status) {
492             case TESTSTATUS_NOTRUN:
493                 return appResources.getString(R.string.midiNotRunLbl);
494 
495             case TESTSTATUS_PASSED:
496                 return appResources.getString(R.string.midiPassedLbl);
497 
498             case TESTSTATUS_FAILED_MISMATCH:
499                 return appResources.getString(R.string.midiFailedMismatchLbl);
500 
501             case TESTSTATUS_FAILED_TIMEOUT:
502                 return appResources.getString(R.string.midiFailedTimeoutLbl);
503 
504             case TESTSTATUS_FAILED_OVERRUN:
505                 return appResources.getString(R.string.midiFailedOverrunLbl);
506 
507             case TESTSTATUS_FAILED_DEVICE:
508                 return appResources.getString(R.string.midiFailedDeviceLbl);
509 
510             case TESTSTATUS_FAILED_JNI:
511                 return appResources.getString(R.string.midiFailedJNILbl);
512 
513             default:
514                 return "Unknown Test Status.";
515             }
516         }
517 
scanDevices(MidiDeviceInfo[] devInfos, int typeID)518         public void scanDevices(MidiDeviceInfo[] devInfos, int typeID) {
519             mIODevice.scanDevices(devInfos, typeID);
520         }
521 
showTimeoutMessage()522         public void showTimeoutMessage() {
523             mMidiActivity.runOnUiThread(new Runnable() {
524                 public void run() {
525                     synchronized (mTestLock) {
526                         if (mTestRunning) {
527                             if (DEBUG) {
528                                 Log.i(TAG, "---- Test Failed - TIMEOUT");
529                             }
530                             mTestStatus = TESTSTATUS_FAILED_TIMEOUT;
531                             mMidiActivity.updateTestStateUI();
532                         }
533                     }
534                 }
535             });
536         }
537 
startLoopbackTest()538         public void startLoopbackTest() {
539             synchronized (mTestLock) {
540                 mTestRunning = true;
541                 mMidiActivity.enableTestButtons(false);
542             }
543 
544             if (DEBUG) {
545                 Log.i(TAG, "---- startLoopbackTest()");
546             }
547 
548             synchronized (mTestLock) {
549                 mTestStatus = TESTSTATUS_NOTRUN;
550             }
551 
552             if (mIODevice.mSendDevInfo != null) {
553                 mMidiManager.openDevice(mIODevice.mSendDevInfo, new TestModuleOpenListener(), null);
554             }
555 
556             // Start the timeout timer
557             TimerTask task = new TimerTask() {
558                 @Override
559                 public void run() {
560                     synchronized (mTestLock) {
561                         if (mTestRunning) {
562                             // Timeout
563                             showTimeoutMessage();
564                             mMidiActivity.enableTestButtons(true);
565                         }
566                     }
567                 }
568             };
569             mTimeoutTimer.schedule(task, TEST_TIMEOUT_MS);
570         }
571 
getInputName()572         public String getInputName() {
573             return mIODevice.getInputName();
574         }
575 
getOutputName()576         public String getOutputName() {
577             return mIODevice.getOutputName();
578         }
579 
isTestReady()580         public boolean isTestReady() {
581             return mIODevice.mReceiveDevInfo != null && mIODevice.mSendDevInfo != null;
582         }
583 
hasTestPassed()584         public boolean hasTestPassed() {
585             int status;
586             synchronized (mTestLock) {
587                 status = mTestStatus;
588             }
589             return status == TESTSTATUS_PASSED;
590         }
591 
endTest(int endCode)592         public void endTest(int endCode) {
593             synchronized (mTestLock) {
594                 mTestRunning = false;
595                 mTestStatus = endCode;
596             }
597             if (endCode != TESTSTATUS_NOTRUN) {
598                 mMidiActivity.updateTestStateUI();
599                 mMidiActivity.enableTestButtons(true);
600             }
601         }
602 
603         /**
604          * Listens for MIDI device opens. Opens I/O ports and sends out the apriori
605          * setup messages.
606          */
607         class TestModuleOpenListener implements MidiManager.OnDeviceOpenedListener {
608             //
609             // This is where the logical part of the test starts
610             //
611             @Override
onDeviceOpened(MidiDevice device)612             public void onDeviceOpened(MidiDevice device) {
613                 if (DEBUG) {
614                     Log.i(TAG, "---- onDeviceOpened()");
615                 }
616                 mNativeMidiManager.startTest(NDKMidiTestModule.this, device);
617             }
618         }
619     } /* class NDKMidiTestModule */
620 
621     /**
622      * Test Module for Bluetooth Loopback.
623      * This is a specialization of NDKMidiTestModule (which has the connections for the BL device
624      * itself) with and added MidiIODevice object for the USB audio device which does the
625      * "looping back".
626      */
627     private class BTMidiTestModule extends NDKMidiTestModule {
628         private static final String TAG = "BTMidiTestModule";
629         private MidiIODevice mUSBLoopbackDevice = new MidiIODevice();
630 
BTMidiTestModule(NDKMidiActivity midiActivity, MidiManager midiManager)631         public BTMidiTestModule(NDKMidiActivity midiActivity, MidiManager midiManager) {
632             super(midiActivity, midiManager);
633         }
634 
635         @Override
scanDevices(MidiDeviceInfo[] devInfos, int typeID)636         public void scanDevices(MidiDeviceInfo[] devInfos, int typeID) {
637             // (normal) Scan for BT MIDI device
638             super.scanDevices(devInfos, typeID);
639             // Find a USB Loopback Device
640             mUSBLoopbackDevice.scanDevices(devInfos, MidiDeviceInfo.TYPE_USB);
641         }
642 
startLoopbackTest()643         public void startLoopbackTest() {
644             if (DEBUG) {
645                 Log.i(TAG, "---- startLoopbackTest()");
646             }
647             // Setup the USB Loopback Device
648             mUSBLoopbackDevice.closePorts();
649 
650             if (mIODevice.mSendDevInfo != null) {
651                 mMidiManager.openDevice(
652                         mUSBLoopbackDevice.mSendDevInfo, new USBLoopbackOpenListener(), null);
653             }
654 
655             // Now start the test as usual
656             super.startLoopbackTest();
657         }
658 
659         /**
660          * We need this OnDeviceOpenedListener to open the USB-Loopback device
661          */
662         private class USBLoopbackOpenListener implements MidiManager.OnDeviceOpenedListener {
663             @Override
onDeviceOpened(MidiDevice device)664             public void onDeviceOpened(MidiDevice device) {
665                 if (DEBUG) {
666                     Log.i("USBLoopbackOpenListener", "---- onDeviceOpened()");
667                 }
668                 mUSBLoopbackDevice.openPorts(device, new USBMidiEchoReceiver());
669             }
670         } /* class USBLoopbackOpenListener */
671 
672         /**
673          * MidiReceiver subclass for BlueTooth Loopback Test
674          *
675          * This class receives bytes from the USB Interface (presumably coming from the
676          * Bluetooth MIDI peripheral) and echoes them back out (presumably to the Bluetooth
677          * MIDI peripheral).
678          */
679         private class USBMidiEchoReceiver extends MidiReceiver {
680             private int mTotalBytesEchoed;
681 
682             @Override
onSend(byte[] msg, int offset, int count, long timestamp)683             public void onSend(byte[] msg, int offset, int count, long timestamp) throws IOException {
684                 mTotalBytesEchoed += count;
685                 if (DEBUG) {
686                     Log.i(TAG, "---- USBMidiEchoReceiver.onSend() count:" + count +
687                             " total:" + mTotalBytesEchoed);
688                 }
689                 mUSBLoopbackDevice.mSendPort.onSend(msg, offset, count, timestamp);
690             }
691         } /* class USBMidiEchoReceiver */
692     } /* class BTMidiTestModule */
693 } /* class NDKMidiActivity */
694