diff options
Diffstat (limited to 'telephony/java')
35 files changed, 4116 insertions, 103 deletions
diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java index 1d3ad81..7829006 100644 --- a/telephony/java/android/telephony/PhoneNumberUtils.java +++ b/telephony/java/android/telephony/PhoneNumberUtils.java @@ -141,9 +141,9 @@ public class PhoneNumberUtils } // TODO: We don't check for SecurityException here (requires - // READ_PHONE_STATE permission). + // CALL_PRIVILEGED permission). if (scheme.equals("voicemail")) { - return TelephonyManager.getDefault().getVoiceMailNumber(); + return TelephonyManager.getDefault().getCompleteVoiceMailNumber(); } if (context == null) { @@ -1692,6 +1692,15 @@ public class PhoneNumberUtils } /** + * @hide + * @param number + * @return true if number contains @ + */ + public static boolean isUriNumber(String number) { + return number != null && number.contains("@"); + } + + /** * This function handles the plus code conversion within NANP CDMA network * If the number format is * 1)+1NANP,remove +, diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index ab63017..27e08d4 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -177,7 +177,7 @@ public class TelephonyManager { /** * Returns the unique device ID, for example, the IMEI for GSM and the MEID - * for CDMA phones. Return null if device ID is not available. + * or ESN for CDMA phones. Return null if device ID is not available. * * <p>Requires Permission: * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} @@ -665,6 +665,25 @@ public class TelephonyManager { } /** + * Returns the complete voice mail number. Return null if it is unavailable. + * <p> + * Requires Permission: + * {@link android.Manifest.permission#CALL_PRIVILEGED CALL_PRIVILEGED} + * + * @hide + */ + public String getCompleteVoiceMailNumber() { + try { + return getSubscriberInfo().getCompleteVoiceMailNumber(); + } catch (RemoteException ex) { + return null; + } catch (NullPointerException ex) { + // This could happen before phone restarts due to crashing + return null; + } + } + + /** * Returns the voice mail count. Return 0 if unavailable. * <p> * Requires Permission: diff --git a/telephony/java/android/telephony/gsm/GsmCellLocation.java b/telephony/java/android/telephony/gsm/GsmCellLocation.java index fa1f985..c4204fa 100644 --- a/telephony/java/android/telephony/gsm/GsmCellLocation.java +++ b/telephony/java/android/telephony/gsm/GsmCellLocation.java @@ -25,6 +25,7 @@ import android.telephony.CellLocation; public class GsmCellLocation extends CellLocation { private int mLac; private int mCid; + private int mPsc; /** * Empty constructor. Initializes the LAC and CID to -1. @@ -32,6 +33,7 @@ public class GsmCellLocation extends CellLocation { public GsmCellLocation() { mLac = -1; mCid = -1; + mPsc = -1; } /** @@ -40,6 +42,7 @@ public class GsmCellLocation extends CellLocation { public GsmCellLocation(Bundle bundle) { mLac = bundle.getInt("lac", mLac); mCid = bundle.getInt("cid", mCid); + mPsc = bundle.getInt("psc", mPsc); } /** @@ -57,11 +60,20 @@ public class GsmCellLocation extends CellLocation { } /** + * @return primary scrambling code for UMTS, -1 if unknown or GSM + * @hide + */ + public int getPsc() { + return mPsc; + } + + /** * Invalidate this object. The location area code and the cell id are set to -1. */ public void setStateInvalid() { mLac = -1; mCid = -1; + mPsc = -1; } /** @@ -72,6 +84,14 @@ public class GsmCellLocation extends CellLocation { mCid = cid; } + /** + * Set the primary scrambling code. + * @hide + */ + public void setPsc(int psc) { + mPsc = psc; + } + @Override public int hashCode() { return mLac ^ mCid; @@ -91,12 +111,13 @@ public class GsmCellLocation extends CellLocation { return false; } - return equalsHandlesNulls(mLac, s.mLac) && equalsHandlesNulls(mCid, s.mCid); + return equalsHandlesNulls(mLac, s.mLac) && equalsHandlesNulls(mCid, s.mCid) + && equalsHandlesNulls(mPsc, s.mPsc); } @Override public String toString() { - return "["+ mLac + "," + mCid + "]"; + return "["+ mLac + "," + mCid + "," + mPsc + "]"; } /** @@ -118,12 +139,13 @@ public class GsmCellLocation extends CellLocation { public void fillInNotifierBundle(Bundle m) { m.putInt("lac", mLac); m.putInt("cid", mCid); + m.putInt("psc", mPsc); } /** * @hide */ public boolean isEmpty() { - return (mLac == -1 && mCid == -1); + return (mLac == -1 && mCid == -1 && mPsc == -1); } } diff --git a/telephony/java/android/telephony/gsm/SmsMessage.java b/telephony/java/android/telephony/gsm/SmsMessage.java index 37ef912..0c63c37 100644 --- a/telephony/java/android/telephony/gsm/SmsMessage.java +++ b/telephony/java/android/telephony/gsm/SmsMessage.java @@ -304,9 +304,9 @@ public class SmsMessage { int septets = GsmAlphabet.countGsmSeptets(messageBody, !use7bitOnly); ret[1] = septets; if (septets > MAX_USER_DATA_SEPTETS) { - ret[0] = (septets / MAX_USER_DATA_SEPTETS_WITH_HEADER) + 1; - ret[2] = MAX_USER_DATA_SEPTETS_WITH_HEADER - - (septets % MAX_USER_DATA_SEPTETS_WITH_HEADER); + ret[0] = (septets + (MAX_USER_DATA_SEPTETS_WITH_HEADER - 1)) / + MAX_USER_DATA_SEPTETS_WITH_HEADER; + ret[2] = (ret[0] * MAX_USER_DATA_SEPTETS_WITH_HEADER) - septets; } else { ret[0] = 1; ret[2] = MAX_USER_DATA_SEPTETS - septets; @@ -318,9 +318,9 @@ public class SmsMessage { ret[1] = messageBody.length(); if (octets > MAX_USER_DATA_BYTES) { // 6 is the size of the user data header - ret[0] = (octets / MAX_USER_DATA_BYTES_WITH_HEADER) + 1; - ret[2] = (MAX_USER_DATA_BYTES_WITH_HEADER - - (octets % MAX_USER_DATA_BYTES_WITH_HEADER))/2; + ret[0] = (octets + (MAX_USER_DATA_BYTES_WITH_HEADER - 1)) / + MAX_USER_DATA_BYTES_WITH_HEADER; + ret[2] = ((ret[0] * MAX_USER_DATA_BYTES_WITH_HEADER) - octets) / 2; } else { ret[0] = 1; ret[2] = (MAX_USER_DATA_BYTES - octets)/2; 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 0000000..12df44e --- /dev/null +++ b/telephony/java/com/android/internal/telephony/CallManager.java @@ -0,0 +1,1371 @@ +/* + * 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; + + // 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 mServiceStateRegistrants + = 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(); + + 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); + } + + 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); + } + + /** + * 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(); + } + } + + /** + * 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){} + + /** + * Unregisters for ServiceStateChange notification. + * Extraneous calls are tolerated silently + */ + public void unregisterForServiceStateChanged(Handler 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 ringing calls + */ + public ArrayList<Call> getRingingCalls() { + return mRingingCalls; + } + + /** + * @return list of 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; + } + } + }; +} diff --git a/telephony/java/com/android/internal/telephony/DefaultPhoneNotifier.java b/telephony/java/com/android/internal/telephony/DefaultPhoneNotifier.java index 4da4b6a..057ba0a 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/GsmAlphabet.java b/telephony/java/com/android/internal/telephony/GsmAlphabet.java index ebdd220..7edf065 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/IPhoneSubInfo.aidl b/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl index e74b9e4..5cba2e1 100644 --- a/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl +++ b/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl @@ -59,6 +59,11 @@ interface IPhoneSubInfo { String getVoiceMailNumber(); /** + * Retrieves the complete voice mail number. + */ + String getCompleteVoiceMailNumber(); + + /** * Retrieves the alpha identifier associated with the voice mail number. */ String getVoiceMailAlphaTag(); diff --git a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl index 5bf8e58..87e4b52 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/Phone.java b/telephony/java/com/android/internal/telephony/Phone.java index 23325f6..9afade3 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 diff --git a/telephony/java/com/android/internal/telephony/PhoneFactory.java b/telephony/java/com/android/internal/telephony/PhoneFactory.java index 803b736..2e391cb 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} @@ -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/PhoneSubInfo.java b/telephony/java/com/android/internal/telephony/PhoneSubInfo.java index 4f71bb1..a45cad1 100644 --- a/telephony/java/com/android/internal/telephony/PhoneSubInfo.java +++ b/telephony/java/com/android/internal/telephony/PhoneSubInfo.java @@ -21,6 +21,7 @@ import java.io.PrintWriter; import android.content.Context; import android.content.pm.PackageManager; import android.os.Binder; +import android.telephony.PhoneNumberUtils; import android.util.Log; public class PhoneSubInfo extends IPhoneSubInfo.Stub { @@ -29,6 +30,9 @@ public class PhoneSubInfo extends IPhoneSubInfo.Stub { private Context mContext; private static final String READ_PHONE_STATE = android.Manifest.permission.READ_PHONE_STATE; + private static final String CALL_PRIVILEGED = + // TODO Add core/res/AndriodManifest.xml#READ_PRIVILEGED_PHONE_STATE + android.Manifest.permission.CALL_PRIVILEGED; public PhoneSubInfo(Phone phone) { mPhone = phone; @@ -101,7 +105,22 @@ public class PhoneSubInfo extends IPhoneSubInfo.Stub { */ public String getVoiceMailNumber() { mContext.enforceCallingOrSelfPermission(READ_PHONE_STATE, "Requires READ_PHONE_STATE"); - return (String) mPhone.getVoiceMailNumber(); + String number = PhoneNumberUtils.extractNetworkPortion(mPhone.getVoiceMailNumber()); + Log.d(LOG_TAG, "VM: PhoneSubInfo.getVoiceMailNUmber: "); // + number); + return number; + } + + /** + * Retrieves the complete voice mail number. + * + * @hide + */ + public String getCompleteVoiceMailNumber() { + mContext.enforceCallingOrSelfPermission(CALL_PRIVILEGED, + "Requires CALL_PRIVILEGED"); + String number = mPhone.getVoiceMailNumber(); + Log.d(LOG_TAG, "VM: PhoneSubInfo.getCompleteVoiceMailNUmber: "); // + number); + return number; } /** diff --git a/telephony/java/com/android/internal/telephony/PhoneSubInfoProxy.java b/telephony/java/com/android/internal/telephony/PhoneSubInfoProxy.java index 202ded2..7009893 100644 --- a/telephony/java/com/android/internal/telephony/PhoneSubInfoProxy.java +++ b/telephony/java/com/android/internal/telephony/PhoneSubInfoProxy.java @@ -82,6 +82,13 @@ public class PhoneSubInfoProxy extends IPhoneSubInfo.Stub { } /** + * Retrieves the complete voice mail number. + */ + public String getCompleteVoiceMailNumber() { + return mPhoneSubInfo.getCompleteVoiceMailNumber(); + } + + /** * Retrieves the alpha identifier associated with the voice mail number. */ public String getVoiceMailAlphaTag() { diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java index 71a80e0..888f721 100644 --- a/telephony/java/com/android/internal/telephony/RILConstants.java +++ b/telephony/java/com/android/internal/telephony/RILConstants.java @@ -79,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/SMSDispatcher.java b/telephony/java/com/android/internal/telephony/SMSDispatcher.java index ca526a5..606b52d 100644 --- a/telephony/java/com/android/internal/telephony/SMSDispatcher.java +++ b/telephony/java/com/android/internal/telephony/SMSDispatcher.java @@ -44,10 +44,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; @@ -75,7 +71,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", @@ -83,8 +79,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; @@ -143,7 +137,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; @@ -154,10 +148,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; @@ -345,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"); @@ -358,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 { @@ -372,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"); @@ -679,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> @@ -706,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> @@ -761,7 +751,7 @@ public abstract class SMSDispatcher extends Handler { * defatult 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> @@ -837,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) { @@ -931,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. * */ @@ -972,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_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); } - - }; + } + }; } 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 0000000..4092c69 --- /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/cdma/CDMAPhone.java b/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java index 7aecf5b..840d366 100755 --- a/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java +++ b/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java @@ -454,9 +454,14 @@ public class CDMAPhone extends PhoneBase { return mMeid; } - //returns MEID in CDMA + //returns MEID or ESN in CDMA public String getDeviceId() { - return getMeid(); + String id = getMeid(); + if ((id == null) || id.matches("^0*$")) { + Log.d(LOG_TAG, "getDeviceId(): MEID is not initialized use ESN"); + id = getEsn(); + } + return id; } public String getDeviceSvn() { diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaCall.java b/telephony/java/com/android/internal/telephony/cdma/CdmaCall.java index c3bb01f..4ad61bb 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/CdmaSMSDispatcher.java b/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java index ed93aea..8eaf4a2 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,7 +124,7 @@ 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(); @@ -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/SmsMessage.java b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java index b50502c..0f3b8ff 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. @@ -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/sms/BearerData.java b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java index c7032ac..ab79fe9 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/GsmCall.java b/telephony/java/com/android/internal/telephony/gsm/GsmCall.java index 9542d20..58124a2 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/GsmDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java index 5f651e7..f6d4491 100644 --- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java +++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java @@ -442,8 +442,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { if ((state == State.IDLE || state == State.SCANNING) && (gprsState == ServiceState.STATE_IN_SERVICE || noAutoAttach) && mGsmPhone.mSIMRecords.getRecordsLoaded() - && (mGsmPhone.mSST.isConcurrentVoiceAndData() || - phone.getState() == Phone.State.IDLE ) + && phone.getState() == Phone.State.IDLE && isDataAllowed() && !mIsPsRestricted && desiredPowerState ) { diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java b/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java index d720516..3079a64 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; @@ -97,20 +96,20 @@ final class GsmSMSDispatcher extends SMSDispatcher { 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, Dont display or store it. Send Ack"); + 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); } @@ -301,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 d539f6f..90ecbd7 100644 --- a/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java +++ b/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java @@ -650,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]); @@ -661,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); } @@ -677,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: diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java index 4fd62fb..9a3c476 100644 --- a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java +++ b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java @@ -800,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; @@ -812,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 0000000..58fb408 --- /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 0000000..fad9663 --- /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 0000000..e7eda4f --- /dev/null +++ b/telephony/java/com/android/internal/telephony/sip/SipCallBase.java @@ -0,0 +1,103 @@ +/* + * 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; +import javax.sip.SipException; + +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 0000000..33c89f8 --- /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 0000000..6c989b4 --- /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 0000000..1325dd3 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/sip/SipPhone.java @@ -0,0 +1,823 @@ +/* + * 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.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; + +import javax.sip.SipException; + +/** + * {@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 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(String errorMessage) { + Log.w(LOG_TAG, "SIP error: " + errorMessage); + if (mSipAudioCall.isInCall()) { + // Don't end the call when in call. + // TODO: how to deliver the error to PhoneApp + return; + } + + // FIXME: specify error + onCallEnded(DisconnectCause.ERROR_UNSPECIFIED); + } + }; + + 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(String errorMessage); + + @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, String errorMessage) { + onError(errorMessage); + } + } +} 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 0000000..4b7e991 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/sip/SipPhoneBase.java @@ -0,0 +1,554 @@ +/* + * 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() { + // FIXME: add SIP phone type + return Phone.PHONE_TYPE_GSM; + } + + 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 0000000..611e3ea --- /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 a120f52..9c72e5a 100644 --- a/telephony/java/com/android/internal/telephony/test/SimulatedCommands.java +++ b/telephony/java/com/android/internal/telephony/test/SimulatedCommands.java @@ -844,7 +844,6 @@ public final class SimulatedCommands extends BaseCommands ret[11] = null; ret[12] = null; ret[13] = null; - ret[14] = null; resultSuccess(result, ret); } |
