From b631dcf3eb449ddec756bea330f4e70b996ffb9e Mon Sep 17 00:00:00 2001 From: Chung-yih Wang Date: Thu, 5 Aug 2010 11:09:54 +0800 Subject: Move SIP telephony related codes to framework. + hardcode the sip service for build dependency. Change-Id: Ib0e9717c9b87eb6e06ffa3a7b01ae31184de61bb --- .../internal/telephony/SipPhoneNotifier.java | 236 +++++++ .../internal/telephony/sip/CallFailCause.java | 50 ++ .../android/internal/telephony/sip/CallProxy.java | 114 ++++ .../internal/telephony/sip/SipCallBase.java | 103 +++ .../telephony/sip/SipCommandInterface.java | 373 ++++++++++ .../internal/telephony/sip/SipConnectionBase.java | 246 +++++++ .../android/internal/telephony/sip/SipPhone.java | 760 +++++++++++++++++++++ .../internal/telephony/sip/SipPhoneBase.java | 558 +++++++++++++++ .../internal/telephony/sip/SipPhoneFactory.java | 67 ++ .../internal/telephony/sip/SipPhoneProxy.java | 749 ++++++++++++++++++++ 10 files changed, 3256 insertions(+) create mode 100644 telephony/java/com/android/internal/telephony/SipPhoneNotifier.java create mode 100644 telephony/java/com/android/internal/telephony/sip/CallFailCause.java create mode 100644 telephony/java/com/android/internal/telephony/sip/CallProxy.java create mode 100644 telephony/java/com/android/internal/telephony/sip/SipCallBase.java create mode 100644 telephony/java/com/android/internal/telephony/sip/SipCommandInterface.java create mode 100644 telephony/java/com/android/internal/telephony/sip/SipConnectionBase.java create mode 100755 telephony/java/com/android/internal/telephony/sip/SipPhone.java create mode 100755 telephony/java/com/android/internal/telephony/sip/SipPhoneBase.java create mode 100644 telephony/java/com/android/internal/telephony/sip/SipPhoneFactory.java create mode 100644 telephony/java/com/android/internal/telephony/sip/SipPhoneProxy.java (limited to 'telephony') 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..81e151e --- /dev/null +++ b/telephony/java/com/android/internal/telephony/SipPhoneNotifier.java @@ -0,0 +1,236 @@ +/* + * 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)); + } 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/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 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 connections = new ArrayList(); + + protected abstract void setState(State newState); + + public void dispose() { + } + + public List 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 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..d48f94a --- /dev/null +++ b/telephony/java/com/android/internal/telephony/sip/SipConnectionBase.java @@ -0,0 +1,246 @@ +/* + * 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: + 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..4e61d30 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/sip/SipPhone.java @@ -0,0 +1,760 @@ +/* + * 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.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; + + //private List connections = new ArrayList(); + + // 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 mProfile.getProfileName(); + } + + 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 { + // TODO + } + + public boolean canTransfer() { + return false; + } + + public void explicitCallTransfer() throws CallStateException { + //mCT.explicitCallTransfer(); + } + + public void clearDisconnected() { + 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()) { + 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) { + 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 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 { + 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 { + AudioGroup audioGroup = getAudioGroup(); + if (audioGroup == null) return; + audioGroup.setMode(AudioGroup.MODE_ON_HOLD); + setState(State.HOLDING); + for (Connection c : connections) ((SipConnection) c).hold(); + } + + void unhold() throws CallStateException { + AudioGroup audioGroup = getAudioGroup(); + if (audioGroup == null) return; + audioGroup.setMode(AudioGroup.MODE_NORMAL); + setState(State.ACTIVE); + for (Connection c : connections) ((SipConnection) c).unhold(); + } + + 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 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 { + try { + mSipAudioCall.holdCall(); + } catch (SipException e) { + throw new CallStateException("hold(): " + e); + } + } + + void unhold() throws CallStateException { + 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 { + Log.v(LOG_TAG, "hangup conn: " + mPeer.getUriString() + ": " + + ": on phone " + getPhone()); + try { + mSipAudioCall.endCall(); + setState(Call.State.DISCONNECTING); + setDisconnectCause(DisconnectCause.LOCAL); + } catch (SipException e) { + throw new CallStateException("hangup(): " + e); + } + } + + @Override + public void separate() throws CallStateException { + // TODO: what's this for SIP? + /* + if (!disconnected) { + owner.separate(this); + } else { + throw new CallStateException ("disconnected"); + } + */ + } + + @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..36d65db --- /dev/null +++ b/telephony/java/com/android/internal/telephony/sip/SipPhoneBase.java @@ -0,0 +1,558 @@ +/* + * 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 String getPhoneName() { + return "SIP"; + } + + 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 getPendingMmiCodes() { + return new ArrayList(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 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..c9e9762 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/sip/SipPhoneFactory.java @@ -0,0 +1,67 @@ +/* + * 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.Phone; +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 { + private static PhoneNotifier sPhoneNotifier = makeDefaultPhoneNotifier(); + private static Context sContext; + + public static void makeDefaultPhones(Context context) { + makeDefaultPhone(context); + } + + public static void makeDefaultPhone(Context context) { + sContext = context; + SipPhoneProxy.getInstance().setPhone( + makePhone("sip:anonymous@localhost")); + } + + public static Phone getDefaultPhone() { + return SipPhoneProxy.getInstance(); + } + + public static SipPhone makePhone(String sipProfileUri) { + try { + SipProfile profile = new SipProfile.Builder(sipProfileUri).build(); + return new SipPhone(sContext, sPhoneNotifier, profile); + } catch (ParseException e) { + Log.v("SipPhoneProxy", "setPhone", e); + return null; + } + } + + private static PhoneNotifier makeDefaultPhoneNotifier() { + try { + return new com.android.internal.telephony.SipPhoneNotifier(); + } catch (Error e) { + Log.e("SipPhoneProxy", "makeDefaultPhoneNotifier", e); + throw e; + } + } +} diff --git a/telephony/java/com/android/internal/telephony/sip/SipPhoneProxy.java b/telephony/java/com/android/internal/telephony/sip/SipPhoneProxy.java new file mode 100644 index 0000000..7cc1a9b --- /dev/null +++ b/telephony/java/com/android/internal/telephony/sip/SipPhoneProxy.java @@ -0,0 +1,749 @@ +/* + * 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 com.android.internal.telephony.gsm.NetworkInfo; +import com.android.internal.telephony.test.SimulatedRadioControl; + +import android.content.Context; +import android.os.Handler; +import android.os.Message; +import android.telephony.CellLocation; +import android.telephony.ServiceState; +import android.telephony.SignalStrength; +import android.util.Log; + +import java.util.List; + +/** + * Temporary. Will be removed after integrating with CallManager. + * (TODO) + * @hide + */ +public class SipPhoneProxy implements Phone { + private static final String LOG_TAG = "PHONE"; + + private static SipPhoneProxy sPhoneProxy = new SipPhoneProxy(); + + public static SipPhoneProxy getInstance() { + return sPhoneProxy; + } + + private SipPhone mActivePhone; + private CallProxy mRingingCall = new CallProxy(); + private CallProxy mForegroundCall = new CallProxy(); + private CallProxy mBackgroundCall = new CallProxy(); + + private SipPhoneProxy() { + } + + public void onNewCall(Object call) { + if (mActivePhone.canTake(call)) { + Log.v("SipPhoneProxy", "onNewCall(): call taken: " + call); + } else { + Log.v("SipPhoneProxy", "onNewCall(): call dropped: " + call); + } + } + + public synchronized void setPhone(SipPhone phone) { + if (phone == null) return; + if (mActivePhone != null) phone.migrateFrom(mActivePhone); + mActivePhone = phone; + mForegroundCall.setTarget(phone.getForegroundCall()); + mBackgroundCall.setTarget(phone.getBackgroundCall()); + mRingingCall.setTarget(phone.getRingingCall()); + } + + public synchronized Call getForegroundCall() { + return mForegroundCall; + } + + public synchronized Call getBackgroundCall() { + return mBackgroundCall; + } + + public synchronized Call getRingingCall() { + return mRingingCall; + } + + + public ServiceState getServiceState() { + return mActivePhone.getServiceState(); + } + + public CellLocation getCellLocation() { + return mActivePhone.getCellLocation(); + } + + public DataState getDataConnectionState() { + return mActivePhone.getDataConnectionState(); + } + + public DataActivityState getDataActivityState() { + return mActivePhone.getDataActivityState(); + } + + public Context getContext() { + return mActivePhone.getContext(); + } + + public void disableDnsCheck(boolean b) { + mActivePhone.disableDnsCheck(b); + } + + public boolean isDnsCheckDisabled() { + return mActivePhone.isDnsCheckDisabled(); + } + + public State getState() { + return mActivePhone.getState(); + } + + public String getPhoneName() { + return mActivePhone.getPhoneName(); + } + + public int getPhoneType() { + return mActivePhone.getPhoneType(); + } + + public String[] getActiveApnTypes() { + return mActivePhone.getActiveApnTypes(); + } + + public String getActiveApn() { + return mActivePhone.getActiveApn(); + } + + public SignalStrength getSignalStrength() { + return mActivePhone.getSignalStrength(); + } + + public void registerForUnknownConnection(Handler h, int what, Object obj) { + mActivePhone.registerForUnknownConnection(h, what, obj); + } + + public void unregisterForUnknownConnection(Handler h) { + mActivePhone.unregisterForUnknownConnection(h); + } + + public void registerForPreciseCallStateChanged(Handler h, int what, Object obj) { + mActivePhone.registerForPreciseCallStateChanged(h, what, obj); + } + + public void unregisterForPreciseCallStateChanged(Handler h) { + mActivePhone.unregisterForPreciseCallStateChanged(h); + } + + public void registerForNewRingingConnection(Handler h, int what, Object obj) { + mActivePhone.registerForNewRingingConnection(h, what, obj); + } + + public void unregisterForNewRingingConnection(Handler h) { + mActivePhone.unregisterForNewRingingConnection(h); + } + + public void registerForIncomingRing(Handler h, int what, Object obj) { + mActivePhone.registerForIncomingRing(h, what, obj); + } + + public void unregisterForIncomingRing(Handler h) { + mActivePhone.unregisterForIncomingRing(h); + } + + public void registerForDisconnect(Handler h, int what, Object obj) { + mActivePhone.registerForDisconnect(h, what, obj); + } + + public void unregisterForDisconnect(Handler h) { + mActivePhone.unregisterForDisconnect(h); + } + + public void registerForMmiInitiate(Handler h, int what, Object obj) { + mActivePhone.registerForMmiInitiate(h, what, obj); + } + + public void unregisterForMmiInitiate(Handler h) { + mActivePhone.unregisterForMmiInitiate(h); + } + + public void registerForMmiComplete(Handler h, int what, Object obj) { + mActivePhone.registerForMmiComplete(h, what, obj); + } + + public void unregisterForMmiComplete(Handler h) { + mActivePhone.unregisterForMmiComplete(h); + } + + public List getPendingMmiCodes() { + return mActivePhone.getPendingMmiCodes(); + } + + public void sendUssdResponse(String ussdMessge) { + mActivePhone.sendUssdResponse(ussdMessge); + } + + public void registerForServiceStateChanged(Handler h, int what, Object obj) { + mActivePhone.registerForServiceStateChanged(h, what, obj); + } + + public void unregisterForServiceStateChanged(Handler h) { + mActivePhone.unregisterForServiceStateChanged(h); + } + + public void registerForSuppServiceNotification(Handler h, int what, Object obj) { + mActivePhone.registerForSuppServiceNotification(h, what, obj); + } + + public void unregisterForSuppServiceNotification(Handler h) { + mActivePhone.unregisterForSuppServiceNotification(h); + } + + public void registerForSuppServiceFailed(Handler h, int what, Object obj) { + mActivePhone.registerForSuppServiceFailed(h, what, obj); + } + + public void unregisterForSuppServiceFailed(Handler h) { + mActivePhone.unregisterForSuppServiceFailed(h); + } + + public void registerForInCallVoicePrivacyOn(Handler h, int what, Object obj){ + mActivePhone.registerForInCallVoicePrivacyOn(h,what,obj); + } + + public void unregisterForInCallVoicePrivacyOn(Handler h){ + mActivePhone.unregisterForInCallVoicePrivacyOn(h); + } + + public void registerForInCallVoicePrivacyOff(Handler h, int what, Object obj){ + mActivePhone.registerForInCallVoicePrivacyOff(h,what,obj); + } + + public void unregisterForInCallVoicePrivacyOff(Handler h){ + mActivePhone.unregisterForInCallVoicePrivacyOff(h); + } + + public void registerForCdmaOtaStatusChange(Handler h, int what, Object obj) { + mActivePhone.registerForCdmaOtaStatusChange(h,what,obj); + } + + public void unregisterForCdmaOtaStatusChange(Handler h) { + mActivePhone.unregisterForCdmaOtaStatusChange(h); + } + + public void registerForSubscriptionInfoReady(Handler h, int what, Object obj) { + mActivePhone.registerForSubscriptionInfoReady(h, what, obj); + } + + public void unregisterForSubscriptionInfoReady(Handler h) { + mActivePhone.unregisterForSubscriptionInfoReady(h); + } + + public void registerForEcmTimerReset(Handler h, int what, Object obj) { + mActivePhone.registerForEcmTimerReset(h,what,obj); + } + + public void unregisterForEcmTimerReset(Handler h) { + mActivePhone.unregisterForEcmTimerReset(h); + } + + public void registerForRingbackTone(Handler h, int what, Object obj) { + mActivePhone.registerForRingbackTone(h,what,obj); + } + + public void unregisterForRingbackTone(Handler h) { + mActivePhone.unregisterForRingbackTone(h); + } + + public void registerForResendIncallMute(Handler h, int what, Object obj) { + mActivePhone.registerForResendIncallMute(h,what,obj); + } + + public void unregisterForResendIncallMute(Handler h) { + mActivePhone.unregisterForResendIncallMute(h); + } + + public boolean getIccRecordsLoaded() { + return mActivePhone.getIccRecordsLoaded(); + } + + public IccCard getIccCard() { + return mActivePhone.getIccCard(); + } + + public void acceptCall() throws CallStateException { + mActivePhone.acceptCall(); + } + + public void rejectCall() throws CallStateException { + mActivePhone.rejectCall(); + } + + public void switchHoldingAndActive() throws CallStateException { + mActivePhone.switchHoldingAndActive(); + } + + public boolean canConference() { + return mActivePhone.canConference(); + } + + public void conference() throws CallStateException { + mActivePhone.conference(); + } + + public void enableEnhancedVoicePrivacy(boolean enable, Message onComplete) { + mActivePhone.enableEnhancedVoicePrivacy(enable, onComplete); + } + + public void getEnhancedVoicePrivacy(Message onComplete) { + mActivePhone.getEnhancedVoicePrivacy(onComplete); + } + + public boolean canTransfer() { + return mActivePhone.canTransfer(); + } + + public void explicitCallTransfer() throws CallStateException { + mActivePhone.explicitCallTransfer(); + } + + public void clearDisconnected() { + mActivePhone.clearDisconnected(); + } + + public Connection dial(String dialString) throws CallStateException { + return mActivePhone.dial(dialString); + } + + public Connection dial(String dialString, UUSInfo uusInfo) throws CallStateException { + return mActivePhone.dial(dialString); + } + + public boolean handlePinMmi(String dialString) { + return mActivePhone.handlePinMmi(dialString); + } + + public boolean handleInCallMmiCommands(String command) throws CallStateException { + return mActivePhone.handleInCallMmiCommands(command); + } + + public void sendDtmf(char c) { + mActivePhone.sendDtmf(c); + } + + public void startDtmf(char c) { + mActivePhone.startDtmf(c); + } + + public void stopDtmf() { + mActivePhone.stopDtmf(); + } + + public void setRadioPower(boolean power) { + mActivePhone.setRadioPower(power); + } + + public boolean getMessageWaitingIndicator() { + return mActivePhone.getMessageWaitingIndicator(); + } + + public boolean getCallForwardingIndicator() { + return mActivePhone.getCallForwardingIndicator(); + } + + public String getLine1Number() { + return mActivePhone.getLine1Number(); + } + + public String getCdmaMin() { + return mActivePhone.getCdmaMin(); + } + + public boolean isMinInfoReady() { + return mActivePhone.isMinInfoReady(); + } + + public String getCdmaPrlVersion() { + return mActivePhone.getCdmaPrlVersion(); + } + + public String getLine1AlphaTag() { + return mActivePhone.getLine1AlphaTag(); + } + + public void setLine1Number(String alphaTag, String number, Message onComplete) { + mActivePhone.setLine1Number(alphaTag, number, onComplete); + } + + public String getVoiceMailNumber() { + return mActivePhone.getVoiceMailNumber(); + } + + /** @hide */ + public int getVoiceMessageCount(){ + return mActivePhone.getVoiceMessageCount(); + } + + public String getVoiceMailAlphaTag() { + return mActivePhone.getVoiceMailAlphaTag(); + } + + public void setVoiceMailNumber(String alphaTag,String voiceMailNumber, + Message onComplete) { + mActivePhone.setVoiceMailNumber(alphaTag, voiceMailNumber, onComplete); + } + + public void getCallForwardingOption(int commandInterfaceCFReason, + Message onComplete) { + mActivePhone.getCallForwardingOption(commandInterfaceCFReason, + onComplete); + } + + public void setCallForwardingOption(int commandInterfaceCFReason, + int commandInterfaceCFAction, String dialingNumber, + int timerSeconds, Message onComplete) { + mActivePhone.setCallForwardingOption(commandInterfaceCFReason, + commandInterfaceCFAction, dialingNumber, timerSeconds, onComplete); + } + + public void getOutgoingCallerIdDisplay(Message onComplete) { + mActivePhone.getOutgoingCallerIdDisplay(onComplete); + } + + public void setOutgoingCallerIdDisplay(int commandInterfaceCLIRMode, + Message onComplete) { + mActivePhone.setOutgoingCallerIdDisplay(commandInterfaceCLIRMode, + onComplete); + } + + public void getCallWaiting(Message onComplete) { + mActivePhone.getCallWaiting(onComplete); + } + + public void setCallWaiting(boolean enable, Message onComplete) { + mActivePhone.setCallWaiting(enable, onComplete); + } + + public void getAvailableNetworks(Message response) { + mActivePhone.getAvailableNetworks(response); + } + + public void setNetworkSelectionModeAutomatic(Message response) { + mActivePhone.setNetworkSelectionModeAutomatic(response); + } + + public void selectNetworkManually(NetworkInfo network, Message response) { + mActivePhone.selectNetworkManually(network, response); + } + + public void setPreferredNetworkType(int networkType, Message response) { + mActivePhone.setPreferredNetworkType(networkType, response); + } + + public void getPreferredNetworkType(Message response) { + mActivePhone.getPreferredNetworkType(response); + } + + public void getNeighboringCids(Message response) { + mActivePhone.getNeighboringCids(response); + } + + public void setOnPostDialCharacter(Handler h, int what, Object obj) { + mActivePhone.setOnPostDialCharacter(h, what, obj); + } + + public void setMute(boolean muted) { + mActivePhone.setMute(muted); + } + + public boolean getMute() { + return mActivePhone.getMute(); + } + + public void invokeOemRilRequestRaw(byte[] data, Message response) { + mActivePhone.invokeOemRilRequestRaw(data, response); + } + + public void invokeOemRilRequestStrings(String[] strings, Message response) { + mActivePhone.invokeOemRilRequestStrings(strings, response); + } + + public void getDataCallList(Message response) { + mActivePhone.getDataCallList(response); + } + + public List getCurrentDataConnectionList() { + return mActivePhone.getCurrentDataConnectionList(); + } + + public void updateServiceLocation() { + mActivePhone.updateServiceLocation(); + } + + public void enableLocationUpdates() { + mActivePhone.enableLocationUpdates(); + } + + public void disableLocationUpdates() { + mActivePhone.disableLocationUpdates(); + } + + public void setUnitTestMode(boolean f) { + mActivePhone.setUnitTestMode(f); + } + + public boolean getUnitTestMode() { + return mActivePhone.getUnitTestMode(); + } + + public void setBandMode(int bandMode, Message response) { + mActivePhone.setBandMode(bandMode, response); + } + + public void queryAvailableBandMode(Message response) { + mActivePhone.queryAvailableBandMode(response); + } + + public boolean getDataRoamingEnabled() { + return mActivePhone.getDataRoamingEnabled(); + } + + public void setDataRoamingEnabled(boolean enable) { + mActivePhone.setDataRoamingEnabled(enable); + } + + public void queryCdmaRoamingPreference(Message response) { + mActivePhone.queryCdmaRoamingPreference(response); + } + + public void setCdmaRoamingPreference(int cdmaRoamingType, Message response) { + mActivePhone.setCdmaRoamingPreference(cdmaRoamingType, response); + } + + public void setCdmaSubscription(int cdmaSubscriptionType, Message response) { + mActivePhone.setCdmaSubscription(cdmaSubscriptionType, response); + } + + public SimulatedRadioControl getSimulatedRadioControl() { + return mActivePhone.getSimulatedRadioControl(); + } + + public boolean enableDataConnectivity() { + return mActivePhone.enableDataConnectivity(); + } + + public boolean disableDataConnectivity() { + return mActivePhone.disableDataConnectivity(); + } + + public int enableApnType(String type) { + return mActivePhone.enableApnType(type); + } + + public int disableApnType(String type) { + return mActivePhone.disableApnType(type); + } + + public boolean isDataConnectivityEnabled() { + return mActivePhone.isDataConnectivityEnabled(); + } + + public boolean isDataConnectivityPossible() { + return mActivePhone.isDataConnectivityPossible(); + } + + public String getInterfaceName(String apnType) { + return mActivePhone.getInterfaceName(apnType); + } + + public String getIpAddress(String apnType) { + return mActivePhone.getIpAddress(apnType); + } + + public String getGateway(String apnType) { + return mActivePhone.getGateway(apnType); + } + + public String[] getDnsServers(String apnType) { + return mActivePhone.getDnsServers(apnType); + } + + public String getDeviceId() { + return mActivePhone.getDeviceId(); + } + + public String getDeviceSvn() { + return mActivePhone.getDeviceSvn(); + } + + public String getSubscriberId() { + return mActivePhone.getSubscriberId(); + } + + public String getIccSerialNumber() { + return mActivePhone.getIccSerialNumber(); + } + + public String getEsn() { + return mActivePhone.getEsn(); + } + + public String getMeid() { + return mActivePhone.getMeid(); + } + + public PhoneSubInfo getPhoneSubInfo(){ + return mActivePhone.getPhoneSubInfo(); + } + + public IccSmsInterfaceManager getIccSmsInterfaceManager(){ + return mActivePhone.getIccSmsInterfaceManager(); + } + + public IccPhoneBookInterfaceManager getIccPhoneBookInterfaceManager(){ + return mActivePhone.getIccPhoneBookInterfaceManager(); + } + + public void setTTYMode(int ttyMode, Message onComplete) { + mActivePhone.setTTYMode(ttyMode, onComplete); + } + + public void queryTTYMode(Message onComplete) { + mActivePhone.queryTTYMode(onComplete); + } + + public void activateCellBroadcastSms(int activate, Message response) { + mActivePhone.activateCellBroadcastSms(activate, response); + } + + public void getCellBroadcastSmsConfig(Message response) { + mActivePhone.getCellBroadcastSmsConfig(response); + } + + public void setCellBroadcastSmsConfig(int[] configValuesArray, Message response) { + mActivePhone.setCellBroadcastSmsConfig(configValuesArray, response); + } + + public void notifyDataActivity() { + mActivePhone.notifyDataActivity(); + } + + public void getSmscAddress(Message result) { + mActivePhone.getSmscAddress(result); + } + + public void setSmscAddress(String address, Message result) { + mActivePhone.setSmscAddress(address, result); + } + + public int getCdmaEriIconIndex() { + return mActivePhone.getCdmaEriIconIndex(); + } + + public String getCdmaEriText() { + return mActivePhone.getCdmaEriText(); + } + + public int getCdmaEriIconMode() { + return mActivePhone.getCdmaEriIconMode(); + } + + public void sendBurstDtmf(String dtmfString, int on, int off, Message onComplete){ + mActivePhone.sendBurstDtmf(dtmfString, on, off, onComplete); + } + + public void exitEmergencyCallbackMode(){ + mActivePhone.exitEmergencyCallbackMode(); + } + + public boolean isOtaSpNumber(String dialStr){ + return mActivePhone.isOtaSpNumber(dialStr); + } + + public void registerForCallWaiting(Handler h, int what, Object obj){ + mActivePhone.registerForCallWaiting(h,what,obj); + } + + public void unregisterForCallWaiting(Handler h){ + mActivePhone.unregisterForCallWaiting(h); + } + + public void registerForSignalInfo(Handler h, int what, Object obj) { + mActivePhone.registerForSignalInfo(h,what,obj); + } + + public void unregisterForSignalInfo(Handler h) { + mActivePhone.unregisterForSignalInfo(h); + } + + public void registerForDisplayInfo(Handler h, int what, Object obj) { + mActivePhone.registerForDisplayInfo(h,what,obj); + } + + public void unregisterForDisplayInfo(Handler h) { + mActivePhone.unregisterForDisplayInfo(h); + } + + public void registerForNumberInfo(Handler h, int what, Object obj) { + mActivePhone.registerForNumberInfo(h, what, obj); + } + + public void unregisterForNumberInfo(Handler h) { + mActivePhone.unregisterForNumberInfo(h); + } + + public void registerForRedirectedNumberInfo(Handler h, int what, Object obj) { + mActivePhone.registerForRedirectedNumberInfo(h, what, obj); + } + + public void unregisterForRedirectedNumberInfo(Handler h) { + mActivePhone.unregisterForRedirectedNumberInfo(h); + } + + public void registerForLineControlInfo(Handler h, int what, Object obj) { + mActivePhone.registerForLineControlInfo( h, what, obj); + } + + public void unregisterForLineControlInfo(Handler h) { + mActivePhone.unregisterForLineControlInfo(h); + } + + public void registerFoT53ClirlInfo(Handler h, int what, Object obj) { + mActivePhone.registerFoT53ClirlInfo(h, what, obj); + } + + public void unregisterForT53ClirInfo(Handler h) { + mActivePhone.unregisterForT53ClirInfo(h); + } + + public void registerForT53AudioControlInfo(Handler h, int what, Object obj) { + mActivePhone.registerForT53AudioControlInfo( h, what, obj); + } + + public void unregisterForT53AudioControlInfo(Handler h) { + mActivePhone.unregisterForT53AudioControlInfo(h); + } + + public void setOnEcbModeExitResponse(Handler h, int what, Object obj){ + mActivePhone.setOnEcbModeExitResponse(h,what,obj); + } + + public void unsetOnEcbModeExitResponse(Handler h){ + mActivePhone.unsetOnEcbModeExitResponse(h); + } +} -- cgit v1.1