diff options
Diffstat (limited to 'telephony/java/com')
60 files changed, 4888 insertions, 279 deletions
diff --git a/telephony/java/com/android/internal/telephony/AdnRecord.java b/telephony/java/com/android/internal/telephony/AdnRecord.java index 0896ba60ee71..1bf2d3c61d50 100644 --- a/telephony/java/com/android/internal/telephony/AdnRecord.java +++ b/telephony/java/com/android/internal/telephony/AdnRecord.java @@ -19,10 +19,9 @@ package com.android.internal.telephony; import android.os.Parcel; import android.os.Parcelable; import android.telephony.PhoneNumberUtils; +import android.text.TextUtils; import android.util.Log; -import com.android.internal.telephony.GsmAlphabet; - import java.util.Arrays; @@ -38,8 +37,8 @@ public class AdnRecord implements Parcelable { //***** Instance Variables - String alphaTag = ""; - String number = ""; + String alphaTag = null; + String number = null; String[] emails; int extRecord = 0xff; int efid; // or 0 if none @@ -63,8 +62,8 @@ public class AdnRecord implements Parcelable { // ADN offset static final int ADN_BCD_NUMBER_LENGTH = 0; static final int ADN_TON_AND_NPI = 1; - static final int ADN_DAILING_NUMBER_START = 2; - static final int ADN_DAILING_NUMBER_END = 11; + static final int ADN_DIALING_NUMBER_START = 2; + static final int ADN_DIALING_NUMBER_END = 11; static final int ADN_CAPABILITY_ID = 12; static final int ADN_EXTENSION_ID = 13; @@ -152,17 +151,31 @@ public class AdnRecord implements Parcelable { } public boolean isEmpty() { - return alphaTag.equals("") && number.equals("") && emails == null; + return TextUtils.isEmpty(alphaTag) && TextUtils.isEmpty(number) && emails == null; } public boolean hasExtendedRecord() { return extRecord != 0 && extRecord != 0xff; } + /** Helper function for {@link #isEqual}. */ + private static boolean stringCompareNullEqualsEmpty(String s1, String s2) { + if (s1 == s2) { + return true; + } + if (s1 == null) { + s1 = ""; + } + if (s2 == null) { + s2 = ""; + } + return (s1.equals(s2)); + } + public boolean isEqual(AdnRecord adn) { - return ( alphaTag.equals(adn.getAlphaTag()) && - number.equals(adn.getNumber()) && - Arrays.equals(emails, adn.getEmails())); + return ( stringCompareNullEqualsEmpty(alphaTag, adn.alphaTag) && + stringCompareNullEqualsEmpty(number, adn.number) && + Arrays.equals(emails, adn.emails)); } //***** Parcelable Implementation @@ -184,36 +197,33 @@ public class AdnRecord implements Parcelable { * * @param recordSize is the size X of EF record * @return hex byte[recordSize] to be written to EF record - * return nulll for wrong format of dialing nubmer or tag + * return null for wrong format of dialing number or tag */ public byte[] buildAdnString(int recordSize) { byte[] bcdNumber; byte[] byteTag; - byte[] adnString = null; + byte[] adnString; int footerOffset = recordSize - FOOTER_SIZE_BYTES; - if (number == null || number.equals("") || - alphaTag == null || alphaTag.equals("")) { + // create an empty record + adnString = new byte[recordSize]; + for (int i = 0; i < recordSize; i++) { + adnString[i] = (byte) 0xFF; + } - Log.w(LOG_TAG, "[buildAdnString] Empty alpha tag or number"); - adnString = new byte[recordSize]; - for (int i = 0; i < recordSize; i++) { - adnString[i] = (byte) 0xFF; - } + if (TextUtils.isEmpty(number)) { + Log.w(LOG_TAG, "[buildAdnString] Empty dialing number"); + return adnString; // return the empty record (for delete) } else if (number.length() - > (ADN_DAILING_NUMBER_END - ADN_DAILING_NUMBER_START + 1) * 2) { + > (ADN_DIALING_NUMBER_END - ADN_DIALING_NUMBER_START + 1) * 2) { Log.w(LOG_TAG, - "[buildAdnString] Max length of dailing number is 20"); - } else if (alphaTag.length() > footerOffset) { + "[buildAdnString] Max length of dialing number is 20"); + return null; + } else if (alphaTag != null && alphaTag.length() > footerOffset) { Log.w(LOG_TAG, "[buildAdnString] Max length of tag is " + footerOffset); + return null; } else { - - adnString = new byte[recordSize]; - for (int i = 0; i < recordSize; i++) { - adnString[i] = (byte) 0xFF; - } - bcdNumber = PhoneNumberUtils.numberToCalledPartyBCD(number); System.arraycopy(bcdNumber, 0, adnString, @@ -222,16 +232,17 @@ public class AdnRecord implements Parcelable { adnString[footerOffset + ADN_BCD_NUMBER_LENGTH] = (byte) (bcdNumber.length); adnString[footerOffset + ADN_CAPABILITY_ID] - = (byte) 0xFF; // Capacility Id + = (byte) 0xFF; // Capability Id adnString[footerOffset + ADN_EXTENSION_ID] = (byte) 0xFF; // Extension Record Id - byteTag = GsmAlphabet.stringToGsm8BitPacked(alphaTag); - System.arraycopy(byteTag, 0, adnString, 0, byteTag.length); + if (!TextUtils.isEmpty(alphaTag)) { + byteTag = GsmAlphabet.stringToGsm8BitPacked(alphaTag); + System.arraycopy(byteTag, 0, adnString, 0, byteTag.length); + } + return adnString; } - - return adnString; } /** diff --git a/telephony/java/com/android/internal/telephony/AdnRecordLoader.java b/telephony/java/com/android/internal/telephony/AdnRecordLoader.java index cfb5aaa84b39..55bdc06b8aea 100644 --- a/telephony/java/com/android/internal/telephony/AdnRecordLoader.java +++ b/telephony/java/com/android/internal/telephony/AdnRecordLoader.java @@ -106,7 +106,7 @@ public class AdnRecordLoader extends Handler { * It will get the record size of EF record and compose hex adn array * then write the hex array to EF record * - * @param adn is set with alphaTag and phoneNubmer + * @param adn is set with alphaTag and phone number * @param ef EF fileid * @param extensionEF extension EF fileid * @param recordNumber 1-based record index @@ -159,7 +159,7 @@ public class AdnRecordLoader extends Handler { data = adn.buildAdnString(recordSize[0]); if(data == null) { - throw new RuntimeException("worong ADN format", + throw new RuntimeException("wrong ADN format", ar.exception); } @@ -218,7 +218,7 @@ public class AdnRecordLoader extends Handler { throw new RuntimeException("load failed", ar.exception); } - Log.d(LOG_TAG,"ADN extention EF: 0x" + Log.d(LOG_TAG,"ADN extension EF: 0x" + Integer.toHexString(extensionEF) + ":" + adn.extRecord + "\n" + IccUtils.bytesToHexString(data)); diff --git a/telephony/java/com/android/internal/telephony/CallManager.java b/telephony/java/com/android/internal/telephony/CallManager.java new file mode 100644 index 000000000000..4bf32829e761 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/CallManager.java @@ -0,0 +1,1414 @@ +/* + * Copyright (C) 2010 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 com.android.internal.telephony.sip.SipPhone; + +import android.content.Context; +import android.media.AudioManager; +import android.os.AsyncResult; +import android.os.Handler; +import android.os.Message; +import android.os.RegistrantList; +import android.telephony.PhoneStateListener; +import android.telephony.ServiceState; +import android.util.Log; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + + + +/** + * @hide + * + * CallManager class provides an abstract layer for PhoneApp to access + * and control calls. It implements Phone interface. + * + * CallManager provides call and connection control as well as + * channel capability. + * + * There are three categories of APIs CallManager provided + * + * 1. Call control and operation, such as dial() and hangup() + * 2. Channel capabilities, such as CanConference() + * 3. Register notification + * + * + */ +public final class CallManager { + + private static final String LOG_TAG ="Phone"; + private static final boolean LOCAL_DEBUG = true; + + private static final int EVENT_DISCONNECT = 100; + private static final int EVENT_PRECISE_CALL_STATE_CHANGED = 101; + private static final int EVENT_NEW_RINGING_CONNECTION = 102; + private static final int EVENT_UNKNOWN_CONNECTION = 103; + private static final int EVENT_INCOMING_RING = 104; + private static final int EVENT_RINGBACK_TONE = 105; + private static final int EVENT_IN_CALL_VOICE_PRIVACY_ON = 106; + private static final int EVENT_IN_CALL_VOICE_PRIVACY_OFF = 107; + private static final int EVENT_CALL_WAITING = 108; + private static final int EVENT_DISPLAY_INFO = 109; + private static final int EVENT_SIGNAL_INFO = 110; + private static final int EVENT_CDMA_OTA_STATUS_CHANGE = 111; + private static final int EVENT_RESEND_INCALL_MUTE = 112; + private static final int EVENT_MMI_INITIATE = 113; + private static final int EVENT_MMI_COMPLETE = 114; + private static final int EVENT_ECM_TIMER_RESET = 115; + private static final int EVENT_SUBSCRIPTION_INFO_READY = 116; + private static final int EVENT_SUPP_SERVICE_FAILED = 117; + private static final int EVENT_SERVICE_STATE_CHANGED = 118; + + // Singleton instance + private static final CallManager INSTANCE = new CallManager(); + + // list of registered phones + private final ArrayList<Phone> mPhones; + + // list of supported ringing calls + private final ArrayList<Call> mRingingCalls; + + // list of supported background calls + private final ArrayList<Call> mBackgroundCalls; + + // list of supported foreground calls + private final ArrayList<Call> mForegroundCalls; + + // empty connection list + private final ArrayList<Connection> emptyConnections = new ArrayList<Connection>(); + + // default phone as the first phone registered + private Phone mDefaultPhone; + + // state registrants + protected final RegistrantList mPreciseCallStateRegistrants + = new RegistrantList(); + + protected final RegistrantList mNewRingingConnectionRegistrants + = new RegistrantList(); + + protected final RegistrantList mIncomingRingRegistrants + = new RegistrantList(); + + protected final RegistrantList mDisconnectRegistrants + = new RegistrantList(); + + protected final RegistrantList mMmiRegistrants + = new RegistrantList(); + + protected final RegistrantList mUnknownConnectionRegistrants + = new RegistrantList(); + + protected final RegistrantList mRingbackToneRegistrants + = new RegistrantList(); + + protected final RegistrantList mInCallVoicePrivacyOnRegistrants + = new RegistrantList(); + + protected final RegistrantList mInCallVoicePrivacyOffRegistrants + = new RegistrantList(); + + protected final RegistrantList mCallWaitingRegistrants + = new RegistrantList(); + + protected final RegistrantList mDisplayInfoRegistrants + = new RegistrantList(); + + protected final RegistrantList mSignalInfoRegistrants + = new RegistrantList(); + + protected final RegistrantList mCdmaOtaStatusChangeRegistrants + = new RegistrantList(); + + protected final RegistrantList mResendIncallMuteRegistrants + = new RegistrantList(); + + protected final RegistrantList mMmiInitiateRegistrants + = new RegistrantList(); + + protected final RegistrantList mMmiCompleteRegistrants + = new RegistrantList(); + + protected final RegistrantList mEcmTimerResetRegistrants + = new RegistrantList(); + + protected final RegistrantList mSubscriptionInfoReadyRegistrants + = new RegistrantList(); + + protected final RegistrantList mSuppServiceFailedRegistrants + = new RegistrantList(); + + protected final RegistrantList mServiceStateChangedRegistrants + = new RegistrantList(); + + private CallManager() { + mPhones = new ArrayList<Phone>(); + mRingingCalls = new ArrayList<Call>(); + mBackgroundCalls = new ArrayList<Call>(); + mForegroundCalls = new ArrayList<Call>(); + mDefaultPhone = null; + } + + /** + * get singleton instance of CallManager + * @return CallManager + */ + public static CallManager getInstance() { + return INSTANCE; + } + + /** + * Returns all the registered phone objects. + * @return all the registered phone objects. + */ + public List<Phone> getAllPhones() { + return Collections.unmodifiableList(mPhones); + } + + /** + * Get current coarse-grained voice call state. + * If the Call Manager has an active call and call waiting occurs, + * then the phone state is RINGING not OFFHOOK + * + */ + public Phone.State getState() { + Phone.State s = Phone.State.IDLE; + + for (Phone phone : mPhones) { + if (phone.getState() == Phone.State.RINGING) { + return Phone.State.RINGING; + } else if (phone.getState() == Phone.State.OFFHOOK) { + s = Phone.State.OFFHOOK; + } + } + return s; + } + + /** + * @return the service state of CallManager, which represents the + * highest priority state of all the service states of phones + * + * The priority is defined as + * + * STATE_IN_SERIVCE > STATE_OUT_OF_SERIVCE > STATE_EMERGENCY > STATE_POWER_OFF + * + */ + + public int getServiceState() { + int resultState = ServiceState.STATE_OUT_OF_SERVICE; + + for (Phone phone : mPhones) { + int serviceState = phone.getServiceState().getState(); + if (serviceState == ServiceState.STATE_IN_SERVICE) { + // IN_SERVICE has the highest priority + resultState = serviceState; + break; + } else if (serviceState == ServiceState.STATE_OUT_OF_SERVICE) { + // OUT_OF_SERVICE replaces EMERGENCY_ONLY and POWER_OFF + // Note: EMERGENCY_ONLY is not in use at this moment + if ( resultState == ServiceState.STATE_EMERGENCY_ONLY || + resultState == ServiceState.STATE_POWER_OFF) { + resultState = serviceState; + } + } else if (serviceState == ServiceState.STATE_EMERGENCY_ONLY) { + if (resultState == ServiceState.STATE_POWER_OFF) { + resultState = serviceState; + } + } + } + return resultState; + } + + /** + * Register phone to CallManager + * @param phone + * @return true if register successfully + */ + public boolean registerPhone(Phone phone) { + if (phone != null && !mPhones.contains(phone)) { + if (mPhones.isEmpty()) { + mDefaultPhone = phone; + } + mPhones.add(phone); + mRingingCalls.add(phone.getRingingCall()); + mBackgroundCalls.add(phone.getBackgroundCall()); + mForegroundCalls.add(phone.getForegroundCall()); + registerForPhoneStates(phone); + return true; + } + return false; + } + + /** + * return the default phone or null if no phone available + */ + public Phone getDefaultPhone() { + return mDefaultPhone; + } + + /** + * @return the phone associated with the foreground call + */ + public Phone getFgPhone() { + return getActiveFgCall().getPhone(); + } + + /** + * @return the phone associated with the background call + */ + public Phone getBgPhone() { + return getFirstActiveBgCall().getPhone(); + } + + /** + * @return the phone associated with the ringing call + */ + public Phone getRingingPhone() { + return getFirstActiveRingingCall().getPhone(); + } + + /** + * unregister phone from CallManager + * @param phone + */ + public void unregisterPhone(Phone phone) { + if (phone != null && mPhones.contains(phone)) { + mPhones.remove(phone); + mRingingCalls.remove(phone.getRingingCall()); + mBackgroundCalls.remove(phone.getBackgroundCall()); + mForegroundCalls.remove(phone.getForegroundCall()); + unregisterForPhoneStates(phone); + if (phone == mDefaultPhone) { + if (mPhones.isEmpty()) { + mDefaultPhone = null; + } else { + mDefaultPhone = mPhones.get(0); + } + } + } + } + + public void setAudioMode() { + Context context = getContext(); + if (context == null) return; + AudioManager audioManager = (AudioManager) + context.getSystemService(Context.AUDIO_SERVICE); + + int mode = AudioManager.MODE_NORMAL; + switch (getState()) { + case RINGING: + mode = AudioManager.MODE_RINGTONE; + break; + case OFFHOOK: + Phone fgPhone = getFgPhone(); + if (!(fgPhone instanceof SipPhone)) { + mode = AudioManager.MODE_IN_CALL; + } + break; + } + // calling audioManager.setMode() multiple times in a short period of + // time seems to break the audio recorder in in-call mode + if (audioManager.getMode() != mode) audioManager.setMode(mode); + } + + private Context getContext() { + Phone defaultPhone = getDefaultPhone(); + return ((defaultPhone == null) ? null : defaultPhone.getContext()); + } + + private void registerForPhoneStates(Phone phone) { + phone.registerForPreciseCallStateChanged(mHandler, EVENT_PRECISE_CALL_STATE_CHANGED, null); + phone.registerForDisconnect(mHandler, EVENT_DISCONNECT, null); + phone.registerForNewRingingConnection(mHandler, EVENT_NEW_RINGING_CONNECTION, null); + phone.registerForUnknownConnection(mHandler, EVENT_UNKNOWN_CONNECTION, null); + phone.registerForIncomingRing(mHandler, EVENT_INCOMING_RING, null); + phone.registerForRingbackTone(mHandler, EVENT_RINGBACK_TONE, null); + phone.registerForInCallVoicePrivacyOn(mHandler, EVENT_IN_CALL_VOICE_PRIVACY_ON, null); + phone.registerForInCallVoicePrivacyOff(mHandler, EVENT_IN_CALL_VOICE_PRIVACY_OFF, null); + phone.registerForCallWaiting(mHandler, EVENT_CALL_WAITING, null); + phone.registerForDisplayInfo(mHandler, EVENT_DISPLAY_INFO, null); + phone.registerForSignalInfo(mHandler, EVENT_SIGNAL_INFO, null); + phone.registerForCdmaOtaStatusChange(mHandler, EVENT_CDMA_OTA_STATUS_CHANGE, null); + phone.registerForResendIncallMute(mHandler, EVENT_RESEND_INCALL_MUTE, null); + phone.registerForMmiInitiate(mHandler, EVENT_MMI_INITIATE, null); + phone.registerForMmiComplete(mHandler, EVENT_MMI_COMPLETE, null); + phone.registerForEcmTimerReset(mHandler, EVENT_ECM_TIMER_RESET, null); + phone.registerForSubscriptionInfoReady(mHandler, EVENT_SUBSCRIPTION_INFO_READY, null); + phone.registerForSuppServiceFailed(mHandler, EVENT_SUPP_SERVICE_FAILED, null); + phone.registerForServiceStateChanged(mHandler, EVENT_SERVICE_STATE_CHANGED, null); + } + + private void unregisterForPhoneStates(Phone phone) { + phone.unregisterForPreciseCallStateChanged(mHandler); + phone.unregisterForDisconnect(mHandler); + phone.unregisterForNewRingingConnection(mHandler); + phone.unregisterForUnknownConnection(mHandler); + phone.unregisterForIncomingRing(mHandler); + phone.unregisterForRingbackTone(mHandler); + phone.unregisterForInCallVoicePrivacyOn(mHandler); + phone.unregisterForInCallVoicePrivacyOff(mHandler); + phone.unregisterForCallWaiting(mHandler); + phone.unregisterForDisplayInfo(mHandler); + phone.unregisterForSignalInfo(mHandler); + phone.unregisterForCdmaOtaStatusChange(mHandler); + phone.unregisterForResendIncallMute(mHandler); + phone.unregisterForMmiInitiate(mHandler); + phone.unregisterForMmiComplete(mHandler); + phone.unregisterForEcmTimerReset(mHandler); + phone.unregisterForSubscriptionInfoReady(mHandler); + phone.unregisterForSuppServiceFailed(mHandler); + phone.unregisterForServiceStateChanged(mHandler); + } + + /** + * Answers a ringing or waiting call. + * + * Active call, if any, go on hold. + * If active call can't be held, i.e., a background call of the same channel exists, + * the active call will be hang up. + * + * Answering occurs asynchronously, and final notification occurs via + * {@link #registerForPreciseCallStateChanged(android.os.Handler, int, + * java.lang.Object) registerForPreciseCallStateChanged()}. + * + * @exception CallStateException when call is not ringing or waiting + */ + public void acceptCall(Call ringingCall) throws CallStateException { + Phone ringingPhone = ringingCall.getPhone(); + + if ( hasActiveFgCall() ) { + Phone activePhone = getActiveFgCall().getPhone(); + boolean hasBgCall = ! (activePhone.getBackgroundCall().isIdle()); + boolean sameChannel = (activePhone == ringingPhone); + + if (LOCAL_DEBUG) { + Log.d(LOG_TAG, "hasBgCall: "+ hasBgCall + "sameChannel:" + sameChannel); + } + + if (sameChannel && hasBgCall) { + getActiveFgCall().hangup(); + } else if (!sameChannel && !hasBgCall) { + activePhone.switchHoldingAndActive(); + } else if (!sameChannel && hasBgCall) { + getActiveFgCall().hangup(); + } + } + + ringingPhone.acceptCall(); + } + + /** + * Reject (ignore) a ringing call. In GSM, this means UDUB + * (User Determined User Busy). Reject occurs asynchronously, + * and final notification occurs via + * {@link #registerForPreciseCallStateChanged(android.os.Handler, int, + * java.lang.Object) registerForPreciseCallStateChanged()}. + * + * @exception CallStateException when no call is ringing or waiting + */ + public void rejectCall(Call ringingCall) throws CallStateException { + Phone ringingPhone = ringingCall.getPhone(); + + ringingPhone.rejectCall(); + } + + /** + * Places active call on hold, and makes held call active. + * Switch occurs asynchronously and may fail. + * + * There are 4 scenarios + * 1. only active call but no held call, aka, hold + * 2. no active call but only held call, aka, unhold + * 3. both active and held calls from same phone, aka, swap + * 4. active and held calls from different phones, aka, phone swap + * + * Final notification occurs via + * {@link #registerForPreciseCallStateChanged(android.os.Handler, int, + * java.lang.Object) registerForPreciseCallStateChanged()}. + * + * @exception CallStateException if active call is ringing, waiting, or + * dialing/alerting, or heldCall can't be active. + * In these cases, this operation may not be performed. + */ + public void switchHoldingAndActive(Call heldCall) throws CallStateException { + Phone activePhone = null; + Phone heldPhone = null; + + if (hasActiveFgCall()) { + activePhone = getActiveFgCall().getPhone(); + } + + if (heldCall != null) { + heldPhone = heldCall.getPhone(); + } + + if (activePhone != null) { + activePhone.switchHoldingAndActive(); + } + + if (heldPhone != null && heldPhone != activePhone) { + heldPhone.switchHoldingAndActive(); + } + } + + /** + * Hangup foreground call and resume the specific background call + * + * Note: this is noop if there is no foreground call or the heldCall is null + * + * @param heldCall to become foreground + * @throws CallStateException + */ + public void hangupForegroundResumeBackground(Call heldCall) throws CallStateException { + Phone foregroundPhone = null; + Phone backgroundPhone = null; + + if (hasActiveFgCall()) { + foregroundPhone = getFgPhone(); + if (heldCall != null) { + backgroundPhone = heldCall.getPhone(); + if (foregroundPhone == backgroundPhone) { + getActiveFgCall().hangup(); + } else { + // the call to be hangup and resumed belongs to different phones + getActiveFgCall().hangup(); + switchHoldingAndActive(heldCall); + } + } + } + } + + /** + * Whether or not the phone can conference in the current phone + * state--that is, one call holding and one call active. + * @return true if the phone can conference; false otherwise. + */ + public boolean canConference(Call heldCall) { + Phone activePhone = null; + Phone heldPhone = null; + + if (hasActiveFgCall()) { + activePhone = getActiveFgCall().getPhone(); + } + + if (heldCall != null) { + heldPhone = heldCall.getPhone(); + } + + return heldPhone.getClass().equals(activePhone.getClass()); + } + + /** + * Conferences holding and active. Conference occurs asynchronously + * and may fail. Final notification occurs via + * {@link #registerForPreciseCallStateChanged(android.os.Handler, int, + * java.lang.Object) registerForPreciseCallStateChanged()}. + * + * @exception CallStateException if canConference() would return false. + * In these cases, this operation may not be performed. + */ + public void conference(Call heldCall) throws CallStateException { + Phone fgPhone = getFgPhone(); + if (fgPhone instanceof SipPhone) { + ((SipPhone) fgPhone).conference(heldCall); + } else if (canConference(heldCall)) { + fgPhone.conference(); + } else { + throw(new CallStateException("Can't conference foreground and selected background call")); + } + } + + /** + * Initiate a new voice connection. This happens asynchronously, so you + * cannot assume the audio path is connected (or a call index has been + * assigned) until PhoneStateChanged notification has occurred. + * + * @exception CallStateException if a new outgoing call is not currently + * possible because no more call slots exist or a call exists that is + * dialing, alerting, ringing, or waiting. Other errors are + * handled asynchronously. + */ + public Connection dial(Phone phone, String dialString) throws CallStateException { + if ( hasActiveFgCall() ) { + Phone activePhone = getActiveFgCall().getPhone(); + boolean hasBgCall = !(activePhone.getBackgroundCall().isIdle()); + + if (LOCAL_DEBUG) { + Log.d(LOG_TAG, "hasBgCall: "+ hasBgCall + "sameChannel:" + (activePhone != phone)); + } + + if (activePhone != phone) { + if (hasBgCall) { + Log.d(LOG_TAG, "Hangup"); + getActiveFgCall().hangup(); + } else { + Log.d(LOG_TAG, "Switch"); + activePhone.switchHoldingAndActive(); + } + } + } + return phone.dial(dialString); + } + + /** + * Initiate a new voice connection. This happens asynchronously, so you + * cannot assume the audio path is connected (or a call index has been + * assigned) until PhoneStateChanged notification has occurred. + * + * @exception CallStateException if a new outgoing call is not currently + * possible because no more call slots exist or a call exists that is + * dialing, alerting, ringing, or waiting. Other errors are + * handled asynchronously. + */ + public Connection dial(Phone phone, String dialString, UUSInfo uusInfo) throws CallStateException { + return phone.dial(dialString, uusInfo); + } + + /** + * clear disconnect connection for each phone + */ + public void clearDisconnected() { + for(Phone phone : mPhones) { + phone.clearDisconnected(); + } + } + + /** + * Whether or not the phone can do explicit call transfer in the current + * phone state--that is, one call holding and one call active. + * @return true if the phone can do explicit call transfer; false otherwise. + */ + public boolean canTransfer(Call heldCall) { + Phone activePhone = null; + Phone heldPhone = null; + + if (hasActiveFgCall()) { + activePhone = getActiveFgCall().getPhone(); + } + + if (heldCall != null) { + heldPhone = heldCall.getPhone(); + } + + return (heldPhone == activePhone && activePhone.canTransfer()); + } + + /** + * Connects the held call and active call + * Disconnects the subscriber from both calls + * + * Explicit Call Transfer occurs asynchronously + * and may fail. Final notification occurs via + * {@link #registerForPreciseCallStateChanged(android.os.Handler, int, + * java.lang.Object) registerForPreciseCallStateChanged()}. + * + * @exception CallStateException if canTransfer() would return false. + * In these cases, this operation may not be performed. + */ + public void explicitCallTransfer(Call heldCall) throws CallStateException { + if (canTransfer(heldCall)) { + heldCall.getPhone().explicitCallTransfer(); + } + } + + /** + * Returns a list of MMI codes that are pending for a phone. (They have initiated + * but have not yet completed). + * Presently there is only ever one. + * + * Use <code>registerForMmiInitiate</code> + * and <code>registerForMmiComplete</code> for change notification. + * @return null if phone doesn't have or support mmi code + */ + public List<? extends MmiCode> getPendingMmiCodes(Phone phone) { + return null; + } + + /** + * Sends user response to a USSD REQUEST message. An MmiCode instance + * representing this response is sent to handlers registered with + * registerForMmiInitiate. + * + * @param ussdMessge Message to send in the response. + * @return false if phone doesn't support ussd service + */ + public boolean sendUssdResponse(Phone phone, String ussdMessge) { + return false; + } + + /** + * Mutes or unmutes the microphone for the active call. The microphone + * is automatically unmuted if a call is answered, dialed, or resumed + * from a holding state. + * + * @param muted true to mute the microphone, + * false to activate the microphone. + */ + + public void setMute(boolean muted) { + if (hasActiveFgCall()) { + getActiveFgCall().getPhone().setMute(muted); + } + } + + /** + * Gets current mute status. Use + * {@link #registerForPreciseCallStateChanged(android.os.Handler, int, + * java.lang.Object) registerForPreciseCallStateChanged()} + * as a change notifcation, although presently phone state changed is not + * fired when setMute() is called. + * + * @return true is muting, false is unmuting + */ + public boolean getMute() { + if (hasActiveFgCall()) { + return getActiveFgCall().getPhone().getMute(); + } + return false; + } + + /** + * Play a DTMF tone on the active call. + * + * @param c should be one of 0-9, '*' or '#'. Other values will be + * silently ignored. + * @return false if no active call or the active call doesn't support + * dtmf tone + */ + public boolean sendDtmf(char c) { + if (hasActiveFgCall()) { + getActiveFgCall().getPhone().sendDtmf(c); + return true; + } + return false; + } + + /** + * Start to paly a DTMF tone on the active call. + * or there is a playing DTMF tone. + * @param c should be one of 0-9, '*' or '#'. Other values will be + * silently ignored. + * + * @return false if no active call or the active call doesn't support + * dtmf tone + */ + public boolean startDtmf(char c) { + if (hasActiveFgCall()) { + getActiveFgCall().getPhone().sendDtmf(c); + return true; + } + return false; + } + + /** + * Stop the playing DTMF tone. Ignored if there is no playing DTMF + * tone or no active call. + */ + public void stopDtmf() { + if (hasActiveFgCall()) getFgPhone().stopDtmf(); + } + + /** + * send burst DTMF tone, it can send the string as single character or multiple character + * ignore if there is no active call or not valid digits string. + * Valid digit means only includes characters ISO-LATIN characters 0-9, *, # + * The difference between sendDtmf and sendBurstDtmf is sendDtmf only sends one character, + * this api can send single character and multiple character, also, this api has response + * back to caller. + * + * @param dtmfString is string representing the dialing digit(s) in the active call + * @param on the DTMF ON length in milliseconds, or 0 for default + * @param off the DTMF OFF length in milliseconds, or 0 for default + * @param onComplete is the callback message when the action is processed by BP + * + */ + public boolean sendBurstDtmf(String dtmfString, int on, int off, Message onComplete) { + if (hasActiveFgCall()) { + getActiveFgCall().getPhone().sendBurstDtmf(dtmfString, on, off, onComplete); + return true; + } + return false; + } + + /** + * Notifies when a voice connection has disconnected, either due to local + * or remote hangup or error. + * + * Messages received from this will have the following members:<p> + * <ul><li>Message.obj will be an AsyncResult</li> + * <li>AsyncResult.userObj = obj</li> + * <li>AsyncResult.result = a Connection object that is + * no longer connected.</li></ul> + */ + public void registerForDisconnect(Handler h, int what, Object obj) { + mDisconnectRegistrants.addUnique(h, what, obj); + } + + /** + * Unregisters for voice disconnection notification. + * Extraneous calls are tolerated silently + */ + public void unregisterForDisconnect(Handler h){ + mDisconnectRegistrants.remove(h); + } + + /** + * Register for getting notifications for change in the Call State {@link Call.State} + * This is called PreciseCallState because the call state is more precise than the + * {@link Phone.State} which can be obtained using the {@link PhoneStateListener} + * + * Resulting events will have an AsyncResult in <code>Message.obj</code>. + * AsyncResult.userData will be set to the obj argument here. + * The <em>h</em> parameter is held only by a weak reference. + */ + public void registerForPreciseCallStateChanged(Handler h, int what, Object obj){ + mPreciseCallStateRegistrants.addUnique(h, what, obj); + } + + /** + * Unregisters for voice call state change notifications. + * Extraneous calls are tolerated silently. + */ + public void unregisterForPreciseCallStateChanged(Handler h){ + mPreciseCallStateRegistrants.remove(h); + } + + /** + * Notifies when a previously untracked non-ringing/waiting connection has appeared. + * This is likely due to some other entity (eg, SIM card application) initiating a call. + */ + public void registerForUnknownConnection(Handler h, int what, Object obj){ + mUnknownConnectionRegistrants.addUnique(h, what, obj); + } + + /** + * Unregisters for unknown connection notifications. + */ + public void unregisterForUnknownConnection(Handler h){ + mUnknownConnectionRegistrants.remove(h); + } + + + /** + * Notifies when a new ringing or waiting connection has appeared.<p> + * + * Messages received from this: + * Message.obj will be an AsyncResult + * AsyncResult.userObj = obj + * AsyncResult.result = a Connection. <p> + * Please check Connection.isRinging() to make sure the Connection + * has not dropped since this message was posted. + * If Connection.isRinging() is true, then + * Connection.getCall() == Phone.getRingingCall() + */ + public void registerForNewRingingConnection(Handler h, int what, Object obj){ + mNewRingingConnectionRegistrants.addUnique(h, what, obj); + } + + /** + * Unregisters for new ringing connection notification. + * Extraneous calls are tolerated silently + */ + + public void unregisterForNewRingingConnection(Handler h){ + mNewRingingConnectionRegistrants.remove(h); + } + + /** + * Notifies when an incoming call rings.<p> + * + * Messages received from this: + * Message.obj will be an AsyncResult + * AsyncResult.userObj = obj + * AsyncResult.result = a Connection. <p> + */ + public void registerForIncomingRing(Handler h, int what, Object obj){ + mIncomingRingRegistrants.addUnique(h, what, obj); + } + + /** + * Unregisters for ring notification. + * Extraneous calls are tolerated silently + */ + + public void unregisterForIncomingRing(Handler h){ + mIncomingRingRegistrants.remove(h); + } + + /** + * Notifies when out-band ringback tone is needed.<p> + * + * Messages received from this: + * Message.obj will be an AsyncResult + * AsyncResult.userObj = obj + * AsyncResult.result = boolean, true to start play ringback tone + * and false to stop. <p> + */ + public void registerForRingbackTone(Handler h, int what, Object obj){ + mRingbackToneRegistrants.addUnique(h, what, obj); + } + + /** + * Unregisters for ringback tone notification. + */ + + public void unregisterForRingbackTone(Handler h){ + mRingbackToneRegistrants.remove(h); + } + + /** + * Registers the handler to reset the uplink mute state to get + * uplink audio. + */ + public void registerForResendIncallMute(Handler h, int what, Object obj){ + mResendIncallMuteRegistrants.addUnique(h, what, obj); + } + + /** + * Unregisters for resend incall mute notifications. + */ + public void unregisterForResendIncallMute(Handler h){ + mResendIncallMuteRegistrants.remove(h); + } + + /** + * Register for notifications of initiation of a new MMI code request. + * MMI codes for GSM are discussed in 3GPP TS 22.030.<p> + * + * Example: If Phone.dial is called with "*#31#", then the app will + * be notified here.<p> + * + * The returned <code>Message.obj</code> will contain an AsyncResult. + * + * <code>obj.result</code> will be an "MmiCode" object. + */ + public void registerForMmiInitiate(Handler h, int what, Object obj){ + mMmiInitiateRegistrants.addUnique(h, what, obj); + } + + /** + * Unregisters for new MMI initiate notification. + * Extraneous calls are tolerated silently + */ + public void unregisterForMmiInitiate(Handler h){ + mMmiInitiateRegistrants.remove(h); + } + + /** + * Register for notifications that an MMI request has completed + * its network activity and is in its final state. This may mean a state + * of COMPLETE, FAILED, or CANCELLED. + * + * <code>Message.obj</code> will contain an AsyncResult. + * <code>obj.result</code> will be an "MmiCode" object + */ + public void registerForMmiComplete(Handler h, int what, Object obj){ + mMmiCompleteRegistrants.addUnique(h, what, obj); + } + + /** + * Unregisters for MMI complete notification. + * Extraneous calls are tolerated silently + */ + public void unregisterForMmiComplete(Handler h){ + mMmiCompleteRegistrants.remove(h); + } + + /** + * Registration point for Ecm timer reset + * @param h handler to notify + * @param what user-defined message code + * @param obj placed in Message.obj + */ + public void registerForEcmTimerReset(Handler h, int what, Object obj){ + mEcmTimerResetRegistrants.addUnique(h, what, obj); + } + + /** + * Unregister for notification for Ecm timer reset + * @param h Handler to be removed from the registrant list. + */ + public void unregisterForEcmTimerReset(Handler h){ + mEcmTimerResetRegistrants.remove(h); + } + + /** + * Register for ServiceState changed. + * Message.obj will contain an AsyncResult. + * AsyncResult.result will be a ServiceState instance + */ + public void registerForServiceStateChanged(Handler h, int what, Object obj){ + mServiceStateChangedRegistrants.addUnique(h, what, obj); + } + + /** + * Unregisters for ServiceStateChange notification. + * Extraneous calls are tolerated silently + */ + public void unregisterForServiceStateChanged(Handler h){ + mServiceStateChangedRegistrants.remove(h); + } + + /** + * Register for notifications when a supplementary service attempt fails. + * Message.obj will contain an AsyncResult. + * + * @param h Handler that receives the notification message. + * @param what User-defined message code. + * @param obj User object. + */ + public void registerForSuppServiceFailed(Handler h, int what, Object obj){ + mSuppServiceFailedRegistrants.addUnique(h, what, obj); + } + + /** + * Unregister for notifications when a supplementary service attempt fails. + * Extraneous calls are tolerated silently + * + * @param h Handler to be removed from the registrant list. + */ + public void unregisterForSuppServiceFailed(Handler h){ + mSuppServiceFailedRegistrants.remove(h); + } + + /** + * Register for notifications when a sInCall VoicePrivacy is enabled + * + * @param h Handler that receives the notification message. + * @param what User-defined message code. + * @param obj User object. + */ + public void registerForInCallVoicePrivacyOn(Handler h, int what, Object obj){ + mInCallVoicePrivacyOnRegistrants.addUnique(h, what, obj); + } + + /** + * Unregister for notifications when a sInCall VoicePrivacy is enabled + * + * @param h Handler to be removed from the registrant list. + */ + public void unregisterForInCallVoicePrivacyOn(Handler h){ + mInCallVoicePrivacyOnRegistrants.remove(h); + } + + /** + * Register for notifications when a sInCall VoicePrivacy is disabled + * + * @param h Handler that receives the notification message. + * @param what User-defined message code. + * @param obj User object. + */ + public void registerForInCallVoicePrivacyOff(Handler h, int what, Object obj){ + mInCallVoicePrivacyOffRegistrants.addUnique(h, what, obj); + } + + /** + * Unregister for notifications when a sInCall VoicePrivacy is disabled + * + * @param h Handler to be removed from the registrant list. + */ + public void unregisterForInCallVoicePrivacyOff(Handler h){ + mInCallVoicePrivacyOffRegistrants.remove(h); + } + + /** + * Register for notifications when CDMA call waiting comes + * + * @param h Handler that receives the notification message. + * @param what User-defined message code. + * @param obj User object. + */ + public void registerForCallWaiting(Handler h, int what, Object obj){ + mCallWaitingRegistrants.addUnique(h, what, obj); + } + + /** + * Unregister for notifications when CDMA Call waiting comes + * @param h Handler to be removed from the registrant list. + */ + public void unregisterForCallWaiting(Handler h){ + mCallWaitingRegistrants.remove(h); + } + + + /** + * Register for signal information notifications from the network. + * Message.obj will contain an AsyncResult. + * AsyncResult.result will be a SuppServiceNotification instance. + * + * @param h Handler that receives the notification message. + * @param what User-defined message code. + * @param obj User object. + */ + + public void registerForSignalInfo(Handler h, int what, Object obj){ + mSignalInfoRegistrants.addUnique(h, what, obj); + } + + /** + * Unregisters for signal information notifications. + * Extraneous calls are tolerated silently + * + * @param h Handler to be removed from the registrant list. + */ + public void unregisterForSignalInfo(Handler h){ + mSignalInfoRegistrants.remove(h); + } + + /** + * Register for display information notifications from the network. + * Message.obj will contain an AsyncResult. + * AsyncResult.result will be a SuppServiceNotification instance. + * + * @param h Handler that receives the notification message. + * @param what User-defined message code. + * @param obj User object. + */ + public void registerForDisplayInfo(Handler h, int what, Object obj){ + mDisplayInfoRegistrants.addUnique(h, what, obj); + } + + /** + * Unregisters for display information notifications. + * Extraneous calls are tolerated silently + * + * @param h Handler to be removed from the registrant list. + */ + public void unregisterForDisplayInfo(Handler h) { + mDisplayInfoRegistrants.remove(h); + } + + /** + * Register for notifications when CDMA OTA Provision status change + * + * @param h Handler that receives the notification message. + * @param what User-defined message code. + * @param obj User object. + */ + public void registerForCdmaOtaStatusChange(Handler h, int what, Object obj){ + mCdmaOtaStatusChangeRegistrants.addUnique(h, what, obj); + } + + /** + * Unregister for notifications when CDMA OTA Provision status change + * @param h Handler to be removed from the registrant list. + */ + public void unregisterForCdmaOtaStatusChange(Handler h){ + mCdmaOtaStatusChangeRegistrants.remove(h); + } + + /** + * Registration point for subscription info ready + * @param h handler to notify + * @param what what code of message when delivered + * @param obj placed in Message.obj + */ + public void registerForSubscriptionInfoReady(Handler h, int what, Object obj){ + mSubscriptionInfoReadyRegistrants.addUnique(h, what, obj); + } + + /** + * Unregister for notifications for subscription info + * @param h Handler to be removed from the registrant list. + */ + public void unregisterForSubscriptionInfoReady(Handler h){ + mSubscriptionInfoReadyRegistrants.remove(h); + } + + /* APIs to access foregroudCalls, backgroudCalls, and ringingCalls + * 1. APIs to access list of calls + * 2. APIs to check if any active call, which has connection other than + * disconnected ones, pleaser refer to Call.isIdle() + * 3. APIs to return first active call + * 4. APIs to return the connections of first active call + * 5. APIs to return other property of first active call + */ + + /** + * @return list of all ringing calls + */ + public ArrayList<Call> getRingingCalls() { + return mRingingCalls; + } + + /** + * @return list of all foreground calls + */ + public ArrayList<Call> getForegroundCalls() { + return mForegroundCalls; + } + + /** + * @return list of all background calls + */ + public ArrayList<Call> getBackgroundCalls() { + return mBackgroundCalls; + } + + /** + * Return true if there is at least one active foreground call + */ + public boolean hasActiveFgCall() { + return (getFirstActiveCall(mForegroundCalls) != null); + } + + /** + * Return true if there is at least one active background call + */ + public boolean hasActiveBgCall() { + // TODO since hasActiveBgCall may get called often + // better to cache it to improve performance + return (getFirstActiveCall(mBackgroundCalls) != null); + } + + /** + * Return true if there is at least one active ringing call + * + */ + public boolean hasActiveRingingCall() { + return (getFirstActiveCall(mRingingCalls) != null); + } + + /** + * return the active foreground call from foreground calls + * + * Active call means the call is NOT in Call.State.IDLE + * + * 1. If there is active foreground call, return it + * 2. If there is no active foreground call, return the + * foreground call associated with default phone, which state is IDLE. + * 3. If there is no phone registered at all, return null. + * + */ + public Call getActiveFgCall() { + for (Call call : mForegroundCalls) { + if (call.getState() != Call.State.IDLE) { + return call; + } + } + return (mDefaultPhone == null) ? + null : mDefaultPhone.getForegroundCall(); + } + + /** + * return one active background call from background calls + * + * Active call means the call is NOT idle defined by Call.isIdle() + * + * 1. If there is only one active background call, return it + * 2. If there is more than one active background call, return the first one + * 3. If there is no active background call, return the background call + * associated with default phone, which state is IDLE. + * 4. If there is no background call at all, return null. + * + * Complete background calls list can be get by getBackgroundCalls() + */ + public Call getFirstActiveBgCall() { + for (Call call : mBackgroundCalls) { + if (!call.isIdle()) { + return call; + } + } + return (mDefaultPhone == null) ? + null : mDefaultPhone.getBackgroundCall(); + } + + /** + * return one active ringing call from ringing calls + * + * Active call means the call is NOT idle defined by Call.isIdle() + * + * 1. If there is only one active ringing call, return it + * 2. If there is more than one active ringing call, return the first one + * 3. If there is no active ringing call, return the ringing call + * associated with default phone, which state is IDLE. + * 4. If there is no ringing call at all, return null. + * + * Complete ringing calls list can be get by getRingingCalls() + */ + public Call getFirstActiveRingingCall() { + for (Call call : mRingingCalls) { + if (!call.isIdle()) { + return call; + } + } + return (mDefaultPhone == null) ? + null : mDefaultPhone.getRingingCall(); + } + + /** + * @return the state of active foreground call + * return IDLE if there is no active foreground call + */ + public Call.State getActiveFgCallState() { + Call fgCall = getActiveFgCall(); + + if (fgCall != null) { + return fgCall.getState(); + } + + return Call.State.IDLE; + } + + /** + * @return the connections of active foreground call + * return null if there is no active foreground call + */ + public List<Connection> getFgCallConnections() { + Call fgCall = getActiveFgCall(); + if ( fgCall != null) { + return fgCall.getConnections(); + } + return emptyConnections; + } + + /** + * @return the connections of active background call + * return empty list if there is no active background call + */ + public List<Connection> getBgCallConnections() { + Call bgCall = getActiveFgCall(); + if ( bgCall != null) { + return bgCall.getConnections(); + } + return emptyConnections; + } + + /** + * @return the latest connection of active foreground call + * return null if there is no active foreground call + */ + public Connection getFgCallLatestConnection() { + Call fgCall = getActiveFgCall(); + if ( fgCall != null) { + return fgCall.getLatestConnection(); + } + return null; + } + + /** + * @return true if there is at least one Foreground call in disconnected state + */ + public boolean hasDisconnectedFgCall() { + return (getFirstCallOfState(mForegroundCalls, Call.State.DISCONNECTED) != null); + } + + /** + * @return true if there is at least one background call in disconnected state + */ + public boolean hasDisconnectedBgCall() { + return (getFirstCallOfState(mBackgroundCalls, Call.State.DISCONNECTED) != null); + } + + /** + * @return the first active call from a call list + */ + private Call getFirstActiveCall(ArrayList<Call> calls) { + for (Call call : calls) { + if (!call.isIdle()) { + return call; + } + } + return null; + } + + /** + * @return the first call in a the Call.state from a call list + */ + private Call getFirstCallOfState(ArrayList<Call> calls, Call.State state) { + for (Call call : calls) { + if (call.getState() == state) { + return call; + } + } + return null; + } + + + + + private Handler mHandler = new Handler() { + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case EVENT_DISCONNECT: + mDisconnectRegistrants.notifyRegistrants((AsyncResult) msg.obj); + break; + case EVENT_PRECISE_CALL_STATE_CHANGED: + mPreciseCallStateRegistrants.notifyRegistrants((AsyncResult) msg.obj); + break; + case EVENT_NEW_RINGING_CONNECTION: + mNewRingingConnectionRegistrants.notifyRegistrants((AsyncResult) msg.obj); + break; + case EVENT_UNKNOWN_CONNECTION: + mUnknownConnectionRegistrants.notifyRegistrants((AsyncResult) msg.obj); + break; + case EVENT_INCOMING_RING: + // The event may come from RIL who's not aware of an ongoing fg call + if (!hasActiveFgCall()) { + mIncomingRingRegistrants.notifyRegistrants((AsyncResult) msg.obj); + } + break; + case EVENT_RINGBACK_TONE: + mRingbackToneRegistrants.notifyRegistrants((AsyncResult) msg.obj); + break; + case EVENT_IN_CALL_VOICE_PRIVACY_ON: + mInCallVoicePrivacyOnRegistrants.notifyRegistrants((AsyncResult) msg.obj); + break; + case EVENT_IN_CALL_VOICE_PRIVACY_OFF: + mInCallVoicePrivacyOffRegistrants.notifyRegistrants((AsyncResult) msg.obj); + break; + case EVENT_CALL_WAITING: + mCallWaitingRegistrants.notifyRegistrants((AsyncResult) msg.obj); + break; + case EVENT_DISPLAY_INFO: + mDisplayInfoRegistrants.notifyRegistrants((AsyncResult) msg.obj); + break; + case EVENT_SIGNAL_INFO: + mSignalInfoRegistrants.notifyRegistrants((AsyncResult) msg.obj); + break; + case EVENT_CDMA_OTA_STATUS_CHANGE: + mCdmaOtaStatusChangeRegistrants.notifyRegistrants((AsyncResult) msg.obj); + break; + case EVENT_RESEND_INCALL_MUTE: + mResendIncallMuteRegistrants.notifyRegistrants((AsyncResult) msg.obj); + break; + case EVENT_MMI_INITIATE: + mMmiInitiateRegistrants.notifyRegistrants((AsyncResult) msg.obj); + break; + case EVENT_MMI_COMPLETE: + mMmiCompleteRegistrants.notifyRegistrants((AsyncResult) msg.obj); + break; + case EVENT_ECM_TIMER_RESET: + mEcmTimerResetRegistrants.notifyRegistrants((AsyncResult) msg.obj); + break; + case EVENT_SUBSCRIPTION_INFO_READY: + mSubscriptionInfoReadyRegistrants.notifyRegistrants((AsyncResult) msg.obj); + break; + case EVENT_SUPP_SERVICE_FAILED: + mSuppServiceFailedRegistrants.notifyRegistrants((AsyncResult) msg.obj); + break; + case EVENT_SERVICE_STATE_CHANGED: + mServiceStateChangedRegistrants.notifyRegistrants((AsyncResult) msg.obj); + } + } + }; +} diff --git a/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java b/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java index 802e79b2cadd..798a5a56b847 100644 --- a/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java +++ b/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java @@ -284,7 +284,7 @@ public class CallerInfoAsyncQuery { */ public static CallerInfoAsyncQuery startQuery(int token, Context context, String number, OnQueryCompleteListener listener, Object cookie) { - //contruct the URI object and start Query. + //construct the URI object and start Query. Uri contactRef = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(number)); CallerInfoAsyncQuery c = new CallerInfoAsyncQuery(); diff --git a/telephony/java/com/android/internal/telephony/CommandException.java b/telephony/java/com/android/internal/telephony/CommandException.java index eb0a440dff25..94c544ecffd5 100644 --- a/telephony/java/com/android/internal/telephony/CommandException.java +++ b/telephony/java/com/android/internal/telephony/CommandException.java @@ -37,6 +37,10 @@ public class CommandException extends RuntimeException { OP_NOT_ALLOWED_DURING_VOICE_CALL, OP_NOT_ALLOWED_BEFORE_REG_NW, SMS_FAIL_RETRY, + SIM_ABSENT, + SUBSCRIPTION_NOT_AVAILABLE, + MODE_NOT_SUPPORTED, + FDN_CHECK_FAILURE, ILLEGAL_SIM_OR_ME, } @@ -69,6 +73,14 @@ public class CommandException extends RuntimeException { return new CommandException(Error.OP_NOT_ALLOWED_BEFORE_REG_NW); case RILConstants.SMS_SEND_FAIL_RETRY: return new CommandException(Error.SMS_FAIL_RETRY); + case RILConstants.SIM_ABSENT: + return new CommandException(Error.SIM_ABSENT); + case RILConstants.SUBSCRIPTION_NOT_AVAILABLE: + return new CommandException(Error.SUBSCRIPTION_NOT_AVAILABLE); + case RILConstants.MODE_NOT_SUPPORTED: + return new CommandException(Error.MODE_NOT_SUPPORTED); + case RILConstants.FDN_CHECK_FAILURE: + return new CommandException(Error.FDN_CHECK_FAILURE); case RILConstants.ILLEGAL_SIM_OR_ME: return new CommandException(Error.ILLEGAL_SIM_OR_ME); default: diff --git a/telephony/java/com/android/internal/telephony/CommandsInterface.java b/telephony/java/com/android/internal/telephony/CommandsInterface.java index d90c3056bf76..8e03c5a5bd5e 100644 --- a/telephony/java/com/android/internal/telephony/CommandsInterface.java +++ b/telephony/java/com/android/internal/telephony/CommandsInterface.java @@ -664,6 +664,19 @@ public interface CommandsInterface { * retMsg.obj = AsyncResult ar * ar.exception carries exception on failure * ar.userObject contains the orignal value of result.obj + * ar.result is null on success and failure + * + * CLIR_DEFAULT == on "use subscription default value" + * CLIR_SUPPRESSION == on "CLIR suppression" (allow CLI presentation) + * CLIR_INVOCATION == on "CLIR invocation" (restrict CLI presentation) + */ + void dial(String address, int clirMode, UUSInfo uusInfo, Message result); + + /** + * returned message + * retMsg.obj = AsyncResult ar + * ar.exception carries exception on failure + * ar.userObject contains the orignal value of result.obj * ar.result is String containing IMSI on success */ void getIMSI(Message result); diff --git a/telephony/java/com/android/internal/telephony/Connection.java b/telephony/java/com/android/internal/telephony/Connection.java index 37e8a99e6799..c20c2006fe90 100644 --- a/telephony/java/com/android/internal/telephony/Connection.java +++ b/telephony/java/com/android/internal/telephony/Connection.java @@ -39,23 +39,26 @@ public abstract class Connection { CONGESTION, /* outgoing call to congested network */ MMI, /* not presently used; dial() returns null */ INVALID_NUMBER, /* invalid dial string */ + INVALID_CREDENTIALS, /* invalid credentials */ + TIMED_OUT, /* client timed out */ LOST_SIGNAL, LIMIT_EXCEEDED, /* eg GSM ACM limit exceeded */ INCOMING_REJECTED, /* an incoming call that was rejected */ POWER_OFF, /* radio is turned off explicitly */ OUT_OF_SERVICE, /* out of service */ ICC_ERROR, /* No ICC, ICC locked, or other ICC error */ - CALL_BARRED, /* call was blocked by call barrring */ + CALL_BARRED, /* call was blocked by call barring */ FDN_BLOCKED, /* call was blocked by fixed dial number */ CS_RESTRICTED, /* call was blocked by restricted all voice access */ CS_RESTRICTED_NORMAL, /* call was blocked by restricted normal voice access */ CS_RESTRICTED_EMERGENCY, /* call was blocked by restricted emergency voice access */ + UNOBTAINABLE_NUMBER, /* Unassigned number (3GPP TS 24.008 table 10.5.123) */ CDMA_LOCKED_UNTIL_POWER_CYCLE, /* MS is locked until next power cycle */ CDMA_DROP, CDMA_INTERCEPT, /* INTERCEPT order received, MS state idle entered */ CDMA_REORDER, /* MS has been redirected, call is cancelled */ CDMA_SO_REJECT, /* service option rejection */ - CDMA_RETRY_ORDER, /* requeseted service is rejected, retry delay is set */ + CDMA_RETRY_ORDER, /* requested service is rejected, retry delay is set */ CDMA_ACCESS_FAILURE, CDMA_PREEMPTED, CDMA_NOT_EMERGENCY, /* not an emergency call */ @@ -68,8 +71,8 @@ public abstract class Connection { /* Instance Methods */ /** - * Gets address (e.g., phone number) associated with connection - * TODO: distinguish reasons for unavailablity + * Gets address (e.g. phone number) associated with connection. + * TODO: distinguish reasons for unavailability * * @return address or null if unavailable */ @@ -77,7 +80,7 @@ public abstract class Connection { public abstract String getAddress(); /** - * Gets cdma CNAP name associated with connection + * Gets CDMA CNAP name associated with connection. * @return cnap name or null if unavailable */ public String getCnapName() { @@ -85,15 +88,15 @@ public abstract class Connection { } /** - * Get orignal dial string - * @return orignal dial string or null if unavailable + * Get original dial string. + * @return original dial string or null if unavailable */ public String getOrigDialString(){ return null; } /** - * Gets cdma CNAP presentation associated with connection + * Gets CDMA CNAP presentation associated with connection. * @return cnap name or null if unavailable */ @@ -115,45 +118,45 @@ public abstract class Connection { public abstract long getCreateTime(); /** - * Connection connect time in currentTimeMillis() format - * For outgoing calls: Begins at (DIALING|ALERTING) -> ACTIVE transition - * For incoming calls: Begins at (INCOMING|WAITING) -> ACTIVE transition - * Returns 0 before then + * Connection connect time in currentTimeMillis() format. + * For outgoing calls: Begins at (DIALING|ALERTING) -> ACTIVE transition. + * For incoming calls: Begins at (INCOMING|WAITING) -> ACTIVE transition. + * Returns 0 before then. */ public abstract long getConnectTime(); /** - * Disconnect time in currentTimeMillis() format - * The time when this Connection makes a transition into ENDED or FAIL - * Returns 0 before then + * Disconnect time in currentTimeMillis() format. + * The time when this Connection makes a transition into ENDED or FAIL. + * Returns 0 before then. */ public abstract long getDisconnectTime(); /** - * returns the number of milliseconds the call has been connected, + * Returns the number of milliseconds the call has been connected, * or 0 if the call has never connected. * If the call is still connected, then returns the elapsed - * time since connect + * time since connect. */ public abstract long getDurationMillis(); /** * If this connection is HOLDING, return the number of milliseconds - * that it has been on hold for (approximently) - * If this connection is in any other state, return 0 + * that it has been on hold for (approximately). + * If this connection is in any other state, return 0. */ public abstract long getHoldDurationMillis(); /** - * Returns "NOT_DISCONNECTED" if not yet disconnected + * Returns "NOT_DISCONNECTED" if not yet disconnected. */ public abstract DisconnectCause getDisconnectCause(); /** * Returns true of this connection originated elsewhere * ("MT" or mobile terminated; another party called this terminal) - * or false if this call originated here (MO or mobile originated) + * or false if this call originated here (MO or mobile originated). */ public abstract boolean isIncoming(); @@ -273,6 +276,13 @@ public abstract class Connection { public abstract int getNumberPresentation(); /** + * Returns the User to User Signaling (UUS) information associated with + * incoming and waiting calls + * @return UUSInfo containing the UUS userdata. + */ + public abstract UUSInfo getUUSInfo(); + + /** * Build a human representation of a connection instance, suitable for debugging. * Don't log personal stuff unless in debug mode. * @return a string representing the internal state of this connection. diff --git a/telephony/java/com/android/internal/telephony/DataConnection.java b/telephony/java/com/android/internal/telephony/DataConnection.java index 1f8bbcfbe424..6634017c84a5 100644 --- a/telephony/java/com/android/internal/telephony/DataConnection.java +++ b/telephony/java/com/android/internal/telephony/DataConnection.java @@ -164,7 +164,7 @@ public abstract class DataConnection extends HierarchicalStateMachine { NONE, OPERATOR_BARRED, INSUFFICIENT_RESOURCES, - MISSING_UKNOWN_APN, + MISSING_UNKNOWN_APN, UNKNOWN_PDP_ADDRESS, USER_AUTHENTICATION, ACTIVATION_REJECT_GGSN, @@ -181,7 +181,7 @@ public abstract class DataConnection extends HierarchicalStateMachine { RADIO_NOT_AVAILABLE; public boolean isPermanentFail() { - return (this == OPERATOR_BARRED) || (this == MISSING_UKNOWN_APN) || + return (this == OPERATOR_BARRED) || (this == MISSING_UNKNOWN_APN) || (this == UNKNOWN_PDP_ADDRESS) || (this == USER_AUTHENTICATION) || (this == ACTIVATION_REJECT_GGSN) || (this == ACTIVATION_REJECT_UNSPECIFIED) || (this == SERVICE_OPTION_NOT_SUPPORTED) || @@ -208,12 +208,12 @@ public abstract class DataConnection extends HierarchicalStateMachine { return "Operator Barred"; case INSUFFICIENT_RESOURCES: return "Insufficient Resources"; - case MISSING_UKNOWN_APN: + case MISSING_UNKNOWN_APN: return "Missing / Unknown APN"; case UNKNOWN_PDP_ADDRESS: return "Unknown PDP Address"; case USER_AUTHENTICATION: - return "Error User Autentication"; + return "Error User Authentication"; case ACTIVATION_REJECT_GGSN: return "Activation Reject GGSN"; case ACTIVATION_REJECT_UNSPECIFIED: diff --git a/telephony/java/com/android/internal/telephony/DefaultPhoneNotifier.java b/telephony/java/com/android/internal/telephony/DefaultPhoneNotifier.java index 4da4b6abe73d..057ba0a7ca15 100644 --- a/telephony/java/com/android/internal/telephony/DefaultPhoneNotifier.java +++ b/telephony/java/com/android/internal/telephony/DefaultPhoneNotifier.java @@ -102,7 +102,8 @@ public class DefaultPhoneNotifier implements PhoneNotifier { sender.getActiveApnTypes(), sender.getInterfaceName(null), ((telephony!=null) ? telephony.getNetworkType() : - TelephonyManager.NETWORK_TYPE_UNKNOWN)); + TelephonyManager.NETWORK_TYPE_UNKNOWN), + sender.getGateway(null)); } catch (RemoteException ex) { // system process is dead } diff --git a/telephony/java/com/android/internal/telephony/DriverCall.java b/telephony/java/com/android/internal/telephony/DriverCall.java index 66f6b9cbff95..663c2842a793 100644 --- a/telephony/java/com/android/internal/telephony/DriverCall.java +++ b/telephony/java/com/android/internal/telephony/DriverCall.java @@ -49,6 +49,7 @@ public class DriverCall implements Comparable { public int numberPresentation; public String name; public int namePresentation; + public UUSInfo uusInfo; /** returns null on error */ static DriverCall diff --git a/telephony/java/com/android/internal/telephony/GsmAlphabet.java b/telephony/java/com/android/internal/telephony/GsmAlphabet.java index ebdd2207db6f..7edf065ee2ce 100644 --- a/telephony/java/com/android/internal/telephony/GsmAlphabet.java +++ b/telephony/java/com/android/internal/telephony/GsmAlphabet.java @@ -23,7 +23,7 @@ import android.util.Log; /** * This class implements the character set mapping between - * the GSM SMS 7-bit alphabet specifed in TS 23.038 6.2.1 + * the GSM SMS 7-bit alphabet specified in TS 23.038 6.2.1 * and UTF-16 * * {@hide} diff --git a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl index 5bf8e5826593..87e4b5287c3d 100644 --- a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl @@ -32,7 +32,8 @@ interface ITelephonyRegistry { void notifyCallForwardingChanged(boolean cfi); void notifyDataActivity(int state); void notifyDataConnection(int state, boolean isDataConnectivityPossible, - String reason, String apn, in String[] apnTypes, String interfaceName, int networkType); + String reason, String apn, in String[] apnTypes, String interfaceName, int networkType, + String gateway); void notifyDataConnectionFailed(String reason); void notifyCellLocation(in Bundle cellLocation); } diff --git a/telephony/java/com/android/internal/telephony/IccProvider.java b/telephony/java/com/android/internal/telephony/IccProvider.java index fa914570c341..3471ec2885af 100644 --- a/telephony/java/com/android/internal/telephony/IccProvider.java +++ b/telephony/java/com/android/internal/telephony/IccProvider.java @@ -417,11 +417,11 @@ public class IccProvider extends ContentProvider { } } - if (TextUtils.isEmpty(tag)) { + if (TextUtils.isEmpty(number)) { return 0; } - if (efType == FDN && TextUtils.isEmpty(pin2)) { + if (efType == IccConstants.EF_FDN && TextUtils.isEmpty(pin2)) { return 0; } diff --git a/telephony/java/com/android/internal/telephony/IccSmsInterfaceManager.java b/telephony/java/com/android/internal/telephony/IccSmsInterfaceManager.java index 8a5a6aee7487..5fef6de70a25 100644 --- a/telephony/java/com/android/internal/telephony/IccSmsInterfaceManager.java +++ b/telephony/java/com/android/internal/telephony/IccSmsInterfaceManager.java @@ -57,7 +57,7 @@ public abstract class IccSmsInterfaceManager extends ISms.Stub { * @param destPort the port to deliver the message to * @param data the body of the message to send * @param sentIntent if not NULL this <code>PendingIntent</code> is - * broadcast when the message is sucessfully sent, or failed. + * broadcast when the message is successfully sent, or failed. * The result code will be <code>Activity.RESULT_OK<code> for success, * or one of these errors:<br> * <code>RESULT_ERROR_GENERIC_FAILURE</code><br> @@ -67,7 +67,7 @@ public abstract class IccSmsInterfaceManager extends ISms.Stub { * the extra "errorCode" containing a radio technology specific value, * generally only useful for troubleshooting.<br> * The per-application based SMS control checks sentIntent. If sentIntent - * is NULL the caller will be checked against all unknown applicaitons, + * is NULL the caller will be checked against all unknown applications, * which cause smaller number of SMS to be sent in checking period. * @param deliveryIntent if not NULL this <code>PendingIntent</code> is * broadcast when the message is delivered to the recipient. The @@ -94,7 +94,7 @@ public abstract class IccSmsInterfaceManager extends ISms.Stub { * the current default SMSC * @param text the body of the message to send * @param sentIntent if not NULL this <code>PendingIntent</code> is - * broadcast when the message is sucessfully sent, or failed. + * broadcast when the message is successfully sent, or failed. * The result code will be <code>Activity.RESULT_OK<code> for success, * or one of these errors:<br> * <code>RESULT_ERROR_GENERIC_FAILURE</code><br> @@ -140,7 +140,7 @@ public abstract class IccSmsInterfaceManager extends ISms.Stub { * <code>RESULT_ERROR_RADIO_OFF</code> * <code>RESULT_ERROR_NULL_PDU</code>. * The per-application based SMS control checks sentIntent. If sentIntent - * is NULL the caller will be checked against all unknown applicaitons, + * is NULL the caller will be checked against all unknown applications, * which cause smaller number of SMS to be sent in checking period. * @param deliveryIntents if not null, an <code>ArrayList</code> of * <code>PendingIntent</code>s (one for each message part) that is diff --git a/telephony/java/com/android/internal/telephony/Phone.java b/telephony/java/com/android/internal/telephony/Phone.java index 7179bef9f165..9afade33b006 100644 --- a/telephony/java/com/android/internal/telephony/Phone.java +++ b/telephony/java/com/android/internal/telephony/Phone.java @@ -103,6 +103,7 @@ public interface Phone { static final String DATA_APN_KEY = "apn"; static final String DATA_IFACE_NAME_KEY = "iface"; + static final String DATA_GATEWAY_KEY = "gateway"; static final String NETWORK_UNAVAILABLE_KEY = "networkUnvailable"; static final String PHONE_IN_ECM_STATE = "phoneinECMState"; @@ -177,6 +178,7 @@ public interface Phone { static final int PHONE_TYPE_NONE = RILConstants.NO_PHONE; static final int PHONE_TYPE_GSM = RILConstants.GSM_PHONE; static final int PHONE_TYPE_CDMA = RILConstants.CDMA_PHONE; + static final int PHONE_TYPE_SIP = RILConstants.SIP_PHONE; // Used for preferred network type // Note NT_* substitute RILConstants.NETWORK_MODE_* above the Phone @@ -243,15 +245,14 @@ public interface Phone { /** * Get the current DataState. No change notification exists at this * interface -- use - * {@link com.android.telephony.PhoneStateListener PhoneStateListener} - * instead. + * {@link android.telephony.PhoneStateListener} instead. */ DataState getDataConnectionState(); /** * Get the current DataActivityState. No change notification exists at this * interface -- use - * {@link TelephonyManager} instead. + * {@link android.telephony.TelephonyManager} instead. */ DataActivityState getDataActivityState(); @@ -789,6 +790,19 @@ public interface Phone { Connection dial(String dialString) throws CallStateException; /** + * Initiate a new voice connection with supplementary User to User + * Information. This happens asynchronously, so you cannot assume the audio + * path is connected (or a call index has been assigned) until + * PhoneStateChanged notification has occurred. + * + * @exception CallStateException if a new outgoing call is not currently + * possible because no more call slots exist or a call exists + * that is dialing, alerting, ringing, or waiting. Other + * errors are handled asynchronously. + */ + Connection dial(String dialString, UUSInfo uusInfo) throws CallStateException; + + /** * Handles PIN MMI commands (PIN/PIN2/PUK/PUK2), which are initiated * without SEND (so <code>dial</code> is not appropriate). * @@ -840,7 +854,7 @@ public interface Phone { * @param dtmfString is string representing the dialing digit(s) in the active call * @param on the DTMF ON length in milliseconds, or 0 for default * @param off the DTMF OFF length in milliseconds, or 0 for default - * @param onCompelte is the callback message when the action is processed by BP + * @param onComplete is the callback message when the action is processed by BP * */ void sendBurstDtmf(String dtmfString, int on, int off, Message onComplete); @@ -980,7 +994,7 @@ public interface Phone { * ((AsyncResult)onComplete.obj) is an array of int, with a length of 2. * * @param onComplete a callback message when the action is completed. - * @see com.android.internal.telephony.CommandsInterface.getCLIR for details. + * @see com.android.internal.telephony.CommandsInterface#getCLIR for details. */ void getOutgoingCallerIdDisplay(Message onComplete); @@ -1002,7 +1016,7 @@ public interface Phone { * ((AsyncResult)onComplete.obj) is an array of int, with a length of 1. * * @param onComplete a callback message when the action is completed. - * @see com.android.internal.telephony.CommandsInterface.queryCallWaiting for details. + * @see com.android.internal.telephony.CommandsInterface#queryCallWaiting for details. */ void getCallWaiting(Message onComplete); @@ -1445,7 +1459,7 @@ public interface Phone { * setTTYMode * sets a TTY mode option. * - * @param enable is a boolean representing the state that you are + * @param ttyMode is a boolean representing the state that you are * requesting, true for enabled, false for disabled. * @param onComplete a callback message when the action is completed */ diff --git a/telephony/java/com/android/internal/telephony/PhoneBase.java b/telephony/java/com/android/internal/telephony/PhoneBase.java index a8f41430488e..ff28773df5c2 100644 --- a/telephony/java/com/android/internal/telephony/PhoneBase.java +++ b/telephony/java/com/android/internal/telephony/PhoneBase.java @@ -286,7 +286,7 @@ public abstract class PhoneBase extends Handler implements Phone { SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext()); SharedPreferences.Editor editor = sp.edit(); editor.putBoolean(DNS_SERVER_CHECK_DISABLED_KEY, b); - editor.commit(); + editor.apply(); } /** @@ -543,7 +543,7 @@ public abstract class PhoneBase extends Handler implements Phone { private void setPropertiesByCarrier() { String carrier = SystemProperties.get("ro.carrier"); - if (null == carrier || 0 == carrier.length()) { + if (null == carrier || 0 == carrier.length() || "unknown".equals(carrier)) { return; } diff --git a/telephony/java/com/android/internal/telephony/PhoneFactory.java b/telephony/java/com/android/internal/telephony/PhoneFactory.java index cd72752e4328..2e391cb96cb2 100644 --- a/telephony/java/com/android/internal/telephony/PhoneFactory.java +++ b/telephony/java/com/android/internal/telephony/PhoneFactory.java @@ -24,6 +24,8 @@ import android.util.Log; import com.android.internal.telephony.cdma.CDMAPhone; import com.android.internal.telephony.gsm.GSMPhone; +import com.android.internal.telephony.sip.SipPhone; +import com.android.internal.telephony.sip.SipPhoneFactory; /** * {@hide} @@ -109,13 +111,13 @@ public class PhoneFactory { int phoneType = getPhoneType(networkMode); if (phoneType == Phone.PHONE_TYPE_GSM) { + Log.i(LOG_TAG, "Creating GSMPhone"); sProxyPhone = new PhoneProxy(new GSMPhone(context, sCommandsInterface, sPhoneNotifier)); - Log.i(LOG_TAG, "Creating GSMPhone"); } else if (phoneType == Phone.PHONE_TYPE_CDMA) { + Log.i(LOG_TAG, "Creating CDMAPhone"); sProxyPhone = new PhoneProxy(new CDMAPhone(context, sCommandsInterface, sPhoneNotifier)); - Log.i(LOG_TAG, "Creating CDMAPhone"); } sMadeDefaults = true; @@ -175,4 +177,13 @@ public class PhoneFactory { return phone; } } + + /** + * Makes a {@link SipPhone} object. + * @param sipUri the local SIP URI the phone runs on + * @return the {@code SipPhone} object or null if the SIP URI is not valid + */ + public static SipPhone makeSipPhone(String sipUri) { + return SipPhoneFactory.makePhone(sipUri, sContext, sPhoneNotifier); + } } diff --git a/telephony/java/com/android/internal/telephony/PhoneProxy.java b/telephony/java/com/android/internal/telephony/PhoneProxy.java index 6d3798ed1459..e1511e6ed79f 100644 --- a/telephony/java/com/android/internal/telephony/PhoneProxy.java +++ b/telephony/java/com/android/internal/telephony/PhoneProxy.java @@ -423,6 +423,10 @@ public class PhoneProxy extends Handler implements Phone { return mActivePhone.dial(dialString); } + public Connection dial(String dialString, UUSInfo uusInfo) throws CallStateException { + return mActivePhone.dial(dialString, uusInfo); + } + public boolean handlePinMmi(String dialString) { return mActivePhone.handlePinMmi(dialString); } diff --git a/telephony/java/com/android/internal/telephony/PhoneSubInfo.java b/telephony/java/com/android/internal/telephony/PhoneSubInfo.java index 0b557368b48e..a45cad1114de 100644 --- a/telephony/java/com/android/internal/telephony/PhoneSubInfo.java +++ b/telephony/java/com/android/internal/telephony/PhoneSubInfo.java @@ -69,7 +69,7 @@ public class PhoneSubInfo extends IPhoneSubInfo.Stub { } /** - * Retrieves the unique sbuscriber ID, e.g., IMSI for GSM phones. + * Retrieves the unique subscriber ID, e.g., IMSI for GSM phones. */ public String getSubscriberId() { mContext.enforceCallingOrSelfPermission(READ_PHONE_STATE, "Requires READ_PHONE_STATE"); @@ -111,7 +111,7 @@ public class PhoneSubInfo extends IPhoneSubInfo.Stub { } /** - * Retrieves the compelete voice mail number. + * Retrieves the complete voice mail number. * * @hide */ diff --git a/telephony/java/com/android/internal/telephony/PhoneSubInfoProxy.java b/telephony/java/com/android/internal/telephony/PhoneSubInfoProxy.java index a0360462f2bf..700989397ae9 100644 --- a/telephony/java/com/android/internal/telephony/PhoneSubInfoProxy.java +++ b/telephony/java/com/android/internal/telephony/PhoneSubInfoProxy.java @@ -47,7 +47,7 @@ public class PhoneSubInfoProxy extends IPhoneSubInfo.Stub { } /** - * Retrieves the unique sbuscriber ID, e.g., IMSI for GSM phones. + * Retrieves the unique subscriber ID, e.g., IMSI for GSM phones. */ public String getSubscriberId() { return mPhoneSubInfo.getSubscriberId(); diff --git a/telephony/java/com/android/internal/telephony/RIL.java b/telephony/java/com/android/internal/telephony/RIL.java index d8e313a79908..3d410fd2c465 100644 --- a/telephony/java/com/android/internal/telephony/RIL.java +++ b/telephony/java/com/android/internal/telephony/RIL.java @@ -796,12 +796,26 @@ public final class RIL extends BaseCommands implements CommandsInterface { public void dial (String address, int clirMode, Message result) { + dial(address, clirMode, null, result); + } + + public void + dial(String address, int clirMode, UUSInfo uusInfo, Message result) { RILRequest rr = RILRequest.obtain(RIL_REQUEST_DIAL, result); rr.mp.writeString(address); rr.mp.writeInt(clirMode); rr.mp.writeInt(0); // UUS information is absent + if (uusInfo == null) { + rr.mp.writeInt(0); // UUS information is absent + } else { + rr.mp.writeInt(1); // UUS information is present + rr.mp.writeInt(uusInfo.getType()); + rr.mp.writeInt(uusInfo.getDcs()); + rr.mp.writeByteArray(uusInfo.getUserData()); + } + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); send(rr); @@ -2837,10 +2851,21 @@ public final class RIL extends BaseCommands implements CommandsInterface { dc.namePresentation = p.readInt(); int uusInfoPresent = p.readInt(); if (uusInfoPresent == 1) { - // TODO: Copy the data to dc to forward to the apps. - p.readInt(); - p.readInt(); - p.createByteArray(); + dc.uusInfo = new UUSInfo(); + dc.uusInfo.setType(p.readInt()); + dc.uusInfo.setDcs(p.readInt()); + byte[] userData = p.createByteArray(); + dc.uusInfo.setUserData(userData); + Log + .v(LOG_TAG, String.format("Incoming UUS : type=%d, dcs=%d, length=%d", + dc.uusInfo.getType(), dc.uusInfo.getDcs(), + dc.uusInfo.getUserData().length)); + Log.v(LOG_TAG, "Incoming UUS : data (string)=" + + new String(dc.uusInfo.getUserData())); + Log.v(LOG_TAG, "Incoming UUS : data (hex): " + + IccUtils.bytesToHexString(dc.uusInfo.getUserData())); + } else { + Log.v(LOG_TAG, "Incoming UUS : NOT present!"); } // Make sure there's a leading + on addresses with a TOA of 145 diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java index 01f4ab274c13..888f72183758 100644 --- a/telephony/java/com/android/internal/telephony/RILConstants.java +++ b/telephony/java/com/android/internal/telephony/RILConstants.java @@ -45,6 +45,11 @@ public interface RILConstants { int OP_NOT_ALLOWED_BEFORE_REG_NW = 9; /* request is not allowed before device registers to network */ int SMS_SEND_FAIL_RETRY = 10; /* send sms fail and need retry */ + int SIM_ABSENT = 11; /* ICC card is absent */ + int SUBSCRIPTION_NOT_AVAILABLE = 12; /* fail to find CDMA subscription from specified + location */ + int MODE_NOT_SUPPORTED = 13; /* HW does not support preferred network type */ + int FDN_CHECK_FAILURE = 14; /* send operation barred error when FDN is enabled */ int ILLEGAL_SIM_OR_ME = 15; /* network selection failure due to wrong SIM/ME and no retries needed */ @@ -74,6 +79,7 @@ public interface RILConstants { int NO_PHONE = 0; int GSM_PHONE = 1; int CDMA_PHONE = 2; + int SIP_PHONE = 3; int CDM_TTY_MODE_DISABLED = 0; int CDM_TTY_MODE_ENABLED = 1; diff --git a/telephony/java/com/android/internal/telephony/RetryManager.java b/telephony/java/com/android/internal/telephony/RetryManager.java index 779f358307ac..b1049a2892b1 100644 --- a/telephony/java/com/android/internal/telephony/RetryManager.java +++ b/telephony/java/com/android/internal/telephony/RetryManager.java @@ -25,7 +25,7 @@ import java.util.ArrayList; /** * Retry manager allows a simple way to declare a series of - * retires timeouts. After creating a RetryManager the configure + * retry timeouts. After creating a RetryManager the configure * method is used to define the sequence. A simple linear series * may be initialized using configure with three integer parameters * The other configure method allows a series to be declared using @@ -54,18 +54,18 @@ import java.util.ArrayList; *<p> * Examples: * <ul> - * <li>3 retires with no randomization value which means its 0: + * <li>3 retries with no randomization value which means its 0: * <ul><li><code>"1000, 2000, 3000"</code></ul> * - * <li>10 retires with a 500 default randomization value for each and + * <li>10 retries with a 500 default randomization value for each and * the 4..10 retries all using 3000 as the delay: * <ul><li><code>"max_retries=10, default_randomization=500, 1000, 2000, 3000"</code></ul> * - * <li>4 retires with a 100 as the default randomization value for the first 2 values and + * <li>4 retries with a 100 as the default randomization value for the first 2 values and * the other two having specified values of 500: * <ul><li><code>"default_randomization=100, 1000, 2000, 4000:500, 5000:500"</code></ul> * - * <li>Infinite number of retires with the first one at 1000, the second at 2000 all + * <li>Infinite number of retries with the first one at 1000, the second at 2000 all * others will be at 3000. * <ul><li><code>"max_retries=infinite,1000,2000,3000</code></ul> * </ul> @@ -75,9 +75,6 @@ import java.util.ArrayList; public class RetryManager { static public final String LOG_TAG = "RetryManager"; static public final boolean DBG = false; - static public final int RETRYIES_NOT_STARTED = 0; - static public final int RETRYIES_ON_GOING = 1; - static public final int RETRYIES_COMPLETED = 2; /** * Retry record with times in milli-seconds @@ -104,7 +101,7 @@ public class RetryManager { */ private int mMaxRetryCount; - /** The current number of retires */ + /** The current number of retries */ private int mRetryCount; /** Random number generator */ @@ -125,7 +122,7 @@ public class RetryManager { * @param randomizationTime a random value between 0 and * randomizationTime will be added to retryTime. this * parameter may be 0. - * @return true if successfull + * @return true if successful */ public boolean configure(int maxRetryCount, int retryTime, int randomizationTime) { Pair<Boolean, Integer> value; @@ -242,7 +239,7 @@ public class RetryManager { /** * Report whether data reconnection should be retried * - * @return {@code true} if the max retires has not been reached. {@code + * @return {@code true} if the max retries has not been reached. {@code * false} otherwise. */ public boolean isRetryNeeded() { @@ -289,7 +286,7 @@ public class RetryManager { if (mRetryCount > mMaxRetryCount) { mRetryCount = mMaxRetryCount; } - if (DBG) log("increseRetryCount: " + mRetryCount); + if (DBG) log("increaseRetryCount: " + mRetryCount); } /** diff --git a/telephony/java/com/android/internal/telephony/SMSDispatcher.java b/telephony/java/com/android/internal/telephony/SMSDispatcher.java index 764d12ebe067..917e1d823a8a 100644 --- a/telephony/java/com/android/internal/telephony/SMSDispatcher.java +++ b/telephony/java/com/android/internal/telephony/SMSDispatcher.java @@ -32,9 +32,11 @@ import android.database.Cursor; import android.database.SQLException; import android.net.Uri; import android.os.AsyncResult; +import android.os.Environment; import android.os.Handler; import android.os.Message; import android.os.PowerManager; +import android.os.StatFs; import android.provider.Telephony; import android.provider.Telephony.Sms.Intents; import android.provider.Settings; @@ -44,10 +46,6 @@ import android.util.Config; import android.util.Log; import android.view.WindowManager; -import com.android.internal.telephony.CommandsInterface; -import com.android.internal.telephony.SmsMessageBase; -import com.android.internal.telephony.SmsResponse; -import com.android.internal.telephony.WapPushOverSms; import com.android.internal.util.HexDump; import java.io.ByteArrayOutputStream; @@ -62,6 +60,7 @@ import static android.telephony.SmsManager.RESULT_ERROR_NO_SERVICE; import static android.telephony.SmsManager.RESULT_ERROR_NULL_PDU; import static android.telephony.SmsManager.RESULT_ERROR_RADIO_OFF; import static android.telephony.SmsManager.RESULT_ERROR_LIMIT_EXCEEDED; +import static android.telephony.SmsManager.RESULT_ERROR_FDN_CHECK_FAILURE; public abstract class SMSDispatcher extends Handler { @@ -74,7 +73,7 @@ public abstract class SMSDispatcher extends Handler { private static final int DEFAULT_SMS_MAX_COUNT = 100; /** Default timeout for SMS sent query */ - private static final int DEFAULT_SMS_TIMOUEOUT = 6000; + private static final int DEFAULT_SMS_TIMEOUT = 6000; protected static final String[] RAW_PROJECTION = new String[] { "pdu", @@ -82,8 +81,6 @@ public abstract class SMSDispatcher extends Handler { "destination_port", }; - static final int MAIL_SEND_SMS = 1; - static final protected int EVENT_NEW_SMS = 1; static final protected int EVENT_SEND_SMS_COMPLETE = 2; @@ -142,7 +139,7 @@ public abstract class SMSDispatcher extends Handler { private SmsCounter mCounter; - private ArrayList mSTrackers = new ArrayList(MO_MSG_QUEUE_LIMIT); + private ArrayList<SmsTracker> mSTrackers = new ArrayList<SmsTracker>(MO_MSG_QUEUE_LIMIT); /** Wake lock to ensure device stays awake while dispatching the SMS intent. */ private PowerManager.WakeLock mWakeLock; @@ -153,10 +150,6 @@ public abstract class SMSDispatcher extends Handler { */ private final int WAKE_LOCK_TIMEOUT = 5000; - private static SmsMessage mSmsMessage; - private static SmsMessageBase mSmsMessageBase; - private SmsMessageBase.SubmitPduBase mSubmitPduBase; - protected boolean mStorageAvailable = true; protected boolean mReportMemoryStatusPending = false; @@ -249,11 +242,9 @@ public abstract class SMSDispatcher extends Handler { // Register for device storage intents. Use these to notify the RIL // that storage for SMS is or is not available. - // TODO: Revisit this for a later release. Storage reporting should - // rely more on application indication. IntentFilter filter = new IntentFilter(); - filter.addAction(Intent.ACTION_DEVICE_STORAGE_LOW); - filter.addAction(Intent.ACTION_DEVICE_STORAGE_OK); + filter.addAction(Intent.ACTION_DEVICE_STORAGE_FULL); + filter.addAction(Intent.ACTION_DEVICE_STORAGE_NOT_FULL); mContext.registerReceiver(mResultReceiver, filter); } @@ -344,7 +335,7 @@ public abstract class SMSDispatcher extends Handler { msg.obj = null; if (mSTrackers.isEmpty() == false) { try { - SmsTracker sTracker = (SmsTracker)mSTrackers.remove(0); + SmsTracker sTracker = mSTrackers.remove(0); sTracker.mSentIntent.send(RESULT_ERROR_LIMIT_EXCEEDED); } catch (CanceledException ex) { Log.e(TAG, "failed to send back RESULT_ERROR_LIMIT_EXCEEDED"); @@ -357,7 +348,7 @@ public abstract class SMSDispatcher extends Handler { case EVENT_SEND_CONFIRMED_SMS: if (mSTrackers.isEmpty() == false) { - SmsTracker sTracker = (SmsTracker)mSTrackers.remove(mSTrackers.size() - 1); + SmsTracker sTracker = mSTrackers.remove(mSTrackers.size() - 1); if (isMultipartTracker(sTracker)) { sendMultipartSms(sTracker); } else { @@ -371,7 +362,7 @@ public abstract class SMSDispatcher extends Handler { if (mSTrackers.isEmpty() == false) { // Remove the latest one. try { - SmsTracker sTracker = (SmsTracker)mSTrackers.remove(mSTrackers.size() - 1); + SmsTracker sTracker = mSTrackers.remove(mSTrackers.size() - 1); sTracker.mSentIntent.send(RESULT_ERROR_LIMIT_EXCEEDED); } catch (CanceledException ex) { Log.e(TAG, "failed to send back RESULT_ERROR_LIMIT_EXCEEDED"); @@ -499,13 +490,20 @@ public abstract class SMSDispatcher extends Handler { Message retryMsg = obtainMessage(EVENT_SEND_RETRY, tracker); sendMessageDelayed(retryMsg, SEND_RETRY_DELAY); } else if (tracker.mSentIntent != null) { + int error = RESULT_ERROR_GENERIC_FAILURE; + + if (((CommandException)(ar.exception)).getCommandError() + == CommandException.Error.FDN_CHECK_FAILURE) { + error = RESULT_ERROR_FDN_CHECK_FAILURE; + } // Done retrying; return an error to the app. try { Intent fillIn = new Intent(); if (ar.result != null) { fillIn.putExtra("errorCode", ((SmsResponse)ar.result).errorCode); } - tracker.mSentIntent.send(mContext, RESULT_ERROR_GENERIC_FAILURE, fillIn); + tracker.mSentIntent.send(mContext, error, fillIn); + } catch (CanceledException ex) {} } } @@ -671,7 +669,7 @@ public abstract class SMSDispatcher extends Handler { * @param destPort the port to deliver the message to * @param data the body of the message to send * @param sentIntent if not NULL this <code>PendingIntent</code> is - * broadcast when the message is sucessfully sent, or failed. + * broadcast when the message is successfully sent, or failed. * The result code will be <code>Activity.RESULT_OK<code> for success, * or one of these errors:<br> * <code>RESULT_ERROR_GENERIC_FAILURE</code><br> @@ -681,7 +679,7 @@ public abstract class SMSDispatcher extends Handler { * the extra "errorCode" containing a radio technology specific value, * generally only useful for troubleshooting.<br> * The per-application based SMS control checks sentIntent. If sentIntent - * is NULL the caller will be checked against all unknown applicaitons, + * is NULL the caller will be checked against all unknown applications, * which cause smaller number of SMS to be sent in checking period. * @param deliveryIntent if not NULL this <code>PendingIntent</code> is * broadcast when the message is delivered to the recipient. The @@ -698,7 +696,7 @@ public abstract class SMSDispatcher extends Handler { * the current default SMSC * @param text the body of the message to send * @param sentIntent if not NULL this <code>PendingIntent</code> is - * broadcast when the message is sucessfully sent, or failed. + * broadcast when the message is successfully sent, or failed. * The result code will be <code>Activity.RESULT_OK<code> for success, * or one of these errors:<br> * <code>RESULT_ERROR_GENERIC_FAILURE</code><br> @@ -734,7 +732,7 @@ public abstract class SMSDispatcher extends Handler { * <code>RESULT_ERROR_RADIO_OFF</code> * <code>RESULT_ERROR_NULL_PDU</code>. * The per-application based SMS control checks sentIntent. If sentIntent - * is NULL the caller will be checked against all unknown applicaitons, + * is NULL the caller will be checked against all unknown applications, * which cause smaller number of SMS to be sent in checking period. * @param deliveryIntents if not null, an <code>ArrayList</code> of * <code>PendingIntent</code>s (one for each message part) that is @@ -750,17 +748,17 @@ public abstract class SMSDispatcher extends Handler { * Send a SMS * * @param smsc the SMSC to send the message through, or NULL for the - * defatult SMSC + * default SMSC * @param pdu the raw PDU to send * @param sentIntent if not NULL this <code>Intent</code> is - * broadcast when the message is sucessfully sent, or failed. + * broadcast when the message is successfully sent, or failed. * The result code will be <code>Activity.RESULT_OK<code> for success, * or one of these errors: * <code>RESULT_ERROR_GENERIC_FAILURE</code> * <code>RESULT_ERROR_RADIO_OFF</code> * <code>RESULT_ERROR_NULL_PDU</code>. * The per-application based SMS control checks sentIntent. If sentIntent - * is NULL the caller will be checked against all unknown applicaitons, + * is NULL the caller will be checked against all unknown applications, * which cause smaller number of SMS to be sent in checking period. * @param deliveryIntent if not NULL this <code>Intent</code> is * broadcast when the message is delivered to the recipient. The @@ -829,7 +827,7 @@ public abstract class SMSDispatcher extends Handler { mSTrackers.add(tracker); sendMessageDelayed ( obtainMessage(EVENT_ALERT_TIMEOUT, d), - DEFAULT_SMS_TIMOUEOUT); + DEFAULT_SMS_TIMEOUT); } protected String getAppNameByIntent(PendingIntent intent) { @@ -923,7 +921,7 @@ public abstract class SMSDispatcher extends Handler { } /** - * Keeps track of an SMS that has been sent to the RIL, until it it has + * Keeps track of an SMS that has been sent to the RIL, until it has * successfully been sent, or we're done trying. * */ @@ -964,27 +962,26 @@ public abstract class SMSDispatcher extends Handler { } }; - private BroadcastReceiver mResultReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - if (intent.getAction().equals(Intent.ACTION_DEVICE_STORAGE_LOW)) { - mStorageAvailable = false; - mCm.reportSmsMemoryStatus(false, obtainMessage(EVENT_REPORT_MEMORY_STATUS_DONE)); - } else if (intent.getAction().equals(Intent.ACTION_DEVICE_STORAGE_OK)) { - mStorageAvailable = true; - mCm.reportSmsMemoryStatus(true, obtainMessage(EVENT_REPORT_MEMORY_STATUS_DONE)); - } else { - // Assume the intent is one of the SMS receive intents that - // was sent as an ordered broadcast. Check result and ACK. - int rc = getResultCode(); - boolean success = (rc == Activity.RESULT_OK) - || (rc == Intents.RESULT_SMS_HANDLED); - - // For a multi-part message, this only ACKs the last part. - // Previous parts were ACK'd as they were received. - acknowledgeLastIncomingSms(success, rc, null); - } + private BroadcastReceiver mResultReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (intent.getAction().equals(Intent.ACTION_DEVICE_STORAGE_FULL)) { + mStorageAvailable = false; + mCm.reportSmsMemoryStatus(false, obtainMessage(EVENT_REPORT_MEMORY_STATUS_DONE)); + } else if (intent.getAction().equals(Intent.ACTION_DEVICE_STORAGE_NOT_FULL)) { + mStorageAvailable = true; + mCm.reportSmsMemoryStatus(true, obtainMessage(EVENT_REPORT_MEMORY_STATUS_DONE)); + } else { + // Assume the intent is one of the SMS receive intents that + // was sent as an ordered broadcast. Check result and ACK. + int rc = getResultCode(); + boolean success = (rc == Activity.RESULT_OK) + || (rc == Intents.RESULT_SMS_HANDLED); + + // For a multi-part message, this only ACKs the last part. + // Previous parts were ACK'd as they were received. + acknowledgeLastIncomingSms(success, rc, null); } - - }; + } + }; } diff --git a/telephony/java/com/android/internal/telephony/ServiceStateTracker.java b/telephony/java/com/android/internal/telephony/ServiceStateTracker.java index 73836497dc4e..e8bbe5e9ecac 100644 --- a/telephony/java/com/android/internal/telephony/ServiceStateTracker.java +++ b/telephony/java/com/android/internal/telephony/ServiceStateTracker.java @@ -44,6 +44,7 @@ public abstract class ServiceStateTracker extends Handler { protected static final int DATA_ACCESS_HSDPA = 9; protected static final int DATA_ACCESS_HSUPA = 10; protected static final int DATA_ACCESS_HSPA = 11; + protected static final int DATA_ACCESS_CDMA_EvDo_B = 12; protected CommandsInterface cm; @@ -206,8 +207,8 @@ public abstract class ServiceStateTracker extends Handler { } /** - * Reregister network through toggle perferred network type - * This is a work aorund to deregister and register network since there is + * Re-register network by toggling preferred network type. + * This is a work-around to deregister and register network since there is * no ril api to set COPS=2 (deregister) only. * * @param onComplete is dispatched when this is complete. it will be @@ -229,7 +230,7 @@ public abstract class ServiceStateTracker extends Handler { /** * These two flags manage the behavior of the cell lock -- the * lock should be held if either flag is true. The intention is - * to allow temporary aquisition of the lock to get a single + * to allow temporary acquisition of the lock to get a single * update. Such a lock grab and release can thus be made to not * interfere with more permanent lock holds -- in other words, the * lock will only be released if both flags are false, and so diff --git a/telephony/java/com/android/internal/telephony/SipPhoneNotifier.java b/telephony/java/com/android/internal/telephony/SipPhoneNotifier.java new file mode 100644 index 000000000000..4092c6970f9b --- /dev/null +++ b/telephony/java/com/android/internal/telephony/SipPhoneNotifier.java @@ -0,0 +1,237 @@ +/* + * Copyright (C) 2010 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 android.os.Bundle; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.telephony.TelephonyManager; +import android.util.Log; + +import com.android.internal.telephony.ITelephonyRegistry; + +/** + * Temporary. Will be removed after integrating with CallManager. + * 100% copy from DefaultPhoneNotifier. Cannot access its package level + * constructor; thus the copy. + * @hide + */ +public class SipPhoneNotifier implements PhoneNotifier { + + static final String LOG_TAG = "GSM"; + private static final boolean DBG = true; + private ITelephonyRegistry mRegistry; + + public SipPhoneNotifier() { + mRegistry = ITelephonyRegistry.Stub.asInterface(ServiceManager.getService( + "telephony.registry")); + } + + public void notifyPhoneState(Phone sender) { + Call ringingCall = sender.getRingingCall(); + String incomingNumber = ""; + if (ringingCall != null && ringingCall.getEarliestConnection() != null){ + incomingNumber = ringingCall.getEarliestConnection().getAddress(); + } + try { + mRegistry.notifyCallState(convertCallState(sender.getState()), incomingNumber); + } catch (RemoteException ex) { + // system process is dead + } + } + + public void notifyServiceState(Phone sender) { + try { + mRegistry.notifyServiceState(sender.getServiceState()); + } catch (RemoteException ex) { + // system process is dead + } + } + + public void notifySignalStrength(Phone sender) { + try { + mRegistry.notifySignalStrength(sender.getSignalStrength()); + } catch (RemoteException ex) { + // system process is dead + } + } + + public void notifyMessageWaitingChanged(Phone sender) { + try { + mRegistry.notifyMessageWaitingChanged(sender.getMessageWaitingIndicator()); + } catch (RemoteException ex) { + // system process is dead + } + } + + public void notifyCallForwardingChanged(Phone sender) { + try { + mRegistry.notifyCallForwardingChanged(sender.getCallForwardingIndicator()); + } catch (RemoteException ex) { + // system process is dead + } + } + + public void notifyDataActivity(Phone sender) { + try { + mRegistry.notifyDataActivity(convertDataActivityState(sender.getDataActivityState())); + } catch (RemoteException ex) { + // system process is dead + } + } + + public void notifyDataConnection(Phone sender, String reason) { + TelephonyManager telephony = TelephonyManager.getDefault(); + try { + mRegistry.notifyDataConnection( + convertDataState(sender.getDataConnectionState()), + sender.isDataConnectivityPossible(), reason, + sender.getActiveApn(), + sender.getActiveApnTypes(), + sender.getInterfaceName(null), + ((telephony!=null) ? telephony.getNetworkType() : + TelephonyManager.NETWORK_TYPE_UNKNOWN), + sender.getGateway(null)); + } catch (RemoteException ex) { + // system process is dead + } + } + + public void notifyDataConnectionFailed(Phone sender, String reason) { + try { + mRegistry.notifyDataConnectionFailed(reason); + } catch (RemoteException ex) { + // system process is dead + } + } + + public void notifyCellLocation(Phone sender) { + Bundle data = new Bundle(); + sender.getCellLocation().fillInNotifierBundle(data); + try { + mRegistry.notifyCellLocation(data); + } catch (RemoteException ex) { + // system process is dead + } + } + + private void log(String s) { + Log.d(LOG_TAG, "[PhoneNotifier] " + s); + } + + /** + * Convert the {@link State} enum into the TelephonyManager.CALL_STATE_* constants + * for the public API. + */ + public static int convertCallState(Phone.State state) { + switch (state) { + case RINGING: + return TelephonyManager.CALL_STATE_RINGING; + case OFFHOOK: + return TelephonyManager.CALL_STATE_OFFHOOK; + default: + return TelephonyManager.CALL_STATE_IDLE; + } + } + + /** + * Convert the TelephonyManager.CALL_STATE_* constants into the {@link State} enum + * for the public API. + */ + public static Phone.State convertCallState(int state) { + switch (state) { + case TelephonyManager.CALL_STATE_RINGING: + return Phone.State.RINGING; + case TelephonyManager.CALL_STATE_OFFHOOK: + return Phone.State.OFFHOOK; + default: + return Phone.State.IDLE; + } + } + + /** + * Convert the {@link DataState} enum into the TelephonyManager.DATA_* constants + * for the public API. + */ + public static int convertDataState(Phone.DataState state) { + switch (state) { + case CONNECTING: + return TelephonyManager.DATA_CONNECTING; + case CONNECTED: + return TelephonyManager.DATA_CONNECTED; + case SUSPENDED: + return TelephonyManager.DATA_SUSPENDED; + default: + return TelephonyManager.DATA_DISCONNECTED; + } + } + + /** + * Convert the TelephonyManager.DATA_* constants into {@link DataState} enum + * for the public API. + */ + public static Phone.DataState convertDataState(int state) { + switch (state) { + case TelephonyManager.DATA_CONNECTING: + return Phone.DataState.CONNECTING; + case TelephonyManager.DATA_CONNECTED: + return Phone.DataState.CONNECTED; + case TelephonyManager.DATA_SUSPENDED: + return Phone.DataState.SUSPENDED; + default: + return Phone.DataState.DISCONNECTED; + } + } + + /** + * Convert the {@link DataState} enum into the TelephonyManager.DATA_* constants + * for the public API. + */ + public static int convertDataActivityState(Phone.DataActivityState state) { + switch (state) { + case DATAIN: + return TelephonyManager.DATA_ACTIVITY_IN; + case DATAOUT: + return TelephonyManager.DATA_ACTIVITY_OUT; + case DATAINANDOUT: + return TelephonyManager.DATA_ACTIVITY_INOUT; + case DORMANT: + return TelephonyManager.DATA_ACTIVITY_DORMANT; + default: + return TelephonyManager.DATA_ACTIVITY_NONE; + } + } + + /** + * Convert the TelephonyManager.DATA_* constants into the {@link DataState} enum + * for the public API. + */ + public static Phone.DataActivityState convertDataActivityState(int state) { + switch (state) { + case TelephonyManager.DATA_ACTIVITY_IN: + return Phone.DataActivityState.DATAIN; + case TelephonyManager.DATA_ACTIVITY_OUT: + return Phone.DataActivityState.DATAOUT; + case TelephonyManager.DATA_ACTIVITY_INOUT: + return Phone.DataActivityState.DATAINANDOUT; + case TelephonyManager.DATA_ACTIVITY_DORMANT: + return Phone.DataActivityState.DORMANT; + default: + return Phone.DataActivityState.NONE; + } + } +} diff --git a/telephony/java/com/android/internal/telephony/UUSInfo.java b/telephony/java/com/android/internal/telephony/UUSInfo.java new file mode 100644 index 000000000000..801b84563c98 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/UUSInfo.java @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2010 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; + +public class UUSInfo { + + /* + * User-to-User signaling Info activation types derived from 3GPP 23.087 + * v8.0 + */ + + public static final int UUS_TYPE1_IMPLICIT = 0; + + public static final int UUS_TYPE1_REQUIRED = 1; + + public static final int UUS_TYPE1_NOT_REQUIRED = 2; + + public static final int UUS_TYPE2_REQUIRED = 3; + + public static final int UUS_TYPE2_NOT_REQUIRED = 4; + + public static final int UUS_TYPE3_REQUIRED = 5; + + public static final int UUS_TYPE3_NOT_REQUIRED = 6; + + /* + * User-to-User Signaling Information data coding schemes. Possible values + * for Octet 3 (Protocol Discriminator field) in the UUIE. The values have + * been specified in section 10.5.4.25 of 3GPP TS 24.008 + */ + + public static final int UUS_DCS_USP = 0; /* User specified protocol */ + + public static final int UUS_DCS_OSIHLP = 1; /* OSI higher layer protocol */ + + public static final int UUS_DCS_X244 = 2; /* X.244 */ + + public static final int UUS_DCS_RMCF = 3; /* + * Reserved for system management + * convergence function + */ + + public static final int UUS_DCS_IA5c = 4; /* IA5 characters */ + + private int uusType; + + private int uusDcs; + + private byte[] uusData; + + public UUSInfo() { + this.uusType = UUS_TYPE1_IMPLICIT; + this.uusDcs = UUS_DCS_IA5c; + this.uusData = null; + } + + public UUSInfo(int uusType, int uusDcs, byte[] uusData) { + this.uusType = uusType; + this.uusDcs = uusDcs; + this.uusData = uusData; + } + + public int getDcs() { + return uusDcs; + } + + public void setDcs(int uusDcs) { + this.uusDcs = uusDcs; + } + + public int getType() { + return uusType; + } + + public void setType(int uusType) { + this.uusType = uusType; + } + + public byte[] getUserData() { + return uusData; + } + + public void setUserData(byte[] uusData) { + this.uusData = uusData; + } +} diff --git a/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java b/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java index 58dfeb985df1..f48c956356a1 100755 --- a/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java +++ b/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java @@ -62,11 +62,13 @@ import com.android.internal.telephony.PhoneProxy; import com.android.internal.telephony.PhoneSubInfo; import com.android.internal.telephony.TelephonyIntents; import com.android.internal.telephony.TelephonyProperties; +import com.android.internal.telephony.UUSInfo; import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_ALPHA; import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC; import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_ISO_COUNTRY; +import java.util.ArrayList; import java.util.List; @@ -98,9 +100,9 @@ public class CDMAPhone extends PhoneBase { CdmaCallTracker mCT; CdmaSMSDispatcher mSMS; CdmaServiceStateTracker mSST; - RuimFileHandler mRuimFileHandler; RuimRecords mRuimRecords; RuimCard mRuimCard; + ArrayList <CdmaMmiCode> mPendingMmis = new ArrayList<CdmaMmiCode>(); RuimPhoneBookInterfaceManager mRuimPhoneBookInterfaceManager; RuimSmsInterfaceManager mRuimSmsInterfaceManager; PhoneSubInfo mSubInfo; @@ -155,7 +157,7 @@ public class CDMAPhone extends PhoneBase { mDataConnection = new CdmaDataConnectionTracker (this); mRuimCard = new RuimCard(this); mRuimPhoneBookInterfaceManager = new RuimPhoneBookInterfaceManager(this); - mRuimSmsInterfaceManager = new RuimSmsInterfaceManager(this); + mRuimSmsInterfaceManager = new RuimSmsInterfaceManager(this, mSMS); mSubInfo = new PhoneSubInfo(this); mEriManager = new EriManager(this, context, EriManager.ERI_FROM_XML); @@ -219,6 +221,8 @@ public class CDMAPhone extends PhoneBase { mSST.unregisterForNetworkAttach(this); //EVENT_REGISTERED_TO_NETWORK mCM.unSetOnSuppServiceNotification(this); + mPendingMmis.clear(); + //Force all referenced classes to unregister their former registered events mCT.dispose(); mDataConnection.dispose(); @@ -344,6 +348,10 @@ public class CDMAPhone extends PhoneBase { return mCT.dial(newDialString); } + public Connection dial(String dialString, UUSInfo uusInfo) throws CallStateException { + throw new CallStateException("Sending UUS information NOT supported in CDMA!"); + } + public SignalStrength getSignalStrength() { return mSST.mSignalStrength; } @@ -355,8 +363,7 @@ public class CDMAPhone extends PhoneBase { public List<? extends MmiCode> getPendingMmiCodes() { - Log.e(LOG_TAG, "method getPendingMmiCodes is NOT supported in CDMA!"); - return null; + return mPendingMmis; } public void registerForSuppServiceNotification( @@ -373,6 +380,15 @@ public class CDMAPhone extends PhoneBase { return false; } + boolean isInCall() { + CdmaCall.State foregroundCallState = getForegroundCall().getState(); + CdmaCall.State backgroundCallState = getBackgroundCall().getState(); + CdmaCall.State ringingCallState = getRingingCall().getState(); + + return (foregroundCallState.isAlive() || backgroundCallState.isAlive() || ringingCallState + .isAlive()); + } + public void setNetworkSelectionModeAutomatic(Message response) { Log.e(LOG_TAG, "method setNetworkSelectionModeAutomatic is NOT supported in CDMA!"); @@ -477,7 +493,18 @@ public class CDMAPhone extends PhoneBase { } public boolean handlePinMmi(String dialString) { - Log.e(LOG_TAG, "method handlePinMmi is NOT supported in CDMA!"); + CdmaMmiCode mmi = CdmaMmiCode.newFromDialString(dialString, this); + + if (mmi == null) { + Log.e(LOG_TAG, "Mmi is NULL!"); + return false; + } else if (mmi.isPukCommand()) { + mPendingMmis.add(mmi); + mMmiRegistrants.notifyRegistrants(new AsyncResult(null, mmi, null)); + mmi.processCode(); + return true; + } + Log.e(LOG_TAG, "Unrecognized mmi!"); return false; } @@ -489,6 +516,22 @@ public class CDMAPhone extends PhoneBase { (mDataConnection.getDataOnRoamingEnabled() || !getServiceState().getRoaming()); } + /** + * Removes the given MMI from the pending list and notifies registrants that + * it is complete. + * + * @param mmi MMI that is done + */ + void onMMIDone(CdmaMmiCode mmi) { + /* + * Only notify complete if it's on the pending list. Otherwise, it's + * already been handled (eg, previously canceled). + */ + if (mPendingMmis.remove(mmi)) { + mMmiCompleteRegistrants.notifyRegistrants(new AsyncResult(null, mmi, null)); + } + } + public void setLine1Number(String alphaTag, String number, Message onComplete) { Log.e(LOG_TAG, "setLine1Number: not possible in CDMA"); } @@ -1361,7 +1404,7 @@ public class CDMAPhone extends PhoneBase { SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext()); SharedPreferences.Editor editor = sp.edit(); editor.putString(VM_NUMBER_CDMA, number); - editor.commit(); + editor.apply(); } /** @@ -1410,5 +1453,4 @@ public class CDMAPhone extends PhoneBase { } return false; } - } diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaCall.java b/telephony/java/com/android/internal/telephony/cdma/CdmaCall.java index c3bb01f6db59..4ad61bb083b6 100644 --- a/telephony/java/com/android/internal/telephony/cdma/CdmaCall.java +++ b/telephony/java/com/android/internal/telephony/cdma/CdmaCall.java @@ -75,8 +75,7 @@ public final class CdmaCall extends Call { public Phone getPhone() { - //TODO, see GsmCall - return null; + return owner.phone; } public boolean isMultiparty() { diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaCallTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaCallTracker.java index 1005d2045f42..3669e6062b02 100644 --- a/telephony/java/com/android/internal/telephony/cdma/CdmaCallTracker.java +++ b/telephony/java/com/android/internal/telephony/cdma/CdmaCallTracker.java @@ -61,7 +61,7 @@ public final class CdmaCallTracker extends CallTracker { RegistrantList callWaitingRegistrants = new RegistrantList(); - // connections dropped durin last poll + // connections dropped during last poll ArrayList<CdmaConnection> droppedDuringPoll = new ArrayList<CdmaConnection>(MAX_CONNECTIONS); diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaConnection.java b/telephony/java/com/android/internal/telephony/cdma/CdmaConnection.java index 188145b33fcb..fbe455e1ffdd 100755 --- a/telephony/java/com/android/internal/telephony/cdma/CdmaConnection.java +++ b/telephony/java/com/android/internal/telephony/cdma/CdmaConnection.java @@ -945,4 +945,10 @@ public class CdmaConnection extends Connection { public int getNumberPresentation() { return numberPresentation; } + + @Override + public UUSInfo getUUSInfo() { + // UUS information not supported in CDMA + return null; + } } diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java index 217e1e86b093..9f2a44b743b7 100644 --- a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java +++ b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java @@ -114,7 +114,7 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker { // if we have no active Apn this is null protected ApnSetting mActiveApn; - // Possibly promoate to base class, the only difference is + // Possibly promote to base class, the only difference is // the INTENT_RECONNECT_ALARM action is a different string. // Do consider technology changes if it is promoted. BroadcastReceiver mIntentReceiver = new BroadcastReceiver () @@ -420,7 +420,7 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker { CdmaDataConnection conn = findFreeDataConnection(); if (conn == null) { - if (DBG) log("setupData: No free CdmaDataConnectionfound!"); + if (DBG) log("setupData: No free CdmaDataConnection found!"); return false; } @@ -646,7 +646,7 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker { } /** - * @override com.android.intenral.telephony.DataConnectionTracker + * @override com.android.internal.telephony.DataConnectionTracker */ @Override protected void onEnableNewApn() { diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaMmiCode.java b/telephony/java/com/android/internal/telephony/cdma/CdmaMmiCode.java new file mode 100644 index 000000000000..8dd8c2e6c679 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/cdma/CdmaMmiCode.java @@ -0,0 +1,296 @@ +/* + * 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.cdma; + +import android.content.Context; + +import com.android.internal.telephony.CommandException; +import com.android.internal.telephony.MmiCode; + +import android.os.AsyncResult; +import android.os.Handler; +import android.os.Message; +import android.util.Log; + +import java.util.regex.Pattern; +import java.util.regex.Matcher; + +/** + * This class can handle Puk code Mmi + * + * {@hide} + * + */ +public final class CdmaMmiCode extends Handler implements MmiCode { + static final String LOG_TAG = "CDMA_MMI"; + + // Constants + + // From TS 22.030 6.5.2 + static final String ACTION_REGISTER = "**"; + + // Supp Service codes from TS 22.030 Annex B + static final String SC_PUK = "05"; + + // Event Constant + + static final int EVENT_SET_COMPLETE = 1; + + // Instance Variables + + CDMAPhone phone; + Context context; + + String action; // ACTION_REGISTER + String sc; // Service Code + String sia, sib, sic; // Service Info a,b,c + String poundString; // Entire MMI string up to and including # + String dialingNumber; + String pwd; // For password registration + + State state = State.PENDING; + CharSequence message; + + // Class Variables + + static Pattern sPatternSuppService = Pattern.compile( + "((\\*|#|\\*#|\\*\\*|##)(\\d{2,3})(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*))?)?)?)?#)(.*)"); +/* 1 2 3 4 5 6 7 8 9 10 11 12 + + 1 = Full string up to and including # + 2 = action + 3 = service code + 5 = SIA + 7 = SIB + 9 = SIC + 10 = dialing number +*/ + + static final int MATCH_GROUP_POUND_STRING = 1; + static final int MATCH_GROUP_ACTION = 2; + static final int MATCH_GROUP_SERVICE_CODE = 3; + static final int MATCH_GROUP_SIA = 5; + static final int MATCH_GROUP_SIB = 7; + static final int MATCH_GROUP_SIC = 9; + static final int MATCH_GROUP_PWD_CONFIRM = 11; + static final int MATCH_GROUP_DIALING_NUMBER = 12; + + + // Public Class methods + + /** + * Check if provided string contains Mmi code in it and create corresponding + * Mmi if it does + */ + + public static CdmaMmiCode + newFromDialString(String dialString, CDMAPhone phone) { + Matcher m; + CdmaMmiCode ret = null; + + m = sPatternSuppService.matcher(dialString); + + // Is this formatted like a standard supplementary service code? + if (m.matches()) { + ret = new CdmaMmiCode(phone); + ret.poundString = makeEmptyNull(m.group(MATCH_GROUP_POUND_STRING)); + ret.action = makeEmptyNull(m.group(MATCH_GROUP_ACTION)); + ret.sc = makeEmptyNull(m.group(MATCH_GROUP_SERVICE_CODE)); + ret.sia = makeEmptyNull(m.group(MATCH_GROUP_SIA)); + ret.sib = makeEmptyNull(m.group(MATCH_GROUP_SIB)); + ret.sic = makeEmptyNull(m.group(MATCH_GROUP_SIC)); + ret.pwd = makeEmptyNull(m.group(MATCH_GROUP_PWD_CONFIRM)); + ret.dialingNumber = makeEmptyNull(m.group(MATCH_GROUP_DIALING_NUMBER)); + + } + + return ret; + } + + // Private Class methods + + /** make empty strings be null. + * Regexp returns empty strings for empty groups + */ + private static String + makeEmptyNull (String s) { + if (s != null && s.length() == 0) return null; + + return s; + } + + // Constructor + + CdmaMmiCode (CDMAPhone phone) { + super(phone.getHandler().getLooper()); + this.phone = phone; + this.context = phone.getContext(); + } + + // MmiCode implementation + + public State + getState() { + return state; + } + + public CharSequence + getMessage() { + return message; + } + + // inherited javadoc suffices + public void + cancel() { + // Complete or failed cannot be cancelled + if (state == State.COMPLETE || state == State.FAILED) { + return; + } + + state = State.CANCELLED; + phone.onMMIDone (this); + } + + public boolean isCancelable() { + return false; + } + + // Instance Methods + + /** + * @return true if the Service Code is PIN/PIN2/PUK/PUK2-related + */ + boolean isPukCommand() { + return sc != null && sc.equals(SC_PUK); + } + + boolean isRegister() { + return action != null && action.equals(ACTION_REGISTER); + } + + public boolean isUssdRequest() { + Log.w(LOG_TAG, "isUssdRequest is not implemented in CdmaMmiCode"); + return false; + } + + /** Process a MMI PUK code */ + void + processCode () { + try { + if (isPukCommand()) { + // sia = old PUK + // sib = new PIN + // sic = new PIN + String oldPinOrPuk = sia; + String newPin = sib; + int pinLen = newPin.length(); + if (isRegister()) { + if (!newPin.equals(sic)) { + // password mismatch; return error + handlePasswordError(com.android.internal.R.string.mismatchPin); + } else if (pinLen < 4 || pinLen > 8 ) { + // invalid length + handlePasswordError(com.android.internal.R.string.invalidPin); + } else { + phone.mCM.supplyIccPuk(oldPinOrPuk, newPin, + obtainMessage(EVENT_SET_COMPLETE, this)); + } + } else { + throw new RuntimeException ("Invalid or Unsupported MMI Code"); + } + } else { + throw new RuntimeException ("Invalid or Unsupported MMI Code"); + } + } catch (RuntimeException exc) { + state = State.FAILED; + message = context.getText(com.android.internal.R.string.mmiError); + phone.onMMIDone(this); + } + } + + private void handlePasswordError(int res) { + state = State.FAILED; + StringBuilder sb = new StringBuilder(getScString()); + sb.append("\n"); + sb.append(context.getText(res)); + message = sb; + phone.onMMIDone(this); + } + + public void + handleMessage (Message msg) { + AsyncResult ar; + + if (msg.what == EVENT_SET_COMPLETE) { + ar = (AsyncResult) (msg.obj); + onSetComplete(ar); + } else { + Log.e(LOG_TAG, "Unexpected reply"); + } + } + // Private instance methods + + private CharSequence getScString() { + if (sc != null) { + if (isPukCommand()) { + return context.getText(com.android.internal.R.string.PinMmi); + } + } + + return ""; + } + + private void + onSetComplete(AsyncResult ar){ + StringBuilder sb = new StringBuilder(getScString()); + sb.append("\n"); + + if (ar.exception != null) { + state = State.FAILED; + if (ar.exception instanceof CommandException) { + CommandException.Error err = ((CommandException)(ar.exception)).getCommandError(); + if (err == CommandException.Error.PASSWORD_INCORRECT) { + if (isPukCommand()) { + sb.append(context.getText( + com.android.internal.R.string.badPuk)); + } else { + sb.append(context.getText( + com.android.internal.R.string.passwordIncorrect)); + } + } else { + sb.append(context.getText( + com.android.internal.R.string.mmiError)); + } + } else { + sb.append(context.getText( + com.android.internal.R.string.mmiError)); + } + } else if (isRegister()) { + state = State.COMPLETE; + sb.append(context.getText( + com.android.internal.R.string.serviceRegistered)); + } else { + state = State.FAILED; + sb.append(context.getText( + com.android.internal.R.string.mmiError)); + } + + message = sb; + phone.onMMIDone(this); + } + +} diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java b/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java index ed93aea18988..dceff2a956b6 100644 --- a/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java +++ b/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java @@ -28,21 +28,20 @@ import android.database.SQLException; import android.os.AsyncResult; import android.os.Message; import android.os.SystemProperties; +import android.preference.PreferenceManager; import android.provider.Telephony; import android.provider.Telephony.Sms.Intents; -import android.preference.PreferenceManager; -import android.util.Config; -import android.util.Log; import android.telephony.SmsManager; import android.telephony.SmsMessage.MessageClass; +import android.util.Config; +import android.util.Log; -import com.android.internal.telephony.TelephonyProperties; import com.android.internal.telephony.CommandsInterface; +import com.android.internal.telephony.SMSDispatcher; import com.android.internal.telephony.SmsHeader; import com.android.internal.telephony.SmsMessageBase; -import com.android.internal.telephony.SMSDispatcher; import com.android.internal.telephony.SmsMessageBase.TextEncodingDetails; -import com.android.internal.telephony.cdma.SmsMessage; +import com.android.internal.telephony.TelephonyProperties; import com.android.internal.telephony.cdma.sms.SmsEnvelope; import com.android.internal.telephony.cdma.sms.UserData; import com.android.internal.util.HexDump; @@ -51,20 +50,16 @@ import java.io.ByteArrayOutputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; -import java.lang.Boolean; final class CdmaSMSDispatcher extends SMSDispatcher { private static final String TAG = "CDMA"; - private CDMAPhone mCdmaPhone; - private byte[] mLastDispatchedSmsFingerprint; private byte[] mLastAcknowledgedSmsFingerprint; CdmaSMSDispatcher(CDMAPhone phone) { super(phone); - mCdmaPhone = phone; } /** @@ -129,10 +124,10 @@ final class CdmaSMSDispatcher extends SMSDispatcher { Log.d(TAG, "Voicemail count=" + voicemailCount); // Store the voicemail count in preferences. SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences( - ((CDMAPhone) mPhone).getContext()); + mPhone.getContext()); SharedPreferences.Editor editor = sp.edit(); editor.putInt(CDMAPhone.VM_COUNT_CDMA, voicemailCount); - editor.commit(); + editor.apply(); ((CDMAPhone) mPhone).updateMessageWaitingIndicator(voicemailCount); handled = true; } else if (((SmsEnvelope.TELESERVICE_WMT == teleService) || @@ -176,7 +171,7 @@ final class CdmaSMSDispatcher extends SMSDispatcher { * TODO(cleanup): Why are we using a getter method for this * (and for so many other sms fields)? Trivial getters and * setters like this are direct violations of the style guide. - * If the purpose is to protect agaist writes (by not + * If the purpose is to protect against writes (by not * providing a setter) then any protection is illusory (and * hence bad) for cases where the values are not primitives, * such as this call for the header. Since this is an issue @@ -440,7 +435,7 @@ final class CdmaSMSDispatcher extends SMSDispatcher { protected void sendSms(SmsTracker tracker) { HashMap map = tracker.mData; - byte smsc[] = (byte[]) map.get("smsc"); + // byte smsc[] = (byte[]) map.get("smsc"); // unused for CDMA byte pdu[] = (byte[]) map.get("pdu"); Message reply = obtainMessage(EVENT_SEND_SMS_COMPLETE, tracker); diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java index 39fe007f4b20..2cad6cc00a65 100644 --- a/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java +++ b/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java @@ -66,7 +66,7 @@ final class CdmaServiceStateTracker extends ServiceStateTracker { CdmaCellLocation cellLoc; CdmaCellLocation newCellLoc; - /** if time between NTIZ updates is less than mNitzUpdateSpacing the update may be ignored. */ + /** if time between NITZ updates is less than mNitzUpdateSpacing the update may be ignored. */ private static final int NITZ_UPDATE_SPACING_DEFAULT = 1000 * 60 * 10; private int mNitzUpdateSpacing = SystemProperties.getInt("ro.nitz_update_spacing", NITZ_UPDATE_SPACING_DEFAULT); @@ -395,7 +395,7 @@ final class CdmaServiceStateTracker extends ServiceStateTracker { } // Release any temporary cell lock, which could have been - // aquired to allow a single-shot location update. + // acquired to allow a single-shot location update. disableSingleLocationUpdate(); break; @@ -591,7 +591,7 @@ final class CdmaServiceStateTracker extends ServiceStateTracker { boolean showPlmn = false; int rule = 0; if (cm.getRadioState().isRUIMReady()) { - // TODO RUIM SPN is not implemnted, EF_SPN has to be read and Display Condition + // TODO RUIM SPN is not implemented, EF_SPN has to be read and Display Condition // Character Encoding, Language Indicator and SPN has to be set // rule = phone.mRuimRecords.getDisplayRule(ss.getOperatorNumeric()); // spn = phone.mSIMRecords.getServiceProvideName(); @@ -872,7 +872,6 @@ final class CdmaServiceStateTracker extends ServiceStateTracker { * and start over again if the radio notifies us that some * event has changed */ - private void pollState() { pollingContext = new int[1]; @@ -945,6 +944,9 @@ final class CdmaServiceStateTracker extends ServiceStateTracker { case DATA_ACCESS_CDMA_EvDo_A: ret = "CDMA - EvDo rev. A"; break; + case DATA_ACCESS_CDMA_EvDo_B: + ret = "CDMA - EvDo rev. B"; + break; default: if (DBG) { Log.e(LOG_TAG, "Wrong network. Can not return a string."); @@ -1237,6 +1239,7 @@ final class CdmaServiceStateTracker extends ServiceStateTracker { case 6: // RADIO_TECHNOLOGY_1xRTT case 7: // RADIO_TECHNOLOGY_EVDO_0 case 8: // RADIO_TECHNOLOGY_EVDO_A + case 12: // RADIO_TECHNOLOGY_EVDO_B retVal = ServiceState.STATE_IN_SERVICE; break; default: @@ -1256,7 +1259,7 @@ final class CdmaServiceStateTracker extends ServiceStateTracker { return ServiceState.STATE_IN_SERVICE; case 2: // 2 is "searching", fall through case 3: // 3 is "registration denied", fall through - case 4: // 4 is "unknown" no vaild in current baseband + case 4: // 4 is "unknown", not valid in current baseband return ServiceState.STATE_OUT_OF_SERVICE; case 5:// 5 is "Registered, roaming" return ServiceState.STATE_IN_SERVICE; @@ -1295,12 +1298,12 @@ final class CdmaServiceStateTracker extends ServiceStateTracker { */ private boolean isRoamIndForHomeSystem(String roamInd) { // retrieve the carrier-specified list of ERIs for home system - String homeRoamIndcators = SystemProperties.get("ro.cdma.homesystem"); + String homeRoamIndicators = SystemProperties.get("ro.cdma.homesystem"); - if (!TextUtils.isEmpty(homeRoamIndcators)) { + if (!TextUtils.isEmpty(homeRoamIndicators)) { // searches through the comma-separated list for a match, // return true if one is found. - for (String homeRoamInd : homeRoamIndcators.split(",")) { + for (String homeRoamInd : homeRoamIndicators.split(",")) { if (homeRoamInd.equals(roamInd)) { return true; } diff --git a/telephony/java/com/android/internal/telephony/cdma/RuimSmsInterfaceManager.java b/telephony/java/com/android/internal/telephony/cdma/RuimSmsInterfaceManager.java index cfcfd98b6840..e97549dc18f7 100644 --- a/telephony/java/com/android/internal/telephony/cdma/RuimSmsInterfaceManager.java +++ b/telephony/java/com/android/internal/telephony/cdma/RuimSmsInterfaceManager.java @@ -27,6 +27,7 @@ import com.android.internal.telephony.IccConstants; import com.android.internal.telephony.IccSmsInterfaceManager; import com.android.internal.telephony.IccUtils; import com.android.internal.telephony.PhoneProxy; +import com.android.internal.telephony.SMSDispatcher; import com.android.internal.telephony.SmsRawData; import java.util.ArrayList; @@ -81,9 +82,9 @@ public class RuimSmsInterfaceManager extends IccSmsInterfaceManager { } }; - public RuimSmsInterfaceManager(CDMAPhone phone) { + public RuimSmsInterfaceManager(CDMAPhone phone, SMSDispatcher dispatcher) { super(phone); - mDispatcher = new CdmaSMSDispatcher(phone); + mDispatcher = dispatcher; } public void dispose() { diff --git a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java index 403b7a1d16d3..0f3b8ff32699 100755 --- a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java +++ b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java @@ -287,7 +287,7 @@ public class SmsMessage extends SmsMessageBase { * @param destAddr Address of the recipient. * @param message String representation of the message payload. * @param statusReportRequested Indicates whether a report is requested for this message. - * @param headerData Array containing the data for the User Data Header, preceded + * @param smsHeader Array containing the data for the User Data Header, preceded * by the Element Identifiers. * @return a <code>SubmitPdu</code> containing the encoded SC * address, if applicable, and the encoded message. @@ -313,7 +313,7 @@ public class SmsMessage extends SmsMessageBase { } /** - * Get an SMS-SUBMIT PDU for a data message to a destination address & port + * Get an SMS-SUBMIT PDU for a data message to a destination address and port. * * @param scAddr Service Centre address. null == use default * @param destAddr the address of the destination for the message @@ -355,7 +355,7 @@ public class SmsMessage extends SmsMessageBase { * Get an SMS-SUBMIT PDU for a data message to a destination address & port * * @param destAddr the address of the destination for the message - * @param userDara the data for the message + * @param userData the data for the message * @param statusReportRequested Indicates whether a report is requested for this message. * @return a <code>SubmitPdu</code> containing the encoded SC * address, if applicable, and the encoded message. @@ -446,7 +446,7 @@ public class SmsMessage extends SmsMessageBase { */ public static TextEncodingDetails calculateLength(CharSequence messageBody, boolean use7bitOnly) { - return BearerData.calcTextEncodingDetails(messageBody.toString(), use7bitOnly); + return BearerData.calcTextEncodingDetails(messageBody, use7bitOnly); } /** diff --git a/telephony/java/com/android/internal/telephony/cdma/TtyIntent.java b/telephony/java/com/android/internal/telephony/cdma/TtyIntent.java index 3813b1da98e0..4907aa946800 100644 --- a/telephony/java/com/android/internal/telephony/cdma/TtyIntent.java +++ b/telephony/java/com/android/internal/telephony/cdma/TtyIntent.java @@ -56,10 +56,10 @@ public class TtyIntent { /** * The lookup key for an int that indicates preferred TTY mode. * Valid modes are: - * - {@link Phone.TTY_MODE_OFF} - * - {@link Phone.TTY_MODE_FULL} - * - {@link Phone.TTY_MODE_HCO} - * - {@link Phone.TTY_MODE_VCO} + * - {@link com.android.internal.telephony.Phone#TTY_MODE_OFF} + * - {@link com.android.internal.telephony.Phone#TTY_MODE_FULL} + * - {@link com.android.internal.telephony.Phone#TTY_MODE_HCO} + * - {@link com.android.internal.telephony.Phone#TTY_MODE_VCO} * * {@hide} */ diff --git a/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java index c7032aca0a71..ab79fe98ea2d 100644 --- a/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java +++ b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java @@ -405,7 +405,8 @@ public final class BearerData { /** * Calculate the message text encoding length, fragmentation, and other details. * - * @param force ignore (but still count) illegal characters if true + * @param msg message text + * @param force7BitEncoding ignore (but still count) illegal characters if true * @return septet count, or -1 on failure */ public static TextEncodingDetails calcTextEncodingDetails(CharSequence msg, @@ -427,9 +428,10 @@ public final class BearerData { ted.codeUnitCount = msg.length(); int octets = ted.codeUnitCount * 2; if (octets > MAX_USER_DATA_BYTES) { - ted.msgCount = (octets / MAX_USER_DATA_BYTES_WITH_HEADER) + 1; - ted.codeUnitsRemaining = (MAX_USER_DATA_BYTES_WITH_HEADER - - (octets % MAX_USER_DATA_BYTES_WITH_HEADER))/2; + ted.msgCount = (octets + (MAX_USER_DATA_BYTES_WITH_HEADER - 1)) / + MAX_USER_DATA_BYTES_WITH_HEADER; + ted.codeUnitsRemaining = ((ted.msgCount * + MAX_USER_DATA_BYTES_WITH_HEADER) - octets) / 2; } else { ted.msgCount = 1; ted.codeUnitsRemaining = (MAX_USER_DATA_BYTES - octets)/2; @@ -802,9 +804,8 @@ public final class BearerData { * Create serialized representation for BearerData object. * (See 3GPP2 C.R1001-F, v1.0, section 4.5 for layout details) * - * @param bearerData an instance of BearerData. - * - * @return data byta array of raw encoded SMS bearer data. + * @param bData an instance of BearerData. + * @return byte array of raw encoded SMS bearer data. */ public static byte[] encode(BearerData bData) { bData.hasUserDataHeader = ((bData.userData != null) && diff --git a/telephony/java/com/android/internal/telephony/gsm/CallFailCause.java b/telephony/java/com/android/internal/telephony/gsm/CallFailCause.java index e7fbf6bd8a8b..af2ad48a9bc0 100644 --- a/telephony/java/com/android/internal/telephony/gsm/CallFailCause.java +++ b/telephony/java/com/android/internal/telephony/gsm/CallFailCause.java @@ -25,6 +25,9 @@ package com.android.internal.telephony.gsm; * */ public interface CallFailCause { + // Unassigned/Unobtainable number + static final int UNOBTAINABLE_NUMBER = 1; + static final int NORMAL_CLEARING = 16; // Busy Tone static final int USER_BUSY = 17; diff --git a/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java b/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java index 2bb796827049..689a97265ef7 100755..100644 --- a/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java +++ b/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java @@ -67,6 +67,7 @@ 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.gsm.stk.StkService; import com.android.internal.telephony.test.SimulatedRadioControl; import com.android.internal.telephony.IccVmNotSupportedException; @@ -116,10 +117,6 @@ public class GSMPhone extends PhoneBase { Thread debugPortThread; ServerSocket debugSocket; - private int mReportedRadioResets; - private int mReportedAttemptedConnects; - private int mReportedSuccessfulConnects; - private String mImei; private String mImeiSv; private String mVmNumber; @@ -150,7 +147,7 @@ public class GSMPhone extends PhoneBase { mSimCard = new SimCard(this); if (!unitTestMode) { mSimPhoneBookIntManager = new SimPhoneBookInterfaceManager(this); - mSimSmsIntManager = new SimSmsInterfaceManager(this); + mSimSmsIntManager = new SimSmsInterfaceManager(this, mSMS); mSubInfo = new PhoneSubInfo(this); } mStkService = StkService.getInstance(mCM, mSIMRecords, mContext, @@ -711,7 +708,12 @@ public class GSMPhone extends PhoneBase { } public Connection - dial (String dialString) throws CallStateException { + dial(String dialString) throws CallStateException { + return dial(dialString, null); + } + + public Connection + dial (String dialString, UUSInfo uusInfo) throws CallStateException { // Need to make sure dialString gets parsed properly String newDialString = PhoneNumberUtils.stripSeparators(dialString); @@ -727,9 +729,9 @@ public class GSMPhone extends PhoneBase { "dialing w/ mmi '" + mmi + "'..."); if (mmi == null) { - return mCT.dial(newDialString); + return mCT.dial(newDialString, uusInfo); } else if (mmi.isTemporaryModeCLIR()) { - return mCT.dial(mmi.dialingNumber, mmi.getCLIRMode()); + return mCT.dial(mmi.dialingNumber, mmi.getCLIRMode(), uusInfo); } else { mPendingMMIs.add(mmi); mMmiRegistrants.notifyRegistrants(new AsyncResult(null, mmi, null)); @@ -801,7 +803,7 @@ public class GSMPhone extends PhoneBase { SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext()); SharedPreferences.Editor editor = sp.edit(); editor.putString(VM_NUMBER, number); - editor.commit(); + editor.apply(); setVmSimImsi(getSubscriberId()); } @@ -824,7 +826,7 @@ public class GSMPhone extends PhoneBase { SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext()); SharedPreferences.Editor editor = sp.edit(); editor.putString(VM_SIM_IMSI, imsi); - editor.commit(); + editor.apply(); } public String getVoiceMailAlphaTag() { diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmCall.java b/telephony/java/com/android/internal/telephony/gsm/GsmCall.java index 9542d20e1e4c..58124a25beea 100644 --- a/telephony/java/com/android/internal/telephony/gsm/GsmCall.java +++ b/telephony/java/com/android/internal/telephony/gsm/GsmCall.java @@ -70,8 +70,7 @@ class GsmCall extends Call { public Phone getPhone() { - //TODO - return null; + return owner.phone; } public boolean diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmCallTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmCallTracker.java index 87530e42874a..06f310c54a38 100644 --- a/telephony/java/com/android/internal/telephony/gsm/GsmCallTracker.java +++ b/telephony/java/com/android/internal/telephony/gsm/GsmCallTracker.java @@ -37,6 +37,7 @@ import com.android.internal.telephony.DriverCall; import com.android.internal.telephony.EventLogTags; import com.android.internal.telephony.Phone; import com.android.internal.telephony.TelephonyProperties; +import com.android.internal.telephony.UUSInfo; import com.android.internal.telephony.gsm.CallFailCause; import com.android.internal.telephony.gsm.GSMPhone; import com.android.internal.telephony.gsm.GsmCall; @@ -65,7 +66,7 @@ public final class GsmCallTracker extends CallTracker { RegistrantList voiceCallStartedRegistrants = new RegistrantList(); - // connections dropped durin last poll + // connections dropped during last poll ArrayList<GsmConnection> droppedDuringPoll = new ArrayList<GsmConnection>(MAX_CONNECTIONS); @@ -167,7 +168,7 @@ public final class GsmCallTracker extends CallTracker { * clirMode is one of the CLIR_ constants */ Connection - dial (String dialString, int clirMode) throws CallStateException { + dial (String dialString, int clirMode, UUSInfo uusInfo) throws CallStateException { // note that this triggers call state changed notif clearDisconnected(); @@ -213,7 +214,7 @@ public final class GsmCallTracker extends CallTracker { // Always unmute when initiating a new call setMute(false); - cm.dial(pendingMO.address, clirMode, obtainCompleteMessage()); + cm.dial(pendingMO.address, clirMode, uusInfo, obtainCompleteMessage()); } updatePhoneState(); @@ -222,10 +223,19 @@ public final class GsmCallTracker extends CallTracker { return pendingMO; } + Connection + dial(String dialString) throws CallStateException { + return dial(dialString, CommandsInterface.CLIR_DEFAULT, null); + } + + Connection + dial(String dialString, UUSInfo uusInfo) throws CallStateException { + return dial(dialString, CommandsInterface.CLIR_DEFAULT, uusInfo); + } Connection - dial (String dialString) throws CallStateException { - return dial(dialString, CommandsInterface.CLIR_DEFAULT); + dial(String dialString, int clirMode) throws CallStateException { + return dial(dialString, clirMode, null); } void diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmConnection.java b/telephony/java/com/android/internal/telephony/gsm/GsmConnection.java index 4788a0130657..7dc25044eb16 100644 --- a/telephony/java/com/android/internal/telephony/gsm/GsmConnection.java +++ b/telephony/java/com/android/internal/telephony/gsm/GsmConnection.java @@ -73,6 +73,7 @@ public class GsmConnection extends Connection { DisconnectCause cause = DisconnectCause.NOT_DISCONNECTED; PostDialState postDialState = PostDialState.NOT_STARTED; int numberPresentation = Connection.PRESENTATION_ALLOWED; + UUSInfo uusInfo; Handler h; @@ -126,6 +127,7 @@ public class GsmConnection extends Connection { isIncoming = dc.isMT; createTime = System.currentTimeMillis(); numberPresentation = dc.numberPresentation; + uusInfo = dc.uusInfo; this.index = index; @@ -356,6 +358,9 @@ public class GsmConnection extends Connection { case CallFailCause.FDN_BLOCKED: return DisconnectCause.FDN_BLOCKED; + case CallFailCause.UNOBTAINABLE_NUMBER: + return DisconnectCause.UNOBTAINABLE_NUMBER; + case CallFailCause.ERROR_UNSPECIFIED: case CallFailCause.NORMAL_CLEARING: default: @@ -728,4 +733,9 @@ public class GsmConnection extends Connection { public int getNumberPresentation() { return numberPresentation; } + + @Override + public UUSInfo getUUSInfo() { + return uusInfo; + } } diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnection.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnection.java index d893ec4c4837..09d46dd6e8b2 100644 --- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnection.java +++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnection.java @@ -134,7 +134,7 @@ public class GsmDataConnection extends DataConnection { cause = FailCause.INSUFFICIENT_RESOURCES; break; case PDP_FAIL_MISSING_UKNOWN_APN: - cause = FailCause.MISSING_UKNOWN_APN; + cause = FailCause.MISSING_UNKNOWN_APN; break; case PDP_FAIL_UNKNOWN_PDP_ADDRESS_TYPE: cause = FailCause.UNKNOWN_PDP_ADDRESS; diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java index 627d94d387cc..e7d57bc22037 100644 --- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java +++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java @@ -45,7 +45,6 @@ import android.provider.Telephony; import android.telephony.ServiceState; import android.telephony.TelephonyManager; import android.telephony.gsm.GsmCellLocation; -import android.text.TextUtils; import android.util.EventLog; import android.util.Log; @@ -151,9 +150,9 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { static final String APN_ID = "apn_id"; private boolean canSetPreferApn = false; - // for tracking retrys on the default APN + // for tracking retries on the default APN private RetryManager mDefaultRetryManager; - // for tracking retrys on a secondary APN + // for tracking retries on a secondary APN private RetryManager mSecondaryRetryManager; BroadcastReceiver mIntentReceiver = new BroadcastReceiver () @@ -190,8 +189,8 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { WifiManager.WIFI_STATE_UNKNOWN) == WifiManager.WIFI_STATE_ENABLED; if (!enabled) { - // when wifi got disabeled, the NETWORK_STATE_CHANGED_ACTION - // quit and wont report disconnected til next enalbing. + // when wifi got disabled, the NETWORK_STATE_CHANGED_ACTION + // quit and won't report disconnected til next enabling. mIsWifiConnected = false; } } @@ -452,7 +451,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { waitingApns = buildWaitingApns(); if (waitingApns.isEmpty()) { if (DBG) log("No APN found"); - notifyNoData(GsmDataConnection.FailCause.MISSING_UKNOWN_APN); + notifyNoData(GsmDataConnection.FailCause.MISSING_UNKNOWN_APN); return false; } else { log ("Create from allApns : " + apnListToString(allApns)); @@ -1130,7 +1129,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { if (isApnTypeActive(Phone.APN_TYPE_DEFAULT)) { SystemProperties.set("gsm.defaultpdpcontext.active", "true"); if (canSetPreferApn && preferredApn == null) { - Log.d(LOG_TAG, "PREFERED APN is null"); + Log.d(LOG_TAG, "PREFERRED APN is null"); preferredApn = mActiveApn; setPreferredApn(preferredApn.id); } @@ -1158,10 +1157,8 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { // No try for permanent failure if (cause.isPermanentFail()) { notifyNoData(cause); - if (!mRequestedApnType.equals(Phone.APN_TYPE_DEFAULT)) { - phone.notifyDataConnection(Phone.REASON_APN_FAILED); - onEnableApn(apnTypeToId(mRequestedApnType), DISABLED); - } + phone.notifyDataConnection(Phone.REASON_APN_FAILED); + onEnableApn(apnTypeToId(mRequestedApnType), DISABLED); return; } @@ -1280,7 +1277,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { if (allApns.isEmpty()) { if (DBG) log("No APN found for carrier: " + operator); preferredApn = null; - notifyNoData(GsmDataConnection.FailCause.MISSING_UKNOWN_APN); + notifyNoData(GsmDataConnection.FailCause.MISSING_UNKNOWN_APN); } else { preferredApn = getPreferredApn(); Log.d(LOG_TAG, "Get PreferredAPN"); diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmMmiCode.java b/telephony/java/com/android/internal/telephony/gsm/GsmMmiCode.java index bcbd127497b2..aa16fa30633b 100644 --- a/telephony/java/com/android/internal/telephony/gsm/GsmMmiCode.java +++ b/telephony/java/com/android/internal/telephony/gsm/GsmMmiCode.java @@ -39,7 +39,7 @@ import java.util.regex.Matcher; * {@hide} * */ -public final class GsmMmiCode extends Handler implements MmiCode { +public final class GsmMmiCode extends Handler implements MmiCode { static final String LOG_TAG = "GSM"; //***** Constants @@ -51,7 +51,7 @@ public final class GsmMmiCode extends Handler implements MmiCode { static final String ACTION_REGISTER = "**"; static final String ACTION_ERASURE = "##"; - // Supp Service cocdes from TS 22.030 Annex B + // Supp Service codes from TS 22.030 Annex B //Called line presentation static final String SC_CLIP = "30"; @@ -154,7 +154,7 @@ public final class GsmMmiCode extends Handler implements MmiCode { /** * Some dial strings in GSM are defined to do non-call setup - * things, such as modify or query supplementry service settings (eg, call + * things, such as modify or query supplementary service settings (eg, call * forwarding). These are generally referred to as "MMI codes". * We look to see if the dial string contains a valid MMI code (potentially * with a dial string at the end as well) and return info here. @@ -457,12 +457,13 @@ public final class GsmMmiCode extends Handler implements MmiCode { && !PhoneNumberUtils.isEmergencyNumber(dialString) && (phone.isInCall() || !((dialString.length() == 2 && dialString.charAt(0) == '1') - /* While contrary to TS 22.030, there is strong precendence + /* While contrary to TS 22.030, there is strong precedence * for treating "0" and "00" as call setup strings. */ || dialString.equals("0") || dialString.equals("00")))); } + /** * @return true if the Service Code is PIN/PIN2/PUK/PUK2-related */ @@ -472,13 +473,12 @@ public final class GsmMmiCode extends Handler implements MmiCode { } /** - * *See TS 22.030 Annex B + * See TS 22.030 Annex B. * In temporary mode, to suppress CLIR for a single call, enter: - * " * 31 # <called number> SEND " + * " * 31 # [called number] SEND " * In temporary mode, to invoke CLIR for a single call enter: - * " # 31 # <called number> SEND " + * " # 31 # [called number] SEND " */ - boolean isTemporaryModeCLIR() { return sc != null && sc.equals(SC_CLIR) && dialingNumber != null @@ -779,7 +779,7 @@ public final class GsmMmiCode extends Handler implements MmiCode { // Note that unlike most everything else, the USSD complete // response does not complete this MMI code...we wait for // an unsolicited USSD "Notify" or "Request". - // The matching up of this is doene in GSMPhone. + // The matching up of this is done in GSMPhone. phone.mCM.sendUSSD(ussdMessage, obtainMessage(EVENT_USSD_COMPLETE, this)); @@ -832,8 +832,7 @@ public final class GsmMmiCode extends Handler implements MmiCode { if (ar.exception != null) { state = State.FAILED; - message = context.getText( - com.android.internal.R.string.mmiError); + message = getErrorMessage(ar); phone.onMMIDone(this); } @@ -852,6 +851,19 @@ public final class GsmMmiCode extends Handler implements MmiCode { } //***** Private instance methods + private CharSequence getErrorMessage(AsyncResult ar) { + + if (ar.exception instanceof CommandException) { + CommandException.Error err = ((CommandException)(ar.exception)).getCommandError(); + if (err == CommandException.Error.FDN_CHECK_FAILURE) { + Log.i(LOG_TAG, "FDN_CHECK_FAILURE"); + return context.getText(com.android.internal.R.string.mmiFdnError); + } + } + + return context.getText(com.android.internal.R.string.mmiError); + } + private CharSequence getScString() { if (sc != null) { if (isServiceCodeCallBarring(sc)) { @@ -904,6 +916,9 @@ public final class GsmMmiCode extends Handler implements MmiCode { sb.append("\n"); sb.append(context.getText( com.android.internal.R.string.needPuk2)); + } else if (err == CommandException.Error.FDN_CHECK_FAILURE) { + Log.i(LOG_TAG, "FDN_CHECK_FAILURE"); + sb.append(context.getText(com.android.internal.R.string.mmiFdnError)); } else { sb.append(context.getText( com.android.internal.R.string.mmiError)); @@ -953,7 +968,7 @@ public final class GsmMmiCode extends Handler implements MmiCode { if (ar.exception != null) { state = State.FAILED; - sb.append(context.getText(com.android.internal.R.string.mmiError)); + sb.append(getErrorMessage(ar)); } else { int clirArgs[]; @@ -1123,7 +1138,7 @@ public final class GsmMmiCode extends Handler implements MmiCode { if (ar.exception != null) { state = State.FAILED; - sb.append(context.getText(com.android.internal.R.string.mmiError)); + sb.append(getErrorMessage(ar)); } else { CallForwardInfo infos[]; @@ -1141,7 +1156,7 @@ public final class GsmMmiCode extends Handler implements MmiCode { // Each bit in the service class gets its own result line // The service classes may be split up over multiple - // CallForwardInfos. So, for each service classs, find out + // CallForwardInfos. So, for each service class, find out // which CallForwardInfo represents it and then build // the response text based on that @@ -1175,7 +1190,7 @@ public final class GsmMmiCode extends Handler implements MmiCode { if (ar.exception != null) { state = State.FAILED; - sb.append(context.getText(com.android.internal.R.string.mmiError)); + sb.append(getErrorMessage(ar)); } else { int[] ints = (int[])ar.result; diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java b/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java index 6ae316d0f08e..3079a64086c7 100644 --- a/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java +++ b/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java @@ -27,13 +27,12 @@ import android.telephony.ServiceState; import android.util.Config; import android.util.Log; -import com.android.internal.telephony.IccUtils; -import com.android.internal.telephony.SmsMessageBase.TextEncodingDetails; -import com.android.internal.telephony.gsm.SmsMessage; import com.android.internal.telephony.CommandsInterface; +import com.android.internal.telephony.IccUtils; import com.android.internal.telephony.SMSDispatcher; import com.android.internal.telephony.SmsHeader; import com.android.internal.telephony.SmsMessageBase; +import com.android.internal.telephony.SmsMessageBase.TextEncodingDetails; import java.util.ArrayList; import java.util.HashMap; @@ -94,16 +93,23 @@ final class GsmSMSDispatcher extends SMSDispatcher { SmsMessage sms = (SmsMessage) smsb; boolean handled = false; + if (sms.isTypeZero()) { + // As per 3GPP TS 23.040 9.2.3.9, Type Zero messages should not be + // Displayed/Stored/Notified. They should only be acknowledged. + Log.d(TAG, "Received short message type 0, Don't display or store it. Send Ack"); + return Intents.RESULT_SMS_HANDLED; + } + // Special case the message waiting indicator messages if (sms.isMWISetMessage()) { mGsmPhone.updateMessageWaitingIndicator(true); - handled |= sms.isMwiDontStore(); + handled = sms.isMwiDontStore(); if (Config.LOGD) { Log.d(TAG, "Received voice mail indicator set SMS shouldStore=" + !handled); } } else if (sms.isMWIClearMessage()) { mGsmPhone.updateMessageWaitingIndicator(false); - handled |= sms.isMwiDontStore(); + handled = sms.isMwiDontStore(); if (Config.LOGD) { Log.d(TAG, "Received voice mail indicator clear SMS shouldStore=" + !handled); } @@ -294,7 +300,7 @@ final class GsmSMSDispatcher extends SMSDispatcher { map.put("smsc", pdus.encodedScAddress); map.put("pdu", pdus.encodedMessage); - SmsTracker tracker = SmsTrackerFactory(map, sentIntent, deliveryIntent); + SmsTracker tracker = SmsTrackerFactory(map, sentIntent, deliveryIntent); sendSms(tracker); } } diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java index 50b8eba92701..90ecbd7c4029 100644 --- a/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java +++ b/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java @@ -40,7 +40,6 @@ import android.provider.Settings.SettingNotFoundException; import android.provider.Telephony.Intents; import android.telephony.ServiceState; import android.telephony.SignalStrength; -import android.telephony.TelephonyManager; import android.telephony.gsm.GsmCellLocation; import android.text.TextUtils; import android.util.Config; @@ -130,7 +129,7 @@ final class GsmServiceStateTracker extends ServiceStateTracker { */ private boolean mNeedToRegForSimLoaded; - /** Started the recheck process after finding gprs should registerd but not. */ + /** Started the recheck process after finding gprs should registered but not. */ private boolean mStartedGprsRegCheck = false; /** Already sent the event-log for no gprs register. */ @@ -415,7 +414,7 @@ final class GsmServiceStateTracker extends ServiceStateTracker { } // Release any temporary cell lock, which could have been - // aquired to allow a single-shot location update. + // acquired to allow a single-shot location update. disableSingleLocationUpdate(); break; @@ -500,9 +499,9 @@ final class GsmServiceStateTracker extends ServiceStateTracker { break; case EVENT_CHECK_REPORT_GPRS: - if (ss != null && !isGprsConsistant(gprsState, ss.getState())) { + if (ss != null && !isGprsConsistent(gprsState, ss.getState())) { - // Can't register data sevice while voice service is ok + // Can't register data service while voice service is ok // i.e. CREG is ok while CGREG is not // possible a network or baseband side error GsmCellLocation loc = ((GsmCellLocation)phone.getCellLocation()); @@ -651,6 +650,7 @@ final class GsmServiceStateTracker extends ServiceStateTracker { int lac = -1; int cid = -1; int regState = -1; + int psc = -1; if (states.length > 0) { try { regState = Integer.parseInt(states[0]); @@ -662,6 +662,11 @@ final class GsmServiceStateTracker extends ServiceStateTracker { cid = Integer.parseInt(states[2], 16); } } + if (states.length > 14) { + if (states[14] != null && states[14].length() > 0) { + psc = Integer.parseInt(states[14], 16); + } + } } catch (NumberFormatException ex) { Log.w(LOG_TAG, "error parsing RegistrationState: " + ex); } @@ -678,6 +683,7 @@ final class GsmServiceStateTracker extends ServiceStateTracker { // LAC and CID are -1 if not avail newCellLoc.setLacAndCid(lac, cid); + newCellLoc.setPsc(psc); break; case EVENT_POLL_STATE_GPRS: @@ -1027,7 +1033,7 @@ final class GsmServiceStateTracker extends ServiceStateTracker { phone.notifyLocationChanged(); } - if (! isGprsConsistant(gprsState, ss.getState())) { + if (! isGprsConsistent(gprsState, ss.getState())) { if (!mStartedGprsRegCheck && !mReportedGprsNoReg) { mStartedGprsRegCheck = true; @@ -1044,13 +1050,13 @@ final class GsmServiceStateTracker extends ServiceStateTracker { } /** - * Check if GPRS got registred while voice is registered + * Check if GPRS got registered while voice is registered. * * @param gprsState for GPRS registration state, i.e. CGREG in GSM * @param serviceState for voice registration state, i.e. CREG in GSM * @return false if device only register to voice but not gprs */ - private boolean isGprsConsistant (int gprsState, int serviceState) { + private boolean isGprsConsistent(int gprsState, int serviceState) { return !((serviceState == ServiceState.STATE_IN_SERVICE) && (gprsState != ServiceState.STATE_IN_SERVICE)); } @@ -1105,13 +1111,13 @@ final class GsmServiceStateTracker extends ServiceStateTracker { long nextTime; - // TODO Done't poll signal strength if screen is off + // TODO Don't poll signal strength if screen is off sendMessageDelayed(msg, POLL_PERIOD_MILLIS); } /** - * send signal-strength-changed notification if changed - * Called both for solicited and unsolicited signal stength updates + * Send signal-strength-changed notification if changed. + * Called both for solicited and unsolicited signal strength updates. */ private void onSignalStrengthResult(AsyncResult ar) { SignalStrength oldSignalStrength = mSignalStrength; @@ -1332,7 +1338,7 @@ final class GsmServiceStateTracker extends ServiceStateTracker { /** * @return true if phone is camping on a technology (eg UMTS) - * that could support voice and data simultaniously. + * that could support voice and data simultaneously. */ boolean isConcurrentVoiceAndData() { return (networkType >= DATA_ACCESS_UMTS); diff --git a/telephony/java/com/android/internal/telephony/gsm/SimSmsInterfaceManager.java b/telephony/java/com/android/internal/telephony/gsm/SimSmsInterfaceManager.java index 2028ca4a78a8..67ecc77a320a 100644 --- a/telephony/java/com/android/internal/telephony/gsm/SimSmsInterfaceManager.java +++ b/telephony/java/com/android/internal/telephony/gsm/SimSmsInterfaceManager.java @@ -25,6 +25,7 @@ import android.util.Log; import com.android.internal.telephony.IccConstants; import com.android.internal.telephony.IccSmsInterfaceManager; import com.android.internal.telephony.IccUtils; +import com.android.internal.telephony.SMSDispatcher; import com.android.internal.telephony.SmsRawData; import java.util.ArrayList; @@ -78,9 +79,9 @@ public class SimSmsInterfaceManager extends IccSmsInterfaceManager { } }; - public SimSmsInterfaceManager(GSMPhone phone) { + public SimSmsInterfaceManager(GSMPhone phone, SMSDispatcher dispatcher) { super(phone); - mDispatcher = new GsmSMSDispatcher(phone); + mDispatcher = dispatcher; } public void dispose() { diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java index d627bafbafe9..50dd402f9bbc 100644 --- a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java +++ b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java @@ -111,6 +111,14 @@ public class SmsMessage extends SmsMessageBase{ } /** + * 3GPP TS 23.040 9.2.3.9 specifies that Type Zero messages are indicated + * by TP_PID field set to value 0x40 + */ + public boolean isTypeZero() { + return (protocolIdentifier == 0x40); + } + + /** * TS 27.005 3.4.1 lines[0] and lines[1] are the two lines read from the * +CMT unsolicited response (PDU mode, of course) * +CMT: [<alpha>],<length><CR><LF><pdu> @@ -792,9 +800,10 @@ public class SmsMessage extends SmsMessageBase{ int septets = GsmAlphabet.countGsmSeptets(msgBody, !use7bitOnly); ted.codeUnitCount = septets; if (septets > MAX_USER_DATA_SEPTETS) { - ted.msgCount = (septets / MAX_USER_DATA_SEPTETS_WITH_HEADER) + 1; - ted.codeUnitsRemaining = MAX_USER_DATA_SEPTETS_WITH_HEADER - - (septets % MAX_USER_DATA_SEPTETS_WITH_HEADER); + ted.msgCount = (septets + (MAX_USER_DATA_SEPTETS_WITH_HEADER - 1)) / + MAX_USER_DATA_SEPTETS_WITH_HEADER; + ted.codeUnitsRemaining = (ted.msgCount * + MAX_USER_DATA_SEPTETS_WITH_HEADER) - septets; } else { ted.msgCount = 1; ted.codeUnitsRemaining = MAX_USER_DATA_SEPTETS - septets; @@ -804,9 +813,10 @@ public class SmsMessage extends SmsMessageBase{ int octets = msgBody.length() * 2; ted.codeUnitCount = msgBody.length(); if (octets > MAX_USER_DATA_BYTES) { - ted.msgCount = (octets / MAX_USER_DATA_BYTES_WITH_HEADER) + 1; - ted.codeUnitsRemaining = (MAX_USER_DATA_BYTES_WITH_HEADER - - (octets % MAX_USER_DATA_BYTES_WITH_HEADER))/2; + ted.msgCount = (octets + (MAX_USER_DATA_BYTES_WITH_HEADER - 1)) / + MAX_USER_DATA_BYTES_WITH_HEADER; + ted.codeUnitsRemaining = ((ted.msgCount * + MAX_USER_DATA_BYTES_WITH_HEADER) - octets) / 2; } else { ted.msgCount = 1; ted.codeUnitsRemaining = (MAX_USER_DATA_BYTES - octets)/2; diff --git a/telephony/java/com/android/internal/telephony/sip/CallFailCause.java b/telephony/java/com/android/internal/telephony/sip/CallFailCause.java new file mode 100644 index 000000000000..58fb408b6161 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/sip/CallFailCause.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2010 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.sip; + +/** + * Call fail causes from TS 24.008 . + * These are mostly the cause codes we need to distinguish for the UI. + * See 22.001 Annex F.4 for mapping of cause codes to local tones. + * + * {@hide} + * + */ +public interface CallFailCause { + static final int NORMAL_CLEARING = 16; + // Busy Tone + static final int USER_BUSY = 17; + + // No Tone + static final int NUMBER_CHANGED = 22; + static final int STATUS_ENQUIRY = 30; + static final int NORMAL_UNSPECIFIED = 31; + + // Congestion Tone + static final int NO_CIRCUIT_AVAIL = 34; + static final int TEMPORARY_FAILURE = 41; + static final int SWITCHING_CONGESTION = 42; + static final int CHANNEL_NOT_AVAIL = 44; + static final int QOS_NOT_AVAIL = 49; + static final int BEARER_NOT_AVAIL = 58; + + // others + static final int ACM_LIMIT_EXCEEDED = 68; + static final int CALL_BARRED = 240; + static final int FDN_BLOCKED = 241; + static final int ERROR_UNSPECIFIED = 0xffff; +} diff --git a/telephony/java/com/android/internal/telephony/sip/CallProxy.java b/telephony/java/com/android/internal/telephony/sip/CallProxy.java new file mode 100644 index 000000000000..fad9663cf2c3 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/sip/CallProxy.java @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2010 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.sip; + +import com.android.internal.telephony.*; +import java.util.List; + +// TODO: remove this class after integrating with CallManager +class CallProxy extends Call { + private Call mTarget; + + void setTarget(Call target) { + mTarget = target; + } + + @Override + public List<Connection> getConnections() { + return mTarget.getConnections(); + } + + @Override + public Phone getPhone() { + return mTarget.getPhone(); + } + + @Override + public boolean isMultiparty() { + return mTarget.isMultiparty(); + } + + @Override + public void hangup() throws CallStateException { + mTarget.hangup(); + } + + @Override + public boolean hasConnection(Connection c) { + return mTarget.hasConnection(c); + } + + @Override + public boolean hasConnections() { + return mTarget.hasConnections(); + } + + @Override + public State getState() { + return mTarget.getState(); + } + + @Override + public boolean isIdle() { + return mTarget.isIdle(); + } + + @Override + public Connection getEarliestConnection() { + return mTarget.getEarliestConnection(); + } + + @Override + public long getEarliestCreateTime() { + return mTarget.getEarliestCreateTime(); + } + + @Override + public long getEarliestConnectTime() { + return mTarget.getEarliestConnectTime(); + } + + @Override + public boolean isDialingOrAlerting() { + return mTarget.isDialingOrAlerting(); + } + + @Override + public boolean isRinging() { + return mTarget.isRinging(); + } + + @Override + public Connection getLatestConnection() { + return mTarget.getLatestConnection(); + } + + @Override + public boolean isGeneric() { + return mTarget.isGeneric(); + } + + @Override + public void setGeneric(boolean generic) { + mTarget.setGeneric(generic); + } + + @Override + public void hangupIfAlive() { + mTarget.hangupIfAlive(); + } +} diff --git a/telephony/java/com/android/internal/telephony/sip/SipCallBase.java b/telephony/java/com/android/internal/telephony/sip/SipCallBase.java new file mode 100644 index 000000000000..7e407b2122c4 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/sip/SipCallBase.java @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2010 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.sip; + +import com.android.internal.telephony.Call; +import com.android.internal.telephony.CallStateException; +import com.android.internal.telephony.Connection; +import com.android.internal.telephony.DriverCall; +import com.android.internal.telephony.Phone; + +import android.net.sip.SipManager; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +abstract class SipCallBase extends Call { + private static final int MAX_CONNECTIONS_PER_CALL = 5; + + protected List<Connection> connections = new ArrayList<Connection>(); + + protected abstract void setState(State newState); + + public void dispose() { + } + + public List<Connection> getConnections() { + // FIXME should return Collections.unmodifiableList(); + return connections; + } + + public boolean isMultiparty() { + return connections.size() > 1; + } + + public String toString() { + return state.toString(); + } + + /** + * Called by SipConnection when it has disconnected + */ + void connectionDisconnected(Connection conn) { + if (state != State.DISCONNECTED) { + /* If only disconnected connections remain, we are disconnected*/ + + boolean hasOnlyDisconnectedConnections = true; + + for (int i = 0, s = connections.size() ; i < s; i ++) { + if (connections.get(i).getState() + != State.DISCONNECTED + ) { + hasOnlyDisconnectedConnections = false; + break; + } + } + + if (hasOnlyDisconnectedConnections) { + state = State.DISCONNECTED; + } + } + } + + + /*package*/ void detach(Connection conn) { + connections.remove(conn); + + if (connections.size() == 0) { + state = State.IDLE; + } + } + + /** + * @return true if there's no space in this call for additional + * connections to be added via "conference" + */ + /*package*/ boolean isFull() { + return connections.size() == MAX_CONNECTIONS_PER_CALL; + } + + void clearDisconnected() { + for (Iterator<Connection> it = connections.iterator(); it.hasNext(); ) { + Connection c = it.next(); + if (c.getState() == State.DISCONNECTED) it.remove(); + } + + if (connections.isEmpty()) setState(State.IDLE); + } +} diff --git a/telephony/java/com/android/internal/telephony/sip/SipCommandInterface.java b/telephony/java/com/android/internal/telephony/sip/SipCommandInterface.java new file mode 100644 index 000000000000..33c89f862e51 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/sip/SipCommandInterface.java @@ -0,0 +1,373 @@ +/* + * Copyright (C) 2010 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.sip; + +import android.content.Context; +import android.os.Handler; +import android.os.Message; + +import com.android.internal.telephony.BaseCommands; +import com.android.internal.telephony.CommandsInterface; +import com.android.internal.telephony.UUSInfo; +import com.android.internal.telephony.gsm.SmsBroadcastConfigInfo; + +/** + * SIP doesn't need CommandsInterface. The class does nothing but made to work + * with PhoneBase's constructor. + */ +class SipCommandInterface extends BaseCommands implements CommandsInterface { + SipCommandInterface(Context context) { + super(context); + } + + @Override public void setOnNITZTime(Handler h, int what, Object obj) { + } + + public void getIccCardStatus(Message result) { + } + + public void supplyIccPin(String pin, Message result) { + } + + public void supplyIccPuk(String puk, String newPin, Message result) { + } + + public void supplyIccPin2(String pin, Message result) { + } + + public void supplyIccPuk2(String puk, String newPin2, Message result) { + } + + public void changeIccPin(String oldPin, String newPin, Message result) { + } + + public void changeIccPin2(String oldPin2, String newPin2, Message result) { + } + + public void changeBarringPassword(String facility, String oldPwd, + String newPwd, Message result) { + } + + public void supplyNetworkDepersonalization(String netpin, Message result) { + } + + public void getCurrentCalls(Message result) { + } + + @Deprecated public void getPDPContextList(Message result) { + } + + public void getDataCallList(Message result) { + } + + public void dial(String address, int clirMode, Message result) { + } + + public void dial(String address, int clirMode, UUSInfo uusInfo, + Message result) { + } + + public void getIMSI(Message result) { + } + + public void getIMEI(Message result) { + } + + public void getIMEISV(Message result) { + } + + + public void hangupConnection (int gsmIndex, Message result) { + } + + public void hangupWaitingOrBackground (Message result) { + } + + public void hangupForegroundResumeBackground (Message result) { + } + + public void switchWaitingOrHoldingAndActive (Message result) { + } + + public void conference (Message result) { + } + + + public void setPreferredVoicePrivacy(boolean enable, Message result) { + } + + public void getPreferredVoicePrivacy(Message result) { + } + + public void separateConnection (int gsmIndex, Message result) { + } + + public void acceptCall (Message result) { + } + + public void rejectCall (Message result) { + } + + public void explicitCallTransfer (Message result) { + } + + public void getLastCallFailCause (Message result) { + } + + /** @deprecated */ + public void getLastPdpFailCause (Message result) { + } + + public void getLastDataCallFailCause (Message result) { + } + + public void setMute (boolean enableMute, Message response) { + } + + public void getMute (Message response) { + } + + public void getSignalStrength (Message result) { + } + + public void getRegistrationState (Message result) { + } + + public void getGPRSRegistrationState (Message result) { + } + + public void getOperator(Message result) { + } + + public void sendDtmf(char c, Message result) { + } + + public void startDtmf(char c, Message result) { + } + + public void stopDtmf(Message result) { + } + + public void sendBurstDtmf(String dtmfString, int on, int off, + Message result) { + } + + public void sendSMS (String smscPDU, String pdu, Message result) { + } + + public void sendCdmaSms(byte[] pdu, Message result) { + } + + public void deleteSmsOnSim(int index, Message response) { + } + + public void deleteSmsOnRuim(int index, Message response) { + } + + public void writeSmsToSim(int status, String smsc, String pdu, Message response) { + } + + public void writeSmsToRuim(int status, String pdu, Message response) { + } + + public void setupDefaultPDP(String apn, String user, String password, + Message result) { + } + + public void deactivateDefaultPDP(int cid, Message result) { + } + + public void setupDataCall(String radioTechnology, String profile, + String apn, String user, String password, String authType, + Message result) { + } + + public void deactivateDataCall(int cid, Message result) { + } + + public void setRadioPower(boolean on, Message result) { + } + + public void setSuppServiceNotifications(boolean enable, Message result) { + } + + public void acknowledgeLastIncomingGsmSms(boolean success, int cause, + Message result) { + } + + public void acknowledgeLastIncomingCdmaSms(boolean success, int cause, + Message result) { + } + + + public void iccIO (int command, int fileid, String path, int p1, int p2, + int p3, String data, String pin2, Message result) { + } + + public void getCLIR(Message result) { + } + + public void setCLIR(int clirMode, Message result) { + } + + public void queryCallWaiting(int serviceClass, Message response) { + } + + public void setCallWaiting(boolean enable, int serviceClass, + Message response) { + } + + public void setNetworkSelectionModeAutomatic(Message response) { + } + + public void setNetworkSelectionModeManual( + String operatorNumeric, Message response) { + } + + public void getNetworkSelectionMode(Message response) { + } + + public void getAvailableNetworks(Message response) { + } + + public void setCallForward(int action, int cfReason, int serviceClass, + String number, int timeSeconds, Message response) { + } + + public void queryCallForwardStatus(int cfReason, int serviceClass, + String number, Message response) { + } + + public void queryCLIP(Message response) { + } + + public void getBasebandVersion (Message response) { + } + + public void queryFacilityLock (String facility, String password, + int serviceClass, Message response) { + } + + public void setFacilityLock (String facility, boolean lockState, + String password, int serviceClass, Message response) { + } + + public void sendUSSD (String ussdString, Message response) { + } + + public void cancelPendingUssd (Message response) { + } + + public void resetRadio(Message result) { + } + + public void invokeOemRilRequestRaw(byte[] data, Message response) { + } + + public void invokeOemRilRequestStrings(String[] strings, Message response) { + } + + public void setBandMode (int bandMode, Message response) { + } + + public void queryAvailableBandMode (Message response) { + } + + public void sendTerminalResponse(String contents, Message response) { + } + + public void sendEnvelope(String contents, Message response) { + } + + public void handleCallSetupRequestFromSim( + boolean accept, Message response) { + } + + public void setPreferredNetworkType(int networkType , Message response) { + } + + public void getPreferredNetworkType(Message response) { + } + + public void getNeighboringCids(Message response) { + } + + public void setLocationUpdates(boolean enable, Message response) { + } + + public void getSmscAddress(Message result) { + } + + public void setSmscAddress(String address, Message result) { + } + + public void reportSmsMemoryStatus(boolean available, Message result) { + } + + public void reportStkServiceIsRunning(Message result) { + } + + public void getGsmBroadcastConfig(Message response) { + } + + public void setGsmBroadcastConfig(SmsBroadcastConfigInfo[] config, Message response) { + } + + public void setGsmBroadcastActivation(boolean activate, Message response) { + } + + + // ***** Methods for CDMA support + public void getDeviceIdentity(Message response) { + } + + public void getCDMASubscription(Message response) { + } + + public void setPhoneType(int phoneType) { //Set by CDMAPhone and GSMPhone constructor + } + + public void queryCdmaRoamingPreference(Message response) { + } + + public void setCdmaRoamingPreference(int cdmaRoamingType, Message response) { + } + + public void setCdmaSubscription(int cdmaSubscription , Message response) { + } + + public void queryTTYMode(Message response) { + } + + public void setTTYMode(int ttyMode, Message response) { + } + + public void sendCDMAFeatureCode(String FeatureCode, Message response) { + } + + public void getCdmaBroadcastConfig(Message response) { + } + + public void setCdmaBroadcastConfig(int[] configValuesArray, Message response) { + } + + public void setCdmaBroadcastActivation(boolean activate, Message response) { + } + + public void exitEmergencyCallbackMode(Message response) { + } +} diff --git a/telephony/java/com/android/internal/telephony/sip/SipConnectionBase.java b/telephony/java/com/android/internal/telephony/sip/SipConnectionBase.java new file mode 100644 index 000000000000..6c989b4a0ef4 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/sip/SipConnectionBase.java @@ -0,0 +1,248 @@ +/* + * Copyright (C) 2010 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.sip; + +import android.content.Context; +import android.net.sip.SipAudioCall; +import android.os.Message; +import android.os.Registrant; +import android.os.SystemClock; +import android.util.Log; +import android.telephony.PhoneNumberUtils; +import android.telephony.ServiceState; + +import com.android.internal.telephony.*; + +abstract class SipConnectionBase extends Connection { + //***** Event Constants + private static final int EVENT_DTMF_DONE = 1; + private static final int EVENT_PAUSE_DONE = 2; + private static final int EVENT_NEXT_POST_DIAL = 3; + private static final int EVENT_WAKE_LOCK_TIMEOUT = 4; + + //***** Constants + private static final int PAUSE_DELAY_FIRST_MILLIS = 100; + private static final int PAUSE_DELAY_MILLIS = 3 * 1000; + private static final int WAKE_LOCK_TIMEOUT_MILLIS = 60*1000; + + private static final String LOG_TAG = "SIP_CONN"; + + private SipAudioCall mSipAudioCall; + + private String dialString; // outgoing calls only + private String postDialString; // outgoing calls only + private int nextPostDialChar; // index into postDialString + private boolean isIncoming; + private boolean disconnected; + + int index; // index in SipCallTracker.connections[], -1 if unassigned + // The Sip index is 1 + this + + /* + * These time/timespan values are based on System.currentTimeMillis(), + * i.e., "wall clock" time. + */ + private long createTime; + private long connectTime; + private long disconnectTime; + + /* + * These time/timespan values are based on SystemClock.elapsedRealTime(), + * i.e., time since boot. They are appropriate for comparison and + * calculating deltas. + */ + private long connectTimeReal; + private long duration; + private long holdingStartTime; // The time when the Connection last transitioned + // into HOLDING + + private DisconnectCause mCause = DisconnectCause.NOT_DISCONNECTED; + private PostDialState postDialState = PostDialState.NOT_STARTED; + + SipConnectionBase(String calleeSipUri) { + dialString = calleeSipUri; + + postDialString = PhoneNumberUtils.extractPostDialPortion(dialString); + + isIncoming = false; + createTime = System.currentTimeMillis(); + } + + protected void setState(Call.State state) { + switch (state) { + case ACTIVE: + if (connectTime == 0) { + connectTimeReal = SystemClock.elapsedRealtime(); + connectTime = System.currentTimeMillis(); + } + break; + case DISCONNECTED: + duration = SystemClock.elapsedRealtime() - connectTimeReal; + disconnectTime = System.currentTimeMillis(); + break; + case HOLDING: + holdingStartTime = SystemClock.elapsedRealtime(); + break; + } + } + + @Override + public long getCreateTime() { + return createTime; + } + + @Override + public long getConnectTime() { + return connectTime; + } + + @Override + public long getDisconnectTime() { + return disconnectTime; + } + + @Override + public long getDurationMillis() { + if (connectTimeReal == 0) { + return 0; + } else if (duration == 0) { + return SystemClock.elapsedRealtime() - connectTimeReal; + } else { + return duration; + } + } + + @Override + public long getHoldDurationMillis() { + if (getState() != Call.State.HOLDING) { + // If not holding, return 0 + return 0; + } else { + return SystemClock.elapsedRealtime() - holdingStartTime; + } + } + + @Override + public DisconnectCause getDisconnectCause() { + return mCause; + } + + void setDisconnectCause(DisconnectCause cause) { + mCause = cause; + } + + @Override + public PostDialState getPostDialState() { + return postDialState; + } + + @Override + public void proceedAfterWaitChar() { + // TODO + } + + @Override + public void proceedAfterWildChar(String str) { + // TODO + } + + @Override + public void cancelPostDial() { + // TODO + } + + protected abstract Phone getPhone(); + + DisconnectCause disconnectCauseFromCode(int causeCode) { + /** + * See 22.001 Annex F.4 for mapping of cause codes + * to local tones + */ + + switch (causeCode) { + case CallFailCause.USER_BUSY: + return DisconnectCause.BUSY; + + case CallFailCause.NO_CIRCUIT_AVAIL: + case CallFailCause.TEMPORARY_FAILURE: + case CallFailCause.SWITCHING_CONGESTION: + case CallFailCause.CHANNEL_NOT_AVAIL: + case CallFailCause.QOS_NOT_AVAIL: + case CallFailCause.BEARER_NOT_AVAIL: + return DisconnectCause.CONGESTION; + + case CallFailCause.ACM_LIMIT_EXCEEDED: + return DisconnectCause.LIMIT_EXCEEDED; + + case CallFailCause.CALL_BARRED: + return DisconnectCause.CALL_BARRED; + + case CallFailCause.FDN_BLOCKED: + return DisconnectCause.FDN_BLOCKED; + + case CallFailCause.ERROR_UNSPECIFIED: + case CallFailCause.NORMAL_CLEARING: + default: + Phone phone = getPhone(); + int serviceState = phone.getServiceState().getState(); + if (serviceState == ServiceState.STATE_POWER_OFF) { + return DisconnectCause.POWER_OFF; + } else if (serviceState == ServiceState.STATE_OUT_OF_SERVICE + || serviceState == ServiceState.STATE_EMERGENCY_ONLY ) { + return DisconnectCause.OUT_OF_SERVICE; + } else if (causeCode == CallFailCause.ERROR_UNSPECIFIED) { + return DisconnectCause.ERROR_UNSPECIFIED; + } else if (causeCode == CallFailCause.NORMAL_CLEARING) { + return DisconnectCause.NORMAL; + } else { + // If nothing else matches, report unknown call drop reason + // to app, not NORMAL call end. + return DisconnectCause.ERROR_UNSPECIFIED; + } + } + } + + @Override + public String getRemainingPostDialString() { + if (postDialState == PostDialState.CANCELLED + || postDialState == PostDialState.COMPLETE + || postDialString == null + || postDialString.length() <= nextPostDialChar) { + return ""; + } + + return postDialString.substring(nextPostDialChar); + } + + private void log(String msg) { + Log.d(LOG_TAG, "[SipConn] " + msg); + } + + @Override + public int getNumberPresentation() { + // TODO: add PRESENTATION_URL + return Connection.PRESENTATION_ALLOWED; + } + + /* + @Override + public UUSInfo getUUSInfo() { + // FIXME: what's this for SIP? + return null; + } + */ +} diff --git a/telephony/java/com/android/internal/telephony/sip/SipPhone.java b/telephony/java/com/android/internal/telephony/sip/SipPhone.java new file mode 100755 index 000000000000..943f21c9e546 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/sip/SipPhone.java @@ -0,0 +1,846 @@ +/* + * Copyright (C) 2010 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.sip; + +import android.content.ContentValues; +import android.content.Context; +import android.content.SharedPreferences; +import android.net.Uri; +import android.net.rtp.AudioGroup; +import android.net.rtp.AudioStream; +import android.net.sip.SipAudioCall; +import android.net.sip.SipErrorCode; +import android.net.sip.SipException; +import android.net.sip.SipManager; +import android.net.sip.SipProfile; +import android.net.sip.SipSessionState; +import android.os.AsyncResult; +import android.os.Handler; +import android.os.Looper; +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.telephony.CellLocation; +import android.telephony.PhoneNumberUtils; +import android.telephony.ServiceState; +import android.telephony.SignalStrength; +import android.text.TextUtils; +import android.util.Log; + +import com.android.internal.telephony.Call; +import com.android.internal.telephony.CallerInfo; +import com.android.internal.telephony.CallStateException; +import com.android.internal.telephony.CommandsInterface; +import com.android.internal.telephony.Connection; +import com.android.internal.telephony.DataConnection; +import com.android.internal.telephony.IccCard; +import com.android.internal.telephony.IccFileHandler; +import com.android.internal.telephony.IccPhoneBookInterfaceManager; +import com.android.internal.telephony.IccSmsInterfaceManager; +import com.android.internal.telephony.MmiCode; +import com.android.internal.telephony.Phone; +import com.android.internal.telephony.PhoneBase; +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 java.io.IOException; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.List; + +/** + * {@hide} + */ +public class SipPhone extends SipPhoneBase { + private static final String LOG_TAG = "SipPhone"; + private static final boolean LOCAL_DEBUG = true; + + // A call that is ringing or (call) waiting + private SipCall ringingCall = new SipCall(); + private SipCall foregroundCall = new SipCall(); + private SipCall backgroundCall = new SipCall(); + + private SipManager mSipManager; + private SipProfile mProfile; + + SipPhone (Context context, PhoneNotifier notifier, SipProfile profile) { + super(context, notifier); + + Log.v(LOG_TAG, " +++++++++++++++++++++ new SipPhone: " + profile.getUriString()); + ringingCall = new SipCall(); + foregroundCall = new SipCall(); + backgroundCall = new SipCall(); + mProfile = profile; + mSipManager = SipManager.getInstance(context); + + // FIXME: what's this for SIP? + //Change the system property + //SystemProperties.set(TelephonyProperties.CURRENT_ACTIVE_PHONE, + // new Integer(Phone.PHONE_TYPE_GSM).toString()); + } + + public String getPhoneName() { + return "SIP:" + getUriString(mProfile); + } + + public String getSipUri() { + return mProfile.getUriString(); + } + + public boolean equals(SipPhone phone) { + return getSipUri().equals(phone.getSipUri()); + } + + public boolean canTake(Object incomingCall) { + synchronized (SipPhone.class) { + if (!(incomingCall instanceof SipAudioCall)) return false; + if (ringingCall.getState().isAlive()) return false; + + // FIXME: is it true that we cannot take any incoming call if + // both foreground and background are active + if (foregroundCall.getState().isAlive() + && backgroundCall.getState().isAlive()) { + return false; + } + + SipAudioCall sipAudioCall = (SipAudioCall) incomingCall; + Log.v(LOG_TAG, " ++++++ taking call from: " + + sipAudioCall.getPeerProfile().getUriString()); + String localUri = sipAudioCall.getLocalProfile().getUriString(); + if (localUri.equals(mProfile.getUriString())) { + boolean makeCallWait = foregroundCall.getState().isAlive(); + ringingCall.initIncomingCall(sipAudioCall, makeCallWait); + return true; + } + return false; + } + } + + public void acceptCall() throws CallStateException { + synchronized (SipPhone.class) { + // FIXME if SWITCH fails, should retry with ANSWER + // in case the active/holding call disappeared and this + // is no longer call waiting + + if (ringingCall.getState() == Call.State.INCOMING) { + Log.v(LOG_TAG, "acceptCall"); + // Always unmute when answering a new call + setMute(false); + // make ringingCall foreground + foregroundCall.switchWith(ringingCall); + foregroundCall.acceptCall(); + } else if (ringingCall.getState() == Call.State.WAITING) { + setMute(false); + switchHoldingAndActive(); + // make ringingCall foreground + foregroundCall.switchWith(ringingCall); + foregroundCall.acceptCall(); + } else { + throw new CallStateException("phone not ringing"); + } + } + } + + public void rejectCall() throws CallStateException { + synchronized (SipPhone.class) { + if (ringingCall.getState().isRinging()) { + Log.v(LOG_TAG, "rejectCall"); + ringingCall.rejectCall(); + } else { + throw new CallStateException("phone not ringing"); + } + } + } + + public Connection dial(String dialString, UUSInfo uusinfo) throws CallStateException { + return dial(dialString); + } + + public Connection dial(String dialString) throws CallStateException { + synchronized (SipPhone.class) { + return dialInternal(dialString); + } + } + + private Connection dialInternal(String dialString) + throws CallStateException { + // TODO: parse SIP URL? + // Need to make sure dialString gets parsed properly + //String newDialString = PhoneNumberUtils.stripSeparators(dialString); + //return mCT.dial(newDialString); + clearDisconnected(); + + if (!canDial()) { + throw new CallStateException("cannot dial in current state"); + } + if (foregroundCall.getState() == SipCall.State.ACTIVE) { + switchHoldingAndActive(); + } + if (foregroundCall.getState() != SipCall.State.IDLE) { + //we should have failed in !canDial() above before we get here + throw new CallStateException("cannot dial in current state"); + } + + setMute(false); + //cm.dial(pendingMO.address, clirMode, obtainCompleteMessage()); + try { + Connection c = foregroundCall.dial(dialString); + return c; + } catch (SipException e) { + Log.e(LOG_TAG, "dial()", e); + throw new CallStateException("dial error: " + e); + } + } + + public void switchHoldingAndActive() throws CallStateException { + Log.v(LOG_TAG, " ~~~~~~ switch fg and bg"); + synchronized (SipPhone.class) { + foregroundCall.switchWith(backgroundCall); + if (backgroundCall.getState().isAlive()) backgroundCall.hold(); + if (foregroundCall.getState().isAlive()) foregroundCall.unhold(); + } + } + + public boolean canConference() { + return true; + } + + public void conference() throws CallStateException { + synchronized (SipPhone.class) { + if ((foregroundCall.getState() != SipCall.State.ACTIVE) + || (foregroundCall.getState() != SipCall.State.ACTIVE)) { + throw new CallStateException("wrong state to merge calls: fg=" + + foregroundCall.getState() + ", bg=" + + backgroundCall.getState()); + } + foregroundCall.merge(backgroundCall); + } + } + + public void conference(Call that) throws CallStateException { + synchronized (SipPhone.class) { + if (!(that instanceof SipCall)) { + throw new CallStateException("expect " + SipCall.class + + ", cannot merge with " + that.getClass()); + } + foregroundCall.merge((SipCall) that); + } + } + + public boolean canTransfer() { + return false; + } + + public void explicitCallTransfer() throws CallStateException { + //mCT.explicitCallTransfer(); + } + + public void clearDisconnected() { + synchronized (SipPhone.class) { + ringingCall.clearDisconnected(); + foregroundCall.clearDisconnected(); + backgroundCall.clearDisconnected(); + + updatePhoneState(); + notifyPreciseCallStateChanged(); + } + } + + public void sendDtmf(char c) { + if (!PhoneNumberUtils.is12Key(c)) { + Log.e(LOG_TAG, + "sendDtmf called with invalid character '" + c + "'"); + } else if (foregroundCall.getState().isAlive()) { + synchronized (SipPhone.class) { + foregroundCall.sendDtmf(c); + } + } + } + + public void startDtmf(char c) { + if (!PhoneNumberUtils.is12Key(c)) { + Log.e(LOG_TAG, + "startDtmf called with invalid character '" + c + "'"); + } else { + sendDtmf(c); + } + } + + public void stopDtmf() { + // no op + } + + public void sendBurstDtmf(String dtmfString) { + Log.e(LOG_TAG, "[SipPhone] sendBurstDtmf() is a CDMA method"); + } + + public void getOutgoingCallerIdDisplay(Message onComplete) { + // FIXME: what to reply? + AsyncResult.forMessage(onComplete, null, null); + onComplete.sendToTarget(); + } + + public void setOutgoingCallerIdDisplay(int commandInterfaceCLIRMode, + Message onComplete) { + // FIXME: what's this for SIP? + AsyncResult.forMessage(onComplete, null, null); + onComplete.sendToTarget(); + } + + public void getCallWaiting(Message onComplete) { + // FIXME: what to reply? + AsyncResult.forMessage(onComplete, null, null); + onComplete.sendToTarget(); + } + + public void setCallWaiting(boolean enable, Message onComplete) { + // FIXME: what to reply? + Log.e(LOG_TAG, "call waiting not supported"); + } + + public void setMute(boolean muted) { + synchronized (SipPhone.class) { + foregroundCall.setMute(muted); + } + } + + public boolean getMute() { + return foregroundCall.getMute(); + } + + public Call getForegroundCall() { + return foregroundCall; + } + + public Call getBackgroundCall() { + return backgroundCall; + } + + public Call getRingingCall() { + return ringingCall; + } + + public ServiceState getServiceState() { + // FIXME: we may need to provide this when data connectivity is lost + // or when server is down + return super.getServiceState(); + } + + private String getUriString(SipProfile p) { + // SipProfile.getUriString() may contain "SIP:" and port + return p.getUserName() + "@" + getSipDomain(p); + } + + private String getSipDomain(SipProfile p) { + String domain = p.getSipDomain(); + // TODO: move this to SipProfile + if (domain.endsWith(":5060")) { + return domain.substring(0, domain.length() - 5); + } else { + return domain; + } + } + + private class SipCall extends SipCallBase { + void switchWith(SipCall that) { + synchronized (SipPhone.class) { + SipCall tmp = new SipCall(); + tmp.takeOver(this); + this.takeOver(that); + that.takeOver(tmp); + } + } + + private void takeOver(SipCall that) { + connections = that.connections; + state = that.state; + for (Connection c : connections) { + ((SipConnection) c).changeOwner(this); + } + } + + @Override + public Phone getPhone() { + return SipPhone.this; + } + + @Override + public List<Connection> getConnections() { + synchronized (SipPhone.class) { + // FIXME should return Collections.unmodifiableList(); + return connections; + } + } + + private CallerInfo getCallerInfo(String number) { + CallerInfo info = CallerInfo.getCallerInfo(mContext, number); + if ((info == null) || (info.name == null)) return null; + Log.v(LOG_TAG, "++******++ got info from contact:"); + Log.v(LOG_TAG, " name: " + info.name); + Log.v(LOG_TAG, " numb: " + info.phoneNumber); + Log.v(LOG_TAG, " pres: " + info.numberPresentation); + return info; + } + + Connection dial(String calleeSipUri) throws SipException { + CallerInfo info = getCallerInfo(calleeSipUri); + if (!calleeSipUri.contains("@")) { + calleeSipUri += "@" + getSipDomain(mProfile); + if (info != null) info.phoneNumber = calleeSipUri; + } + try { + SipProfile callee = + new SipProfile.Builder(calleeSipUri).build(); + SipConnection c = new SipConnection(this, callee, info); + connections.add(c); + c.dial(); + setState(Call.State.DIALING); + return c; + } catch (ParseException e) { + // TODO: notify someone + throw new SipException("dial", e); + } + } + + @Override + public void hangup() throws CallStateException { + synchronized (SipPhone.class) { + Log.v(LOG_TAG, "hang up call: " + getState() + ": " + this + + " on phone " + getPhone()); + CallStateException excp = null; + for (Connection c : connections) { + try { + c.hangup(); + } catch (CallStateException e) { + excp = e; + } + } + if (excp != null) throw excp; + setState(State.DISCONNECTING); + } + } + + void initIncomingCall(SipAudioCall sipAudioCall, boolean makeCallWait) { + SipProfile callee = sipAudioCall.getPeerProfile(); + CallerInfo info = getCallerInfo(getUriString(callee)); + if (info == null) info = getCallerInfo(callee.getUserName()); + if (info == null) info = getCallerInfo(callee.getDisplayName()); + SipConnection c = new SipConnection(this, callee, info); + connections.add(c); + + Call.State newState = makeCallWait ? State.WAITING : State.INCOMING; + c.initIncomingCall(sipAudioCall, newState); + + setState(newState); + notifyNewRingingConnectionP(c); + } + + void rejectCall() throws CallStateException { + hangup(); + } + + void acceptCall() throws CallStateException { + if (this != foregroundCall) { + throw new CallStateException("acceptCall() in a non-fg call"); + } + if (connections.size() != 1) { + throw new CallStateException("acceptCall() in a conf call"); + } + ((SipConnection) connections.get(0)).acceptCall(); + } + + void hold() throws CallStateException { + setState(State.HOLDING); + AudioGroup audioGroup = getAudioGroup(); + if (audioGroup != null) { + audioGroup.setMode(AudioGroup.MODE_ON_HOLD); + } + for (Connection c : connections) ((SipConnection) c).hold(); + } + + void unhold() throws CallStateException { + setState(State.ACTIVE); + AudioGroup audioGroup = new AudioGroup(); + for (Connection c : connections) { + ((SipConnection) c).unhold(audioGroup); + } + } + + void setMute(boolean muted) { + AudioGroup audioGroup = getAudioGroup(); + if (audioGroup == null) return; + audioGroup.setMode( + muted ? AudioGroup.MODE_MUTED : AudioGroup.MODE_NORMAL); + } + + boolean getMute() { + AudioGroup audioGroup = getAudioGroup(); + if (audioGroup == null) return false; + return (audioGroup.getMode() == AudioGroup.MODE_MUTED); + } + + void merge(SipCall that) throws CallStateException { + AudioGroup audioGroup = getAudioGroup(); + for (Connection c : that.connections) { + SipConnection conn = (SipConnection) c; + add(conn); + if (conn.getState() == Call.State.HOLDING) { + conn.unhold(audioGroup); + } + } + that.setState(Call.State.IDLE); + } + + private void add(SipConnection conn) { + SipCall call = conn.getCall(); + if (call == this) return; + if (call != null) call.connections.remove(conn); + + connections.add(conn); + conn.changeOwner(this); + } + + void sendDtmf(char c) { + AudioGroup audioGroup = getAudioGroup(); + if (audioGroup == null) return; + audioGroup.sendDtmf(convertDtmf(c)); + } + + private int convertDtmf(char c) { + int code = c - '0'; + if ((code < 0) || (code > 9)) { + switch (c) { + case '*': return 10; + case '#': return 11; + case 'A': return 12; + case 'B': return 13; + case 'C': return 14; + case 'D': return 15; + default: + throw new IllegalArgumentException( + "invalid DTMF char: " + (int) c); + } + } + return code; + } + + @Override + protected void setState(State newState) { + if (state != newState) { + Log.v(LOG_TAG, "++******++ call state changed: " + state + + " --> " + newState + ": " + this + ": on phone " + + getPhone() + " " + connections.size()); + + if (newState == Call.State.ALERTING) { + state = newState; // need in ALERTING to enable ringback + SipPhone.this.startRingbackTone(); + } else if (state == Call.State.ALERTING) { + SipPhone.this.stopRingbackTone(); + } + state = newState; + updatePhoneState(); + notifyPreciseCallStateChanged(); + } + } + + void onConnectionStateChanged(SipConnection conn) { + // this can be called back when a conf call is formed + if (state != State.ACTIVE) { + setState(conn.getState()); + } + } + + void onConnectionEnded(SipConnection conn) { + // set state to DISCONNECTED only when all conns are disconnected + if (state != State.DISCONNECTED) { + boolean allConnectionsDisconnected = true; + for (Connection c : connections) { + if (c.getState() != State.DISCONNECTED) { + allConnectionsDisconnected = false; + break; + } + } + if (allConnectionsDisconnected) setState(State.DISCONNECTED); + } + notifyDisconnectP(conn); + } + + private AudioGroup getAudioGroup() { + if (connections.isEmpty()) return null; + return ((SipConnection) connections.get(0)).getAudioGroup(); + } + } + + private class SipConnection extends SipConnectionBase { + private SipCall mOwner; + private SipAudioCall mSipAudioCall; + private Call.State mState = Call.State.IDLE; + private SipProfile mPeer; + private boolean mIncoming = false; + + private SipAudioCallAdapter mAdapter = new SipAudioCallAdapter() { + @Override + protected void onCallEnded(DisconnectCause cause) { + if (getDisconnectCause() != DisconnectCause.LOCAL) { + setDisconnectCause(cause); + } + synchronized (SipPhone.class) { + setState(Call.State.DISCONNECTED); + mOwner.onConnectionEnded(SipConnection.this); + Log.v(LOG_TAG, "-------- connection ended: " + + mPeer.getUriString() + ": " + + mSipAudioCall.getState() + ", cause: " + + getDisconnectCause() + ", on phone " + + getPhone()); + } + } + + @Override + public void onChanged(SipAudioCall call) { + synchronized (SipPhone.class) { + Call.State newState = getCallStateFrom(call); + if (mState == newState) return; + if (newState == Call.State.INCOMING) { + setState(mOwner.getState()); // INCOMING or WAITING + } else { + setState(newState); + } + mOwner.onConnectionStateChanged(SipConnection.this); + Log.v(LOG_TAG, "++******++ connection state changed: " + + mPeer.getUriString() + ": " + mState + + " on phone " + getPhone()); + } + } + + @Override + protected void onError(DisconnectCause cause) { + Log.w(LOG_TAG, "SIP error: " + cause); + if (mSipAudioCall.isInCall() + && (cause != DisconnectCause.LOST_SIGNAL)) { + // Don't end the call when in a call. + return; + } + + onCallEnded(cause); + } + }; + + public SipConnection(SipCall owner, SipProfile callee, + CallerInfo info) { + super(getUriString(callee)); + mOwner = owner; + mPeer = callee; + if (info == null) info = createCallerInfo(); + setUserData(info); + } + + private CallerInfo createCallerInfo() { + SipProfile p = mPeer; + String name = p.getDisplayName(); + if (TextUtils.isEmpty(name)) name = p.getUserName(); + CallerInfo info = new CallerInfo(); + info.name = name; + info.phoneNumber = getUriString(p); + return info; + } + + void initIncomingCall(SipAudioCall sipAudioCall, Call.State newState) { + setState(newState); + mSipAudioCall = sipAudioCall; + sipAudioCall.setListener(mAdapter); // call back to set state + mIncoming = true; + } + + void acceptCall() throws CallStateException { + try { + mSipAudioCall.answerCall(); + } catch (SipException e) { + throw new CallStateException("acceptCall(): " + e); + } + } + + void changeOwner(SipCall owner) { + mOwner = owner; + } + + AudioGroup getAudioGroup() { + if (mSipAudioCall == null) return null; + return mSipAudioCall.getAudioGroup(); + } + + void dial() throws SipException { + setState(Call.State.DIALING); + mSipAudioCall = mSipManager.makeAudioCall(mContext, mProfile, + mPeer, null); + mSipAudioCall.setRingbackToneEnabled(false); + mSipAudioCall.setListener(mAdapter); + } + + void hold() throws CallStateException { + setState(Call.State.HOLDING); + try { + mSipAudioCall.holdCall(); + } catch (SipException e) { + throw new CallStateException("hold(): " + e); + } + } + + void unhold(AudioGroup audioGroup) throws CallStateException { + mSipAudioCall.setAudioGroup(audioGroup); + setState(Call.State.ACTIVE); + try { + mSipAudioCall.continueCall(); + } catch (SipException e) { + throw new CallStateException("unhold(): " + e); + } + } + + @Override + protected void setState(Call.State state) { + if (state == mState) return; + super.setState(state); + mState = state; + } + + @Override + public Call.State getState() { + return mState; + } + + @Override + public boolean isIncoming() { + return mIncoming; + } + + @Override + public String getAddress() { + return getUriString(mPeer); + } + + @Override + public SipCall getCall() { + return mOwner; + } + + @Override + protected Phone getPhone() { + return mOwner.getPhone(); + } + + @Override + public void hangup() throws CallStateException { + synchronized (SipPhone.class) { + Log.v(LOG_TAG, "hangup conn: " + mPeer.getUriString() + ": " + + ": on phone " + getPhone().getPhoneName()); + try { + mSipAudioCall.endCall(); + setState(Call.State.DISCONNECTING); + setDisconnectCause(DisconnectCause.LOCAL); + } catch (SipException e) { + throw new CallStateException("hangup(): " + e); + } + } + } + + @Override + public void separate() throws CallStateException { + synchronized (SipPhone.class) { + SipCall call = (SipCall) SipPhone.this.getBackgroundCall(); + if (call.getState() != Call.State.IDLE) { + throw new CallStateException( + "cannot put conn back to a call in non-idle state: " + + call.getState()); + } + Log.v(LOG_TAG, "separate conn: " + mPeer.getUriString() + + " from " + mOwner + " back to " + call); + + AudioGroup audioGroup = call.getAudioGroup(); // may be null + call.add(this); + mSipAudioCall.setAudioGroup(audioGroup); + call.hold(); + } + } + + @Override + public UUSInfo getUUSInfo() { + return null; + } + } + + private static Call.State getCallStateFrom(SipAudioCall sipAudioCall) { + if (sipAudioCall.isOnHold()) return Call.State.HOLDING; + SipSessionState sessionState = sipAudioCall.getState(); + switch (sessionState) { + case READY_TO_CALL: return Call.State.IDLE; + case INCOMING_CALL: + case INCOMING_CALL_ANSWERING: return Call.State.INCOMING; + case OUTGOING_CALL: return Call.State.DIALING; + case OUTGOING_CALL_RING_BACK: return Call.State.ALERTING; + case OUTGOING_CALL_CANCELING: return Call.State.DISCONNECTING; + case IN_CALL: return Call.State.ACTIVE; + default: + Log.w(LOG_TAG, "illegal connection state: " + sessionState); + return Call.State.DISCONNECTED; + } + } + + private abstract class SipAudioCallAdapter extends SipAudioCall.Adapter { + protected abstract void onCallEnded(Connection.DisconnectCause cause); + protected abstract void onError(Connection.DisconnectCause cause); + + @Override + public void onCallEnded(SipAudioCall call) { + onCallEnded(Connection.DisconnectCause.NORMAL); + } + + @Override + public void onCallBusy(SipAudioCall call) { + onCallEnded(Connection.DisconnectCause.BUSY); + } + + @Override + public void onError(SipAudioCall call, SipErrorCode errorCode, + String errorMessage) { + switch (errorCode) { + case INVALID_REMOTE_URI: + onError(Connection.DisconnectCause.INVALID_NUMBER); + break; + case TIME_OUT: + case TRANSACTION_TERMINTED: + onError(Connection.DisconnectCause.TIMED_OUT); + break; + case DATA_CONNECTION_LOST: + onError(Connection.DisconnectCause.LOST_SIGNAL); + break; + case INVALID_CREDENTIALS: + onError(Connection.DisconnectCause.INVALID_CREDENTIALS); + break; + case SOCKET_ERROR: + case SERVER_ERROR: + case CLIENT_ERROR: + default: + onError(Connection.DisconnectCause.ERROR_UNSPECIFIED); + } + } + } +} diff --git a/telephony/java/com/android/internal/telephony/sip/SipPhoneBase.java b/telephony/java/com/android/internal/telephony/sip/SipPhoneBase.java new file mode 100755 index 000000000000..9098e6ffb154 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/sip/SipPhoneBase.java @@ -0,0 +1,553 @@ +/* + * Copyright (C) 2010 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.sip; + +import android.content.ContentValues; +import android.content.Context; +import android.content.SharedPreferences; +import android.net.Uri; +import android.os.AsyncResult; +import android.os.Handler; +import android.os.Looper; +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.telephony.CellLocation; +import android.telephony.PhoneNumberUtils; +import android.telephony.ServiceState; +import android.telephony.SignalStrength; +import android.text.TextUtils; +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 static com.android.internal.telephony.TelephonyProperties.PROPERTY_BASEBAND_VERSION; + +import com.android.internal.telephony.Call; +import com.android.internal.telephony.CallStateException; +import com.android.internal.telephony.CommandsInterface; +import com.android.internal.telephony.Connection; +import com.android.internal.telephony.DataConnection; +import com.android.internal.telephony.IccCard; +import com.android.internal.telephony.IccFileHandler; +import com.android.internal.telephony.IccPhoneBookInterfaceManager; +import com.android.internal.telephony.IccSmsInterfaceManager; +import com.android.internal.telephony.MmiCode; +import com.android.internal.telephony.Phone; +import com.android.internal.telephony.PhoneBase; +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 java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +abstract class SipPhoneBase extends PhoneBase { + // NOTE that LOG_TAG here is "Sip", 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 = "SipPhone"; + private static final boolean LOCAL_DEBUG = true; + + //SipCallTracker mCT; + PhoneSubInfo mSubInfo; + + Registrant mPostDialHandler; + + final RegistrantList mRingbackRegistrants = new RegistrantList(); + + private State state = State.IDLE; + + public SipPhoneBase(Context context, PhoneNotifier notifier) { + super(notifier, context, new SipCommandInterface(context), false); + + // FIXME: what's this for SIP? + //Change the system property + //SystemProperties.set(TelephonyProperties.CURRENT_ACTIVE_PHONE, + // new Integer(Phone.PHONE_TYPE_GSM).toString()); + } + + public abstract Call getForegroundCall(); + + public abstract Call getBackgroundCall(); + + public abstract Call getRingingCall(); + + /* + public Connection dial(String dialString, UUSInfo uusInfo) + throws CallStateException { + // ignore UUSInfo + return dial(dialString); + } + */ + + void migrateFrom(SipPhoneBase from) { + migrate(mRingbackRegistrants, from.mRingbackRegistrants); + migrate(mPreciseCallStateRegistrants, from.mPreciseCallStateRegistrants); + migrate(mNewRingingConnectionRegistrants, from.mNewRingingConnectionRegistrants); + migrate(mIncomingRingRegistrants, from.mIncomingRingRegistrants); + migrate(mDisconnectRegistrants, from.mDisconnectRegistrants); + migrate(mServiceStateRegistrants, from.mServiceStateRegistrants); + migrate(mMmiCompleteRegistrants, from.mMmiCompleteRegistrants); + migrate(mMmiRegistrants, from.mMmiRegistrants); + migrate(mUnknownConnectionRegistrants, from.mUnknownConnectionRegistrants); + migrate(mSuppServiceFailedRegistrants, from.mSuppServiceFailedRegistrants); + } + + static void migrate(RegistrantList to, RegistrantList from) { + from.removeCleared(); + for (int i = 0, n = from.size(); i < n; i++) { + to.add((Registrant) from.get(i)); + } + } + + @Override + public void registerForRingbackTone(Handler h, int what, Object obj) { + mRingbackRegistrants.addUnique(h, what, obj); + } + + @Override + public void unregisterForRingbackTone(Handler h) { + mRingbackRegistrants.remove(h); + } + + protected void startRingbackTone() { + AsyncResult result = new AsyncResult(null, new Boolean(true), null); + mRingbackRegistrants.notifyRegistrants(result); + } + + protected void stopRingbackTone() { + AsyncResult result = new AsyncResult(null, new Boolean(false), null); + mRingbackRegistrants.notifyRegistrants(result); + } + + public void dispose() { + mIsTheCurrentActivePhone = false; + mSubInfo.dispose(); + } + + public void removeReferences() { + mSubInfo = null; + } + + public ServiceState getServiceState() { + // FIXME: we may need to provide this when data connectivity is lost + // or when server is down + ServiceState s = new ServiceState(); + s.setState(ServiceState.STATE_IN_SERVICE); + return s; + } + + public CellLocation getCellLocation() { + return null; //mSST.cellLoc; + } + + public State getState() { + return state; + } + + public int getPhoneType() { + return Phone.PHONE_TYPE_SIP; + } + + public SignalStrength getSignalStrength() { + return new SignalStrength(); + } + + public boolean getMessageWaitingIndicator() { + return false; + } + + public boolean getCallForwardingIndicator() { + return false; + } + + public List<? extends MmiCode> getPendingMmiCodes() { + return new ArrayList<MmiCode>(0); + } + + public DataState getDataConnectionState() { + return DataState.DISCONNECTED; + } + + public DataState getDataConnectionState(String apnType) { + return DataState.DISCONNECTED; + } + + public DataActivityState getDataActivityState() { + return DataActivityState.NONE; + } + + /** + * Notify any interested party of a Phone state change {@link Phone.State} + */ + void notifyPhoneStateChanged() { + mNotifier.notifyPhoneState(this); + } + + /** + * Notify registrants of a change in the call state. This notifies changes in {@link Call.State} + * Use this when changes in the precise call state are needed, else use notifyPhoneStateChanged. + */ + void notifyPreciseCallStateChanged() { + /* we'd love it if this was package-scoped*/ + super.notifyPreciseCallStateChangedP(); + } + + void notifyNewRingingConnection(Connection c) { + /* we'd love it if this was package-scoped*/ + super.notifyNewRingingConnectionP(c); + } + + void notifyDisconnect(Connection cn) { + mDisconnectRegistrants.notifyResult(cn); + } + + void notifyUnknownConnection() { + mUnknownConnectionRegistrants.notifyResult(this); + } + + void notifySuppServiceFailed(SuppService code) { + mSuppServiceFailedRegistrants.notifyResult(code); + } + + void notifyServiceStateChanged(ServiceState ss) { + super.notifyServiceStateChangedP(ss); + } + + public void notifyCallForwardingIndicator() { + mNotifier.notifyCallForwardingChanged(this); + } + + public boolean canDial() { + int serviceState = getServiceState().getState(); + Log.v(LOG_TAG, "canDial(): serviceState = " + serviceState); + if (serviceState == ServiceState.STATE_POWER_OFF) return false; + + String disableCall = SystemProperties.get( + TelephonyProperties.PROPERTY_DISABLE_CALL, "false"); + Log.v(LOG_TAG, "canDial(): disableCall = " + disableCall); + if (disableCall.equals("true")) return false; + + Log.v(LOG_TAG, "canDial(): ringingCall: " + getRingingCall().getState()); + Log.v(LOG_TAG, "canDial(): foregndCall: " + getForegroundCall().getState()); + Log.v(LOG_TAG, "canDial(): backgndCall: " + getBackgroundCall().getState()); + return !getRingingCall().isRinging() + && (!getForegroundCall().getState().isAlive() + || !getBackgroundCall().getState().isAlive()); + } + + public boolean handleInCallMmiCommands(String dialString) + throws CallStateException { + return false; + } + + boolean isInCall() { + Call.State foregroundCallState = getForegroundCall().getState(); + Call.State backgroundCallState = getBackgroundCall().getState(); + Call.State ringingCallState = getRingingCall().getState(); + + return (foregroundCallState.isAlive() || backgroundCallState.isAlive() + || ringingCallState.isAlive()); + } + + public boolean handlePinMmi(String dialString) { + return false; + } + + public void sendUssdResponse(String ussdMessge) { + } + + public void registerForSuppServiceNotification( + Handler h, int what, Object obj) { + } + + public void unregisterForSuppServiceNotification(Handler h) { + } + + public void setRadioPower(boolean power) { + } + + public String getVoiceMailNumber() { + return null; + } + + public String getVoiceMailAlphaTag() { + return null; + } + + public String getDeviceId() { + return null; + } + + public String getDeviceSvn() { + return null; + } + + public String getEsn() { + Log.e(LOG_TAG, "[SipPhone] getEsn() is a CDMA method"); + return "0"; + } + + public String getMeid() { + Log.e(LOG_TAG, "[SipPhone] getMeid() is a CDMA method"); + return "0"; + } + + public String getSubscriberId() { + return null; + } + + public String getIccSerialNumber() { + return null; + } + + public String getLine1Number() { + return null; + } + + public String getLine1AlphaTag() { + return null; + } + + public void setLine1Number(String alphaTag, String number, Message onComplete) { + // FIXME: what to reply for SIP? + AsyncResult.forMessage(onComplete, null, null); + onComplete.sendToTarget(); + } + + public void setVoiceMailNumber(String alphaTag, String voiceMailNumber, + Message onComplete) { + // FIXME: what to reply for SIP? + AsyncResult.forMessage(onComplete, null, null); + onComplete.sendToTarget(); + } + + 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; + } + } + + 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; + } + } + + protected boolean isCfEnable(int action) { + return (action == CF_ACTION_ENABLE) || (action == CF_ACTION_REGISTRATION); + } + + public void getCallForwardingOption(int commandInterfaceCFReason, Message onComplete) { + if (isValidCommandInterfaceCFReason(commandInterfaceCFReason)) { + // FIXME: what to reply? + AsyncResult.forMessage(onComplete, null, null); + onComplete.sendToTarget(); + } + } + + public void setCallForwardingOption(int commandInterfaceCFAction, + int commandInterfaceCFReason, String dialingNumber, + int timerSeconds, Message onComplete) { + if (isValidCommandInterfaceCFAction(commandInterfaceCFAction) + && isValidCommandInterfaceCFReason(commandInterfaceCFReason)) { + // FIXME: what to reply? + AsyncResult.forMessage(onComplete, null, null); + onComplete.sendToTarget(); + } + } + + public void getOutgoingCallerIdDisplay(Message onComplete) { + // FIXME: what to reply? + AsyncResult.forMessage(onComplete, null, null); + onComplete.sendToTarget(); + } + + public void setOutgoingCallerIdDisplay(int commandInterfaceCLIRMode, + Message onComplete) { + // FIXME: what's this for SIP? + AsyncResult.forMessage(onComplete, null, null); + onComplete.sendToTarget(); + } + + public void getCallWaiting(Message onComplete) { + // FIXME: what to reply? + AsyncResult.forMessage(onComplete, null, null); + onComplete.sendToTarget(); + } + + public void setCallWaiting(boolean enable, Message onComplete) { + // FIXME: what to reply? + Log.e(LOG_TAG, "call waiting not supported"); + } + + public boolean getIccRecordsLoaded() { + return false; + } + + public IccCard getIccCard() { + return null; + } + + public void getAvailableNetworks(Message response) { + // FIXME: what to reply? + } + + public void setNetworkSelectionModeAutomatic(Message response) { + // FIXME: what to reply? + } + + public void selectNetworkManually( + com.android.internal.telephony.gsm.NetworkInfo network, + Message response) { + // FIXME: what to reply? + } + + public void getNeighboringCids(Message response) { + // FIXME: what to reply? + } + + public void setOnPostDialCharacter(Handler h, int what, Object obj) { + mPostDialHandler = new Registrant(h, what, obj); + } + + public void getDataCallList(Message response) { + // FIXME: what to reply? + } + + public List<DataConnection> getCurrentDataConnectionList () { + return null; + } + + public void updateServiceLocation() { + } + + public void enableLocationUpdates() { + } + + public void disableLocationUpdates() { + } + + public boolean getDataRoamingEnabled() { + return false; + } + + public void setDataRoamingEnabled(boolean enable) { + } + + public boolean enableDataConnectivity() { + return false; + } + + public boolean disableDataConnectivity() { + return false; + } + + public boolean isDataConnectivityPossible() { + return false; + } + + boolean updateCurrentCarrierInProvider() { + return false; + } + + public void saveClirSetting(int commandInterfaceCLIRMode) { + // FIXME: what's this for SIP? + } + + /** + * Retrieves the PhoneSubInfo of the SipPhone + */ + public PhoneSubInfo getPhoneSubInfo(){ + return mSubInfo; + } + + /** {@inheritDoc} */ + public IccSmsInterfaceManager getIccSmsInterfaceManager(){ + return null; + } + + /** {@inheritDoc} */ + public IccPhoneBookInterfaceManager getIccPhoneBookInterfaceManager(){ + return null; + } + + /** {@inheritDoc} */ + public IccFileHandler getIccFileHandler(){ + return null; + } + + public void activateCellBroadcastSms(int activate, Message response) { + Log.e(LOG_TAG, "Error! This functionality is not implemented for SIP."); + } + + public void getCellBroadcastSmsConfig(Message response) { + Log.e(LOG_TAG, "Error! This functionality is not implemented for SIP."); + } + + public void setCellBroadcastSmsConfig(int[] configValuesArray, Message response){ + Log.e(LOG_TAG, "Error! This functionality is not implemented for SIP."); + } + + void updatePhoneState() { + State oldState = state; + + if (getRingingCall().isRinging()) { + state = State.RINGING; + } else if (getForegroundCall().isIdle() + && getBackgroundCall().isIdle()) { + state = State.IDLE; + } else { + state = State.OFFHOOK; + } + Log.e(LOG_TAG, " ^^^^^^ new phone state: " + state); + + if (state != oldState) { + notifyPhoneStateChanged(); + } + } +} diff --git a/telephony/java/com/android/internal/telephony/sip/SipPhoneFactory.java b/telephony/java/com/android/internal/telephony/sip/SipPhoneFactory.java new file mode 100644 index 000000000000..611e3ea020ec --- /dev/null +++ b/telephony/java/com/android/internal/telephony/sip/SipPhoneFactory.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2010 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.sip; + +import com.android.internal.telephony.PhoneNotifier; + +import android.content.Context; +import android.net.sip.SipProfile; +import android.util.Log; + +import java.text.ParseException; + +/** + * {@hide} + */ +public class SipPhoneFactory { + /** + * Makes a {@link SipPhone} object. + * @param sipUri the local SIP URI the phone runs on + * @param context {@code Context} needed to create a Phone object + * @param phoneNotifier {@code PhoneNotifier} needed to create a Phone + * object + * @return the {@code SipPhone} object or null if the SIP URI is not valid + */ + public static SipPhone makePhone(String sipUri, Context context, + PhoneNotifier phoneNotifier) { + try { + SipProfile profile = new SipProfile.Builder(sipUri).build(); + return new SipPhone(context, phoneNotifier, profile); + } catch (ParseException e) { + Log.w("SipPhoneFactory", "makePhone", e); + return null; + } + } +} diff --git a/telephony/java/com/android/internal/telephony/test/SimulatedCommands.java b/telephony/java/com/android/internal/telephony/test/SimulatedCommands.java index 11b3fd6fa12c..9c72e5a5052b 100644 --- a/telephony/java/com/android/internal/telephony/test/SimulatedCommands.java +++ b/telephony/java/com/android/internal/telephony/test/SimulatedCommands.java @@ -29,6 +29,7 @@ import com.android.internal.telephony.CommandsInterface; import com.android.internal.telephony.DataCallState; import com.android.internal.telephony.IccCard; import com.android.internal.telephony.Phone; +import com.android.internal.telephony.UUSInfo; import com.android.internal.telephony.gsm.CallFailCause; import com.android.internal.telephony.gsm.SmsBroadcastConfigInfo; import com.android.internal.telephony.gsm.SuppServiceNotification; @@ -496,6 +497,23 @@ public final class SimulatedCommands extends BaseCommands * retMsg.obj = AsyncResult ar * ar.exception carries exception on failure * ar.userObject contains the orignal value of result.obj + * ar.result is null on success and failure + * + * CLIR_DEFAULT == on "use subscription default value" + * CLIR_SUPPRESSION == on "CLIR suppression" (allow CLI presentation) + * CLIR_INVOCATION == on "CLIR invocation" (restrict CLI presentation) + */ + public void dial(String address, int clirMode, UUSInfo uusInfo, Message result) { + simulatedCallState.onDial(address); + + resultSuccess(result, null); + } + + /** + * returned message + * retMsg.obj = AsyncResult ar + * ar.exception carries exception on failure + * ar.userObject contains the orignal value of result.obj * ar.result is String containing IMSI on success */ public void getIMSI(Message result) { @@ -826,7 +844,6 @@ public final class SimulatedCommands extends BaseCommands ret[11] = null; ret[12] = null; ret[13] = null; - ret[14] = null; resultSuccess(result, ret); } |
