/*
 * Copyright (C) 2012 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 *  Communicate with a peer using NFC-DEP, LLCP, SNEP.
 */
#include "PeerToPeer.h"

#include <android-base/stringprintf.h>
#include <base/logging.h>
#include <nativehelper/ScopedLocalRef.h>

#include "JavaClassConstants.h"
#include "NfcJniUtil.h"
#include "llcp_defs.h"
#include "nfc_config.h"

using android::base::StringPrintf;

/* Some older PN544-based solutions would only send the first SYMM back
 * (as an initiator) after the full LTO (750ms). But our connect timer
 * starts immediately, and hence we may timeout if the timer is set to
 * 1000 ms. Worse, this causes us to immediately connect to the NPP
 * socket, causing concurrency issues in that stack. Increase the default
 * timeout to 2000 ms, giving us enough time to complete the first connect.
 */
#define LLCP_DATA_LINK_TIMEOUT 2000

using namespace android;

namespace android {
extern void nativeNfcTag_registerNdefTypeHandler();
extern void nativeNfcTag_deregisterNdefTypeHandler();
extern void startRfDiscovery(bool isStart);
extern bool isDiscoveryStarted();
}  // namespace android

PeerToPeer PeerToPeer::sP2p;
const std::string P2pServer::sSnepServiceName("urn:nfc:sn:snep");

extern bool nfc_debug_enabled;

/*******************************************************************************
**
** Function:        PeerToPeer
**
** Description:     Initialize member variables.
**
** Returns:         None
**
*******************************************************************************/
PeerToPeer::PeerToPeer()
    : mRemoteWKS(0),
      mIsP2pListening(false),
      mP2pListenTechMask(NFA_TECHNOLOGY_MASK_A | NFA_TECHNOLOGY_MASK_F |
                         NFA_TECHNOLOGY_MASK_A_ACTIVE |
                         NFA_TECHNOLOGY_MASK_F_ACTIVE),
      mNextJniHandle(1) {
  memset(mServers, 0, sizeof(mServers));
  memset(mClients, 0, sizeof(mClients));
}

/*******************************************************************************
**
** Function:        ~PeerToPeer
**
** Description:     Free all resources.
**
** Returns:         None
**
*******************************************************************************/
PeerToPeer::~PeerToPeer() {}

/*******************************************************************************
**
** Function:        getInstance
**
** Description:     Get the singleton PeerToPeer object.
**
** Returns:         Singleton PeerToPeer object.
**
*******************************************************************************/
PeerToPeer& PeerToPeer::getInstance() { return sP2p; }

/*******************************************************************************
**
** Function:        initialize
**
** Description:     Initialize member variables.
**
** Returns:         None
**
*******************************************************************************/
void PeerToPeer::initialize() {
  DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf("PeerToPeer::initialize");

  if (NfcConfig::hasKey(NAME_P2P_LISTEN_TECH_MASK))
    mP2pListenTechMask = NfcConfig::getUnsigned(NAME_P2P_LISTEN_TECH_MASK);
}

/*******************************************************************************
**
** Function:        findServerLocked
**
** Description:     Find a PeerToPeer object by connection handle.
**                  Assumes mMutex is already held
**                  nfaP2pServerHandle: Connectin handle.
**
** Returns:         PeerToPeer object.
**
*******************************************************************************/
sp<P2pServer> PeerToPeer::findServerLocked(tNFA_HANDLE nfaP2pServerHandle) {
  for (int i = 0; i < sMax; i++) {
    if ((mServers[i] != NULL) &&
        (mServers[i]->mNfaP2pServerHandle == nfaP2pServerHandle)) {
      return (mServers[i]);
    }
  }

  // If here, not found
  return NULL;
}

/*******************************************************************************
**
** Function:        findServerLocked
**
** Description:     Find a PeerToPeer object by connection handle.
**                  Assumes mMutex is already held
**                  serviceName: service name.
**
** Returns:         PeerToPeer object.
**
*******************************************************************************/
sp<P2pServer> PeerToPeer::findServerLocked(tJNI_HANDLE jniHandle) {
  for (int i = 0; i < sMax; i++) {
    if ((mServers[i] != NULL) && (mServers[i]->mJniHandle == jniHandle)) {
      return (mServers[i]);
    }
  }

  // If here, not found
  return NULL;
}

/*******************************************************************************
**
** Function:        findServerLocked
**
** Description:     Find a PeerToPeer object by service name
**                  Assumes mMutex is already heldf
**                  serviceName: service name.
**
** Returns:         PeerToPeer object.
**
*******************************************************************************/
sp<P2pServer> PeerToPeer::findServerLocked(const char* serviceName) {
  for (int i = 0; i < sMax; i++) {
    if ((mServers[i] != NULL) &&
        (mServers[i]->mServiceName.compare(serviceName) == 0))
      return (mServers[i]);
  }

  // If here, not found
  return NULL;
}

/*******************************************************************************
**
** Function:        registerServer
**
** Description:     Let a server start listening for peer's connection request.
**                  jniHandle: Connection handle.
**                  serviceName: Server's service name.
**
** Returns:         True if ok.
**
*******************************************************************************/
bool PeerToPeer::registerServer(tJNI_HANDLE jniHandle,
                                const char* serviceName) {
  static const char fn[] = "PeerToPeer::registerServer";
  DLOG_IF(INFO, nfc_debug_enabled)
      << StringPrintf("%s: enter; service name: %s  JNI handle: %u", fn,
                      serviceName, jniHandle);
  sp<P2pServer> pSrv = NULL;

  mMutex.lock();
  // Check if already registered
  if ((pSrv = findServerLocked(serviceName)) != NULL) {
    DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
        "%s: service name=%s  already registered, handle: 0x%04x", fn,
        serviceName, pSrv->mNfaP2pServerHandle);

    // Update JNI handle
    pSrv->mJniHandle = jniHandle;
    mMutex.unlock();
    return (true);
  }

  for (int ii = 0; ii < sMax; ii++) {
    if (mServers[ii] == NULL) {
      pSrv = mServers[ii] = new P2pServer(jniHandle, serviceName);

      DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
          "%s: added new p2p server  index: %d  handle: %u  name: %s", fn, ii,
          jniHandle, serviceName);
      break;
    }
  }
  mMutex.unlock();

  if (pSrv == NULL) {
    LOG(ERROR) << StringPrintf("%s: service name=%s  no free entry", fn,
                               serviceName);
    return (false);
  }

  if (pSrv->registerWithStack()) {
    DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
        "%s: got new p2p server h=0x%X", fn, pSrv->mNfaP2pServerHandle);
    return (true);
  } else {
    LOG(ERROR) << StringPrintf("%s: invalid server handle", fn);
    removeServer(jniHandle);
    return (false);
  }
}

/*******************************************************************************
**
** Function:        removeServer
**
** Description:     Free resources related to a server.
**                  jniHandle: Connection handle.
**
** Returns:         None
**
*******************************************************************************/
void PeerToPeer::removeServer(tJNI_HANDLE jniHandle) {
  static const char fn[] = "PeerToPeer::removeServer";

  AutoMutex mutex(mMutex);

  for (int i = 0; i < sMax; i++) {
    if ((mServers[i] != NULL) && (mServers[i]->mJniHandle == jniHandle)) {
      DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
          "%s: server jni_handle: %u;  nfa_handle: 0x%04x; name: %s; index=%d",
          fn, jniHandle, mServers[i]->mNfaP2pServerHandle,
          mServers[i]->mServiceName.c_str(), i);

      mServers[i] = NULL;
      return;
    }
  }
  LOG(ERROR) << StringPrintf("%s: unknown server jni handle: %u", fn,
                             jniHandle);
}

