| /* |
| * Copyright (C) 2021 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.internal.telephony; |
| |
| import static android.telephony.TelephonyManager.HAL_SERVICE_VOICE; |
| |
| import static com.android.internal.telephony.RILConstants.RIL_UNSOL_CALL_RING; |
| import static com.android.internal.telephony.RILConstants.RIL_UNSOL_CDMA_CALL_WAITING; |
| import static com.android.internal.telephony.RILConstants.RIL_UNSOL_CDMA_INFO_REC; |
| import static com.android.internal.telephony.RILConstants.RIL_UNSOL_CDMA_OTA_PROVISION_STATUS; |
| import static com.android.internal.telephony.RILConstants.RIL_UNSOL_EMERGENCY_NUMBER_LIST; |
| import static com.android.internal.telephony.RILConstants.RIL_UNSOL_ENTER_EMERGENCY_CALLBACK_MODE; |
| import static com.android.internal.telephony.RILConstants.RIL_UNSOL_EXIT_EMERGENCY_CALLBACK_MODE; |
| import static com.android.internal.telephony.RILConstants.RIL_UNSOL_ON_SS; |
| import static com.android.internal.telephony.RILConstants.RIL_UNSOL_ON_USSD; |
| import static com.android.internal.telephony.RILConstants.RIL_UNSOL_RESEND_INCALL_MUTE; |
| import static com.android.internal.telephony.RILConstants.RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED; |
| import static com.android.internal.telephony.RILConstants.RIL_UNSOL_RINGBACK_TONE; |
| import static com.android.internal.telephony.RILConstants.RIL_UNSOL_SRVCC_STATE_NOTIFY; |
| import static com.android.internal.telephony.RILConstants.RIL_UNSOL_STK_CALL_SETUP; |
| import static com.android.internal.telephony.RILConstants.RIL_UNSOL_STK_CC_ALPHA_NOTIFY; |
| |
| import android.hardware.radio.voice.IRadioVoiceIndication; |
| import android.os.AsyncResult; |
| import android.telephony.emergency.EmergencyNumber; |
| |
| import com.android.internal.telephony.cdma.CdmaCallWaitingNotification; |
| import com.android.internal.telephony.cdma.CdmaInformationRecords; |
| import com.android.internal.telephony.gsm.SsData; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| /** |
| * Interface declaring unsolicited radio indications for voice APIs. |
| */ |
| public class VoiceIndication extends IRadioVoiceIndication.Stub { |
| private final RIL mRil; |
| |
| public VoiceIndication(RIL ril) { |
| mRil = ril; |
| } |
| |
| /** |
| * Ring indication for an incoming call (eg, RING or CRING event). |
| * The rate of these events is controlled by ro.telephony.call_ring.delay and has a default |
| * value of 3000 (3 seconds) if absent. |
| * @param indicationType Type of radio indication |
| * @param isGsm true for GSM & false for CDMA |
| * @param record CDMA signal information record |
| */ |
| public void callRing(int indicationType, boolean isGsm, |
| android.hardware.radio.voice.CdmaSignalInfoRecord record) { |
| mRil.processIndication(HAL_SERVICE_VOICE, indicationType); |
| |
| char[] response = null; |
| |
| // Ignore record for gsm |
| if (!isGsm) { |
| // TODO: Clean this up with a parcelable class for better self-documentation |
| response = new char[4]; |
| response[0] = (char) (record.isPresent ? 1 : 0); |
| response[1] = (char) record.signalType; |
| response[2] = (char) record.alertPitch; |
| response[3] = (char) record.signal; |
| mRil.writeMetricsCallRing(response); |
| } |
| |
| if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_CALL_RING, response); |
| |
| if (mRil.mRingRegistrant != null) { |
| mRil.mRingRegistrant.notifyRegistrant(new AsyncResult(null, response, null)); |
| } |
| } |
| |
| /** |
| * Indicates when call state has changed. Redundant or extraneous invocations are tolerated. |
| * @param indicationType Type of radio indication |
| */ |
| public void callStateChanged(int indicationType) { |
| mRil.processIndication(HAL_SERVICE_VOICE, indicationType); |
| |
| if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED); |
| |
| mRil.mCallStateRegistrants.notifyRegistrants(); |
| } |
| |
| /** |
| * Indicates when CDMA radio receives a call waiting indication. |
| * @param indicationType Type of radio indication |
| * @param callWaitingRecord Cdma CallWaiting information |
| */ |
| public void cdmaCallWaiting(int indicationType, |
| android.hardware.radio.voice.CdmaCallWaiting callWaitingRecord) { |
| mRil.processIndication(HAL_SERVICE_VOICE, indicationType); |
| |
| // TODO: create a CdmaCallWaitingNotification constructor that takes in these fields to make |
| // sure no fields are missing |
| CdmaCallWaitingNotification notification = new CdmaCallWaitingNotification(); |
| notification.number = callWaitingRecord.number; |
| notification.numberPresentation = CdmaCallWaitingNotification.presentationFromCLIP( |
| callWaitingRecord.numberPresentation); |
| notification.name = callWaitingRecord.name; |
| notification.namePresentation = notification.numberPresentation; |
| notification.isPresent = callWaitingRecord.signalInfoRecord.isPresent ? 1 : 0; |
| notification.signalType = callWaitingRecord.signalInfoRecord.signalType; |
| notification.alertPitch = callWaitingRecord.signalInfoRecord.alertPitch; |
| notification.signal = callWaitingRecord.signalInfoRecord.signal; |
| notification.numberType = callWaitingRecord.numberType; |
| notification.numberPlan = callWaitingRecord.numberPlan; |
| |
| if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_CDMA_CALL_WAITING, notification); |
| |
| mRil.mCallWaitingInfoRegistrants.notifyRegistrants( |
| new AsyncResult(null, notification, null)); |
| } |
| |
| /** |
| * Indicates when CDMA radio receives one or more info recs. |
| * @param indicationType Type of radio indication |
| * @param records New CDMA information |
| */ |
| public void cdmaInfoRec(int indicationType, |
| android.hardware.radio.voice.CdmaInformationRecord[] records) { |
| mRil.processIndication(HAL_SERVICE_VOICE, indicationType); |
| |
| for (int i = 0; i < records.length; i++) { |
| android.hardware.radio.voice.CdmaInformationRecord record = records[i]; |
| int id = record.name; |
| CdmaInformationRecords cdmaInformationRecords; |
| switch (id) { |
| case CdmaInformationRecords.RIL_CDMA_DISPLAY_INFO_REC: |
| case CdmaInformationRecords.RIL_CDMA_EXTENDED_DISPLAY_INFO_REC: |
| CdmaInformationRecords.CdmaDisplayInfoRec cdmaDisplayInfoRec = |
| new CdmaInformationRecords.CdmaDisplayInfoRec(id, |
| record.display[0].alphaBuf); |
| cdmaInformationRecords = new CdmaInformationRecords(cdmaDisplayInfoRec); |
| break; |
| |
| case CdmaInformationRecords.RIL_CDMA_CALLED_PARTY_NUMBER_INFO_REC: |
| case CdmaInformationRecords.RIL_CDMA_CALLING_PARTY_NUMBER_INFO_REC: |
| case CdmaInformationRecords.RIL_CDMA_CONNECTED_NUMBER_INFO_REC: |
| android.hardware.radio.voice.CdmaNumberInfoRecord numInfoRecord = |
| record.number[0]; |
| CdmaInformationRecords.CdmaNumberInfoRec cdmaNumberInfoRec = |
| new CdmaInformationRecords.CdmaNumberInfoRec(id, numInfoRecord.number, |
| numInfoRecord.numberType, numInfoRecord.numberPlan, |
| numInfoRecord.pi, numInfoRecord.si); |
| cdmaInformationRecords = new CdmaInformationRecords(cdmaNumberInfoRec); |
| break; |
| |
| case CdmaInformationRecords.RIL_CDMA_SIGNAL_INFO_REC: |
| android.hardware.radio.voice.CdmaSignalInfoRecord signalInfoRecord = |
| record.signal[0]; |
| CdmaInformationRecords.CdmaSignalInfoRec cdmaSignalInfoRec = |
| new CdmaInformationRecords.CdmaSignalInfoRec( |
| signalInfoRecord.isPresent ? 1 : 0, signalInfoRecord.signalType, |
| signalInfoRecord.alertPitch, signalInfoRecord.signal); |
| cdmaInformationRecords = new CdmaInformationRecords(cdmaSignalInfoRec); |
| break; |
| |
| case CdmaInformationRecords.RIL_CDMA_REDIRECTING_NUMBER_INFO_REC: |
| android.hardware.radio.voice.CdmaRedirectingNumberInfoRecord |
| redirectingNumberInfoRecord = record.redir[0]; |
| CdmaInformationRecords.CdmaRedirectingNumberInfoRec |
| cdmaRedirectingNumberInfoRec = |
| new CdmaInformationRecords.CdmaRedirectingNumberInfoRec( |
| redirectingNumberInfoRecord.redirectingNumber.number, |
| redirectingNumberInfoRecord.redirectingNumber.numberType, |
| redirectingNumberInfoRecord.redirectingNumber.numberPlan, |
| redirectingNumberInfoRecord.redirectingNumber.pi, |
| redirectingNumberInfoRecord.redirectingNumber.si, |
| redirectingNumberInfoRecord.redirectingReason); |
| cdmaInformationRecords = new CdmaInformationRecords( |
| cdmaRedirectingNumberInfoRec); |
| break; |
| |
| case CdmaInformationRecords.RIL_CDMA_LINE_CONTROL_INFO_REC: |
| android.hardware.radio.voice.CdmaLineControlInfoRecord lineControlInfoRecord = |
| record.lineCtrl[0]; |
| CdmaInformationRecords.CdmaLineControlInfoRec cdmaLineControlInfoRec = |
| new CdmaInformationRecords.CdmaLineControlInfoRec( |
| lineControlInfoRecord.lineCtrlPolarityIncluded, |
| lineControlInfoRecord.lineCtrlToggle, |
| lineControlInfoRecord.lineCtrlReverse, |
| lineControlInfoRecord.lineCtrlPowerDenial); |
| cdmaInformationRecords = new CdmaInformationRecords(cdmaLineControlInfoRec); |
| break; |
| |
| case CdmaInformationRecords.RIL_CDMA_T53_CLIR_INFO_REC: |
| CdmaInformationRecords.CdmaT53ClirInfoRec cdmaT53ClirInfoRec = |
| new CdmaInformationRecords.CdmaT53ClirInfoRec(record.clir[0].cause); |
| cdmaInformationRecords = new CdmaInformationRecords(cdmaT53ClirInfoRec); |
| break; |
| |
| case CdmaInformationRecords.RIL_CDMA_T53_AUDIO_CONTROL_INFO_REC: |
| android.hardware.radio.voice.CdmaT53AudioControlInfoRecord |
| audioControlInfoRecord = record.audioCtrl[0]; |
| CdmaInformationRecords.CdmaT53AudioControlInfoRec cdmaT53AudioControlInfoRec = |
| new CdmaInformationRecords.CdmaT53AudioControlInfoRec( |
| audioControlInfoRecord.upLink, |
| audioControlInfoRecord.downLink); |
| cdmaInformationRecords = new CdmaInformationRecords(cdmaT53AudioControlInfoRec); |
| break; |
| |
| default: |
| throw new RuntimeException("RIL_UNSOL_CDMA_INFO_REC: unsupported record. Got " |
| + CdmaInformationRecords.idToString(id) + " "); |
| } |
| |
| if (mRil.isLogOrTrace()) { |
| mRil.unsljLogRet(RIL_UNSOL_CDMA_INFO_REC, cdmaInformationRecords); |
| } |
| mRil.notifyRegistrantsCdmaInfoRec(cdmaInformationRecords); |
| } |
| } |
| |
| /** |
| * Indicates when CDMA radio receives an update of the progress of an OTASP/OTAPA call. |
| * @param indicationType Type of radio indication |
| * @param status CDMA OTA provision status |
| */ |
| public void cdmaOtaProvisionStatus(int indicationType, int status) { |
| mRil.processIndication(HAL_SERVICE_VOICE, indicationType); |
| |
| int[] response = new int[] {status}; |
| |
| if (mRil.isLogOrTrace()) { |
| mRil.unsljLogRet(RIL_UNSOL_CDMA_OTA_PROVISION_STATUS, response); |
| } |
| |
| mRil.mOtaProvisionRegistrants.notifyRegistrants(new AsyncResult(null, response, null)); |
| } |
| |
| |
| /** |
| * Indicates current emergency number list. |
| * @param indicationType Type of radio indication |
| * @param emergencyNumberList Current list of emergency numbers known to radio |
| */ |
| public void currentEmergencyNumberList(int indicationType, |
| android.hardware.radio.voice.EmergencyNumber[] emergencyNumberList) { |
| mRil.processIndication(HAL_SERVICE_VOICE, indicationType); |
| |
| List<EmergencyNumber> response = new ArrayList<>(emergencyNumberList.length); |
| for (android.hardware.radio.voice.EmergencyNumber enHal : emergencyNumberList) { |
| EmergencyNumber emergencyNumber = new EmergencyNumber(enHal.number, |
| MccTable.countryCodeForMcc(enHal.mcc), enHal.mnc, enHal.categories, |
| RILUtils.primitiveArrayToArrayList(enHal.urns), enHal.sources, |
| EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN); |
| response.add(emergencyNumber); |
| } |
| |
| if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_EMERGENCY_NUMBER_LIST, response); |
| |
| // Cache emergency number list from last indication. |
| mRil.cacheEmergencyNumberListIndication(response); |
| |
| // Notify emergency number list from radio to registrants |
| mRil.mEmergencyNumberListRegistrants.notifyRegistrants( |
| new AsyncResult(null, response, null)); |
| } |
| |
| /** |
| * Indicates that the radio system selection module has autonomously entered emergency |
| * callback mode. |
| * @param indicationType Type of radio indication |
| */ |
| public void enterEmergencyCallbackMode(int indicationType) { |
| mRil.processIndication(HAL_SERVICE_VOICE, indicationType); |
| |
| if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_ENTER_EMERGENCY_CALLBACK_MODE); |
| |
| if (mRil.mEmergencyCallbackModeRegistrant != null) { |
| mRil.mEmergencyCallbackModeRegistrant.notifyRegistrant(); |
| } |
| } |
| |
| /** |
| * Indicates when Emergency Callback Mode ends. Indicates that the radio system selection module |
| * has proactively exited emergency callback mode. |
| * @param indicationType Type of radio indication |
| */ |
| public void exitEmergencyCallbackMode(int indicationType) { |
| mRil.processIndication(HAL_SERVICE_VOICE, indicationType); |
| |
| if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_EXIT_EMERGENCY_CALLBACK_MODE); |
| |
| mRil.mExitEmergencyCallbackModeRegistrants.notifyRegistrants(); |
| } |
| |
| /** |
| * Indicates that network doesn't have in-band information, need to play out-band tone. |
| * @param indicationType Type of radio indication |
| * @param start true = start play ringback tone, false = stop playing ringback tone |
| */ |
| public void indicateRingbackTone(int indicationType, boolean start) { |
| mRil.processIndication(HAL_SERVICE_VOICE, indicationType); |
| |
| if (mRil.isLogOrTrace()) mRil.unsljLogvRet(RIL_UNSOL_RINGBACK_TONE, start); |
| |
| mRil.mRingbackToneRegistrants.notifyRegistrants(new AsyncResult(null, start, null)); |
| } |
| |
| /** |
| * Indicates when Supplementary service(SS) response is received when DIAL/USSD/SS is changed to |
| * SS by call control. |
| * @param indicationType Type of radio indication |
| * @param ss StkCcUnsolSsResult |
| */ |
| public void onSupplementaryServiceIndication(int indicationType, |
| android.hardware.radio.voice.StkCcUnsolSsResult ss) { |
| mRil.processIndication(HAL_SERVICE_VOICE, indicationType); |
| |
| int num; |
| SsData ssData = new SsData(); |
| |
| ssData.serviceType = ssData.ServiceTypeFromRILInt(ss.serviceType); |
| ssData.requestType = ssData.RequestTypeFromRILInt(ss.requestType); |
| ssData.teleserviceType = ssData.TeleserviceTypeFromRILInt(ss.teleserviceType); |
| ssData.serviceClass = ss.serviceClass; // This is service class sent in the SS request. |
| ssData.result = ss.result; // This is the result of the SS request. |
| |
| if (ssData.serviceType.isTypeCF() && ssData.requestType.isTypeInterrogation()) { |
| android.hardware.radio.voice.CfData cfData = ss.cfData[0]; |
| num = cfData.cfInfo.length; |
| ssData.cfInfo = new CallForwardInfo[num]; |
| |
| for (int i = 0; i < num; i++) { |
| android.hardware.radio.voice.CallForwardInfo cfInfo = cfData.cfInfo[i]; |
| ssData.cfInfo[i] = new CallForwardInfo(); |
| ssData.cfInfo[i].status = cfInfo.status; |
| ssData.cfInfo[i].reason = cfInfo.reason; |
| ssData.cfInfo[i].serviceClass = cfInfo.serviceClass; |
| ssData.cfInfo[i].toa = cfInfo.toa; |
| ssData.cfInfo[i].number = cfInfo.number; |
| ssData.cfInfo[i].timeSeconds = cfInfo.timeSeconds; |
| mRil.riljLog("[SS Data] CF Info " + i + " : " + ssData.cfInfo[i]); |
| } |
| } else { |
| android.hardware.radio.voice.SsInfoData ssInfo = ss.ssInfo[0]; |
| num = ssInfo.ssInfo.length; |
| ssData.ssInfo = new int[num]; |
| for (int i = 0; i < num; i++) { |
| ssData.ssInfo[i] = ssInfo.ssInfo[i]; |
| mRil.riljLog("[SS Data] SS Info " + i + " : " + ssData.ssInfo[i]); |
| } |
| } |
| |
| if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_ON_SS, ssData); |
| |
| if (mRil.mSsRegistrant != null) { |
| mRil.mSsRegistrant.notifyRegistrant(new AsyncResult(null, ssData, null)); |
| } |
| } |
| |
| /** |
| * Indicates when a new USSD message is received. The USSD session is assumed to persist if the |
| * type code is REQUEST, otherwise the current session (if any) is assumed to have terminated. |
| * @param indicationType Type of radio indication |
| * @param ussdModeType USSD type code |
| * @param msg Message string in UTF-8, if applicable |
| */ |
| public void onUssd(int indicationType, int ussdModeType, String msg) { |
| mRil.processIndication(HAL_SERVICE_VOICE, indicationType); |
| |
| if (mRil.isLogOrTrace()) mRil.unsljLogMore(RIL_UNSOL_ON_USSD, "" + ussdModeType); |
| |
| // TODO: Clean this up with a parcelable class for better self-documentation |
| String[] resp = new String[]{"" + ussdModeType, msg}; |
| if (mRil.mUSSDRegistrant != null) { |
| mRil.mUSSDRegistrant.notifyRegistrant(new AsyncResult(null, resp, null)); |
| } |
| } |
| |
| /** |
| * Indicates that framework/application must reset the uplink mute state. |
| * @param indicationType Type of radio indication |
| */ |
| public void resendIncallMute(int indicationType) { |
| mRil.processIndication(HAL_SERVICE_VOICE, indicationType); |
| |
| if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_RESEND_INCALL_MUTE); |
| |
| mRil.mResendIncallMuteRegistrants.notifyRegistrants(); |
| } |
| |
| /** |
| * Indicates when Single Radio Voice Call Continuity (SRVCC) progress state has changed. |
| * @param indicationType Type of radio indication |
| * @param state New SRVCC State |
| */ |
| public void srvccStateNotify(int indicationType, int state) { |
| mRil.processIndication(HAL_SERVICE_VOICE, indicationType); |
| |
| int[] response = new int[] {state}; |
| |
| if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_SRVCC_STATE_NOTIFY, response); |
| |
| mRil.writeMetricsSrvcc(state); |
| mRil.mSrvccStateRegistrants.notifyRegistrants(new AsyncResult(null, response, null)); |
| } |
| |
| /** |
| * Indicates when there is an ALPHA from UICC during Call Control. |
| * @param indicationType Type of radio indication |
| * @param alpha ALPHA string from UICC in UTF-8 format |
| */ |
| public void stkCallControlAlphaNotify(int indicationType, String alpha) { |
| mRil.processIndication(HAL_SERVICE_VOICE, indicationType); |
| |
| if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_STK_CC_ALPHA_NOTIFY, alpha); |
| |
| if (mRil.mCatCcAlphaRegistrant != null) { |
| mRil.mCatCcAlphaRegistrant.notifyRegistrant(new AsyncResult(null, alpha, null)); |
| } |
| } |
| |
| /** |
| * Indicates when SIM wants application to setup a voice call. |
| * @param indicationType Type of radio indication |
| * @param timeout Timeout value in milliseconds for setting up voice call |
| */ |
| public void stkCallSetup(int indicationType, long timeout) { |
| mRil.processIndication(HAL_SERVICE_VOICE, indicationType); |
| |
| if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_STK_CALL_SETUP, timeout); |
| |
| if (mRil.mCatCallSetUpRegistrant != null) { |
| mRil.mCatCallSetUpRegistrant.notifyRegistrant(new AsyncResult(null, timeout, null)); |
| } |
| } |
| |
| @Override |
| public String getInterfaceHash() { |
| return IRadioVoiceIndication.HASH; |
| } |
| |
| @Override |
| public int getInterfaceVersion() { |
| return IRadioVoiceIndication.VERSION; |
| } |
| } |