diff --git a/ril/libril/Android.mk b/ril/libril/Android.mk index 71278b03..a310d73b 100644 --- a/ril/libril/Android.mk +++ b/ril/libril/Android.mk @@ -7,7 +7,9 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ ril.cpp \ - ril_event.cpp + ril_event.cpp\ + RilSocket.cpp \ + RilSapSocket.cpp \ LOCAL_SHARED_LIBRARIES := \ liblog \ @@ -15,7 +17,10 @@ LOCAL_SHARED_LIBRARIES := \ libbinder \ libcutils \ libhardware_legacy \ - librilutils + librilutils \ + +LOCAL_STATIC_LIBRARIES := \ + libnanopb-c-2.8.0-enable_malloc \ ifneq ($(filter xmm6262 xmm6360,$(BOARD_MODEM_TYPE)),) LOCAL_CFLAGS := -DMODEM_TYPE_XMM6262 @@ -30,10 +35,15 @@ ifeq ($(BOARD_MODEM_TYPE),m7450) LOCAL_CFLAGS := -DMODEM_TYPE_M7450 endif +LOCAL_C_INCLUDES += $(TARGET_OUT_HEADER)/librilutils +LOCAL_C_INCLUDES += external/nanopb-c + LOCAL_MODULE:= libril -include $(BUILD_SHARED_LIBRARY) +LOCAL_COPY_HEADERS_TO := libril +LOCAL_COPY_HEADERS := ril_ex.h +include $(BUILD_SHARED_LIBRARY) # For RdoServD which needs a static library # ========================================= diff --git a/ril/libril/RilSapSocket.cpp b/ril/libril/RilSapSocket.cpp new file mode 100644 index 00000000..92cc98c8 --- /dev/null +++ b/ril/libril/RilSapSocket.cpp @@ -0,0 +1,457 @@ +/* +* Copyright (C) 2014 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. +*/ + +#define __STDC_LIMIT_MACROS +#include +#define RIL_SHLIB +#include "telephony/ril.h" +#include "RilSapSocket.h" +#include "pb_decode.h" +#include "pb_encode.h" +#define LOG_TAG "RIL_UIM_SOCKET" +#include +#include + +RilSapSocket::RilSapSocketList *head; + +void ril_sap_on_request_complete ( + RIL_Token t, RIL_Errno e, + void *response, size_t responselen +); + +void ril_sap_on_unsolicited_response ( + int unsolResponse, const void *data, + size_t datalen +); +extern "C" void +RIL_requestTimedCallback (RIL_TimedCallback callback, void *param, + const struct timeval *relativeTime); + +struct RIL_Env RilSapSocket::uimRilEnv = { + .OnRequestComplete = RilSapSocket::sOnRequestComplete, + .OnUnsolicitedResponse = RilSapSocket::sOnUnsolicitedResponse, + .RequestTimedCallback = RIL_requestTimedCallback +}; + +void RilSapSocket::sOnRequestComplete (RIL_Token t, + RIL_Errno e, + void *response, + size_t responselen) { + RilSapSocket *sap_socket; + SapSocketRequest *request = (SapSocketRequest*) t; + + RLOGD("Socket id:%d", request->socketId); + + sap_socket = getSocketById(request->socketId); + + if (sap_socket) { + sap_socket->onRequestComplete(t,e,response,responselen); + } else { + RLOGE("Invalid socket id"); + free(request->curr); + free(request); + } +} + +#if defined(ANDROID_MULTI_SIM) +void RilSapSocket::sOnUnsolicitedResponse(int unsolResponse, + const void *data, + size_t datalen, + RIL_SOCKET_ID socketId) { + RilSapSocket *sap_socket = getSocketById(socketId); + if (sap_socket) { + sap_socket->onUnsolicitedResponse(unsolResponse, (void *)data, datalen); + } +} +#else +void RilSapSocket::sOnUnsolicitedResponse(int unsolResponse, + const void *data, + size_t datalen) { + RilSapSocket *sap_socket = getSocketById(RIL_SOCKET_1); + sap_socket->onUnsolicitedResponse(unsolResponse, (void *)data, datalen); +} +#endif + +void RilSapSocket::printList() { + RilSapSocketList *current = head; + RLOGD("Printing socket list"); + while(NULL != current) { + RLOGD("SocketName:%s",current->socket->name); + RLOGD("Socket id:%d",current->socket->id); + current = current->next; + } +} + +RilSapSocket *RilSapSocket::getSocketById(RIL_SOCKET_ID socketId) { + RilSapSocket *sap_socket; + RilSapSocketList *current = head; + + RLOGD("Entered getSocketById"); + printList(); + + while(NULL != current) { + if(socketId == current->socket->id) { + sap_socket = current->socket; + return sap_socket; + } + current = current->next; + } + return NULL; +} + +void RilSapSocket::initSapSocket(const char *socketName, + RIL_RadioFunctions *uimFuncs) { + + if (strcmp(socketName, "sap_uim_socket1") == 0) { + if(!SocketExists(socketName)) { + addSocketToList(socketName, RIL_SOCKET_1, uimFuncs); + } + } + +#if (SIM_COUNT >= 2) + if (strcmp(socketName, "sap_uim_socket2") == 0) { + if(!SocketExists(socketName)) { + addSocketToList(socketName, RIL_SOCKET_2, uimFuncs); + } + } +#endif + +#if (SIM_COUNT >= 3) + if (strcmp(socketName, "sap_uim_socket3") == 0) { + if(!SocketExists(socketName)) { + addSocketToList(socketName, RIL_SOCKET_3, uimFuncs); + } + } +#endif + +#if (SIM_COUNT >= 4) + if (strcmp(socketName, "sap_uim_socket4") == 0) { + if(!SocketExists(socketName)) { + addSocketToList(socketName, RIL_SOCKET_4, uimFuncs); + } + } +#endif +} + +void RilSapSocket::addSocketToList(const char *socketName, RIL_SOCKET_ID socketid, + RIL_RadioFunctions *uimFuncs) { + RilSapSocket* socket = NULL; + RilSapSocketList* listItem = (RilSapSocketList*)malloc(sizeof(RilSapSocketList)); + RilSapSocketList *current; + + if(!SocketExists(socketName)) { + socket = new RilSapSocket(socketName, socketid, uimFuncs); + listItem->socket = socket; + listItem->next = NULL; + + RLOGD("Adding socket with id: %d", socket->id); + + if(NULL == head) { + head = listItem; + head->next = NULL; + } + else { + current = head; + while(NULL != current->next) { + current = current->next; + } + current->next = listItem; + } + socket->socketInit(); + } +} + +bool RilSapSocket::SocketExists(const char *socketName) { + RilSapSocketList* current = head; + + while(NULL != current) { + if(strcmp(current->socket->name, socketName) == 0) { + return true; + } + current = current->next; + } + return false; +} + +void* RilSapSocket::processRequestsLoop(void) { + SapSocketRequest *req = (SapSocketRequest*)malloc(sizeof(SapSocketRequest)); + RLOGI("UIM_SOCKET:Request loop started"); + + while(true) { + req = dispatchQueue.dequeue(); + + RLOGI("New request from the dispatch Queue"); + + if (req != NULL) { + processRequest(req->curr); + free(req); + } else { + RLOGE("Fetched null buffer from queue!"); + } + } + return NULL; +} + +RilSapSocket::RilSapSocket(const char *socketName, + RIL_SOCKET_ID socketId, + RIL_RadioFunctions *inputUimFuncs): + RilSocket(socketName, socketId) { + if (inputUimFuncs) { + uimFuncs = inputUimFuncs; + } +} + +int RilSapSocket::processRequest(MsgHeader *request) { + dispatchRequest(request); + return 0; +} + +#define BYTES_PER_LINE 16 + +#define NIBBLE_TO_HEX(n) ({ \ + uint8_t __n = (uint8_t) n & 0x0f; \ + __nibble >= 10 ? 'A' + __n - 10: '0' + __n; \ +}) + +#define HEX_HIGH(b) ({ \ + uint8_t __b = (uint8_t) b; \ + uint8_t __nibble = (__b >> 4) & 0x0f; \ + NIBBLE_TO_HEX(__nibble); \ +}) + +#define HEX_LOW(b) ({ \ + uint8_t __b = (uint8_t) b; \ + uint8_t __nibble = __b & 0x0f; \ + NIBBLE_TO_HEX(__nibble); \ +}) + +void log_hex(const char *who, const uint8_t *buffer, int length) { + char out[80]; + int source = 0; + int dest = 0; + int dest_len = sizeof(out); + int per_line = 0; + + do { + dest += sprintf(out, "%8.8s [%8.8x] ", who, source); + for(; source < length && dest_len - dest > 3 && per_line < BYTES_PER_LINE; source++, + per_line ++) { + out[dest++] = HEX_HIGH(buffer[source]); + out[dest++] = HEX_LOW(buffer[source]); + out[dest++] = ' '; + } + if (dest < dest_len && (per_line == BYTES_PER_LINE || source >= length)) { + out[dest++] = 0; + per_line = 0; + dest = 0; + RLOGD("%s\n", out); + } + } while(source < length && dest < dest_len); +} + +void RilSapSocket::dispatchRequest(MsgHeader *req) { + SapSocketRequest* currRequest=(SapSocketRequest*)malloc(sizeof(SapSocketRequest)); + currRequest->token = req->token; + currRequest->curr = req; + currRequest->p_next = NULL; + currRequest->socketId = id; + + pendingResponseQueue.enqueue(currRequest); + + if (uimFuncs) { + RLOGI("[%d] > SAP REQUEST type: %d. id: %d. error: %d", + req->token, + req->type, + req->id, + req->error ); + +#if defined(ANDROID_MULTI_SIM) + uimFuncs->onRequest(req->id, req->payload->bytes, req->payload->size, currRequest, id); +#else + uimFuncs->onRequest(req->id, req->payload->bytes, req->payload->size, currRequest); +#endif + } +} + +void RilSapSocket::onRequestComplete(RIL_Token t, RIL_Errno e, void *response, + size_t response_len) { + SapSocketRequest* request= (SapSocketRequest*)t; + MsgHeader *hdr = request->curr; + pb_bytes_array_t *payload = (pb_bytes_array_t *) + calloc(1,sizeof(pb_bytes_array_t) + response_len); + + if (hdr && payload) { + memcpy(payload->bytes, response, response_len); + payload->size = response_len; + hdr->payload = payload; + hdr->type = MsgType_RESPONSE; + hdr->error = (Error) e; + + RLOGE("Token:%d, MessageId:%d", hdr->token, hdr->id); + + if(!pendingResponseQueue.checkAndDequeue(hdr->id, hdr->token)) { + RLOGE("Token:%d, MessageId:%d", hdr->token, hdr->id); + RLOGE ("RilSapSocket::onRequestComplete: invalid Token or Message Id"); + return; + } + + sendResponse(hdr); + free(hdr); + } +} + +void RilSapSocket::sendResponse(MsgHeader* hdr) { + size_t encoded_size = 0; + uint32_t written_size; + size_t buffer_size = 0; + pb_ostream_t ostream; + bool success = false; + + pthread_mutex_lock(&write_lock); + + if ((success = pb_get_encoded_size(&encoded_size, MsgHeader_fields, + hdr)) && encoded_size <= INT32_MAX && commandFd != -1) { + buffer_size = encoded_size + sizeof(uint32_t); + uint8_t buffer[buffer_size]; + written_size = htonl((uint32_t) encoded_size); + ostream = pb_ostream_from_buffer(buffer, buffer_size); + pb_write(&ostream, (uint8_t *)&written_size, sizeof(written_size)); + success = pb_encode(&ostream, MsgHeader_fields, hdr); + + if (success) { + RLOGD("Size: %d (0x%x) Size as written: 0x%x", encoded_size, encoded_size, + written_size); + log_hex("onRequestComplete", &buffer[sizeof(written_size)], encoded_size); + RLOGI("[%d] < SAP RESPONSE type: %d. id: %d. error: %d", + hdr->token, hdr->type, hdr->id,hdr->error ); + + if ( 0 != blockingWrite_helper(commandFd, buffer, buffer_size)) { + RLOGE("Error %d while writing to fd", errno); + } else { + RLOGD("Write successful"); + } + } else { + RLOGE("Error while encoding response of type %d id %d buffer_size: %d: %s.", + hdr->type, hdr->id, buffer_size, PB_GET_ERROR(&ostream)); + } + } else { + RLOGE("Not sending response type %d: encoded_size: %u. commandFd: %d. encoded size result: %d", + hdr->type, encoded_size, commandFd, success); + } + + pthread_mutex_unlock(&write_lock); +} + +void RilSapSocket::onUnsolicitedResponse(int unsolResponse, void *data, size_t datalen) { + MsgHeader *hdr = new MsgHeader; + pb_bytes_array_t *payload = (pb_bytes_array_t *) + calloc(1, sizeof(pb_bytes_array_t) + datalen); + if (hdr && payload) { + memcpy(payload->bytes, data, datalen); + payload->size = datalen; + hdr->payload = payload; + hdr->type = MsgType_UNSOL_RESPONSE; + hdr->id = (MsgId)unsolResponse; + hdr->error = Error_RIL_E_SUCCESS; + sendResponse(hdr); + delete hdr; + } +} + +void RilSapSocket::pushRecord(void *p_record, size_t recordlen) { + int ret; + SapSocketRequest *recv = (SapSocketRequest*)malloc(sizeof(SapSocketRequest)); + MsgHeader *reqHeader; + pb_istream_t stream; + + stream = pb_istream_from_buffer((uint8_t *)p_record, recordlen); + reqHeader = (MsgHeader *)malloc(sizeof (MsgHeader)); + memset(reqHeader, 0, sizeof(MsgHeader)); + + log_hex("BtSapTest-Payload", (const uint8_t*)p_record, recordlen); + + if (!pb_decode(&stream, MsgHeader_fields, reqHeader) ) { + RLOGE("Error decoding protobuf buffer : %s", PB_GET_ERROR(&stream)); + } else { + recv->token = reqHeader->token; + recv->curr = reqHeader; + recv->socketId = id; + + dispatchQueue.enqueue(recv); + } +} + +void RilSapSocket::sendDisconnect() { + MsgHeader *hdr = new MsgHeader; + pb_bytes_array_t *payload ; + size_t encoded_size = 0; + uint32_t written_size; + size_t buffer_size = 0; + pb_ostream_t ostream; + bool success = false; + ssize_t written_bytes; + + RIL_SIM_SAP_DISCONNECT_REQ disconnectReq; + + if ((success = pb_get_encoded_size(&encoded_size, RIL_SIM_SAP_DISCONNECT_REQ_fields, + &disconnectReq)) && encoded_size <= INT32_MAX) { + buffer_size = encoded_size + sizeof(uint32_t); + uint8_t buffer[buffer_size]; + written_size = htonl((uint32_t) encoded_size); + ostream = pb_ostream_from_buffer(buffer, buffer_size); + pb_write(&ostream, (uint8_t *)&written_size, sizeof(written_size)); + success = pb_encode(&ostream, RIL_SIM_SAP_DISCONNECT_REQ_fields, buffer); + + if(success) { + pb_bytes_array_t *payload = (pb_bytes_array_t *) + calloc(1,sizeof(pb_bytes_array_t) + written_size); + + memcpy(payload->bytes, buffer, written_size); + payload->size = written_size; + hdr->payload = payload; + hdr->type = MsgType_REQUEST; + hdr->id = MsgId_RIL_SIM_SAP_DISCONNECT; + hdr->error = Error_RIL_E_SUCCESS; + dispatchDisconnect(hdr); + } + else { + RLOGE("Encode failed in send disconnect!"); + delete hdr; + free(payload); + } + } +} + +void RilSapSocket::dispatchDisconnect(MsgHeader *req) { + SapSocketRequest* currRequest=(SapSocketRequest*)malloc(sizeof(SapSocketRequest)); + currRequest->token = -1; + currRequest->curr = req; + currRequest->p_next = NULL; + currRequest->socketId = (RIL_SOCKET_ID)99; + + RLOGD("Sending disconnect on command close!"); + +#if defined(ANDROID_MULTI_SIM) + uimFuncs->onRequest(req->id, req->payload->bytes, req->payload->size, currRequest, id); +#else + uimFuncs->onRequest(req->id, req->payload->bytes, req->payload->size, currRequest); +#endif +} + +void RilSapSocket::onCommandsSocketClosed() { + sendDisconnect(); + RLOGE("Socket command closed"); +} diff --git a/ril/libril/RilSapSocket.h b/ril/libril/RilSapSocket.h new file mode 100644 index 00000000..4261f934 --- /dev/null +++ b/ril/libril/RilSapSocket.h @@ -0,0 +1,262 @@ +/* +* Copyright (C) 2014 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. +*/ + +#ifndef RIL_UIM_SOCKET_H_INCLUDED +#define RIL_UIM_SOCKET_H_INCLUDED +#define RIL_SHLIB +#include "telephony/ril.h" +#include "RilSocket.h" +#include + +/** + * RilSapSocket is a derived class, derived from the RilSocket abstract + * class, representing sockets for communication between bluetooth SAP module and + * the ril daemon. + *

