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 #include <atomic>
18 #include <inttypes.h>
19 #include <stdio.h>
20 #include <string>
21 #include <thread>
22 #include <time.h>
23 #include <vector>
24 
25 //#define LOG_NDEBUG 0
26 #define LOG_TAG "NativeMidiManager-JNI"
27 #include <android/log.h>
28 
29 #include <jni.h>
30 
31 #include <amidi/AMidi.h>
32 
33 extern "C" {
34 
35 /*
36  * Structures for storing data flowing through the echo server.
37  */
38 #define SIZE_DATABUFFER 256
39 /*
40  * Received Messages
41  */
42 typedef struct {
43     std::unique_ptr<uint8_t[]> dataBuff;
44     size_t numDataBytes;
45     int32_t opCode;
46     int64_t timestamp;
47     int64_t timeReceived;
48 } ReceivedMessageRecord;
49 
50 /*
51  * Sent Messages
52  */
53 typedef struct {
54     std::unique_ptr<uint8_t[]> dataBuff;
55     size_t numDataBytes;
56     int64_t timestamp;
57     long timeSent;
58 } SentMessageRecord;
59 
60 /*
61  * Context
62  * Holds the state of a given test and native MIDI I/O setup for that test.
63  * NOTE: There is one of these per test (and therefore unique to each test thread).
64  */
65 class TestContext {
66 private:
67     // counters
68     std::atomic<int> mNumSends;
69     std::atomic<int> mNumBytesSent;
70     std::atomic<int> mNumReceives;
71     std::atomic<int> mNumBytesReceived;
72 
73     std::vector<ReceivedMessageRecord> mReceivedMsgs;
74     std::vector<SentMessageRecord> mSentMsgs;
75 
76     // Java NativeMidiMessage class stuff, for passing messages back out to the Java client.
77     jclass mClsNativeMidiMessage;
78     jmethodID mMidNativeMidiMessage_ctor;
79     jfieldID mFid_opcode;
80     jfieldID mFid_buffer;
81     jfieldID mFid_len;
82     jfieldID mFid_timestamp;
83     jfieldID mFid_timeReceived;
84 
85     std::mutex lock;
86 
87 public:
88     // read Thread
89     std::unique_ptr<std::thread> mReadThread;
90     std::atomic<bool> mReading;
91 
92     AMidiDevice* nativeDevice;
93     std::atomic<AMidiOutputPort*> midiOutputPort;
94     std::atomic<AMidiInputPort*> midiInputPort;
95 
TestContext()96     TestContext() :
97         mNumSends(0),
98         mNumBytesSent(0),
99         mNumReceives(0),
100         mNumBytesReceived(0),
101         mClsNativeMidiMessage(0),
102         mMidNativeMidiMessage_ctor(0),
103         mFid_opcode(0),
104         mFid_buffer(0),
105         mFid_len(0),
106         mFid_timestamp(0),
107         mFid_timeReceived(0),
108         mReading(false),
109         nativeDevice(nullptr),
110         midiOutputPort(nullptr),
111         midiInputPort(nullptr)
112     {}
113 
114     bool initN(JNIEnv* env);
115 
getNumSends()116     int getNumSends() { return mNumSends; }
incNumSends()117     void incNumSends() { mNumSends++; }
118 
getNumBytesSent()119     int getNumBytesSent() { return mNumBytesSent; }
incNumBytesSent(int numBytes)120     void incNumBytesSent(int numBytes) { mNumBytesSent += numBytes; }
121 
getNumReceives()122     int getNumReceives() { return mNumReceives; }
incNumReceives()123     void incNumReceives() { mNumReceives++; }
124 
getNumBytesReceived()125     int getNumBytesReceived() { return mNumBytesReceived; }
incNumBytesReceived(int numBytes)126     void incNumBytesReceived(int numBytes) { mNumBytesReceived += numBytes; }
127 
addSent(SentMessageRecord && msg)128     void addSent(SentMessageRecord&& msg) { mSentMsgs.push_back(std::move(msg)); }
getNumSentMsgs()129     size_t getNumSentMsgs() { return mSentMsgs.size(); }
130 
addReceived(ReceivedMessageRecord && msg)131     void addReceived(ReceivedMessageRecord&& msg) { mReceivedMsgs.push_back(std::move(msg)); }
getNumReceivedMsgs()132     size_t getNumReceivedMsgs() { return mReceivedMsgs.size(); }
133 
134     jobject transferReceiveMsgAt(JNIEnv* env, int index);
135 
136     static const int COMPARE_SUCCESS = 0;
137     static const int COMPARE_COUNTMISSMATCH = 1;
138     static const int COMPARE_DATALENMISMATCH = 2;
139     static const int COMPARE_DATAMISMATCH = 3;
140     static const int COMPARE_TIMESTAMPMISMATCH = 4;
141     int compareInsAndOuts();
142 
143     static const int CHECKLATENCY_SUCCESS = 0;
144     static const int CHECKLATENCY_COUNTMISSMATCH = 1;
145     static const int CHECKLATENCY_LATENCYEXCEEDED = 2;
146     int checkInOutLatency(long maxLatencyNanos);
147 };
148 
149 //
150 // Helpers
151 //
System_nanoTime()152 static long System_nanoTime() {
153     // this code is the implementation of System.nanoTime()
154     // from system/code/ojluni/src/main/native/System.
155     struct timespec now;
156     clock_gettime(CLOCK_MONOTONIC, &now);
157     return now.tv_sec * 1000000000LL + now.tv_nsec;
158 }
159 
initN(JNIEnv * env)160 bool TestContext::initN(JNIEnv* env) {
161     static const char* clsSigNativeMidiMessage = "android/nativemidi/cts/NativeMidiMessage";
162 
163     jclass cls = env->FindClass(clsSigNativeMidiMessage);
164     if (cls == NULL) {
165         __android_log_print(ANDROID_LOG_ERROR, LOG_TAG,
166                 "JNI Error - couldn't find NativeMidiMessage class");
167         return false; // we are doomed, so bail.
168     }
169     mClsNativeMidiMessage = (jclass)env->NewGlobalRef(cls);
170     if (mClsNativeMidiMessage == NULL) {
171         __android_log_print(ANDROID_LOG_ERROR, LOG_TAG,
172                 "JNI Error - couldn't allocate NativeMidiMessage");
173         return false; // we are doomed, so bail.
174     }
175 
176     mMidNativeMidiMessage_ctor = env->GetMethodID(mClsNativeMidiMessage, "<init>", "()V");
177     mFid_opcode = env->GetFieldID(mClsNativeMidiMessage, "opcode", "I");
178     mFid_buffer = env->GetFieldID(mClsNativeMidiMessage, "buffer", "[B");
179     mFid_len = env->GetFieldID( mClsNativeMidiMessage, "len", "I");
180     mFid_timestamp = env->GetFieldID(mClsNativeMidiMessage, "timestamp", "J");
181     mFid_timeReceived = env->GetFieldID(mClsNativeMidiMessage, "timeReceived", "J");
182     if (mMidNativeMidiMessage_ctor == NULL ||
183         mFid_opcode == NULL ||
184         mFid_buffer == NULL ||
185         mFid_len == NULL ||
186         mFid_timestamp == NULL ||
187         mFid_timeReceived == NULL) {
188         __android_log_print(ANDROID_LOG_ERROR, LOG_TAG,
189                 "JNI Error - couldn't load Field IDs");
190         return false; // we are doomed, so bail.
191     }
192 
193     return true;
194 }
195 
transferReceiveMsgAt(JNIEnv * env,int index)196 jobject TestContext::transferReceiveMsgAt(JNIEnv* env, int index) {
197     jobject msg = NULL;
198 
199     if (index < (int)mReceivedMsgs.size()) {
200         ReceivedMessageRecord receiveRec = std::move(mReceivedMsgs.at(index));
201         msg = env->NewObject(mClsNativeMidiMessage, mMidNativeMidiMessage_ctor);
202 
203         env->SetIntField(msg, mFid_opcode, receiveRec.opCode);
204         env->SetIntField(msg, mFid_len, receiveRec.numDataBytes);
205         jobject buffer_array = env->GetObjectField(msg, mFid_buffer);
206         env->SetByteArrayRegion(reinterpret_cast<jbyteArray>(buffer_array), 0,
207                 receiveRec.numDataBytes, (jbyte*)receiveRec.dataBuff.get());
208         env->SetLongField(msg, mFid_timestamp, receiveRec.timestamp);
209         env->SetLongField(msg, mFid_timeReceived, receiveRec.timeReceived);
210     }
211 
212     return msg;
213 }
214 
compareInsAndOuts()215 int TestContext::compareInsAndOuts() {
216     // Number of messages sent/received
217     if (mReceivedMsgs.size() != mSentMsgs.size()) {
218         __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "---- COMPARE_COUNTMISSMATCH r:%zu s:%zu",
219                 mReceivedMsgs.size(), mSentMsgs.size());
220        return COMPARE_COUNTMISSMATCH;
221     }
222 
223     // we know that both vectors have the same number of messages from the test above.
224     size_t numMessages = mSentMsgs.size();
225     for (size_t msgIndex = 0; msgIndex < numMessages; msgIndex++) {
226         // Data Length?
227         if (mReceivedMsgs[msgIndex].numDataBytes != mSentMsgs[msgIndex].numDataBytes) {
228             __android_log_print(ANDROID_LOG_ERROR, LOG_TAG,
229                     "---- COMPARE_DATALENMISMATCH r:%zu s:%zu",
230                     mReceivedMsgs[msgIndex].numDataBytes, mSentMsgs[msgIndex].numDataBytes);
231             return COMPARE_DATALENMISMATCH;
232         }
233 
234         // Timestamps
235         if (mReceivedMsgs[msgIndex].timestamp != mSentMsgs[msgIndex].timestamp) {
236             __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "---- COMPARE_TIMESTAMPMISMATCH");
237             return COMPARE_TIMESTAMPMISMATCH;
238         }
239 
240         // we know that the data in both messages have the same number of bytes from the test above.
241         int dataLen = mReceivedMsgs[msgIndex].numDataBytes;
242         for (int dataIndex = 0; dataIndex < dataLen; dataIndex++) {
243             // Data Values?
244             if (mReceivedMsgs[msgIndex].dataBuff[dataIndex] !=
245                     mSentMsgs[msgIndex].dataBuff[dataIndex]) {
246                 __android_log_print(ANDROID_LOG_ERROR, LOG_TAG,
247                         "---- COMPARE_DATAMISMATCH r:%d s:%d",
248                         (int)mReceivedMsgs[msgIndex].dataBuff[dataIndex],
249                         (int)mSentMsgs[msgIndex].dataBuff[dataIndex]);
250                 return COMPARE_DATAMISMATCH;
251             }
252         }
253     }
254 
255     return COMPARE_SUCCESS;
256 }
257 
checkInOutLatency(long maxLatencyNanos)258 int TestContext::checkInOutLatency(long maxLatencyNanos) {
259     if (mReceivedMsgs.size() != mSentMsgs.size()) {
260         __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "  ---- CHECKLATENCY_COUNTMISSMATCH");
261         return CHECKLATENCY_COUNTMISSMATCH;
262     }
263 
264     // we know that both vectors have the same number of messages
265     // from the test above.
266     int numMessages = mSentMsgs.size();
267     for (int msgIndex = 0; msgIndex < numMessages; msgIndex++) {
268         long timeDelta =  mSentMsgs[msgIndex].timeSent - mReceivedMsgs[msgIndex].timestamp;
269         if (timeDelta > maxLatencyNanos) {
270             __android_log_print(ANDROID_LOG_ERROR, LOG_TAG,
271                     "  ---- CHECKLATENCY_LATENCYEXCEEDED %ld", timeDelta);
272             return CHECKLATENCY_LATENCYEXCEEDED;
273         }
274     }
275 
276     return CHECKLATENCY_SUCCESS;
277 }
278 
Java_android_nativemidi_cts_NativeMidiEchoTest_allocTestContext(JNIEnv * env,jclass)279 JNIEXPORT jlong JNICALL Java_android_nativemidi_cts_NativeMidiEchoTest_allocTestContext(
280         JNIEnv* env, jclass) {
281     __android_log_print(ANDROID_LOG_INFO, LOG_TAG, "allocTestContext()");
282 
283     TestContext* context = new TestContext;
284     if (!context->initN(env)) {
285         delete context;
286         context = NULL;
287     }
288 
289     return (jlong)context;
290 }
291 
Java_android_nativemidi_cts_NativeMidiEchoTest_freeTestContext(JNIEnv *,jclass,jlong context)292 JNIEXPORT void JNICALL Java_android_nativemidi_cts_NativeMidiEchoTest_freeTestContext(
293         JNIEnv*, jclass, jlong context) {
294     __android_log_print(ANDROID_LOG_INFO, LOG_TAG, "freeTestContext()");
295     delete (TestContext*)context;
296 }
297 
298 /*
299  * Receiving API
300  */
301 //static void DumpDataMessage(ReceivedMessageRecord* msg) {
302 //    char midiDumpBuffer[SIZE_DATABUFFER * 4]; // more than enough
303 //    memset(midiDumpBuffer, 0, sizeof(midiDumpBuffer));
304 //    int pos = snprintf(midiDumpBuffer, sizeof(midiDumpBuffer),
305 //            "%" PRIx64 " ", msg->timestamp);
306 //    for (uint8_t *b = msg->buffer, *e = b + msg->numDataBytes; b < e; ++b) {
307 //        pos += snprintf(midiDumpBuffer + pos, sizeof(midiDumpBuffer) - pos,
308 //                "%02x ", *b);
309 //    }
310 //    __android_log_print(ANDROID_LOG_INFO, LOG_TAG, "---- DUMP %s", midiDumpBuffer);
311 //}
312 
readThreadRoutine(TestContext * context)313 void readThreadRoutine(TestContext* context) {
314     int32_t opCode;
315     uint8_t inDataBuffer[SIZE_DATABUFFER];
316     size_t numDataBytes;
317     int64_t timestamp;
318 
319     while (context->mReading) {
320         AMidiOutputPort* outputPort = context->midiOutputPort.load();
321         if (outputPort != nullptr) {
322             ssize_t numMessages =
323                 AMidiOutputPort_receive(outputPort, &opCode,
324                     inDataBuffer, sizeof(inDataBuffer), &numDataBytes, &timestamp);
325 
326             if (numMessages > 0) {
327                 context->incNumReceives();
328                 context->incNumBytesReceived(numDataBytes);
329                 ReceivedMessageRecord receiveRec;
330                 receiveRec.timeReceived = System_nanoTime();
331                 receiveRec.numDataBytes = numDataBytes;
332                 receiveRec.dataBuff.reset(new uint8_t[receiveRec.numDataBytes]);
333                 memcpy(receiveRec.dataBuff.get(), inDataBuffer, receiveRec.numDataBytes);
334                 receiveRec.opCode = opCode;
335                 receiveRec.timestamp = timestamp;
336                 context->addReceived(std::move(receiveRec));
337             }
338         }
339     }
340 }
341 
commonDeviceOpen(JNIEnv * env,jobject midiDeviceObj,AMidiDevice ** device)342 static media_status_t commonDeviceOpen(JNIEnv *env, jobject midiDeviceObj, AMidiDevice** device) {
343     media_status_t status = AMidiDevice_fromJava(env, midiDeviceObj, device);
344     if (status == AMEDIA_OK) {
345         // __android_log_print(ANDROID_LOG_INFO, LOG_TAG,
346         //      "---- Obtained device token for obj %p: dev %p", midiDeviceObj, device);
347     } else {
348         __android_log_print(ANDROID_LOG_ERROR, LOG_TAG,
349                 "---- Could not obtain device token for obj %p: status:%d", midiDeviceObj, status);
350     }
351 
352     return status;
353 }
354 
Java_android_nativemidi_cts_NativeMidiEchoTest_openNativeMidiDevice(JNIEnv * env,jobject,jlong ctx,jobject deviceObj)355 JNIEXPORT jint JNICALL Java_android_nativemidi_cts_NativeMidiEchoTest_openNativeMidiDevice(
356         JNIEnv* env, jobject, jlong ctx, jobject deviceObj) {
357     TestContext* context = (TestContext*)ctx;
358     media_status_t status = commonDeviceOpen(env, deviceObj, &context->nativeDevice);
359     return status;
360 }
361 
Java_android_nativemidi_cts_NativeMidiEchoTest_closeNativeMidiDevice(JNIEnv *,jobject,jlong ctx)362 JNIEXPORT jint JNICALL Java_android_nativemidi_cts_NativeMidiEchoTest_closeNativeMidiDevice(
363         JNIEnv*, jobject, jlong ctx) {
364     TestContext* context = (TestContext*)ctx;
365     media_status_t status = AMidiDevice_release(context->nativeDevice);
366     return status;
367 }
368 
369 /*
370  * Sending API
371  */
Java_android_nativemidi_cts_NativeMidiEchoTest_startWritingMidi(JNIEnv *,jobject,jlong ctx,jint portNumber)372 JNIEXPORT jint JNICALL Java_android_nativemidi_cts_NativeMidiEchoTest_startWritingMidi(
373         JNIEnv*, jobject, jlong ctx, jint portNumber) {
374 
375     TestContext* context = (TestContext*)ctx;
376 
377     AMidiInputPort* inputPort;
378     media_status_t status = AMidiInputPort_open(context->nativeDevice, portNumber, &inputPort);
379     if (status == AMEDIA_OK) {
380         // __android_log_print(ANDROID_LOG_INFO, LOG_TAG,
381         //      "---- Opened INPUT port %d: token %p", portNumber, inputPort);
382         context->midiInputPort.store(inputPort);
383     } else {
384         __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "---- Could not open INPUT port %p:%d %d",
385                 context->nativeDevice, portNumber, status);
386         return status;
387     }
388 
389     return AMEDIA_OK;
390 }
391 
Java_android_nativemidi_cts_NativeMidiEchoTest_stopWritingMidi(JNIEnv *,jobject,jlong ctx)392 JNIEXPORT jint JNICALL Java_android_nativemidi_cts_NativeMidiEchoTest_stopWritingMidi(
393         JNIEnv*, jobject, jlong ctx) {
394 
395     TestContext* context = (TestContext*)ctx;
396 
397     AMidiInputPort* inputPort = context->midiInputPort.exchange(nullptr);
398     if (inputPort == nullptr) {
399         return -1;
400     }
401 
402     AMidiInputPort_close(inputPort);
403 
404     return 0;
405 }
406 
Java_android_nativemidi_cts_NativeMidiEchoTest_writeMidiWithTimestamp(JNIEnv * env,jobject,jlong ctx,jbyteArray data,jint offset,jint numBytes,jlong timestamp)407 JNIEXPORT jint JNICALL Java_android_nativemidi_cts_NativeMidiEchoTest_writeMidiWithTimestamp(
408         JNIEnv* env, jobject,
409         jlong ctx, jbyteArray data, jint offset, jint numBytes, jlong timestamp) {
410 
411     TestContext* context = (TestContext*)ctx;
412     context->incNumSends();
413     context->incNumBytesSent(numBytes);
414 
415     jbyte* bufferPtr = env->GetByteArrayElements(data, NULL);
416     if (bufferPtr == NULL) {
417         return -1;
418     }
419 
420     int numWritten =  AMidiInputPort_sendWithTimestamp(
421             context->midiInputPort, (uint8_t*)bufferPtr + offset, numBytes, timestamp);
422     if (numWritten > 0) {
423         // Don't save a send record if we didn't send!
424         SentMessageRecord sendRec;
425         sendRec.numDataBytes = numBytes;
426         sendRec.dataBuff.reset(new uint8_t[sendRec.numDataBytes]);
427         memcpy(sendRec.dataBuff.get(), (uint8_t*)bufferPtr + offset, numBytes);
428         sendRec.timestamp = timestamp;
429         sendRec.timeSent = System_nanoTime();
430         context->addSent(std::move(sendRec));
431     }
432 
433     env->ReleaseByteArrayElements(data, bufferPtr, JNI_ABORT);
434 
435     return numWritten;
436 }
437 
Java_android_nativemidi_cts_NativeMidiEchoTest_writeMidi(JNIEnv * env,jobject j_object,jlong ctx,jbyteArray data,jint offset,jint numBytes)438 JNIEXPORT jint JNICALL Java_android_nativemidi_cts_NativeMidiEchoTest_writeMidi(
439         JNIEnv* env, jobject j_object, jlong ctx, jbyteArray data, jint offset, jint numBytes) {
440     return Java_android_nativemidi_cts_NativeMidiEchoTest_writeMidiWithTimestamp(
441             env, j_object, ctx, data, offset, numBytes, 0L);
442 }
443 
Java_android_nativemidi_cts_NativeMidiEchoTest_flushSentMessages(JNIEnv *,jobject,jlong ctx)444 JNIEXPORT jint JNICALL Java_android_nativemidi_cts_NativeMidiEchoTest_flushSentMessages(
445         JNIEnv*, jobject, jlong ctx) {
446     TestContext* context = (TestContext*)ctx;
447     return AMidiInputPort_sendFlush(context->midiInputPort);
448 }
449 
Java_android_nativemidi_cts_NativeMidiEchoTest_getNumSends(JNIEnv *,jobject,jlong ctx)450 JNIEXPORT jint JNICALL Java_android_nativemidi_cts_NativeMidiEchoTest_getNumSends(
451         JNIEnv*, jobject, jlong ctx) {
452     return ((TestContext*)ctx)->getNumSends();
453 }
454 
Java_android_nativemidi_cts_NativeMidiEchoTest_getNumBytesSent(JNIEnv *,jobject,jlong ctx)455 JNIEXPORT jint JNICALL Java_android_nativemidi_cts_NativeMidiEchoTest_getNumBytesSent(
456         JNIEnv*, jobject, jlong ctx) {
457     return ((TestContext*)ctx)->getNumBytesSent();
458 }
459 
Java_android_nativemidi_cts_NativeMidiEchoTest_getNumReceives(JNIEnv *,jobject,jlong ctx)460 JNIEXPORT jint JNICALL Java_android_nativemidi_cts_NativeMidiEchoTest_getNumReceives(
461         JNIEnv*, jobject, jlong ctx) {
462     return ((TestContext*)ctx)->getNumReceives();
463 }
464 
Java_android_nativemidi_cts_NativeMidiEchoTest_getNumBytesReceived(JNIEnv *,jobject,jlong ctx)465 JNIEXPORT jint JNICALL Java_android_nativemidi_cts_NativeMidiEchoTest_getNumBytesReceived(
466         JNIEnv*, jobject, jlong ctx) {
467     return ((TestContext*)ctx)->getNumBytesReceived();
468 }
469 
Java_android_nativemidi_cts_NativeMidiEchoTest_startReadingMidi(JNIEnv *,jobject,jlong ctx,jint portNumber)470 JNIEXPORT jint JNICALL Java_android_nativemidi_cts_NativeMidiEchoTest_startReadingMidi(
471         JNIEnv*, jobject, jlong ctx, jint portNumber) {
472 
473     // __android_log_print(ANDROID_LOG_INFO, LOG_TAG, "++++ startReadingMidi()");
474     TestContext* context = (TestContext*)ctx;
475     if (context == nullptr) {
476         __android_log_print(ANDROID_LOG_INFO, LOG_TAG,
477                 "Test Context is null in  startReadingMidi()");
478         return AMEDIA_ERROR_INVALID_OBJECT;
479     }
480 
481     AMidiOutputPort* outputPort;
482     media_status_t status = AMidiOutputPort_open(context->nativeDevice, portNumber, &outputPort);
483     if (status == AMEDIA_OK) {
484         context->midiOutputPort.store(outputPort);
485     } else {
486         __android_log_print(ANDROID_LOG_ERROR, LOG_TAG,
487                 "---- Could not open OUTPUT port %p : %d %d",
488                 context->nativeDevice, portNumber, status);
489         return status;
490     }
491 
492     // Start read thread
493     context->mReading = true;
494     context->mReadThread.reset(new std::thread(readThreadRoutine, context));
495     std::this_thread::yield(); // let the read thread startup.
496 
497     return status;
498 }
499 
Java_android_nativemidi_cts_NativeMidiEchoTest_stopReadingMidi(JNIEnv *,jobject,jlong ctx)500 JNIEXPORT jint JNICALL Java_android_nativemidi_cts_NativeMidiEchoTest_stopReadingMidi(
501         JNIEnv*, jobject, jlong ctx) {
502 
503     // __android_log_print(ANDROID_LOG_INFO, LOG_TAG, "++++ stopReadingMidi()");
504     TestContext* context = (TestContext*)ctx;
505     if (context == nullptr) {
506         __android_log_print(ANDROID_LOG_INFO, LOG_TAG,
507                 "Test Context is null in  stopReadingMidi()");
508         return AMEDIA_ERROR_INVALID_OBJECT;
509     }
510     context->mReading = false;
511 
512     context->mReadThread->join();
513 
514     AMidiOutputPort* outputPort = context->midiOutputPort.exchange(nullptr);
515     if (outputPort == nullptr) {
516         return -1;
517     }
518 
519     AMidiOutputPort_close(outputPort);
520 
521     return 0;
522 }
523 
524 /*
525  * Messages
526  */
Java_android_nativemidi_cts_NativeMidiEchoTest_getNumReceivedMessages(JNIEnv *,jobject,jlong ctx)527 JNIEXPORT jint JNICALL Java_android_nativemidi_cts_NativeMidiEchoTest_getNumReceivedMessages(
528         JNIEnv*, jobject, jlong ctx) {
529     return ((TestContext*)ctx)->getNumReceivedMsgs();
530 }
531 
Java_android_nativemidi_cts_NativeMidiEchoTest_getReceivedMessageAt(JNIEnv * env,jobject,jlong ctx,jint index)532 JNIEXPORT jobject JNICALL Java_android_nativemidi_cts_NativeMidiEchoTest_getReceivedMessageAt(
533         JNIEnv* env, jobject, jlong ctx, jint index) {
534     return ((TestContext*)ctx)->transferReceiveMsgAt(env, index);
535 }
536 
Java_android_nativemidi_cts_NativeMidiEchoTest_matchNativeMessages(JNIEnv *,jobject,jlong ctx)537 JNIEXPORT jint JNICALL Java_android_nativemidi_cts_NativeMidiEchoTest_matchNativeMessages(
538         JNIEnv*, jobject, jlong ctx) {
539     return ((TestContext*)ctx)->compareInsAndOuts();
540 }
541 
Java_android_nativemidi_cts_NativeMidiEchoTest_checkNativeLatency(JNIEnv *,jobject,jlong ctx,jlong maxLatencyNanos)542 JNIEXPORT jint JNICALL Java_android_nativemidi_cts_NativeMidiEchoTest_checkNativeLatency(
543         JNIEnv*, jobject, jlong ctx, jlong maxLatencyNanos) {
544     return ((TestContext*)ctx)->checkInOutLatency(maxLatencyNanos);
545 }
546 
547 } // extern "C"
548