/*******************************************************************************
**
** Function:        llcpActivatedHandler
**
** Description:     Receive LLLCP-activated event from stack.
**                  nat: JVM-related data.
**                  activated: Event data.
**
** Returns:         None
**
*******************************************************************************/
void PeerToPeer::llcpActivatedHandler(nfc_jni_native_data* nat,
                                      tNFA_LLCP_ACTIVATED& activated) {
  static const char fn[] = "PeerToPeer::llcpActivatedHandler";
  DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf("%s: enter", fn);

  // no longer need to receive NDEF message from a tag
  android::nativeNfcTag_deregisterNdefTypeHandler();

  mRemoteWKS = activated.remote_wks;

  JNIEnv* e = NULL;
  ScopedAttach attach(nat->vm, &e);
  if (e == NULL) {
    LOG(ERROR) << StringPrintf("%s: jni env is null", fn);
    return;
  }

  DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf("%s: get object class", fn);
  ScopedLocalRef<jclass> tag_cls(e, e->GetObjectClass(nat->cached_P2pDevice));
  if (e->ExceptionCheck()) {
    e->ExceptionClear();
    LOG(ERROR) << StringPrintf("%s: fail get p2p device", fn);
    return;
  }

  DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf("%s: instantiate", fn);
  /* New target instance */
  jmethodID ctor = e->GetMethodID(tag_cls.get(), "<init>", "()V");
  ScopedLocalRef<jobject> tag(e, e->NewObject(tag_cls.get(), ctor));

  /* Set P2P Target mode */
  jfieldID f = e->GetFieldID(tag_cls.get(), "mMode", "I");

  if (activated.is_initiator == TRUE) {
    DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf("%s: p2p initiator", fn);
    e->SetIntField(tag.get(), f, (jint)MODE_P2P_INITIATOR);
  } else {
    DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf("%s: p2p target", fn);
    e->SetIntField(tag.get(), f, (jint)MODE_P2P_TARGET);
  }
  /* Set LLCP version */
  f = e->GetFieldID(tag_cls.get(), "mLlcpVersion", "B");
  e->SetByteField(tag.get(), f, (jbyte)activated.remote_version);

  /* Set tag handle */
  f = e->GetFieldID(tag_cls.get(), "mHandle", "I");
  e->SetIntField(tag.get(), f,
                 (jint)0x1234);  // ?? This handle is not used for anything

  if (nat->tag != NULL) {
    e->DeleteGlobalRef(nat->tag);
  }
  nat->tag = e->NewGlobalRef(tag.get());

  DLOG_IF(INFO, nfc_debug_enabled)
      << StringPrintf("%s: notify nfc service", fn);

  /* Notify manager that new a P2P device was found */
  e->CallVoidMethod(nat->manager,
                    android::gCachedNfcManagerNotifyLlcpLinkActivation,
                    tag.get());
  if (e->ExceptionCheck()) {
    e->ExceptionClear();
    LOG(ERROR) << StringPrintf("%s: fail notify", fn);
  }

  DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf("%s: exit", fn);
}

/*******************************************************************************
**
** Function:        llcpDeactivatedHandler
**
** Description:     Receive LLLCP-deactivated event from stack.
**                  nat: JVM-related data.
**                  deactivated: Event data.
**
** Returns:         None
**
*******************************************************************************/
void PeerToPeer::llcpDeactivatedHandler(
    nfc_jni_native_data* nat, tNFA_LLCP_DEACTIVATED& /*deactivated*/) {
  static const char fn[] = "PeerToPeer::llcpDeactivatedHandler";
  DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf("%s: enter", fn);

  JNIEnv* e = NULL;
  ScopedAttach attach(nat->vm, &e);
  if (e == NULL) {
    LOG(ERROR) << StringPrintf("%s: jni env is null", fn);
    return;
  }

  DLOG_IF(INFO, nfc_debug_enabled)
      << StringPrintf("%s: notify nfc service", fn);
  /* Notify manager that the LLCP is lost or deactivated */
  e->CallVoidMethod(nat->manager,
                    android::gCachedNfcManagerNotifyLlcpLinkDeactivated,
                    nat->tag);
  if (e->ExceptionCheck()) {
    e->ExceptionClear();
    LOG(ERROR) << StringPrintf("%s: fail notify", fn);
  }

  // let the tag-reading code handle NDEF data event
  android::nativeNfcTag_registerNdefTypeHandler();
  DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf("%s: exit", fn);
}

void PeerToPeer::llcpFirstPacketHandler(nfc_jni_native_data* nat) {
  static const char fn[] = "PeerToPeer::llcpFirstPacketHandler";
  DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf("%s: enter", fn);

  JNIEnv* e = NULL;
  ScopedAttach attach(nat->vm, &e);
  if (e == NULL) {
    LOG(ERROR) << StringPrintf("%s: jni env is null", fn);
    return;
  }

  DLOG_IF(INFO, nfc_debug_enabled)
      << StringPrintf("%s: notify nfc service", fn);
  /* Notify manager that the LLCP is lost or deactivated */
  e->CallVoidMethod(nat->manager,
                    android::gCachedNfcManagerNotifyLlcpFirstPacketReceived,
                    nat->tag);
  if (e->ExceptionCheck()) {
    e->ExceptionClear();
    LOG(ERROR) << StringPrintf("%s: fail notify", fn);
  }

  DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf("%s: exit", fn);
}
/*******************************************************************************
**
** Function:        accept
**
** Description:     Accept a peer's request to connect.
**                  serverJniHandle: Server's handle.
**                  connJniHandle: Connection handle.
**                  maxInfoUnit: Maximum information unit.
**                  recvWindow: Receive window size.
**
** Returns:         True if ok.
**
*******************************************************************************/
bool PeerToPeer::accept(tJNI_HANDLE serverJniHandle, tJNI_HANDLE connJniHandle,
                        int maxInfoUnit, int recvWindow) {
  static const char fn[] = "PeerToPeer::accept";
  sp<P2pServer> pSrv = NULL;

  DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
      "%s: enter; server jni handle: %u; conn jni handle: %u; maxInfoUnit: %d; "
      "recvWindow: %d",
      fn, serverJniHandle, connJniHandle, maxInfoUnit, recvWindow);

  mMutex.lock();
  if ((pSrv = findServerLocked(serverJniHandle)) == NULL) {
    LOG(ERROR) << StringPrintf("%s: unknown server jni handle: %u", fn,
                               serverJniHandle);
    mMutex.unlock();
    return (false);
  }
  mMutex.unlock();

  return pSrv->accept(serverJniHandle, connJniHandle, maxInfoUnit, recvWindow);
}

/*******************************************************************************
**
** Function:        deregisterServer
**
** Description:     Stop a P2pServer from listening for peer.
**
** Returns:         True if ok.
**
*******************************************************************************/
bool PeerToPeer::deregisterServer(tJNI_HANDLE jniHandle) {
  static const char fn[] = "PeerToPeer::deregisterServer";
  DLOG_IF(INFO, nfc_debug_enabled)
      << StringPrintf("%s: enter; JNI handle: %u", fn, jniHandle);
  tNFA_STATUS nfaStat = NFA_STATUS_FAILED;
  sp<P2pServer> pSrv = NULL;
  bool isPollingTempStopped = false;

  mMutex.lock();
  if ((pSrv = findServerLocked(jniHandle)) == NULL) {
    LOG(ERROR) << StringPrintf("%s: unknown service handle: %u", fn, jniHandle);
    mMutex.unlock();
    return (false);
  }
  mMutex.unlock();
  if (isDiscoveryStarted()) {
    isPollingTempStopped = true;
    startRfDiscovery(false);
  }

  {
    // Server does not call NFA_P2pDisconnect(), so unblock the accept()
    SyncEventGuard guard(pSrv->mConnRequestEvent);
    pSrv->mConnRequestEvent.notifyOne();
  }

  nfaStat = NFA_P2pDeregister(pSrv->mNfaP2pServerHandle);
  if (nfaStat != NFA_STATUS_OK) {
    LOG(ERROR) << StringPrintf("%s: deregister error=0x%X", fn, nfaStat);
  }

  removeServer(jniHandle);

  if (isPollingTempStopped) {
    startRfDiscovery(true);
  }

  DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf("%s: exit", fn);
  return true;
}