+ * This class performs the following functions : + *

    + *
  • Initialize the socket. + *
  • Process the requests coming on the socket. + *
  • Provide handlers for Unsolicited and request responses. + *
  • Request and pending response queue handling. + *
+ */ +class RilSapSocket : public RilSocket { + /** + * Function pointer to the ril initialization funtion. + * + * @param Ril environment variable with place request and + * response handlers and timeout handler. + * + * @param Number of arguements for the initialization function. + * + * @param Arguements to the initialization function used to + * generate instance id of the ril daemon. + * + * @return Radio functions with handlers for onRequest, onStateRequest, + * supports, onCancel and getVersion. + */ + RIL_RadioFunctions *(*UimInit)(const struct RIL_Env *, int argc, char **argv); + + /** + * Place holder for the radio functions returned by the initialization + * function. Currenty only onRequest handler is being used. + */ + RIL_RadioFunctions* uimFuncs; + + /** + * Wrapper struct for handling the requests in the queue. + */ + typedef struct SapSocketRequest { + int token; + MsgHeader* curr; + struct SapSocketRequest* p_next; + RIL_SOCKET_ID socketId; + } SapSocketRequest; + + /** + * Queue for requests that are pending dispatch. + */ + Ril_queue dispatchQueue; + + /** + * Queue for requests that are dispatched but are pending response + */ + Ril_queue pendingResponseQueue; + + public: + /** + * Initialize the socket and add the socket to the list. + * + * @param Name of the socket. + * @param Radio functions to be used by the socket. + */ + static void initSapSocket(const char *socketName, + RIL_RadioFunctions *uimFuncs); + + /** + * Process requests from the dispatch request queue. + * @param Request to be dispatched. + */ + int processRequest(MsgHeader *request); + + /** + * Ril envoronment variable that holds the request and + * unsol response handlers. + */ + static struct RIL_Env uimRilEnv; + + /** + * Function to print the socket list. + */ + static void printList(); + + /** + * Clean up method to be called on command close. + */ + void onCommandsSocketClosed(void); + + /** + * Datatype to handle the socket list. + */ + typedef struct RilSapSocketList { + RilSapSocket* socket; + RilSapSocketList *next; + } RilSapSocketList; + + protected: + /** + * Process each record read from the socket and + * push a new request created from that record to + * the dispatch request queue. + * + * @param The record data. + * @param The record length. + */ + void pushRecord(void *record, size_t recordlen); + + /** + * Socket handler to be called when a request has + * been completed. + * + * @param Token associated with the request. + * @param Error, if any, while processing the request. + * @param The response payload. + * @param Response payload length. + */ + void onRequestComplete(RIL_Token t,RIL_Errno e, + void *response, size_t response_len); + + /** + * Socket handler to be called when there is an + * unsolicited response. + * + * @param Message id. + * @param Response data. + * @param Response data length. + */ + void onUnsolicitedResponse(int unsolResponse, + void *data, size_t datalen); + + /** + * Class method to get the socket from the socket list. + * + * @param Socket id. + * @return the sap socket. + */ + static RilSapSocket* getSocketById(RIL_SOCKET_ID socketId); + + /** + * Method to send response to SAP. It does an atomic write operation on the + * socket. + * + * @param the response header with the payload. + */ + void sendResponse(MsgHeader *hdr); + + /** + * A loop for processing the requests in the request dispatch queue. + */ + void *processRequestsLoop(void); + + /** + * Class method to add the sap socket to the list of sockets. + * Does nothing if the socket is already present in the list. + * Otherwise, calls the constructor of the parent class(To startlistening) + * and add socket to the socket list. + */ + static void addSocketToList(const char *socketName, RIL_SOCKET_ID socketid, + RIL_RadioFunctions *uimFuncs); + + /** + * Check if a socket of the given name exists in the socket list. + * + * @param Socket name. + * @return true if exists, false otherwise. + */ + static bool SocketExists(const char *socketName); + + /** + * Send a clean up SAP DISCONNECT if the socket disconnects before doing a SAP + * disconnect. + */ + void sendDisconnect(void); + + /** + * Dispatch the clean up disconnect request. + */ + void dispatchDisconnect(MsgHeader *req); + + + private: + /** + * Constructor. + * + * @param Socket name. + * @param Socket id. + * @param Radio functions. + */ + RilSapSocket(const char *socketName, + RIL_SOCKET_ID socketId, + RIL_RadioFunctions *inputUimFuncs); + + /** + * Called by the processRequest method to dispatch the request to + * the lower layers. It calls the on request function. + * + * @param The request message. + */ + void dispatchRequest(MsgHeader *request); + + /** + * Class method that selects the socket on which the onRequestComplete + * is called. + * + * @param Token associated with the request. + * @param Error, if any, while processing the request. + * @param The response payload. + * @param Response payload length. + */ + static void sOnRequestComplete(RIL_Token t, + RIL_Errno e, void *response, size_t responselen); + +#if defined(ANDROID_MULTI_SIM) + /** + * Class method that selects the socket on which the onUnsolicitedResponse + * is called. + * + * @param Message id. + * @param Response data. + * @param Response data length. + * @param Socket id. + */ + static void sOnUnsolicitedResponse(int unsolResponse, const void *data, + size_t datalen, RIL_SOCKET_ID socket_id); +#else + /** + * Class method that selects the socket on which the onUnsolicitedResponse + * is called. + * + * @param Message id. + * @param Response data. + * @param Response data length. + */ + static void sOnUnsolicitedResponse(int unsolResponse, const void *data, + size_t datalen); +#endif +}; + +#endif /*RIL_UIM_SOCKET_H_INCLUDED*/ diff --git a/ril/libril/RilSocket.cpp b/ril/libril/RilSocket.cpp new file mode 100644 index 00000000..a002d940 --- /dev/null +++ b/ril/libril/RilSocket.cpp @@ -0,0 +1,177 @@ +/* +* Copyright (C) 2014 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. +*/ + +extern "C" +void *ril_socket_process_requests_loop(void *arg); + +#include "RilSocket.h" +#include +#include +#include +#define SOCKET_LISTEN_BACKLOG 0 + +int RilSocket::socketInit(void) { + int ret; + + listenCb = &RilSocket::sSocketListener; + commandCb = &RilSocket::sSocketRequestsHandler; + listenFd = android_get_control_socket(name); + + //Start listening + ret = listen(listenFd, SOCKET_LISTEN_BACKLOG); + + if (ret < 0) { + RLOGE("Failed to listen on %s socket '%d': %s", + name, listenFd, strerror(errno)); + return ret; + } + //Add listen event to the event loop + ril_event_set(&listenEvent, listenFd, false, listenCb, this); + rilEventAddWakeup_helper(&listenEvent); + return ret; +} + +void RilSocket::sSocketListener(int fd, short flags, void *param) { + RilSocket *theSocket = (RilSocket *) param; + MySocketListenParam listenParam; + listenParam.socket = theSocket; + listenParam.sListenParam.type = RIL_SAP_SOCKET; + + listenCallback_helper(fd, flags, (void*)&listenParam); +} + +void RilSocket::onNewCommandConnect() { + pthread_attr_t attr; + PthreadPtr pptr = ril_socket_process_requests_loop; + int result; + + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + + //Start socket request processing loop thread + result = pthread_create(&socketThreadId, &attr, pptr, this); + if(result < 0) { + RLOGE("pthread_create failed with result:%d",result); + } + + RLOGE("New socket command connected and socket request thread started"); +} + +void RilSocket::sSocketRequestsHandler(int fd, short flags, void *param) { + socketClient *sc = (socketClient *) param; + RilSocket *theSocket = sc->socketPtr; + RecordStream *rs = sc->rs; + + theSocket->socketRequestsHandler(fd, flags, rs); +} + +void RilSocket::socketRequestsHandler(int fd, short flags, RecordStream *p_rs) { + int ret; + assert(fd == commandFd); + void *p_record; + size_t recordlen; + + for (;;) { + /* loop until EAGAIN/EINTR, end of stream, or other error */ + ret = record_stream_get_next(p_rs, &p_record, &recordlen); + + if (ret == 0 && p_record == NULL) { + /* end-of-stream */ + break; + } else if (ret < 0) { + break; + } else if (ret == 0) { + pushRecord(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."); + } + + close(commandFd); + commandFd = -1; + + ril_event_del(&callbackEvent); + + record_stream_free(p_rs); + + /* start listening for new connections again */ + + rilEventAddWakeup_helper(&listenEvent); + + onCommandsSocketClosed(); + } +} + +void RilSocket::setListenFd(int fd) { + listenFd = fd; +} + +void RilSocket::setCommandFd(int fd) { + commandFd = fd; +} + +int RilSocket::getListenFd(void) { + return listenFd; +} + +int RilSocket::getCommandFd(void) { + return commandFd; +} + +void RilSocket::setListenCb(ril_event_cb cb) { + listenCb = cb; +} + +void RilSocket::setCommandCb(ril_event_cb cb) { + commandCb = cb; +} + +ril_event_cb RilSocket::getListenCb(void) { + return listenCb; +} + +ril_event_cb RilSocket::getCommandCb(void) { + return commandCb; +} + +void RilSocket::setListenEvent(ril_event event) { + listenEvent = event; +} + +void RilSocket::setCallbackEvent(ril_event event) { + callbackEvent = event; +} + +ril_event* RilSocket::getListenEvent(void) { + return &listenEvent; +} + +ril_event* RilSocket::getCallbackEvent(void) { + return &callbackEvent; +} + +extern "C" +void *ril_socket_process_requests_loop(void *arg) { + RilSocket *socket = (RilSocket *)arg; + socket->processRequestsLoop(); + return NULL; +} diff --git a/ril/libril/RilSocket.h b/ril/libril/RilSocket.h new file mode 100644 index 00000000..053df3e4 --- /dev/null +++ b/ril/libril/RilSocket.h @@ -0,0 +1,270 @@ +/* +* Copyright (C) 2014 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. +*/ + +#ifndef RIL_SOCKET_H_INCLUDED +#define RIL_SOCKET_H_INCLUDED +#include "ril_ex.h" +#include "rilSocketQueue.h" +#include + +using namespace std; + +/** + * Abstract socket class representing sockets in rild. + *

+ * This class performs the following functions : + *

    + *
  • Start socket listen. + *
  • Handle socket listen and command callbacks. + *
+ */ +class RilSocket { + protected: + + /** + * Socket name. + */ + const char* name; + + /** + * Socket id. + */ + RIL_SOCKET_ID id; + + /** + * Listen socket file descriptor. + */ + int listenFd = -1; + + /** + * Commands socket file descriptor. + */ + int commandFd = -1; + + /** + * Socket request loop thread id. + */ + pthread_t socketThreadId; + + /** + * Listen event callack. Callback called when the other ends does accept. + */ + ril_event_cb listenCb; + + /** + * Commands event callack.Callback called when there are requests from the other side. + */ + ril_event_cb commandCb; + + /** + * Listen event to be added to eventloop after socket listen. + */ + struct ril_event listenEvent; + + /** + * Commands event to be added to eventloop after accept. + */ + struct ril_event callbackEvent; + + /** + * Static socket listen handler. Chooses the socket to call the listen callback + * from ril.cpp. + * + * @param Listen fd. + * @param flags. + * @param Parameter for the listen handler. + */ + static void sSocketListener(int fd, short flags, void *param); + + /** + * Static socket request handler. Chooses the socket to call the request handler on. + * + * @param Commands fd. + * @param flags. + * @param Parameter for the request handler. + */ + static void sSocketRequestsHandler(int fd, short flags, void *param); + + /** + * Process record from the record stream and push the requests onto the queue. + * + * @param record data. + * @param record length. + */ + virtual void pushRecord(void *record, size_t recordlen) = 0; + + /** + * Socket lock for writing data on the socket. + */ + pthread_mutex_t write_lock = PTHREAD_MUTEX_INITIALIZER; + + /** + * The loop to process the incoming requests. + */ + virtual void *processRequestsLoop(void) = 0; + + private: + friend void *::ril_socket_process_requests_loop(void *arg); + + public: + + /** + * Constructor. + * + * @param Socket name. + * @param Socket id. + */ + RilSocket(const char* socketName, RIL_SOCKET_ID socketId) { + name = socketName; + id = socketId; + } + + /** + * Clean up function on commands socket close. + */ + virtual void onCommandsSocketClosed(void) = 0; + + /** + * Function called on new commands socket connect. Request loop thread is started here. + */ + void onNewCommandConnect(void); + + /** + * Set listen socket fd. + * + * @param Input fd. + */ + void setListenFd(int listenFd); + + /** + * Set commands socket fd. + * + * @param Input fd. + */ + void setCommandFd(int commandFd); + + /** + * Get listen socket fd. + * + * @return Listen fd. + */ + int getListenFd(void); + + /** + * Get commands socket fd. + * + * @return Commands fd. + */ + int getCommandFd(void); + + /** + * Set listen event callback. + * + * @param Input event callback. + */ + void setListenCb(ril_event_cb listenCb); + + /** + * Set command event callback. + * + * @param Input event callback. + */ + void setCommandCb(ril_event_cb commandCb); + + /** + * Get listen event callback. + * + * @return Listen event callback. + */ + ril_event_cb getListenCb(void); + + /** + * Gey command event callback. + * + * @return Command event callback. + */ + ril_event_cb getCommandCb(void); + + /** + * Set listen event. + * + * @param Input event. + */ + void setListenEvent(ril_event listenEvent); + + /** + * Set command callback event. + * + * @param Input event. + */ + void setCallbackEvent(ril_event commandEvent); + + /** + * Get listen event. + * + * @return Listen event. + */ + ril_event* getListenEvent(void); + + /** + * Get commands callback event. + * + * @return Commands callback event. + */ + ril_event* getCallbackEvent(void); + + virtual ~RilSocket(){} + + protected: + + /** + * Start listening on the socket and add the socket listen callback event. + * + * @return Result of the socket listen. + */ + int socketInit(void); + + /** + * Socket request handler + * + * @param Commands fd. + * @param flags. + * @param Record stream. + */ + void socketRequestsHandler(int fd, short flags, RecordStream *rs); +}; + +class socketClient { + public: + RilSocket *socketPtr; + RecordStream *rs; + + socketClient(RilSocket *socketPtr, RecordStream *rs) { + this->socketPtr = socketPtr; + this->rs = rs; + } +}; + +typedef struct MySocketListenParam { + SocketListenParam sListenParam; + RilSocket *socket; +} MySocketListenParam; + +typedef void* (RilSocket::*RilSocketFuncPtr)(void); +typedef void (RilSocket::*RilSocketEventPtr)(int fd,short flags, void *param); +typedef void* (*PthreadPtr)(void*); + +#endif diff --git a/ril/libril/ril.cpp b/ril/libril/ril.cpp index 975518d1..fe1c65f2 100644 --- a/ril/libril/ril.cpp +++ b/ril/libril/ril.cpp @@ -18,7 +18,6 @@ #define LOG_TAG "RILC" #include - #include #include #include @@ -29,11 +28,9 @@ #include #include #include - #include #include #include - #include #include #include @@ -49,12 +46,14 @@ #include #include #include +#include -#include - +extern "C" void +RIL_onRequestComplete(RIL_Token t, RIL_Errno e, void *response, size_t responselen); namespace android { #define PHONE_PROCESS "radio" +#define BLUETOOTH_PROCESS "bluetooth" #define SOCKET_NAME_RIL "rild" #define SOCKET2_NAME_RIL "rild2" @@ -148,17 +147,6 @@ typedef struct UserCallbackInfo { struct UserCallbackInfo *p_next; } UserCallbackInfo; -typedef struct SocketListenParam { - RIL_SOCKET_ID socket_id; - int fdListen; - int fdCommand; - char* processName; - struct ril_event* commands_event; - struct ril_event* listen_event; - void (*processCommandsCallback)(int fd, short flags, void *param); - RecordStream *p_rs; -} SocketListenParam; - extern "C" const char * requestToString(int request); extern "C" const char * failCauseToString(RIL_Errno); extern "C" const char * callStateToString(RIL_CallState); @@ -385,7 +373,7 @@ static char * RIL_getRilSocketName() { } extern "C" -void RIL_setRilSocketName(char * s) { +void RIL_setRilSocketName(const char * s) { strncpy(rild, s, MAX_SOCKET_NAME_LENGTH); } @@ -2127,7 +2115,7 @@ blockingWrite(int fd, const void *buffer, size_t len) { return -1; } } - + RLOGE("RIL Response bytes written:%d", writeOffset); return 0; } @@ -3898,9 +3886,19 @@ static void listenCallback (int fd, short flags, void *param) { int err; int is_phone_socket; int fdCommand = -1; + char* processName; RecordStream *p_rs; + MySocketListenParam* listenParam; + RilSocket *sapSocket = NULL; + socketClient *sClient = NULL; + SocketListenParam *p_info = (SocketListenParam *)param; + if(RIL_SAP_SOCKET == p_info->type) { + listenParam = (MySocketListenParam *)param; + sapSocket = listenParam->socket; + } + struct sockaddr_un peeraddr; socklen_t socklen = sizeof (peeraddr); @@ -3909,15 +3907,27 @@ static void listenCallback (int fd, short flags, void *param) { struct passwd *pwd = NULL; - assert (*p_info->fdCommand < 0); - assert (fd == *p_info->fdListen); + if(NULL == sapSocket) { + assert (*p_info->fdCommand < 0); + assert (fd == *p_info->fdListen); + processName = PHONE_PROCESS; + } else { + assert (sapSocket->commandFd < 0); + assert (fd == sapSocket->listenFd); + processName = BLUETOOTH_PROCESS; + } + fdCommand = accept(fd, (sockaddr *) &peeraddr, &socklen); if (fdCommand < 0 ) { RLOGE("Error on accept() errno:%d", errno); /* start listening for new connections again */ - rilEventAddWakeup(p_info->listen_event); + if(NULL == sapSocket) { + rilEventAddWakeup(p_info->listen_event); + } else { + rilEventAddWakeup(sapSocket->getListenEvent()); + } return; } @@ -3933,7 +3943,7 @@ static void listenCallback (int fd, short flags, void *param) { errno = 0; pwd = getpwuid(creds.uid); if (pwd != NULL) { - if (strcmp(pwd->pw_name, p_info->processName) == 0) { + if (strcmp(pwd->pw_name, processName) == 0) { is_phone_socket = 1; } else { RLOGE("RILD can't accept socket from process %s", pwd->pw_name); @@ -3946,17 +3956,24 @@ static void listenCallback (int fd, short flags, void *param) { } if (!is_phone_socket) { - RLOGE("RILD must accept socket from %s", p_info->processName); + RLOGE("RILD must accept socket from %s", processName); - close(fdCommand); - fdCommand = -1; + close(fdCommand); + fdCommand = -1; - onCommandsSocketClosed(p_info->socket_id); + if(NULL == sapSocket) { + onCommandsSocketClosed(p_info->socket_id); - /* start listening for new connections again */ - rilEventAddWakeup(p_info->listen_event); + /* start listening for new connections again */ + rilEventAddWakeup(p_info->listen_event); + } else { + sapSocket->onCommandsSocketClosed(); - return; + /* start listening for new connections again */ + rilEventAddWakeup(sapSocket->getListenEvent()); + } + + return; } ret = fcntl(fdCommand, F_SETFL, O_NONBLOCK); @@ -3965,20 +3982,30 @@ static void listenCallback (int fd, short flags, void *param) { RLOGE ("Error setting O_NONBLOCK errno:%d", errno); } - RLOGI("libril: new connection to %s", rilSocketIdToString(p_info->socket_id)); + if(NULL == sapSocket) { + RLOGI("libril: new connection to %s", rilSocketIdToString(p_info->socket_id)); - p_info->fdCommand = fdCommand; + p_info->fdCommand = fdCommand; + p_rs = record_stream_new(p_info->fdCommand, MAX_COMMAND_BYTES); + p_info->p_rs = p_rs; - p_rs = record_stream_new(p_info->fdCommand, MAX_COMMAND_BYTES); - - p_info->p_rs = p_rs; - - ril_event_set (p_info->commands_event, p_info->fdCommand, 1, + ril_event_set (p_info->commands_event, p_info->fdCommand, 1, p_info->processCommandsCallback, p_info); + rilEventAddWakeup (p_info->commands_event); - rilEventAddWakeup (p_info->commands_event); + onNewCommandConnect(p_info->socket_id); + } else { + RLOGI("libril: new connection"); - onNewCommandConnect(p_info->socket_id); + sapSocket->setCommandFd(fdCommand); + p_rs = record_stream_new(sapSocket->getCommandFd(), MAX_COMMAND_BYTES); + sClient = new socketClient(sapSocket,p_rs); + ril_event_set (sapSocket->getCallbackEvent(), sapSocket->getCommandFd(), 1, + sapSocket->getCommandCb(), sClient); + + rilEventAddWakeup(sapSocket->getCallbackEvent()); + sapSocket->onNewCommandConnect(); + } } static void freeDebugCallbackArgs(int number, char **args) { @@ -4464,6 +4491,33 @@ RIL_register (const RIL_RadioFunctions *callbacks) { } +extern "C" void +RIL_register_socket (RIL_RadioFunctions *(*Init)(const struct RIL_Env *, int, char **),RIL_SOCKET_TYPE socketType, int argc, char **argv) { + + RIL_RadioFunctions* UimFuncs = NULL; + + if(Init) { + UimFuncs = Init(&RilSapSocket::uimRilEnv, argc, argv); + + switch(socketType) { + case RIL_SAP_SOCKET: + RilSapSocket::initSapSocket("sap_uim_socket1", UimFuncs); + +#if (SIM_COUNT >= 2) + RilSapSocket::initSapSocket("sap_uim_socket2", UimFuncs); +#endif + +#if (SIM_COUNT >= 3) + RilSapSocket::initSapSocket("sap_uim_socket3", UimFuncs); +#endif + +#if (SIM_COUNT >= 4) + RilSapSocket::initSapSocket("sap_uim_socket4", UimFuncs); +#endif + } + } +} + static int checkAndDequeueRequestInfo(struct RequestInfo *pRI) { int ret = 0; @@ -5185,3 +5239,15 @@ rilSocketIdToString(RIL_SOCKET_ID socket_id) } } /* namespace android */ + +void rilEventAddWakeup_helper(struct ril_event *ev) { + android::rilEventAddWakeup(ev); +} + +void listenCallback_helper(int fd, short flags, void *param) { + android::listenCallback(fd, flags, param); +} + +int blockingWrite_helper(int fd, void *buffer, size_t len) { + return android::blockingWrite(fd, buffer, len); +} diff --git a/ril/libril/rilSocketQueue.h b/ril/libril/rilSocketQueue.h new file mode 100644 index 00000000..eaa51552 --- /dev/null +++ b/ril/libril/rilSocketQueue.h @@ -0,0 +1,167 @@ +/* +* Copyright (C) 2014 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. +*/ + +#include "pb_decode.h" +#include +#include +#include + +using namespace std; + +/** + * Template queue class to handling requests for a rild socket. + *

+ * This class performs the following functions : + *

    + *
  • Enqueue. + *
  • Dequeue. + *
  • Check and dequeue. + *
+ */ + +template +class Ril_queue { + + /** + * Mutex attribute used in queue mutex initialization. + */ + pthread_mutexattr_t attr; + + /** + * Queue mutex variable for synchronized queue access. + */ + pthread_mutex_t mutex_instance; + + /** + * Condition to be waited on for dequeuing. + */ + pthread_cond_t cond; + + /** + * Front of the queue. + */ + T *front; + + public: + + /** + * Remove the first element of the queue. + * + * @return first element of the queue. + */ + T* dequeue(void); + + /** + * Add a request to the front of the queue. + * + * @param Request to be added. + */ + void enqueue(T* request); + + /** + * Check if the queue is empty. + */ + int empty(void); + + /** + * Check and remove an element with a particular message id and token. + * + * @param Request message id. + * @param Request token. + */ + int checkAndDequeue( MsgId id, int token); + + /** + * Queue constructor. + */ + Ril_queue(void); +}; + +template +Ril_queue::Ril_queue(void) { + pthread_mutexattr_init(&attr); + pthread_mutex_init(&mutex_instance, &attr); + cond = PTHREAD_COND_INITIALIZER; + front = NULL; +} + +template +T* Ril_queue::dequeue(void) { + T* temp = NULL; + + pthread_mutex_lock(&mutex_instance); + while(empty()) { + pthread_cond_wait(&cond, &mutex_instance); + } + temp = this->front; + if(NULL != this->front->p_next) { + this->front = this->front->p_next; + } else { + this->front = NULL; + } + pthread_mutex_unlock(&mutex_instance); + + return temp; +} + +template +void Ril_queue::enqueue(T* request) { + + pthread_mutex_lock(&mutex_instance); + + if(NULL == this->front) { + this->front = request; + request->p_next = NULL; + } else { + request->p_next = this->front; + this->front = request; + } + pthread_cond_broadcast(&cond); + pthread_mutex_unlock(&mutex_instance); +} + +template +int Ril_queue::checkAndDequeue(MsgId id, int token) { + int ret = 0; + T* temp; + + pthread_mutex_lock(&mutex_instance); + + for(T **ppCur = &(this->front); *ppCur != NULL; ppCur = &((*ppCur)->p_next)) { + if (token == (*ppCur)->token && id == (*ppCur)->curr->id) { + ret = 1; + temp = *ppCur; + *ppCur = (*ppCur)->p_next; + free(temp); + break; + } + } + + pthread_mutex_unlock(&mutex_instance); + + return ret; +} + + +template +int Ril_queue::empty(void) { + + if(this->front == NULL) { + return 1; + } else { + return 0; + } +} diff --git a/ril/libril/ril_ex.h b/ril/libril/ril_ex.h new file mode 100644 index 00000000..8e15d654 --- /dev/null +++ b/ril/libril/ril_ex.h @@ -0,0 +1,48 @@ +/* +* Copyright (C) 2014 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. +*/ + +#ifndef RIL_EX_H_INCLUDED +#define RIL_EX_H_INCLUDED + +#include +#include + +#define NUM_ELEMS_SOCKET(a) (sizeof (a) / sizeof (a)[0]) + +void rilEventAddWakeup_helper(struct ril_event *ev); +void listenCallback_helper(int fd, short flags, void *param); +int blockingWrite_helper(int fd, void* data, size_t len); + +enum SocketWakeType {DONT_WAKE, WAKE_PARTIAL}; + +typedef enum { + RIL_TELEPHONY_SOCKET, + RIL_SAP_SOCKET +} RIL_SOCKET_TYPE; + +typedef struct SocketListenParam { + RIL_SOCKET_ID socket_id; + int fdListen; + int fdCommand; + char* processName; + struct ril_event* commands_event; + struct ril_event* listen_event; + void (*processCommandsCallback)(int fd, short flags, void *param); + RecordStream *p_rs; + RIL_SOCKET_TYPE type; +} SocketListenParam; + +#endif