/* * Credits go to LeTama for the data connection code and phh for the wrapper code * Copyright (C) 2010 Sebastian Heinecke (gauner1986) * * 0.8 by cedesmith */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #undef LOG_TAG #define LOG_TAG "RIL_WRAP" #define RIL_onRequestComplete(t, e, response, responselen) s_rilenv->OnRequestComplete(t,e, response, responselen) #define msleep(x) usleep(x*1000); static const struct RIL_Env *s_rilenv; static struct RIL_Env htcril_env; static void *ril_handler=NULL; static int rmnet_mode = 1; static int nand_init = 0; static volatile int pppd_pid; static volatile int delayedNetworkReady=0; static volatile RIL_Token request_registration_state_token = NULL; char current_apn[80]; char current_user[80]; char current_addr[16]; static volatile int registrationState=0; static volatile int gprsRegistrationState=0; static volatile RIL_LastDataCallActivateFailCause lastDataError=PDP_FAIL_ERROR_UNSPECIFIED; char* logtime() { static char sTime[10]; time_t t = time(NULL); strftime(sTime, sizeof(sTime), "%H:%M:%S", localtime(&t)); return sTime; } typedef enum { DATA_STATE_DISCONNECTED=0, DATA_STATE_DISCONNECTING, DATA_STATE_PPP_DIED, DATA_STATE_CONNECTING, DATA_STATE_CONNECTED, DATA_STATE_CONNECTION_KILL, } Wrap_DataCallState; static volatile Wrap_DataCallState dataConnectionState=DATA_STATE_DISCONNECTED; static volatile int fd_smd=-1; int open_modem() { if(fd_smd!=-1) return fd_smd; fd_smd = open ("/dev/smd0", O_RDWR); if(fd_smd == -1) { LOGE("%s: send_modem: Error opening smd0", logtime()); return -1; //AT_ERROR_GENERIC; } struct termios ios; tcgetattr( fd_smd, &ios ); ios.c_lflag = 0; /* disable ECHO, ICANON, etc... */ tcsetattr( fd_smd, TCSANOW, &ios ); //fcntl(fd_smd, F_SETFL, O_NONBLOCK | fcntl(fd_smd, F_GETFL, 0)); return fd_smd; } void close_modem(){ if(fd_smd!=-1) { close(fd_smd); fd_smd=-1; } } int send_modem(const char * cmd) { int err = 0; size_t cur = 0; ssize_t written; size_t len = strlen(cmd); if(open_modem() == -1) return -1; //AT_ERROR_GENERIC; LOGD("%s: AT> %s", logtime(), cmd); /* the main string */ while (cur < len) { do { written = write (fd_smd, cmd + cur, len - cur); } while (written < 0 && errno == EINTR); if (written < 0) return -1;// AT_ERROR_GENERIC; cur += written; } /* the \r */ do { written = write (fd_smd, "\r" , 1); } while ((written < 0 && errno == EINTR) || (written == 0)); if (written < 0) { LOGE("%s: send_modem: write failure", logtime()); return -1; //AT_ERROR_GENERIC; } return written; } int read_modem(char* response, size_t responseLen) { if(open_modem() == -1) return -1; char *pread=response; while( pread %s", logtime(), response); else LOGD("%s MODEM>", logtime()); return pread-response; } const char* requestToString(int request) { switch(request) { case RIL_REQUEST_GET_SIM_STATUS: return "GET_SIM_STATUS"; case RIL_REQUEST_ENTER_SIM_PIN: return "ENTER_SIM_PIN"; case RIL_REQUEST_ENTER_SIM_PUK: return "ENTER_SIM_PUK"; case RIL_REQUEST_ENTER_SIM_PIN2: return "ENTER_SIM_PIN2"; case RIL_REQUEST_ENTER_SIM_PUK2: return "ENTER_SIM_PUK2"; case RIL_REQUEST_CHANGE_SIM_PIN: return "CHANGE_SIM_PIN"; case RIL_REQUEST_CHANGE_SIM_PIN2: return "CHANGE_SIM_PIN2"; case RIL_REQUEST_ENTER_NETWORK_DEPERSONALIZATION: return "ENTER_NETWORK_DEPERSONALIZATION"; case RIL_REQUEST_GET_CURRENT_CALLS: return "GET_CURRENT_CALLS"; case RIL_REQUEST_DIAL: return "DIAL"; case RIL_REQUEST_GET_IMSI: return "GET_IMSI"; case RIL_REQUEST_HANGUP: return "HANGUP"; case RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND: return "HANGUP_WAITING_OR_BACKGROUND"; case RIL_REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND: return "HANGUP_FOREGROUND_RESUME_BACKGROUND"; case RIL_REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE: return "SWITCH_WAITING_OR_HOLDING_AND_ACTIVE"; case RIL_REQUEST_CONFERENCE: return "CONFERENCE"; case RIL_REQUEST_UDUB: return "UDUB"; case RIL_REQUEST_LAST_CALL_FAIL_CAUSE: return "LAST_CALL_FAIL_CAUSE"; case RIL_REQUEST_SIGNAL_STRENGTH: return "SIGNAL_STRENGTH"; case RIL_REQUEST_REGISTRATION_STATE: return "REGISTRATION_STATE"; case RIL_REQUEST_GPRS_REGISTRATION_STATE: return "GPRS_REGISTRATION_STATE"; case RIL_REQUEST_OPERATOR: return "OPERATOR"; case RIL_REQUEST_RADIO_POWER: return "RADIO_POWER"; case RIL_REQUEST_DTMF: return "DTMF"; case RIL_REQUEST_SEND_SMS: return "SEND_SMS"; case RIL_REQUEST_SEND_SMS_EXPECT_MORE: return "SEND_SMS_EXPECT_MORE"; case RIL_REQUEST_SETUP_DATA_CALL: return "SETUP_DATA_CALL"; case RIL_REQUEST_SIM_IO: return "SIM_IO"; case RIL_REQUEST_SEND_USSD: return "SEND_USSD"; case RIL_REQUEST_CANCEL_USSD: return "CANCEL_USSD"; case RIL_REQUEST_GET_CLIR: return "GET_CLIR"; case RIL_REQUEST_SET_CLIR: return "SET_CLIR"; case RIL_REQUEST_QUERY_CALL_FORWARD_STATUS: return "QUERY_CALL_FORWARD_STATUS"; case RIL_REQUEST_SET_CALL_FORWARD: return "SET_CALL_FORWARD"; case RIL_REQUEST_QUERY_CALL_WAITING: return "QUERY_CALL_WAITING"; case RIL_REQUEST_SET_CALL_WAITING: return "SET_CALL_WAITING"; case RIL_REQUEST_SMS_ACKNOWLEDGE: return "SMS_ACKNOWLEDGE"; case RIL_REQUEST_GET_IMEI: return "GET_IMEI"; case RIL_REQUEST_GET_IMEISV: return "GET_IMEISV"; case RIL_REQUEST_ANSWER: return "ANSWER"; case RIL_REQUEST_DEACTIVATE_DATA_CALL: return "DEACTIVATE_DATA_CALL"; case RIL_REQUEST_QUERY_FACILITY_LOCK: return "QUERY_FACILITY_LOCK"; case RIL_REQUEST_SET_FACILITY_LOCK: return "SET_FACILITY_LOCK"; case RIL_REQUEST_CHANGE_BARRING_PASSWORD: return "CHANGE_BARRING_PASSWORD"; case RIL_REQUEST_QUERY_NETWORK_SELECTION_MODE: return "QUERY_NETWORK_SELECTION_MODE"; case RIL_REQUEST_SET_NETWORK_SELECTION_AUTOMATIC: return "SET_NETWORK_SELECTION_AUTOMATIC"; case RIL_REQUEST_SET_NETWORK_SELECTION_MANUAL: return "SET_NETWORK_SELECTION_MANUAL"; case RIL_REQUEST_QUERY_AVAILABLE_NETWORKS : return "QUERY_AVAILABLE_NETWORKS "; case RIL_REQUEST_DTMF_START: return "DTMF_START"; case RIL_REQUEST_DTMF_STOP: return "DTMF_STOP"; case RIL_REQUEST_BASEBAND_VERSION: return "BASEBAND_VERSION"; case RIL_REQUEST_SEPARATE_CONNECTION: return "SEPARATE_CONNECTION"; case RIL_REQUEST_SET_PREFERRED_NETWORK_TYPE: return "SET_PREFERRED_NETWORK_TYPE"; case RIL_REQUEST_GET_PREFERRED_NETWORK_TYPE: return "GET_PREFERRED_NETWORK_TYPE"; case RIL_REQUEST_GET_NEIGHBORING_CELL_IDS: return "GET_NEIGHBORING_CELL_IDS"; case RIL_REQUEST_SET_MUTE: return "SET_MUTE"; case RIL_REQUEST_GET_MUTE: return "GET_MUTE"; case RIL_REQUEST_QUERY_CLIP: return "QUERY_CLIP"; case RIL_REQUEST_LAST_DATA_CALL_FAIL_CAUSE: return "LAST_DATA_CALL_FAIL_CAUSE"; case RIL_REQUEST_DATA_CALL_LIST: return "DATA_CALL_LIST"; case RIL_REQUEST_RESET_RADIO: return "RESET_RADIO"; case RIL_REQUEST_OEM_HOOK_RAW: return "OEM_HOOK_RAW"; case RIL_REQUEST_OEM_HOOK_STRINGS: return "OEM_HOOK_STRINGS"; case RIL_REQUEST_SET_BAND_MODE: return "SET_BAND_MODE"; case RIL_REQUEST_QUERY_AVAILABLE_BAND_MODE: return "QUERY_AVAILABLE_BAND_MODE"; case RIL_REQUEST_STK_GET_PROFILE: return "STK_GET_PROFILE"; case RIL_REQUEST_STK_SET_PROFILE: return "STK_SET_PROFILE"; case RIL_REQUEST_STK_SEND_ENVELOPE_COMMAND: return "STK_SEND_ENVELOPE_COMMAND"; case RIL_REQUEST_STK_SEND_TERMINAL_RESPONSE: return "STK_SEND_TERMINAL_RESPONSE"; case RIL_REQUEST_STK_HANDLE_CALL_SETUP_REQUESTED_FROM_SIM: return "STK_HANDLE_CALL_SETUP_REQUESTED_FROM_SIM"; case RIL_REQUEST_SCREEN_STATE: return "SCREEN_STATE"; case RIL_REQUEST_EXPLICIT_CALL_TRANSFER: return "EXPLICIT_CALL_TRANSFER"; case RIL_REQUEST_SET_LOCATION_UPDATES: return "SET_LOCATION_UPDATES"; case RIL_REQUEST_CDMA_SET_SUBSCRIPTION:return"CDMA_SET_SUBSCRIPTION"; case RIL_REQUEST_CDMA_SET_ROAMING_PREFERENCE:return"CDMA_SET_ROAMING_PREFERENCE"; case RIL_REQUEST_CDMA_QUERY_ROAMING_PREFERENCE:return"CDMA_QUERY_ROAMING_PREFERENCE"; case RIL_REQUEST_SET_TTY_MODE:return"SET_TTY_MODE"; case RIL_REQUEST_QUERY_TTY_MODE:return"QUERY_TTY_MODE"; case RIL_REQUEST_CDMA_SET_PREFERRED_VOICE_PRIVACY_MODE:return"CDMA_SET_PREFERRED_VOICE_PRIVACY_MODE"; case RIL_REQUEST_CDMA_QUERY_PREFERRED_VOICE_PRIVACY_MODE:return"CDMA_QUERY_PREFERRED_VOICE_PRIVACY_MODE"; case RIL_REQUEST_CDMA_FLASH:return"CDMA_FLASH"; case RIL_REQUEST_CDMA_BURST_DTMF:return"CDMA_BURST_DTMF"; case RIL_REQUEST_CDMA_SEND_SMS:return"CDMA_SEND_SMS"; case RIL_REQUEST_CDMA_SMS_ACKNOWLEDGE:return"CDMA_SMS_ACKNOWLEDGE"; case RIL_REQUEST_GSM_GET_BROADCAST_SMS_CONFIG:return"GSM_GET_BROADCAST_SMS_CONFIG"; case RIL_REQUEST_GSM_SET_BROADCAST_SMS_CONFIG:return"GSM_SET_BROADCAST_SMS_CONFIG"; case RIL_REQUEST_CDMA_GET_BROADCAST_SMS_CONFIG:return "CDMA_GET_BROADCAST_SMS_CONFIG"; case RIL_REQUEST_CDMA_SET_BROADCAST_SMS_CONFIG:return "CDMA_SET_BROADCAST_SMS_CONFIG"; case RIL_REQUEST_CDMA_SMS_BROADCAST_ACTIVATION:return "CDMA_SMS_BROADCAST_ACTIVATION"; case RIL_REQUEST_CDMA_VALIDATE_AND_WRITE_AKEY: return"CDMA_VALIDATE_AND_WRITE_AKEY"; case RIL_REQUEST_CDMA_SUBSCRIPTION: return"CDMA_SUBSCRIPTION"; case RIL_REQUEST_CDMA_WRITE_SMS_TO_RUIM: return "CDMA_WRITE_SMS_TO_RUIM"; case RIL_REQUEST_CDMA_DELETE_SMS_ON_RUIM: return "CDMA_DELETE_SMS_ON_RUIM"; case RIL_REQUEST_DEVICE_IDENTITY: return "DEVICE_IDENTITY"; case RIL_REQUEST_EXIT_EMERGENCY_CALLBACK_MODE: return "EXIT_EMERGENCY_CALLBACK_MODE"; case RIL_REQUEST_GET_SMSC_ADDRESS: return "GET_SMSC_ADDRESS"; case RIL_REQUEST_SET_SMSC_ADDRESS: return "SET_SMSC_ADDRESS"; case RIL_REQUEST_REPORT_SMS_MEMORY_STATUS: return "REPORT_SMS_MEMORY_STATUS"; case RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED: return "UNSOL_RESPONSE_RADIO_STATE_CHANGED"; case RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED: return "UNSOL_RESPONSE_CALL_STATE_CHANGED"; case RIL_UNSOL_RESPONSE_NETWORK_STATE_CHANGED: return "UNSOL_RESPONSE_NETWORK_STATE_CHANGED"; case RIL_UNSOL_RESPONSE_NEW_SMS: return "UNSOL_RESPONSE_NEW_SMS"; case RIL_UNSOL_RESPONSE_NEW_SMS_STATUS_REPORT: return "UNSOL_RESPONSE_NEW_SMS_STATUS_REPORT"; case RIL_UNSOL_RESPONSE_NEW_SMS_ON_SIM: return "UNSOL_RESPONSE_NEW_SMS_ON_SIM"; case RIL_UNSOL_ON_USSD: return "UNSOL_ON_USSD"; case RIL_UNSOL_ON_USSD_REQUEST: return "UNSOL_ON_USSD_REQUEST(obsolete)"; case RIL_UNSOL_NITZ_TIME_RECEIVED: return "UNSOL_NITZ_TIME_RECEIVED"; case RIL_UNSOL_SIGNAL_STRENGTH: return "UNSOL_SIGNAL_STRENGTH"; case RIL_UNSOL_STK_SESSION_END: return "UNSOL_STK_SESSION_END"; case RIL_UNSOL_STK_PROACTIVE_COMMAND: return "UNSOL_STK_PROACTIVE_COMMAND"; case RIL_UNSOL_STK_EVENT_NOTIFY: return "UNSOL_STK_EVENT_NOTIFY"; case RIL_UNSOL_STK_CALL_SETUP: return "UNSOL_STK_CALL_SETUP"; case RIL_UNSOL_SIM_SMS_STORAGE_FULL: return "UNSOL_SIM_SMS_STORAGE_FUL"; case RIL_UNSOL_SIM_REFRESH: return "UNSOL_SIM_REFRESH"; case RIL_UNSOL_DATA_CALL_LIST_CHANGED: return "UNSOL_DATA_CALL_LIST_CHANGED"; case RIL_UNSOL_CALL_RING: return "UNSOL_CALL_RING"; case RIL_UNSOL_RESPONSE_SIM_STATUS_CHANGED: return "UNSOL_RESPONSE_SIM_STATUS_CHANGED"; case RIL_UNSOL_RESPONSE_CDMA_NEW_SMS: return "UNSOL_NEW_CDMA_SMS"; case RIL_UNSOL_RESPONSE_NEW_BROADCAST_SMS: return "UNSOL_NEW_BROADCAST_SMS"; case RIL_UNSOL_CDMA_RUIM_SMS_STORAGE_FULL: return "UNSOL_CDMA_RUIM_SMS_STORAGE_FULL"; case RIL_UNSOL_RESTRICTED_STATE_CHANGED: return "UNSOL_RESTRICTED_STATE_CHANGED"; case RIL_UNSOL_ENTER_EMERGENCY_CALLBACK_MODE: return "UNSOL_ENTER_EMERGENCY_CALLBACK_MODE"; case RIL_UNSOL_CDMA_CALL_WAITING: return "UNSOL_CDMA_CALL_WAITING"; case RIL_UNSOL_CDMA_OTA_PROVISION_STATUS: return "UNSOL_CDMA_OTA_PROVISION_STATUS"; case RIL_UNSOL_CDMA_INFO_REC: return "UNSOL_CDMA_INFO_REC"; case RIL_UNSOL_OEM_HOOK_RAW: return "UNSOL_OEM_HOOK_RAW"; case RIL_UNSOL_RINGBACK_TONE: return "UNSOL_RINGBACK_TONE"; case RIL_UNSOL_RESEND_INCALL_MUTE: return "UNSOL_RESEND_INCALL_MUTE"; default: return ""; } } struct RequestInfo { RIL_Token token; int request; time_t startTime; }; struct RequestInfo pendingRequests[200]; void requestStarted(RIL_Token t, int request) { LOGD("%s: Request Received %p %s", logtime(), t, requestToString(request)); for(size_t i=0; i 10) { LOGD("%s: Request delete %p %s after %d ms", logtime(), pendingRequests[i].token, requestToString(pendingRequests[i].request), (unsigned int)(clock()-pendingRequests[i].startTime)/(CLOCKS_PER_SEC/1000)); requestRemoveAt(i); i--; } else { count++; } } } return count; } void requestsLOGD() { for(size_t i=0; i0) { LOGD("%s: Request pending... waiting to complete", logtime()); requestsLOGD(); while(requestsPending()>0) msleep(1); LOGD("%s: Request completed... continue %s", logtime(), msg!=NULL ? msg : NULL); } } void* pppd_thread(void *param) { LOGD("%s: pppd_thread enter", logtime()); requestsWaitComplete("pppd_thread"); pppd_pid = fork(); if(pppd_pid == 0) { char buff[256]; kill(getppid(), SIGSTOP); //stop stealing my mojo int act=0; send_modem("AT+CGACT?"); do { read_modem(buff, sizeof(buff)); char* actpos=strstr(buff, "+CGACT: 1,"); if(actpos!=NULL) act=atoi(actpos+10); }while(buff[0]!='0'); if(act!=0) { kill(getppid(), SIGCONT); exit(202); } sprintf(buff, "AT+CGDCONT=1,\"IP\",\"%s\",,0,0", current_apn); send_modem(buff); read_modem(buff, sizeof(buff)); send_modem("ATD*99***1#"); //send_modem("AT+CGDATA=\"PPP\",1"); while(read_modem(buff, sizeof(buff))>0 && buff[0]=='+'); //read_modem(buff, sizeof(buff)); kill(getppid(), SIGCONT); int atd=atoi(buff); if(atd!=1 && atd!=3) exit(201); sleep(1); close_modem(); //close modem handle before we transform to pppd int err = execl("/system/bin/pppd", "pppd", "/dev/smd1", "local","nodetach", "defaultroute", "noipdefault", "usepeerdns", "user", current_user, "debug", NULL); LOGE("%s: PPPD EXEC FAILED (%d)", logtime(),err); exit(200); } else { LOGD("%s: pppd pid is %d", logtime(),pppd_pid); int status=0; time_t start = time(NULL); waitpid(pppd_pid, &status, 0); pppd_pid=0; int runTime = time(NULL)-start; LOGD("%s: PPPD DIED after %d seconds with status %d", logtime(), runTime, status); if(lastDataError==PDP_FAIL_ERROR_UNSPECIFIED && WIFEXITED(status)){ switch(WEXITSTATUS(status)) { case 1: lastDataError=PDP_FAIL_INSUFFICIENT_RESOURCES; break; case 2: lastDataError=PDP_FAIL_SERVICE_OPTION_NOT_SUPPORTED; break; case 3: case 4: lastDataError=PDP_FAIL_PROTOCOL_ERRORS; break; case 19: lastDataError=PDP_FAIL_USER_AUTHENTICATION; break; } } requestsWaitComplete("pppd_thread"); send_modem("AT+CGACT=0,1"); if(dataConnectionState==DATA_STATE_CONNECTED || dataConnectionState==DATA_STATE_CONNECTION_KILL) { dataConnectionState=DATA_STATE_DISCONNECTED; RIL_Data_Call_Response dataCall={ .cid=1, .active=0, .type="IP", .apn=current_apn, .address=current_addr }; s_rilenv->OnUnsolicitedResponse(RIL_UNSOL_DATA_CALL_LIST_CHANGED, &dataCall, sizeof(RIL_Data_Call_Response)); } } LOGD("%s: pppd_thread exit", logtime()); return NULL; } void* DeactivateData(void* t) { dataConnectionState = DATA_STATE_DISCONNECTING; LOGD("%s: DeactivateData", logtime()); int pid=pppd_pid; //work with pppd_pid copy as thread will set it to 0 after kill if(pid!=0) { int status=0; LOGD(" waiting for pppd to end %d", pid); kill(pid, SIGTERM); waitpid(pid, &status, 0); LOGD("%s: DeactivateData: pppd ended", logtime()); } //clear dns entries property_set("net.ppp0.dns1", ""); property_set("net.ppp0.dns2", ""); dataConnectionState = DATA_STATE_DISCONNECTED; RIL_onRequestComplete((RIL_Token) t, RIL_E_SUCCESS, NULL, 0); return NULL; } void hackDeactivateData(void *data, size_t datalen, RIL_Token t) { LOGD("%s: DeactivateData Request", logtime()); char* cid = ((char **)data)[0]; if(atoi(cid)!=1) { LOGE(" wrong CID %s expected 1", cid); return; } pthread_t tid; pthread_create(&tid, NULL, DeactivateData, t); } int ifc_get_info(const char *name, unsigned *addr, unsigned *mask, unsigned *flags); int ifc_init(void); void ifc_close(void); void* SetupData(void* t) { //this should never happen but let's check if(pppd_pid!=0 ) { LOGD(" waiting pppd to die"); int status=0; kill(pppd_pid, SIGTERM); waitpid(pppd_pid, &status, 0); } //reset ppp.dns property_set("net.ppp0.dns1", "0.0.0.0"); property_set("net.ppp0.dns2", "0.0.0.0"); strcpy(current_addr, "255.255.255.255"); pthread_t thread; pthread_create(&thread, NULL, pppd_thread, NULL); //wait for pppd connect if(ifc_init()) { LOGE("%s: IFC failed to init", logtime()); sleep(7); } else { clock_t start=clock(); //loop till timeout or connected while(1) { //check if ppp0 interface is up, if true break loop, else record dnschange value unsigned addr, mask, flags; ifc_get_info("ppp0", &addr, &mask, &flags); if(flags & 1) { struct in_addr in_addr = {.s_addr=addr}; strcpy(current_addr, inet_ntoa(in_addr)); LOGD("%s: IP: %s", logtime(),current_addr); break; } //if timeout goto error if ( (clock()-start)/CLOCKS_PER_SEC > 60 ){ LOGE("%s: ppp0 connect timed out, giving up", logtime()); ifc_close(); goto error; } int status, pid=pppd_pid; if(pid==0 || waitpid(pid, &status, WNOHANG)>0){ LOGE("%s: ppp0 connect timed out, giving up", logtime()); ifc_close(); goto error; } msleep(100); } } ifc_close(); //if ip-up exists wait for dns change char dns1[PROPERTY_VALUE_MAX]; char dns2[PROPERTY_VALUE_MAX]; struct stat sts; if(stat("/etc/ppp/ip-up", &sts)==0 && (S_ISREG(sts.st_mode) || S_ISLNK(sts.st_mode))) { clock_t start=clock(); while(1) { //check if dnschange changed property_get("net.ppp0.dns1", dns1, "0.0.0.0"); property_get("net.ppp0.dns2", dns2, "0.0.0.0"); if(strcmp(dns1, "0.0.0.0")!=0 && strcmp(dns2, "0.0.0.0")!=0) break; if((clock()-start)/CLOCKS_PER_SEC > 2) { LOGE("%s: timeout waiting for dns change", logtime()); break; } msleep(100); } } //check ppp.dns values and set defaults if suspect wrong property_get("net.ppp0.dns1", dns1, ""); if(strlen(dns1)<7 || strcmp(dns1,"0.0.0.0")==0 || strcmp(dns1, "10.11.12.13")==0) { LOGD("%s: DNS1: %s wrong setting to 8.8.8.8", logtime(),dns1); property_set("net.ppp0.dns1", "8.8.8.8"); } else { LOGD("%s: DNS1: %s", logtime(),dns1); } property_get("net.ppp0.dns2", dns2, ""); if(strlen(dns2)<7 || strcmp(dns2, "0.0.0.0")==0 || strcmp(dns2, "10.11.12.14")==0) { LOGD("%s: DNS2: %s wrong setting to 8.8.4.4", logtime(),dns2); property_set("net.ppp0.dns2", "8.8.4.4"); } else { LOGD("%s: DNS2: %s", logtime(),dns2); } char *response[3] = { "1", "ppp0", current_addr }; dataConnectionState=DATA_STATE_CONNECTED; RIL_onRequestComplete(t, RIL_E_SUCCESS, response, sizeof(response)); return NULL; error: dataConnectionState=DATA_STATE_DISCONNECTED; RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0); return NULL; } void hackSetupData(char **data, size_t datalen, RIL_Token t) { LOGD("%s: SetupData(%s) Request", logtime(),((const char **)data)[2]); lastDataError=PDP_FAIL_ERROR_UNSPECIFIED; if(*data[0]=='0') { LOGE(" Android want us to connect as CDMA while we are a GSM phone !"); goto error; } if(!registrationState) { LOGE(" network registration state wrong"); lastDataError=PDP_FAIL_REGISTRATION_FAIL; goto error; } if(!gprsRegistrationState) { LOGE(" gprs registration state wrong"); lastDataError=PDP_FAIL_GPRS_REGISTRATION_FAIL; goto error; } dataConnectionState = DATA_STATE_CONNECTING; char *apn = ((char **)data)[2]; char *user = ((char **)data)[3]; char *pass = ((char **)data)[4]; if(apn==NULL) apn=""; if(user==NULL || strlen(user)<2) user = "dummy"; if(pass==NULL || strlen(pass)<2) pass = "dummy"; strcpy(current_apn, apn); strcpy(current_user, user); //save auth truncate("/etc/ppp/pap-secrets", 0); truncate("/etc/ppp/chap-secrets", 0); char buff[128]; sprintf(buff, "%s * %s\n", user, pass); int fd; if((fd=creat("/etc/ppp/pap-secrets", S_IRUSR|S_IWUSR))==-1) { LOGE("Failed to create /etc/ppp/pap-secrets"); lastDataError=PDP_FAIL_USER_AUTHENTICATION; goto error; } write(fd, buff, strlen(buff)); close(fd); if((fd=creat("/etc/ppp/chap-secrets", S_IRUSR|S_IWUSR))==-1) { LOGE("Failed to create /etc/ppp/chap-secrets"); lastDataError=PDP_FAIL_USER_AUTHENTICATION; goto error; } write(fd, buff, strlen(buff)); close(fd); pthread_t tid; pthread_create(&tid, NULL, SetupData, t); return; error: dataConnectionState=DATA_STATE_DISCONNECTED; RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0); } void interceptOnRequestComplete(RIL_Token t, RIL_Errno e, void *response, size_t responselen) { if(!rmnet_mode) { struct RequestInfo requestInfo= requestCompleted(t); if( requestInfo.request==RIL_REQUEST_REGISTRATION_STATE) { if(responselen>=14*sizeof(char *)) { char **strings = (char **)response; int registration = atoi(strings[0]); //1 - Registered, home network; 5 - Registered, roaming int radio = atoi(strings[3]); //0 == unknown registrationState=((registration==1 || registration==5) && radio!=0); LOGD("%s: Registration state %d %d = %d", logtime(), registration, radio, registrationState); if(!registrationState && pppd_pid!=0 && dataConnectionState==DATA_STATE_CONNECTED){ LOGE("%s: data disconnect due to network registration state", logtime()); lastDataError=PDP_FAIL_REGISTRATION_FAIL; dataConnectionState=DATA_STATE_CONNECTION_KILL; kill(pppd_pid, SIGTERM); } } } if( requestInfo.request==RIL_REQUEST_GPRS_REGISTRATION_STATE) { if(responselen>=4*sizeof(char *)) { char **strings = (char **)response; int registration = atoi(strings[0]); //1 - Registered, home network; 5 - Registered, roaming int radio = atoi(strings[3]); //0 == unknown; 4 ("unknown") is treated as "out of service" in the Android telephony system gprsRegistrationState=((registration==1 || registration==5) && (radio!=0 && radio!=4)); LOGD("%s: Registration state %d %d = %d", logtime(), registration, radio, gprsRegistrationState); if(!gprsRegistrationState && pppd_pid!=0 && dataConnectionState==DATA_STATE_CONNECTED){ LOGE("%s: data disconnect due to gprs registration state", logtime()); lastDataError=PDP_FAIL_GPRS_REGISTRATION_FAIL; dataConnectionState=DATA_STATE_CONNECTION_KILL; kill(pppd_pid, SIGTERM); } } } } s_rilenv->OnRequestComplete(t, e, response, responselen); } void hackDataCallList(char **data, size_t datalen, RIL_Token t) { RIL_Data_Call_Response dataCall={ .cid=1, .active=0, .type="IP", .apn=current_apn, .address=current_addr }; LOGD("%s: DataCallList", logtime()); LOGD(" cid=%d, active=%d, type=%s, apn=%s, add=%s", dataCall.cid, dataCall.active, dataCall.type, dataCall.apn, dataCall.address); s_rilenv->OnRequestComplete(t, RIL_E_SUCCESS, &dataCall, sizeof(RIL_Data_Call_Response)); } void interceptOnUnsolicitedResponse(int unsolResponse, const void *data, size_t datalen) { LOGD("%s: UNSOL %s", logtime(), requestToString(unsolResponse)); s_rilenv->OnUnsolicitedResponse(unsolResponse, data, datalen); } void (*htc_onRequest)(int request, void *data, size_t datalen, RIL_Token t); void onRequest(int request, void *data, size_t datalen, RIL_Token t) { if(!rmnet_mode) { switch(request) { case RIL_REQUEST_SETUP_DATA_CALL: return hackSetupData(data, datalen, t); case RIL_REQUEST_DEACTIVATE_DATA_CALL: return hackDeactivateData(data, datalen, t); case RIL_REQUEST_DATA_CALL_LIST: return hackDataCallList(data, datalen, t); case RIL_REQUEST_LAST_DATA_CALL_FAIL_CAUSE: s_rilenv->OnRequestComplete(t, RIL_E_SUCCESS,(RIL_LastDataCallActivateFailCause*) &lastDataError, sizeof(RIL_LastDataCallActivateFailCause)); return; } requestStarted(t, request); } return htc_onRequest(request, data, datalen, t); } void writeAdditionalNandInit(){ LOGD("NAND boot, writing additional init commands to /dev/smd0"); send_modem("AT@BRIC=0"); send_modem("AT+CFUN=0"); send_modem("AT+COPS=2"); } const RIL_RadioFunctions *RIL_Init(const struct RIL_Env *env, int argc, char **argv) { int i; int ril_argc = 0; char **ril_argv; RIL_RadioFunctions *(*htc_RIL_Init)(const struct RIL_Env *env, int argc, char **argv); RIL_RadioFunctions *s_callbacks; s_rilenv = env; LOGD("----------- HTC Ril Wrapper v0.8b5 starting ------------"); // we never free this, but we can't tell if htc ril uses argv after init ril_argv = (char **)malloc(argc * sizeof(char*)); struct stat sts; if(stat("/system/ppp", &sts)==0 && (S_ISREG(sts.st_mode) || S_ISLNK(sts.st_mode))) rmnet_mode = 0; LOGD("rmnet_mode=%d", rmnet_mode); // Parse command line and prepare ril command line for(i = 0; i < argc ; i++) { LOGW("RIL_Init arg[%d]=%s", i, argv[i]); if(strcmp(argv[i], "rmnet_mode") == 0) continue; if(strcmp(argv[i], "nand_init") == 0) nand_init = 1; else { ril_argv[ril_argc++] = argv[i]; } } if(nand_init) writeAdditionalNandInit(); ril_handler=dlopen("/system/lib/libhtc_ril.so", 0/*Need to RTFM, 0 seems fine*/); htc_RIL_Init = dlsym(ril_handler, "RIL_Init"); // re-route to our man in the middle functions htcril_env.OnRequestComplete = interceptOnRequestComplete; htcril_env.OnUnsolicitedResponse = interceptOnUnsolicitedResponse; htcril_env.RequestTimedCallback = s_rilenv->RequestTimedCallback; s_callbacks = htc_RIL_Init(&htcril_env, ril_argc, ril_argv); htc_onRequest = s_callbacks->onRequest; s_callbacks->onRequest=onRequest; return s_callbacks; }