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