/* * Copyright (C) 2006 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.gsm; import android.content.ContentValues; import android.content.Context; import android.content.SharedPreferences; import android.database.SQLException; import android.net.Uri; import android.os.AsyncResult; import android.os.Handler; import android.os.Message; import android.os.Registrant; import android.os.RegistrantList; import android.os.SystemProperties; import android.preference.PreferenceManager; import android.provider.Telephony; import android.telecom.VideoProfile; import android.telephony.CellLocation; import android.telephony.PhoneNumberUtils; import android.telephony.ServiceState; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import com.android.ims.ImsManager; import com.android.internal.telephony.CallTracker; import android.text.TextUtils; import android.telephony.Rlog; import android.util.Log; import static com.android.internal.telephony.CommandsInterface.CF_ACTION_DISABLE; import static com.android.internal.telephony.CommandsInterface.CF_ACTION_ENABLE; import static com.android.internal.telephony.CommandsInterface.CF_ACTION_ERASURE; import static com.android.internal.telephony.CommandsInterface.CF_ACTION_REGISTRATION; import static com.android.internal.telephony.CommandsInterface.CF_REASON_ALL; import static com.android.internal.telephony.CommandsInterface.CF_REASON_ALL_CONDITIONAL; import static com.android.internal.telephony.CommandsInterface.CF_REASON_NO_REPLY; import static com.android.internal.telephony.CommandsInterface.CF_REASON_NOT_REACHABLE; import static com.android.internal.telephony.CommandsInterface.CF_REASON_BUSY; import static com.android.internal.telephony.CommandsInterface.CF_REASON_UNCONDITIONAL; import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_VOICE; import com.android.internal.telephony.dataconnection.DcTracker; import com.android.internal.telephony.Call; import com.android.internal.telephony.CallForwardInfo; import com.android.internal.telephony.CallStateException; import com.android.internal.telephony.CommandsInterface; import com.android.internal.telephony.Connection; import com.android.internal.telephony.IccPhoneBookInterfaceManager; import com.android.internal.telephony.MmiCode; import com.android.internal.telephony.OperatorInfo; import com.android.internal.telephony.Phone; import com.android.internal.telephony.PhoneBase; import com.android.internal.telephony.PhoneConstants; import com.android.internal.telephony.PhoneNotifier; import com.android.internal.telephony.PhoneProxy; import com.android.internal.telephony.PhoneSubInfo; import com.android.internal.telephony.TelephonyProperties; import com.android.internal.telephony.UUSInfo; import com.android.internal.telephony.imsphone.ImsPhone; import com.android.internal.telephony.test.SimulatedRadioControl; import com.android.internal.telephony.uicc.IccRecords; import com.android.internal.telephony.uicc.IccVmNotSupportedException; import com.android.internal.telephony.uicc.UiccCard; import com.android.internal.telephony.uicc.UiccCardApplication; import com.android.internal.telephony.uicc.UiccController; import com.android.internal.telephony.ServiceStateTracker; import com.android.internal.telephony.uicc.IsimRecords; import com.android.internal.telephony.uicc.IsimUiccRecords; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; /** * {@hide} */ public class GSMPhone extends PhoneBase { // NOTE that LOG_TAG here is "GSM", which means that log messages // from this file will go into the radio log rather than the main // log. (Use "adb logcat -b radio" to see them.) static final String LOG_TAG = "GSMPhone"; private static final boolean LOCAL_DEBUG = true; private static final boolean VDBG = false; /* STOPSHIP if true */ // Key used to read/write current ciphering state public static final String CIPHERING_KEY = "ciphering_key"; // Key used to read/write voice mail number public static final String VM_NUMBER = "vm_number_key"; // Key used to read/write the SIM IMSI used for storing the voice mail public static final String VM_SIM_IMSI = "vm_sim_imsi_key"; // Instance Variables GsmCallTracker mCT; GsmServiceStateTracker mSST; ArrayList mPendingMMIs = new ArrayList(); SimPhoneBookInterfaceManager mSimPhoneBookIntManager; PhoneSubInfo mSubInfo; Registrant mPostDialHandler; /** List of Registrants to receive Supplementary Service Notifications. */ RegistrantList mSsnRegistrants = new RegistrantList(); // mEcmTimerResetRegistrants are informed after Ecm timer is canceled or re-started private final RegistrantList mEcmTimerResetRegistrants = new RegistrantList(); private String mImei; private String mImeiSv; private String mVmNumber; private IsimUiccRecords mIsimUiccRecords; // Create Cfu (Call forward unconditional) so that dialing number & // mOnComplete (Message object passed by client) can be packed & // given as a single Cfu object as user data to RIL. private static class Cfu { final String mSetCfNumber; final Message mOnComplete; Cfu(String cfNumber, Message onComplete) { mSetCfNumber = cfNumber; mOnComplete = onComplete; } } // Constructors public GSMPhone(Context context, CommandsInterface ci, PhoneNotifier notifier, boolean unitTestMode) { super("GSM", notifier, context, ci, unitTestMode); if (ci instanceof SimulatedRadioControl) { mSimulatedRadioControl = (SimulatedRadioControl) ci; } mCi.setPhoneType(PhoneConstants.PHONE_TYPE_GSM); mCT = new GsmCallTracker(this); mSST = new GsmServiceStateTracker(this); mDcTracker = new DcTracker(this); if (!unitTestMode) { mSimPhoneBookIntManager = new SimPhoneBookInterfaceManager(this); mSubInfo = new PhoneSubInfo(this); } mCi.registerForAvailable(this, EVENT_RADIO_AVAILABLE, null); mCi.registerForOffOrNotAvailable(this, EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null); mCi.registerForOn(this, EVENT_RADIO_ON, null); mCi.setOnUSSD(this, EVENT_USSD, null); mCi.setOnSuppServiceNotification(this, EVENT_SSN, null); mSST.registerForNetworkAttached(this, EVENT_REGISTERED_TO_NETWORK, null); mCi.setOnSs(this, EVENT_SS, null); setProperties(); } public GSMPhone(Context context, CommandsInterface ci, PhoneNotifier notifier, int phoneId) { this(context, ci, notifier, false, phoneId); } public GSMPhone(Context context, CommandsInterface ci, PhoneNotifier notifier, boolean unitTestMode, int phoneId) { super("GSM", notifier, context, ci, unitTestMode, phoneId); if (ci instanceof SimulatedRadioControl) { mSimulatedRadioControl = (SimulatedRadioControl) ci; } mCi.setPhoneType(PhoneConstants.PHONE_TYPE_GSM); mCT = new GsmCallTracker(this); mSST = new GsmServiceStateTracker(this); mDcTracker = new DcTracker(this); if (!unitTestMode) { mSimPhoneBookIntManager = new SimPhoneBookInterfaceManager(this); mSubInfo = new PhoneSubInfo(this); } mCi.registerForAvailable(this, EVENT_RADIO_AVAILABLE, null); mCi.registerForOffOrNotAvailable(this, EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null); mCi.registerForOn(this, EVENT_RADIO_ON, null); mCi.setOnUSSD(this, EVENT_USSD, null); mCi.setOnSuppServiceNotification(this, EVENT_SSN, null); mSST.registerForNetworkAttached(this, EVENT_REGISTERED_TO_NETWORK, null); mCi.setOnSs(this, EVENT_SS, null); setProperties(); log("GSMPhone: constructor: sub = " + mPhoneId); setProperties(); } protected void setProperties() { TelephonyManager.from(mContext).setPhoneType(getPhoneId(), PhoneConstants.PHONE_TYPE_GSM); } @Override public void dispose() { synchronized(PhoneProxy.lockForRadioTechnologyChange) { super.dispose(); //Unregister from all former registered events mCi.unregisterForAvailable(this); //EVENT_RADIO_AVAILABLE unregisterForSimRecordEvents(); mCi.unregisterForOffOrNotAvailable(this); //EVENT_RADIO_OFF_OR_NOT_AVAILABLE mCi.unregisterForOn(this); //EVENT_RADIO_ON mSST.unregisterForNetworkAttached(this); //EVENT_REGISTERED_TO_NETWORK mCi.unSetOnUSSD(this); mCi.unSetOnSuppServiceNotification(this); mCi.unSetOnSs(this); mPendingMMIs.clear(); //Force all referenced classes to unregister their former registered events mCT.dispose(); mDcTracker.dispose(); mSST.dispose(); mSimPhoneBookIntManager.dispose(); mSubInfo.dispose(); } } @Override public void removeReferences() { Rlog.d(LOG_TAG, "removeReferences"); mSimulatedRadioControl = null; mSimPhoneBookIntManager = null; mSubInfo = null; mCT = null; mSST = null; super.removeReferences(); } @Override protected void finalize() { if(LOCAL_DEBUG) Rlog.d(LOG_TAG, "GSMPhone finalized"); } @Override public ServiceState getServiceState() { if (mSST == null || mSST.mSS.getState() != ServiceState.STATE_IN_SERVICE) { if (mImsPhone != null) { return ServiceState.mergeServiceStates( (mSST == null) ? new ServiceState() : mSST.mSS, mImsPhone.getServiceState()); } } if (mSST != null) { return mSST.mSS; } else { // avoid potential NPE in EmergencyCallHelper during Phone switch return new ServiceState(); } } @Override public CellLocation getCellLocation() { return mSST.getCellLocation(); } @Override public PhoneConstants.State getState() { if (mImsPhone != null) { PhoneConstants.State imsState = mImsPhone.getState(); if (imsState != PhoneConstants.State.IDLE) { return imsState; } } return mCT.mState; } @Override public int getPhoneType() { return PhoneConstants.PHONE_TYPE_GSM; } @Override public ServiceStateTracker getServiceStateTracker() { return mSST; } @Override public CallTracker getCallTracker() { return mCT; } // pending voice mail count updated after phone creation private void updateVoiceMail() { int countVoiceMessages = 0; IccRecords r = mIccRecords.get(); if (r != null) { // get voice mail count from SIM countVoiceMessages = r.getVoiceMessageCount(); } int countVoiceMessagesStored = getStoredVoiceMessageCount(); if (countVoiceMessages == -1 && countVoiceMessagesStored != 0) { countVoiceMessages = countVoiceMessagesStored; } Rlog.d(LOG_TAG, "updateVoiceMail countVoiceMessages = " + countVoiceMessages +" subId "+getSubId()); setVoiceMessageCount(countVoiceMessages); } @Override public List getPendingMmiCodes() { return mPendingMMIs; } @Override public PhoneConstants.DataState getDataConnectionState(String apnType) { PhoneConstants.DataState ret = PhoneConstants.DataState.DISCONNECTED; if (mSST == null) { // Radio Technology Change is ongoning, dispose() and removeReferences() have // already been called ret = PhoneConstants.DataState.DISCONNECTED; } else if (!apnType.equals(PhoneConstants.APN_TYPE_EMERGENCY) && mSST.getCurrentDataConnectionState() != ServiceState.STATE_IN_SERVICE) { // If we're out of service, open TCP sockets may still work // but no data will flow // Emergency APN is available even in Out Of Service // Pass the actual State of EPDN ret = PhoneConstants.DataState.DISCONNECTED; } else if (mDcTracker.isApnTypeEnabled(apnType) == false || mDcTracker.isApnTypeActive(apnType) == false) { //TODO: isApnTypeActive() is just checking whether ApnContext holds // Dataconnection or not. Checking each ApnState below should // provide the same state. Calling isApnTypeActive() can be removed. ret = PhoneConstants.DataState.DISCONNECTED; } else { /* mSST.gprsState == ServiceState.STATE_IN_SERVICE */ switch (mDcTracker.getState(apnType)) { case RETRYING: case FAILED: case IDLE: ret = PhoneConstants.DataState.DISCONNECTED; break; case CONNECTED: case DISCONNECTING: if ( mCT.mState != PhoneConstants.State.IDLE && !mSST.isConcurrentVoiceAndDataAllowed()) { ret = PhoneConstants.DataState.SUSPENDED; } else { ret = PhoneConstants.DataState.CONNECTED; } break; case CONNECTING: case SCANNING: ret = PhoneConstants.DataState.CONNECTING; break; } } return ret; } @Override public DataActivityState getDataActivityState() { DataActivityState ret = DataActivityState.NONE; if (mSST.getCurrentDataConnectionState() == ServiceState.STATE_IN_SERVICE) { switch (mDcTracker.getActivity()) { case DATAIN: ret = DataActivityState.DATAIN; break; case DATAOUT: ret = DataActivityState.DATAOUT; break; case DATAINANDOUT: ret = DataActivityState.DATAINANDOUT; break; case DORMANT: ret = DataActivityState.DORMANT; break; default: ret = DataActivityState.NONE; break; } } return ret; } /** * Notify any interested party of a Phone state change * {@link com.android.internal.telephony.PhoneConstants.State} */ /*package*/ void notifyPhoneStateChanged() { mNotifier.notifyPhoneState(this); } /** * Notify registrants of a change in the call state. This notifies changes in * {@link com.android.internal.telephony.Call.State}. Use this when changes * in the precise call state are needed, else use notifyPhoneStateChanged. */ /*package*/ void notifyPreciseCallStateChanged() { /* we'd love it if this was package-scoped*/ super.notifyPreciseCallStateChangedP(); } public void notifyNewRingingConnection(Connection c) { super.notifyNewRingingConnectionP(c); } /*package*/ void notifyDisconnect(Connection cn) { mDisconnectRegistrants.notifyResult(cn); mNotifier.notifyDisconnectCause(cn.getDisconnectCause(), cn.getPreciseDisconnectCause()); } void notifyUnknownConnection(Connection cn) { mUnknownConnectionRegistrants.notifyResult(cn); } void notifySuppServiceFailed(SuppService code) { mSuppServiceFailedRegistrants.notifyResult(code); } /*package*/ void notifyServiceStateChanged(ServiceState ss) { super.notifyServiceStateChangedP(ss); } /*package*/ void notifyLocationChanged() { mNotifier.notifyCellLocation(this); } @Override public void notifyCallForwardingIndicator() { mNotifier.notifyCallForwardingChanged(this); } // override for allowing access from other classes of this package /** * {@inheritDoc} */ @Override public void setSystemProperty(String property, String value) { TelephonyManager.setTelephonyProperty(mPhoneId, property, value); } @Override public void registerForSuppServiceNotification( Handler h, int what, Object obj) { mSsnRegistrants.addUnique(h, what, obj); if (mSsnRegistrants.size() == 1) mCi.setSuppServiceNotifications(true, null); } @Override public void unregisterForSuppServiceNotification(Handler h) { mSsnRegistrants.remove(h); if (mSsnRegistrants.size() == 0) mCi.setSuppServiceNotifications(false, null); } @Override public void registerForSimRecordsLoaded(Handler h, int what, Object obj) { mSimRecordsLoadedRegistrants.addUnique(h, what, obj); } @Override public void unregisterForSimRecordsLoaded(Handler h) { mSimRecordsLoadedRegistrants.remove(h); } @Override public void acceptCall(int videoState) throws CallStateException { ImsPhone imsPhone = mImsPhone; if ( imsPhone != null && imsPhone.getRingingCall().isRinging() ) { imsPhone.acceptCall(videoState); } else { mCT.acceptCall(); } } @Override public void rejectCall() throws CallStateException { mCT.rejectCall(); } @Override public void switchHoldingAndActive() throws CallStateException { mCT.switchWaitingOrHoldingAndActive(); } @Override public boolean canConference() { boolean canImsConference = false; if (mImsPhone != null) { canImsConference = mImsPhone.canConference(); } return mCT.canConference() || canImsConference; } public boolean canDial() { return mCT.canDial(); } @Override public void conference() { if (mImsPhone != null && mImsPhone.canConference()) { log("conference() - delegated to IMS phone"); mImsPhone.conference(); return; } mCT.conference(); } @Override public void clearDisconnected() { mCT.clearDisconnected(); } @Override public boolean canTransfer() { return mCT.canTransfer(); } @Override public void explicitCallTransfer() { mCT.explicitCallTransfer(); } @Override public GsmCall getForegroundCall() { return mCT.mForegroundCall; } @Override public GsmCall getBackgroundCall() { return mCT.mBackgroundCall; } @Override public Call getRingingCall() { ImsPhone imsPhone = mImsPhone; if ( mCT.mRingingCall != null && mCT.mRingingCall.isRinging() ) { return mCT.mRingingCall; } else if ( imsPhone != null ) { return imsPhone.getRingingCall(); } return mCT.mRingingCall; } private boolean handleCallDeflectionIncallSupplementaryService( String dialString) { if (dialString.length() > 1) { return false; } if (getRingingCall().getState() != GsmCall.State.IDLE) { if (LOCAL_DEBUG) Rlog.d(LOG_TAG, "MmiCode 0: rejectCall"); try { mCT.rejectCall(); } catch (CallStateException e) { if (LOCAL_DEBUG) Rlog.d(LOG_TAG, "reject failed", e); notifySuppServiceFailed(Phone.SuppService.REJECT); } } else if (getBackgroundCall().getState() != GsmCall.State.IDLE) { if (LOCAL_DEBUG) Rlog.d(LOG_TAG, "MmiCode 0: hangupWaitingOrBackground"); mCT.hangupWaitingOrBackground(); } return true; } private boolean handleCallWaitingIncallSupplementaryService( String dialString) { int len = dialString.length(); if (len > 2) { return false; } GsmCall call = getForegroundCall(); try { if (len > 1) { char ch = dialString.charAt(1); int callIndex = ch - '0'; if (callIndex >= 1 && callIndex <= GsmCallTracker.MAX_CONNECTIONS) { if (LOCAL_DEBUG) Rlog.d(LOG_TAG, "MmiCode 1: hangupConnectionByIndex " + callIndex); mCT.hangupConnectionByIndex(call, callIndex); } } else { if (call.getState() != GsmCall.State.IDLE) { if (LOCAL_DEBUG) Rlog.d(LOG_TAG, "MmiCode 1: hangup foreground"); //mCT.hangupForegroundResumeBackground(); mCT.hangup(call); } else { if (LOCAL_DEBUG) Rlog.d(LOG_TAG, "MmiCode 1: switchWaitingOrHoldingAndActive"); mCT.switchWaitingOrHoldingAndActive(); } } } catch (CallStateException e) { if (LOCAL_DEBUG) Rlog.d(LOG_TAG, "hangup failed", e); notifySuppServiceFailed(Phone.SuppService.HANGUP); } return true; } private boolean handleCallHoldIncallSupplementaryService(String dialString) { int len = dialString.length(); if (len > 2) { return false; } GsmCall call = getForegroundCall(); if (len > 1) { try { char ch = dialString.charAt(1); int callIndex = ch - '0'; GsmConnection conn = mCT.getConnectionByIndex(call, callIndex); // gsm index starts at 1, up to 5 connections in a call, if (conn != null && callIndex >= 1 && callIndex <= GsmCallTracker.MAX_CONNECTIONS) { if (LOCAL_DEBUG) Rlog.d(LOG_TAG, "MmiCode 2: separate call "+ callIndex); mCT.separate(conn); } else { if (LOCAL_DEBUG) Rlog.d(LOG_TAG, "separate: invalid call index "+ callIndex); notifySuppServiceFailed(Phone.SuppService.SEPARATE); } } catch (CallStateException e) { if (LOCAL_DEBUG) Rlog.d(LOG_TAG, "separate failed", e); notifySuppServiceFailed(Phone.SuppService.SEPARATE); } } else { try { if (getRingingCall().getState() != GsmCall.State.IDLE) { if (LOCAL_DEBUG) Rlog.d(LOG_TAG, "MmiCode 2: accept ringing call"); mCT.acceptCall(); } else { if (LOCAL_DEBUG) Rlog.d(LOG_TAG, "MmiCode 2: switchWaitingOrHoldingAndActive"); mCT.switchWaitingOrHoldingAndActive(); } } catch (CallStateException e) { if (LOCAL_DEBUG) Rlog.d(LOG_TAG, "switch failed", e); notifySuppServiceFailed(Phone.SuppService.SWITCH); } } return true; } private boolean handleMultipartyIncallSupplementaryService( String dialString) { if (dialString.length() > 1) { return false; } if (LOCAL_DEBUG) Rlog.d(LOG_TAG, "MmiCode 3: merge calls"); conference(); return true; } private boolean handleEctIncallSupplementaryService(String dialString) { int len = dialString.length(); if (len != 1) { return false; } if (LOCAL_DEBUG) Rlog.d(LOG_TAG, "MmiCode 4: explicit call transfer"); explicitCallTransfer(); return true; } private boolean handleCcbsIncallSupplementaryService(String dialString) { if (dialString.length() > 1) { return false; } Rlog.i(LOG_TAG, "MmiCode 5: CCBS not supported!"); // Treat it as an "unknown" service. notifySuppServiceFailed(Phone.SuppService.UNKNOWN); return true; } @Override public boolean handleInCallMmiCommands(String dialString) throws CallStateException { ImsPhone imsPhone = mImsPhone; if (imsPhone != null && imsPhone.getServiceState().getState() == ServiceState.STATE_IN_SERVICE) { return imsPhone.handleInCallMmiCommands(dialString); } if (!isInCall()) { return false; } if (TextUtils.isEmpty(dialString)) { return false; } boolean result = false; char ch = dialString.charAt(0); switch (ch) { case '0': result = handleCallDeflectionIncallSupplementaryService( dialString); break; case '1': result = handleCallWaitingIncallSupplementaryService( dialString); break; case '2': result = handleCallHoldIncallSupplementaryService(dialString); break; case '3': result = handleMultipartyIncallSupplementaryService(dialString); break; case '4': result = handleEctIncallSupplementaryService(dialString); break; case '5': result = handleCcbsIncallSupplementaryService(dialString); break; default: break; } return result; } boolean isInCall() { GsmCall.State foregroundCallState = getForegroundCall().getState(); GsmCall.State backgroundCallState = getBackgroundCall().getState(); GsmCall.State ringingCallState = getRingingCall().getState(); return (foregroundCallState.isAlive() || backgroundCallState.isAlive() || ringingCallState.isAlive()); } @Override public Connection dial(String dialString, int videoState) throws CallStateException { return dial(dialString, null, videoState); } @Override public Connection dial (String dialString, UUSInfo uusInfo, int videoState) throws CallStateException { ImsPhone imsPhone = mImsPhone; boolean imsUseEnabled = ImsManager.isVolteEnabledByPlatform(mContext) && ImsManager.isEnhanced4gLteModeSettingEnabledByUser(mContext) && ImsManager.isNonTtyOrTtyOnVolteEnabled(mContext); if (!imsUseEnabled) { Rlog.w(LOG_TAG, "IMS is disabled: forced to CS"); } if (LOCAL_DEBUG) { Rlog.d(LOG_TAG, "imsUseEnabled=" + imsUseEnabled + ", imsPhone=" + imsPhone + ", imsPhone.isVolteEnabled()=" + ((imsPhone != null) ? imsPhone.isVolteEnabled() : "N/A") + ", imsPhone.getServiceState().getState()=" + ((imsPhone != null) ? imsPhone.getServiceState().getState() : "N/A")); } if (imsUseEnabled && imsPhone != null && imsPhone.isVolteEnabled() && ((imsPhone.getServiceState().getState() == ServiceState.STATE_IN_SERVICE && !PhoneNumberUtils.isEmergencyNumber(dialString)) || (PhoneNumberUtils.isEmergencyNumber(dialString) && mContext.getResources().getBoolean( com.android.internal.R.bool.useImsAlwaysForEmergencyCall))) ) { try { if (LOCAL_DEBUG) Rlog.d(LOG_TAG, "Trying IMS PS call"); return imsPhone.dial(dialString, videoState); } catch (CallStateException e) { if (LOCAL_DEBUG) Rlog.d(LOG_TAG, "IMS PS call exception " + e + "imsUseEnabled =" + imsUseEnabled + ", imsPhone =" + imsPhone); if (!ImsPhone.CS_FALLBACK.equals(e.getMessage())) { CallStateException ce = new CallStateException(e.getMessage()); ce.setStackTrace(e.getStackTrace()); throw ce; } } } if (LOCAL_DEBUG) Rlog.d(LOG_TAG, "Trying (non-IMS) CS call"); return dialInternal(dialString, null, VideoProfile.VideoState.AUDIO_ONLY); } @Override protected Connection dialInternal (String dialString, UUSInfo uusInfo, int videoState) throws CallStateException { // Need to make sure dialString gets parsed properly String newDialString = PhoneNumberUtils.stripSeparators(dialString); // handle in-call MMI first if applicable if (handleInCallMmiCommands(newDialString)) { return null; } // Only look at the Network portion for mmi String networkPortion = PhoneNumberUtils.extractNetworkPortionAlt(newDialString); GsmMmiCode mmi = GsmMmiCode.newFromDialString(networkPortion, this, mUiccApplication.get()); if (LOCAL_DEBUG) Rlog.d(LOG_TAG, "dialing w/ mmi '" + mmi + "'..."); if (mmi == null) { return mCT.dial(newDialString, uusInfo); } else if (mmi.isTemporaryModeCLIR()) { return mCT.dial(mmi.mDialingNumber, mmi.getCLIRMode(), uusInfo); } else { mPendingMMIs.add(mmi); mMmiRegistrants.notifyRegistrants(new AsyncResult(null, mmi, null)); mmi.processCode(); // FIXME should this return null or something else? return null; } } @Override public boolean handlePinMmi(String dialString) { GsmMmiCode mmi = GsmMmiCode.newFromDialString(dialString, this, mUiccApplication.get()); if (mmi != null && mmi.isPinPukCommand()) { mPendingMMIs.add(mmi); mMmiRegistrants.notifyRegistrants(new AsyncResult(null, mmi, null)); mmi.processCode(); return true; } return false; } @Override public void sendUssdResponse(String ussdMessge) { GsmMmiCode mmi = GsmMmiCode.newFromUssdUserInput(ussdMessge, this, mUiccApplication.get()); mPendingMMIs.add(mmi); mMmiRegistrants.notifyRegistrants(new AsyncResult(null, mmi, null)); mmi.sendUssd(ussdMessge); } @Override public void sendDtmf(char c) { if (!PhoneNumberUtils.is12Key(c)) { Rlog.e(LOG_TAG, "sendDtmf called with invalid character '" + c + "'"); } else { if (mCT.mState == PhoneConstants.State.OFFHOOK) { mCi.sendDtmf(c, null); } } } @Override public void startDtmf(char c) { if (!PhoneNumberUtils.is12Key(c)) { Rlog.e(LOG_TAG, "startDtmf called with invalid character '" + c + "'"); } else { mCi.startDtmf(c, null); } } @Override public void stopDtmf() { mCi.stopDtmf(null); } public void sendBurstDtmf(String dtmfString) { Rlog.e(LOG_TAG, "[GSMPhone] sendBurstDtmf() is a CDMA method"); } @Override public void setRadioPower(boolean power) { mSST.setRadioPower(power); } private void storeVoiceMailNumber(String number) { SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext()); SharedPreferences.Editor editor = sp.edit(); editor.putString(VM_NUMBER + getPhoneId(), number); editor.apply(); setVmSimImsi(getSubscriberId()); } @Override public String getVoiceMailNumber() { // Read from the SIM. If its null, try reading from the shared preference area. IccRecords r = mIccRecords.get(); String number = (r != null) ? r.getVoiceMailNumber() : ""; if (TextUtils.isEmpty(number)) { SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext()); number = sp.getString(VM_NUMBER + getPhoneId(), null); } if (TextUtils.isEmpty(number)) { String[] listArray = getContext().getResources() .getStringArray(com.android.internal.R.array.config_default_vm_number); if (listArray != null && listArray.length > 0) { for (int i=0; i 0) { if (defaultVMNumberArray.length == 1) { number = defaultVMNumberArray[0]; } else if (defaultVMNumberArray.length == 2 && !TextUtils.isEmpty(defaultVMNumberArray[1]) && defaultVMNumberArray[1].equalsIgnoreCase(getGroupIdLevel1())) { number = defaultVMNumberArray[0]; break; } } } } } } return number; } private String getVmSimImsi() { SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext()); return sp.getString(VM_SIM_IMSI + getPhoneId(), null); } private void setVmSimImsi(String imsi) { SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext()); SharedPreferences.Editor editor = sp.edit(); editor.putString(VM_SIM_IMSI + getPhoneId(), imsi); editor.apply(); } @Override public String getVoiceMailAlphaTag() { String ret; IccRecords r = mIccRecords.get(); ret = (r != null) ? r.getVoiceMailAlphaTag() : ""; if (ret == null || ret.length() == 0) { return mContext.getText( com.android.internal.R.string.defaultVoiceMailAlphaTag).toString(); } return ret; } @Override public String getDeviceId() { return mImei; } @Override public String getDeviceSvn() { return mImeiSv; } @Override public IsimRecords getIsimRecords() { return mIsimUiccRecords; } @Override public String getImei() { return mImei; } @Override public String getEsn() { Rlog.e(LOG_TAG, "[GSMPhone] getEsn() is a CDMA method"); return "0"; } @Override public String getMeid() { Rlog.e(LOG_TAG, "[GSMPhone] getMeid() is a CDMA method"); return "0"; } @Override public String getNai() { IccRecords r = mUiccController.getIccRecords(mPhoneId, UiccController.APP_FAM_3GPP2); if (Log.isLoggable(LOG_TAG, Log.VERBOSE)) { Rlog.v(LOG_TAG, "IccRecords is " + r); } return (r != null) ? r.getNAI() : null; } @Override public String getSubscriberId() { IccRecords r = mIccRecords.get(); return (r != null) ? r.getIMSI() : null; } @Override public String getGroupIdLevel1() { IccRecords r = mIccRecords.get(); return (r != null) ? r.getGid1() : null; } @Override public String getLine1Number() { IccRecords r = mIccRecords.get(); return (r != null) ? r.getMsisdnNumber() : null; } @Override public String getMsisdn() { IccRecords r = mIccRecords.get(); return (r != null) ? r.getMsisdnNumber() : null; } @Override public String getLine1AlphaTag() { IccRecords r = mIccRecords.get(); return (r != null) ? r.getMsisdnAlphaTag() : null; } @Override public boolean setLine1Number(String alphaTag, String number, Message onComplete) { IccRecords r = mIccRecords.get(); if (r != null) { r.setMsisdnNumber(alphaTag, number, onComplete); return true; } else { return false; } } @Override public void setVoiceMailNumber(String alphaTag, String voiceMailNumber, Message onComplete) { Message resp; mVmNumber = voiceMailNumber; resp = obtainMessage(EVENT_SET_VM_NUMBER_DONE, 0, 0, onComplete); IccRecords r = mIccRecords.get(); if (r != null) { r.setVoiceMailNumber(alphaTag, mVmNumber, resp); } } private boolean isValidCommandInterfaceCFReason (int commandInterfaceCFReason) { switch (commandInterfaceCFReason) { case CF_REASON_UNCONDITIONAL: case CF_REASON_BUSY: case CF_REASON_NO_REPLY: case CF_REASON_NOT_REACHABLE: case CF_REASON_ALL: case CF_REASON_ALL_CONDITIONAL: return true; default: return false; } } @Override public String getSystemProperty(String property, String defValue) { if(getUnitTestMode()) { return null; } return TelephonyManager.getTelephonyProperty(mPhoneId, property, defValue); } private boolean isValidCommandInterfaceCFAction (int commandInterfaceCFAction) { switch (commandInterfaceCFAction) { case CF_ACTION_DISABLE: case CF_ACTION_ENABLE: case CF_ACTION_REGISTRATION: case CF_ACTION_ERASURE: return true; default: return false; } } public void updateDataConnectionTracker() { ((DcTracker)mDcTracker).update(); } protected boolean isCfEnable(int action) { return (action == CF_ACTION_ENABLE) || (action == CF_ACTION_REGISTRATION); } @Override public void getCallForwardingOption(int commandInterfaceCFReason, Message onComplete) { ImsPhone imsPhone = mImsPhone; if ((imsPhone != null) && (imsPhone.getServiceState().getState() == ServiceState.STATE_IN_SERVICE)) { imsPhone.getCallForwardingOption(commandInterfaceCFReason, onComplete); return; } if (isValidCommandInterfaceCFReason(commandInterfaceCFReason)) { if (LOCAL_DEBUG) Rlog.d(LOG_TAG, "requesting call forwarding query."); Message resp; if (commandInterfaceCFReason == CF_REASON_UNCONDITIONAL) { resp = obtainMessage(EVENT_GET_CALL_FORWARD_DONE, onComplete); } else { resp = onComplete; } mCi.queryCallForwardStatus(commandInterfaceCFReason,0,null,resp); } } @Override public void setCallForwardingOption(int commandInterfaceCFAction, int commandInterfaceCFReason, String dialingNumber, int timerSeconds, Message onComplete) { ImsPhone imsPhone = mImsPhone; if ((imsPhone != null) && (imsPhone.getServiceState().getState() == ServiceState.STATE_IN_SERVICE)) { imsPhone.setCallForwardingOption(commandInterfaceCFAction, commandInterfaceCFReason, dialingNumber, timerSeconds, onComplete); return; } if ( (isValidCommandInterfaceCFAction(commandInterfaceCFAction)) && (isValidCommandInterfaceCFReason(commandInterfaceCFReason))) { Message resp; if (commandInterfaceCFReason == CF_REASON_UNCONDITIONAL) { Cfu cfu = new Cfu(dialingNumber, onComplete); resp = obtainMessage(EVENT_SET_CALL_FORWARD_DONE, isCfEnable(commandInterfaceCFAction) ? 1 : 0, 0, cfu); } else { resp = onComplete; } mCi.setCallForward(commandInterfaceCFAction, commandInterfaceCFReason, CommandsInterface.SERVICE_CLASS_VOICE, dialingNumber, timerSeconds, resp); } } @Override public void getOutgoingCallerIdDisplay(Message onComplete) { mCi.getCLIR(onComplete); } @Override public void setOutgoingCallerIdDisplay(int commandInterfaceCLIRMode, Message onComplete) { mCi.setCLIR(commandInterfaceCLIRMode, obtainMessage(EVENT_SET_CLIR_COMPLETE, commandInterfaceCLIRMode, 0, onComplete)); } @Override public void getCallWaiting(Message onComplete) { ImsPhone imsPhone = mImsPhone; if ((imsPhone != null) && (imsPhone.getServiceState().getState() == ServiceState.STATE_IN_SERVICE)) { imsPhone.getCallWaiting(onComplete); return; } //As per 3GPP TS 24.083, section 1.6 UE doesn't need to send service //class parameter in call waiting interrogation to network mCi.queryCallWaiting(CommandsInterface.SERVICE_CLASS_NONE, onComplete); } @Override public void setCallWaiting(boolean enable, Message onComplete) { ImsPhone imsPhone = mImsPhone; if ((imsPhone != null) && (imsPhone.getServiceState().getState() == ServiceState.STATE_IN_SERVICE)) { imsPhone.setCallWaiting(enable, onComplete); return; } mCi.setCallWaiting(enable, CommandsInterface.SERVICE_CLASS_VOICE, onComplete); } @Override public void getAvailableNetworks(Message response) { mCi.getAvailableNetworks(response); } @Override public void getNeighboringCids(Message response) { mCi.getNeighboringCids(response); } @Override public void setOnPostDialCharacter(Handler h, int what, Object obj) { mPostDialHandler = new Registrant(h, what, obj); } @Override public void setUiTTYMode(int uiTtyMode, Message onComplete) { if (mImsPhone != null) { mImsPhone.setUiTTYMode(uiTtyMode, onComplete); } } @Override public void setMute(boolean muted) { mCT.setMute(muted); } @Override public boolean getMute() { return mCT.getMute(); } @Override public void getDataCallList(Message response) { mCi.getDataCallList(response); } @Override public void updateServiceLocation() { mSST.enableSingleLocationUpdate(); } @Override public void enableLocationUpdates() { mSST.enableLocationUpdates(); } @Override public void disableLocationUpdates() { mSST.disableLocationUpdates(); } @Override public boolean getDataRoamingEnabled() { return mDcTracker.getDataOnRoamingEnabled(); } @Override public void setDataRoamingEnabled(boolean enable) { mDcTracker.setDataOnRoamingEnabled(enable); } @Override public boolean getDataEnabled() { return mDcTracker.getDataEnabled(); } @Override public void setDataEnabled(boolean enable) { mDcTracker.setDataEnabled(enable); } /** * Removes the given MMI from the pending list and notifies * registrants that it is complete. * @param mmi MMI that is done */ /*package*/ void onMMIDone(GsmMmiCode mmi) { /* Only notify complete if it's on the pending list. * Otherwise, it's already been handled (eg, previously canceled). * The exception is cancellation of an incoming USSD-REQUEST, which is * not on the list. */ if (mPendingMMIs.remove(mmi) || mmi.isUssdRequest() || mmi.isSsInfo()) { mMmiCompleteRegistrants.notifyRegistrants( new AsyncResult(null, mmi, null)); } } private void onNetworkInitiatedUssd(GsmMmiCode mmi) { mMmiCompleteRegistrants.notifyRegistrants( new AsyncResult(null, mmi, null)); } /** ussdMode is one of CommandsInterface.USSD_MODE_* */ private void onIncomingUSSD (int ussdMode, String ussdMessage) { boolean isUssdError; boolean isUssdRequest; boolean isUssdRelease; isUssdRequest = (ussdMode == CommandsInterface.USSD_MODE_REQUEST); isUssdError = (ussdMode != CommandsInterface.USSD_MODE_NOTIFY && ussdMode != CommandsInterface.USSD_MODE_REQUEST); isUssdRelease = (ussdMode == CommandsInterface.USSD_MODE_NW_RELEASE); // See comments in GsmMmiCode.java // USSD requests aren't finished until one // of these two events happen GsmMmiCode found = null; for (int i = 0, s = mPendingMMIs.size() ; i < s; i++) { if(mPendingMMIs.get(i).isPendingUSSD()) { found = mPendingMMIs.get(i); break; } } if (found != null) { // Complete pending USSD if (isUssdRelease) { found.onUssdRelease(); } else if (isUssdError) { found.onUssdFinishedError(); } else { found.onUssdFinished(ussdMessage, isUssdRequest); } } else { // pending USSD not found // The network may initiate its own USSD request // ignore everything that isnt a Notify or a Request // also, discard if there is no message to present if (!isUssdError && ussdMessage != null) { GsmMmiCode mmi; mmi = GsmMmiCode.newNetworkInitiatedUssd(ussdMessage, isUssdRequest, GSMPhone.this, mUiccApplication.get()); onNetworkInitiatedUssd(mmi); } } } /** * Make sure the network knows our preferred setting. */ protected void syncClirSetting() { SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext()); int clirSetting = sp.getInt(CLIR_KEY + getPhoneId(), -1); if (clirSetting >= 0) { mCi.setCLIR(clirSetting, null); } } @Override public void handleMessage (Message msg) { AsyncResult ar; Message onComplete; // messages to be handled whether or not the phone is being destroyed // should only include messages which are being re-directed and do not use // resources of the phone being destroyed switch (msg.what) { // handle the select network completion callbacks. case EVENT_SET_NETWORK_MANUAL_COMPLETE: case EVENT_SET_NETWORK_AUTOMATIC_COMPLETE: super.handleMessage(msg); return; } if (!mIsTheCurrentActivePhone) { Rlog.e(LOG_TAG, "Received message " + msg + "[" + msg.what + "] while being destroyed. Ignoring."); return; } switch (msg.what) { case EVENT_RADIO_AVAILABLE: { mCi.getBasebandVersion( obtainMessage(EVENT_GET_BASEBAND_VERSION_DONE)); mCi.getIMEI(obtainMessage(EVENT_GET_IMEI_DONE)); mCi.getIMEISV(obtainMessage(EVENT_GET_IMEISV_DONE)); mCi.getRadioCapability(obtainMessage(EVENT_GET_RADIO_CAPABILITY)); } break; case EVENT_RADIO_ON: // do-nothing break; case EVENT_REGISTERED_TO_NETWORK: syncClirSetting(); break; case EVENT_SIM_RECORDS_LOADED: updateCurrentCarrierInProvider(); // Check if this is a different SIM than the previous one. If so unset the // voice mail number. String imsi = getVmSimImsi(); String imsiFromSIM = getSubscriberId(); if (imsi != null && imsiFromSIM != null && !imsiFromSIM.equals(imsi)) { storeVoiceMailNumber(null); setVmSimImsi(null); } mSimRecordsLoadedRegistrants.notifyRegistrants(); updateVoiceMail(); break; case EVENT_GET_BASEBAND_VERSION_DONE: ar = (AsyncResult)msg.obj; if (ar.exception != null) { break; } if (LOCAL_DEBUG) Rlog.d(LOG_TAG, "Baseband version: " + ar.result); TelephonyManager.from(mContext).setBasebandVersionForPhone(getPhoneId(), (String)ar.result); break; case EVENT_GET_IMEI_DONE: ar = (AsyncResult)msg.obj; if (ar.exception != null) { break; } mImei = (String)ar.result; break; case EVENT_GET_IMEISV_DONE: ar = (AsyncResult)msg.obj; if (ar.exception != null) { break; } mImeiSv = (String)ar.result; break; case EVENT_USSD: ar = (AsyncResult)msg.obj; String[] ussdResult = (String[]) ar.result; if (ussdResult.length > 1) { try { onIncomingUSSD(Integer.parseInt(ussdResult[0]), ussdResult[1]); } catch (NumberFormatException e) { Rlog.w(LOG_TAG, "error parsing USSD"); } } break; case EVENT_RADIO_OFF_OR_NOT_AVAILABLE: { // Some MMI requests (eg USSD) are not completed // within the course of a CommandsInterface request // If the radio shuts off or resets while one of these // is pending, we need to clean up. for (int i = mPendingMMIs.size() - 1; i >= 0; i--) { if (mPendingMMIs.get(i).isPendingUSSD()) { mPendingMMIs.get(i).onUssdFinishedError(); } } ImsPhone imsPhone = mImsPhone; if (imsPhone != null) { imsPhone.getServiceState().setStateOff(); } mRadioOffOrNotAvailableRegistrants.notifyRegistrants(); break; } case EVENT_SSN: ar = (AsyncResult)msg.obj; SuppServiceNotification not = (SuppServiceNotification) ar.result; mSsnRegistrants.notifyRegistrants(ar); break; case EVENT_SET_CALL_FORWARD_DONE: ar = (AsyncResult)msg.obj; IccRecords r = mIccRecords.get(); Cfu cfu = (Cfu) ar.userObj; if (ar.exception == null && r != null) { r.setVoiceCallForwardingFlag(1, msg.arg1 == 1, cfu.mSetCfNumber); } if (cfu.mOnComplete != null) { AsyncResult.forMessage(cfu.mOnComplete, ar.result, ar.exception); cfu.mOnComplete.sendToTarget(); } break; case EVENT_SET_VM_NUMBER_DONE: ar = (AsyncResult)msg.obj; if (IccVmNotSupportedException.class.isInstance(ar.exception)) { storeVoiceMailNumber(mVmNumber); ar.exception = null; } onComplete = (Message) ar.userObj; if (onComplete != null) { AsyncResult.forMessage(onComplete, ar.result, ar.exception); onComplete.sendToTarget(); } break; case EVENT_GET_CALL_FORWARD_DONE: ar = (AsyncResult)msg.obj; if (ar.exception == null) { handleCfuQueryResult((CallForwardInfo[])ar.result); } onComplete = (Message) ar.userObj; if (onComplete != null) { AsyncResult.forMessage(onComplete, ar.result, ar.exception); onComplete.sendToTarget(); } break; case EVENT_SET_NETWORK_AUTOMATIC: // Automatic network selection from EF_CSP SIM record ar = (AsyncResult) msg.obj; if (mSST.mSS.getIsManualSelection()) { setNetworkSelectionModeAutomatic((Message) ar.result); Rlog.d(LOG_TAG, "SET_NETWORK_SELECTION_AUTOMATIC: set to automatic"); } else { // prevent duplicate request which will push current PLMN to low priority Rlog.d(LOG_TAG, "SET_NETWORK_SELECTION_AUTOMATIC: already automatic, ignore"); } break; case EVENT_ICC_RECORD_EVENTS: ar = (AsyncResult)msg.obj; processIccRecordEvents((Integer)ar.result); break; case EVENT_SET_CLIR_COMPLETE: ar = (AsyncResult)msg.obj; if (ar.exception == null) { saveClirSetting(msg.arg1); } onComplete = (Message) ar.userObj; if (onComplete != null) { AsyncResult.forMessage(onComplete, ar.result, ar.exception); onComplete.sendToTarget(); } break; case EVENT_SS: ar = (AsyncResult)msg.obj; Rlog.d(LOG_TAG, "Event EVENT_SS received"); // SS data is already being handled through MMI codes. // So, this result if processed as MMI response would help // in re-using the existing functionality. GsmMmiCode mmi = new GsmMmiCode(this, mUiccApplication.get()); mmi.processSsData(ar); break; default: super.handleMessage(msg); } } protected UiccCardApplication getUiccCardApplication() { return mUiccController.getUiccCardApplication(mPhoneId, UiccController.APP_FAM_3GPP); } @Override protected void onUpdateIccAvailability() { if (mUiccController == null ) { return; } UiccCardApplication newUiccApplication = mUiccController.getUiccCardApplication(mPhoneId, UiccController.APP_FAM_IMS); IsimUiccRecords newIsimUiccRecords = null; if (newUiccApplication != null) { newIsimUiccRecords = (IsimUiccRecords)newUiccApplication.getIccRecords(); if (LOCAL_DEBUG) log("New ISIM application found"); } mIsimUiccRecords = newIsimUiccRecords; newUiccApplication = getUiccCardApplication(); UiccCardApplication app = mUiccApplication.get(); if (app != newUiccApplication) { if (app != null) { if (LOCAL_DEBUG) log("Removing stale icc objects."); if (mIccRecords.get() != null) { unregisterForSimRecordEvents(); mSimPhoneBookIntManager.updateIccRecords(null); } mIccRecords.set(null); mUiccApplication.set(null); } if (newUiccApplication != null) { if (LOCAL_DEBUG) log("New Uicc application found"); mUiccApplication.set(newUiccApplication); mIccRecords.set(newUiccApplication.getIccRecords()); registerForSimRecordEvents(); mSimPhoneBookIntManager.updateIccRecords(mIccRecords.get()); } } } private void processIccRecordEvents(int eventCode) { switch (eventCode) { case IccRecords.EVENT_CFI: notifyCallForwardingIndicator(); break; } } /** * Sets the "current" field in the telephony provider according to the SIM's operator * * @return true for success; false otherwise. */ public boolean updateCurrentCarrierInProvider() { long currentDds = SubscriptionManager.getDefaultDataSubId(); String operatorNumeric = getOperatorNumeric(); log("updateCurrentCarrierInProvider: mSubId = " + getSubId() + " currentDds = " + currentDds + " operatorNumeric = " + operatorNumeric); if (!TextUtils.isEmpty(operatorNumeric) && (getSubId() == currentDds)) { try { Uri uri = Uri.withAppendedPath(Telephony.Carriers.CONTENT_URI, "current"); ContentValues map = new ContentValues(); map.put(Telephony.Carriers.NUMERIC, operatorNumeric); mContext.getContentResolver().insert(uri, map); return true; } catch (SQLException e) { Rlog.e(LOG_TAG, "Can't store current operator", e); } } return false; } /** * Saves CLIR setting so that we can re-apply it as necessary * (in case the RIL resets it across reboots). */ public void saveClirSetting(int commandInterfaceCLIRMode) { // open the shared preferences editor, and write the value. SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext()); SharedPreferences.Editor editor = sp.edit(); editor.putInt(CLIR_KEY + getPhoneId(), commandInterfaceCLIRMode); // commit and log the result. if (! editor.commit()) { Rlog.e(LOG_TAG, "failed to commit CLIR preference"); } } private void handleCfuQueryResult(CallForwardInfo[] infos) { IccRecords r = mIccRecords.get(); if (r != null) { if (infos == null || infos.length == 0) { // Assume the default is not active // Set unconditional CFF in SIM to false r.setVoiceCallForwardingFlag(1, false, null); } else { for (int i = 0, s = infos.length; i < s; i++) { if ((infos[i].serviceClass & SERVICE_CLASS_VOICE) != 0) { r.setVoiceCallForwardingFlag(1, (infos[i].status == 1), infos[i].number); // should only have the one break; } } } } } /** * Retrieves the PhoneSubInfo of the GSMPhone */ @Override public PhoneSubInfo getPhoneSubInfo(){ return mSubInfo; } /** * Retrieves the IccPhoneBookInterfaceManager of the GSMPhone */ @Override public IccPhoneBookInterfaceManager getIccPhoneBookInterfaceManager(){ return mSimPhoneBookIntManager; } /** * Activate or deactivate cell broadcast SMS. * * @param activate 0 = activate, 1 = deactivate * @param response Callback message is empty on completion */ @Override public void activateCellBroadcastSms(int activate, Message response) { Rlog.e(LOG_TAG, "[GSMPhone] activateCellBroadcastSms() is obsolete; use SmsManager"); response.sendToTarget(); } /** * Query the current configuration of cdma cell broadcast SMS. * * @param response Callback message is empty on completion */ @Override public void getCellBroadcastSmsConfig(Message response) { Rlog.e(LOG_TAG, "[GSMPhone] getCellBroadcastSmsConfig() is obsolete; use SmsManager"); response.sendToTarget(); } /** * Configure cdma cell broadcast SMS. * * @param response Callback message is empty on completion */ @Override public void setCellBroadcastSmsConfig(int[] configValuesArray, Message response) { Rlog.e(LOG_TAG, "[GSMPhone] setCellBroadcastSmsConfig() is obsolete; use SmsManager"); response.sendToTarget(); } @Override public boolean isCspPlmnEnabled() { IccRecords r = mIccRecords.get(); return (r != null) ? r.isCspPlmnEnabled() : false; } boolean isManualNetSelAllowed() { int nwMode = Phone.PREFERRED_NT_MODE; int subId = getSubId(); nwMode = android.provider.Settings.Global.getInt(mContext.getContentResolver(), android.provider.Settings.Global.PREFERRED_NETWORK_MODE + subId, nwMode); Rlog.d(LOG_TAG, "isManualNetSelAllowed in mode = " + nwMode); /* * For multimode targets in global mode manual network * selection is disallowed */ if (isManualSelProhibitedInGlobalMode() && ((nwMode == Phone.NT_MODE_LTE_CDMA_EVDO_GSM_WCDMA) || (nwMode == Phone.NT_MODE_GLOBAL)) ){ Rlog.d(LOG_TAG, "Manual selection not supported in mode = " + nwMode); return false; } else { Rlog.d(LOG_TAG, "Manual selection is supported in mode = " + nwMode); } /* * Single mode phone with - GSM network modes/global mode * LTE only for 3GPP * LTE centric + 3GPP Legacy * Note: the actual enabling/disabling manual selection for these * cases will be controlled by csp */ return true; } private boolean isManualSelProhibitedInGlobalMode() { boolean isProhibited = false; final String configString = getContext().getResources().getString(com.android.internal. R.string.prohibit_manual_network_selection_in_gobal_mode); if (!TextUtils.isEmpty(configString)) { String[] configArray = configString.split(";"); if (configArray != null && ((configArray.length == 1 && configArray[0].equalsIgnoreCase("true")) || (configArray.length == 2 && !TextUtils.isEmpty(configArray[1]) && configArray[0].equalsIgnoreCase("true") && configArray[1].equalsIgnoreCase(getGroupIdLevel1())))) { isProhibited = true; } } Rlog.d(LOG_TAG, "isManualNetSelAllowedInGlobal in current carrier is " + isProhibited); return isProhibited; } private void registerForSimRecordEvents() { IccRecords r = mIccRecords.get(); if (r == null) { return; } r.registerForNetworkSelectionModeAutomatic( this, EVENT_SET_NETWORK_AUTOMATIC, null); r.registerForRecordsEvents(this, EVENT_ICC_RECORD_EVENTS, null); r.registerForRecordsLoaded(this, EVENT_SIM_RECORDS_LOADED, null); } private void unregisterForSimRecordEvents() { IccRecords r = mIccRecords.get(); if (r == null) { return; } r.unregisterForNetworkSelectionModeAutomatic(this); r.unregisterForRecordsEvents(this); r.unregisterForRecordsLoaded(this); } @Override public void exitEmergencyCallbackMode() { if (mImsPhone != null) { mImsPhone.exitEmergencyCallbackMode(); } } @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { pw.println("GSMPhone extends:"); super.dump(fd, pw, args); pw.println(" mCT=" + mCT); pw.println(" mSST=" + mSST); pw.println(" mPendingMMIs=" + mPendingMMIs); pw.println(" mSimPhoneBookIntManager=" + mSimPhoneBookIntManager); pw.println(" mSubInfo=" + mSubInfo); if (VDBG) pw.println(" mImei=" + mImei); if (VDBG) pw.println(" mImeiSv=" + mImeiSv); pw.println(" mVmNumber=" + mVmNumber); } @Override public boolean setOperatorBrandOverride(String brand) { if (mUiccController == null) { return false; } UiccCard card = mUiccController.getUiccCard(getPhoneId()); if (card == null) { return false; } boolean status = card.setOperatorBrandOverride(brand); // Refresh. if (status) { IccRecords iccRecords = mIccRecords.get(); if (iccRecords != null) { TelephonyManager.from(mContext).setSimOperatorNameForPhone( getPhoneId(), iccRecords.getServiceProviderName()); } if (mSST != null) { mSST.pollState(); } } return status; } /** * @return operator numeric. */ public String getOperatorNumeric() { String operatorNumeric = null; IccRecords r = mIccRecords.get(); if (r != null) { operatorNumeric = r.getOperatorNumeric(); } return operatorNumeric; } public void registerForAllDataDisconnected(Handler h, int what, Object obj) { ((DcTracker)mDcTracker) .registerForAllDataDisconnected(h, what, obj); } public void unregisterForAllDataDisconnected(Handler h) { ((DcTracker)mDcTracker).unregisterForAllDataDisconnected(h); } public void setInternalDataEnabled(boolean enable, Message onCompleteMsg) { ((DcTracker)mDcTracker) .setInternalDataEnabled(enable, onCompleteMsg); } public boolean setInternalDataEnabledFlag(boolean enable) { return ((DcTracker)mDcTracker) .setInternalDataEnabledFlag(enable); } public void notifyEcbmTimerReset(Boolean flag) { mEcmTimerResetRegistrants.notifyResult(flag); } /** * Registration point for Ecm timer reset * * @param h handler to notify * @param what User-defined message code * @param obj placed in Message.obj */ @Override public void registerForEcmTimerReset(Handler h, int what, Object obj) { mEcmTimerResetRegistrants.addUnique(h, what, obj); } @Override public void unregisterForEcmTimerReset(Handler h) { mEcmTimerResetRegistrants.remove(h); } /** * Sets the SIM voice message waiting indicator records. * @param line GSM Subscriber Profile Number, one-based. Only '1' is supported * @param countWaiting The number of messages waiting, if known. Use * -1 to indicate that an unknown number of * messages are waiting */ @Override public void setVoiceMessageWaiting(int line, int countWaiting) { IccRecords r = mIccRecords.get(); if (r != null) { r.setVoiceMessageWaiting(line, countWaiting); } else { log("SIM Records not found, MWI not updated"); } } protected void log(String s) { Rlog.d(LOG_TAG, "[GSMPhone] " + s); } }