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 <semaphore.h>
18#include <errno.h>
19
20#include "com_android_nfc.h"
21
22namespace android {
23
24extern void nfc_jni_llcp_transport_socket_err_callback(void*      pContext,
25                                                       uint8_t    nErrCode);
26/*
27 * Callbacks
28 */
29static void nfc_jni_llcp_accept_socket_callback(void*        pContext,
30                                                NFCSTATUS    status)
31{
32   struct nfc_jni_callback_data * pCallbackData = (struct nfc_jni_callback_data *) pContext;
33   LOG_CALLBACK("nfc_jni_llcp_accept_socket_callback", status);
34
35   /* Report the callback status and wake up the caller */
36   pCallbackData->status = status;
37   sem_post(&pCallbackData->sem);
38}
39
40
41/*
42 * Utils
43 */
44
45static phLibNfc_Handle getIncomingSocket(nfc_jni_native_monitor_t * pMonitor,
46                                                 phLibNfc_Handle hServerSocket)
47{
48   nfc_jni_listen_data_t * pListenData;
49   phLibNfc_Handle pIncomingSocket = NULL;
50
51   /* Look for a pending incoming connection on the current server */
52   LIST_FOREACH(pListenData, &pMonitor->incoming_socket_head, entries)
53   {
54      if (pListenData->pServerSocket == hServerSocket)
55      {
56         pIncomingSocket = pListenData->pIncomingSocket;
57         LIST_REMOVE(pListenData, entries);
58         free(pListenData);
59         break;
60      }
61   }
62
63   return pIncomingSocket;
64}
65
66/*
67 * Methods
68 */
69static jobject com_NativeLlcpServiceSocket_doAccept(JNIEnv *e, jobject o, jint miu, jint rw, jint linearBufferLength)
70{
71   NFCSTATUS ret = NFCSTATUS_SUCCESS;
72   struct timespec ts;
73   phLibNfc_Llcp_sSocketOptions_t sOptions;
74   phNfc_sData_t sWorkingBuffer;
75   jfieldID f;
76   jclass clsNativeLlcpSocket;
77   jobject clientSocket = NULL;
78   struct nfc_jni_callback_data cb_data;
79   phLibNfc_Handle hIncomingSocket, hServerSocket;
80   nfc_jni_native_monitor_t * pMonitor = nfc_jni_get_monitor();
81
82   /* Create the local semaphore */
83   if (!nfc_cb_data_init(&cb_data, NULL))
84   {
85      goto clean_and_return;
86   }
87
88   /* Get server socket */
89   hServerSocket = nfc_jni_get_nfc_socket_handle(e,o);
90
91   /* Set socket options with the socket options of the service */
92   sOptions.miu = miu;
93   sOptions.rw = rw;
94
95   /* Allocate Working buffer length */
96   sWorkingBuffer.buffer = (uint8_t*)malloc((miu*rw)+miu+linearBufferLength);
97   sWorkingBuffer.length = (miu*rw)+ miu + linearBufferLength;
98
99   while(cb_data.status != NFCSTATUS_SUCCESS)
100   {
101      /* Wait for tag Notification */
102      pthread_mutex_lock(&pMonitor->incoming_socket_mutex);
103      while ((hIncomingSocket = getIncomingSocket(pMonitor, hServerSocket)) == NULL) {
104         pthread_cond_wait(&pMonitor->incoming_socket_cond, &pMonitor->incoming_socket_mutex);
105      }
106      pthread_mutex_unlock(&pMonitor->incoming_socket_mutex);
107
108      /* Accept the incomming socket */
109      TRACE("phLibNfc_Llcp_Accept()");
110      REENTRANCE_LOCK();
111      ret = phLibNfc_Llcp_Accept( hIncomingSocket,
112                                  &sOptions,
113                                  &sWorkingBuffer,
114                                  nfc_jni_llcp_transport_socket_err_callback,
115                                  nfc_jni_llcp_accept_socket_callback,
116                                  (void*)&cb_data);
117      REENTRANCE_UNLOCK();
118      if(ret != NFCSTATUS_PENDING)
119      {
120         // NOTE: This may happen if link went down since incoming socket detected, then
121         //       just drop it and start a new accept loop.
122         LOGD("phLibNfc_Llcp_Accept() returned 0x%04x[%s]", ret, nfc_jni_get_status_name(ret));
123         continue;
124      }
125      TRACE("phLibNfc_Llcp_Accept() returned 0x%04x[%s]", ret, nfc_jni_get_status_name(ret));
126
127      /* Wait for callback response */
128      if(sem_wait(&cb_data.sem))
129      {
130         LOGE("Failed to wait for semaphore (errno=0x%08x)", errno);
131         goto clean_and_return;
132      }
133
134      if(cb_data.status != NFCSTATUS_SUCCESS)
135      {
136         /* NOTE: Do not generate an error if the accept failed to avoid error in server application */
137         LOGD("Failed to accept incoming socket  0x%04x[%s]", cb_data.status, nfc_jni_get_status_name(cb_data.status));
138      }
139   }
140
141   /* Create new LlcpSocket object */
142   if(nfc_jni_cache_object(e,"com/android/nfc/nxp/NativeLlcpSocket",&(clientSocket)) == -1)
143   {
144      LOGD("LLCP Socket creation error");
145      goto clean_and_return;
146   }
147
148   /* Get NativeConnectionOriented class object */
149   clsNativeLlcpSocket = e->GetObjectClass(clientSocket);
150   if(e->ExceptionCheck())
151   {
152      LOGD("LLCP Socket get class object error");
153      goto clean_and_return;
154   }
155
156   /* Set socket handle */
157   f = e->GetFieldID(clsNativeLlcpSocket, "mHandle", "I");
158   e->SetIntField(clientSocket, f,(jint)hIncomingSocket);
159
160   /* Set socket MIU */
161   f = e->GetFieldID(clsNativeLlcpSocket, "mLocalMiu", "I");
162   e->SetIntField(clientSocket, f,(jint)miu);
163
164   /* Set socket RW */
165   f = e->GetFieldID(clsNativeLlcpSocket, "mLocalRw", "I");
166   e->SetIntField(clientSocket, f,(jint)rw);
167
168   TRACE("socket handle 0x%02x: MIU = %d, RW = %d\n",hIncomingSocket, miu, rw);
169
170clean_and_return:
171   nfc_cb_data_deinit(&cb_data);
172   return clientSocket;
173}
174
175static jboolean com_NativeLlcpServiceSocket_doClose(JNIEnv *e, jobject o)
176{
177   NFCSTATUS ret;
178   phLibNfc_Handle hLlcpSocket;
179   nfc_jni_native_monitor_t * pMonitor = nfc_jni_get_monitor();
180
181   TRACE("Close Service socket");
182
183   /* Retrieve socket handle */
184   hLlcpSocket = nfc_jni_get_nfc_socket_handle(e,o);
185
186   pthread_mutex_lock(&pMonitor->incoming_socket_mutex);
187   /* TODO: implement accept abort */
188   pthread_cond_broadcast(&pMonitor->incoming_socket_cond);
189   pthread_mutex_unlock(&pMonitor->incoming_socket_mutex);
190
191   REENTRANCE_LOCK();
192   ret = phLibNfc_Llcp_Close(hLlcpSocket);
193   REENTRANCE_UNLOCK();
194   if(ret == NFCSTATUS_SUCCESS)
195   {
196      TRACE("Close Service socket OK");
197      return TRUE;
198   }
199   else
200   {
201      LOGD("Close Service socket KO");
202      return FALSE;
203   }
204}
205
206
207/*
208 * JNI registration.
209 */
210static JNINativeMethod gMethods[] =
211{
212   {"doAccept", "(III)Lcom/android/nfc/nxp/NativeLlcpSocket;",
213      (void *)com_NativeLlcpServiceSocket_doAccept},
214
215   {"doClose", "()Z",
216      (void *)com_NativeLlcpServiceSocket_doClose},
217};
218
219
220int register_com_android_nfc_NativeLlcpServiceSocket(JNIEnv *e)
221{
222   return jniRegisterNativeMethods(e,
223      "com/android/nfc/nxp/NativeLlcpServiceSocket",
224      gMethods, NELEM(gMethods));
225}
226
227} // namespace android
228