/*******************************************************************************
**
** Function:        createClient
**
** Description:     Create a P2pClient object for a new out-bound connection.
**                  jniHandle: Connection handle.
**                  miu: Maximum information unit.
**                  rw: Receive window size.
**
** Returns:         True if ok.
**
*******************************************************************************/
bool PeerToPeer::createClient(tJNI_HANDLE jniHandle, uint16_t miu, uint8_t rw) {
  static const char fn[] = "PeerToPeer::createClient";
  int i = 0;
  DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
      "%s: enter: jni h: %u  miu: %u  rw: %u", fn, jniHandle, miu, rw);

  mMutex.lock();
  sp<P2pClient> client = NULL;
  for (i = 0; i < sMax; i++) {
    if (mClients[i] == NULL) {
      mClients[i] = client = new P2pClient();

      mClients[i]->mClientConn->mJniHandle = jniHandle;
      mClients[i]->mClientConn->mMaxInfoUnit = miu;
      mClients[i]->mClientConn->mRecvWindow = rw;
      break;
    }
  }
  mMutex.unlock();

  if (client == NULL) {
    LOG(ERROR) << StringPrintf("%s: fail", fn);
    return (false);
  }

  DLOG_IF(INFO, nfc_debug_enabled)
      << StringPrintf("%s: pClient: 0x%p  assigned for client jniHandle: %u",
                      fn, client.get(), jniHandle);

  {
    SyncEventGuard guard(mClients[i]->mRegisteringEvent);
    NFA_P2pRegisterClient(NFA_P2P_DLINK_TYPE, nfaClientCallback);
    mClients[i]->mRegisteringEvent.wait();  // wait for NFA_P2P_REG_CLIENT_EVT
  }

  if (mClients[i]->mNfaP2pClientHandle != NFA_HANDLE_INVALID) {
    DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
        "%s: exit; new client jniHandle: %u   NFA Handle: 0x%04x", fn,
        jniHandle, client->mClientConn->mNfaConnHandle);
    return (true);
  } else {
    LOG(ERROR) << StringPrintf(
        "%s: FAILED; new client jniHandle: %u   NFA Handle: 0x%04x", fn,
        jniHandle, client->mClientConn->mNfaConnHandle);
    removeConn(jniHandle);
    return (false);
  }
}

/*******************************************************************************
**
** Function:        removeConn
**
** Description:     Free resources related to a connection.
**                  jniHandle: Connection handle.
**
** Returns:         None
**
*******************************************************************************/
void PeerToPeer::removeConn(tJNI_HANDLE jniHandle) {
  static const char fn[] = "PeerToPeer::removeConn";

  AutoMutex mutex(mMutex);
  // If the connection is a for a client, delete the client itself
  for (int ii = 0; ii < sMax; ii++) {
    if ((mClients[ii] != NULL) &&
        (mClients[ii]->mClientConn->mJniHandle == jniHandle)) {
      if (mClients[ii]->mNfaP2pClientHandle != NFA_HANDLE_INVALID)
        NFA_P2pDeregister(mClients[ii]->mNfaP2pClientHandle);

      mClients[ii] = NULL;
      DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
          "%s: deleted client handle: %u  index: %u", fn, jniHandle, ii);
      return;
    }
  }

  // If the connection is for a server, just delete the connection
  for (int ii = 0; ii < sMax; ii++) {
    if (mServers[ii] != NULL) {
      if (mServers[ii]->removeServerConnection(jniHandle)) {
        return;
      }
    }
  }

  LOG(ERROR) << StringPrintf("%s: could not find handle: %u", fn, jniHandle);
}

/*******************************************************************************
**
** Function:        connectConnOriented
**
** Description:     Establish a connection-oriented connection to a peer.
**                  jniHandle: Connection handle.
**                  serviceName: Peer's service name.
**
** Returns:         True if ok.
**
*******************************************************************************/
bool PeerToPeer::connectConnOriented(tJNI_HANDLE jniHandle,
                                     const char* serviceName) {
  static const char fn[] = "PeerToPeer::connectConnOriented";
  DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
      "%s: enter; h: %u  service name=%s", fn, jniHandle, serviceName);
  bool stat = createDataLinkConn(jniHandle, serviceName, 0);
  DLOG_IF(INFO, nfc_debug_enabled)
      << StringPrintf("%s: exit; h: %u  stat: %u", fn, jniHandle, stat);
  return stat;
}

/*******************************************************************************
**
** Function:        connectConnOriented
**
** Description:     Establish a connection-oriented connection to a peer.
**                  jniHandle: Connection handle.
**                  destinationSap: Peer's service access point.
**
** Returns:         True if ok.
**
*******************************************************************************/
bool PeerToPeer::connectConnOriented(tJNI_HANDLE jniHandle,
                                     uint8_t destinationSap) {
  static const char fn[] = "PeerToPeer::connectConnOriented";
  DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
      "%s: enter; h: %u  dest sap: 0x%X", fn, jniHandle, destinationSap);
  bool stat = createDataLinkConn(jniHandle, NULL, destinationSap);
  DLOG_IF(INFO, nfc_debug_enabled)
      << StringPrintf("%s: exit; h: %u  stat: %u", fn, jniHandle, stat);
  return stat;
}

/*******************************************************************************
**
** Function:        createDataLinkConn
**
** Description:     Establish a connection-oriented connection to a peer.
**                  jniHandle: Connection handle.
**                  serviceName: Peer's service name.
**                  destinationSap: Peer's service access point.
**
** Returns:         True if ok.
**
*******************************************************************************/
bool PeerToPeer::createDataLinkConn(tJNI_HANDLE jniHandle,
                                    const char* serviceName,
                                    uint8_t destinationSap) {
  static const char fn[] = "PeerToPeer::createDataLinkConn";
  DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf("%s: enter", fn);
  tNFA_STATUS nfaStat = NFA_STATUS_FAILED;
  sp<P2pClient> pClient = NULL;

  if ((pClient = findClient(jniHandle)) == NULL) {
    LOG(ERROR) << StringPrintf("%s: can't find client, JNI handle: %u", fn,
                               jniHandle);
    return (false);
  }

  {
    SyncEventGuard guard(pClient->mConnectingEvent);
    pClient->mIsConnecting = true;

    if (serviceName)
      nfaStat = NFA_P2pConnectByName(pClient->mNfaP2pClientHandle,
                                     const_cast<char*>(serviceName),
                                     pClient->mClientConn->mMaxInfoUnit,
                                     pClient->mClientConn->mRecvWindow);
    else if (destinationSap)
      nfaStat =
          NFA_P2pConnectBySap(pClient->mNfaP2pClientHandle, destinationSap,
                              pClient->mClientConn->mMaxInfoUnit,
                              pClient->mClientConn->mRecvWindow);
    if (nfaStat == NFA_STATUS_OK) {
      DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
          "%s: wait for connected event  mConnectingEvent: 0x%p", fn,
          pClient.get());
      pClient->mConnectingEvent.wait();
    }
  }

  if (nfaStat == NFA_STATUS_OK) {
    if (pClient->mClientConn->mNfaConnHandle == NFA_HANDLE_INVALID) {
      removeConn(jniHandle);
      nfaStat = NFA_STATUS_FAILED;
    } else
      pClient->mIsConnecting = false;
  } else {
    removeConn(jniHandle);
    LOG(ERROR) << StringPrintf("%s: fail; error=0x%X", fn, nfaStat);
  }

  DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf("%s: exit", fn);
  return nfaStat == NFA_STATUS_OK;
}

/*******************************************************************************
**
** Function:        findClient
**
** Description:     Find a PeerToPeer object with a client connection handle.
**                  nfaConnHandle: Connection handle.
**
** Returns:         PeerToPeer object.
**
*******************************************************************************/
sp<P2pClient> PeerToPeer::findClient(tNFA_HANDLE nfaConnHandle) {
  AutoMutex mutex(mMutex);
  for (int i = 0; i < sMax; i++) {
    if ((mClients[i] != NULL) &&
        (mClients[i]->mNfaP2pClientHandle == nfaConnHandle))
      return (mClients[i]);
  }
  return (NULL);
}

/*******************************************************************************
**
** Function:        findClient
**
** Description:     Find a PeerToPeer object with a client connection handle.
**                  jniHandle: Connection handle.
**
** Returns:         PeerToPeer object.
**
*******************************************************************************/
sp<P2pClient> PeerToPeer::findClient(tJNI_HANDLE jniHandle) {
  AutoMutex mutex(mMutex);
  for (int i = 0; i < sMax; i++) {
    if ((mClients[i] != NULL) &&
        (mClients[i]->mClientConn->mJniHandle == jniHandle))
      return (mClients[i]);
  }
  return (NULL);
}

/*******************************************************************************
**
** Function:        findClientCon
**
** Description:     Find a PeerToPeer object with a client connection handle.
**                  nfaConnHandle: Connection handle.
**
** Returns:         PeerToPeer object.
**
*******************************************************************************/
sp<P2pClient> PeerToPeer::findClientCon(tNFA_HANDLE nfaConnHandle) {
  AutoMutex mutex(mMutex);
  for (int i = 0; i < sMax; i++) {
    if ((mClients[i] != NULL) &&
        (mClients[i]->mClientConn->mNfaConnHandle == nfaConnHandle))
      return (mClients[i]);
  }
  return (NULL);
}

