| /* |
| ** Copyright 2017, 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 ioogle/s 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 "guest/hals/ril/cuttlefish_ril.h" |
| |
| #include <cutils/properties.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <sys/types.h> |
| #include <time.h> |
| |
| #include <map> |
| #include <set> |
| #include <string> |
| #include <vector> |
| |
| #include "common/libs/device_config/device_config.h" |
| #include "common/libs/net/netlink_client.h" |
| #include "common/libs/net/network_interface.h" |
| #include "common/libs/net/network_interface_manager.h" |
| |
| #define CUTTLEFISH_RIL_VERSION_STRING "Android Cuttlefish RIL 1.4" |
| |
| /* Modem Technology bits */ |
| #define MDM_GSM 0x01 |
| #define MDM_WCDMA 0x02 |
| #define MDM_CDMA 0x04 |
| #define MDM_EVDO 0x08 |
| #define MDM_LTE 0x10 |
| |
| typedef enum { |
| SIM_ABSENT = 0, |
| SIM_NOT_READY = 1, |
| SIM_READY = 2, // SIM_READY means the radio state is RADIO_STATE_SIM_READY |
| SIM_PIN = 3, |
| SIM_PUK = 4, |
| SIM_NETWORK_PERSONALIZATION = 5, |
| RUIM_ABSENT = 6, |
| RUIM_NOT_READY = 7, |
| RUIM_READY = 8, |
| RUIM_PIN = 9, |
| RUIM_PUK = 10, |
| RUIM_NETWORK_PERSONALIZATION = 11 |
| } SIM_Status; |
| |
| static std::unique_ptr<cvd::DeviceConfig> global_ril_config = nullptr; |
| |
| static const struct RIL_Env* gce_ril_env; |
| |
| static const struct timeval TIMEVAL_SIMPOLL = {3, 0}; |
| |
| static time_t gce_ril_start_time; |
| |
| static void pollSIMState(void* param); |
| |
| RIL_RadioState gRadioPowerState = RADIO_STATE_OFF; |
| RIL_RadioAccessFamily default_access = RAF_LTE; |
| |
| struct DataCall { |
| enum AllowedAuthenticationType { kNone = 0, kPap = 1, kChap = 2, kBoth = 3 }; |
| |
| enum ConnectionType { |
| kConnTypeIPv4, |
| kConnTypeIPv6, |
| kConnTypeIPv4v6, |
| kConnTypePPP |
| }; |
| |
| enum LinkState { |
| kLinkStateInactive = 0, |
| kLinkStateDown = 1, |
| kLinkStateUp = 2, |
| }; |
| |
| RIL_RadioTechnology technology_; |
| RIL_DataProfile profile_; |
| std::string access_point_; |
| std::string username_; |
| std::string password_; |
| AllowedAuthenticationType auth_type_; |
| ConnectionType connection_type_; |
| LinkState link_state_; |
| RIL_DataCallFailCause fail_cause_; |
| std::string other_properties_; |
| }; |
| |
| static std::string gSimPIN = "0000"; |
| static const std::string gSimPUK = "11223344"; |
| static int gSimPINAttempts = 0; |
| static const int gSimPINAttemptsMax = 3; |
| static SIM_Status gSimStatus = SIM_NOT_READY; |
| static bool areUiccApplicationsEnabled = true; |
| |
| // SetUpNetworkInterface configures IP and Broadcast addresses on a RIL |
| // controlled network interface. |
| // This call returns true, if operation was successful. |
| bool SetUpNetworkInterface(const char* ipaddr, int prefixlen, |
| const char* bcaddr) { |
| auto factory = cvd::NetlinkClientFactory::Default(); |
| std::unique_ptr<cvd::NetlinkClient> nl(factory->New(NETLINK_ROUTE)); |
| std::unique_ptr<cvd::NetworkInterfaceManager> nm( |
| cvd::NetworkInterfaceManager::New(factory)); |
| std::unique_ptr<cvd::NetworkInterface> ni(nm->Open("rmnet0", "eth1")); |
| |
| if (ni) { |
| ni->SetName("rmnet0"); |
| ni->SetAddress(ipaddr); |
| ni->SetBroadcastAddress(bcaddr); |
| ni->SetPrefixLength(prefixlen); |
| ni->SetOperational(true); |
| bool res = nm->ApplyChanges(*ni); |
| if (!res) ALOGE("Could not configure rmnet0"); |
| return res; |
| } |
| return false; |
| } |
| |
| // TearDownNetworkInterface disables network interface. |
| // This call returns true, if operation was successful. |
| bool TearDownNetworkInterface() { |
| auto nm(cvd::NetworkInterfaceManager::New(nullptr)); |
| auto ni(nm->Open("rmnet0", "eth1")); |
| |
| if (ni) { |
| ni->SetOperational(false); |
| bool res = nm->ApplyChanges(*ni); |
| if (!res) ALOGE("Could not disable rmnet0"); |
| return res; |
| } |
| return false; |
| } |
| |
| static int gNextDataCallId = 8; |
| static std::map<int, DataCall> gDataCalls; |
| static bool gRilConnected = false; |
| |
| static int request_or_send_data_calllist(RIL_Token* t) { |
| RIL_Data_Call_Response_v11* responses = |
| new RIL_Data_Call_Response_v11[gDataCalls.size()]; |
| |
| int index = 0; |
| |
| ALOGV("Query data call list: %zu data calls tracked.", gDataCalls.size()); |
| |
| for (std::map<int, DataCall>::iterator iter = gDataCalls.begin(); |
| iter != gDataCalls.end(); ++iter, ++index) { |
| responses[index].status = iter->second.fail_cause_; |
| responses[index].suggestedRetryTime = -1; |
| responses[index].cid = iter->first; |
| responses[index].active = iter->second.link_state_; |
| |
| switch (iter->second.connection_type_) { |
| case DataCall::kConnTypeIPv4: |
| responses[index].type = (char*)"IP"; |
| break; |
| case DataCall::kConnTypeIPv6: |
| responses[index].type = (char*)"IPV6"; |
| break; |
| case DataCall::kConnTypeIPv4v6: |
| responses[index].type = (char*)"IPV4V6"; |
| break; |
| case DataCall::kConnTypePPP: |
| responses[index].type = (char*)"PPP"; |
| break; |
| default: |
| responses[index].type = (char*)"IP"; |
| break; |
| } |
| |
| responses[index].ifname = (char*)"rmnet0"; |
| responses[index].addresses = |
| const_cast<char*>(global_ril_config->ril_address_and_prefix()); |
| responses[index].dnses = const_cast<char*>(global_ril_config->ril_dns()); |
| responses[index].gateways = const_cast<char*>(global_ril_config->ril_gateway()); |
| responses[index].pcscf = (char*)""; |
| responses[index].mtu = 1440; |
| } |
| |
| bool new_conn_state = (gDataCalls.size() > 0); |
| |
| if (gRilConnected != new_conn_state) { |
| time_t curr_time; |
| time(&curr_time); |
| double diff_in_secs = difftime(curr_time, gce_ril_start_time); |
| |
| gRilConnected = new_conn_state; |
| |
| if (new_conn_state) { |
| ALOGV("MOBILE_DATA_CONNECTED %.2lf seconds", diff_in_secs); |
| } else { |
| ALOGV("MOBILE_DATA_DISCONNECTED %.2lf seconds", diff_in_secs); |
| } |
| |
| if (property_set("ril.net_connected", new_conn_state ? "1" : "0")) { |
| ALOGE("Couldn't set a system property ril.net_connected."); |
| } |
| } |
| |
| if (t != NULL) { |
| gce_ril_env->OnRequestComplete(*t, RIL_E_SUCCESS, responses, |
| gDataCalls.size() * sizeof(*responses)); |
| } else { |
| gce_ril_env->OnUnsolicitedResponse(RIL_UNSOL_DATA_CALL_LIST_CHANGED, |
| responses, |
| gDataCalls.size() * sizeof(*responses)); |
| } |
| delete[] responses; |
| return 0; |
| } |
| |
| static void request_datacall_fail_cause(RIL_Token t) { |
| RIL_DataCallFailCause fail = PDP_FAIL_DATA_REGISTRATION_FAIL; |
| |
| if (gDataCalls.size() > 0) { |
| fail = gDataCalls.rbegin()->second.fail_cause_; |
| } |
| |
| ALOGV("Requesting last data call setup fail cause (%d)", fail); |
| gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, &fail, sizeof(fail)); |
| }; |
| |
| static void request_data_calllist(void* /*data*/, size_t /*datalen*/, |
| RIL_Token t) { |
| request_or_send_data_calllist(&t); |
| } |
| |
| static void request_setup_data_call(void* data, size_t datalen, RIL_Token t) { |
| char** details = static_cast<char**>(data); |
| const size_t fields = datalen / sizeof(details[0]); |
| |
| // There are two different versions of this interface, one providing 7 strings |
| // and the other providing 8. The code below will assume the presence of 7 |
| // strings in all cases, so bail out here if things appear to be wrong. We |
| // protect the 8 string case below. |
| if (fields < 7) { |
| ALOGE("%s returning: called with small datalen %zu", __FUNCTION__, datalen); |
| return; |
| } |
| |
| DataCall call; |
| int tech = atoi(details[0]); |
| switch (tech) { |
| case 0: |
| case 2 + RADIO_TECH_1xRTT: |
| call.technology_ = RADIO_TECH_1xRTT; |
| break; |
| |
| case 1: |
| case 2 + RADIO_TECH_EDGE: |
| call.technology_ = RADIO_TECH_EDGE; |
| break; |
| |
| default: |
| call.technology_ = RIL_RadioTechnology(tech - 2); |
| break; |
| } |
| |
| int profile = atoi(details[1]); |
| call.profile_ = RIL_DataProfile(profile); |
| |
| if (details[2]) call.access_point_ = details[2]; |
| if (details[3]) call.username_ = details[3]; |
| if (details[4]) call.password_ = details[4]; |
| |
| int auth_type = atoi(details[5]); |
| call.auth_type_ = DataCall::AllowedAuthenticationType(auth_type); |
| |
| if (!strcmp("IP", details[6])) { |
| call.connection_type_ = DataCall::kConnTypeIPv4; |
| } else if (!strcmp("IPV6", details[6])) { |
| call.connection_type_ = DataCall::kConnTypeIPv6; |
| } else if (!strcmp("IPV4V6", details[6])) { |
| call.connection_type_ = DataCall::kConnTypeIPv4v6; |
| } else if (!strcmp("PPP", details[6])) { |
| call.connection_type_ = DataCall::kConnTypePPP; |
| } else { |
| ALOGW("Unknown / unsupported connection type %s. Falling back to IPv4", |
| details[6]); |
| call.connection_type_ = DataCall::kConnTypeIPv4; |
| } |
| |
| if (call.connection_type_ != DataCall::kConnTypeIPv4) { |
| ALOGE("Non-IPv4 connections are not supported by Cuttlefish RIL."); |
| gce_ril_env->OnRequestComplete(t, RIL_E_INVALID_ARGUMENTS, NULL, 0); |
| return; |
| } |
| |
| call.link_state_ = DataCall::kLinkStateUp; |
| call.fail_cause_ = PDP_FAIL_NONE; |
| if (fields > 7) { |
| if (details[7]) call.other_properties_ = details[7]; |
| } |
| |
| if (gDataCalls.empty()) { |
| SetUpNetworkInterface(global_ril_config->ril_ipaddr(), |
| global_ril_config->ril_prefixlen(), |
| global_ril_config->ril_broadcast()); |
| } |
| |
| gDataCalls[gNextDataCallId] = call; |
| gNextDataCallId++; |
| |
| ALOGV("Requesting data call setup to APN %s, technology %s, prof %s", |
| details[2], details[0], details[1]); |
| |
| request_or_send_data_calllist(&t); |
| |
| gRilConnected = (gDataCalls.size() > 0); |
| } |
| |
| static void request_teardown_data_call(void* data, size_t /*datalen*/, |
| RIL_Token t) { |
| char** data_strs = (char**)data; |
| int call_id = atoi(data_strs[0]); |
| int reason = atoi(data_strs[1]); |
| |
| ALOGV("Tearing down data call %d, reason: %d", call_id, reason); |
| |
| gDataCalls.erase(call_id); |
| gRilConnected = (gDataCalls.size() > 0); |
| |
| if (!gRilConnected) { |
| TearDownNetworkInterface(); |
| } |
| gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, NULL, 0); |
| } |
| |
| static void set_radio_state(RIL_RadioState new_state, RIL_Token t) { |
| // From header: |
| // Toggle radio on and off (for "airplane" mode) |
| // If the radio is is turned off/on the radio modem subsystem |
| // is expected return to an initialized state. For instance, |
| // any voice and data calls will be terminated and all associated |
| // lists emptied. |
| gDataCalls.clear(); |
| |
| gSimStatus = SIM_NOT_READY; |
| ALOGV("RIL_RadioState change %d to %d", gRadioPowerState, new_state); |
| gRadioPowerState = new_state; |
| |
| if (new_state == RADIO_STATE_OFF) { |
| TearDownNetworkInterface(); |
| } |
| |
| if (t != NULL) { |
| gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, NULL, 0); |
| } |
| |
| gce_ril_env->OnUnsolicitedResponse(RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED, |
| NULL, 0); |
| |
| pollSIMState(NULL); |
| } |
| |
| // returns 1 if on, 0 if off, and -1 on error |
| static void request_radio_power(void* data, size_t /*datalen*/, RIL_Token t) { |
| int on = ((int*)data)[0]; |
| set_radio_state(on ? RADIO_STATE_ON : RADIO_STATE_OFF, t); |
| } |
| |
| // TODO(ender): this should be a class member. Move where it belongs. |
| struct CallState { |
| RIL_CallState state; // e.g. RIL_CALL_HOLDING; |
| bool isInternational; |
| bool isMobileTerminated; |
| bool isVoice; |
| bool isMultiParty; |
| |
| std::string number; |
| std::string name; |
| std::string dtmf; |
| |
| bool canPresentNumber; |
| bool canPresentName; |
| |
| CallState() |
| : state(RIL_CallState(0)), |
| isInternational(false), |
| isMobileTerminated(true), |
| isVoice(true), |
| isMultiParty(false), |
| canPresentNumber(true), |
| canPresentName(true) {} |
| |
| CallState(const std::string& number) |
| : state(RIL_CALL_INCOMING), |
| isInternational(false), |
| isMobileTerminated(true), |
| isVoice(true), |
| isMultiParty(false), |
| number(number), |
| name(number), |
| canPresentNumber(true), |
| canPresentName(true) {} |
| |
| bool isBackground() { return state == RIL_CALL_HOLDING; } |
| |
| bool isActive() { return state == RIL_CALL_ACTIVE; } |
| |
| bool isDialing() { return state == RIL_CALL_DIALING; } |
| |
| bool isIncoming() { return state == RIL_CALL_INCOMING; } |
| |
| bool isWaiting() { return state == RIL_CALL_WAITING; } |
| |
| void addDtmfDigit(char c) { |
| dtmf.push_back(c); |
| ALOGV("Call to %s: DTMF %s", number.c_str(), dtmf.c_str()); |
| } |
| |
| bool makeBackground() { |
| if (state == RIL_CALL_ACTIVE) { |
| state = RIL_CALL_HOLDING; |
| return true; |
| } |
| |
| return false; |
| } |
| |
| bool makeActive() { |
| if (state == RIL_CALL_INCOMING || state == RIL_CALL_WAITING || |
| state == RIL_CALL_DIALING || state == RIL_CALL_HOLDING) { |
| state = RIL_CALL_ACTIVE; |
| return true; |
| } |
| |
| return false; |
| } |
| }; |
| |
| static int gLastActiveCallIndex = 1; |
| static int gMicrophoneMute = 0; |
| static std::map<int, CallState> gActiveCalls; |
| |
| static void request_get_current_calls(void* /*data*/, size_t /*datalen*/, |
| RIL_Token t) { |
| const int countCalls = gActiveCalls.size(); |
| |
| RIL_Call** pp_calls = (RIL_Call**)alloca(countCalls * sizeof(RIL_Call*)); |
| RIL_Call* p_calls = (RIL_Call*)alloca(countCalls * sizeof(RIL_Call)); |
| |
| memset(p_calls, 0, countCalls * sizeof(RIL_Call)); |
| |
| /* init the pointer array */ |
| for (int i = 0; i < countCalls; i++) { |
| pp_calls[i] = &(p_calls[i]); |
| } |
| |
| // TODO(ender): This should be built from calls requested via RequestDial. |
| for (std::map<int, CallState>::iterator iter = gActiveCalls.begin(); |
| iter != gActiveCalls.end(); ++iter, ++p_calls) { |
| p_calls->state = iter->second.state; |
| p_calls->index = iter->first; |
| p_calls->toa = iter->second.isInternational ? 145 : 129; |
| p_calls->isMpty = iter->second.isMultiParty; |
| p_calls->isMT = iter->second.isMobileTerminated; |
| p_calls->als = iter->first; |
| p_calls->isVoice = iter->second.isVoice; |
| p_calls->isVoicePrivacy = 0; |
| p_calls->number = strdup(iter->second.number.c_str()); |
| p_calls->numberPresentation = iter->second.canPresentNumber ? 0 : 1; |
| p_calls->name = strdup(iter->second.name.c_str()); |
| p_calls->namePresentation = iter->second.canPresentName ? 0 : 1; |
| p_calls->uusInfo = NULL; |
| |
| ALOGV("Call to %s (%s): voice=%d mt=%d type=%d state=%d index=%d", |
| p_calls->name, p_calls->number, p_calls->isVoice, p_calls->isMT, |
| p_calls->toa, p_calls->state, p_calls->index); |
| } |
| |
| gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, pp_calls, |
| countCalls * sizeof(RIL_Call*)); |
| |
| ALOGV("Get Current calls: %d calls found.\n", countCalls); |
| } |
| |
| static void simulate_pending_calls_answered(void* /*ignore*/) { |
| ALOGV("Simulating outgoing call answered."); |
| // This also resumes held calls. |
| for (std::map<int, CallState>::iterator iter = gActiveCalls.begin(); |
| iter != gActiveCalls.end(); ++iter) { |
| if (iter->second.isDialing()) { |
| iter->second.makeActive(); |
| } |
| } |
| |
| // Only unsolicited here. |
| gce_ril_env->OnUnsolicitedResponse(RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED, |
| NULL, 0); |
| } |
| |
| static void request_dial(void* data, size_t /*datalen*/, RIL_Token t) { |
| RIL_Dial* p_dial = (RIL_Dial*)data; |
| |
| ALOGV("Dialing %s, number presentation is %s.", p_dial->address, |
| (p_dial->clir == 0) ? "defined by operator" |
| : (p_dial->clir == 1) ? "allowed" : "restricted"); |
| |
| CallState state(p_dial->address); |
| state.isMobileTerminated = false; |
| state.state = RIL_CALL_DIALING; |
| |
| switch (p_dial->clir) { |
| case 0: // default |
| case 1: // allow |
| state.canPresentNumber = true; |
| break; |
| |
| case 2: // restrict |
| state.canPresentNumber = false; |
| break; |
| } |
| |
| int call_index = gLastActiveCallIndex++; |
| gActiveCalls[call_index] = state; |
| |
| static const struct timeval kAnswerTime = {5, 0}; |
| gce_ril_env->RequestTimedCallback(simulate_pending_calls_answered, NULL, |
| &kAnswerTime); |
| |
| // success or failure is ignored by the upper layer here. |
| // it will call GET_CURRENT_CALLS and determine success that way |
| gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, NULL, 0); |
| } |
| |
| void request_set_mute(void* data, size_t /*datalen*/, RIL_Token t) { |
| gMicrophoneMute = ((int*)data)[0] != 0; |
| gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, NULL, 0); |
| } |
| |
| void request_get_mute(RIL_Token t) { |
| gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, &gMicrophoneMute, |
| sizeof(gMicrophoneMute)); |
| } |
| |
| // TODO(ender): this should be a class member. Move where it belongs. |
| struct SmsMessage { |
| enum SmsStatus { kUnread = 0, kRead = 1, kUnsent = 2, kSent = 3 }; |
| |
| std::string message; |
| SmsStatus status; |
| }; |
| |
| static int gNextMessageId = 1; |
| static std::map<int, SmsMessage> gMessagesOnSimCard; |
| |
| static void request_write_sms_to_sim(void* data, size_t /*datalen*/, |
| RIL_Token t) { |
| RIL_SMS_WriteArgs* p_args = (RIL_SMS_WriteArgs*)data; |
| |
| SmsMessage message; |
| message.status = SmsMessage::SmsStatus(p_args->status); |
| message.message = p_args->pdu; |
| |
| ALOGV("Storing SMS message: '%s' with state: %s.", message.message.c_str(), |
| (message.status < SmsMessage::kUnsent) |
| ? ((message.status == SmsMessage::kRead) ? "READ" : "UNREAD") |
| : ((message.status == SmsMessage::kSent) ? "SENT" : "UNSENT")); |
| |
| // TODO(ender): simulate SIM FULL? |
| int index = gNextMessageId++; |
| gMessagesOnSimCard[index] = message; |
| |
| gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, &index, sizeof(index)); |
| } |
| |
| static void request_delete_sms_on_sim(void* data, size_t /*datalen*/, |
| RIL_Token t) { |
| int index = *(int*)data; |
| |
| ALOGV("Delete SMS message %d", index); |
| |
| if (gMessagesOnSimCard.erase(index) == 0) { |
| // No such message |
| gce_ril_env->OnRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0); |
| return; |
| } |
| |
| gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, NULL, 0); |
| } |
| |
| static void request_hangup(void* data, size_t /*datalen*/, RIL_Token t) { |
| int* p_line = (int*)data; |
| |
| ALOGV("Hanging up call %d.", *p_line); |
| std::map<int, CallState>::iterator iter = gActiveCalls.find(*p_line); |
| |
| if (iter == gActiveCalls.end()) { |
| ALOGV("No such call: %d.", *p_line); |
| gce_ril_env->OnRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0); |
| } else { |
| gActiveCalls.erase(iter); |
| gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, NULL, 0); |
| } |
| } |
| |
| static void request_hangup_waiting(void* /*data*/, size_t /*datalen*/, |
| RIL_Token t) { |
| ALOGV("Hanging up background/held calls."); |
| for (std::map<int, CallState>::iterator iter = gActiveCalls.begin(); |
| iter != gActiveCalls.end();) { |
| if (iter->second.isBackground()) { |
| // C++98 -- std::map::erase doesn't return iterator. |
| std::map<int, CallState>::iterator temp = iter++; |
| gActiveCalls.erase(temp); |
| } else { |
| ++iter; |
| } |
| } |
| gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, NULL, 0); |
| } |
| |
| static void request_hangup_current(RIL_Token t) { |
| ALOGV("Hanging up foreground/active calls."); |
| // This also resumes held calls. |
| for (std::map<int, CallState>::iterator iter = gActiveCalls.begin(); |
| iter != gActiveCalls.end();) { |
| if (iter->second.isBackground()) { |
| iter->second.makeActive(); |
| ++iter; |
| } else { |
| // C++98 -- std::map::erase doesn't return iterator. |
| std::map<int, CallState>::iterator temp = iter++; |
| gActiveCalls.erase(temp); |
| } |
| } |
| gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, NULL, 0); |
| } |
| |
| static void request_switch_current_and_waiting(RIL_Token t) { |
| ALOGV("Toggle foreground and background calls."); |
| // TODO(ender): fix all states. Max 2 calls. |
| // BEFORE AFTER |
| // Call 1 Call 2 Call 1 Call 2 |
| // ACTIVE HOLDING HOLDING ACTIVE |
| // ACTIVE WAITING HOLDING ACTIVE |
| // HOLDING WAITING HOLDING ACTIVE |
| // ACTIVE IDLE HOLDING IDLE |
| // IDLE IDLE IDLE IDLE |
| for (std::map<int, CallState>::iterator iter = gActiveCalls.begin(); |
| iter != gActiveCalls.end(); ++iter) { |
| // TODO(ender): call could also be waiting or dialing or... |
| if (iter->second.isBackground()) { |
| iter->second.makeActive(); |
| } else { |
| iter->second.makeBackground(); |
| } |
| } |
| gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, NULL, 0); |
| } |
| |
| static void request_answer_incoming(RIL_Token t) { |
| ALOGV("Answering incoming call."); |
| |
| // There's two types of incoming calls: |
| // - incoming: we are receiving this call while nothing happens, |
| // - waiting: we are receiving this call while we're already talking. |
| // We only accept the incoming ones. |
| for (std::map<int, CallState>::iterator iter = gActiveCalls.begin(); |
| iter != gActiveCalls.end(); ++iter) { |
| if (iter->second.isIncoming()) { |
| iter->second.makeActive(); |
| } |
| } |
| gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, NULL, 0); |
| } |
| |
| static void request_combine_multiparty_call(void* /*data*/, size_t /*datalen*/, |
| RIL_Token t) { |
| ALOGW("Conference calls are not supported."); |
| gce_ril_env->OnRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0); |
| } |
| |
| static void request_split_multiparty_call(void* /*data*/, size_t /*datalen*/, |
| RIL_Token t) { |
| ALOGW("Conference calls are not supported."); |
| gce_ril_env->OnRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0); |
| } |
| |
| static void request_udub_on_incoming_calls(RIL_Token t) { |
| // UDUB = user determined user busy. |
| // We don't exactly do that. We simply drop these calls. |
| ALOGV("Reporting busy signal to incoming calls."); |
| for (std::map<int, CallState>::iterator iter = gActiveCalls.begin(); |
| iter != gActiveCalls.end();) { |
| // If we have an incoming call, there should be no waiting call. |
| // If we have a waiting call, then previous incoming call has been answered. |
| if (iter->second.isIncoming() || iter->second.isWaiting()) { |
| // C++98 -- std::map::erase doesn't return iterator. |
| std::map<int, CallState>::iterator temp = iter++; |
| gActiveCalls.erase(temp); |
| } else { |
| ++iter; |
| } |
| } |
| gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, NULL, 0); |
| } |
| |
| static void request_send_dtmf(void* data, size_t /*datalen*/, RIL_Token t) { |
| char c = ((char*)data)[0]; |
| ALOGV("Sending DTMF digit '%c'", c); |
| |
| for (std::map<int, CallState>::iterator iter = gActiveCalls.begin(); |
| iter != gActiveCalls.end(); ++iter) { |
| if (iter->second.isActive()) { |
| iter->second.addDtmfDigit(c); |
| break; |
| } |
| } |
| |
| gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, NULL, 0); |
| } |
| |
| static void request_send_dtmf_stop(RIL_Token t) { |
| ALOGV("DTMF tone end."); |
| |
| gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, NULL, 0); |
| } |
| |
| // Check SignalStrength.java file for more details on how these map to signal |
| // strength bars. |
| const int kGatewaySignalStrengthMin = 4; |
| const int kGatewaySignalStrengthMax = 30; |
| const int kCDMASignalStrengthMin = -110; |
| const int kCDMASignalStrengthMax = -60; |
| const int kEVDOSignalStrengthMin = -160; |
| const int kEVDOSignalStrengthMax = -70; |
| const int kLTESignalStrengthMin = 4; |
| const int kLTESignalStrengthMax = 30; |
| |
| static int gGatewaySignalStrength = kGatewaySignalStrengthMax; |
| static int gCDMASignalStrength = kCDMASignalStrengthMax; |
| static int gEVDOSignalStrength = kEVDOSignalStrengthMax; |
| static int gLTESignalStrength = kLTESignalStrengthMax; |
| |
| static void request_signal_strength(void* /*data*/, size_t /*datalen*/, |
| RIL_Token t) { |
| // TODO(ender): possible to support newer APIs here. |
| RIL_SignalStrength_v10 strength; |
| |
| gGatewaySignalStrength += (rand() % 3 - 1); |
| gCDMASignalStrength += (rand() % 3 - 1); |
| gEVDOSignalStrength += (rand() % 3 - 1); |
| gLTESignalStrength += (rand() % 3 - 1); |
| |
| if (gGatewaySignalStrength < kGatewaySignalStrengthMin) |
| gGatewaySignalStrength = kGatewaySignalStrengthMin; |
| if (gGatewaySignalStrength > kGatewaySignalStrengthMax) |
| gGatewaySignalStrength = kGatewaySignalStrengthMax; |
| if (gCDMASignalStrength < kCDMASignalStrengthMin) |
| gCDMASignalStrength = kCDMASignalStrengthMin; |
| if (gCDMASignalStrength > kCDMASignalStrengthMax) |
| gCDMASignalStrength = kCDMASignalStrengthMax; |
| if (gEVDOSignalStrength < kEVDOSignalStrengthMin) |
| gEVDOSignalStrength = kEVDOSignalStrengthMin; |
| if (gEVDOSignalStrength > kEVDOSignalStrengthMax) |
| gEVDOSignalStrength = kEVDOSignalStrengthMax; |
| if (gLTESignalStrength < kLTESignalStrengthMin) |
| gLTESignalStrength = kLTESignalStrengthMin; |
| if (gLTESignalStrength > kLTESignalStrengthMax) |
| gLTESignalStrength = kLTESignalStrengthMax; |
| |
| strength.GW_SignalStrength.signalStrength = gGatewaySignalStrength; |
| strength.GW_SignalStrength.bitErrorRate = 0; // 0..7% |
| |
| strength.CDMA_SignalStrength.dbm = gCDMASignalStrength; |
| strength.CDMA_SignalStrength.ecio = 0; // Ec/Io; keep high to use dbm. |
| |
| strength.EVDO_SignalStrength.dbm = gEVDOSignalStrength; |
| strength.EVDO_SignalStrength.ecio = 0; // Ec/Io; keep high to use dbm. |
| |
| strength.LTE_SignalStrength.signalStrength = gLTESignalStrength; |
| strength.LTE_SignalStrength.rsrp = INT_MAX; // Invalid = Use signalStrength. |
| strength.LTE_SignalStrength.rsrq = INT_MAX; // Invalid = Use signalStrength. |
| strength.LTE_SignalStrength.rssnr = INT_MAX; // Invalid = Use signalStrength. |
| strength.LTE_SignalStrength.cqi = INT_MAX; // Invalid = Use signalStrength. |
| |
| ALOGV("Reporting signal strength: GW=%d CDMA=%d EVDO=%d LTE=%d", |
| gGatewaySignalStrength, gCDMASignalStrength, gEVDOSignalStrength, |
| gLTESignalStrength); |
| |
| gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, &strength, sizeof(strength)); |
| } |
| |
| static std::map<RIL_PreferredNetworkType, int> gModemSupportedNetworkTypes; |
| |
| static void init_modem_supported_network_types() { |
| gModemSupportedNetworkTypes[PREF_NET_TYPE_GSM_WCDMA] = MDM_GSM | MDM_WCDMA; |
| gModemSupportedNetworkTypes[PREF_NET_TYPE_GSM_ONLY] = MDM_GSM; |
| gModemSupportedNetworkTypes[PREF_NET_TYPE_WCDMA] = MDM_WCDMA; |
| gModemSupportedNetworkTypes[PREF_NET_TYPE_GSM_WCDMA_AUTO] = |
| MDM_GSM | MDM_WCDMA; |
| gModemSupportedNetworkTypes[PREF_NET_TYPE_CDMA_EVDO_AUTO] = |
| MDM_CDMA | MDM_EVDO; |
| gModemSupportedNetworkTypes[PREF_NET_TYPE_CDMA_ONLY] = MDM_CDMA; |
| gModemSupportedNetworkTypes[PREF_NET_TYPE_EVDO_ONLY] = MDM_EVDO; |
| gModemSupportedNetworkTypes[PREF_NET_TYPE_GSM_WCDMA_CDMA_EVDO_AUTO] = |
| MDM_GSM | MDM_WCDMA | MDM_CDMA | MDM_EVDO; |
| gModemSupportedNetworkTypes[PREF_NET_TYPE_LTE_CDMA_EVDO] = |
| MDM_LTE | MDM_CDMA | MDM_EVDO; |
| gModemSupportedNetworkTypes[PREF_NET_TYPE_LTE_GSM_WCDMA] = |
| MDM_LTE | MDM_GSM | MDM_WCDMA; |
| gModemSupportedNetworkTypes[PREF_NET_TYPE_LTE_CMDA_EVDO_GSM_WCDMA] = |
| MDM_LTE | MDM_CDMA | MDM_EVDO | MDM_GSM | MDM_WCDMA; |
| gModemSupportedNetworkTypes[PREF_NET_TYPE_LTE_ONLY] = MDM_LTE; |
| } |
| |
| static std::map<RIL_PreferredNetworkType, int> gModemTechnologies; |
| |
| RIL_RadioTechnology gDataTechnologiesPreferenceOrder[] = { |
| RADIO_TECH_LTE, RADIO_TECH_EHRPD, RADIO_TECH_HSPAP, RADIO_TECH_HSPA, |
| RADIO_TECH_HSDPA, RADIO_TECH_HSUPA, RADIO_TECH_EVDO_B, RADIO_TECH_EVDO_A, |
| RADIO_TECH_EVDO_0, RADIO_TECH_1xRTT, RADIO_TECH_UMTS, RADIO_TECH_EDGE, |
| RADIO_TECH_GPRS}; |
| |
| RIL_RadioTechnology gVoiceTechnologiesPreferenceOrder[] = { |
| RADIO_TECH_LTE, RADIO_TECH_EHRPD, RADIO_TECH_EVDO_B, RADIO_TECH_EVDO_A, |
| RADIO_TECH_EVDO_0, RADIO_TECH_1xRTT, RADIO_TECH_IS95B, RADIO_TECH_IS95A, |
| RADIO_TECH_UMTS, RADIO_TECH_GSM}; |
| |
| static void init_modem_technologies() { |
| gModemTechnologies[PREF_NET_TYPE_GSM_WCDMA] = |
| (1 << RADIO_TECH_GSM) | (1 << RADIO_TECH_GPRS) | (1 << RADIO_TECH_EDGE) | |
| (1 << RADIO_TECH_UMTS); |
| gModemTechnologies[PREF_NET_TYPE_GSM_ONLY] = |
| (1 << RADIO_TECH_GSM) | (1 << RADIO_TECH_GPRS) | (1 << RADIO_TECH_EDGE); |
| gModemTechnologies[PREF_NET_TYPE_WCDMA] = |
| (1 << RADIO_TECH_EDGE) | (1 << RADIO_TECH_UMTS); |
| gModemTechnologies[PREF_NET_TYPE_GSM_WCDMA_AUTO] = |
| (1 << RADIO_TECH_GSM) | (1 << RADIO_TECH_GPRS) | (1 << RADIO_TECH_EDGE) | |
| (1 << RADIO_TECH_UMTS); |
| gModemTechnologies[PREF_NET_TYPE_CDMA_EVDO_AUTO] = |
| (1 << RADIO_TECH_IS95A) | (1 << RADIO_TECH_IS95B) | |
| (1 << RADIO_TECH_1xRTT) | (1 << RADIO_TECH_EVDO_0) | |
| (1 << RADIO_TECH_EVDO_A) | (1 << RADIO_TECH_HSDPA) | |
| (1 << RADIO_TECH_HSUPA) | (1 << RADIO_TECH_HSPA) | |
| (1 << RADIO_TECH_EVDO_B); |
| gModemTechnologies[PREF_NET_TYPE_CDMA_ONLY] = (1 << RADIO_TECH_IS95A) | |
| (1 << RADIO_TECH_IS95B) | |
| (1 << RADIO_TECH_1xRTT); |
| gModemTechnologies[PREF_NET_TYPE_EVDO_ONLY] = |
| (1 << RADIO_TECH_EVDO_0) | (1 << RADIO_TECH_EVDO_A) | |
| (1 << RADIO_TECH_EVDO_A) | (1 << RADIO_TECH_HSDPA) | |
| (1 << RADIO_TECH_HSUPA) | (1 << RADIO_TECH_HSPA) | |
| (1 << RADIO_TECH_EVDO_B); |
| gModemTechnologies[PREF_NET_TYPE_GSM_WCDMA_CDMA_EVDO_AUTO] = |
| (1 << RADIO_TECH_GSM) | (1 << RADIO_TECH_GPRS) | (1 << RADIO_TECH_EDGE) | |
| (1 << RADIO_TECH_UMTS) | (1 << RADIO_TECH_IS95A) | |
| (1 << RADIO_TECH_IS95B) | (1 << RADIO_TECH_1xRTT) | |
| (1 << RADIO_TECH_EVDO_0) | (1 << RADIO_TECH_EVDO_A) | |
| (1 << RADIO_TECH_HSDPA) | (1 << RADIO_TECH_HSUPA) | |
| (1 << RADIO_TECH_HSPA) | (1 << RADIO_TECH_EVDO_B); |
| gModemTechnologies[PREF_NET_TYPE_LTE_CDMA_EVDO] = |
| (1 << RADIO_TECH_HSPAP) | (1 << RADIO_TECH_LTE) | |
| (1 << RADIO_TECH_EHRPD) | (1 << RADIO_TECH_IS95A) | |
| (1 << RADIO_TECH_IS95B) | (1 << RADIO_TECH_1xRTT) | |
| (1 << RADIO_TECH_EVDO_0) | (1 << RADIO_TECH_EVDO_A) | |
| (1 << RADIO_TECH_HSDPA) | (1 << RADIO_TECH_HSUPA) | |
| (1 << RADIO_TECH_HSPA) | (1 << RADIO_TECH_EVDO_B); |
| gModemTechnologies[PREF_NET_TYPE_LTE_GSM_WCDMA] = |
| (1 << RADIO_TECH_HSPAP) | (1 << RADIO_TECH_LTE) | |
| (1 << RADIO_TECH_EHRPD) | (1 << RADIO_TECH_GSM) | (1 << RADIO_TECH_GPRS) | |
| (1 << RADIO_TECH_EDGE) | (1 << RADIO_TECH_UMTS); |
| |
| gModemTechnologies[PREF_NET_TYPE_LTE_CMDA_EVDO_GSM_WCDMA] = |
| (1 << RADIO_TECH_HSPAP) | (1 << RADIO_TECH_LTE) | |
| (1 << RADIO_TECH_EHRPD) | (1 << RADIO_TECH_IS95A) | |
| (1 << RADIO_TECH_IS95B) | (1 << RADIO_TECH_1xRTT) | |
| (1 << RADIO_TECH_EVDO_0) | (1 << RADIO_TECH_EVDO_A) | |
| (1 << RADIO_TECH_HSDPA) | (1 << RADIO_TECH_HSUPA) | |
| (1 << RADIO_TECH_HSPA) | (1 << RADIO_TECH_EVDO_B) | |
| (1 << RADIO_TECH_GSM) | (1 << RADIO_TECH_GPRS) | (1 << RADIO_TECH_EDGE) | |
| (1 << RADIO_TECH_UMTS); |
| gModemTechnologies[PREF_NET_TYPE_LTE_ONLY] = |
| (1 << RADIO_TECH_HSPAP) | (1 << RADIO_TECH_LTE) | (1 << RADIO_TECH_EHRPD); |
| } |
| |
| static const RIL_PreferredNetworkType gModemDefaultType = |
| PREF_NET_TYPE_LTE_GSM_WCDMA; |
| static RIL_PreferredNetworkType gModemCurrentType = gModemDefaultType; |
| static RIL_RadioTechnology gModemVoiceTechnology = RADIO_TECH_LTE; |
| |
| // Report technology change. |
| // Select best technology from the list of supported techs. |
| // Demotes RADIO_TECH_GSM as it's voice-only. |
| static RIL_RadioTechnology getBestDataTechnology( |
| RIL_PreferredNetworkType network_type) { |
| RIL_RadioTechnology technology = RADIO_TECH_GPRS; |
| |
| std::map<RIL_PreferredNetworkType, int>::iterator iter = |
| gModemTechnologies.find(network_type); |
| |
| ALOGV("Searching for best data technology for network type %d...", |
| network_type); |
| |
| // Find which technology bits are lit. Pick the top most. |
| for (size_t tech_index = 0; |
| tech_index < sizeof(gDataTechnologiesPreferenceOrder) / |
| sizeof(gDataTechnologiesPreferenceOrder[0]); |
| ++tech_index) { |
| if (iter->second & (1 << gDataTechnologiesPreferenceOrder[tech_index])) { |
| technology = gDataTechnologiesPreferenceOrder[tech_index]; |
| break; |
| } |
| } |
| |
| ALOGV("Best data technology: %d.", technology); |
| return technology; |
| } |
| |
| static RIL_RadioTechnology getBestVoiceTechnology( |
| RIL_PreferredNetworkType network_type) { |
| RIL_RadioTechnology technology = RADIO_TECH_GSM; |
| |
| std::map<RIL_PreferredNetworkType, int>::iterator iter = |
| gModemTechnologies.find(network_type); |
| |
| ALOGV("Searching for best voice technology for network type %d...", |
| network_type); |
| |
| // Find which technology bits are lit. Pick the top most. |
| for (size_t tech_index = 0; |
| tech_index < sizeof(gVoiceTechnologiesPreferenceOrder) / |
| sizeof(gVoiceTechnologiesPreferenceOrder[0]); |
| ++tech_index) { |
| if (iter->second & (1 << gVoiceTechnologiesPreferenceOrder[tech_index])) { |
| technology = gVoiceTechnologiesPreferenceOrder[tech_index]; |
| break; |
| } |
| } |
| |
| ALOGV("Best voice technology: %d.", technology); |
| return technology; |
| } |
| |
| static void setRadioTechnology(RIL_PreferredNetworkType network_type) { |
| RIL_RadioTechnology technology = getBestVoiceTechnology(network_type); |
| |
| if (technology != gModemVoiceTechnology) { |
| gModemVoiceTechnology = technology; |
| gce_ril_env->OnUnsolicitedResponse(RIL_UNSOL_VOICE_RADIO_TECH_CHANGED, |
| &gModemVoiceTechnology, |
| sizeof(gModemVoiceTechnology)); |
| } |
| } |
| |
| static void request_get_radio_capability(RIL_Token t) { |
| ALOGV("Requesting radio capability."); |
| RIL_RadioCapability rc; |
| rc.version = RIL_RADIO_CAPABILITY_VERSION; |
| rc.session = 1; |
| rc.phase = RC_PHASE_CONFIGURED; |
| rc.rat = RAF_HSPAP; |
| strncpy(rc.logicalModemUuid, "com.google.cvdgce1.modem", MAX_UUID_LENGTH); |
| rc.status = RC_STATUS_SUCCESS; |
| gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, &rc, sizeof(rc)); |
| } |
| |
| static void request_set_radio_capability(void* data, size_t datalen, |
| RIL_Token t) { |
| RIL_RadioCapability* rc = (RIL_RadioCapability*)data; |
| ALOGV( |
| "RadioCapability version %d session %d phase %d rat %d " |
| "logicalModemUuid %s status %d", |
| rc->version, rc->session, rc->phase, rc->rat, rc->logicalModemUuid, |
| rc->status); |
| // TODO(ender): do something about these numbers. |
| gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, rc, datalen); |
| } |
| |
| static void request_set_preferred_network_type(int /*request*/, void* data, |
| size_t /*datalen*/, |
| RIL_Token t) { |
| RIL_PreferredNetworkType desired_type = *(RIL_PreferredNetworkType*)(data); |
| |
| // TODO(ender): telephony still believes this phone is GSM only. |
| ALOGV("Requesting modem technology change -> %d", desired_type); |
| |
| if (gModemSupportedNetworkTypes.find(desired_type) == |
| gModemSupportedNetworkTypes.end()) { |
| desired_type = gModemSupportedNetworkTypes.begin()->first; |
| } |
| |
| if (gModemCurrentType == desired_type) { |
| ALOGV("Modem technology already set to %d.", desired_type); |
| gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, NULL, 0); |
| return; |
| } |
| |
| int supported_technologies = gModemSupportedNetworkTypes[gModemDefaultType]; |
| int desired_technologies = gModemSupportedNetworkTypes[desired_type]; |
| |
| ALOGV("Requesting modem technology change %d -> %d", gModemCurrentType, |
| desired_type); |
| |
| // Check if we support this technology. |
| if ((supported_technologies & desired_technologies) != desired_technologies) { |
| ALOGV("Desired technology is not supported."); |
| gce_ril_env->OnRequestComplete(t, RIL_E_MODE_NOT_SUPPORTED, NULL, 0); |
| return; |
| } |
| |
| gModemCurrentType = desired_type; |
| setRadioTechnology(desired_type); |
| ALOGV("Technology change successful."); |
| gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, NULL, 0); |
| } |
| |
| static void request_get_preferred_network_type(int /*request*/, void* /*data*/, |
| size_t /*datalen*/, |
| RIL_Token t) { |
| gce_ril_env->OnRequestComplete( |
| t, RIL_E_SUCCESS, |
| const_cast<RIL_PreferredNetworkType*>(&gModemDefaultType), |
| sizeof(gModemDefaultType)); |
| } |
| |
| enum RegistrationState { |
| kUnregistered = 0, |
| kRegisteredInHomeNetwork = 1, |
| kSearchingForOperators = 2, |
| kRegistrationDenied = 3, |
| kUnknown = 4, |
| kRegisteredInRoamingMode = 5, |
| |
| kUnregistered_EmergencyCallsOnly = 10, |
| kSearchingForOperators_EmergencyCallsOnly = 12, |
| kRegistrationDenied_EmergencyCallsOnly = 13, |
| kUnknown_EmergencyCallsOnly = 14 |
| }; |
| |
| static const char kCdmaMobileDeviceNumber[] = "5551234567"; |
| static const char kCdmaSID[] = "123"; |
| static const char kCdmaNID[] = "65535"; // special: indicates free roaming. |
| |
| static void request_registration_state(int request, void* /*data*/, |
| size_t /*datalen*/, RIL_Token t) { |
| char** responseStr = NULL; |
| int numElements = 0; |
| |
| // See RIL_REQUEST_VOICE_REGISTRATION_STATE and |
| // RIL_REQUEST_DATA_REGISTRATION_STATE. |
| numElements = (request == RIL_REQUEST_VOICE_REGISTRATION_STATE) ? 15 : 6; |
| responseStr = (char**)malloc(numElements * sizeof(char*)); |
| |
| asprintf(&responseStr[0], "%d", kRegisteredInHomeNetwork); |
| responseStr[1] = NULL; // LAC - needed for GSM / WCDMA only. |
| responseStr[2] = NULL; // CID - needed for GSM / WCDMA only. |
| |
| // This is (and always has been) a huge memory leak. |
| if (request == RIL_REQUEST_VOICE_REGISTRATION_STATE) { |
| ALOGV("Requesting voice registration state."); |
| asprintf(&responseStr[3], "%d", getBestVoiceTechnology(gModemCurrentType)); |
| responseStr[4] = strdup("1"); // BSID |
| responseStr[5] = strdup("123"); // Latitude |
| responseStr[6] = strdup("222"); // Longitude |
| responseStr[7] = strdup("0"); // CSS Indicator |
| responseStr[8] = strdup(kCdmaSID); // SID |
| responseStr[9] = strdup(kCdmaNID); // NID |
| responseStr[10] = strdup("0"); // Roaming indicator |
| responseStr[11] = strdup("1"); // System is in PRL |
| responseStr[12] = strdup("0"); // Default Roaming indicator |
| responseStr[13] = strdup("0"); // Reason for denial |
| responseStr[14] = strdup("0"); // Primary Scrambling Code of Current |
| } else if (request == RIL_REQUEST_DATA_REGISTRATION_STATE) { |
| ALOGV("Requesting data registration state."); |
| asprintf(&responseStr[3], "%d", getBestDataTechnology(gModemCurrentType)); |
| responseStr[4] = strdup(""); // DataServiceDenyReason |
| responseStr[5] = strdup("1"); // Max simultaneous data calls. |
| } else { |
| ALOGV("Unexpected request type: %d", request); |
| return; |
| } |
| |
| gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, responseStr, |
| numElements * sizeof(responseStr)); |
| } |
| |
| static void request_baseband_version(RIL_Token t) { |
| const char* response_str = "CVD_R1.0.0"; |
| |
| ALOGV("Requested phone baseband version."); |
| |
| gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, strdup(response_str), |
| sizeof(response_str)); |
| } |
| |
| // Returns true, if modem is CDMA capable. |
| static bool isCDMA() { |
| switch (gModemCurrentType) { |
| case PREF_NET_TYPE_GSM_WCDMA: |
| case PREF_NET_TYPE_GSM_ONLY: |
| case PREF_NET_TYPE_WCDMA: |
| case PREF_NET_TYPE_GSM_WCDMA_AUTO: |
| case PREF_NET_TYPE_LTE_GSM_WCDMA: |
| case PREF_NET_TYPE_LTE_ONLY: |
| return false; |
| |
| case PREF_NET_TYPE_CDMA_EVDO_AUTO: |
| case PREF_NET_TYPE_CDMA_ONLY: |
| case PREF_NET_TYPE_EVDO_ONLY: |
| case PREF_NET_TYPE_LTE_CDMA_EVDO: |
| case PREF_NET_TYPE_LTE_CMDA_EVDO_GSM_WCDMA: |
| case PREF_NET_TYPE_GSM_WCDMA_CDMA_EVDO_AUTO: |
| return true; |
| default: |
| break; |
| } |
| |
| ALOGE("INVALID MODEM TYPE: %d", gModemCurrentType); |
| return false; |
| } |
| |
| // Returns true, if modem is GSM capable. |
| // Note, this is not same as !isCDMA(). |
| static bool isGSM() { |
| switch (gModemCurrentType) { |
| case PREF_NET_TYPE_GSM_WCDMA: |
| case PREF_NET_TYPE_GSM_ONLY: |
| case PREF_NET_TYPE_WCDMA: |
| case PREF_NET_TYPE_GSM_WCDMA_AUTO: |
| case PREF_NET_TYPE_LTE_GSM_WCDMA: |
| case PREF_NET_TYPE_LTE_ONLY: |
| case PREF_NET_TYPE_GSM_WCDMA_CDMA_EVDO_AUTO: |
| return true; |
| |
| case PREF_NET_TYPE_CDMA_EVDO_AUTO: |
| case PREF_NET_TYPE_CDMA_ONLY: |
| case PREF_NET_TYPE_EVDO_ONLY: |
| case PREF_NET_TYPE_LTE_CDMA_EVDO: |
| case PREF_NET_TYPE_LTE_CMDA_EVDO_GSM_WCDMA: |
| return false; |
| default: |
| break; |
| } |
| |
| ALOGE("INVALID MODEM TYPE: %d", gModemCurrentType); |
| return false; |
| } |
| |
| static const char gIdentityGsmImei[] = "12345678902468"; // Luhn cksum = 0. |
| static const char gIdentityGsmImeiSv[] = "01"; // Arbitrary version. |
| static const char gIdentityCdmaEsn[] = "A0123456"; // 8 digits, ^[A-F].* |
| static const char gIdentityCdmaMeid[] = |
| "A0123456789012"; // 14 digits, ^[A-F].* |
| |
| static void request_get_imei(RIL_Token t) { |
| ALOGV("Requesting IMEI"); |
| gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, |
| const_cast<char*>(gIdentityGsmImei), |
| strlen(gIdentityGsmImei)); |
| } |
| |
| static void request_get_imei_sv(RIL_Token t) { |
| ALOGV("Requesting IMEI SV"); |
| gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, |
| const_cast<char*>(gIdentityGsmImeiSv), |
| strlen(gIdentityGsmImeiSv)); |
| } |
| |
| static void request_device_identity(int /*request*/, void* /*data*/, |
| size_t /*datalen*/, RIL_Token t) { |
| char* response[4] = {NULL}; |
| |
| ALOGV("Requesting device identity..."); |
| |
| if (isCDMA()) { |
| response[2] = strdup(&gIdentityCdmaEsn[0]); |
| response[3] = strdup(&gIdentityCdmaMeid[0]); |
| } |
| |
| if (isGSM()) { |
| response[0] = strdup(&gIdentityGsmImei[0]); |
| response[1] = strdup(&gIdentityGsmImeiSv[0]); |
| } |
| |
| gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, &response, sizeof(response)); |
| |
| free(response[0]); |
| free(response[1]); |
| } |
| |
| // Let's pretend we have SIM for CDMA (by default). |
| static bool gCdmaHasSim = true; |
| static RIL_CdmaSubscriptionSource gCdmaSubscriptionType = |
| CDMA_SUBSCRIPTION_SOURCE_RUIM_SIM; |
| |
| static void request_cdma_get_subscription_source(int /*request*/, |
| void* /*data*/, |
| size_t /*datalen*/, |
| RIL_Token t) { |
| ALOGV("Requesting CDMA Subscription source."); |
| |
| if (!isCDMA()) { |
| // No such radio. |
| gce_ril_env->OnRequestComplete(t, RIL_E_RADIO_NOT_AVAILABLE, NULL, 0); |
| return; |
| } |
| |
| gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, &gCdmaSubscriptionType, |
| sizeof(gCdmaSubscriptionType)); |
| } |
| |
| static void request_cdma_set_subscription_source(int /*request*/, void* data, |
| size_t /*datalen*/, |
| RIL_Token t) { |
| ALOGV("Setting CDMA Subscription source."); |
| |
| if (!isCDMA()) { |
| // No such radio. |
| gce_ril_env->OnRequestComplete(t, RIL_E_RADIO_NOT_AVAILABLE, NULL, 0); |
| return; |
| } |
| |
| RIL_CdmaSubscriptionSource new_source = *(RIL_CdmaSubscriptionSource*)(data); |
| |
| if (new_source == CDMA_SUBSCRIPTION_SOURCE_RUIM_SIM && !gCdmaHasSim) { |
| // No such radio. |
| gce_ril_env->OnRequestComplete(t, RIL_E_SIM_ABSENT, NULL, 0); |
| return; |
| } |
| |
| ALOGV("Changed CDMA subscription type from %d to %d", gCdmaSubscriptionType, |
| new_source); |
| gCdmaSubscriptionType = new_source; |
| |
| gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, NULL, 0); |
| gce_ril_env->OnUnsolicitedResponse(RIL_UNSOL_CDMA_SUBSCRIPTION_SOURCE_CHANGED, |
| &gCdmaSubscriptionType, |
| sizeof(gCdmaSubscriptionType)); |
| } |
| |
| static void request_cdma_subscription(int /*request*/, void* /*data*/, |
| size_t /*datalen*/, RIL_Token t) { |
| ALOGV("Requesting CDMA Subscription."); |
| |
| if (!isCDMA()) { |
| // No such radio. |
| gce_ril_env->OnRequestComplete(t, RIL_E_RADIO_NOT_AVAILABLE, NULL, 0); |
| return; |
| } |
| |
| char* responseStr[5] = {NULL}; |
| responseStr[0] = strdup(&kCdmaMobileDeviceNumber[0]); // MDN |
| responseStr[1] = strdup(&kCdmaSID[0]); // SID |
| responseStr[2] = strdup(&kCdmaNID[0]); // NID |
| responseStr[3] = strdup(&kCdmaMobileDeviceNumber[0]); // MIN |
| responseStr[4] = strdup("1"); // PRL Version |
| gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, responseStr, |
| sizeof(responseStr)); |
| } |
| |
| static const int gMaxConcurrentVoiceCalls = 4; |
| static const int gMaxConcurrentDataCalls = 4; |
| static const int gMaxConcurrentStandbyConnections = 4; |
| |
| static void request_hardware_config(RIL_Token t) { |
| RIL_HardwareConfig hw_cfg[2]; |
| |
| ALOGV("Requesting hardware configuration."); |
| |
| strncpy(hw_cfg[0].uuid, "com.google.cvdgce1.modem", sizeof(hw_cfg[0].uuid)); |
| strncpy(hw_cfg[1].uuid, "com.google.cvdgce1.sim", sizeof(hw_cfg[1].uuid)); |
| |
| int technologies = 0; // = unknown. |
| std::map<RIL_PreferredNetworkType, int>::iterator iter = |
| gModemTechnologies.find(gModemDefaultType); |
| if (iter != gModemTechnologies.end()) { |
| technologies = iter->second; |
| } |
| |
| hw_cfg[0].type = RIL_HARDWARE_CONFIG_MODEM; |
| hw_cfg[0].state = RIL_HARDWARE_CONFIG_STATE_ENABLED; |
| hw_cfg[0].cfg.modem.rilModel = 0; |
| hw_cfg[0].cfg.modem.rat = technologies; |
| hw_cfg[0].cfg.modem.maxVoice = gMaxConcurrentVoiceCalls; |
| hw_cfg[0].cfg.modem.maxData = gMaxConcurrentDataCalls; |
| hw_cfg[0].cfg.modem.maxStandby = gMaxConcurrentStandbyConnections; |
| |
| hw_cfg[1].type = RIL_HARDWARE_CONFIG_SIM; |
| hw_cfg[1].state = RIL_HARDWARE_CONFIG_STATE_ENABLED; |
| memcpy(hw_cfg[1].cfg.sim.modemUuid, hw_cfg[0].uuid, |
| sizeof(hw_cfg[1].cfg.sim.modemUuid)); |
| |
| gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, &hw_cfg, sizeof(hw_cfg)); |
| } |
| |
| // 0 = Home network only, 1 = preferred networks only, 2 = all networks. |
| static int gCdmaRoamingPreference = 2; |
| |
| static void request_cdma_get_roaming_preference(int /*request*/, void* /*data*/, |
| size_t /*datalen*/, |
| RIL_Token t) { |
| if (!isCDMA()) { |
| // No such radio. |
| gce_ril_env->OnRequestComplete(t, RIL_E_RADIO_NOT_AVAILABLE, NULL, 0); |
| return; |
| } |
| |
| ALOGV("Requesting CDMA Roaming preference"); |
| |
| gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, &gCdmaRoamingPreference, |
| sizeof(gCdmaRoamingPreference)); |
| } |
| |
| static void request_cdma_set_roaming_preference(int /*request*/, void* data, |
| size_t /*datalen*/, |
| RIL_Token t) { |
| if (!isCDMA()) { |
| // No such radio. |
| gce_ril_env->OnRequestComplete(t, RIL_E_RADIO_NOT_AVAILABLE, NULL, 0); |
| return; |
| } |
| |
| int pref = *(int*)data; |
| ALOGV("Changing CDMA roaming preference: %d -> %d", gCdmaRoamingPreference, |
| pref); |
| |
| if ((pref < 0) || (pref > 2)) { |
| ALOGV("Unsupported roaming preference: %d", pref); |
| gce_ril_env->OnRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0); |
| return; |
| } |
| |
| gCdmaRoamingPreference = pref; |
| gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, NULL, 0); |
| } |
| |
| static void request_send_ussd(void* /*data*/, size_t /*datalen*/, RIL_Token t) { |
| ALOGV("Sending USSD code is currently not supported"); |
| // TODO(ender): support this feature |
| gce_ril_env->OnRequestComplete(t, RIL_E_REQUEST_NOT_SUPPORTED, NULL, 0); |
| } |
| |
| static void request_cancel_ussd(RIL_Token t) { |
| ALOGV("Cancelling USSD code is currently not supported"); |
| // TODO(ender): support this feature |
| gce_ril_env->OnRequestComplete(t, RIL_E_REQUEST_NOT_SUPPORTED, NULL, 0); |
| } |
| |
| static void request_exit_emergency_mode(void* /*data*/, size_t /*datalen*/, |
| RIL_Token t) { |
| ALOGV("Exiting emergency callback mode."); |
| |
| gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, NULL, 0); |
| } |
| |
| static RIL_RadioState gce_ril_current_state() { |
| ALOGV("Reporting radio state %d", gRadioPowerState); |
| return gRadioPowerState; |
| } |
| |
| static int gce_ril_on_supports(int requestCode) { |
| ALOGE("%s: Request code %d not implemented", __FUNCTION__, requestCode); |
| return 1; |
| } |
| |
| static void gce_ril_on_cancel(RIL_Token /*t*/) { |
| ALOGE("Cancel operation not implemented"); |
| } |
| |
| static const char* gce_ril_get_version(void) { |
| ALOGV("Reporting Cuttlefish version " CUTTLEFISH_RIL_VERSION_STRING); |
| return CUTTLEFISH_RIL_VERSION_STRING; |
| } |
| |
| static int s_cell_info_rate_ms = INT_MAX; |
| static int s_mcc = 0; |
| static int s_mnc = 0; |
| static int s_lac = 0; |
| static int s_cid = 0; |
| |
| std::vector<RIL_NeighboringCell> gGSMNeighboringCells; |
| |
| static void request_get_neighboring_cell_ids(void* /*data*/, size_t /*datalen*/, |
| RIL_Token t) { |
| ALOGV("Requesting GSM neighboring cell ids"); |
| |
| if (!isGSM() || gGSMNeighboringCells.empty()) { |
| gce_ril_env->OnRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0); |
| return; |
| } |
| |
| RIL_NeighboringCell** cells = |
| new RIL_NeighboringCell*[gGSMNeighboringCells.size()]; |
| |
| for (size_t index = 0; index < gGSMNeighboringCells.size(); ++index) { |
| cells[index] = &gGSMNeighboringCells[index]; |
| } |
| |
| gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, cells, |
| sizeof(RIL_NeighboringCell*)); |
| delete[] cells; |
| } |
| |
| static void request_get_cell_info_list(void* /*data*/, size_t /*datalen*/, |
| RIL_Token t) { |
| struct timespec now; |
| uint64_t curTime; |
| |
| ALOGV("Requesting Cell Info List"); |
| |
| clock_gettime(CLOCK_MONOTONIC, &now); |
| curTime = now.tv_sec * 1000000000LL + now.tv_nsec; |
| |
| RIL_CellInfo_v12 ci; |
| |
| if (isGSM()) { |
| ci.cellInfoType = RIL_CELL_INFO_TYPE_GSM; |
| ci.registered = 1; |
| ci.timeStampType = RIL_TIMESTAMP_TYPE_ANTENNA; // Our own timestamp. |
| ci.timeStamp = curTime - 1000; // Fake time in the past. |
| ci.CellInfo.gsm.cellIdentityGsm.mcc = s_mcc; |
| ci.CellInfo.gsm.cellIdentityGsm.mnc = s_mnc; |
| ci.CellInfo.gsm.cellIdentityGsm.lac = s_lac; |
| ci.CellInfo.gsm.cellIdentityGsm.cid = s_cid; |
| ci.CellInfo.gsm.signalStrengthGsm.signalStrength = 10; |
| ci.CellInfo.gsm.signalStrengthGsm.bitErrorRate = 0; |
| |
| gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, &ci, sizeof(ci)); |
| } else if (isCDMA()) { |
| // TODO(ender): CDMA cell support. And LTE. |
| gce_ril_env->OnRequestComplete(t, RIL_E_RADIO_NOT_AVAILABLE, NULL, 0); |
| } else { |
| gce_ril_env->OnRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0); |
| } |
| } |
| |
| struct NetworkOperator { |
| std::string long_name; |
| std::string short_name; |
| bool is_accessible; |
| |
| NetworkOperator() {} |
| |
| NetworkOperator(const std::string& long_name, const std::string& short_name, |
| bool is_accessible) |
| : long_name(long_name), |
| short_name(short_name), |
| is_accessible(is_accessible) {} |
| }; |
| |
| static std::map<std::string, NetworkOperator> gNetworkOperators; |
| static std::string gCurrentNetworkOperator = ""; |
| |
| enum OperatorSelectionMethod { |
| kOperatorAutomatic = 0, |
| kOperatorManual = 1, |
| kOperatorDeregistered = 2, |
| kOperatorManualThenAutomatic = 4 |
| }; |
| |
| static void init_virtual_network() { |
| gGSMNeighboringCells.resize(1); |
| gGSMNeighboringCells[0].cid = (char*)"0000"; |
| gGSMNeighboringCells[0].rssi = 75; |
| gNetworkOperators["311740"] = |
| NetworkOperator("Android Virtual Operator", "Android", true); |
| gNetworkOperators["310300"] = |
| NetworkOperator("Alternative Operator", "Alternative", true); |
| gNetworkOperators["310400"] = |
| NetworkOperator("Hermetic Network Operator", "Hermetic", false); |
| } |
| |
| static OperatorSelectionMethod gOperatorSelectionMethod = kOperatorDeregistered; |
| |
| static void request_query_network_selection_mode(void* /*data*/, |
| size_t /*datalen*/, |
| RIL_Token t) { |
| ALOGV("Query operator selection mode (%d)", gOperatorSelectionMethod); |
| gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, &gOperatorSelectionMethod, |
| sizeof(gOperatorSelectionMethod)); |
| } |
| |
| static void request_operator(void* /*data*/, size_t /*datalen*/, RIL_Token t) { |
| std::map<std::string, NetworkOperator>::iterator iter = |
| gNetworkOperators.find(gCurrentNetworkOperator); |
| |
| ALOGV("Requesting current operator info"); |
| |
| if (iter == gNetworkOperators.end()) { |
| gce_ril_env->OnRequestComplete(t, RIL_E_RADIO_NOT_AVAILABLE, NULL, 0); |
| return; |
| } |
| |
| const char* response[] = {iter->second.long_name.c_str(), |
| iter->second.short_name.c_str(), |
| iter->first.c_str()}; |
| |
| gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, response, sizeof(response)); |
| } |
| |
| static void request_query_available_networks(void* /*data*/, size_t /*datalen*/, |
| RIL_Token t) { |
| const char** available_networks = |
| new const char*[gNetworkOperators.size() * 4]; |
| |
| ALOGV("Querying available networks."); |
| |
| // TODO(ender): this should only respond once operator is selected and |
| // registered. |
| int index = 0; |
| for (std::map<std::string, NetworkOperator>::iterator iter = |
| gNetworkOperators.begin(); |
| iter != gNetworkOperators.end(); ++iter) { |
| // TODO(ender): wrap in a neat structure maybe? |
| available_networks[index++] = iter->second.long_name.c_str(); |
| available_networks[index++] = iter->second.short_name.c_str(); |
| available_networks[index++] = iter->first.c_str(); |
| if (!iter->second.is_accessible) { |
| available_networks[index++] = "forbidden"; |
| } else if (iter->first == gCurrentNetworkOperator) { |
| available_networks[index++] = "current"; |
| } else { |
| available_networks[index++] = "available"; |
| } |
| } |
| |
| gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, &available_networks, |
| 4 * gNetworkOperators.size()); |
| delete[] available_networks; |
| } |
| |
| static void request_set_automatic_network_selection(RIL_Token t) { |
| ALOGV("Requesting automatic operator selection"); |
| gCurrentNetworkOperator = gNetworkOperators.begin()->first; |
| gOperatorSelectionMethod = kOperatorAutomatic; |
| gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, NULL, 0); |
| } |
| |
| static void request_set_manual_network_selection(void* data, size_t /*datalen*/, |
| RIL_Token t) { |
| char* mccmnc = (char*)data; |
| |
| ALOGV("Requesting manual operator selection: %s", mccmnc); |
| |
| std::map<std::string, NetworkOperator>::iterator iter = |
| gNetworkOperators.find(mccmnc); |
| |
| if (iter == gNetworkOperators.end() || iter->second.is_accessible) { |
| gce_ril_env->OnRequestComplete(t, RIL_E_ILLEGAL_SIM_OR_ME, NULL, 0); |
| return; |
| } |
| |
| gCurrentNetworkOperator = mccmnc; |
| gOperatorSelectionMethod = kOperatorManual; |
| |
| gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, NULL, 0); |
| } |
| |
| static const char kDefaultSMSC[] = "00"; |
| static int gNextSmsMessageId = 1; |
| |
| static void request_cdma_send_SMS(void* /*data*/, RIL_Token t) { |
| RIL_SMS_Response response = {0, 0, 0}; |
| // RIL_CDMA_SMS_Message* rcsm = (RIL_CDMA_SMS_Message*) data; |
| |
| ALOGW("CDMA SMS Send is currently not implemented."); |
| |
| // Cdma Send SMS implementation will go here: |
| // But it is not implemented yet. |
| memset(&response, 0, sizeof(response)); |
| response.messageRef = -1; // This must be BearerData MessageId. |
| gce_ril_env->OnRequestComplete(t, RIL_E_SMS_SEND_FAIL_RETRY, &response, |
| sizeof(response)); |
| } |
| |
| static void request_send_SMS(void* data, RIL_Token t) { |
| RIL_SMS_Response response = {0, 0, 0}; |
| |
| ALOGV("Send GSM SMS Message"); |
| |
| // SMSC is an address of SMS center or NULL for default. |
| const char* smsc = ((const char**)data)[0]; |
| if (smsc == NULL) smsc = &kDefaultSMSC[0]; |
| |
| response.messageRef = gNextSmsMessageId++; |
| response.ackPDU = NULL; |
| response.errorCode = 0; |
| |
| gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, &response, sizeof(response)); |
| |
| // response.messageRef = -1; |
| // gce_ril_env->OnRequestComplete(t, RIL_E_SMS_SEND_FAIL_RETRY, &response, |
| // sizeof(response)); |
| } |
| |
| static void request_set_cell_info_list_rate(void* data, size_t /*datalen*/, |
| RIL_Token t) { |
| // For now we'll save the rate but no RIL_UNSOL_CELL_INFO_LIST messages |
| // will be sent. |
| ALOGV("Setting cell info list rate."); |
| s_cell_info_rate_ms = ((int*)data)[0]; |
| gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, NULL, 0); |
| } |
| static void request_ims_send_SMS(void* data, size_t /*datalen*/, RIL_Token t) { |
| RIL_IMS_SMS_Message* args = (RIL_IMS_SMS_Message*)data; |
| RIL_SMS_Response response{}; |
| |
| ALOGV("Send IMS SMS Message"); |
| |
| switch (args->tech) { |
| case RADIO_TECH_3GPP: |
| return request_send_SMS(args->message.gsmMessage, t); |
| |
| case RADIO_TECH_3GPP2: |
| return request_cdma_send_SMS(args->message.gsmMessage, t); |
| |
| default: |
| ALOGE("Invalid SMS format value: %d", args->tech); |
| response.messageRef = -2; |
| gce_ril_env->OnRequestComplete(t, RIL_E_GENERIC_FAILURE, &response, |
| sizeof(response)); |
| } |
| } |
| |
| static void request_SMS_acknowledge(void* data, size_t /*datalen*/, |
| RIL_Token t) { |
| int* ack = (int*)data; |
| |
| // TODO(ender): we should retain "incoming" sms for later reception. |
| ALOGV("SMS receipt %ssuccessful (reason %d).", ack[0] ? "" : "un", ack[1]); |
| |
| gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, NULL, 0); |
| } |
| |
| struct SimFileCommand { |
| uint8_t command; |
| uint16_t efid; |
| uint8_t param1; |
| uint8_t param2; |
| uint8_t param3; |
| |
| bool operator<(const SimFileCommand& other) const { |
| uint64_t sum1, sum2; |
| sum1 = (command * 1ull << 40) | (efid * 1ull << 24) | (param1 << 16) | |
| (param2 << 8) | (param3); |
| sum2 = (other.command * 1ull << 40) | (other.efid * 1ull << 24) | |
| (other.param1 << 16) | (other.param2 << 8) | (other.param3); |
| return sum1 < sum2; |
| } |
| |
| SimFileCommand(uint8_t cmd, uint16_t efid, uint8_t p1, uint8_t p2, uint8_t p3) |
| : command(cmd), efid(efid), param1(p1), param2(p2), param3(p3) {} |
| }; |
| |
| struct SimFileResponse { |
| uint8_t sw1; |
| uint8_t sw2; |
| const char* data; |
| |
| SimFileResponse() : sw1(0), sw2(0), data(NULL) {} |
| |
| SimFileResponse(uint8_t sw1, uint8_t sw2, const char* data) |
| : sw1(sw1), sw2(sw2), data(data) {} |
| }; |
| |
| // TODO(ender): Double check & rewrite these. |
| std::map<SimFileCommand, SimFileResponse> gSimFileSystem; |
| |
| static void init_sim_file_system() { |
| gSimFileSystem[SimFileCommand(192, 28436, 0, 0, 15)] = |
| SimFileResponse(144, 0, "000000146f1404001aa0aa01020000"); |
| gSimFileSystem[SimFileCommand(176, 28436, 0, 0, 20)] = |
| SimFileResponse(144, 0, "416e64726f6964ffffffffffffffffffffffffff"); |
| gSimFileSystem[SimFileCommand(192, 28433, 0, 0, 15)] = |
| SimFileResponse(144, 0, "000000016f11040011a0aa01020000"); |
| gSimFileSystem[SimFileCommand(176, 28433, 0, 0, 1)] = |
| SimFileResponse(144, 0, "55"); |
| gSimFileSystem[SimFileCommand(192, 12258, 0, 0, 15)] = |
| SimFileResponse(144, 0, "0000000a2fe204000fa0aa01020000"); |
| gSimFileSystem[SimFileCommand(176, 12258, 0, 0, 10)] = |
| SimFileResponse(144, 0, "98101430121181157002"); |
| gSimFileSystem[SimFileCommand(192, 28435, 0, 0, 15)] = |
| SimFileResponse(144, 0, "000000016f13040011a0aa01020000"); |
| gSimFileSystem[SimFileCommand(176, 28435, 0, 0, 1)] = |
| SimFileResponse(144, 0, "55"); |
| gSimFileSystem[SimFileCommand(192, 28472, 0, 0, 15)] = |
| SimFileResponse(144, 0, "0000000f6f3804001aa0aa01020000"); |
| gSimFileSystem[SimFileCommand(176, 28472, 0, 0, 15)] = |
| SimFileResponse(144, 0, "ff30ffff3c003c03000c0000f03f00"); |
| gSimFileSystem[SimFileCommand(192, 28617, 0, 0, 15)] = |
| SimFileResponse(144, 0, "000000086fc9040011a0aa01020104"); |
| gSimFileSystem[SimFileCommand(178, 28617, 1, 4, 4)] = |
| SimFileResponse(144, 0, "01000000"); |
| gSimFileSystem[SimFileCommand(192, 28618, 0, 0, 15)] = |
| SimFileResponse(144, 0, "0000000a6fca040011a0aa01020105"); |
| gSimFileSystem[SimFileCommand(178, 28618, 1, 4, 5)] = |
| SimFileResponse(144, 0, "0000000000"); |
| gSimFileSystem[SimFileCommand(192, 28589, 0, 0, 15)] = |
| SimFileResponse(144, 0, "000000046fad04000aa0aa01020000"); |
| gSimFileSystem[SimFileCommand(176, 28589, 0, 0, 4)] = |
| SimFileResponse(144, 0, "00000003"); |
| gSimFileSystem[SimFileCommand(192, 28438, 0, 0, 15)] = |
| SimFileResponse(144, 0, "000000026f1604001aa0aa01020000"); |
| gSimFileSystem[SimFileCommand(176, 28438, 0, 0, 2)] = |
| SimFileResponse(144, 0, "0233"); |
| gSimFileSystem[SimFileCommand(192, 28486, 0, 0, 15)] = |
| SimFileResponse(148, 4, NULL); |
| gSimFileSystem[SimFileCommand(192, 28621, 0, 0, 15)] = |
| SimFileResponse(148, 4, NULL); |
| gSimFileSystem[SimFileCommand(192, 28613, 0, 0, 15)] = |
| SimFileResponse(144, 0, "000000f06fc504000aa0aa01020118"); |
| gSimFileSystem[SimFileCommand(178, 28613, 1, 4, 24)] = SimFileResponse( |
| 144, 0, "43058441aa890affffffffffffffffffffffffffffffffff"); |
| gSimFileSystem[SimFileCommand(192, 28480, 0, 0, 15)] = |
| SimFileResponse(144, 0, "000000806f40040011a0aa01020120"); |
| // Primary phone number encapsulated |
| // [51][55][21][43][65][f7] = 1 555 1234 567$ |
| gSimFileSystem[SimFileCommand(178, 28480, 1, 4, 32)] = SimFileResponse( |
| 144, 0, |
| "ffffffffffffffffffffffffffffffffffff07915155214365f7ffffffffffff"); |
| gSimFileSystem[SimFileCommand(192, 28615, 0, 0, 15)] = |
| SimFileResponse(144, 0, "000000406fc7040011a0aa01020120"); |
| // Voice mail number encapsulated |
| // [56][6f][69][63][65][6d][61][69][6c] = 'Voicemail' |
| // [51][55][67][45][23][f1] = 1 555 7654 321$ |
| gSimFileSystem[SimFileCommand(178, 28615, 1, 4, 32)] = SimFileResponse( |
| 144, 0, |
| "566f6963656d61696cffffffffffffffffff07915155674523f1ffffffffffff"); |
| gSimFileSystem[SimFileCommand(192, 12037, 0, 0, 15)] = |
| SimFileResponse(148, 4, NULL); |
| gSimFileSystem[SimFileCommand(192, 28437, 0, 0, 15)] = |
| SimFileResponse(148, 4, NULL); |
| gSimFileSystem[SimFileCommand(192, 28478, 0, 0, 15)] = |
| SimFileResponse(148, 4, NULL); |
| gSimFileSystem[SimFileCommand(192, 28450, 0, 0, 15)] = |
| SimFileResponse(148, 4, NULL); |
| gSimFileSystem[SimFileCommand(192, 28456, 0, 0, 15)] = |
| SimFileResponse(148, 4, NULL); |
| gSimFileSystem[SimFileCommand(192, 28474, 0, 0, 15)] = |
| SimFileResponse(148, 4, NULL); |
| gSimFileSystem[SimFileCommand(192, 28481, 0, 0, 15)] = |
| SimFileResponse(148, 4, NULL); |
| gSimFileSystem[SimFileCommand(192, 28484, 0, 0, 15)] = |
| SimFileResponse(148, 4, NULL); |
| gSimFileSystem[SimFileCommand(192, 28493, 0, 0, 15)] = |
| SimFileResponse(148, 4, NULL); |
| gSimFileSystem[SimFileCommand(192, 28619, 0, 0, 15)] = |
| SimFileResponse(148, 4, NULL); |
| gSimFileSystem[SimFileCommand(176, 28506, 0, 0, 4)] = |
| SimFileResponse(144, 0, "00000013"); |
| } |
| |
| static void request_SIM_IO(void* data, size_t /*datalen*/, RIL_Token t) { |
| const RIL_SIM_IO_v6& args = *(RIL_SIM_IO_v6*)data; |
| RIL_SIM_IO_Response sr = {0, 0, 0}; |
| |
| ALOGV( |
| "Requesting SIM File IO: %d EFID %x, Params: %d, %d, %d, path: %s, " |
| "data %s PIN: %s AID: %s", |
| args.command, args.fileid, args.p1, args.p2, args.p3, args.path, |
| args.data, args.pin2, args.aidPtr); |
| |
| SimFileCommand cmd(args.command, args.fileid, args.p1, args.p2, args.p3); |
| |
| std::map<SimFileCommand, SimFileResponse>::iterator resp = |
| gSimFileSystem.find(cmd); |
| |
| if (resp != gSimFileSystem.end()) { |
| sr.sw1 = resp->second.sw1; |
| sr.sw2 = resp->second.sw2; |
| if (resp->second.data) sr.simResponse = strdup(resp->second.data); |
| gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, &sr, sizeof(sr)); |
| return; |
| } |
| |
| ALOGW("Unsupported SIM File IO command."); |
| gce_ril_env->OnRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0); |
| } |
| |
| static void request_enter_sim_pin(void* data, size_t /*datalen*/, RIL_Token t) { |
| const char** pin_aid = (const char**)data; |
| |
| ALOGV("Entering PIN: %s / %s", pin_aid[0], pin_aid[1]); |
| |
| ++gSimPINAttempts; |
| int remaining_attempts = gSimPINAttemptsMax - gSimPINAttempts; |
| |
| bool is_valid = false; |
| |
| if (gSimStatus == SIM_PIN) { |
| is_valid = (gSimPIN == pin_aid[0]); |
| } else if (gSimStatus == SIM_PUK) { |
| is_valid = (gSimPUK == pin_aid[0]); |
| } else { |
| ALOGV("Unexpected SIM status for unlock: %d", gSimStatus); |
| gce_ril_env->OnRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0); |
| return; |
| } |
| |
| if (!is_valid) { |
| if (gSimPINAttempts == gSimPINAttemptsMax) { |
| if (gSimStatus == SIM_PIN) { |
| gSimStatus = SIM_PUK; |
| gSimPINAttempts = 0; |
| } else { |
| ALOGV("PIN and PUK verification failed; locking SIM card."); |
| gSimStatus = SIM_NOT_READY; |
| gce_ril_env->OnRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0); |
| return; |
| } |
| } |
| |
| gce_ril_env->OnRequestComplete(t, RIL_E_PASSWORD_INCORRECT, |
| &remaining_attempts, |
| sizeof(remaining_attempts)); |
| } else { |
| if (gSimStatus == SIM_PUK) { |
| ALOGV("Resetting SIM PIN to %s", pin_aid[1]); |
| gSimPIN = pin_aid[1]; |
| } |
| |
| gSimPINAttempts = 0; |
| gSimStatus = SIM_READY; |
| gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, &remaining_attempts, |
| sizeof(remaining_attempts)); |
| } |
| |
| pollSIMState(NULL); |
| } |
| |
| /** |
| * No longer POLL. |
| */ |
| static void pollSIMState(void* /*param*/) { |
| // TODO(ender): check radio state? |
| |
| ALOGV("Polling SIM Status."); |
| |
| switch (gSimStatus) { |
| case SIM_ABSENT: |
| case SIM_PIN: |
| case SIM_PUK: |
| case SIM_NETWORK_PERSONALIZATION: |
| default: |
| ALOGV("SIM Absent or Locked"); |
| break; |
| |
| case SIM_NOT_READY: |
| // Transition directly to READY. Set default network operator. |
| if (gRadioPowerState == RADIO_STATE_ON) { |
| gSimStatus = SIM_READY; |
| gCurrentNetworkOperator = "311740"; |
| } |
| |
| gce_ril_env->RequestTimedCallback(pollSIMState, NULL, &TIMEVAL_SIMPOLL); |
| break; |
| |
| case SIM_READY: |
| ALOGV("SIM Ready. Notifying network state changed."); |
| break; |
| } |
| |
| if (gRadioPowerState != RADIO_STATE_OFF) { |
| gce_ril_env->OnUnsolicitedResponse(RIL_UNSOL_RESPONSE_SIM_STATUS_CHANGED, |
| NULL, 0); |
| gce_ril_env->OnUnsolicitedResponse( |
| RIL_UNSOL_RESPONSE_VOICE_NETWORK_STATE_CHANGED, NULL, 0); |
| } |
| } |
| |
| std::map<SIM_Status, RIL_AppStatus> gRilAppStatus; |
| |
| static void init_sim_status() { |
| gRilAppStatus[SIM_ABSENT] = (RIL_AppStatus){RIL_APPTYPE_UNKNOWN, |
| RIL_APPSTATE_UNKNOWN, |
| RIL_PERSOSUBSTATE_UNKNOWN, |
| NULL, |
| NULL, |
| 0, |
| RIL_PINSTATE_UNKNOWN, |
| RIL_PINSTATE_UNKNOWN}; |
| gRilAppStatus[SIM_NOT_READY] = |
| (RIL_AppStatus){RIL_APPTYPE_SIM, |
| RIL_APPSTATE_DETECTED, |
| RIL_PERSOSUBSTATE_UNKNOWN, |
| NULL, |
| NULL, |
| 0, |
| RIL_PINSTATE_ENABLED_NOT_VERIFIED, |
| RIL_PINSTATE_ENABLED_NOT_VERIFIED}; |
| gRilAppStatus[SIM_READY] = (RIL_AppStatus){ |
| RIL_APPTYPE_SIM, |
| RIL_APPSTATE_READY, |
| RIL_PERSOSUBSTATE_READY, |
| NULL, |
| NULL, |
| 0, |
| RIL_PINSTATE_ENABLED_VERIFIED, |
| RIL_PINSTATE_ENABLED_VERIFIED, |
| }; |
| gRilAppStatus[SIM_PIN] = (RIL_AppStatus){RIL_APPTYPE_SIM, |
| RIL_APPSTATE_PIN, |
| RIL_PERSOSUBSTATE_UNKNOWN, |
| NULL, |
| NULL, |
| 0, |
| RIL_PINSTATE_ENABLED_NOT_VERIFIED, |
| RIL_PINSTATE_UNKNOWN}; |
| gRilAppStatus[SIM_PUK] = (RIL_AppStatus){RIL_APPTYPE_SIM, |
| RIL_APPSTATE_PUK, |
| RIL_PERSOSUBSTATE_UNKNOWN, |
| NULL, |
| NULL, |
| 0, |
| RIL_PINSTATE_ENABLED_BLOCKED, |
| RIL_PINSTATE_UNKNOWN}; |
| gRilAppStatus[SIM_NETWORK_PERSONALIZATION] = |
| (RIL_AppStatus){RIL_APPTYPE_SIM, |
| RIL_APPSTATE_SUBSCRIPTION_PERSO, |
| RIL_PERSOSUBSTATE_SIM_NETWORK, |
| NULL, |
| NULL, |
| 0, |
| RIL_PINSTATE_ENABLED_NOT_VERIFIED, |
| RIL_PINSTATE_UNKNOWN}; |
| gRilAppStatus[RUIM_ABSENT] = (RIL_AppStatus){RIL_APPTYPE_UNKNOWN, |
| RIL_APPSTATE_UNKNOWN, |
| RIL_PERSOSUBSTATE_UNKNOWN, |
| NULL, |
| NULL, |
| 0, |
| RIL_PINSTATE_UNKNOWN, |
| RIL_PINSTATE_UNKNOWN}; |
| gRilAppStatus[RUIM_NOT_READY] = (RIL_AppStatus){RIL_APPTYPE_RUIM, |
| RIL_APPSTATE_DETECTED, |
| RIL_PERSOSUBSTATE_UNKNOWN, |
| NULL, |
| NULL, |
| 0, |
| RIL_PINSTATE_UNKNOWN, |
| RIL_PINSTATE_UNKNOWN}; |
| gRilAppStatus[RUIM_READY] = (RIL_AppStatus){RIL_APPTYPE_RUIM, |
| RIL_APPSTATE_READY, |
| RIL_PERSOSUBSTATE_READY, |
| NULL, |
| NULL, |
| 0, |
| RIL_PINSTATE_UNKNOWN, |
| RIL_PINSTATE_UNKNOWN}; |
| gRilAppStatus[RUIM_PIN] = (RIL_AppStatus){RIL_APPTYPE_RUIM, |
| RIL_APPSTATE_PIN, |
| RIL_PERSOSUBSTATE_UNKNOWN, |
| NULL, |
| NULL, |
| 0, |
| RIL_PINSTATE_ENABLED_NOT_VERIFIED, |
| RIL_PINSTATE_UNKNOWN}; |
| gRilAppStatus[RUIM_PUK] = (RIL_AppStatus){RIL_APPTYPE_RUIM, |
| RIL_APPSTATE_PUK, |
| RIL_PERSOSUBSTATE_UNKNOWN, |
| NULL, |
| NULL, |
| 0, |
| RIL_PINSTATE_ENABLED_BLOCKED, |
| RIL_PINSTATE_UNKNOWN}; |
| gRilAppStatus[RUIM_NETWORK_PERSONALIZATION] = |
| (RIL_AppStatus){RIL_APPTYPE_RUIM, |
| RIL_APPSTATE_SUBSCRIPTION_PERSO, |
| RIL_PERSOSUBSTATE_SIM_NETWORK, |
| NULL, |
| NULL, |
| 0, |
| RIL_PINSTATE_ENABLED_NOT_VERIFIED, |
| RIL_PINSTATE_UNKNOWN}; |
| } |
| |
| /** |
| * Get the current card status. |
| */ |
| static void getCardStatus(RIL_Token t) { |
| ALOGV("Querying SIM status."); |
| RIL_CardStatus_v6 card_status; |
| |
| if (gSimStatus == SIM_ABSENT) { |
| card_status.card_state = RIL_CARDSTATE_ABSENT; |
| card_status.num_applications = 0; |
| } else { |
| card_status.card_state = RIL_CARDSTATE_PRESENT; |
| card_status.num_applications = 1; |
| } |
| |
| card_status.universal_pin_state = RIL_PINSTATE_UNKNOWN; |
| card_status.gsm_umts_subscription_app_index = -1; |
| card_status.cdma_subscription_app_index = -1; |
| card_status.ims_subscription_app_index = -1; |
| |
| // Initialize application status |
| for (int i = 0; i < RIL_CARD_MAX_APPS; i++) { |
| card_status.applications[i] = gRilAppStatus[SIM_ABSENT]; |
| } |
| |
| if (card_status.num_applications > 0) { |
| card_status.gsm_umts_subscription_app_index = 0; |
| |
| card_status.applications[0] = gRilAppStatus[gSimStatus]; |
| card_status.universal_pin_state = card_status.applications[0].pin1; |
| // To enable basic CDMA (currently neither supported nor functional): |
| // card_status.num_applications = 2; |
| // card_status.cdma_subscription_app_index = 1; |
| // card_status.applications[1] = |
| // gRilAppStatus[SIM_Status(gSimStatus + RUIM_ABSENT)]; |
| } |
| |
| gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, &card_status, |
| sizeof(card_status)); |
| } |
| |
| struct SimSession { |
| std::string aid; |
| }; |
| |
| static int gNextSimSessionId = 1; |
| static std::map<int, SimSession> gSimSessions; |
| |
| static void request_sim_open_channel(void* data, size_t /*datalen*/, |
| RIL_Token t) { |
| char* aid = (char*)data; |
| SimSession session; |
| |
| ALOGV("Requesting new SIM session"); |
| |
| if (aid != NULL) { |
| session.aid = aid; |
| } |
| |
| int response = gNextSimSessionId++; |
| gSimSessions[response] = session; |
| |
| gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, &response, sizeof(response)); |
| } |
| |
| static void request_sim_close_channel(void* data, size_t /*datalen*/, |
| RIL_Token t) { |
| int session = *(int*)(data); |
| |
| ALOGV("Closing SIM session %d", session); |
| |
| if (gSimSessions.erase(session) == 0) { |
| // No such session. |
| gce_ril_env->OnRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0); |
| } else { |
| gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, NULL, 0); |
| } |
| } |
| |
| static void request_sim_apdu(void* data, size_t /*datalen*/, RIL_Token t) { |
| RIL_SIM_APDU* apdu = (RIL_SIM_APDU*)data; |
| |
| ALOGV("Requesting APDU: Session %d CLA %d INST %d Params: %d %d %d, data %s", |
| apdu->sessionid, apdu->cla, apdu->instruction, apdu->p1, apdu->p2, |
| apdu->p3, apdu->data); |
| |
| if (gSimSessions.find(apdu->sessionid) != gSimSessions.end()) { |
| RIL_SIM_IO_Response sr{}; |
| |
| // Fallback / default behavior. |
| sr.sw1 = 144; |
| sr.sw2 = 0; |
| sr.simResponse = NULL; |
| |
| gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, &sr, sizeof(sr)); |
| } else { |
| gce_ril_env->OnRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0); |
| } |
| } |
| |
| // 0 = Lock is available, but disabled. |
| // 1 = Lock is available and enabled, |
| // 2 = lock is neither available nor enabled |
| static const int kFacilityLockAllDisabled = 0; |
| |
| static void request_facility_lock(void* data, size_t /*datalen*/, RIL_Token t) { |
| char** data_vec = (char**)data; |
| |
| // TODO(ender): implement this; essentially: AT+CLCK |
| // See http://www.activexperts.com/sms-component/at/commands/?at=%2BCLCK |
| // and |
| // opt/telephony/src/java/com/android/internal/telephony/CommandsInterface.java |
| // opt/telephony/src/java/com/android/internal/telephony/uicc/UiccCardApplication.java |
| |
| ALOGV("Query Facility Lock Code: %s PIN2: %s Service(s): %s AID: %s", |
| data_vec[0], data_vec[1], data_vec[2], data_vec[3]); |
| |
| // TODO(ender): there should be a bit vector of responses for each of the |
| // services requested. |
| // Depending on lock code, facilities may be unlocked or locked. We report |
| // these are all unlocked, regardless of the query. |
| gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, |
| const_cast<int*>(&kFacilityLockAllDisabled), |
| sizeof(kFacilityLockAllDisabled)); |
| } |
| |
| static void request_international_subscriber_id_number(RIL_Token t) { |
| // TODO(ender): Reuse MCC and MNC. |
| std::string subscriber_id = gCurrentNetworkOperator.c_str(); |
| subscriber_id += "123456789"; |
| |
| gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, |
| strdup(subscriber_id.c_str()), sizeof(char*)); |
| } |
| |
| static bool gScreenIsOn = true; |
| |
| static void request_set_screen_state(void* data, size_t /*datalen*/, |
| RIL_Token t) { |
| gScreenIsOn = *(int*)data ? true : false; |
| ALOGV("Screen is %s", gScreenIsOn ? "on" : "off"); |
| gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, NULL, 0); |
| } |
| |
| // Unsure which section this belongs in. |
| |
| static int gModemTtyMode = 1; // 0 = off, 1 = full, 2 = HCO, 3 = VCO. |
| static void request_set_tty_mode(void* data, size_t /*datalen*/, RIL_Token t) { |
| int new_tty_mode = *(int*)(data); |
| ALOGV("Switching modem TTY mode %d -> %d", gModemTtyMode, new_tty_mode); |
| |
| if (new_tty_mode >= 0 && new_tty_mode <= 3) { |
| gModemTtyMode = new_tty_mode; |
| gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, NULL, 0); |
| } else { |
| gce_ril_env->OnRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0); |
| } |
| } |
| |
| static void request_get_tty_mode(RIL_Token t) { |
| ALOGV("Querying TTY mode"); |
| gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, &gModemTtyMode, |
| sizeof(gModemTtyMode)); |
| } |
| |
| static bool gImsRegistered = false; |
| static int gImsFormat = RADIO_TECH_3GPP; |
| |
| static void request_ims_registration_state(RIL_Token t) { |
| ALOGV("Querying IMS mode"); |
| int reply[2]; |
| reply[0] = gImsRegistered; |
| reply[1] = gImsFormat; |
| |
| ALOGV("Requesting IMS Registration state: %d, format=%d ", reply[0], |
| reply[1]); |
| |
| gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, reply, sizeof(reply)); |
| } |
| |
| // New functions after P. |
| static void request_start_network_scan(RIL_Token t) { |
| ALOGV("Scanning network - void"); |
| gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, NULL, 0); |
| return; |
| } |
| |
| static void request_set_preferred_network_type_bitmap(int /*request*/, void* data, |
| size_t /*datalen*/, |
| RIL_Token t) { |
| RIL_RadioAccessFamily desired_access = *(RIL_RadioAccessFamily*)(data); |
| |
| ALOGV("Requesting modem technology change %d -> %d", default_access, desired_access); |
| |
| /** TODO future implementation: set modem type based on radio access family. |
| * 1) find supported_technologies and desired_technologies |
| * 2) return RIL_E_MODE_NOT_SUPPORTED error if not supported |
| */ |
| default_access = desired_access; |
| gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, NULL, 0); |
| return; |
| } |
| |
| static void request_get_preferred_network_type_bitmap(int /*request*/, void* /*data*/, |
| size_t /*datalen*/, |
| RIL_Token t) { |
| ALOGV("Requesting modem radio access family: %d", default_access); |
| gce_ril_env->OnRequestComplete( |
| t, RIL_E_SUCCESS, (RIL_RadioAccessFamily*)(&default_access), sizeof(default_access)); |
| } |
| |
| static void request_emergency_dial(int /*request*/, void* /*data*/, size_t /*datalen*/, |
| RIL_Token t) { |
| ALOGV("Emergency dial"); |
| gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, NULL, 0); |
| return; |
| } |
| |
| static void request_set_sim_card_power(int /*request*/, void* /*data*/, size_t /*datalen*/, |
| RIL_Token t) { |
| ALOGV("Set sim card power - void"); |
| gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, NULL, 0); |
| return; |
| } |
| |
| static void request_get_modem_stack_status(int /*request*/, RIL_Token t) { |
| ALOGV("Getting modem stack status - void"); |
| gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, NULL, 0); |
| return; |
| } |
| |
| static void request_enable_modem(int /*request*/, RIL_Token t) { |
| ALOGV("Enabling modem - void"); |
| gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, NULL, 0); |
| return; |
| } |
| |
| static void request_set_system_selection_channels(int /*request*/, RIL_Token t) { |
| ALOGV("request_set_system_selection_channels - void"); |
| gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, NULL, 0); |
| return; |
| } |
| |
| // New functions after Q. |
| static void request_set_signal_strength_reporting_criteria(int /*request*/, void* /*data*/, |
| size_t /*datalen*/, RIL_Token t) { |
| ALOGV("request_set_signal_strength_reporting_criteria - void"); |
| gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, NULL, 0); |
| return; |
| } |
| |
| static void request_set_link_capacity_reporting_criteria(int /*request*/, void* /*data*/, |
| size_t /*datalen*/, RIL_Token t) { |
| ALOGV("request_set_link_capacity_reporting_criteria - void"); |
| gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, NULL, 0); |
| return; |
| } |
| |
| static void request_enable_uicc_applications(int /*request*/, void* data, |
| size_t datalen, |
| RIL_Token t) { |
| ALOGV("Enable uicc applications."); |
| |
| if (data == NULL || datalen != sizeof(int)) { |
| gce_ril_env->OnRequestComplete(t, RIL_E_INTERNAL_ERR, NULL, 0); |
| return; |
| } |
| |
| bool enable = *(int *)(data) != 0; |
| |
| ALOGV("areUiccApplicationsEnabled change from %d to %d", areUiccApplicationsEnabled, enable); |
| |
| areUiccApplicationsEnabled = enable; |
| |
| gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, NULL, 0); |
| } |
| |
| static void request_are_uicc_applications_enabled(int /*request*/, void* /*data*/, |
| size_t /*datalen*/, |
| RIL_Token t) { |
| ALOGV("Getting whether uicc applications are enabled."); |
| |
| gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, &areUiccApplicationsEnabled, sizeof(bool)); |
| } |
| |
| static void request_enter_sim_depersonalization(RIL_Token t) { |
| ALOGV("request_enter_sim_depersonalization - void"); |
| gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, NULL, 0); |
| return; |
| } |
| |
| static void request_cdma_send_sms_expect_more(RIL_Token t) { |
| ALOGV("request_cdma_send_sms_expect_more - void"); |
| gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, NULL, 0); |
| return; |
| } |
| |
| static void gce_ril_on_request(int request, void* data, size_t datalen, |
| RIL_Token t) { |
| // Ignore all requests except RIL_REQUEST_GET_SIM_STATUS |
| // when RADIO_STATE_UNAVAILABLE. |
| if (gRadioPowerState == RADIO_STATE_UNAVAILABLE && |
| request != RIL_REQUEST_GET_SIM_STATUS) { |
| gce_ril_env->OnRequestComplete(t, RIL_E_RADIO_NOT_AVAILABLE, NULL, 0); |
| return; |
| } |
| |
| // Ignore all non-power requests when RADIO_STATE_OFF. |
| if (gRadioPowerState == RADIO_STATE_OFF) { |
| switch (request) { |
| case RIL_REQUEST_GET_SIM_STATUS: |
| case RIL_REQUEST_OPERATOR: |
| case RIL_REQUEST_RADIO_POWER: |
| case RIL_REQUEST_QUERY_NETWORK_SELECTION_MODE: |
| // Process all the above, even though the radio is off |
| break; |
| default: |
| gce_ril_env->OnRequestComplete(t, RIL_E_RADIO_NOT_AVAILABLE, NULL, 0); |
| return; |
| } |
| } |
| |
| ALOGV("Received request %d", request); |
| |
| switch (request) { |
| case RIL_REQUEST_QUERY_AVAILABLE_NETWORKS: |
| request_query_available_networks(data, datalen, t); |
| break; |
| case RIL_REQUEST_GET_IMEI: |
| request_get_imei(t); |
| break; |
| case RIL_REQUEST_GET_IMEISV: |
| request_get_imei_sv(t); |
| break; |
| case RIL_REQUEST_DEACTIVATE_DATA_CALL: |
| request_teardown_data_call(data, datalen, t); |
| break; |
| case RIL_REQUEST_SCREEN_STATE: |
| request_set_screen_state(data, datalen, t); |
| break; |
| case RIL_REQUEST_GET_SIM_STATUS: |
| getCardStatus(t); |
| break; |
| case RIL_REQUEST_GET_CURRENT_CALLS: |
| request_get_current_calls(data, datalen, t); |
| break; |
| case RIL_REQUEST_DIAL: |
| request_dial(data, datalen, t); |
| break; |
| case RIL_REQUEST_HANGUP: |
| request_hangup(data, datalen, t); |
| break; |
| case RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND: |
| request_hangup_waiting(data, datalen, t); |
| break; |
| case RIL_REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND: |
| request_hangup_current(t); |
| break; |
| case RIL_REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE: |
| request_switch_current_and_waiting(t); |
| break; |
| case RIL_REQUEST_ANSWER: |
| request_answer_incoming(t); |
| break; |
| case RIL_REQUEST_SET_MUTE: |
| request_set_mute(data, datalen, t); |
| break; |
| case RIL_REQUEST_GET_MUTE: |
| request_get_mute(t); |
| break; |
| case RIL_REQUEST_CONFERENCE: |
| request_combine_multiparty_call(data, datalen, t); |
| break; |
| case RIL_REQUEST_SEPARATE_CONNECTION: |
| request_split_multiparty_call(data, datalen, t); |
| break; |
| case RIL_REQUEST_UDUB: |
| request_udub_on_incoming_calls(t); |
| break; |
| case RIL_REQUEST_SIGNAL_STRENGTH: |
| request_signal_strength(data, datalen, t); |
| break; |
| case RIL_REQUEST_VOICE_REGISTRATION_STATE: |
| case RIL_REQUEST_DATA_REGISTRATION_STATE: |
| request_registration_state(request, data, datalen, t); |
| break; |
| case RIL_REQUEST_OPERATOR: |
| request_operator(data, datalen, t); |
| break; |
| case RIL_REQUEST_RADIO_POWER: |
| request_radio_power(data, datalen, t); |
| break; |
| case RIL_REQUEST_DTMF: |
| case RIL_REQUEST_DTMF_START: |
| request_send_dtmf(data, datalen, t); |
| break; |
| case RIL_REQUEST_DTMF_STOP: |
| request_send_dtmf_stop(t); |
| break; |
| case RIL_REQUEST_SEND_SMS: |
| request_send_SMS(data, t); |
| break; |
| case RIL_REQUEST_CDMA_SEND_SMS: |
| request_cdma_send_SMS(data, t); |
| break; |
| case RIL_REQUEST_SETUP_DATA_CALL: |
| request_setup_data_call(data, datalen, t); |
| break; |
| case RIL_REQUEST_SMS_ACKNOWLEDGE: |
| request_SMS_acknowledge(data, datalen, t); |
| break; |
| case RIL_REQUEST_GET_IMSI: |
| request_international_subscriber_id_number(t); |
| break; |
| case RIL_REQUEST_QUERY_FACILITY_LOCK: |
| request_facility_lock(data, datalen, t); |
| break; |
| case RIL_REQUEST_SIM_IO: |
| request_SIM_IO(data, datalen, t); |
| break; |
| case RIL_REQUEST_SEND_USSD: |
| request_send_ussd(data, datalen, t); |
| break; |
| case RIL_REQUEST_CANCEL_USSD: |
| request_cancel_ussd(t); |
| break; |
| case RIL_REQUEST_SET_NETWORK_SELECTION_AUTOMATIC: |
| request_set_automatic_network_selection(t); |
| break; |
| case RIL_REQUEST_SET_NETWORK_SELECTION_MANUAL: |
| request_set_manual_network_selection(data, datalen, t); |
| break; |
| case RIL_REQUEST_DATA_CALL_LIST: |
| request_data_calllist(data, datalen, t); |
| break; |
| case RIL_REQUEST_LAST_DATA_CALL_FAIL_CAUSE: |
| request_datacall_fail_cause(t); |
| break; |
| case RIL_REQUEST_QUERY_NETWORK_SELECTION_MODE: |
| request_query_network_selection_mode(data, datalen, t); |
| break; |
| case RIL_REQUEST_OEM_HOOK_RAW: |
| case RIL_REQUEST_OEM_HOOK_STRINGS: |
| ALOGV("OEM Hooks not supported!"); |
| gce_ril_env->OnRequestComplete(t, RIL_E_REQUEST_NOT_SUPPORTED, NULL, 0); |
| break; |
| case RIL_REQUEST_WRITE_SMS_TO_SIM: |
| request_write_sms_to_sim(data, datalen, t); |
| break; |
| case RIL_REQUEST_DELETE_SMS_ON_SIM: |
| request_delete_sms_on_sim(data, datalen, t); |
| break; |
| case RIL_REQUEST_ENTER_SIM_PIN: |
| case RIL_REQUEST_ENTER_SIM_PUK: |
| case RIL_REQUEST_ENTER_SIM_PIN2: |
| case RIL_REQUEST_ENTER_SIM_PUK2: |
| case RIL_REQUEST_CHANGE_SIM_PIN: |
| case RIL_REQUEST_CHANGE_SIM_PIN2: |
| request_enter_sim_pin(data, datalen, t); |
| break; |
| case RIL_REQUEST_VOICE_RADIO_TECH: { |
| RIL_RadioTechnology tech = getBestVoiceTechnology(gModemCurrentType); |
| gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, &tech, sizeof(tech)); |
| break; |
| } |
| case RIL_REQUEST_SET_PREFERRED_NETWORK_TYPE: |
| request_set_preferred_network_type(request, data, datalen, t); |
| break; |
| case RIL_REQUEST_GET_PREFERRED_NETWORK_TYPE: |
| request_get_preferred_network_type(request, data, datalen, t); |
| break; |
| case RIL_REQUEST_GET_NEIGHBORING_CELL_IDS: |
| request_get_neighboring_cell_ids(data, datalen, t); |
| break; |
| case RIL_REQUEST_GET_CELL_INFO_LIST: |
| request_get_cell_info_list(data, datalen, t); |
| break; |
| case RIL_REQUEST_SET_UNSOL_CELL_INFO_LIST_RATE: |
| request_set_cell_info_list_rate(data, datalen, t); |
| break; |
| case RIL_REQUEST_BASEBAND_VERSION: |
| request_baseband_version(t); |
| break; |
| case RIL_REQUEST_SET_TTY_MODE: |
| request_set_tty_mode(data, datalen, t); |
| break; |
| case RIL_REQUEST_QUERY_TTY_MODE: |
| request_get_tty_mode(t); |
| break; |
| case RIL_REQUEST_GET_RADIO_CAPABILITY: |
| request_get_radio_capability(t); |
| break; |
| case RIL_REQUEST_SET_RADIO_CAPABILITY: |
| request_set_radio_capability(data, datalen, t); |
| break; |
| case RIL_REQUEST_SET_DATA_PROFILE: |
| gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, NULL, 0); |
| break; |
| case RIL_REQUEST_GET_HARDWARE_CONFIG: |
| request_hardware_config(t); |
| break; |
| case RIL_REQUEST_IMS_REGISTRATION_STATE: |
| request_ims_registration_state(t); |
| break; |
| case RIL_REQUEST_SIM_TRANSMIT_APDU_CHANNEL: |
| request_sim_apdu(data, datalen, t); |
| break; |
| case RIL_REQUEST_SIM_OPEN_CHANNEL: |
| request_sim_open_channel(data, datalen, t); |
| break; |
| case RIL_REQUEST_SIM_CLOSE_CHANNEL: |
| request_sim_close_channel(data, datalen, t); |
| break; |
| case RIL_REQUEST_IMS_SEND_SMS: |
| request_ims_send_SMS(data, datalen, t); |
| break; |
| case RIL_REQUEST_SET_INITIAL_ATTACH_APN: |
| ALOGW("INITIAL ATTACH APN"); |
| gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, NULL, 0); |
| break; |
| |
| // New requests after P. |
| case RIL_REQUEST_START_NETWORK_SCAN: |
| request_start_network_scan(t); |
| break; |
| case RIL_REQUEST_GET_MODEM_STACK_STATUS: |
| request_get_modem_stack_status(request, t); |
| break; |
| case RIL_REQUEST_ENABLE_MODEM: |
| request_enable_modem(request, t); |
| break; |
| case RIL_REQUEST_EMERGENCY_DIAL: |
| request_emergency_dial(request, data, datalen, t); |
| break; |
| case RIL_REQUEST_SET_SIM_CARD_POWER: |
| request_set_sim_card_power(request, data, datalen, t); |
| break; |
| case RIL_REQUEST_GET_PREFERRED_NETWORK_TYPE_BITMAP: |
| request_get_preferred_network_type_bitmap(request, data, datalen, t); |
| break; |
| case RIL_REQUEST_SET_PREFERRED_NETWORK_TYPE_BITMAP: |
| request_set_preferred_network_type_bitmap(request, data, datalen, t); |
| break; |
| case RIL_REQUEST_SET_SYSTEM_SELECTION_CHANNELS: |
| request_set_system_selection_channels(request, t); |
| break; |
| case RIL_REQUEST_REPORT_STK_SERVICE_IS_RUNNING: |
| gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, NULL, 0); |
| break; |
| case RIL_REQUEST_DEVICE_IDENTITY: |
| request_device_identity(request, data, datalen, t); |
| break; |
| case RIL_REQUEST_CDMA_GET_SUBSCRIPTION_SOURCE: |
| request_cdma_get_subscription_source(request, data, datalen, t); |
| break; |
| case RIL_REQUEST_CDMA_SUBSCRIPTION: |
| request_cdma_subscription(request, data, datalen, t); |
| break; |
| case RIL_REQUEST_CDMA_SET_SUBSCRIPTION_SOURCE: |
| request_cdma_set_subscription_source(request, data, datalen, t); |
| break; |
| case RIL_REQUEST_CDMA_QUERY_ROAMING_PREFERENCE: |
| request_cdma_get_roaming_preference(request, data, datalen, t); |
| break; |
| case RIL_REQUEST_CDMA_SET_ROAMING_PREFERENCE: |
| request_cdma_set_roaming_preference(request, data, datalen, t); |
| break; |
| case RIL_REQUEST_EXIT_EMERGENCY_CALLBACK_MODE: |
| request_exit_emergency_mode(data, datalen, t); |
| break; |
| |
| // New requests after Q. |
| case RIL_REQUEST_SET_SIGNAL_STRENGTH_REPORTING_CRITERIA: |
| request_set_signal_strength_reporting_criteria(request, data, datalen, t); |
| break; |
| case RIL_REQUEST_SET_LINK_CAPACITY_REPORTING_CRITERIA: |
| request_set_link_capacity_reporting_criteria(request, data, datalen, t); |
| break; |
| case RIL_REQUEST_ENABLE_UICC_APPLICATIONS: |
| request_enable_uicc_applications(request, data, datalen, t); |
| break; |
| case RIL_REQUEST_ARE_UICC_APPLICATIONS_ENABLED: |
| request_are_uicc_applications_enabled(request, data, datalen, t); |
| break; |
| case RIL_REQUEST_ENTER_SIM_DEPERSONALIZATION: |
| request_enter_sim_depersonalization(t); |
| break; |
| case RIL_REQUEST_CDMA_SEND_SMS_EXPECT_MORE: |
| request_cdma_send_sms_expect_more(t); |
| break; |
| default: |
| ALOGE("Request %d not supported.", request); |
| gce_ril_env->OnRequestComplete(t, RIL_E_REQUEST_NOT_SUPPORTED, NULL, 0); |
| break; |
| } |
| } |
| |
| #define CUTTLEFISH_RIL_VERSION 6 |
| |
| static const RIL_RadioFunctions ril_callbacks = { |
| CUTTLEFISH_RIL_VERSION, gce_ril_on_request, gce_ril_current_state, |
| gce_ril_on_supports, gce_ril_on_cancel, gce_ril_get_version}; |
| |
| extern "C" { |
| |
| const RIL_RadioFunctions* RIL_Init(const struct RIL_Env* env, int /*argc*/, |
| char** /*argv*/) { |
| time(&gce_ril_start_time); |
| gce_ril_env = env; |
| |
| global_ril_config = cvd::DeviceConfig::Get(); |
| if (!global_ril_config) { |
| ALOGE("Failed to open device configuration!!!"); |
| return nullptr; |
| } |
| |
| TearDownNetworkInterface(); |
| |
| init_modem_supported_network_types(); |
| init_modem_technologies(); |
| init_virtual_network(); |
| init_sim_file_system(); |
| init_sim_status(); |
| |
| return &ril_callbacks; |
| } |
| |
| } // extern "C" |