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