1 /* 2 * Copyright (C) 2021 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.content.ComponentName; 20 import android.content.Context; 21 import android.content.Intent; 22 import android.content.ServiceConnection; 23 import android.content.pm.PackageManager; 24 import android.content.res.Resources; 25 import android.media.midi.MidiDeviceInfo; 26 import android.media.midi.MidiManager; 27 import android.os.Bundle; 28 import android.os.Handler; 29 import android.os.IBinder; 30 import android.os.Looper; 31 import android.util.Log; 32 import android.view.View; 33 import android.widget.Button; 34 import android.widget.TextView; 35 36 import com.android.cts.verifier.PassFailButtons; 37 import com.android.cts.verifier.R; 38 import com.android.cts.verifier.audio.midilib.MidiTestModule; 39 import com.android.midi.VerifierMidiEchoService; 40 41 import java.util.Collection; 42 import java.util.concurrent.Executor; 43 44 /** 45 * Common information and behaviors for the MidiJavaTestActivity and MidiNativeTestActivity 46 */ 47 public abstract class MidiTestActivityBase 48 extends PassFailButtons.Activity 49 implements View.OnClickListener { 50 51 private static final String TAG = "MidiTestActivityBase"; 52 private static final boolean DEBUG = true; 53 54 protected MidiManager mMidiManager; 55 56 protected Intent mMidiServiceIntent; 57 private MidiServiceConnection mMidiServiceConnection; 58 59 // Flags 60 protected boolean mHasMIDI; 61 62 private MidiTestModule mUSBTestModule; 63 private MidiTestModule mVirtualTestModule; 64 private MidiTestModule mBTTestModule; 65 66 // Widgets 67 protected Button mUSBTestBtn; 68 protected Button mVirtTestBtn; 69 protected Button mBTTestBtn; 70 71 protected TextView mUSBIInputDeviceLbl; 72 protected TextView mUSBOutputDeviceLbl; 73 protected TextView mUSBTestStatusTxt; 74 75 protected TextView mVirtInputDeviceLbl; 76 protected TextView mVirtOutputDeviceLbl; 77 protected TextView mVirtTestStatusTxt; 78 79 protected TextView mBTInputDeviceLbl; 80 protected TextView mBTOutputDeviceLbl; 81 protected TextView mBTTestStatusTxt; 82 MidiTestActivityBase()83 public MidiTestActivityBase() { 84 } 85 initTestModules(MidiTestModule USBTestModule, MidiTestModule virtualTestModule, MidiTestModule BTTestModule)86 protected void initTestModules(MidiTestModule USBTestModule, 87 MidiTestModule virtualTestModule, 88 MidiTestModule BTTestModule) { 89 mUSBTestModule = USBTestModule; 90 mVirtualTestModule = virtualTestModule; 91 mBTTestModule = BTTestModule; 92 } 93 94 @Override onCreate(Bundle savedInstanceState)95 protected void onCreate(Bundle savedInstanceState) { 96 super.onCreate(savedInstanceState); 97 98 mMidiManager = getSystemService(MidiManager.class); 99 100 // Standard PassFailButtons.Activity initialization 101 setPassFailButtonClickListeners(); 102 setInfoResources(R.string.midi_test, R.string.midi_info, -1); 103 104 // May as well calculate this right off the bat. 105 mHasMIDI = hasMIDI(); 106 ((TextView)findViewById(R.id.midiHasMIDILbl)).setText("" + mHasMIDI); 107 108 mUSBTestBtn = (Button)findViewById(R.id.midiTestUSBInterfaceBtn); 109 mUSBTestBtn.setOnClickListener(this); 110 mUSBIInputDeviceLbl = (TextView)findViewById(R.id.midiUSBInputLbl); 111 mUSBOutputDeviceLbl = (TextView)findViewById(R.id.midiUSBOutputLbl); 112 mUSBTestStatusTxt = (TextView)findViewById(R.id.midiUSBTestStatusLbl); 113 114 mVirtTestBtn = (Button)findViewById(R.id.midiTestVirtInterfaceBtn); 115 mVirtTestBtn.setOnClickListener(this); 116 mVirtInputDeviceLbl = (TextView)findViewById(R.id.midiVirtInputLbl); 117 mVirtOutputDeviceLbl = (TextView)findViewById(R.id.midiVirtOutputLbl); 118 mVirtTestStatusTxt = (TextView)findViewById(R.id.midiVirtTestStatusLbl); 119 120 mBTTestBtn = (Button)findViewById(R.id.midiTestBTInterfaceBtn); 121 mBTTestBtn.setOnClickListener(this); 122 mBTInputDeviceLbl = (TextView)findViewById(R.id.midiBTInputLbl); 123 mBTOutputDeviceLbl = (TextView)findViewById(R.id.midiBTOutputLbl); 124 mBTTestStatusTxt = (TextView)findViewById(R.id.midiBTTestStatusLbl); 125 126 calcTestPassed(); 127 } 128 129 @Override onResume()130 protected void onResume() { 131 super.onResume(); 132 if (DEBUG) { 133 Log.i(TAG, "---- Loading Virtual MIDI Service ..."); 134 } 135 mMidiServiceConnection = new MidiServiceConnection(); 136 boolean isBound = 137 bindService(mMidiServiceIntent, mMidiServiceConnection, Context.BIND_AUTO_CREATE); 138 if (DEBUG) { 139 Log.i(TAG, "---- Virtual MIDI Service loaded: " + isBound); 140 } 141 } 142 143 @Override onPause()144 protected void onPause() { 145 super.onPause(); 146 if (DEBUG) { 147 Log.i(TAG, "---- onPause()"); 148 } 149 150 unbindService(mMidiServiceConnection); 151 mMidiServiceConnection = null; 152 } 153 hasMIDI()154 private boolean hasMIDI() { 155 // CDD Section C-1-4: android.software.midi 156 return getPackageManager().hasSystemFeature(PackageManager.FEATURE_MIDI); 157 } 158 startMidiEchoServer()159 void startMidiEchoServer() { 160 // Init MIDI Stuff 161 mMidiServiceIntent = new Intent(this, VerifierMidiEchoService.class); 162 } 163 connectDeviceListener()164 void connectDeviceListener() { 165 // Plug in device connect/disconnect callback 166 final Handler handler = new Handler(Looper.getMainLooper()); 167 final Executor executor = handler::post; 168 mMidiManager.registerDeviceCallback(MidiManager.TRANSPORT_MIDI_BYTE_STREAM, 169 executor, new MidiDeviceCallback()); 170 } 171 startWiredLoopbackTest()172 void startWiredLoopbackTest() { 173 mUSBTestModule.startLoopbackTest(MidiTestModule.TESTID_USBLOOPBACK); 174 } 175 startVirtualLoopbackTest()176 void startVirtualLoopbackTest() { 177 mVirtualTestModule.startLoopbackTest(MidiTestModule.TESTID_VIRTUALLOOPBACK); 178 } 179 startBTLoopbackTest()180 void startBTLoopbackTest() { 181 mBTTestModule.startLoopbackTest(MidiTestModule.TESTID_BTLOOPBACK); 182 } 183 calcTestPassed()184 boolean calcTestPassed() { 185 boolean hasPassed = false; 186 if (!mHasMIDI) { 187 // if it doesn't report MIDI support, then it doesn't have to pass the other tests. 188 hasPassed = true; 189 } else { 190 hasPassed = mUSBTestModule.hasTestPassed() && 191 mVirtualTestModule.hasTestPassed() && 192 mBTTestModule.hasTestPassed(); 193 } 194 195 getPassButton().setEnabled(hasPassed); 196 return hasPassed; 197 } 198 scanMidiDevices()199 void scanMidiDevices() { 200 if (DEBUG) { 201 Log.i(TAG, "scanMidiDevices()...."); 202 } 203 204 // Get the list of all MIDI devices attached 205 Collection<MidiDeviceInfo> devInfos = mMidiManager.getDevicesForTransport( 206 MidiManager.TRANSPORT_MIDI_BYTE_STREAM); 207 if (DEBUG) { 208 Log.i(TAG, " numDevices:" + devInfos.size()); 209 } 210 211 // Let each module select (if available) the associated device for their type 212 mUSBTestModule.scanDevices(devInfos); 213 mVirtualTestModule.scanDevices(devInfos); 214 mBTTestModule.scanDevices(devInfos); 215 216 showConnectedMIDIPeripheral(); 217 } 218 219 // 220 // UI Updaters 221 // showConnectedMIDIPeripheral()222 void showConnectedMIDIPeripheral() { 223 // USB 224 mUSBIInputDeviceLbl.setText(mUSBTestModule.getInputName()); 225 mUSBOutputDeviceLbl.setText(mUSBTestModule.getOutputName()); 226 mUSBTestBtn.setEnabled(mUSBTestModule.isTestReady()); 227 228 // Virtual MIDI 229 mVirtInputDeviceLbl.setText(mVirtualTestModule.getInputName()); 230 mVirtOutputDeviceLbl.setText(mVirtualTestModule.getOutputName()); 231 mVirtTestBtn.setEnabled(mVirtualTestModule.isTestReady()); 232 233 // Bluetooth 234 mBTInputDeviceLbl.setText(mBTTestModule.getInputName()); 235 mBTOutputDeviceLbl.setText(mBTTestModule.getOutputName()); 236 // use mUSBTestModule.isTestReady() as a proxy for knowing the interface loopback 237 // is connected 238 mBTTestBtn.setEnabled(mBTTestModule.isTestReady() && mUSBTestModule.isTestReady()); 239 } 240 241 // 242 // UI Updaters 243 // showUSBTestStatus()244 void showUSBTestStatus() { 245 mUSBTestStatusTxt.setText(getTestStatusString(mUSBTestModule.getTestStatus())); 246 } 247 showVirtTestStatus()248 void showVirtTestStatus() { 249 mVirtTestStatusTxt.setText(getTestStatusString(mVirtualTestModule.getTestStatus())); 250 } 251 showBTTestStatus()252 void showBTTestStatus() { 253 mBTTestStatusTxt.setText(getTestStatusString(mBTTestModule.getTestStatus())); 254 } 255 enableTestButtons(boolean enable)256 void enableTestButtons(boolean enable) { 257 runOnUiThread(new Runnable() { 258 public void run() { 259 if (enable) { 260 // remember, a given test might not be enabled, so we can't just enable 261 // all of the buttons 262 showConnectedMIDIPeripheral(); 263 } else { 264 mUSBTestBtn.setEnabled(enable); 265 mVirtTestBtn.setEnabled(enable); 266 mBTTestBtn.setEnabled(enable); 267 } 268 } 269 }); 270 } 271 272 // Need this to update UI from MIDI read thread updateTestStateUI()273 public void updateTestStateUI() { 274 runOnUiThread(new Runnable() { 275 public void run() { 276 calcTestPassed(); 277 showUSBTestStatus(); 278 showVirtTestStatus(); 279 showBTTestStatus(); 280 } 281 }); 282 } 283 284 // UI Helper getTestStatusString(int status)285 public String getTestStatusString(int status) { 286 Resources appResources = getApplicationContext().getResources(); 287 switch (status) { 288 case MidiTestModule.TESTSTATUS_NOTRUN: 289 return appResources.getString(R.string.midiNotRunLbl); 290 291 case MidiTestModule.TESTSTATUS_PASSED: 292 return appResources.getString(R.string.midiPassedLbl); 293 294 case MidiTestModule.TESTSTATUS_FAILED_MISMATCH: 295 return appResources.getString(R.string.midiFailedMismatchLbl); 296 297 case MidiTestModule.TESTSTATUS_FAILED_TIMEOUT: 298 return appResources.getString(R.string.midiFailedTimeoutLbl); 299 300 case MidiTestModule.TESTSTATUS_FAILED_OVERRUN: 301 return appResources.getString(R.string.midiFailedOverrunLbl); 302 303 case MidiTestModule.TESTSTATUS_FAILED_DEVICE: 304 return appResources.getString(R.string.midiFailedDeviceLbl); 305 306 case MidiTestModule.TESTSTATUS_FAILED_JNI: 307 return appResources.getString(R.string.midiFailedJNILbl); 308 309 case MidiTestModule.TESTSTATUS_FAILED_SETUP: 310 return appResources.getString(R.string.midiFailedSetupLbl); 311 312 case MidiTestModule.TESTSTATUS_FAILED_SEND: 313 return appResources.getString(R.string.midiFailedSendLbl); 314 315 default: 316 return "Unknown Test Status."; 317 } 318 } 319 320 // 321 // View.OnClickListener Override - Handles button clicks 322 // 323 @Override onClick(View view)324 public void onClick(View view) { 325 int id = view.getId(); 326 if (id == R.id.midiTestUSBInterfaceBtn) { 327 startWiredLoopbackTest(); 328 } else if (id == R.id.midiTestVirtInterfaceBtn) { 329 startVirtualLoopbackTest(); 330 } else if (id == R.id.midiTestBTInterfaceBtn) { 331 startBTLoopbackTest(); 332 } else { 333 assert false : "Unhandled button click"; 334 } 335 } 336 337 class MidiServiceConnection implements ServiceConnection { 338 private static final String TAG = "MidiServiceConnection"; 339 @Override onServiceConnected(ComponentName name, IBinder service)340 public void onServiceConnected(ComponentName name, IBinder service) { 341 if (DEBUG) { 342 Log.i(TAG, "MidiServiceConnection.onServiceConnected()"); 343 } 344 scanMidiDevices(); 345 } 346 347 @Override onServiceDisconnected(ComponentName name)348 public void onServiceDisconnected(ComponentName name) { 349 if (DEBUG) { 350 Log.i(TAG, "MidiServiceConnection.onServiceDisconnected()"); 351 } 352 } 353 } 354 355 /** 356 * Callback class for MIDI device connect/disconnect. 357 */ 358 class MidiDeviceCallback extends MidiManager.DeviceCallback { 359 private static final String TAG = "MidiDeviceCallback"; 360 361 @Override onDeviceAdded(MidiDeviceInfo device)362 public void onDeviceAdded(MidiDeviceInfo device) { 363 scanMidiDevices(); 364 } 365 366 @Override onDeviceRemoved(MidiDeviceInfo device)367 public void onDeviceRemoved(MidiDeviceInfo device) { 368 scanMidiDevices(); 369 } 370 } /* class MidiDeviceCallback */ 371 } 372