/*******************************************************************************
**
** Function:        findConnection
**
** Description:     Find a PeerToPeer object with a connection handle.
**                  nfaConnHandle: Connection handle.
**
** Returns:         PeerToPeer object.
**
*******************************************************************************/
sp<NfaConn> PeerToPeer::findConnection(tNFA_HANDLE nfaConnHandle) {
  AutoMutex mutex(mMutex);
  // First, look through all the client control blocks
  for (int ii = 0; ii < sMax; ii++) {
    if ((mClients[ii] != NULL) &&
        (mClients[ii]->mClientConn->mNfaConnHandle == nfaConnHandle)) {
      return mClients[ii]->mClientConn;
    }
  }

  // Not found yet. Look through all the server control blocks
  for (int ii = 0; ii < sMax; ii++) {
    if (mServers[ii] != NULL) {
      sp<NfaConn> conn = mServers[ii]->findServerConnection(nfaConnHandle);
      if (conn != NULL) {
        return conn;
      }
    }
  }

  // Not found...
  return NULL;
}

/*******************************************************************************
**
** Function:        findConnection
**
** Description:     Find a PeerToPeer object with a connection handle.
**                  jniHandle: Connection handle.
**
** Returns:         PeerToPeer object.
**
*******************************************************************************/
sp<NfaConn> PeerToPeer::findConnection(tJNI_HANDLE jniHandle) {
  AutoMutex mutex(mMutex);
  // First, look through all the client control blocks
  for (int ii = 0; ii < sMax; ii++) {
    if ((mClients[ii] != NULL) &&
        (mClients[ii]->mClientConn->mJniHandle == jniHandle)) {
      return mClients[ii]->mClientConn;
    }
  }

  // Not found yet. Look through all the server control blocks
  for (int ii = 0; ii < sMax; ii++) {
    if (mServers[ii] != NULL) {
      sp<NfaConn> conn = mServers[ii]->findServerConnection(jniHandle);
      if (conn != NULL) {
        return conn;
      }
    }
  }

  // Not found...
  return NULL;
}

/*******************************************************************************
**
** Function:        send
**
** Description:     Send data to peer.
**                  jniHandle: Handle of connection.
**                  buffer: Buffer of data.
**                  bufferLen: Length of data.
**
** Returns:         True if ok.
**
*******************************************************************************/
bool PeerToPeer::send(tJNI_HANDLE jniHandle, uint8_t* buffer,
                      uint16_t bufferLen) {
  static const char fn[] = "PeerToPeer::send";
  tNFA_STATUS nfaStat = NFA_STATUS_FAILED;
  sp<NfaConn> pConn = NULL;

  if ((pConn = findConnection(jniHandle)) == NULL) {
    LOG(ERROR) << StringPrintf("%s: can't find connection handle: %u", fn,
                               jniHandle);
    return (false);
  }

  DLOG_IF(INFO, nfc_debug_enabled)
      << StringPrintf("%s: send data; jniHandle: %u  nfaHandle: 0x%04X", fn,
                      pConn->mJniHandle, pConn->mNfaConnHandle);

  while (true) {
    SyncEventGuard guard(pConn->mCongEvent);
    nfaStat = NFA_P2pSendData(pConn->mNfaConnHandle, bufferLen, buffer);
    if (nfaStat == NFA_STATUS_CONGESTED)
      pConn->mCongEvent.wait();  // wait for NFA_P2P_CONGEST_EVT
    else
      break;

    if (pConn->mNfaConnHandle ==
        NFA_HANDLE_INVALID)  // peer already disconnected
    {
      DLOG_IF(INFO, nfc_debug_enabled)
          << StringPrintf("%s: peer disconnected", fn);
      return (false);
    }
  }

  if (nfaStat == NFA_STATUS_OK)
    DLOG_IF(INFO, nfc_debug_enabled)
        << StringPrintf("%s: exit OK; JNI handle: %u  NFA Handle: 0x%04x", fn,
                        jniHandle, pConn->mNfaConnHandle);
  else
    LOG(ERROR) << StringPrintf(
        "%s: Data not sent; JNI handle: %u  NFA Handle: 0x%04x  error: 0x%04x",
        fn, jniHandle, pConn->mNfaConnHandle, nfaStat);

  return nfaStat == NFA_STATUS_OK;
}

/*******************************************************************************
**
** Function:        receive
**
** Description:     Receive data from peer.
**                  jniHandle: Handle of connection.
**                  buffer: Buffer to store data.
**                  bufferLen: Max length of buffer.
**                  actualLen: Actual length received.
**
** Returns:         True if ok.
**
*******************************************************************************/
bool PeerToPeer::receive(tJNI_HANDLE jniHandle, uint8_t* buffer,
                         uint16_t bufferLen, uint16_t& actualLen) {
  static const char fn[] = "PeerToPeer::receive";
  DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
      "%s: enter; jniHandle: %u  bufferLen: %u", fn, jniHandle, bufferLen);
  sp<NfaConn> pConn = NULL;
  tNFA_STATUS stat = NFA_STATUS_FAILED;
  uint32_t actualDataLen2 = 0;
  bool isMoreData = TRUE;
  bool retVal = false;

  if ((pConn = findConnection(jniHandle)) == NULL) {
    LOG(ERROR) << StringPrintf("%s: can't find connection handle: %u", fn,
                               jniHandle);
    return (false);
  }

  DLOG_IF(INFO, nfc_debug_enabled)
      << StringPrintf("%s: jniHandle: %u  nfaHandle: 0x%04X  buf len=%u", fn,
                      pConn->mJniHandle, pConn->mNfaConnHandle, bufferLen);

  while (pConn->mNfaConnHandle != NFA_HANDLE_INVALID) {
    // NFA_P2pReadData() is synchronous
    stat = NFA_P2pReadData(pConn->mNfaConnHandle, bufferLen, &actualDataLen2,
                           buffer, &isMoreData);
    if ((stat == NFA_STATUS_OK) && (actualDataLen2 > 0))  // received some data
    {
      actualLen = (uint16_t)actualDataLen2;
      retVal = true;
      break;
    }
    DLOG_IF(INFO, nfc_debug_enabled)
        << StringPrintf("%s: waiting for data...", fn);
    {
      SyncEventGuard guard(pConn->mReadEvent);
      pConn->mReadEvent.wait();
    }
  }  // while

  DLOG_IF(INFO, nfc_debug_enabled)
      << StringPrintf("%s: exit; nfa h: 0x%X  ok: %u  actual len: %u", fn,
                      pConn->mNfaConnHandle, retVal, actualLen);
  return retVal;
}

/*******************************************************************************
**
** Function:        disconnectConnOriented
**
** Description:     Disconnect a connection-oriented connection with peer.
**                  jniHandle: Handle of connection.
**
** Returns:         True if ok.
**
*******************************************************************************/
bool PeerToPeer::disconnectConnOriented(tJNI_HANDLE jniHandle) {
  static const char fn[] = "PeerToPeer::disconnectConnOriented";
  tNFA_STATUS nfaStat = NFA_STATUS_FAILED;
  sp<P2pClient> pClient = NULL;
  sp<NfaConn> pConn = NULL;

  DLOG_IF(INFO, nfc_debug_enabled)
      << StringPrintf("%s: enter; jni handle: %u", fn, jniHandle);

  if ((pConn = findConnection(jniHandle)) == NULL) {
    LOG(ERROR) << StringPrintf("%s: can't find connection handle: %u", fn,
                               jniHandle);
    return (false);
  }

  // If this is a client, he may not be connected yet, so unblock him just in
  // case
  if (((pClient = findClient(jniHandle)) != NULL) && (pClient->mIsConnecting)) {
    SyncEventGuard guard(pClient->mConnectingEvent);
    pClient->mConnectingEvent.notifyOne();
    return (true);
  }

  {
    SyncEventGuard guard1(pConn->mCongEvent);
    pConn->mCongEvent.notifyOne();  // unblock send() if congested
  }
  {
    SyncEventGuard guard2(pConn->mReadEvent);
    pConn->mReadEvent.notifyOne();  // unblock receive()
  }

  if (pConn->mNfaConnHandle != NFA_HANDLE_INVALID) {
    DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
        "%s: try disconn nfa h=0x%04X", fn, pConn->mNfaConnHandle);
    SyncEventGuard guard(pConn->mDisconnectingEvent);
    nfaStat = NFA_P2pDisconnect(pConn->mNfaConnHandle, FALSE);

    if (nfaStat != NFA_STATUS_OK)
      LOG(ERROR) << StringPrintf("%s: fail p2p disconnect", fn);
    else
      pConn->mDisconnectingEvent.wait();
  }

  mDisconnectMutex.lock();
  removeConn(jniHandle);
  mDisconnectMutex.unlock();

  DLOG_IF(INFO, nfc_debug_enabled)
      << StringPrintf("%s: exit; jni handle: %u", fn, jniHandle);
  return nfaStat == NFA_STATUS_OK;
}

