blob: 18536151d905c24c4df9424c9653b303d6d1c6d6 [file] [log] [blame]
/******************************************************************************
* * atj-server.cpp - launch a server thread to handler the AT requests from uplayer
*
* *(C) Copyright 2013 Marvell International Ltd.
* * All Rights Reserved
* ******************************************************************************/
#define LOG_TAG "ATSERVER"
#include <utils/Log.h>
#include <ctype.h>
#include <errno.h>
#include <pthread.h>
#include <binder/Parcel.h>
#include <cutils/jstring.h>
#include <cutils/sockets.h>
#include <net/if.h>
#include <cutils/properties.h>
#include <netutils/ifc.h>
#include <telephony/ril.h>
#include <atchannel/at_command_type.h>
#include <atchannel/atchannel.h>
#include <atchannel/work-queue.h>
#include <marvell-ril/service_group.h>
#include <atserver/atil_response.h>
#include <atserver/at-server.h>
#define AT_COMMAND_TIMEOUT (45 * 1000)
using android::Parcel;
using android::NO_ERROR;
using android::Mutex;
static int processSetMbmsCnf(const char *s)
{
//bring up embms net interface if service enable succeed
int ret = -1;
char ifname[PROPERTY_VALUE_MAX] = "embms0";
if(strstr(s, "\"MBMS_PREFERENCE\",1"))
{
ifc_init();
if (ifc_up(ifname))
{
RLOGE("%s: fail to bring up %s for %s!\n", __FUNCTION__, ifname, strerror(errno));
ifc_close();
return AT_RESPONSE_ERROR_GENERIC;
}
in_addr_t addr;
char ip6_addr[INET6_ADDRSTRLEN] = "2001::AAAA:FFFF:FFFF:0000";
inet_pton(AF_INET, "127.255.255.255", &addr);
ifc_set_addr(ifname, addr);
ifc_add_address(ifname, ip6_addr, 64);
ifc_close();
if( ret < 0 )
{
RLOGE("%s: CI_DAT_MBMS_CMD_ENABLE handling failed in ioctl!\n", __FUNCTION__);
return AT_RESPONSE_ERROR_GENERIC;
}
}
else if(strstr(s, "\"MBMS_PREFERENCE\",0"))
{
//bring down embms net interface
ifc_init();
ifc_set_addr(ifname, 0);
ifc_down(ifname);
ifc_close();
}
return 0;
}
struct AtInfo {
const char *cmd;
int(*handler)(const char * s);
};
static const struct AtInfo s_AtHandler [] =
{
{"\"MBMS_PREFERENCE\"", processSetMbmsCnf},
};
static int processSpecialAT(const char *s)
{
size_t k = 0;
int ret = 0;
for ( k = 0; k < sizeof(s_AtHandler) / sizeof(s_AtHandler[0]); k++)
{
if(strstr(s, s_AtHandler[k].cmd))
{
RLOGD("processSpecialAT: handler for AT command contains \"%s\" found", s);
ret = s_AtHandler[k].handler(s);
break;
}
}
return ret;
}
int BaseClient::writeLocked(const void * buffer, size_t len)
{
size_t offset = 0;
const uint8_t * tmpWrite;
tmpWrite = (const uint8_t *)buffer;
if (fd < 0) {
RLOGE ("writeUntil: invalid fd: %d",fd);
return -1;
}
while (offset < len) {
ssize_t written;
do {
written = send(fd, tmpWrite + offset, len - offset, MSG_NOSIGNAL);
} while (written < 0 && errno == EINTR);
if (written >= 0) {
offset += written;
} else {
RLOGE ("writeUntil: unexpected error on write %d errno:%s",fd, strerror(errno));
return -1;
}
}
return 0;
};
ATServer::ATServer(ServiceProvider* service, const char *socketName, ClientCreatFunction create){
mService = service;
mSocketName = socketName;
mSock = -1;
mClientCreat= create;
mClientToken = 1;
}
int ATServer::startServer() {
mSock = android_get_control_socket(mSocketName);
if (mSock < 0)
{
RLOGE("Failed to get socket %s", mSocketName);
return -1;
}
int ret = listen(mSock, 4);
if (ret < 0)
{
RLOGE("Failed to listen on control socket '%d': %s",
ret, strerror(errno));
return -1;
}
return 0;
}
void ATServer::broadCastUnsolicited(const char *s) {
Mutex::Autolock lock(mClientsLock);
for (auto client : mClients) {
if(client->getSocket()> 0 ) {
client->handleUnsolicited(s);
}
}
}
int ATServer::addSocketsToFdset(fd_set& read_fds) {
int max = mSock;
FD_SET(mSock, &read_fds);
mClientsLock.lock();
for (auto client : mClients) {
int fd = client->getSocket();
FD_SET(fd, &read_fds);
if (fd > max)
max = fd;
}
mClientsLock.unlock();
return max;
}
void ATServer::handleReadEvent(fd_set& read_fds) {
if (FD_ISSET(mSock, &read_fds)) {
struct sockaddr addr;
socklen_t alen;
int c;
do {
alen = sizeof(addr);
c = accept(mSock, &addr, &alen);
RLOGD("ATServer(%s): got %d from accept", mSocketName, c);
} while (c < 0 && errno == EINTR);
if (c < 0) {
RLOGE("ATServer(%s): accept failed (%s)", mSocketName, strerror(errno));
} else {
if (fcntl(c, F_SETFL, O_NONBLOCK) < 0) {
RLOGE ("Error setting O_NONBLOCK errno:%d", errno);
}
mClientsLock.lock();
mClients.push_back(mClientCreat(this, c, mClientToken++));
mClientsLock.unlock();
}
}
/* Add all active clients to the pending list first */
ClientCollection pendingList;
mClientsLock.lock();
for (auto client : mClients) {
int fd = client->getSocket();
if (FD_ISSET(fd, &read_fds)) {
pendingList.push_back(client);
}
}
mClientsLock.unlock();
/* Process the pending list, since it is owned by the thread,
* there is no need to lock it */
while (!pendingList.empty()) {
/* Pop the first item from the list */
ClientCollection::iterator it = pendingList.begin();
BaseClient* c = *it;
pendingList.erase(it);
/* Process it, if false is returned and our sockets are
* connection-based, remove and destroy it */
if (!c->onDataAvailable()) {
/* Remove the client from our array */
RLOGD("ATServer(%s): zap %d", mSocketName, c->getSocket());
mClientsLock.lock();
for (it = mClients.begin(); it != mClients.end(); ++it) {
if (*it == c) {
mClients.erase(it);
break;
}
}
mClientsLock.unlock();
/* Remove our reference to the client */
c->decRef();
}
}
}
static char * strdupReadString(Parcel &p)
{
size_t stringlen;
const char16_t *s16;
s16 = p.readString16Inplace(&stringlen);
return strndup16to8(s16, stringlen);
}
int FormattedCommandClient::sendResponse(const void *data, size_t size) {
uint32_t header = htonl(size);
Mutex::Autolock lock(mWriteMutex);
int ret = writeLocked((void *)&header, sizeof(header));
if(ret < 0)
return ret;
return writeLocked(data,size);
}
void FormattedCommandClient::runCommand(BaseClient::Command* c)
{
Parcel pr;
ATSrvCmd *atSrvCmd = (ATSrvCmd*)c;
pr.writeInt32(AT_RESPONSE_TYPE_SOLICITED);
pr.writeInt32(atSrvCmd->token);
ATResponse *p_response = NULL;
ATServer::ServiceProvider *s = mServer->getService();
channel_struct* chan = s->getThreadChannel();
int error = atchan_send_command_full(chan, atSrvCmd->atCmd, (ATCommandType)atSrvCmd->atCmdType, atSrvCmd->atPrefix,NULL,AT_COMMAND_TIMEOUT,&p_response);
RLOGD("processATSrvCmd: %s, error:%d", atSrvCmd->atCmd, error);
if( error < 0 || p_response->success ==0) {
pr.writeInt32(AT_RESPONSE_ERROR_GENERIC);
} else {
pr.writeInt32(AT_RESPONSE_ERROR_NONE);
size_t num_pos = pr.dataPosition();
pr.writeInt32(0);
int num = 0;
for (ATLine *p_cur = p_response->p_intermediates; p_cur != NULL; p_cur = p_cur->p_next) {
pr.writeString16(android::String16(p_cur->line));
num++;
}
if (num > 0) {
pr.setDataPosition(num_pos);
pr.writeInt32(num);
}
}
at_response_free(p_response);
error = sendResponse(pr.data(), pr.dataSize());
delete atSrvCmd;
}
bool FormattedCommandClient::onDataAvailable()
{
void *p_record;
size_t recordlen;
int ret;
for (;;) {
/* loop until EAGAIN/EINTR, end of stream, or other error */
ret = record_stream_get_next(reader, &p_record, &recordlen);
if (ret == 0 && p_record == NULL) {
/* end-of-stream */
break;
} else if (ret < 0) {
break;
} else if (ret == 0) { /* && p_record != NULL */
processCommandBuffer(p_record, recordlen);
}
}
if (ret == 0 || !(errno == EAGAIN || errno == EINTR)) {
/* fatal error or end-of-stream */
if (ret != 0) {
RLOGE("error on reading command socket errno:%d\n", errno);
} else {
RLOGW("EOS. Closing command socket.");
}
return false;
}
return true;
}
void FormattedCommandClient::processCommandBuffer(void *buffer, size_t buflen) {
Parcel p;
int status, token, atCmdType;
int srvType;
p.setData((uint8_t *) buffer, buflen);
status = p.readInt32 (&token);
if (status != NO_ERROR)
{
RLOGE("invalid request block");
return;
}
status = p.readInt32 (&atCmdType);
if (status != NO_ERROR)
{
RLOGE("invalid request block");
return;
}
status = p.readInt32 (&srvType);
if (status != NO_ERROR)
{
RLOGE("invalid request block");
return;
}
char* cmd = strdupReadString(p);
char* prefix = strdupReadString(p);
RLOGD("Client %d processCommandBuffer: process req No.%d, type=%d, srvType=%d, cmd=%s, prefix=%s",
mToken, token, atCmdType, srvType, cmd, prefix);
ATSrvCmd *atCmd = new ATSrvCmd(this, token, atCmdType,cmd, prefix );
enque(mServer->getService()->getWorkQueue(srvType),
BaseClient::processCommand,
atCmd);
}
void FormattedCommandClient::handleUnsolicited(const char *s)
{
RLOGV("FormattedCommandClient::handleUnsolicited: to client %d", mToken);
Parcel pr;
pr.writeInt32(AT_RESPONSE_TYPE_UNSOLICITED);
pr.writeInt32(0);
pr.writeInt32(AT_RESPONSE_ERROR_NONE);
pr.writeString16(android::String16(s));
sendResponse(pr.data(), pr.dataSize());
}
void RawCommandClient::runCommand(BaseClient::Command* c)
{
Command* command = (Command*)c;
const char* s = command->cmd;
size_t len = strlen(s);
channel_struct* chan;
RLOGV("RawCommandClient[%d]::processLine: %s", mToken, s);
char tmp[MAX_COMMAND_BYTES];
int rlen = 0;
int ret = -1;
ATResponse *p_response = NULL;
int err;
if (len <2 || toupper(s[0]) != 'A' || toupper(s[1]) != 'T') {
RLOGD("RawCommandClient[%d]::processLine: INVALID COMMAND", mToken);
goto error;
}
RLOGD("RawCommandClient[%d]::processLine: cmd:%s", mToken, s);
chan = mServer->getService()->getThreadChannel();
err = atchan_send_command_multiline_timeout(chan, s, "", &p_response, AT_COMMAND_TIMEOUT);
if (!p_response || !p_response->finalResponse) {
RLOGD("RawCommandClient[%d]::processLine: EMPTY finalResponse", mToken);
goto error;
}
// we don't return error though it fail to operate network imterface in kernel
ret = processSpecialAT(s);
if ( ret != 0 )
RLOGD("RawCommandClient[%d]::processLine: processSpeicalAT return error %d", mToken, ret);
for (ATLine *p_cur = p_response->p_intermediates; p_cur; p_cur = p_cur->p_next) {
rlen += snprintf(tmp + rlen, sizeof(tmp) - rlen, "%s\n", p_cur->line);
}
rlen += snprintf(tmp + rlen, sizeof(tmp) - rlen, "%s\n", p_response->finalResponse);
goto exit;
error:
rlen = snprintf(tmp, sizeof(tmp), "ERROR\n");
exit:
sendResponse(tmp, rlen);
delete command;
at_response_free(p_response);
}
bool RawCommandClient::onDataAvailable()
{
int ret;
for (;;) {
/* loop until EAGAIN/EINTR, end of stream, or other error */
ret = read(fd, mEnd, mBuf + MAX_COMMAND_BYTES - mEnd);
RLOGV("RawCommandClient[%d]::onDataAvailable read ret: %d", mToken, ret);
if (ret == 0) {
/* end-of-stream */
break;
} else if (ret < 0) {
break;
} else {
mEnd += ret;
char *s = mBuf;
for(;;) {
//skip \r\n at head
while ((*s == '\r' || *s == '\n') && s < mEnd)
s++;
char *p =s;
//find the line end
while ((*p != '\r' && *p != '\n') && p < mEnd)
p++;
if (p > s && p < mEnd && (*p == '\r' || *p == '\n')) {
*p = '\0';
enque(mServer->getService()->getWorkQueue(SERVICE_EMBMS),
BaseClient::processCommand,
new Command(this, s));
s = p+1;
} else {
break;
}
}
int remain = mEnd - s;
if (s != mBuf) {
memmove(mBuf, s, remain);
mEnd = mBuf+ remain;
}
}
}
if (ret == 0 || !(errno == EAGAIN || errno == EINTR)) {
/* fatal error or end-of-stream */
if (ret != 0) {
RLOGE("error on reading command socket errno:%d\n", errno);
} else {
RLOGW("EOS. Closing command socket.");
}
return false;
}
return true;
}
void RawCommandClient::handleUnsolicited(const char * s) {
RLOGV("RawCommandClient::handleUnsolicited: to client %d", mToken);
char tmp[MAX_COMMAND_BYTES];
int len = snprintf(tmp, sizeof(tmp), "%s\n", s);
sendResponse(tmp, len);
}
void runAtServers(const ServerCollection& servers) {
while (true) {
fd_set read_fds;
int rc = 0;
int max = -1;
FD_ZERO(&read_fds);
for (auto server : servers) {
int m = server->addSocketsToFdset(read_fds);
if (m > max)
max = m;
}
RLOGV("ATServer select max=%d", max);
if ((rc = select(max + 1, &read_fds, NULL, NULL, NULL)) < 0) {
if (errno == EINTR)
continue;
RLOGE("select failed (%s) max=%d", strerror(errno), max);
sleep(1);
continue;
} else if (!rc)
continue;
for (auto server : servers) {
server->handleReadEvent(read_fds);
}
}
}