| /****************************************************************************** |
| * * 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); |
| } |
| } |
| } |
| |