/*******************************************************************************
**
** Function:        getRemoteMaxInfoUnit
**
** Description:     Get peer's max information unit.
**                  jniHandle: Handle of the connection.
**
** Returns:         Peer's max information unit.
**
*******************************************************************************/
uint16_t PeerToPeer::getRemoteMaxInfoUnit(tJNI_HANDLE jniHandle) {
  static const char fn[] = "PeerToPeer::getRemoteMaxInfoUnit";
  sp<NfaConn> pConn = NULL;

  if ((pConn = findConnection(jniHandle)) == NULL) {
    LOG(ERROR) << StringPrintf("%s: can't find client  jniHandle: %u", fn,
                               jniHandle);
    return 0;
  }
  DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
      "%s: jniHandle: %u   MIU: %u", fn, jniHandle, pConn->mRemoteMaxInfoUnit);
  return (pConn->mRemoteMaxInfoUnit);
}

/*******************************************************************************
**
** Function:        getRemoteRecvWindow
**
** Description:     Get peer's receive window size.
**                  jniHandle: Handle of the connection.
**
** Returns:         Peer's receive window size.
**
*******************************************************************************/
uint8_t PeerToPeer::getRemoteRecvWindow(tJNI_HANDLE jniHandle) {
  static const char fn[] = "PeerToPeer::getRemoteRecvWindow";
  DLOG_IF(INFO, nfc_debug_enabled)
      << StringPrintf("%s: client jni handle: %u", fn, jniHandle);
  sp<NfaConn> pConn = NULL;

  if ((pConn = findConnection(jniHandle)) == NULL) {
    LOG(ERROR) << StringPrintf("%s: can't find client", fn);
    return 0;
  }
  return pConn->mRemoteRecvWindow;
}

/*******************************************************************************
**
** Function:        setP2pListenMask
**
** Description:     Sets the p2p listen technology mask.
**                  p2pListenMask: the p2p listen mask to be set?
**
** Returns:         None
**
*******************************************************************************/
void PeerToPeer::setP2pListenMask(tNFA_TECHNOLOGY_MASK p2pListenMask) {
  mP2pListenTechMask = p2pListenMask;
}

/*******************************************************************************
**
** Function:        getP2pListenMask
**
** Description:     Get the set of technologies that P2P is listening.
**
** Returns:         Set of technologies.
**
*******************************************************************************/
tNFA_TECHNOLOGY_MASK PeerToPeer::getP2pListenMask() {
  return mP2pListenTechMask;
}

/*******************************************************************************
**
** Function:        resetP2pListenMask
**
** Description:     Reset the p2p listen technology mask to initial value.
**
** Returns:         None.
**
*******************************************************************************/
void PeerToPeer::resetP2pListenMask() {
  mP2pListenTechMask = NFA_TECHNOLOGY_MASK_A | NFA_TECHNOLOGY_MASK_F |
                       NFA_TECHNOLOGY_MASK_A_ACTIVE |
                       NFA_TECHNOLOGY_MASK_F_ACTIVE;
  if (NfcConfig::hasKey("P2P_LISTEN_TECH_MASK"))
    mP2pListenTechMask = NfcConfig::getUnsigned("P2P_LISTEN_TECH_MASK");
}

/*******************************************************************************
**
** Function:        enableP2pListening
**
** Description:     Start/stop polling/listening to peer that supports P2P.
**                  isEnable: Is enable polling/listening?
**
** Returns:         None
**
*******************************************************************************/
void PeerToPeer::enableP2pListening(bool isEnable) {
  static const char fn[] = "PeerToPeer::enableP2pListening";
  tNFA_STATUS nfaStat = NFA_STATUS_FAILED;

  DLOG_IF(INFO, nfc_debug_enabled)
      << StringPrintf("%s: enter isEnable: %u  mIsP2pListening: %u", fn,
                      isEnable, mIsP2pListening);

  // If request to enable P2P listening, and we were not already listening
  if ((isEnable == true) && (mIsP2pListening == false) &&
      (mP2pListenTechMask != 0)) {
    SyncEventGuard guard(mSetTechEvent);
    if ((nfaStat = NFA_SetP2pListenTech(mP2pListenTechMask)) == NFA_STATUS_OK) {
      mSetTechEvent.wait();
      mIsP2pListening = true;
    } else
      LOG(ERROR) << StringPrintf("%s: fail enable listen; error=0x%X", fn,
                                 nfaStat);
  } else if ((isEnable == false) && (mIsP2pListening == true)) {
    SyncEventGuard guard(mSetTechEvent);
    // Request to disable P2P listening, check if it was enabled
    if ((nfaStat = NFA_SetP2pListenTech(0)) == NFA_STATUS_OK) {
      mSetTechEvent.wait();
      mIsP2pListening = false;
    } else
      LOG(ERROR) << StringPrintf("%s: fail disable listen; error=0x%X", fn,
                                 nfaStat);
  }
  DLOG_IF(INFO, nfc_debug_enabled)
      << StringPrintf("%s: exit; mIsP2pListening: %u", fn, mIsP2pListening);
}

/*******************************************************************************
**
** Function:        handleNfcOnOff
**
** Description:     Handle events related to turning NFC on/off by the user.
**                  isOn: Is NFC turning on?
**
** Returns:         None
**
*******************************************************************************/
void PeerToPeer::handleNfcOnOff(bool isOn) {
  static const char fn[] = "PeerToPeer::handleNfcOnOff";
  DLOG_IF(INFO, nfc_debug_enabled)
      << StringPrintf("%s: enter; is on=%u", fn, isOn);

  mIsP2pListening = false;  // In both cases, P2P will not be listening

  AutoMutex mutex(mMutex);
  if (isOn) {
    // Start with no clients or servers
    memset(mServers, 0, sizeof(mServers));
    memset(mClients, 0, sizeof(mClients));
  } else {
    // Disconnect through all the clients
    for (int ii = 0; ii < sMax; ii++) {
      if (mClients[ii] != NULL) {
        if (mClients[ii]->mClientConn->mNfaConnHandle == NFA_HANDLE_INVALID) {
          SyncEventGuard guard(mClients[ii]->mConnectingEvent);
          mClients[ii]->mConnectingEvent.notifyOne();
        } else {
          mClients[ii]->mClientConn->mNfaConnHandle = NFA_HANDLE_INVALID;
          {
            SyncEventGuard guard1(mClients[ii]->mClientConn->mCongEvent);
            mClients[ii]
                ->mClientConn->mCongEvent.notifyOne();  // unblock send()
          }
          {
            SyncEventGuard guard2(mClients[ii]->mClientConn->mReadEvent);
            mClients[ii]
                ->mClientConn->mReadEvent.notifyOne();  // unblock receive()
          }
        }
      }
    }  // loop

    // Now look through all the server control blocks
    for (int ii = 0; ii < sMax; ii++) {
      if (mServers[ii] != NULL) {
        mServers[ii]->unblockAll();
      }
    }  // loop
  }
  DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf("%s: exit", fn);
}

