• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2010 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 <stdlib.h>
18 
19 #include "errno.h"
20 #include "com_android_nfc.h"
21 #include "com_android_nfc_list.h"
22 #include "phLibNfcStatus.h"
23 
24 /*
25  * JNI Initialization
26  */
JNI_OnLoad(JavaVM * jvm,void * reserved)27 jint JNI_OnLoad(JavaVM *jvm, void *reserved)
28 {
29    JNIEnv *e;
30 
31    LOGD("NFC Service : loading JNI\n");
32 
33    // Check JNI version
34    if(jvm->GetEnv((void **)&e, JNI_VERSION_1_6))
35       return JNI_ERR;
36 
37    android::vm = jvm;
38 
39    if (android::register_com_android_nfc_NativeNfcManager(e) == -1)
40       return JNI_ERR;
41    if (android::register_com_android_nfc_NativeNfcTag(e) == -1)
42       return JNI_ERR;
43    if (android::register_com_android_nfc_NativeP2pDevice(e) == -1)
44       return JNI_ERR;
45    if (android::register_com_android_nfc_NativeLlcpSocket(e) == -1)
46       return JNI_ERR;
47    if (android::register_com_android_nfc_NativeLlcpConnectionlessSocket(e) == -1)
48       return JNI_ERR;
49    if (android::register_com_android_nfc_NativeLlcpServiceSocket(e) == -1)
50       return JNI_ERR;
51    if (android::register_com_android_nfc_NativeNfcSecureElement(e) == -1)
52       return JNI_ERR;
53 
54    return JNI_VERSION_1_6;
55 }
56 
57 namespace android {
58 
59 extern struct nfc_jni_native_data *exported_nat;
60 
61 JavaVM *vm;
62 
63 /*
64  * JNI Utils
65  */
nfc_get_env()66 JNIEnv *nfc_get_env()
67 {
68     JNIEnv *e;
69     if (vm->GetEnv((void **)&e, JNI_VERSION_1_6) != JNI_OK) {
70         LOGE("Current thread is not attached to VM");
71         abort();
72     }
73     return e;
74 }
75 
nfc_cb_data_init(nfc_jni_callback_data * pCallbackData,void * pContext)76 bool nfc_cb_data_init(nfc_jni_callback_data* pCallbackData, void* pContext)
77 {
78    /* Create semaphore */
79    if(sem_init(&pCallbackData->sem, 0, 0) == -1)
80    {
81       LOGE("Semaphore creation failed (errno=0x%08x)", errno);
82       return false;
83    }
84 
85    /* Set default status value */
86    pCallbackData->status = NFCSTATUS_FAILED;
87 
88    /* Copy the context */
89    pCallbackData->pContext = pContext;
90 
91    /* Add to active semaphore list */
92    if (!listAdd(&nfc_jni_get_monitor()->sem_list, pCallbackData))
93    {
94       LOGE("Failed to add the semaphore to the list");
95    }
96 
97    return true;
98 }
99 
nfc_cb_data_deinit(nfc_jni_callback_data * pCallbackData)100 void nfc_cb_data_deinit(nfc_jni_callback_data* pCallbackData)
101 {
102    /* Destroy semaphore */
103    if (sem_destroy(&pCallbackData->sem))
104    {
105       LOGE("Failed to destroy semaphore (errno=0x%08x)", errno);
106    }
107 
108    /* Remove from active semaphore list */
109    if (!listRemove(&nfc_jni_get_monitor()->sem_list, pCallbackData))
110    {
111       LOGE("Failed to remove semaphore from the list");
112    }
113 
114 }
115 
nfc_cb_data_releaseAll()116 void nfc_cb_data_releaseAll()
117 {
118    nfc_jni_callback_data* pCallbackData;
119 
120    while (listGetAndRemoveNext(&nfc_jni_get_monitor()->sem_list, (void**)&pCallbackData))
121    {
122       pCallbackData->status = NFCSTATUS_FAILED;
123       sem_post(&pCallbackData->sem);
124    }
125 }
126 
nfc_jni_cache_object(JNIEnv * e,const char * clsname,jobject * cached_obj)127 int nfc_jni_cache_object(JNIEnv *e, const char *clsname,
128    jobject *cached_obj)
129 {
130    jclass cls;
131    jobject obj;
132    jmethodID ctor;
133 
134    cls = e->FindClass(clsname);
135    if(cls == NULL)
136    {
137       return -1;
138       LOGD("Find class error\n");
139    }
140 
141 
142    ctor = e->GetMethodID(cls, "<init>", "()V");
143 
144    obj = e->NewObject(cls, ctor);
145    if(obj == NULL)
146    {
147       return -1;
148       LOGD("Create object error\n");
149    }
150 
151    *cached_obj = e->NewGlobalRef(obj);
152    if(*cached_obj == NULL)
153    {
154       e->DeleteLocalRef(obj);
155       LOGD("Global ref error\n");
156       return -1;
157    }
158 
159    e->DeleteLocalRef(obj);
160 
161    return 0;
162 }
163 
164 
nfc_jni_get_nat(JNIEnv * e,jobject o)165 struct nfc_jni_native_data* nfc_jni_get_nat(JNIEnv *e, jobject o)
166 {
167    jclass c;
168    jfieldID f;
169 
170    /* Retrieve native structure address */
171    c = e->GetObjectClass(o);
172    f = e->GetFieldID(c, "mNative", "I");
173    return (struct nfc_jni_native_data*)e->GetIntField(o, f);
174 }
175 
nfc_jni_get_nat_ext(JNIEnv * e)176 struct nfc_jni_native_data* nfc_jni_get_nat_ext(JNIEnv *e)
177 {
178    return exported_nat;
179 }
180 
181 static nfc_jni_native_monitor_t *nfc_jni_native_monitor = NULL;
182 
nfc_jni_init_monitor(void)183 nfc_jni_native_monitor_t* nfc_jni_init_monitor(void)
184 {
185 
186    pthread_mutexattr_t recursive_attr;
187 
188    pthread_mutexattr_init(&recursive_attr);
189    pthread_mutexattr_settype(&recursive_attr, PTHREAD_MUTEX_RECURSIVE_NP);
190 
191    if(nfc_jni_native_monitor == NULL)
192    {
193       nfc_jni_native_monitor = (nfc_jni_native_monitor_t*)malloc(sizeof(nfc_jni_native_monitor_t));
194    }
195 
196    if(nfc_jni_native_monitor != NULL)
197    {
198       memset(nfc_jni_native_monitor, 0, sizeof(nfc_jni_native_monitor_t));
199 
200       if(pthread_mutex_init(&nfc_jni_native_monitor->reentrance_mutex, &recursive_attr) == -1)
201       {
202          LOGE("NFC Manager Reentrance Mutex creation returned 0x%08x", errno);
203          return NULL;
204       }
205 
206       if(pthread_mutex_init(&nfc_jni_native_monitor->concurrency_mutex, NULL) == -1)
207       {
208          LOGE("NFC Manager Concurrency Mutex creation returned 0x%08x", errno);
209          return NULL;
210       }
211 
212       if(!listInit(&nfc_jni_native_monitor->sem_list))
213       {
214          LOGE("NFC Manager Semaphore List creation failed");
215          return NULL;
216       }
217 
218       LIST_INIT(&nfc_jni_native_monitor->incoming_socket_head);
219 
220       if(pthread_mutex_init(&nfc_jni_native_monitor->incoming_socket_mutex, NULL) == -1)
221       {
222          LOGE("NFC Manager incoming socket mutex creation returned 0x%08x", errno);
223          return NULL;
224       }
225 
226       if(pthread_cond_init(&nfc_jni_native_monitor->incoming_socket_cond, NULL) == -1)
227       {
228          LOGE("NFC Manager incoming socket condition creation returned 0x%08x", errno);
229          return NULL;
230       }
231 
232 }
233 
234    return nfc_jni_native_monitor;
235 }
236 
nfc_jni_get_monitor(void)237 nfc_jni_native_monitor_t* nfc_jni_get_monitor(void)
238 {
239    return nfc_jni_native_monitor;
240 }
241 
242 
nfc_jni_get_p2p_device_handle(JNIEnv * e,jobject o)243 phLibNfc_Handle nfc_jni_get_p2p_device_handle(JNIEnv *e, jobject o)
244 {
245    jclass c;
246    jfieldID f;
247 
248    c = e->GetObjectClass(o);
249    f = e->GetFieldID(c, "mHandle", "I");
250 
251    return e->GetIntField(o, f);
252 }
253 
nfc_jni_get_p2p_device_mode(JNIEnv * e,jobject o)254 jshort nfc_jni_get_p2p_device_mode(JNIEnv *e, jobject o)
255 {
256    jclass c;
257    jfieldID f;
258 
259    c = e->GetObjectClass(o);
260    f = e->GetFieldID(c, "mMode", "S");
261 
262    return e->GetShortField(o, f);
263 }
264 
265 
nfc_jni_get_connected_tech_index(JNIEnv * e,jobject o)266 int nfc_jni_get_connected_tech_index(JNIEnv *e, jobject o)
267 {
268 
269    jclass c;
270    jfieldID f;
271 
272    c = e->GetObjectClass(o);
273    f = e->GetFieldID(c, "mConnectedTechnology", "I");
274 
275    return e->GetIntField(o, f);
276 
277 }
278 
nfc_jni_get_connected_technology(JNIEnv * e,jobject o)279 jint nfc_jni_get_connected_technology(JNIEnv *e, jobject o)
280 {
281    jclass c;
282    jfieldID f;
283    int connectedTech = -1;
284 
285    int connectedTechIndex = nfc_jni_get_connected_tech_index(e,o);
286    jintArray techTypes = nfc_jni_get_nfc_tag_type(e, o);
287 
288    if ((connectedTechIndex != -1) && (techTypes != NULL) &&
289            (connectedTechIndex < e->GetArrayLength(techTypes))) {
290        jint* technologies = e->GetIntArrayElements(techTypes, 0);
291        if (technologies != NULL) {
292            connectedTech = technologies[connectedTechIndex];
293            e->ReleaseIntArrayElements(techTypes, technologies, JNI_ABORT);
294        }
295    }
296 
297    return connectedTech;
298 
299 }
300 
nfc_jni_get_connected_technology_libnfc_type(JNIEnv * e,jobject o)301 jint nfc_jni_get_connected_technology_libnfc_type(JNIEnv *e, jobject o)
302 {
303    jclass c;
304    jfieldID f;
305    jint connectedLibNfcType = -1;
306 
307    int connectedTechIndex = nfc_jni_get_connected_tech_index(e,o);
308    c = e->GetObjectClass(o);
309    f = e->GetFieldID(c, "mTechLibNfcTypes", "[I");
310    jintArray libNfcTypes =  (jintArray) e->GetObjectField(o, f);
311 
312    if ((connectedTechIndex != -1) && (libNfcTypes != NULL) &&
313            (connectedTechIndex < e->GetArrayLength(libNfcTypes))) {
314        jint* types = e->GetIntArrayElements(libNfcTypes, 0);
315        if (types != NULL) {
316            connectedLibNfcType = types[connectedTechIndex];
317            e->ReleaseIntArrayElements(libNfcTypes, types, JNI_ABORT);
318        }
319    }
320    return connectedLibNfcType;
321 
322 }
323 
nfc_jni_get_connected_handle(JNIEnv * e,jobject o)324 phLibNfc_Handle nfc_jni_get_connected_handle(JNIEnv *e, jobject o)
325 {
326    jclass c;
327    jfieldID f;
328    phLibNfc_Handle connectedHandle = -1;
329 
330    int connectedTechIndex = nfc_jni_get_connected_tech_index(e,o);
331    c = e->GetObjectClass(o);
332    f = e->GetFieldID(c, "mTechHandles", "[I");
333    jintArray techHandles =  (jintArray) e->GetObjectField(o, f);
334 
335    if ((connectedTechIndex != -1) && (techHandles != NULL) &&
336            (connectedTechIndex < e->GetArrayLength(techHandles))) {
337        jint* handles = e->GetIntArrayElements(techHandles, 0);
338        if (handles != NULL) {
339            connectedHandle = handles[connectedTechIndex];
340            e->ReleaseIntArrayElements(techHandles, handles, JNI_ABORT);
341        }
342    }
343    return connectedHandle;
344 
345 }
346 
nfc_jni_get_nfc_socket_handle(JNIEnv * e,jobject o)347 phLibNfc_Handle nfc_jni_get_nfc_socket_handle(JNIEnv *e, jobject o)
348 {
349    jclass c;
350    jfieldID f;
351 
352    c = e->GetObjectClass(o);
353    f = e->GetFieldID(c, "mHandle", "I");
354 
355    return e->GetIntField(o, f);
356 }
357 
nfc_jni_get_nfc_tag_type(JNIEnv * e,jobject o)358 jintArray nfc_jni_get_nfc_tag_type(JNIEnv *e, jobject o)
359 {
360   jclass c;
361   jfieldID f;
362   jintArray techtypes;
363 
364   c = e->GetObjectClass(o);
365   f = e->GetFieldID(c, "mTechList","[I");
366 
367   /* Read the techtypes  */
368   techtypes = (jintArray) e->GetObjectField(o, f);
369 
370   return techtypes;
371 }
372 
373 
374 
375 //Display status code
nfc_jni_get_status_name(NFCSTATUS status)376 const char* nfc_jni_get_status_name(NFCSTATUS status)
377 {
378    #define STATUS_ENTRY(status) { status, #status }
379 
380    struct status_entry {
381       NFCSTATUS   code;
382       const char  *name;
383    };
384 
385    const struct status_entry sNameTable[] = {
386       STATUS_ENTRY(NFCSTATUS_SUCCESS),
387       STATUS_ENTRY(NFCSTATUS_FAILED),
388       STATUS_ENTRY(NFCSTATUS_INVALID_PARAMETER),
389       STATUS_ENTRY(NFCSTATUS_INSUFFICIENT_RESOURCES),
390       STATUS_ENTRY(NFCSTATUS_TARGET_LOST),
391       STATUS_ENTRY(NFCSTATUS_INVALID_HANDLE),
392       STATUS_ENTRY(NFCSTATUS_MULTIPLE_TAGS),
393       STATUS_ENTRY(NFCSTATUS_ALREADY_REGISTERED),
394       STATUS_ENTRY(NFCSTATUS_FEATURE_NOT_SUPPORTED),
395       STATUS_ENTRY(NFCSTATUS_SHUTDOWN),
396       STATUS_ENTRY(NFCSTATUS_ABORTED),
397       STATUS_ENTRY(NFCSTATUS_REJECTED ),
398       STATUS_ENTRY(NFCSTATUS_NOT_INITIALISED),
399       STATUS_ENTRY(NFCSTATUS_PENDING),
400       STATUS_ENTRY(NFCSTATUS_BUFFER_TOO_SMALL),
401       STATUS_ENTRY(NFCSTATUS_ALREADY_INITIALISED),
402       STATUS_ENTRY(NFCSTATUS_BUSY),
403       STATUS_ENTRY(NFCSTATUS_TARGET_NOT_CONNECTED),
404       STATUS_ENTRY(NFCSTATUS_MULTIPLE_PROTOCOLS),
405       STATUS_ENTRY(NFCSTATUS_DESELECTED),
406       STATUS_ENTRY(NFCSTATUS_INVALID_DEVICE),
407       STATUS_ENTRY(NFCSTATUS_MORE_INFORMATION),
408       STATUS_ENTRY(NFCSTATUS_RF_TIMEOUT),
409       STATUS_ENTRY(NFCSTATUS_RF_ERROR),
410       STATUS_ENTRY(NFCSTATUS_BOARD_COMMUNICATION_ERROR),
411       STATUS_ENTRY(NFCSTATUS_INVALID_STATE),
412       STATUS_ENTRY(NFCSTATUS_NOT_REGISTERED),
413       STATUS_ENTRY(NFCSTATUS_RELEASED),
414       STATUS_ENTRY(NFCSTATUS_NOT_ALLOWED),
415       STATUS_ENTRY(NFCSTATUS_INVALID_REMOTE_DEVICE),
416       STATUS_ENTRY(NFCSTATUS_SMART_TAG_FUNC_NOT_SUPPORTED),
417       STATUS_ENTRY(NFCSTATUS_READ_FAILED),
418       STATUS_ENTRY(NFCSTATUS_WRITE_FAILED),
419       STATUS_ENTRY(NFCSTATUS_NO_NDEF_SUPPORT),
420       STATUS_ENTRY(NFCSTATUS_EOF_NDEF_CONTAINER_REACHED),
421       STATUS_ENTRY(NFCSTATUS_INVALID_RECEIVE_LENGTH),
422       STATUS_ENTRY(NFCSTATUS_INVALID_FORMAT),
423       STATUS_ENTRY(NFCSTATUS_INSUFFICIENT_STORAGE),
424       STATUS_ENTRY(NFCSTATUS_FORMAT_ERROR),
425    };
426 
427    int i = sizeof(sNameTable)/sizeof(status_entry);
428 
429    while(i>0)
430    {
431       i--;
432       if (sNameTable[i].code == PHNFCSTATUS(status))
433       {
434          return sNameTable[i].name;
435       }
436    }
437 
438    return "UNKNOWN";
439 }
440 
addTechIfNeeded(int * techList,int * handleList,int * typeList,int listSize,int maxListSize,int techToAdd,int handleToAdd,int typeToAdd)441 int addTechIfNeeded(int *techList, int* handleList, int* typeList, int listSize,
442         int maxListSize, int techToAdd, int handleToAdd, int typeToAdd) {
443     bool found = false;
444     for (int i = 0; i < listSize; i++) {
445         if (techList[i] == techToAdd) {
446             found = true;
447             break;
448         }
449     }
450     if (!found && listSize < maxListSize) {
451         techList[listSize] = techToAdd;
452         handleList[listSize] = handleToAdd;
453         typeList[listSize] = typeToAdd;
454         return listSize + 1;
455     }
456     else {
457         return listSize;
458     }
459 }
460 
461 
462 #define MAX_NUM_TECHNOLOGIES 32
463 
464 /*
465  *  Utility to get a technology tree and a corresponding handle list from a detected tag.
466  */
nfc_jni_get_technology_tree(JNIEnv * e,phLibNfc_RemoteDevList_t * devList,uint8_t count,jintArray * techList,jintArray * handleList,jintArray * libnfcTypeList)467 void nfc_jni_get_technology_tree(JNIEnv* e, phLibNfc_RemoteDevList_t* devList,
468         uint8_t count, jintArray* techList, jintArray* handleList,
469         jintArray* libnfcTypeList)
470 {
471    int technologies[MAX_NUM_TECHNOLOGIES];
472    int handles[MAX_NUM_TECHNOLOGIES];
473    int libnfctypes[MAX_NUM_TECHNOLOGIES];
474 
475    int index = 0;
476    // TODO: This counts from up to down because on multi-protocols, the
477    // ISO handle is usually the second, and we prefer the ISO. Should implement
478    // a method to find the "preferred handle order" and use that instead,
479    // since we shouldn't have dependencies on the tech list ordering.
480    for (int target = count - 1; target >= 0; target--) {
481        int type = devList[target].psRemoteDevInfo->RemDevType;
482        int handle = devList[target].hTargetDev;
483        switch (type)
484        {
485           case phNfc_eISO14443_A_PICC:
486           case phNfc_eISO14443_4A_PICC:
487             {
488               index = addTechIfNeeded(technologies, handles, libnfctypes, index,
489                       MAX_NUM_TECHNOLOGIES, TARGET_TYPE_ISO14443_4, handle, type);
490               index = addTechIfNeeded(technologies, handles, libnfctypes, index,
491                       MAX_NUM_TECHNOLOGIES, TARGET_TYPE_ISO14443_3A, handle, type);
492               break;
493             }
494           case phNfc_eISO14443_4B_PICC:
495             {
496               index = addTechIfNeeded(technologies, handles, libnfctypes, index,
497                       MAX_NUM_TECHNOLOGIES, TARGET_TYPE_ISO14443_4, handle, type);
498               index = addTechIfNeeded(technologies, handles, libnfctypes, index,
499                       MAX_NUM_TECHNOLOGIES, TARGET_TYPE_ISO14443_3B, handle, type);
500             }break;
501           case phNfc_eISO14443_3A_PICC:
502             {
503               index = addTechIfNeeded(technologies, handles, libnfctypes,
504                       index, MAX_NUM_TECHNOLOGIES, TARGET_TYPE_ISO14443_3A, handle, type);
505             }break;
506           case phNfc_eISO14443_B_PICC:
507             {
508               // TODO a bug in libnfc will cause 14443-3B only cards
509               // to be returned as this type as well, but these cards
510               // are very rare. Hence assume it's -4B
511               index = addTechIfNeeded(technologies, handles, libnfctypes,
512                       index, MAX_NUM_TECHNOLOGIES, TARGET_TYPE_ISO14443_4, handle, type);
513               index = addTechIfNeeded(technologies, handles, libnfctypes,
514                       index, MAX_NUM_TECHNOLOGIES, TARGET_TYPE_ISO14443_3B, handle, type);
515             }break;
516           case phNfc_eISO15693_PICC:
517             {
518               index = addTechIfNeeded(technologies, handles, libnfctypes,
519                       index, MAX_NUM_TECHNOLOGIES, TARGET_TYPE_ISO15693, handle, type);
520             }break;
521           case phNfc_eMifare_PICC:
522             {
523               // We don't want to be too clever here; libnfc has already determined
524               // it's a Mifare, so we only check for UL, for all other tags
525               // we assume it's a mifare classic. This should make us more
526               // future-proof.
527               int sak = devList[target].psRemoteDevInfo->RemoteDevInfo.Iso14443A_Info.Sak;
528               switch(sak)
529               {
530                 case 0x00:
531                   // could be UL or UL-C
532                   index = addTechIfNeeded(technologies, handles, libnfctypes,
533                           index, MAX_NUM_TECHNOLOGIES, TARGET_TYPE_MIFARE_UL, handle, type);
534                   index = addTechIfNeeded(technologies, handles, libnfctypes,
535                           index, MAX_NUM_TECHNOLOGIES, TARGET_TYPE_ISO14443_3A, handle, type);
536                   break;
537                 default:
538                   index = addTechIfNeeded(technologies, handles, libnfctypes,
539                           index, MAX_NUM_TECHNOLOGIES, TARGET_TYPE_MIFARE_CLASSIC, handle, type);
540                   index = addTechIfNeeded(technologies, handles, libnfctypes,
541                           index, MAX_NUM_TECHNOLOGIES, TARGET_TYPE_ISO14443_3A, handle, type);
542                   break;
543               }
544             }break;
545           case phNfc_eFelica_PICC:
546             {
547               index = addTechIfNeeded(technologies, handles, libnfctypes,
548                       index, MAX_NUM_TECHNOLOGIES, TARGET_TYPE_FELICA, handle, type);
549             }break;
550           case phNfc_eJewel_PICC:
551             {
552               // Jewel represented as NfcA
553               index = addTechIfNeeded(technologies, handles, libnfctypes,
554                       index, MAX_NUM_TECHNOLOGIES, TARGET_TYPE_ISO14443_3A, handle, type);
555             }break;
556           default:
557             {
558               index = addTechIfNeeded(technologies, handles, libnfctypes,
559                       index, MAX_NUM_TECHNOLOGIES, TARGET_TYPE_UNKNOWN, handle, type);
560             }
561         }
562    }
563 
564    // Build the Java arrays
565    if (techList != NULL) {
566        *techList = e->NewIntArray(index);
567        jint* techItems = e->GetIntArrayElements(*techList, NULL);
568        for (int i = 0; i < index; i++) {
569            techItems[i] = technologies[i];
570        }
571        e->ReleaseIntArrayElements(*techList, techItems, 0);
572    }
573 
574    if (handleList != NULL) {
575        *handleList = e->NewIntArray(index);
576        jint* handleItems = e->GetIntArrayElements(*handleList, NULL);
577        for (int i = 0; i < index; i++) {
578            handleItems[i] = handles[i];
579        }
580        e->ReleaseIntArrayElements(*handleList, handleItems, 0);
581    }
582 
583    if (libnfcTypeList != NULL) {
584        *libnfcTypeList = e->NewIntArray(index);
585 
586        jint* typeItems = e->GetIntArrayElements(*libnfcTypeList, NULL);
587        for (int i = 0; i < index; i++) {
588            typeItems[i] = libnfctypes[i];
589        }
590        e->ReleaseIntArrayElements(*libnfcTypeList, typeItems, 0);
591    }
592 }
593 
594 } // namespace android
595