• 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 #include <cstring>
17 #include <pthread.h>
18 #include <unistd.h>
19 
20 #define TAG "MidiTestManager"
21 #include <android/log.h>
22 #define ALOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__)
23 #define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__)
24 
25 #include "MidiTestManager.h"
26 
27 static pthread_t readThread;
28 
29 static const bool DEBUG = false;
30 static const bool DEBUG_MIDIDATA = false;
31 
32 //
33 // MIDI Messages
34 //
35 // Channel Commands
36 static const uint8_t kMIDIChanCmd_KeyDown = 9;
37 static const uint8_t kMIDIChanCmd_KeyUp = 8;
38 static const uint8_t kMIDIChanCmd_PolyPress = 10;
39 static const uint8_t kMIDIChanCmd_Control = 11;
40 static const uint8_t kMIDIChanCmd_ProgramChange = 12;
41 static const uint8_t kMIDIChanCmd_ChannelPress = 13;
42 static const uint8_t kMIDIChanCmd_PitchWheel = 14;
43 // System Commands
44 static const uint8_t kMIDISysCmd_SysEx = 0xF0;
45 static const uint8_t kMIDISysCmd_EndOfSysEx =  0xF7;
46 static const uint8_t kMIDISysCmd_ActiveSensing = 0xFE;
47 static const uint8_t kMIDISysCmd_Reset = 0xFF;
48 
readThreadRoutine(void * context)49 static void* readThreadRoutine(void * context) {
50     MidiTestManager* testManager = (MidiTestManager*)context;
51     return reinterpret_cast<void*>(static_cast<intptr_t>(testManager->ProcessInput()));
52 }
53 
54 /*
55  * TestMessage
56  */
57 #define makeMIDICmd(cmd, channel)  (uint8_t)((cmd << 4) | (channel & 0x0F))
58 
59 class TestMessage {
60 public:
61     uint8_t*   mMsgBytes;
62     int     mNumMsgBytes;
63 
TestMessage()64     TestMessage()
65         : mMsgBytes(NULL), mNumMsgBytes(0)
66     {}
67 
~TestMessage()68     ~TestMessage() {
69         delete[] mMsgBytes;
70     }
71 
set(uint8_t * msgBytes,int numMsgBytes)72     bool set(uint8_t* msgBytes, int numMsgBytes) {
73         if (msgBytes == NULL || numMsgBytes <= 0) {
74             return false;
75         }
76         mNumMsgBytes = numMsgBytes;
77         mMsgBytes = new uint8_t[numMsgBytes];
78         memcpy(mMsgBytes, msgBytes, mNumMsgBytes * sizeof(uint8_t));
79         return true;
80     }
81 }; /* class TestMessage */
82 
83 /*
84  * MidiTestManager
85  */
MidiTestManager()86 MidiTestManager::MidiTestManager()
87     : mTestModuleObj(NULL),
88       mTestStream(NULL), mNumTestStreamBytes(0),
89       mReceiveStreamPos(0),
90       mMidiSendPort(NULL), mMidiReceivePort(NULL),
91       mTestMsgs(NULL), mNumTestMsgs(0)
92 {}
93 
~MidiTestManager()94 MidiTestManager::~MidiTestManager(){
95     delete[] mTestStream;
96 }
97 
jniSetup(JNIEnv * env)98 void MidiTestManager::jniSetup(JNIEnv* env) {
99     env->GetJavaVM(&mJvm);
100 
101     jclass clsMidiTestModule =
102         env->FindClass("com/android/cts/verifier/audio/NDKMidiActivity$NDKMidiTestModule");
103     if (DEBUG) {
104         ALOGI("gClsMidiTestModule:%p", clsMidiTestModule);
105     }
106 
107     // public void endTest(int endCode)
108     mMidEndTest = env->GetMethodID(clsMidiTestModule, "endTest", "(I)V");
109     if (DEBUG) {
110         ALOGI("mMidEndTestgMidEndTest:%p", mMidEndTest);
111     }
112 }
113 
buildTestStream()114 void MidiTestManager::buildTestStream() {
115     // add up the total space
116     mNumTestStreamBytes = 0;
117     for(int msgIndex = 0; msgIndex < mNumTestMsgs; msgIndex++) {
118         mNumTestStreamBytes += mTestMsgs[msgIndex].mNumMsgBytes;
119     }
120 
121     delete[] mTestStream;
122     mTestStream = new uint8_t[mNumTestStreamBytes];
123     int streamIndex = 0;
124     for(int msgIndex = 0; msgIndex < mNumTestMsgs; msgIndex++) {
125         for(int byteIndex = 0; byteIndex < mTestMsgs[msgIndex].mNumMsgBytes; byteIndex++) {
126             mTestStream[streamIndex++] = mTestMsgs[msgIndex].mMsgBytes[byteIndex];
127         }
128     }
129 
130     // Reset stream position
131     mReceiveStreamPos = 0;
132 }
133 
134 /**
135  * Compares the supplied bytes against the sent message stream at the current postion
136  * and advances the stream position.
137  */
matchStream(uint8_t * bytes,int count)138 bool MidiTestManager::matchStream(uint8_t* bytes, int count) {
139     if (DEBUG) {
140         ALOGI("---- matchStream() count:%d", count);
141     }
142     bool matches = true;
143 
144     for (int index = 0; index < count; index++) {
145         if (bytes[index] != mTestStream[mReceiveStreamPos]) {
146             matches = false;
147             if (DEBUG) {
148                 ALOGI("---- mismatch @%d [%d : %d]",
149                         index, bytes[index], mTestStream[mReceiveStreamPos]);
150             }
151         }
152         mReceiveStreamPos++;
153     }
154 
155     if (DEBUG) {
156         ALOGI("  returns:%d", matches);
157     }
158     return matches;
159 }
160 
161 /**
162  * Writes out the list of MIDI messages to the output port.
163  * Returns total number of bytes sent.
164  */
sendMessages()165 int MidiTestManager::sendMessages() {
166     if (DEBUG) {
167         ALOGI("---- sendMessages()...");
168         for(int msgIndex = 0; msgIndex < mNumTestMsgs; msgIndex++) {
169             if (DEBUG_MIDIDATA) {
170             ALOGI("--------");
171                 for(int byteIndex = 0; byteIndex < mTestMsgs[msgIndex].mNumMsgBytes; byteIndex++) {
172                     ALOGI("  0x%X", mTestMsgs[msgIndex].mMsgBytes[byteIndex]);
173                 }
174             }
175         }
176     }
177 
178     int totalSent = 0;
179     for(int msgIndex = 0; msgIndex < mNumTestMsgs; msgIndex++) {
180         ssize_t numSent =
181             AMidiInputPort_send(mMidiSendPort,
182                     mTestMsgs[msgIndex].mMsgBytes, mTestMsgs[msgIndex].mNumMsgBytes);
183         totalSent += numSent;
184     }
185 
186     if (DEBUG) {
187         ALOGI("---- totalSent:%d", totalSent);
188     }
189 
190     return totalSent;
191 }
192 
ProcessInput()193 int MidiTestManager::ProcessInput() {
194     uint8_t readBuffer[128];
195     size_t totalNumReceived = 0;
196 
197     bool testRunning = true;
198     int testResult = TESTSTATUS_NOTRUN;
199 
200     int32_t opCode;
201     size_t numBytesReceived;
202     int64_t timeStamp;
203     while (testRunning) {
204         // AMidiOutputPort_receive is non-blocking, so let's not burn up the CPU unnecessarily
205         usleep(2000);
206 
207         numBytesReceived = 0;
208         ssize_t numMessagesReceived =
209             AMidiOutputPort_receive(mMidiReceivePort, &opCode, readBuffer, 128,
210                         &numBytesReceived, &timeStamp);
211 
212         if (testRunning &&
213             numBytesReceived > 0 &&
214             opCode == AMIDI_OPCODE_DATA &&
215             readBuffer[0] != kMIDISysCmd_ActiveSensing &&
216             readBuffer[0] != kMIDISysCmd_Reset) {
217             if (DEBUG) {
218                 ALOGI("---- msgs:%zd, bytes:%zu", numMessagesReceived, numBytesReceived);
219             }
220             // Process Here
221             if (!matchStream(readBuffer, numBytesReceived)) {
222                 testResult = TESTSTATUS_FAILED_MISMATCH;
223                 testRunning = false;   // bail
224             }
225             totalNumReceived += numBytesReceived;
226             if (totalNumReceived > mNumTestStreamBytes) {
227                 testResult = TESTSTATUS_FAILED_OVERRUN;
228                 testRunning = false;   // bail
229             }
230             if (totalNumReceived == mNumTestStreamBytes) {
231                 testResult = TESTSTATUS_PASSED;
232                 testRunning = false;   // done
233             }
234         }
235     }
236 
237     return testResult;
238 }
239 
StartReading(AMidiDevice * nativeReadDevice)240 bool MidiTestManager::StartReading(AMidiDevice* nativeReadDevice) {
241     ALOGI("StartReading()...");
242 
243     media_status_t m_status =
244         AMidiOutputPort_open(nativeReadDevice, 0, &mMidiReceivePort);
245     if (m_status != 0) {
246         ALOGE("Can't open MIDI device for reading err:%d", m_status);
247         return false;
248     }
249 
250     // Start read thread
251     int status = pthread_create(&readThread, NULL, readThreadRoutine, this);
252     if (status != 0) {
253         ALOGE("Can't start readThread: %s (%d)", strerror(status), status);
254     }
255     return status == 0;
256 }
257 
StartWriting(AMidiDevice * nativeWriteDevice)258 bool MidiTestManager::StartWriting(AMidiDevice* nativeWriteDevice) {
259     ALOGI("StartWriting()...");
260 
261     media_status_t status =
262         AMidiInputPort_open(nativeWriteDevice, 0, &mMidiSendPort);
263     if (status != 0) {
264         ALOGE("Can't open MIDI device for writing err:%d", status);
265         return false;
266     }
267     return true;
268 }
269 
270 uint8_t msg0[] = {makeMIDICmd(kMIDIChanCmd_KeyDown, 0), 64, 120};
271 //uint8_t msg0Alt[] = {makeMIDICmd(kMIDIChanCmd_KeyDown, 0), 65, 120};
272 uint8_t msg1[] = {makeMIDICmd(kMIDIChanCmd_KeyUp, 0), 64, 35};
273 
RunTest(jobject testModuleObj,AMidiDevice * sendDevice,AMidiDevice * receiveDevice)274 bool MidiTestManager::RunTest(jobject testModuleObj, AMidiDevice* sendDevice,
275         AMidiDevice* receiveDevice) {
276     if (DEBUG) {
277         ALOGI("RunTest(%p, %p, %p)", testModuleObj, sendDevice, receiveDevice);
278     }
279 
280     JNIEnv* env;
281     mJvm->AttachCurrentThread(&env, NULL);
282     if (env == NULL) {
283         EndTest(TESTSTATUS_FAILED_JNI);
284     }
285 
286     mTestModuleObj = env->NewGlobalRef(testModuleObj);
287 
288     // Call StartWriting first because StartReading starts a thread.
289     if (!StartWriting(sendDevice) || !StartReading(receiveDevice)) {
290         // Test call to EndTest will close any open devices.
291         EndTest(TESTSTATUS_FAILED_DEVICE);
292         return false; // bail
293     }
294 
295     // setup messages
296     delete[] mTestMsgs;
297     mNumTestMsgs = 3;
298     mTestMsgs = new TestMessage[mNumTestMsgs];
299 
300     int sysExSize = 8;
301     uint8_t* sysExMsg = new uint8_t[sysExSize];
302     sysExMsg[0] = kMIDISysCmd_SysEx;
303     for(int index = 1; index < sysExSize-1; index++) {
304         sysExMsg[index] = (uint8_t)index;
305     }
306     sysExMsg[sysExSize-1] = kMIDISysCmd_EndOfSysEx;
307 
308     if (!mTestMsgs[0].set(msg0, sizeof(msg0)) ||
309         !mTestMsgs[1].set(msg1, sizeof(msg1)) ||
310         !mTestMsgs[2].set(sysExMsg, sysExSize)) {
311         return false;
312     }
313     delete[] sysExMsg;
314 
315     buildTestStream();
316 
317     // Inject an error
318     // mTestMsgs[0].set(msg0Alt, 3);
319 
320     sendMessages();
321     void* threadRetval = (void*)TESTSTATUS_NOTRUN;
322     int status = pthread_join(readThread, &threadRetval);
323     if (status != 0) {
324         ALOGE("Failed to join readThread: %s (%d)", strerror(status), status);
325     }
326     EndTest(static_cast<int>(reinterpret_cast<intptr_t>(threadRetval)));
327     return true;
328 }
329 
EndTest(int endCode)330 void MidiTestManager::EndTest(int endCode) {
331 
332     JNIEnv* env;
333     mJvm->AttachCurrentThread(&env, NULL);
334     if (env == NULL) {
335         ALOGE("Error retrieving JNI Env");
336     }
337 
338     env->CallVoidMethod(mTestModuleObj, mMidEndTest, endCode);
339     env->DeleteGlobalRef(mTestModuleObj);
340 
341     // EndTest() will ALWAYS be called, so we can close the ports here.
342     if (mMidiSendPort != NULL) {
343         AMidiInputPort_close(mMidiSendPort);
344         mMidiSendPort = NULL;
345     }
346     if (mMidiReceivePort != NULL) {
347         AMidiOutputPort_close(mMidiReceivePort);
348         mMidiReceivePort = NULL;
349     }
350 }
351