/*******************************************************************************
**
** Function:        nfaServerCallback
**
** Description:     Receive LLCP-related events from the stack.
**                  p2pEvent: Event code.
**                  eventData: Event data.
**
** Returns:         None
**
*******************************************************************************/
void PeerToPeer::nfaServerCallback(tNFA_P2P_EVT p2pEvent,
                                   tNFA_P2P_EVT_DATA* eventData) {
  static const char fn[] = "PeerToPeer::nfaServerCallback";
  sp<P2pServer> pSrv = NULL;
  sp<NfaConn> pConn = NULL;

  DLOG_IF(INFO, nfc_debug_enabled)
      << StringPrintf("%s: enter; event=0x%X", fn, p2pEvent);

  switch (p2pEvent) {
    case NFA_P2P_REG_SERVER_EVT:  // NFA_P2pRegisterServer() has started to
                                  // listen
      DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
          "%s: NFA_P2P_REG_SERVER_EVT; handle: 0x%04x; service sap=0x%02x  "
          "name: "
          "%s",
          fn, eventData->reg_server.server_handle,
          eventData->reg_server.server_sap, eventData->reg_server.service_name);

      sP2p.mMutex.lock();
      pSrv = sP2p.findServerLocked(eventData->reg_server.service_name);
      sP2p.mMutex.unlock();
      if (pSrv == NULL) {
        LOG(ERROR) << StringPrintf(
            "%s: NFA_P2P_REG_SERVER_EVT for unknown service: %s", fn,
            eventData->reg_server.service_name);
      } else {
        SyncEventGuard guard(pSrv->mRegServerEvent);
        pSrv->mNfaP2pServerHandle = eventData->reg_server.server_handle;
        pSrv->mRegServerEvent.notifyOne();  // unblock registerServer()
      }
      break;

    case NFA_P2P_ACTIVATED_EVT:  // remote device has activated
      DLOG_IF(INFO, nfc_debug_enabled)
          << StringPrintf("%s: NFA_P2P_ACTIVATED_EVT; handle: 0x%04x", fn,
                          eventData->activated.handle);
      break;

    case NFA_P2P_DEACTIVATED_EVT:
      DLOG_IF(INFO, nfc_debug_enabled)
          << StringPrintf("%s: NFA_P2P_DEACTIVATED_EVT; handle: 0x%04x", fn,
                          eventData->activated.handle);
      break;

    case NFA_P2P_CONN_REQ_EVT:
      DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
          "%s: NFA_P2P_CONN_REQ_EVT; nfa server h=0x%04x; nfa conn h=0x%04x; "
          "remote sap=0x%02x",
          fn, eventData->conn_req.server_handle,
          eventData->conn_req.conn_handle, eventData->conn_req.remote_sap);

      sP2p.mMutex.lock();
      pSrv = sP2p.findServerLocked(eventData->conn_req.server_handle);
      sP2p.mMutex.unlock();
      if (pSrv == NULL) {
        LOG(ERROR) << StringPrintf("%s: NFA_P2P_CONN_REQ_EVT; unknown server h",
                                   fn);
        return;
      }
      DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
          "%s: NFA_P2P_CONN_REQ_EVT; server jni h=%u", fn, pSrv->mJniHandle);

      // Look for a connection block that is waiting (handle invalid)
      if ((pConn = pSrv->findServerConnection(
               (tNFA_HANDLE)NFA_HANDLE_INVALID)) == NULL) {
        LOG(ERROR) << StringPrintf(
            "%s: NFA_P2P_CONN_REQ_EVT; server not listening", fn);
      } else {
        SyncEventGuard guard(pSrv->mConnRequestEvent);
        pConn->mNfaConnHandle = eventData->conn_req.conn_handle;
        pConn->mRemoteMaxInfoUnit = eventData->conn_req.remote_miu;
        pConn->mRemoteRecvWindow = eventData->conn_req.remote_rw;
        DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
            "%s: NFA_P2P_CONN_REQ_EVT; server jni h=%u; conn jni "
            "h=%u; notify conn req",
            fn, pSrv->mJniHandle, pConn->mJniHandle);
        pSrv->mConnRequestEvent.notifyOne();  // unblock accept()
      }
      break;

    case NFA_P2P_CONNECTED_EVT:
      DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
          "%s: NFA_P2P_CONNECTED_EVT; h=0x%x  remote sap=0x%X", fn,
          eventData->connected.client_handle, eventData->connected.remote_sap);
      break;

    case NFA_P2P_DISC_EVT:
      DLOG_IF(INFO, nfc_debug_enabled)
          << StringPrintf("%s: NFA_P2P_DISC_EVT; h=0x%04x; reason=0x%X", fn,
                          eventData->disc.handle, eventData->disc.reason);
      // Look for the connection block
      if ((pConn = sP2p.findConnection(eventData->disc.handle)) == NULL) {
        LOG(ERROR) << StringPrintf(
            "%s: NFA_P2P_DISC_EVT: can't find conn for NFA handle: 0x%04x", fn,
            eventData->disc.handle);
      } else {
        sP2p.mDisconnectMutex.lock();
        pConn->mNfaConnHandle = NFA_HANDLE_INVALID;
        {
          DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
              "%s: NFA_P2P_DISC_EVT; try guard disconn event", fn);
          SyncEventGuard guard3(pConn->mDisconnectingEvent);
          pConn->mDisconnectingEvent.notifyOne();
          DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
              "%s: NFA_P2P_DISC_EVT; notified disconn event", fn);
        }
        {
          DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
              "%s: NFA_P2P_DISC_EVT; try guard congest event", fn);
          SyncEventGuard guard1(pConn->mCongEvent);
          pConn->mCongEvent.notifyOne();  // unblock write (if congested)
          DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
              "%s: NFA_P2P_DISC_EVT; notified congest event", fn);
        }
        {
          DLOG_IF(INFO, nfc_debug_enabled)
              << StringPrintf("%s: NFA_P2P_DISC_EVT; try guard read event", fn);
          SyncEventGuard guard2(pConn->mReadEvent);
          pConn->mReadEvent.notifyOne();  // unblock receive()
          DLOG_IF(INFO, nfc_debug_enabled)
              << StringPrintf("%s: NFA_P2P_DISC_EVT; notified read event", fn);
        }
        sP2p.mDisconnectMutex.unlock();
      }
      break;

    case NFA_P2P_DATA_EVT:
      // Look for the connection block
      if ((pConn = sP2p.findConnection(eventData->data.handle)) == NULL) {
        LOG(ERROR) << StringPrintf(
            "%s: NFA_P2P_DATA_EVT: can't find conn for NFA handle: 0x%04x", fn,
            eventData->data.handle);
      } else {
        DLOG_IF(INFO, nfc_debug_enabled)
            << StringPrintf("%s: NFA_P2P_DATA_EVT; h=0x%X; remote sap=0x%X", fn,
                            eventData->data.handle, eventData->data.remote_sap);
        SyncEventGuard guard(pConn->mReadEvent);
        pConn->mReadEvent.notifyOne();
      }
      break;

    case NFA_P2P_CONGEST_EVT:
      // Look for the connection block
      if ((pConn = sP2p.findConnection(eventData->congest.handle)) == NULL) {
        LOG(ERROR) << StringPrintf(
            "%s: NFA_P2P_CONGEST_EVT: can't find conn for NFA handle: 0x%04x",
            fn, eventData->congest.handle);
      } else {
        DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
            "%s: NFA_P2P_CONGEST_EVT; nfa handle: 0x%04x  congested: %u", fn,
            eventData->congest.handle, eventData->congest.is_congested);
        if (eventData->congest.is_congested == FALSE) {
          SyncEventGuard guard(pConn->mCongEvent);
          pConn->mCongEvent.notifyOne();
        }
      }
      break;

    default:
      DLOG_IF(INFO, nfc_debug_enabled)
          << StringPrintf("%s: unknown event 0x%X ????", fn, p2pEvent);
      break;
  }
  DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf("%s: exit", fn);
}

