blob: 731d1d00324a9d0fa6f4f50c45936891a7e77049 [file] [log] [blame]
/*
** Copyright 2006, The Android Open Source Project
** Copyright (c) 2010, Code Aurora Forum. All rights reserved.
**
** 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.
*/
#define LOG_TAG "CND_PROCESS"
#define LOCAL_TAG "CND_PROCESS_DEBUG"
#include <cutils/sockets.h>
#include <cutils/jstring.h>
#include <cutils/record_stream.h>
#include <utils/Log.h>
#include <utils/SystemClock.h>
#include <pthread.h>
#include <binder/Parcel.h>
#include <cutils/jstring.h>
#include <sys/types.h>
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <time.h>
#include <errno.h>
#include <assert.h>
#include <ctype.h>
#include <alloca.h>
#include <sys/un.h>
#include <assert.h>
#include <netinet/in.h>
#include <cutils/properties.h>
#include <dirent.h>
#include <cnd_event.h>
#include <cnd.h>
#include <cne.h>
#include <cnd_iproute2.h>
namespace android {
#define SOCKET_NAME_CND "cnd"
// match with constant in .java
#define MAX_COMMAND_BYTES (8 * 1024)
// Basically: memset buffers that the client library
// shouldn't be using anymore in an attempt to find
// memory usage issues sooner.
#define MEMSET_FREED 1
#define NUM_ELEMS(a) (sizeof (a) / sizeof (a)[0])
/* Constants for response types */
#define SOLICITED_RESPONSE 0
#define UNSOLICITED_MESSAGE 1
typedef struct {
int commandNumber;
void (*dispatchFunction) (Parcel &p, struct RequestInfo *pRI);
int(*responseFunction) (Parcel &p, void *response, size_t responselen);
} CommandInfo;
typedef struct {
int messageNumber;
int (*responseFunction) (Parcel &p, void *response, size_t responselen);
} UnsolMessageInfo;
typedef struct RequestInfo {
int32_t token; //this is not CND_Token
int fd;
CommandInfo *pCI;
struct RequestInfo *p_next;
char cancelled;
} RequestInfo;
/*******************************************************************/
static int s_registerCalled = 0;
static pthread_t s_tid_dispatch;
static int s_started = 0;
static int s_fdListen = -1;
static int s_fdCommand = -1;
static int cnmSvcFd = -1;
static struct cnd_event s_commands_event[MAX_FD_EVENTS];
static struct cnd_event s_listen_event;
static int command_index = 0;
static int cneServiceEnabled = 0;
static pthread_mutex_t s_pendingRequestsMutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t s_writeMutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t s_startupMutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t s_startupCond = PTHREAD_COND_INITIALIZER;
static pthread_mutex_t s_dispatchMutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t s_dispatchCond = PTHREAD_COND_INITIALIZER;
static RequestInfo *s_pendingRequests = NULL;
static RequestInfo *s_toDispatchHead = NULL;
static RequestInfo *s_toDispatchTail = NULL;
/*******************************************************************/
static void dispatchVoid (Parcel& p, RequestInfo *pRI);
static void dispatchString (Parcel& p, RequestInfo *pRI);
static void dispatchStrings (Parcel& p, RequestInfo *pRI);
static void dispatchInts (Parcel& p, RequestInfo *pRI);
static void dispatchWlanInfo(Parcel &p, RequestInfo *pRI);
static void dispatchWwanInfo(Parcel &p, RequestInfo *pRI);
static void dispatchWlanScanResults(Parcel &p, RequestInfo *pRI);
static void dispatchRaw(Parcel& p, RequestInfo *pRI);
static void dispatchRatStatus(Parcel &p, RequestInfo *pRI);
static void dispatchIproute2Cmd(Parcel &p, RequestInfo *pRI);
static int responseInts(Parcel &p, void *response, size_t responselen);
static int responseStrings(Parcel &p, void *response, size_t responselen);
static int responseString(Parcel &p, void *response, size_t responselen);
static int responseVoid(Parcel &p, void *response, size_t responselen);
static int responseRaw(Parcel &p, void *response, size_t responselen);
static int responseStartTrans(Parcel &p, void *response, size_t responselen);
static int sendResponseRaw (const void *data, size_t dataSize, int fdCommand);
static int sendResponse (Parcel &p, int fd);
static int rspCompatibleNws(Parcel &p, void *response, size_t responselen);
static int evtMorePrefNw(Parcel &p, void *response, size_t responselen);
static int eventRatChange (Parcel &p, void *response, size_t responselen);
static char *strdupReadString(Parcel &p);
static void writeStringToParcel(Parcel &p, const char *s);
static void memsetString (char *s);
static int writeData(int fd, const void *buffer, size_t len);
static void unsolicitedMessage(int unsolMessage, void *data, size_t datalen, int fd);
static void processCommand (int command, void *data, size_t datalen, CND_Token t);
static int processCommandBuffer(void *buffer, size_t buflen, int fd);
static void onCommandsSocketClosed(void);
static void processCommandsCallback(int fd, void *param);
static void listenCallback (int fd, void *param);
static void *eventLoop(void *param);
static int checkAndDequeueRequestInfo(struct RequestInfo *pRI);
static void cnd_commandComplete(CND_Token t, CND_Errno e, void *response, size_t responselen);
extern "C" const char * requestToString(int request);
extern "C" cneProcessCmdFnType cne_processCommand;
extern "C" cneRegMsgCbFnType cne_regMessageCb;
/** Index == commandNumber */
static CommandInfo s_commands[] = {
#include "cnd_commands.h"
};
static UnsolMessageInfo s_unsolMessages[] = {
#include "cnd_unsol_messages.h"
};
#define TEMP_BUFFER_SIZE (80)
void cnd_sendUnsolicitedMsg(int targetFd, int msgType, int dataLen, void *data)
{
int fd;
if (targetFd == 0) // TODO find the correct fd, who keeps track of it?
fd = cnmSvcFd;
else
fd = targetFd;
CNE_LOGV ("cnd_sendUnsolicitedMsg: Fd=%d, msgType=%d, datalen=%d\n",
targetFd, msgType, dataLen);
unsolicitedMessage(msgType, data, dataLen, fd);
}
static void
processCommand (int command, void *data, size_t datalen, CND_Token t)
{
CNE_LOGV ("processCommand: command=%d, datalen=%d", command, datalen);
/* Special handling for iproute2 command to setup iproute2 table */
if ((command == CNE_REQUEST_CONFIG_IPROUTE2_CMD) && (cneServiceEnabled))
{
int cmd = 0;
unsigned char *ipAddr = NULL;
unsigned char *gatewayAddr = NULL;
unsigned char *ifName = NULL;
if ((data == NULL) || (datalen ==0)) {
CNE_LOGD ("processCommand: invalid data");
return;
}
cmd = ((int *)data)[0];
ifName = ((unsigned char **)data)[1];
ipAddr = ((unsigned char **)data)[2];
gatewayAddr = ((unsigned char **)data)[3];
CNE_LOGV ("processCommand: iproute2cmd=%d, ipAddr=%s, gatewayAddr=%s, "
"ifName=%s", cmd, ipAddr, gatewayAddr, ifName);
cnd_iproute2* cnd_iproute2_ptr = cnd_iproute2::getInstance();
if (cnd_iproute2_ptr != NULL) {
// Call iproute2 API
switch(cmd)
{
case CNE_IPROUTE2_ADD_ROUTING:
cnd_iproute2::getInstance()->addRoutingTable(ifName, ipAddr, gatewayAddr);
break;
case CNE_IPROUTE2_DELETE_ROUTING:
case CNE_IPROUTE2_DELETE_HOST_ROUTING:
cnd_iproute2::getInstance()->deleteRoutingTable(ifName);
break;
case CNE_IPROUTE2_DELETE_DEFAULT_IN_MAIN:
case CNE_IPROUTE2_DELETE_HOST_DEFAULT_IN_MAIN:
cnd_iproute2::getInstance()->deleteDefaultEntryInMainTable(ifName);
break;
case CNE_IPROUTE2_REPLACE_DEFAULT_ENTRY_IN_MAIN:
case CNE_IPROUTE2_REPLACE_HOST_DEFAULT_ENTRY_IN_MAIN:
cnd_iproute2::getInstance()->replaceDefaultEntryInMainTable(ifName, gatewayAddr);
break;
case CNE_IPROUTE2_ADD_HOST_IN_MAIN:
cnd_iproute2::getInstance()->addCustomEntryInMainTable(ipAddr, ifName, gatewayAddr);
break;
case CNE_IPROUTE2_DELETE_HOST_IN_MAIN:
cnd_iproute2::getInstance()->deleteDeviceCustomEntriesInMainTable(ifName);
break;
default:
CNE_LOGD ("processCommand: not iproute2 command=%d", command);
break;
}
}
return;
}
if(cne_processCommand != NULL)
{
cne_processCommand(command, data, datalen);
}
else
{
CNE_LOGD("cne_processCommand is NULL");
}
cnd_commandComplete(t, CND_E_SUCCESS, NULL, 0);
return;
}
static char *
strdupReadString(Parcel &p)
{
size_t stringlen;
const char16_t *s16;
s16 = p.readString16Inplace(&stringlen);
return strndup16to8(s16, stringlen);
}
static void writeStringToParcel(Parcel &p, const char *s)
{
char16_t *s16;
size_t s16_len;
s16 = strdup8to16(s, &s16_len);
p.writeString16(s16, s16_len);
free(s16);
}
static void
memsetString (char *s)
{
if (s != NULL) {
memset (s, 0, strlen(s));
}
}
/** Callee expects NULL */
static void
dispatchVoid (Parcel& p, RequestInfo *pRI)
{
processCommand(pRI->pCI->commandNumber, NULL, 0, pRI);
}
/** Callee expects const char * */
static void
dispatchString (Parcel& p, RequestInfo *pRI)
{
status_t status;
size_t datalen;
size_t stringlen;
char *string8 = NULL;
string8 = strdupReadString(p);
CNE_LOGV ("dispatchString: strlen=%d", strlen(string8));
processCommand(pRI->pCI->commandNumber, string8, strlen(string8), pRI);
#ifdef MEMSET_FREED
memsetString(string8);
#endif
free(string8);
return;
}
/** Callee expects const char ** */
static void
dispatchStrings (Parcel &p, RequestInfo *pRI)
{
int32_t countStrings;
status_t status;
size_t datalen;
char **pStrings;
status = p.readInt32 (&countStrings);
if (status != NO_ERROR){
CNE_LOGD ("dispatchStrings: invalid block");
return;
}
if (countStrings == 0) {
// just some non-null pointer
pStrings = (char **)alloca(sizeof(char *));
datalen = 0;
} else if (((int)countStrings) == -1) {
pStrings = NULL;
datalen = 0;
} else {
datalen = sizeof(char *) * countStrings;
pStrings = (char **)alloca(datalen);
for (int i = 0 ; i < countStrings ; i++) {
pStrings[i] = strdupReadString(p);
}
}
processCommand(pRI->pCI->commandNumber, pStrings, datalen, pRI);
if (pStrings != NULL) {
for (int i = 0 ; i < countStrings ; i++) {
#ifdef MEMSET_FREED
memsetString (pStrings[i]);
#endif
free(pStrings[i]);
}
#ifdef MEMSET_FREED
memset(pStrings, 0, datalen);
#endif
}
return;
}
/** Callee expects const int * */
static void
dispatchInts (Parcel &p, RequestInfo *pRI)
{
int32_t count;
status_t status;
size_t datalen;
int *pInts;
status = p.readInt32 (&count);
if (status != NO_ERROR || count == 0) {
CNE_LOGD ("dispatchInts: invalid block");
return;
}
datalen = sizeof(int) * count;
pInts = (int *)alloca(datalen);
if (pInts == NULL) {
CNE_LOGW ("dispatchInts: alloc failed");
return;
}
for (int i = 0 ; i < count ; i++) {
int32_t t;
status = p.readInt32(&t);
pInts[i] = (int)t;
if (status != NO_ERROR) {
CNE_LOGD ("dispatchInts: invalid block");
return;
}
}
processCommand(pRI->pCI->commandNumber, const_cast<int *>(pInts),
datalen, pRI);
#ifdef MEMSET_FREED
memset(pInts, 0, datalen);
#endif
return;
}
static void
dispatchWlanInfo(Parcel &p, RequestInfo *pRI)
{
int32_t t;
status_t status;
CneWlanInfoType args;
memset(&args, 0, sizeof(args));
status = p.readInt32 (&t);
args.type = (int)t;
status = p.readInt32 (&t);
args.status = (int)t;
status = p.readInt32 (&t);
args.rssi = (int)t;
args.ssid = strdupReadString(p);
args.ipAddr = strdupReadString(p);
args.timeStamp = strdupReadString(p);
if (status != NO_ERROR){
CNE_LOGD ("dispatchWlanInfo: invalid block");
return;
}
CNE_LOGV ("dispatchWlanInfo: state=%d, rssi=%d, ssid=%s, ipAddr=%s, "
"timeStamp=%s",
args.status, args.rssi, args.ssid, args.ipAddr, args.timeStamp);
processCommand(pRI->pCI->commandNumber, &args, sizeof(args), pRI);
return;
}
static void
dispatchWlanScanResults(Parcel &p, RequestInfo *pRI)
{
int32_t t;
status_t status;
CneWlanScanResultsType args;
int32_t numItems;
status = p.readInt32 (&t);
if (status != NO_ERROR){
CNE_LOGD ("dispatchWlanScanResults: invalid block");
return;
}
args.numItems = (int)t;
int max = (t < CNE_MAX_SCANLIST_SIZE)? t:CNE_MAX_SCANLIST_SIZE;
for (int i = 0; i < max; i++)
{
args.numItems = max;
status = p.readInt32 (&t);
args.scanList[i].level = (int)t;
status = p.readInt32 (&t);
args.scanList[i].frequency = (int)t;
args.scanList[i].ssid = strdupReadString(p);
args.scanList[i].bssid = strdupReadString(p);
args.scanList[i].capabilities = strdupReadString(p);
if (status != NO_ERROR){
CNE_LOGD ("dispatchWlanScanResults: invalid block");
return;
}
}
processCommand(pRI->pCI->commandNumber, &args, sizeof(args), pRI);
return;
}
static void
dispatchWwanInfo(Parcel &p, RequestInfo *pRI)
{
int32_t t;
status_t status;
CneWwanInfoType args;
memset(&args, 0, sizeof(args));
status = p.readInt32 (&t);
args.type = (int)t;
status = p.readInt32 (&t);
args.status = (int)t;
status = p.readInt32 (&t);
args.rssi = (int)t;
status = p.readInt32 (&t);
args.roaming = (int)t;
args.ipAddr = strdupReadString(p);
args.timeStamp = strdupReadString(p);
if (status != NO_ERROR){
CNE_LOGD ("dispatchWwanInfo: invalid block");
return;
}
CNE_LOGV ("dispatchWwanInfo: type=%d, state=%d, rssi=%d, roaming=%d, "
"ipAddr=%s, timeStamp=%s", args.type, args.status, args.rssi,
args.roaming, args.ipAddr, args.timeStamp);
processCommand(pRI->pCI->commandNumber, &args, sizeof(args), pRI);
return;
}
static void
dispatchRatStatus(Parcel &p, RequestInfo *pRI)
{
int32_t t;
status_t status;
CneRatStatusType args;
memset(&args, 0, sizeof(args));
status = p.readInt32 (&t);
args.rat = (cne_rat_type)t;
status = p.readInt32 (&t);
args.ratStatus = (cne_network_state_enum_type)t;
args.ipAddr = strdupReadString(p);
if (status != NO_ERROR){
CNE_LOGD ("dispatchRatStatus: invalid block");
return;
}
CNE_LOGV ("dispatchRatStatus: type=%d, ratStatus=%d, ipAddr=%s",
args.rat, args.ratStatus, args.ipAddr);
processCommand(pRI->pCI->commandNumber, &args, sizeof(args), pRI);
return;
}
static void
dispatchIproute2Cmd(Parcel &p, RequestInfo *pRI)
{
int32_t t;
status_t status;
CneIpRoute2CmdType args;
memset(&args, 0, sizeof(args));
status = p.readInt32 (&t);
args.cmd = t;
args.ifName = strdupReadString(p);
args.ipAddr = strdupReadString(p);
args.gatewayAddr = strdupReadString(p);
if ((status != NO_ERROR) || (args.ifName == NULL)) {
CNE_LOGD ("dispatchIproute2Cmd: invalid block");
return;
}
CNE_LOGV ("dispatchIproute2Cmd: cmd=%d, ifName=%s, ipAddr=%s, gatewayAddr=%s",
args.cmd, args.ifName, args.ipAddr, args.gatewayAddr);
processCommand(pRI->pCI->commandNumber, &args, sizeof(args), pRI);
if (args.ifName != NULL) {
free(args.ifName);
}
if (args.ipAddr != NULL) {
free(args.ipAddr);
}
if (args.gatewayAddr != NULL) {
free(args.gatewayAddr);
}
return;
}
static void
dispatchRaw(Parcel &p, RequestInfo *pRI)
{
int32_t len;
status_t status;
const void *data;
status = p.readInt32(&len);
if (status != NO_ERROR){
CNE_LOGD ("dispatchRaw: invalid block");
return;
}
// The java code writes -1 for null arrays
if (((int)len) == -1) {
data = NULL;
len = 0;
}
data = p.readInplace(len);
processCommand(pRI->pCI->commandNumber, const_cast<void *>(data), len, pRI);
return;
}
static int
writeData(int fd, const void *buffer, size_t len)
{
size_t writeOffset = 0;
const uint8_t *toWrite;
toWrite = (const uint8_t *)buffer;
CNE_LOGV ("writeData: len=%d",len);
while (writeOffset < len) {
ssize_t written;
do {
written = write (fd, toWrite + writeOffset,
len - writeOffset);
} while (written < 0 && errno == EINTR);
if (written >= 0) {
writeOffset += written;
} else { // written < 0
CNE_LOGD ("writeData: unexpected error on write errno:%d", errno);
close(fd);
return -1;
}
}
return 0;
}
static int
sendResponseRaw (const void *data, size_t dataSize, int fdCommand)
{
int fd = fdCommand;
int ret;
uint32_t header;
if (fdCommand < 0) {
return -1;
}
if (dataSize > MAX_COMMAND_BYTES) {
CNE_LOGD("sendResponseRaw: packet larger than %u (%u)",
MAX_COMMAND_BYTES, (unsigned int )dataSize);
return -1;
}
pthread_mutex_lock(&s_writeMutex);
header = htonl(dataSize);
ret = writeData(fd, (void *)&header, sizeof(header));
if (ret < 0) {
return ret;
}
writeData(fd, data, dataSize);
if (ret < 0) {
pthread_mutex_unlock(&s_writeMutex);
return ret;
}
pthread_mutex_unlock(&s_writeMutex);
return 0;
}
static int
sendResponse (Parcel &p, int fd)
{
return sendResponseRaw(p.data(), p.dataSize(), fd);
}
/** response is an int* pointing to an array of ints*/
static int
responseInts(Parcel &p, void *response, size_t responselen)
{
int numInts;
if (response == NULL && responselen != 0) {
CNE_LOGD("invalid response: NULL");
return CND_E_INVALID_RESPONSE;
}
if (responselen % sizeof(int) != 0) {
CNE_LOGD("invalid response length %d expected multiple of %d\n",
(int)responselen, (int)sizeof(int));
return CND_E_INVALID_RESPONSE;
}
int *p_int = (int *) response;
numInts = responselen / sizeof(int *);
p.writeInt32 (numInts);
/* each int*/
for (int i = 0 ; i < numInts ; i++) {
p.writeInt32(p_int[i]);
}
return 0;
}
/** response is a char **, pointing to an array of char *'s */
static int responseStrings(Parcel &p, void *response, size_t responselen)
{
int numStrings;
if (response == NULL && responselen != 0) {
CNE_LOGD("invalid response: NULL");
return CND_E_INVALID_RESPONSE;
}
if (responselen % sizeof(char *) != 0) {
CNE_LOGD("invalid response length %d expected multiple of %d\n",
(int)responselen, (int)sizeof(char *));
return CND_E_INVALID_RESPONSE;
}
if (response == NULL) {
p.writeInt32 (0);
} else {
char **p_cur = (char **) response;
numStrings = responselen / sizeof(char *);
p.writeInt32 (numStrings);
/* each string*/
for (int i = 0 ; i < numStrings ; i++) {
writeStringToParcel (p, p_cur[i]);
}
}
return 0;
}
/**
* NULL strings are accepted
* FIXME currently ignores responselen
*/
static int responseString(Parcel &p, void *response, size_t responselen)
{
CNE_LOGV("responseString called");
/* one string only */
writeStringToParcel(p, (const char *)response);
return 0;
}
static int responseVoid(Parcel &p, void *response, size_t responselen)
{
return 0;
}
static int responseRaw(Parcel &p, void *response, size_t responselen)
{
if (response == NULL && responselen != 0) {
CNE_LOGD("invalid response: NULL with responselen != 0");
return CND_E_INVALID_RESPONSE;
}
// The java code reads -1 size as null byte array
if (response == NULL) {
p.writeInt32(-1);
} else {
p.writeInt32(responselen);
p.write(response, responselen);
}
return 0;
}
static int rspCompatibleNws(Parcel &p, void *response, size_t responselen)
{
if (response == NULL && responselen != 0)
{
CNE_LOGD("invalid response: NULL");
return CND_E_INVALID_RESPONSE;
}
cne_get_compatible_nws_evt_rsp_data_type *p_cur =
((cne_get_compatible_nws_evt_rsp_data_type *) response);
p.writeInt32((int)p_cur->reg_id);
p.writeInt32((int)p_cur->is_success);
p.writeInt32((int)p_cur->best_rat);
for(int index = 0; index<CNE_RAT_MAX; index++)
{
p.writeInt32((int)p_cur->rat_pref_order[index]);
}
writeStringToParcel(p,p_cur->ip_addr);
p.writeInt32((int)p_cur->fl_bw_est);
p.writeInt32((int)p_cur->rl_bw_est);
return 0;
}
static int evtMorePrefNw(Parcel &p, void *response, size_t responselen)
{
if (response == NULL && responselen != 0)
{
CNE_LOGD("invalid response: NULL");
return CND_E_INVALID_RESPONSE;
}
cne_pref_rat_avail_evt_data_type *p_cur =
((cne_pref_rat_avail_evt_data_type *) response);
p.writeInt32((int)p_cur->reg_id);
p.writeInt32((int)p_cur->rat);
writeStringToParcel(p,p_cur->ip_addr);
p.writeInt32((int)p_cur->fl_bw_est);
p.writeInt32((int)p_cur->rl_bw_est);
return 0;
}
static int eventRatChange(Parcel &p, void *response, size_t responselen)
{
if (response == NULL && responselen != 0)
{
CNE_LOGD("invalid response: NULL");
return CND_E_INVALID_RESPONSE;
}
CneRatInfoType *p_cur = ((CneRatInfoType *) response);
p.writeInt32((int)p_cur->rat);
if (p_cur->rat == CNE_RAT_WLAN)
{
writeStringToParcel (p, p_cur->wlan.ssid);
}
return 0;
}
static int
checkAndDequeueRequestInfo(struct RequestInfo *pRI)
{
int ret = 0;
if (pRI == NULL) {
return 0;
}
pthread_mutex_lock(&s_pendingRequestsMutex);
for(RequestInfo **ppCur = &s_pendingRequests
; *ppCur != NULL
; ppCur = &((*ppCur)->p_next)
) {
if (pRI == *ppCur) {
ret = 1;
*ppCur = (*ppCur)->p_next;
break;
}
}
pthread_mutex_unlock(&s_pendingRequestsMutex);
return ret;
}
static void onCommandsSocketClosed()
{
int ret;
RequestInfo *p_cur;
/* mark pending requests as "cancelled" so we dont report responses */
ret = pthread_mutex_lock(&s_pendingRequestsMutex);
assert (ret == 0);
p_cur = s_pendingRequests;
for (p_cur = s_pendingRequests
; p_cur != NULL
; p_cur = p_cur->p_next
) {
p_cur->cancelled = 1;
}
ret = pthread_mutex_unlock(&s_pendingRequestsMutex);
assert (ret == 0);
}
static void unsolicitedMessage(int unsolMessage, void *data, size_t datalen, int fd)
{
int unsolMessageIndex;
int ret;
if (s_registerCalled == 0) {
CNE_LOGD("unsolicitedMessage called before cnd_init ignored");
return;
}
Parcel p;
p.writeInt32 (UNSOLICITED_MESSAGE);
p.writeInt32 (unsolMessage);
ret = s_unsolMessages[unsolMessage]
.responseFunction(p, data, datalen);
if (ret != 0) {
// Problem with the response. Don't continue;
CNE_LOGD("unsolicitedMessage: problem with response");
return;
}
ret = sendResponse(p, fd);
return;
}
static int
processCommandBuffer(void *buffer, size_t buflen, int fd)
{
Parcel p;
status_t status;
int32_t request;
int32_t token;
RequestInfo *pRI;
int ret;
p.setData((uint8_t *) buffer, buflen);
// status checked at end
status = p.readInt32(&request);
status = p.readInt32 (&token);
if (status != NO_ERROR) {
CNE_LOGD("processCommandBuffer: invalid request block");
return -1;
}
if (request < 1 || request >= (int32_t)NUM_ELEMS(s_commands)) {
CNE_LOGD("processCommandBuffer: unsupported request code %d token %d",
request, token);
return -1;
}
pRI = (RequestInfo *)calloc(1, sizeof(RequestInfo));
if (pRI == NULL) {
CNE_LOGW("processCommandBuffer: calloc failed");
return -1;
}
pRI->token = token;
pRI->fd = fd;
pRI->pCI = &(s_commands[request]);
ret = pthread_mutex_lock(&s_pendingRequestsMutex);
assert (ret == 0);
pRI->p_next = s_pendingRequests;
s_pendingRequests = pRI;
ret = pthread_mutex_unlock(&s_pendingRequestsMutex);
assert (ret == 0);
pRI->pCI->dispatchFunction(p, pRI);
return 0;
}
static void processCommandsCallback(int fd, void *param)
{
RecordStream *p_rs;
void *p_record;
size_t recordlen;
int ret;
p_rs = (RecordStream *)param;
for (;;) {
/* loop until EAGAIN/EINTR, end of stream, or other error */
ret = record_stream_get_next(p_rs, &p_record, &recordlen);
CNE_LOGV ("processCommandsCallback: len=%d, ret=%d", recordlen, ret);
if (ret == 0 && p_record == NULL) {
CNE_LOGV ("processCommandsCallback: end of stream");
break;
} else if (ret < 0) {
break;
} else if (ret == 0) { /* && p_record != NULL */
processCommandBuffer(p_record, recordlen, fd);
}
}
if (ret == 0 || !(errno == EAGAIN || errno == EINTR)) {
/* fatal error or end-of-stream */
if (ret != 0) {
CNE_LOGD("error on reading command socket errno:%d\n", errno);
} else {
CNE_LOGD("EOS. Closing command socket.");
}
close(s_fdCommand);
s_fdCommand = -1;
command_index = 0;
record_stream_free(p_rs);
/* start listening for new connections again */
cnd_event_add(&s_listen_event);
onCommandsSocketClosed();
}
}
static void listenCallback (int fd, void *param)
{
int ret;
int err;
int is_cnm_svc_socket;
RecordStream *p_rs;
int i;
char tmpBuf[10];
int32_t pid, pid2, pid3, pid4;
struct sockaddr_un peeraddr;
socklen_t socklen = sizeof (peeraddr);
struct ucred creds;
socklen_t szCreds = sizeof(creds);
struct passwd *pwd = NULL;
assert (s_fdCommand < 0);
assert (fd == s_fdListen);
s_fdCommand = accept(s_fdListen, (sockaddr *) &peeraddr, &socklen);
if (s_fdCommand < 0 ) {
CNE_LOGD("Error on accept() errno:%d", errno);
/* start listening for new connections again */
cnd_event_add(&s_listen_event);
return;
}
errno = 0;
err = getsockopt(s_fdCommand, SOL_SOCKET, SO_PEERCRED, &creds, &szCreds);
cnmSvcFd = s_fdCommand; // save command descriptor to be used for communication
ret = fcntl(s_fdCommand, F_SETFL, O_NONBLOCK);
if (ret < 0) {
CNE_LOGD("Error setting O_NONBLOCK errno = %d", errno);
}
CNE_LOGV("listenCallback: accept new connection, fd=%d", s_fdCommand);
p_rs = record_stream_new(s_fdCommand, MAX_COMMAND_BYTES);
// note: persistent = 1, not removed from table
if (command_index >= MAX_FD_EVENTS)
{
CNE_LOGD ("Error: exceeding number of supported connection");
return;
}
cnd_event_set (&s_commands_event[command_index], s_fdCommand, 1,
processCommandsCallback, p_rs);
cnd_event_add (&s_commands_event[command_index]);
command_index++;
return;
}
static void *
eventLoop(void *param)
{
int ret;
int filedes[2];
CNE_LOGV ("eventLoop: s_started=%d", s_started);
pthread_mutex_lock(&s_startupMutex);
s_started = 1;
pthread_cond_broadcast(&s_startupCond);
pthread_mutex_unlock(&s_startupMutex);
cnd_event_loop();
return NULL;
}
extern "C" void
cnd_startEventLoop(void)
{
int ret;
pthread_attr_t attr;
/* spin up eventLoop thread and wait for it to get started */
s_started = 0;
pthread_mutex_lock(&s_startupMutex);
pthread_attr_init (&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
ret = pthread_create(&s_tid_dispatch, &attr, eventLoop, NULL);
while (s_started == 0) {
pthread_cond_wait(&s_startupCond, &s_startupMutex);
}
pthread_mutex_unlock(&s_startupMutex);
if (ret < 0) {
CNE_LOGD("Failed to create dispatch thread errno:%d", errno);
return;
}
}
extern "C" void
cnd_init (void)
{
int ret;
if (s_registerCalled > 0) {
CNE_LOGD("cnd_init has been called more than once. "
"Subsequent call ignored");
return;
}
s_registerCalled = 1;
cneServiceEnabled = cnd_event_init();
if(cne_regMessageCb != NULL)
{
cne_regMessageCb(cnd_sendUnsolicitedMsg);
}
else
{
CNE_LOGD("cne_regMessageCb is NULL");
}
s_fdListen = android_get_control_socket(SOCKET_NAME_CND);
if (s_fdListen < 0) {
CNE_LOGD("Failed to get socket '" SOCKET_NAME_CND "'");
exit(-1);
}
ret = listen(s_fdListen, 4);
if (ret < 0) {
CNE_LOGD("Failed to listen on control socket '%d': %s",
s_fdListen, strerror(errno));
exit(-1);
}
// persistent to accept multiple connections at same time
cnd_event_set (&s_listen_event, s_fdListen, 1, listenCallback, NULL);
cnd_event_add (&s_listen_event);
}
static void
cnd_commandComplete(CND_Token t, CND_Errno e, void *response, size_t responselen)
{
RequestInfo *pRI;
int ret;
size_t errorOffset;
pRI = (RequestInfo *)t;
if (!checkAndDequeueRequestInfo(pRI)) {
CNE_LOGD ("cnd_commandComplete: invalid Token");
return;
}
if (pRI->cancelled == 0) {
Parcel p;
p.writeInt32 (SOLICITED_RESPONSE);
p.writeInt32 (pRI->token);
errorOffset = p.dataPosition();
p.writeInt32 (e);
if (e == CND_E_SUCCESS) {
/* process response on success */
ret = pRI->pCI->responseFunction(p, response, responselen);
/* if an error occurred, rewind and mark it */
if (ret != 0) {
p.setDataPosition(errorOffset);
p.writeInt32 (ret);
}
} else {
CNE_LOGD ("cnd_commandComplete: Error");
}
if (pRI->fd < 0) {
CNE_LOGD ("cnd_commandComplete: Command channel closed");
}
sendResponse(p, pRI->fd);
}
}
} /* namespace android */