/*******************************************************************************
**
** Function:        nfaClientCallback
**
** Description:     Receive LLCP-related events from the stack.
**                  p2pEvent: Event code.
**                  eventData: Event data.
**
** Returns:         None
**
*******************************************************************************/
void PeerToPeer::nfaClientCallback(tNFA_P2P_EVT p2pEvent,
                                   tNFA_P2P_EVT_DATA* eventData) {
  static const char fn[] = "PeerToPeer::nfaClientCallback";
  sp<NfaConn> pConn = NULL;
  sp<P2pClient> pClient = NULL;

  DLOG_IF(INFO, nfc_debug_enabled)
      << StringPrintf("%s: enter; event=%u", fn, p2pEvent);

  switch (p2pEvent) {
    case NFA_P2P_REG_CLIENT_EVT:
      // Look for a client that is trying to register
      if ((pClient = sP2p.findClient((tNFA_HANDLE)NFA_HANDLE_INVALID)) ==
          NULL) {
        LOG(ERROR) << StringPrintf(
            "%s: NFA_P2P_REG_CLIENT_EVT: can't find waiting client", fn);
      } else {
        DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
            "%s: NFA_P2P_REG_CLIENT_EVT; Conn Handle: 0x%04x, pClient: 0x%p",
            fn, eventData->reg_client.client_handle, pClient.get());

        SyncEventGuard guard(pClient->mRegisteringEvent);
        pClient->mNfaP2pClientHandle = eventData->reg_client.client_handle;
        pClient->mRegisteringEvent.notifyOne();
      }
      break;

    case NFA_P2P_ACTIVATED_EVT:
      // Look for a client that is trying to register
      if ((pClient = sP2p.findClient(eventData->activated.handle)) == NULL) {
        LOG(ERROR) << StringPrintf(
            "%s: NFA_P2P_ACTIVATED_EVT: can't find client", fn);
      } else {
        DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
            "%s: NFA_P2P_ACTIVATED_EVT; Conn Handle: 0x%04x, pClient: 0x%p", fn,
            eventData->activated.handle, pClient.get());
      }
      break;

    case NFA_P2P_DEACTIVATED_EVT:
      DLOG_IF(INFO, nfc_debug_enabled)
          << StringPrintf("%s: NFA_P2P_DEACTIVATED_EVT: conn handle: 0x%X", fn,
                          eventData->deactivated.handle);
      break;

    case NFA_P2P_CONNECTED_EVT:
      // Look for the client that is trying to connect
      if ((pClient = sP2p.findClient(eventData->connected.client_handle)) ==
          NULL) {
        LOG(ERROR) << StringPrintf(
            "%s: NFA_P2P_CONNECTED_EVT: can't find client: 0x%04x", fn,
            eventData->connected.client_handle);
      } else {
        DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
            "%s: NFA_P2P_CONNECTED_EVT; client_handle=0x%04x  "
            "conn_handle: 0x%04x  remote sap=0x%X  pClient: 0x%p",
            fn, eventData->connected.client_handle,
            eventData->connected.conn_handle, eventData->connected.remote_sap,
            pClient.get());

        SyncEventGuard guard(pClient->mConnectingEvent);
        pClient->mClientConn->mNfaConnHandle = eventData->connected.conn_handle;
        pClient->mClientConn->mRemoteMaxInfoUnit =
            eventData->connected.remote_miu;
        pClient->mClientConn->mRemoteRecvWindow =
            eventData->connected.remote_rw;
        pClient->mConnectingEvent.notifyOne();  // unblock createDataLinkConn()
      }
      break;

    case NFA_P2P_DISC_EVT:
      DLOG_IF(INFO, nfc_debug_enabled)
          << StringPrintf("%s: NFA_P2P_DISC_EVT; h=0x%04x; reason=0x%X", fn,
                          eventData->disc.handle, eventData->disc.reason);
      // Look for the connection block
      if ((pConn = sP2p.findConnection(eventData->disc.handle)) == NULL) {
        // If no connection, may be a client that is trying to connect
        if ((pClient = sP2p.findClient(eventData->disc.handle)) == NULL) {
          LOG(ERROR) << StringPrintf(
              "%s: NFA_P2P_DISC_EVT: can't find client for NFA handle: 0x%04x",
              fn, eventData->disc.handle);
          return;
        }
        // Unblock createDataLinkConn()
        SyncEventGuard guard(pClient->mConnectingEvent);
        pClient->mConnectingEvent.notifyOne();
      } else {
        sP2p.mDisconnectMutex.lock();
        pConn->mNfaConnHandle = NFA_HANDLE_INVALID;
        {
          DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
              "%s: NFA_P2P_DISC_EVT; try guard disconn event", fn);
          SyncEventGuard guard3(pConn->mDisconnectingEvent);
          pConn->mDisconnectingEvent.notifyOne();
          DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
              "%s: NFA_P2P_DISC_EVT; notified disconn event", fn);
        }
        {
          DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
              "%s: NFA_P2P_DISC_EVT; try guard congest event", fn);
          SyncEventGuard guard1(pConn->mCongEvent);
          pConn->mCongEvent.notifyOne();  // unblock write (if congested)
          DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
              "%s: NFA_P2P_DISC_EVT; notified congest event", fn);
        }
        {
          DLOG_IF(INFO, nfc_debug_enabled)
              << StringPrintf("%s: NFA_P2P_DISC_EVT; try guard read event", fn);
          SyncEventGuard guard2(pConn->mReadEvent);
          pConn->mReadEvent.notifyOne();  // unblock receive()
          DLOG_IF(INFO, nfc_debug_enabled)
              << StringPrintf("%s: NFA_P2P_DISC_EVT; notified read event", fn);
        }
        sP2p.mDisconnectMutex.unlock();
      }
      break;

    case NFA_P2P_DATA_EVT:
      // Look for the connection block
      if ((pConn = sP2p.findConnection(eventData->data.handle)) == NULL) {
        LOG(ERROR) << StringPrintf(
            "%s: NFA_P2P_DATA_EVT: can't find conn for NFA handle: 0x%04x", fn,
            eventData->data.handle);
      } else {
        DLOG_IF(INFO, nfc_debug_enabled)
            << StringPrintf("%s: NFA_P2P_DATA_EVT; h=0x%X; remote sap=0x%X", fn,
                            eventData->data.handle, eventData->data.remote_sap);
        SyncEventGuard guard(pConn->mReadEvent);
        pConn->mReadEvent.notifyOne();
      }
      break;

    case NFA_P2P_CONGEST_EVT:
      // Look for the connection block
      if ((pConn = sP2p.findConnection(eventData->congest.handle)) == NULL) {
        LOG(ERROR) << StringPrintf(
            "%s: NFA_P2P_CONGEST_EVT: can't find conn for NFA handle: 0x%04x",
            fn, eventData->congest.handle);
      } else {
        DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
            "%s: NFA_P2P_CONGEST_EVT; nfa handle: 0x%04x  congested: %u", fn,
            eventData->congest.handle, eventData->congest.is_congested);

        SyncEventGuard guard(pConn->mCongEvent);
        pConn->mCongEvent.notifyOne();
      }
      break;

    default:
      LOG(ERROR) << StringPrintf("%s: unknown event 0x%X ????", fn, p2pEvent);
      break;
  }
}

/*******************************************************************************
**
** Function:        connectionEventHandler
**
** Description:     Receive events from the stack.
**                  event: Event code.
**                  eventData: Event data.
**
** Returns:         None
**
*******************************************************************************/
void PeerToPeer::connectionEventHandler(uint8_t event,
                                        tNFA_CONN_EVT_DATA* /*eventData*/) {
  switch (event) {
    case NFA_SET_P2P_LISTEN_TECH_EVT: {
      SyncEventGuard guard(mSetTechEvent);
      mSetTechEvent.notifyOne();  // unblock NFA_SetP2pListenTech()
      break;
    }
  }
}

/*******************************************************************************
**
** Function:        getNextJniHandle
**
** Description:     Get a new JNI handle.
**
** Returns:         A new JNI handle.
**
*******************************************************************************/
PeerToPeer::tJNI_HANDLE PeerToPeer::getNewJniHandle() {
  tJNI_HANDLE newHandle = 0;

  mNewJniHandleMutex.lock();
  newHandle = mNextJniHandle++;
  mNewJniHandleMutex.unlock();
  return newHandle;
}

/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////

/*******************************************************************************
**
** Function:        P2pServer
**
** Description:     Initialize member variables.
**
** Returns:         None
**
*******************************************************************************/
P2pServer::P2pServer(PeerToPeer::tJNI_HANDLE jniHandle, const char* serviceName)
    : mNfaP2pServerHandle(NFA_HANDLE_INVALID), mJniHandle(jniHandle) {
  mServiceName.assign(serviceName);

  memset(mServerConn, 0, sizeof(mServerConn));
}

bool P2pServer::registerWithStack() {
  static const char fn[] = "P2pServer::registerWithStack";
  DLOG_IF(INFO, nfc_debug_enabled)
      << StringPrintf("%s: enter; service name: %s  JNI handle: %u", fn,
                      mServiceName.c_str(), mJniHandle);
  tNFA_STATUS stat = NFA_STATUS_OK;
  uint8_t serverSap = NFA_P2P_ANY_SAP;

  /**********************
 default values for all LLCP parameters:
 - Local Link MIU (LLCP_MIU)
 - Option parameter (LLCP_OPT_VALUE)
 - Response Waiting Time Index (LLCP_WAITING_TIME)
 - Local Link Timeout (LLCP_LTO_VALUE)
 - Inactivity Timeout as initiator role (LLCP_INIT_INACTIVITY_TIMEOUT)
 - Inactivity Timeout as target role (LLCP_TARGET_INACTIVITY_TIMEOUT)
 - Delay SYMM response (LLCP_DELAY_RESP_TIME)
 - Data link connection timeout (LLCP_DATA_LINK_CONNECTION_TOUT)
 - Delay timeout to send first PDU as initiator
 (LLCP_DELAY_TIME_TO_SEND_FIRST_PDU)
 ************************/
  stat = NFA_P2pSetLLCPConfig(LLCP_MAX_MIU, LLCP_OPT_VALUE, LLCP_WAITING_TIME,
                              LLCP_LTO_VALUE,
                              0,  // use 0 for infinite timeout for symmetry
                                  // procedure when acting as initiator
                              0,  // use 0 for infinite timeout for symmetry
                                  // procedure when acting as target
                              LLCP_DELAY_RESP_TIME, LLCP_DATA_LINK_TIMEOUT,
                              LLCP_DELAY_TIME_TO_SEND_FIRST_PDU);
  if (stat != NFA_STATUS_OK)
    LOG(ERROR) << StringPrintf("%s: fail set LLCP config; error=0x%X", fn,
                               stat);

  if (sSnepServiceName.compare(mServiceName) == 0)
    serverSap = 4;  // LLCP_SAP_SNEP == 4

  {
    SyncEventGuard guard(mRegServerEvent);
    stat = NFA_P2pRegisterServer(serverSap, NFA_P2P_DLINK_TYPE,
                                 const_cast<char*>(mServiceName.c_str()),
                                 PeerToPeer::nfaServerCallback);
    if (stat != NFA_STATUS_OK) {
      LOG(ERROR) << StringPrintf("%s: fail register p2p server; error=0x%X", fn,
                                 stat);
      return (false);
    }
    DLOG_IF(INFO, nfc_debug_enabled)
        << StringPrintf("%s: wait for listen-completion event", fn);
    // Wait for NFA_P2P_REG_SERVER_EVT
    mRegServerEvent.wait();
  }

  return (mNfaP2pServerHandle != NFA_HANDLE_INVALID);
}

bool P2pServer::accept(PeerToPeer::tJNI_HANDLE serverJniHandle,
                       PeerToPeer::tJNI_HANDLE connJniHandle, int maxInfoUnit,
                       int recvWindow) {
  static const char fn[] = "P2pServer::accept";
  tNFA_STATUS nfaStat = NFA_STATUS_OK;

  sp<NfaConn> connection = allocateConnection(connJniHandle);
  if (connection == NULL) {
    LOG(ERROR) << StringPrintf("%s: failed to allocate new server connection",
                               fn);
    return false;
  }

  {
    // Wait for NFA_P2P_CONN_REQ_EVT or NFA_NDEF_DATA_EVT when remote device
    // requests connection
    SyncEventGuard guard(mConnRequestEvent);
    DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
        "%s: serverJniHandle: %u; connJniHandle: %u; wait for "
        "incoming connection",
        fn, serverJniHandle, connJniHandle);
    mConnRequestEvent.wait();
    DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
        "%s: serverJniHandle: %u; connJniHandle: %u; nfa conn h: 0x%X; got "
        "incoming connection",
        fn, serverJniHandle, connJniHandle, connection->mNfaConnHandle);
  }

  if (connection->mNfaConnHandle == NFA_HANDLE_INVALID) {
    removeServerConnection(connJniHandle);
    DLOG_IF(INFO, nfc_debug_enabled)
        << StringPrintf("%s: no handle assigned", fn);
    return (false);
  }

  if (maxInfoUnit > (int)LLCP_MIU) {
    DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
        "%s: overriding the miu passed by the app(%d) with stack miu(%zu)", fn,
        maxInfoUnit, LLCP_MIU);
    maxInfoUnit = LLCP_MIU;
  }

  DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
      "%s: serverJniHandle: %u; connJniHandle: %u; nfa conn h: 0x%X; try "
      "accept",
      fn, serverJniHandle, connJniHandle, connection->mNfaConnHandle);
  nfaStat =
      NFA_P2pAcceptConn(connection->mNfaConnHandle, maxInfoUnit, recvWindow);

  if (nfaStat != NFA_STATUS_OK) {
    LOG(ERROR) << StringPrintf("%s: fail to accept remote; error=0x%X", fn,
                               nfaStat);
    return (false);
  }

  DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
      "%s: exit; serverJniHandle: %u; connJniHandle: %u; nfa conn h: 0x%X", fn,
      serverJniHandle, connJniHandle, connection->mNfaConnHandle);
  return (true);
}

void P2pServer::unblockAll() {
  AutoMutex mutex(mMutex);
  for (int jj = 0; jj < MAX_NFA_CONNS_PER_SERVER; jj++) {
    if (mServerConn[jj] != NULL) {
      mServerConn[jj]->mNfaConnHandle = NFA_HANDLE_INVALID;
      {
        SyncEventGuard guard1(mServerConn[jj]->mCongEvent);
        mServerConn[jj]
            ->mCongEvent.notifyOne();  // unblock write (if congested)
      }
      {
        SyncEventGuard guard2(mServerConn[jj]->mReadEvent);
        mServerConn[jj]->mReadEvent.notifyOne();  // unblock receive()
      }
    }
  }
}

sp<NfaConn> P2pServer::allocateConnection(PeerToPeer::tJNI_HANDLE jniHandle) {
  AutoMutex mutex(mMutex);
  // First, find a free connection block to handle the connection
  for (int ii = 0; ii < MAX_NFA_CONNS_PER_SERVER; ii++) {
    if (mServerConn[ii] == NULL) {
      mServerConn[ii] = new NfaConn;
      mServerConn[ii]->mJniHandle = jniHandle;
      return mServerConn[ii];
    }
  }

  return NULL;
}

/*******************************************************************************
**
** Function:        findServerConnection
**
** Description:     Find a P2pServer that has the handle.
**                  nfaConnHandle: NFA connection handle.
**
** Returns:         P2pServer object.
**
*******************************************************************************/
sp<NfaConn> P2pServer::findServerConnection(tNFA_HANDLE nfaConnHandle) {
  int jj = 0;

  AutoMutex mutex(mMutex);
  for (jj = 0; jj < MAX_NFA_CONNS_PER_SERVER; jj++) {
    if ((mServerConn[jj] != NULL) &&
        (mServerConn[jj]->mNfaConnHandle == nfaConnHandle))
      return (mServerConn[jj]);
  }

  // If here, not found
  return (NULL);
}

/*******************************************************************************
**
** Function:        findServerConnection
**
** Description:     Find a P2pServer that has the handle.
**                  nfaConnHandle: NFA connection handle.
**
** Returns:         P2pServer object.
**
*******************************************************************************/
sp<NfaConn> P2pServer::findServerConnection(PeerToPeer::tJNI_HANDLE jniHandle) {
  int jj = 0;

  AutoMutex mutex(mMutex);
  for (jj = 0; jj < MAX_NFA_CONNS_PER_SERVER; jj++) {
    if ((mServerConn[jj] != NULL) && (mServerConn[jj]->mJniHandle == jniHandle))
      return (mServerConn[jj]);
  }

  // If here, not found
  return (NULL);
}

/*******************************************************************************
**
** Function:        removeServerConnection
**
** Description:     Find a P2pServer that has the handle.
**                  nfaConnHandle: NFA connection handle.
**
** Returns:         P2pServer object.
**
*******************************************************************************/
bool P2pServer::removeServerConnection(PeerToPeer::tJNI_HANDLE jniHandle) {
  int jj = 0;

  AutoMutex mutex(mMutex);
  for (jj = 0; jj < MAX_NFA_CONNS_PER_SERVER; jj++) {
    if ((mServerConn[jj] != NULL) &&
        (mServerConn[jj]->mJniHandle == jniHandle)) {
      mServerConn[jj] = NULL;
      return true;
    }
  }

  // If here, not found
  return false;
}
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////

/*******************************************************************************
**
** Function:        P2pClient
**
** Description:     Initialize member variables.
**
** Returns:         None
**
*******************************************************************************/
P2pClient::P2pClient()
    : mNfaP2pClientHandle(NFA_HANDLE_INVALID), mIsConnecting(false) {
  mClientConn = new NfaConn();
}

/*******************************************************************************
**
** Function:        ~P2pClient
**
** Description:     Free all resources.
**
** Returns:         None
**
*******************************************************************************/
P2pClient::~P2pClient() {}

/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////

/*******************************************************************************
**
** Function:        NfaConn
**
** Description:     Initialize member variables.
**
** Returns:         None
**
*******************************************************************************/
NfaConn::NfaConn()
    : mNfaConnHandle(NFA_HANDLE_INVALID),
      mJniHandle(0),
      mMaxInfoUnit(0),
      mRecvWindow(0),
      mRemoteMaxInfoUnit(0),
      mRemoteRecvWindow(0) {}