summaryrefslogtreecommitdiffstats
path: root/telephony/java
diff options
context:
space:
mode:
Diffstat (limited to 'telephony/java')
-rw-r--r--telephony/java/android/telephony/PhoneNumberUtils.java13
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java21
-rw-r--r--telephony/java/android/telephony/gsm/GsmCellLocation.java28
-rw-r--r--telephony/java/android/telephony/gsm/SmsMessage.java12
-rw-r--r--telephony/java/com/android/internal/telephony/CallManager.java1371
-rw-r--r--telephony/java/com/android/internal/telephony/DefaultPhoneNotifier.java3
-rw-r--r--telephony/java/com/android/internal/telephony/GsmAlphabet.java2
-rw-r--r--telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl5
-rw-r--r--telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl3
-rw-r--r--telephony/java/com/android/internal/telephony/Phone.java2
-rw-r--r--telephony/java/com/android/internal/telephony/PhoneFactory.java11
-rw-r--r--telephony/java/com/android/internal/telephony/PhoneSubInfo.java21
-rw-r--r--telephony/java/com/android/internal/telephony/PhoneSubInfoProxy.java7
-rw-r--r--telephony/java/com/android/internal/telephony/RILConstants.java1
-rw-r--r--telephony/java/com/android/internal/telephony/SMSDispatcher.java73
-rw-r--r--telephony/java/com/android/internal/telephony/SipPhoneNotifier.java237
-rwxr-xr-xtelephony/java/com/android/internal/telephony/cdma/CDMAPhone.java9
-rw-r--r--telephony/java/com/android/internal/telephony/cdma/CdmaCall.java3
-rw-r--r--telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java21
-rwxr-xr-xtelephony/java/com/android/internal/telephony/cdma/SmsMessage.java6
-rw-r--r--telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java15
-rw-r--r--telephony/java/com/android/internal/telephony/gsm/GsmCall.java3
-rw-r--r--telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java3
-rw-r--r--telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java13
-rw-r--r--telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java7
-rw-r--r--telephony/java/com/android/internal/telephony/gsm/SmsMessage.java14
-rw-r--r--telephony/java/com/android/internal/telephony/sip/CallFailCause.java50
-rw-r--r--telephony/java/com/android/internal/telephony/sip/CallProxy.java114
-rw-r--r--telephony/java/com/android/internal/telephony/sip/SipCallBase.java103
-rw-r--r--telephony/java/com/android/internal/telephony/sip/SipCommandInterface.java373
-rw-r--r--telephony/java/com/android/internal/telephony/sip/SipConnectionBase.java248
-rwxr-xr-xtelephony/java/com/android/internal/telephony/sip/SipPhone.java823
-rwxr-xr-xtelephony/java/com/android/internal/telephony/sip/SipPhoneBase.java554
-rw-r--r--telephony/java/com/android/internal/telephony/sip/SipPhoneFactory.java49
-rw-r--r--telephony/java/com/android/internal/telephony/test/SimulatedCommands.java1
35 files changed, 4116 insertions, 103 deletions
diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java
index 1d3ad81..7829006 100644
--- a/telephony/java/android/telephony/PhoneNumberUtils.java
+++ b/telephony/java/android/telephony/PhoneNumberUtils.java
@@ -141,9 +141,9 @@ public class PhoneNumberUtils
}
// TODO: We don't check for SecurityException here (requires
- // READ_PHONE_STATE permission).
+ // CALL_PRIVILEGED permission).
if (scheme.equals("voicemail")) {
- return TelephonyManager.getDefault().getVoiceMailNumber();
+ return TelephonyManager.getDefault().getCompleteVoiceMailNumber();
}
if (context == null) {
@@ -1692,6 +1692,15 @@ public class PhoneNumberUtils
}
/**
+ * @hide
+ * @param number
+ * @return true if number contains @
+ */
+ public static boolean isUriNumber(String number) {
+ return number != null && number.contains("@");
+ }
+
+ /**
* This function handles the plus code conversion within NANP CDMA network
* If the number format is
* 1)+1NANP,remove +,
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index ab63017..27e08d4 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -177,7 +177,7 @@ public class TelephonyManager {
/**
* Returns the unique device ID, for example, the IMEI for GSM and the MEID
- * for CDMA phones. Return null if device ID is not available.
+ * or ESN for CDMA phones. Return null if device ID is not available.
*
* <p>Requires Permission:
* {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
@@ -665,6 +665,25 @@ public class TelephonyManager {
}
/**
+ * Returns the complete voice mail number. Return null if it is unavailable.
+ * <p>
+ * Requires Permission:
+ * {@link android.Manifest.permission#CALL_PRIVILEGED CALL_PRIVILEGED}
+ *
+ * @hide
+ */
+ public String getCompleteVoiceMailNumber() {
+ try {
+ return getSubscriberInfo().getCompleteVoiceMailNumber();
+ } catch (RemoteException ex) {
+ return null;
+ } catch (NullPointerException ex) {
+ // This could happen before phone restarts due to crashing
+ return null;
+ }
+ }
+
+ /**
* Returns the voice mail count. Return 0 if unavailable.
* <p>
* Requires Permission:
diff --git a/telephony/java/android/telephony/gsm/GsmCellLocation.java b/telephony/java/android/telephony/gsm/GsmCellLocation.java
index fa1f985..c4204fa 100644
--- a/telephony/java/android/telephony/gsm/GsmCellLocation.java
+++ b/telephony/java/android/telephony/gsm/GsmCellLocation.java
@@ -25,6 +25,7 @@ import android.telephony.CellLocation;
public class GsmCellLocation extends CellLocation {
private int mLac;
private int mCid;
+ private int mPsc;
/**
* Empty constructor. Initializes the LAC and CID to -1.
@@ -32,6 +33,7 @@ public class GsmCellLocation extends CellLocation {
public GsmCellLocation() {
mLac = -1;
mCid = -1;
+ mPsc = -1;
}
/**
@@ -40,6 +42,7 @@ public class GsmCellLocation extends CellLocation {
public GsmCellLocation(Bundle bundle) {
mLac = bundle.getInt("lac", mLac);
mCid = bundle.getInt("cid", mCid);
+ mPsc = bundle.getInt("psc", mPsc);
}
/**
@@ -57,11 +60,20 @@ public class GsmCellLocation extends CellLocation {
}
/**
+ * @return primary scrambling code for UMTS, -1 if unknown or GSM
+ * @hide
+ */
+ public int getPsc() {
+ return mPsc;
+ }
+
+ /**
* Invalidate this object. The location area code and the cell id are set to -1.
*/
public void setStateInvalid() {
mLac = -1;
mCid = -1;
+ mPsc = -1;
}
/**
@@ -72,6 +84,14 @@ public class GsmCellLocation extends CellLocation {
mCid = cid;
}
+ /**
+ * Set the primary scrambling code.
+ * @hide
+ */
+ public void setPsc(int psc) {
+ mPsc = psc;
+ }
+
@Override
public int hashCode() {
return mLac ^ mCid;
@@ -91,12 +111,13 @@ public class GsmCellLocation extends CellLocation {
return false;
}
- return equalsHandlesNulls(mLac, s.mLac) && equalsHandlesNulls(mCid, s.mCid);
+ return equalsHandlesNulls(mLac, s.mLac) && equalsHandlesNulls(mCid, s.mCid)
+ && equalsHandlesNulls(mPsc, s.mPsc);
}
@Override
public String toString() {
- return "["+ mLac + "," + mCid + "]";
+ return "["+ mLac + "," + mCid + "," + mPsc + "]";
}
/**
@@ -118,12 +139,13 @@ public class GsmCellLocation extends CellLocation {
public void fillInNotifierBundle(Bundle m) {
m.putInt("lac", mLac);
m.putInt("cid", mCid);
+ m.putInt("psc", mPsc);
}
/**
* @hide
*/
public boolean isEmpty() {
- return (mLac == -1 && mCid == -1);
+ return (mLac == -1 && mCid == -1 && mPsc == -1);
}
}
diff --git a/telephony/java/android/telephony/gsm/SmsMessage.java b/telephony/java/android/telephony/gsm/SmsMessage.java
index 37ef912..0c63c37 100644
--- a/telephony/java/android/telephony/gsm/SmsMessage.java
+++ b/telephony/java/android/telephony/gsm/SmsMessage.java
@@ -304,9 +304,9 @@ public class SmsMessage {
int septets = GsmAlphabet.countGsmSeptets(messageBody, !use7bitOnly);
ret[1] = septets;
if (septets > MAX_USER_DATA_SEPTETS) {
- ret[0] = (septets / MAX_USER_DATA_SEPTETS_WITH_HEADER) + 1;
- ret[2] = MAX_USER_DATA_SEPTETS_WITH_HEADER
- - (septets % MAX_USER_DATA_SEPTETS_WITH_HEADER);
+ ret[0] = (septets + (MAX_USER_DATA_SEPTETS_WITH_HEADER - 1)) /
+ MAX_USER_DATA_SEPTETS_WITH_HEADER;
+ ret[2] = (ret[0] * MAX_USER_DATA_SEPTETS_WITH_HEADER) - septets;
} else {
ret[0] = 1;
ret[2] = MAX_USER_DATA_SEPTETS - septets;
@@ -318,9 +318,9 @@ public class SmsMessage {
ret[1] = messageBody.length();
if (octets > MAX_USER_DATA_BYTES) {
// 6 is the size of the user data header
- ret[0] = (octets / MAX_USER_DATA_BYTES_WITH_HEADER) + 1;
- ret[2] = (MAX_USER_DATA_BYTES_WITH_HEADER
- - (octets % MAX_USER_DATA_BYTES_WITH_HEADER))/2;
+ ret[0] = (octets + (MAX_USER_DATA_BYTES_WITH_HEADER - 1)) /
+ MAX_USER_DATA_BYTES_WITH_HEADER;
+ ret[2] = ((ret[0] * MAX_USER_DATA_BYTES_WITH_HEADER) - octets) / 2;
} else {
ret[0] = 1;
ret[2] = (MAX_USER_DATA_BYTES - octets)/2;
diff --git a/telephony/java/com/android/internal/telephony/CallManager.java b/telephony/java/com/android/internal/telephony/CallManager.java
new file mode 100644
index 0000000..12df44e
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/CallManager.java
@@ -0,0 +1,1371 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import com.android.internal.telephony.sip.SipPhone;
+
+import android.content.Context;
+import android.media.AudioManager;
+import android.os.AsyncResult;
+import android.os.Handler;
+import android.os.Message;
+import android.os.RegistrantList;
+import android.telephony.PhoneStateListener;
+import android.telephony.ServiceState;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+
+
+/**
+ * @hide
+ *
+ * CallManager class provides an abstract layer for PhoneApp to access
+ * and control calls. It implements Phone interface.
+ *
+ * CallManager provides call and connection control as well as
+ * channel capability.
+ *
+ * There are three categories of APIs CallManager provided
+ *
+ * 1. Call control and operation, such as dial() and hangup()
+ * 2. Channel capabilities, such as CanConference()
+ * 3. Register notification
+ *
+ *
+ */
+public final class CallManager {
+
+ private static final String LOG_TAG ="Phone";
+ private static final boolean LOCAL_DEBUG = true;
+
+ private static final int EVENT_DISCONNECT = 100;
+ private static final int EVENT_PRECISE_CALL_STATE_CHANGED = 101;
+ private static final int EVENT_NEW_RINGING_CONNECTION = 102;
+ private static final int EVENT_UNKNOWN_CONNECTION = 103;
+ private static final int EVENT_INCOMING_RING = 104;
+ private static final int EVENT_RINGBACK_TONE = 105;
+ private static final int EVENT_IN_CALL_VOICE_PRIVACY_ON = 106;
+ private static final int EVENT_IN_CALL_VOICE_PRIVACY_OFF = 107;
+ private static final int EVENT_CALL_WAITING = 108;
+ private static final int EVENT_DISPLAY_INFO = 109;
+ private static final int EVENT_SIGNAL_INFO = 110;
+ private static final int EVENT_CDMA_OTA_STATUS_CHANGE = 111;
+ private static final int EVENT_RESEND_INCALL_MUTE = 112;
+ private static final int EVENT_MMI_INITIATE = 113;
+ private static final int EVENT_MMI_COMPLETE = 114;
+ private static final int EVENT_ECM_TIMER_RESET = 115;
+ private static final int EVENT_SUBSCRIPTION_INFO_READY = 116;
+ private static final int EVENT_SUPP_SERVICE_FAILED = 117;
+
+ // Singleton instance
+ private static final CallManager INSTANCE = new CallManager();
+
+ // list of registered phones
+ private final ArrayList<Phone> mPhones;
+
+ // list of supported ringing calls
+ private final ArrayList<Call> mRingingCalls;
+
+ // list of supported background calls
+ private final ArrayList<Call> mBackgroundCalls;
+
+ // list of supported foreground calls
+ private final ArrayList<Call> mForegroundCalls;
+
+ // empty connection list
+ private final ArrayList<Connection> emptyConnections = new ArrayList<Connection>();
+
+ // default phone as the first phone registered
+ private Phone mDefaultPhone;
+
+ // state registrants
+ protected final RegistrantList mPreciseCallStateRegistrants
+ = new RegistrantList();
+
+ protected final RegistrantList mNewRingingConnectionRegistrants
+ = new RegistrantList();
+
+ protected final RegistrantList mIncomingRingRegistrants
+ = new RegistrantList();
+
+ protected final RegistrantList mDisconnectRegistrants
+ = new RegistrantList();
+
+ protected final RegistrantList mServiceStateRegistrants
+ = new RegistrantList();
+
+ protected final RegistrantList mMmiRegistrants
+ = new RegistrantList();
+
+ protected final RegistrantList mUnknownConnectionRegistrants
+ = new RegistrantList();
+
+ protected final RegistrantList mRingbackToneRegistrants
+ = new RegistrantList();
+
+ protected final RegistrantList mInCallVoicePrivacyOnRegistrants
+ = new RegistrantList();
+
+ protected final RegistrantList mInCallVoicePrivacyOffRegistrants
+ = new RegistrantList();
+
+ protected final RegistrantList mCallWaitingRegistrants
+ = new RegistrantList();
+
+ protected final RegistrantList mDisplayInfoRegistrants
+ = new RegistrantList();
+
+ protected final RegistrantList mSignalInfoRegistrants
+ = new RegistrantList();
+
+ protected final RegistrantList mCdmaOtaStatusChangeRegistrants
+ = new RegistrantList();
+
+ protected final RegistrantList mResendIncallMuteRegistrants
+ = new RegistrantList();
+
+ protected final RegistrantList mMmiInitiateRegistrants
+ = new RegistrantList();
+
+ protected final RegistrantList mMmiCompleteRegistrants
+ = new RegistrantList();
+
+ protected final RegistrantList mEcmTimerResetRegistrants
+ = new RegistrantList();
+
+ protected final RegistrantList mSubscriptionInfoReadyRegistrants
+ = new RegistrantList();
+
+ protected final RegistrantList mSuppServiceFailedRegistrants
+ = new RegistrantList();
+
+ private CallManager() {
+ mPhones = new ArrayList<Phone>();
+ mRingingCalls = new ArrayList<Call>();
+ mBackgroundCalls = new ArrayList<Call>();
+ mForegroundCalls = new ArrayList<Call>();
+ mDefaultPhone = null;
+ }
+
+ /**
+ * get singleton instance of CallManager
+ * @return CallManager
+ */
+ public static CallManager getInstance() {
+ return INSTANCE;
+ }
+
+ /**
+ * Returns all the registered phone objects.
+ * @return all the registered phone objects.
+ */
+ public List<Phone> getAllPhones() {
+ return Collections.unmodifiableList(mPhones);
+ }
+
+ /**
+ * Get current coarse-grained voice call state.
+ * If the Call Manager has an active call and call waiting occurs,
+ * then the phone state is RINGING not OFFHOOK
+ *
+ */
+ public Phone.State getState() {
+ Phone.State s = Phone.State.IDLE;
+
+ for (Phone phone : mPhones) {
+ if (phone.getState() == Phone.State.RINGING) {
+ return Phone.State.RINGING;
+ } else if (phone.getState() == Phone.State.OFFHOOK) {
+ s = Phone.State.OFFHOOK;
+ }
+ }
+ return s;
+ }
+
+ /**
+ * @return the service state of CallManager, which represents the
+ * highest priority state of all the service states of phones
+ *
+ * The priority is defined as
+ *
+ * STATE_IN_SERIVCE > STATE_OUT_OF_SERIVCE > STATE_EMERGENCY > STATE_POWER_OFF
+ *
+ */
+
+ public int getServiceState() {
+ int resultState = ServiceState.STATE_OUT_OF_SERVICE;
+
+ for (Phone phone : mPhones) {
+ int serviceState = phone.getServiceState().getState();
+ if (serviceState == ServiceState.STATE_IN_SERVICE) {
+ // IN_SERVICE has the highest priority
+ resultState = serviceState;
+ break;
+ } else if (serviceState == ServiceState.STATE_OUT_OF_SERVICE) {
+ // OUT_OF_SERVICE replaces EMERGENCY_ONLY and POWER_OFF
+ // Note: EMERGENCY_ONLY is not in use at this moment
+ if ( resultState == ServiceState.STATE_EMERGENCY_ONLY ||
+ resultState == ServiceState.STATE_POWER_OFF) {
+ resultState = serviceState;
+ }
+ } else if (serviceState == ServiceState.STATE_EMERGENCY_ONLY) {
+ if (resultState == ServiceState.STATE_POWER_OFF) {
+ resultState = serviceState;
+ }
+ }
+ }
+ return resultState;
+ }
+
+ /**
+ * Register phone to CallManager
+ * @param phone
+ * @return true if register successfully
+ */
+ public boolean registerPhone(Phone phone) {
+ if (phone != null && !mPhones.contains(phone)) {
+ if (mPhones.isEmpty()) {
+ mDefaultPhone = phone;
+ }
+ mPhones.add(phone);
+ mRingingCalls.add(phone.getRingingCall());
+ mBackgroundCalls.add(phone.getBackgroundCall());
+ mForegroundCalls.add(phone.getForegroundCall());
+ registerForPhoneStates(phone);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * return the default phone or null if no phone available
+ */
+ public Phone getDefaultPhone() {
+ return mDefaultPhone;
+ }
+
+ /**
+ * @return the phone associated with the foreground call
+ */
+ public Phone getFgPhone() {
+ return getActiveFgCall().getPhone();
+ }
+
+ /**
+ * @return the phone associated with the background call
+ */
+ public Phone getBgPhone() {
+ return getFirstActiveBgCall().getPhone();
+ }
+
+ /**
+ * @return the phone associated with the ringing call
+ */
+ public Phone getRingingPhone() {
+ return getFirstActiveRingingCall().getPhone();
+ }
+
+ /**
+ * unregister phone from CallManager
+ * @param phone
+ */
+ public void unregisterPhone(Phone phone) {
+ if (phone != null && mPhones.contains(phone)) {
+ mPhones.remove(phone);
+ mRingingCalls.remove(phone.getRingingCall());
+ mBackgroundCalls.remove(phone.getBackgroundCall());
+ mForegroundCalls.remove(phone.getForegroundCall());
+ unregisterForPhoneStates(phone);
+ if (phone == mDefaultPhone) {
+ if (mPhones.isEmpty()) {
+ mDefaultPhone = null;
+ } else {
+ mDefaultPhone = mPhones.get(0);
+ }
+ }
+ }
+ }
+
+ public void setAudioMode() {
+ Context context = getContext();
+ if (context == null) return;
+ AudioManager audioManager = (AudioManager)
+ context.getSystemService(Context.AUDIO_SERVICE);
+
+ int mode = AudioManager.MODE_NORMAL;
+ switch (getState()) {
+ case RINGING:
+ mode = AudioManager.MODE_RINGTONE;
+ break;
+ case OFFHOOK:
+ Phone fgPhone = getFgPhone();
+ if (!(fgPhone instanceof SipPhone)) {
+ mode = AudioManager.MODE_IN_CALL;
+ }
+ break;
+ }
+ // calling audioManager.setMode() multiple times in a short period of
+ // time seems to break the audio recorder in in-call mode
+ if (audioManager.getMode() != mode) audioManager.setMode(mode);
+ }
+
+ private Context getContext() {
+ Phone defaultPhone = getDefaultPhone();
+ return ((defaultPhone == null) ? null : defaultPhone.getContext());
+ }
+
+ private void registerForPhoneStates(Phone phone) {
+ phone.registerForPreciseCallStateChanged(mHandler, EVENT_PRECISE_CALL_STATE_CHANGED, null);
+ phone.registerForDisconnect(mHandler, EVENT_DISCONNECT, null);
+ phone.registerForNewRingingConnection(mHandler, EVENT_NEW_RINGING_CONNECTION, null);
+ phone.registerForUnknownConnection(mHandler, EVENT_UNKNOWN_CONNECTION, null);
+ phone.registerForIncomingRing(mHandler, EVENT_INCOMING_RING, null);
+ phone.registerForRingbackTone(mHandler, EVENT_RINGBACK_TONE, null);
+ phone.registerForInCallVoicePrivacyOn(mHandler, EVENT_IN_CALL_VOICE_PRIVACY_ON, null);
+ phone.registerForInCallVoicePrivacyOff(mHandler, EVENT_IN_CALL_VOICE_PRIVACY_OFF, null);
+ phone.registerForCallWaiting(mHandler, EVENT_CALL_WAITING, null);
+ phone.registerForDisplayInfo(mHandler, EVENT_DISPLAY_INFO, null);
+ phone.registerForSignalInfo(mHandler, EVENT_SIGNAL_INFO, null);
+ phone.registerForCdmaOtaStatusChange(mHandler, EVENT_CDMA_OTA_STATUS_CHANGE, null);
+ phone.registerForResendIncallMute(mHandler, EVENT_RESEND_INCALL_MUTE, null);
+ phone.registerForMmiInitiate(mHandler, EVENT_MMI_INITIATE, null);
+ phone.registerForMmiComplete(mHandler, EVENT_MMI_COMPLETE, null);
+ phone.registerForEcmTimerReset(mHandler, EVENT_ECM_TIMER_RESET, null);
+ phone.registerForSubscriptionInfoReady(mHandler, EVENT_SUBSCRIPTION_INFO_READY, null);
+ phone.registerForSuppServiceFailed(mHandler, EVENT_SUPP_SERVICE_FAILED, null);
+ }
+
+ private void unregisterForPhoneStates(Phone phone) {
+ phone.unregisterForPreciseCallStateChanged(mHandler);
+ phone.unregisterForDisconnect(mHandler);
+ phone.unregisterForNewRingingConnection(mHandler);
+ phone.unregisterForUnknownConnection(mHandler);
+ phone.unregisterForIncomingRing(mHandler);
+ phone.unregisterForRingbackTone(mHandler);
+ phone.unregisterForInCallVoicePrivacyOn(mHandler);
+ phone.unregisterForInCallVoicePrivacyOff(mHandler);
+ phone.unregisterForCallWaiting(mHandler);
+ phone.unregisterForDisplayInfo(mHandler);
+ phone.unregisterForSignalInfo(mHandler);
+ phone.unregisterForCdmaOtaStatusChange(mHandler);
+ phone.unregisterForResendIncallMute(mHandler);
+ phone.unregisterForMmiInitiate(mHandler);
+ phone.unregisterForMmiComplete(mHandler);
+ phone.unregisterForEcmTimerReset(mHandler);
+ phone.unregisterForSubscriptionInfoReady(mHandler);
+ phone.unregisterForSuppServiceFailed(mHandler);
+ }
+
+ /**
+ * Answers a ringing or waiting call.
+ *
+ * Active call, if any, go on hold.
+ * If active call can't be held, i.e., a background call of the same channel exists,
+ * the active call will be hang up.
+ *
+ * Answering occurs asynchronously, and final notification occurs via
+ * {@link #registerForPreciseCallStateChanged(android.os.Handler, int,
+ * java.lang.Object) registerForPreciseCallStateChanged()}.
+ *
+ * @exception CallStateException when call is not ringing or waiting
+ */
+ public void acceptCall(Call ringingCall) throws CallStateException {
+ Phone ringingPhone = ringingCall.getPhone();
+
+ if ( hasActiveFgCall() ) {
+ Phone activePhone = getActiveFgCall().getPhone();
+ boolean hasBgCall = ! (activePhone.getBackgroundCall().isIdle());
+ boolean sameChannel = (activePhone == ringingPhone);
+
+ if (LOCAL_DEBUG) {
+ Log.d(LOG_TAG, "hasBgCall: "+ hasBgCall + "sameChannel:" + sameChannel);
+ }
+
+ if (sameChannel && hasBgCall) {
+ getActiveFgCall().hangup();
+ } else if (!sameChannel && !hasBgCall) {
+ activePhone.switchHoldingAndActive();
+ } else if (!sameChannel && hasBgCall) {
+ getActiveFgCall().hangup();
+ }
+ }
+
+ ringingPhone.acceptCall();
+ }
+
+ /**
+ * Reject (ignore) a ringing call. In GSM, this means UDUB
+ * (User Determined User Busy). Reject occurs asynchronously,
+ * and final notification occurs via
+ * {@link #registerForPreciseCallStateChanged(android.os.Handler, int,
+ * java.lang.Object) registerForPreciseCallStateChanged()}.
+ *
+ * @exception CallStateException when no call is ringing or waiting
+ */
+ public void rejectCall(Call ringingCall) throws CallStateException {
+ Phone ringingPhone = ringingCall.getPhone();
+
+ ringingPhone.rejectCall();
+ }
+
+ /**
+ * Places active call on hold, and makes held call active.
+ * Switch occurs asynchronously and may fail.
+ *
+ * There are 4 scenarios
+ * 1. only active call but no held call, aka, hold
+ * 2. no active call but only held call, aka, unhold
+ * 3. both active and held calls from same phone, aka, swap
+ * 4. active and held calls from different phones, aka, phone swap
+ *
+ * Final notification occurs via
+ * {@link #registerForPreciseCallStateChanged(android.os.Handler, int,
+ * java.lang.Object) registerForPreciseCallStateChanged()}.
+ *
+ * @exception CallStateException if active call is ringing, waiting, or
+ * dialing/alerting, or heldCall can't be active.
+ * In these cases, this operation may not be performed.
+ */
+ public void switchHoldingAndActive(Call heldCall) throws CallStateException {
+ Phone activePhone = null;
+ Phone heldPhone = null;
+
+ if (hasActiveFgCall()) {
+ activePhone = getActiveFgCall().getPhone();
+ }
+
+ if (heldCall != null) {
+ heldPhone = heldCall.getPhone();
+ }
+
+ if (activePhone != null) {
+ activePhone.switchHoldingAndActive();
+ }
+
+ if (heldPhone != null && heldPhone != activePhone) {
+ heldPhone.switchHoldingAndActive();
+ }
+ }
+
+ /**
+ * Whether or not the phone can conference in the current phone
+ * state--that is, one call holding and one call active.
+ * @return true if the phone can conference; false otherwise.
+ */
+ public boolean canConference(Call heldCall) {
+ Phone activePhone = null;
+ Phone heldPhone = null;
+
+ if (hasActiveFgCall()) {
+ activePhone = getActiveFgCall().getPhone();
+ }
+
+ if (heldCall != null) {
+ heldPhone = heldCall.getPhone();
+ }
+
+ return heldPhone.getClass().equals(activePhone.getClass());
+ }
+
+ /**
+ * Conferences holding and active. Conference occurs asynchronously
+ * and may fail. Final notification occurs via
+ * {@link #registerForPreciseCallStateChanged(android.os.Handler, int,
+ * java.lang.Object) registerForPreciseCallStateChanged()}.
+ *
+ * @exception CallStateException if canConference() would return false.
+ * In these cases, this operation may not be performed.
+ */
+ public void conference(Call heldCall) throws CallStateException {
+ Phone fgPhone = getFgPhone();
+ if (fgPhone instanceof SipPhone) {
+ ((SipPhone) fgPhone).conference(heldCall);
+ } else if (canConference(heldCall)) {
+ fgPhone.conference();
+ } else {
+ throw(new CallStateException("Can't conference foreground and selected background call"));
+ }
+ }
+
+ /**
+ * Initiate a new voice connection. This happens asynchronously, so you
+ * cannot assume the audio path is connected (or a call index has been
+ * assigned) until PhoneStateChanged notification has occurred.
+ *
+ * @exception CallStateException if a new outgoing call is not currently
+ * possible because no more call slots exist or a call exists that is
+ * dialing, alerting, ringing, or waiting. Other errors are
+ * handled asynchronously.
+ */
+ public Connection dial(Phone phone, String dialString) throws CallStateException {
+ if ( hasActiveFgCall() ) {
+ Phone activePhone = getActiveFgCall().getPhone();
+ boolean hasBgCall = !(activePhone.getBackgroundCall().isIdle());
+
+ if (LOCAL_DEBUG) {
+ Log.d(LOG_TAG, "hasBgCall: "+ hasBgCall + "sameChannel:" + (activePhone != phone));
+ }
+
+ if (activePhone != phone) {
+ if (hasBgCall) {
+ Log.d(LOG_TAG, "Hangup");
+ getActiveFgCall().hangup();
+ } else {
+ Log.d(LOG_TAG, "Switch");
+ activePhone.switchHoldingAndActive();
+ }
+ }
+ }
+ return phone.dial(dialString);
+ }
+
+ /**
+ * Initiate a new voice connection. This happens asynchronously, so you
+ * cannot assume the audio path is connected (or a call index has been
+ * assigned) until PhoneStateChanged notification has occurred.
+ *
+ * @exception CallStateException if a new outgoing call is not currently
+ * possible because no more call slots exist or a call exists that is
+ * dialing, alerting, ringing, or waiting. Other errors are
+ * handled asynchronously.
+ */
+ public Connection dial(Phone phone, String dialString, UUSInfo uusInfo) throws CallStateException {
+ return phone.dial(dialString, uusInfo);
+ }
+
+ /**
+ * clear disconnect connection for each phone
+ */
+ public void clearDisconnected() {
+ for(Phone phone : mPhones) {
+ phone.clearDisconnected();
+ }
+ }
+
+ /**
+ * Whether or not the phone can do explicit call transfer in the current
+ * phone state--that is, one call holding and one call active.
+ * @return true if the phone can do explicit call transfer; false otherwise.
+ */
+ public boolean canTransfer(Call heldCall) {
+ Phone activePhone = null;
+ Phone heldPhone = null;
+
+ if (hasActiveFgCall()) {
+ activePhone = getActiveFgCall().getPhone();
+ }
+
+ if (heldCall != null) {
+ heldPhone = heldCall.getPhone();
+ }
+
+ return (heldPhone == activePhone && activePhone.canTransfer());
+ }
+
+ /**
+ * Connects the held call and active call
+ * Disconnects the subscriber from both calls
+ *
+ * Explicit Call Transfer occurs asynchronously
+ * and may fail. Final notification occurs via
+ * {@link #registerForPreciseCallStateChanged(android.os.Handler, int,
+ * java.lang.Object) registerForPreciseCallStateChanged()}.
+ *
+ * @exception CallStateException if canTransfer() would return false.
+ * In these cases, this operation may not be performed.
+ */
+ public void explicitCallTransfer(Call heldCall) throws CallStateException {
+ if (canTransfer(heldCall)) {
+ heldCall.getPhone().explicitCallTransfer();
+ }
+ }
+
+ /**
+ * Returns a list of MMI codes that are pending for a phone. (They have initiated
+ * but have not yet completed).
+ * Presently there is only ever one.
+ *
+ * Use <code>registerForMmiInitiate</code>
+ * and <code>registerForMmiComplete</code> for change notification.
+ * @return null if phone doesn't have or support mmi code
+ */
+ public List<? extends MmiCode> getPendingMmiCodes(Phone phone) {
+ return null;
+ }
+
+ /**
+ * Sends user response to a USSD REQUEST message. An MmiCode instance
+ * representing this response is sent to handlers registered with
+ * registerForMmiInitiate.
+ *
+ * @param ussdMessge Message to send in the response.
+ * @return false if phone doesn't support ussd service
+ */
+ public boolean sendUssdResponse(Phone phone, String ussdMessge) {
+ return false;
+ }
+
+ /**
+ * Mutes or unmutes the microphone for the active call. The microphone
+ * is automatically unmuted if a call is answered, dialed, or resumed
+ * from a holding state.
+ *
+ * @param muted true to mute the microphone,
+ * false to activate the microphone.
+ */
+
+ public void setMute(boolean muted) {
+ if (hasActiveFgCall()) {
+ getActiveFgCall().getPhone().setMute(muted);
+ }
+ }
+
+ /**
+ * Gets current mute status. Use
+ * {@link #registerForPreciseCallStateChanged(android.os.Handler, int,
+ * java.lang.Object) registerForPreciseCallStateChanged()}
+ * as a change notifcation, although presently phone state changed is not
+ * fired when setMute() is called.
+ *
+ * @return true is muting, false is unmuting
+ */
+ public boolean getMute() {
+ if (hasActiveFgCall()) {
+ return getActiveFgCall().getPhone().getMute();
+ }
+ return false;
+ }
+
+ /**
+ * Play a DTMF tone on the active call.
+ *
+ * @param c should be one of 0-9, '*' or '#'. Other values will be
+ * silently ignored.
+ * @return false if no active call or the active call doesn't support
+ * dtmf tone
+ */
+ public boolean sendDtmf(char c) {
+ if (hasActiveFgCall()) {
+ getActiveFgCall().getPhone().sendDtmf(c);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Start to paly a DTMF tone on the active call.
+ * or there is a playing DTMF tone.
+ * @param c should be one of 0-9, '*' or '#'. Other values will be
+ * silently ignored.
+ *
+ * @return false if no active call or the active call doesn't support
+ * dtmf tone
+ */
+ public boolean startDtmf(char c) {
+ if (hasActiveFgCall()) {
+ getActiveFgCall().getPhone().sendDtmf(c);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Stop the playing DTMF tone. Ignored if there is no playing DTMF
+ * tone or no active call.
+ */
+ public void stopDtmf() {
+ if (hasActiveFgCall()) getFgPhone().stopDtmf();
+ }
+
+ /**
+ * send burst DTMF tone, it can send the string as single character or multiple character
+ * ignore if there is no active call or not valid digits string.
+ * Valid digit means only includes characters ISO-LATIN characters 0-9, *, #
+ * The difference between sendDtmf and sendBurstDtmf is sendDtmf only sends one character,
+ * this api can send single character and multiple character, also, this api has response
+ * back to caller.
+ *
+ * @param dtmfString is string representing the dialing digit(s) in the active call
+ * @param on the DTMF ON length in milliseconds, or 0 for default
+ * @param off the DTMF OFF length in milliseconds, or 0 for default
+ * @param onComplete is the callback message when the action is processed by BP
+ *
+ */
+ public boolean sendBurstDtmf(String dtmfString, int on, int off, Message onComplete) {
+ if (hasActiveFgCall()) {
+ getActiveFgCall().getPhone().sendBurstDtmf(dtmfString, on, off, onComplete);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Notifies when a voice connection has disconnected, either due to local
+ * or remote hangup or error.
+ *
+ * Messages received from this will have the following members:<p>
+ * <ul><li>Message.obj will be an AsyncResult</li>
+ * <li>AsyncResult.userObj = obj</li>
+ * <li>AsyncResult.result = a Connection object that is
+ * no longer connected.</li></ul>
+ */
+ public void registerForDisconnect(Handler h, int what, Object obj) {
+ mDisconnectRegistrants.addUnique(h, what, obj);
+ }
+
+ /**
+ * Unregisters for voice disconnection notification.
+ * Extraneous calls are tolerated silently
+ */
+ public void unregisterForDisconnect(Handler h){
+ mDisconnectRegistrants.remove(h);
+ }
+
+ /**
+ * Register for getting notifications for change in the Call State {@link Call.State}
+ * This is called PreciseCallState because the call state is more precise than the
+ * {@link Phone.State} which can be obtained using the {@link PhoneStateListener}
+ *
+ * Resulting events will have an AsyncResult in <code>Message.obj</code>.
+ * AsyncResult.userData will be set to the obj argument here.
+ * The <em>h</em> parameter is held only by a weak reference.
+ */
+ public void registerForPreciseCallStateChanged(Handler h, int what, Object obj){
+ mPreciseCallStateRegistrants.addUnique(h, what, obj);
+ }
+
+ /**
+ * Unregisters for voice call state change notifications.
+ * Extraneous calls are tolerated silently.
+ */
+ public void unregisterForPreciseCallStateChanged(Handler h){
+ mPreciseCallStateRegistrants.remove(h);
+ }
+
+ /**
+ * Notifies when a previously untracked non-ringing/waiting connection has appeared.
+ * This is likely due to some other entity (eg, SIM card application) initiating a call.
+ */
+ public void registerForUnknownConnection(Handler h, int what, Object obj){
+ mUnknownConnectionRegistrants.addUnique(h, what, obj);
+ }
+
+ /**
+ * Unregisters for unknown connection notifications.
+ */
+ public void unregisterForUnknownConnection(Handler h){
+ mUnknownConnectionRegistrants.remove(h);
+ }
+
+
+ /**
+ * Notifies when a new ringing or waiting connection has appeared.<p>
+ *
+ * Messages received from this:
+ * Message.obj will be an AsyncResult
+ * AsyncResult.userObj = obj
+ * AsyncResult.result = a Connection. <p>
+ * Please check Connection.isRinging() to make sure the Connection
+ * has not dropped since this message was posted.
+ * If Connection.isRinging() is true, then
+ * Connection.getCall() == Phone.getRingingCall()
+ */
+ public void registerForNewRingingConnection(Handler h, int what, Object obj){
+ mNewRingingConnectionRegistrants.addUnique(h, what, obj);
+ }
+
+ /**
+ * Unregisters for new ringing connection notification.
+ * Extraneous calls are tolerated silently
+ */
+
+ public void unregisterForNewRingingConnection(Handler h){
+ mNewRingingConnectionRegistrants.remove(h);
+ }
+
+ /**
+ * Notifies when an incoming call rings.<p>
+ *
+ * Messages received from this:
+ * Message.obj will be an AsyncResult
+ * AsyncResult.userObj = obj
+ * AsyncResult.result = a Connection. <p>
+ */
+ public void registerForIncomingRing(Handler h, int what, Object obj){
+ mIncomingRingRegistrants.addUnique(h, what, obj);
+ }
+
+ /**
+ * Unregisters for ring notification.
+ * Extraneous calls are tolerated silently
+ */
+
+ public void unregisterForIncomingRing(Handler h){
+ mIncomingRingRegistrants.remove(h);
+ }
+
+ /**
+ * Notifies when out-band ringback tone is needed.<p>
+ *
+ * Messages received from this:
+ * Message.obj will be an AsyncResult
+ * AsyncResult.userObj = obj
+ * AsyncResult.result = boolean, true to start play ringback tone
+ * and false to stop. <p>
+ */
+ public void registerForRingbackTone(Handler h, int what, Object obj){
+ mRingbackToneRegistrants.addUnique(h, what, obj);
+ }
+
+ /**
+ * Unregisters for ringback tone notification.
+ */
+
+ public void unregisterForRingbackTone(Handler h){
+ mRingbackToneRegistrants.remove(h);
+ }
+
+ /**
+ * Registers the handler to reset the uplink mute state to get
+ * uplink audio.
+ */
+ public void registerForResendIncallMute(Handler h, int what, Object obj){
+ mResendIncallMuteRegistrants.addUnique(h, what, obj);
+ }
+
+ /**
+ * Unregisters for resend incall mute notifications.
+ */
+ public void unregisterForResendIncallMute(Handler h){
+ mResendIncallMuteRegistrants.remove(h);
+ }
+
+ /**
+ * Register for notifications of initiation of a new MMI code request.
+ * MMI codes for GSM are discussed in 3GPP TS 22.030.<p>
+ *
+ * Example: If Phone.dial is called with "*#31#", then the app will
+ * be notified here.<p>
+ *
+ * The returned <code>Message.obj</code> will contain an AsyncResult.
+ *
+ * <code>obj.result</code> will be an "MmiCode" object.
+ */
+ public void registerForMmiInitiate(Handler h, int what, Object obj){
+ mMmiInitiateRegistrants.addUnique(h, what, obj);
+ }
+
+ /**
+ * Unregisters for new MMI initiate notification.
+ * Extraneous calls are tolerated silently
+ */
+ public void unregisterForMmiInitiate(Handler h){
+ mMmiInitiateRegistrants.remove(h);
+ }
+
+ /**
+ * Register for notifications that an MMI request has completed
+ * its network activity and is in its final state. This may mean a state
+ * of COMPLETE, FAILED, or CANCELLED.
+ *
+ * <code>Message.obj</code> will contain an AsyncResult.
+ * <code>obj.result</code> will be an "MmiCode" object
+ */
+ public void registerForMmiComplete(Handler h, int what, Object obj){
+ mMmiCompleteRegistrants.addUnique(h, what, obj);
+ }
+
+ /**
+ * Unregisters for MMI complete notification.
+ * Extraneous calls are tolerated silently
+ */
+ public void unregisterForMmiComplete(Handler h){
+ mMmiCompleteRegistrants.remove(h);
+ }
+
+ /**
+ * Registration point for Ecm timer reset
+ * @param h handler to notify
+ * @param what user-defined message code
+ * @param obj placed in Message.obj
+ */
+ public void registerForEcmTimerReset(Handler h, int what, Object obj){
+ mEcmTimerResetRegistrants.addUnique(h, what, obj);
+ }
+
+ /**
+ * Unregister for notification for Ecm timer reset
+ * @param h Handler to be removed from the registrant list.
+ */
+ public void unregisterForEcmTimerReset(Handler h){
+ mEcmTimerResetRegistrants.remove(h);
+ }
+
+ /**
+ * Register for ServiceState changed.
+ * Message.obj will contain an AsyncResult.
+ * AsyncResult.result will be a ServiceState instance
+ */
+ public void registerForServiceStateChanged(Handler h, int what, Object obj){}
+
+ /**
+ * Unregisters for ServiceStateChange notification.
+ * Extraneous calls are tolerated silently
+ */
+ public void unregisterForServiceStateChanged(Handler h){}
+
+ /**
+ * Register for notifications when a supplementary service attempt fails.
+ * Message.obj will contain an AsyncResult.
+ *
+ * @param h Handler that receives the notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ */
+ public void registerForSuppServiceFailed(Handler h, int what, Object obj){
+ mSuppServiceFailedRegistrants.addUnique(h, what, obj);
+ }
+
+ /**
+ * Unregister for notifications when a supplementary service attempt fails.
+ * Extraneous calls are tolerated silently
+ *
+ * @param h Handler to be removed from the registrant list.
+ */
+ public void unregisterForSuppServiceFailed(Handler h){
+ mSuppServiceFailedRegistrants.remove(h);
+ }
+
+ /**
+ * Register for notifications when a sInCall VoicePrivacy is enabled
+ *
+ * @param h Handler that receives the notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ */
+ public void registerForInCallVoicePrivacyOn(Handler h, int what, Object obj){
+ mInCallVoicePrivacyOnRegistrants.addUnique(h, what, obj);
+ }
+
+ /**
+ * Unregister for notifications when a sInCall VoicePrivacy is enabled
+ *
+ * @param h Handler to be removed from the registrant list.
+ */
+ public void unregisterForInCallVoicePrivacyOn(Handler h){
+ mInCallVoicePrivacyOnRegistrants.remove(h);
+ }
+
+ /**
+ * Register for notifications when a sInCall VoicePrivacy is disabled
+ *
+ * @param h Handler that receives the notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ */
+ public void registerForInCallVoicePrivacyOff(Handler h, int what, Object obj){
+ mInCallVoicePrivacyOffRegistrants.addUnique(h, what, obj);
+ }
+
+ /**
+ * Unregister for notifications when a sInCall VoicePrivacy is disabled
+ *
+ * @param h Handler to be removed from the registrant list.
+ */
+ public void unregisterForInCallVoicePrivacyOff(Handler h){
+ mInCallVoicePrivacyOffRegistrants.remove(h);
+ }
+
+ /**
+ * Register for notifications when CDMA call waiting comes
+ *
+ * @param h Handler that receives the notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ */
+ public void registerForCallWaiting(Handler h, int what, Object obj){
+ mCallWaitingRegistrants.addUnique(h, what, obj);
+ }
+
+ /**
+ * Unregister for notifications when CDMA Call waiting comes
+ * @param h Handler to be removed from the registrant list.
+ */
+ public void unregisterForCallWaiting(Handler h){
+ mCallWaitingRegistrants.remove(h);
+ }
+
+
+ /**
+ * Register for signal information notifications from the network.
+ * Message.obj will contain an AsyncResult.
+ * AsyncResult.result will be a SuppServiceNotification instance.
+ *
+ * @param h Handler that receives the notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ */
+
+ public void registerForSignalInfo(Handler h, int what, Object obj){
+ mSignalInfoRegistrants.addUnique(h, what, obj);
+ }
+
+ /**
+ * Unregisters for signal information notifications.
+ * Extraneous calls are tolerated silently
+ *
+ * @param h Handler to be removed from the registrant list.
+ */
+ public void unregisterForSignalInfo(Handler h){
+ mSignalInfoRegistrants.remove(h);
+ }
+
+ /**
+ * Register for display information notifications from the network.
+ * Message.obj will contain an AsyncResult.
+ * AsyncResult.result will be a SuppServiceNotification instance.
+ *
+ * @param h Handler that receives the notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ */
+ public void registerForDisplayInfo(Handler h, int what, Object obj){
+ mDisplayInfoRegistrants.addUnique(h, what, obj);
+ }
+
+ /**
+ * Unregisters for display information notifications.
+ * Extraneous calls are tolerated silently
+ *
+ * @param h Handler to be removed from the registrant list.
+ */
+ public void unregisterForDisplayInfo(Handler h) {
+ mDisplayInfoRegistrants.remove(h);
+ }
+
+ /**
+ * Register for notifications when CDMA OTA Provision status change
+ *
+ * @param h Handler that receives the notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ */
+ public void registerForCdmaOtaStatusChange(Handler h, int what, Object obj){
+ mCdmaOtaStatusChangeRegistrants.addUnique(h, what, obj);
+ }
+
+ /**
+ * Unregister for notifications when CDMA OTA Provision status change
+ * @param h Handler to be removed from the registrant list.
+ */
+ public void unregisterForCdmaOtaStatusChange(Handler h){
+ mCdmaOtaStatusChangeRegistrants.remove(h);
+ }
+
+ /**
+ * Registration point for subscription info ready
+ * @param h handler to notify
+ * @param what what code of message when delivered
+ * @param obj placed in Message.obj
+ */
+ public void registerForSubscriptionInfoReady(Handler h, int what, Object obj){
+ mSubscriptionInfoReadyRegistrants.addUnique(h, what, obj);
+ }
+
+ /**
+ * Unregister for notifications for subscription info
+ * @param h Handler to be removed from the registrant list.
+ */
+ public void unregisterForSubscriptionInfoReady(Handler h){
+ mSubscriptionInfoReadyRegistrants.remove(h);
+ }
+
+ /* APIs to access foregroudCalls, backgroudCalls, and ringingCalls
+ * 1. APIs to access list of calls
+ * 2. APIs to check if any active call, which has connection other than
+ * disconnected ones, pleaser refer to Call.isIdle()
+ * 3. APIs to return first active call
+ * 4. APIs to return the connections of first active call
+ * 5. APIs to return other property of first active call
+ */
+
+ /**
+ * @return list of ringing calls
+ */
+ public ArrayList<Call> getRingingCalls() {
+ return mRingingCalls;
+ }
+
+ /**
+ * @return list of background calls
+ */
+ public ArrayList<Call> getBackgroundCalls() {
+ return mBackgroundCalls;
+ }
+
+ /**
+ * Return true if there is at least one active foreground call
+ */
+ public boolean hasActiveFgCall() {
+ return (getFirstActiveCall(mForegroundCalls) != null);
+ }
+
+ /**
+ * Return true if there is at least one active background call
+ */
+ public boolean hasActiveBgCall() {
+ // TODO since hasActiveBgCall may get called often
+ // better to cache it to improve performance
+ return (getFirstActiveCall(mBackgroundCalls) != null);
+ }
+
+ /**
+ * Return true if there is at least one active ringing call
+ *
+ */
+ public boolean hasActiveRingingCall() {
+ return (getFirstActiveCall(mRingingCalls) != null);
+ }
+
+ /**
+ * return the active foreground call from foreground calls
+ *
+ * Active call means the call is NOT in Call.State.IDLE
+ *
+ * 1. If there is active foreground call, return it
+ * 2. If there is no active foreground call, return the
+ * foreground call associated with default phone, which state is IDLE.
+ * 3. If there is no phone registered at all, return null.
+ *
+ */
+ public Call getActiveFgCall() {
+ for (Call call : mForegroundCalls) {
+ if (call.getState() != Call.State.IDLE) {
+ return call;
+ }
+ }
+ return (mDefaultPhone == null) ?
+ null : mDefaultPhone.getForegroundCall();
+ }
+
+ /**
+ * return one active background call from background calls
+ *
+ * Active call means the call is NOT idle defined by Call.isIdle()
+ *
+ * 1. If there is only one active background call, return it
+ * 2. If there is more than one active background call, return the first one
+ * 3. If there is no active background call, return the background call
+ * associated with default phone, which state is IDLE.
+ * 4. If there is no background call at all, return null.
+ *
+ * Complete background calls list can be get by getBackgroundCalls()
+ */
+ public Call getFirstActiveBgCall() {
+ for (Call call : mBackgroundCalls) {
+ if (!call.isIdle()) {
+ return call;
+ }
+ }
+ return (mDefaultPhone == null) ?
+ null : mDefaultPhone.getBackgroundCall();
+ }
+
+ /**
+ * return one active ringing call from ringing calls
+ *
+ * Active call means the call is NOT idle defined by Call.isIdle()
+ *
+ * 1. If there is only one active ringing call, return it
+ * 2. If there is more than one active ringing call, return the first one
+ * 3. If there is no active ringing call, return the ringing call
+ * associated with default phone, which state is IDLE.
+ * 4. If there is no ringing call at all, return null.
+ *
+ * Complete ringing calls list can be get by getRingingCalls()
+ */
+ public Call getFirstActiveRingingCall() {
+ for (Call call : mRingingCalls) {
+ if (!call.isIdle()) {
+ return call;
+ }
+ }
+ return (mDefaultPhone == null) ?
+ null : mDefaultPhone.getRingingCall();
+ }
+
+ /**
+ * @return the state of active foreground call
+ * return IDLE if there is no active foreground call
+ */
+ public Call.State getActiveFgCallState() {
+ Call fgCall = getActiveFgCall();
+
+ if (fgCall != null) {
+ return fgCall.getState();
+ }
+
+ return Call.State.IDLE;
+ }
+
+ /**
+ * @return the connections of active foreground call
+ * return null if there is no active foreground call
+ */
+ public List<Connection> getFgCallConnections() {
+ Call fgCall = getActiveFgCall();
+ if ( fgCall != null) {
+ return fgCall.getConnections();
+ }
+ return emptyConnections;
+ }
+
+ /**
+ * @return the connections of active background call
+ * return empty list if there is no active background call
+ */
+ public List<Connection> getBgCallConnections() {
+ Call bgCall = getActiveFgCall();
+ if ( bgCall != null) {
+ return bgCall.getConnections();
+ }
+ return emptyConnections;
+ }
+
+ /**
+ * @return the latest connection of active foreground call
+ * return null if there is no active foreground call
+ */
+ public Connection getFgCallLatestConnection() {
+ Call fgCall = getActiveFgCall();
+ if ( fgCall != null) {
+ return fgCall.getLatestConnection();
+ }
+ return null;
+ }
+
+ /**
+ * @return true if there is at least one Foreground call in disconnected state
+ */
+ public boolean hasDisconnectedFgCall() {
+ return (getFirstCallOfState(mForegroundCalls, Call.State.DISCONNECTED) != null);
+ }
+
+ /**
+ * @return true if there is at least one background call in disconnected state
+ */
+ public boolean hasDisconnectedBgCall() {
+ return (getFirstCallOfState(mBackgroundCalls, Call.State.DISCONNECTED) != null);
+ }
+
+ /**
+ * @return the first active call from a call list
+ */
+ private Call getFirstActiveCall(ArrayList<Call> calls) {
+ for (Call call : calls) {
+ if (!call.isIdle()) {
+ return call;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * @return the first call in a the Call.state from a call list
+ */
+ private Call getFirstCallOfState(ArrayList<Call> calls, Call.State state) {
+ for (Call call : calls) {
+ if (call.getState() == state) {
+ return call;
+ }
+ }
+ return null;
+ }
+
+
+
+
+ private Handler mHandler = new Handler() {
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case EVENT_DISCONNECT:
+ mDisconnectRegistrants.notifyRegistrants((AsyncResult) msg.obj);
+ break;
+ case EVENT_PRECISE_CALL_STATE_CHANGED:
+ mPreciseCallStateRegistrants.notifyRegistrants((AsyncResult) msg.obj);
+ break;
+ case EVENT_NEW_RINGING_CONNECTION:
+ mNewRingingConnectionRegistrants.notifyRegistrants((AsyncResult) msg.obj);
+ break;
+ case EVENT_UNKNOWN_CONNECTION:
+ mUnknownConnectionRegistrants.notifyRegistrants((AsyncResult) msg.obj);
+ break;
+ case EVENT_INCOMING_RING:
+ // The event may come from RIL who's not aware of an ongoing fg call
+ if (!hasActiveFgCall()) {
+ mIncomingRingRegistrants.notifyRegistrants((AsyncResult) msg.obj);
+ }
+ break;
+ case EVENT_RINGBACK_TONE:
+ mRingbackToneRegistrants.notifyRegistrants((AsyncResult) msg.obj);
+ break;
+ case EVENT_IN_CALL_VOICE_PRIVACY_ON:
+ mInCallVoicePrivacyOnRegistrants.notifyRegistrants((AsyncResult) msg.obj);
+ break;
+ case EVENT_IN_CALL_VOICE_PRIVACY_OFF:
+ mInCallVoicePrivacyOffRegistrants.notifyRegistrants((AsyncResult) msg.obj);
+ break;
+ case EVENT_CALL_WAITING:
+ mCallWaitingRegistrants.notifyRegistrants((AsyncResult) msg.obj);
+ break;
+ case EVENT_DISPLAY_INFO:
+ mDisplayInfoRegistrants.notifyRegistrants((AsyncResult) msg.obj);
+ break;
+ case EVENT_SIGNAL_INFO:
+ mSignalInfoRegistrants.notifyRegistrants((AsyncResult) msg.obj);
+ break;
+ case EVENT_CDMA_OTA_STATUS_CHANGE:
+ mCdmaOtaStatusChangeRegistrants.notifyRegistrants((AsyncResult) msg.obj);
+ break;
+ case EVENT_RESEND_INCALL_MUTE:
+ mResendIncallMuteRegistrants.notifyRegistrants((AsyncResult) msg.obj);
+ break;
+ case EVENT_MMI_INITIATE:
+ mMmiInitiateRegistrants.notifyRegistrants((AsyncResult) msg.obj);
+ break;
+ case EVENT_MMI_COMPLETE:
+ mMmiCompleteRegistrants.notifyRegistrants((AsyncResult) msg.obj);
+ break;
+ case EVENT_ECM_TIMER_RESET:
+ mEcmTimerResetRegistrants.notifyRegistrants((AsyncResult) msg.obj);
+ break;
+ case EVENT_SUBSCRIPTION_INFO_READY:
+ mSubscriptionInfoReadyRegistrants.notifyRegistrants((AsyncResult) msg.obj);
+ break;
+ case EVENT_SUPP_SERVICE_FAILED:
+ mSuppServiceFailedRegistrants.notifyRegistrants((AsyncResult) msg.obj);
+ break;
+ }
+ }
+ };
+}
diff --git a/telephony/java/com/android/internal/telephony/DefaultPhoneNotifier.java b/telephony/java/com/android/internal/telephony/DefaultPhoneNotifier.java
index 4da4b6a..057ba0a 100644
--- a/telephony/java/com/android/internal/telephony/DefaultPhoneNotifier.java
+++ b/telephony/java/com/android/internal/telephony/DefaultPhoneNotifier.java
@@ -102,7 +102,8 @@ public class DefaultPhoneNotifier implements PhoneNotifier {
sender.getActiveApnTypes(),
sender.getInterfaceName(null),
((telephony!=null) ? telephony.getNetworkType() :
- TelephonyManager.NETWORK_TYPE_UNKNOWN));
+ TelephonyManager.NETWORK_TYPE_UNKNOWN),
+ sender.getGateway(null));
} catch (RemoteException ex) {
// system process is dead
}
diff --git a/telephony/java/com/android/internal/telephony/GsmAlphabet.java b/telephony/java/com/android/internal/telephony/GsmAlphabet.java
index ebdd220..7edf065 100644
--- a/telephony/java/com/android/internal/telephony/GsmAlphabet.java
+++ b/telephony/java/com/android/internal/telephony/GsmAlphabet.java
@@ -23,7 +23,7 @@ import android.util.Log;
/**
* This class implements the character set mapping between
- * the GSM SMS 7-bit alphabet specifed in TS 23.038 6.2.1
+ * the GSM SMS 7-bit alphabet specified in TS 23.038 6.2.1
* and UTF-16
*
* {@hide}
diff --git a/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl b/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl
index e74b9e4..5cba2e1 100644
--- a/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl
+++ b/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl
@@ -59,6 +59,11 @@ interface IPhoneSubInfo {
String getVoiceMailNumber();
/**
+ * Retrieves the complete voice mail number.
+ */
+ String getCompleteVoiceMailNumber();
+
+ /**
* Retrieves the alpha identifier associated with the voice mail number.
*/
String getVoiceMailAlphaTag();
diff --git a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index 5bf8e58..87e4b52 100644
--- a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -32,7 +32,8 @@ interface ITelephonyRegistry {
void notifyCallForwardingChanged(boolean cfi);
void notifyDataActivity(int state);
void notifyDataConnection(int state, boolean isDataConnectivityPossible,
- String reason, String apn, in String[] apnTypes, String interfaceName, int networkType);
+ String reason, String apn, in String[] apnTypes, String interfaceName, int networkType,
+ String gateway);
void notifyDataConnectionFailed(String reason);
void notifyCellLocation(in Bundle cellLocation);
}
diff --git a/telephony/java/com/android/internal/telephony/Phone.java b/telephony/java/com/android/internal/telephony/Phone.java
index 23325f6..9afade3 100644
--- a/telephony/java/com/android/internal/telephony/Phone.java
+++ b/telephony/java/com/android/internal/telephony/Phone.java
@@ -103,6 +103,7 @@ public interface Phone {
static final String DATA_APN_KEY = "apn";
static final String DATA_IFACE_NAME_KEY = "iface";
+ static final String DATA_GATEWAY_KEY = "gateway";
static final String NETWORK_UNAVAILABLE_KEY = "networkUnvailable";
static final String PHONE_IN_ECM_STATE = "phoneinECMState";
@@ -177,6 +178,7 @@ public interface Phone {
static final int PHONE_TYPE_NONE = RILConstants.NO_PHONE;
static final int PHONE_TYPE_GSM = RILConstants.GSM_PHONE;
static final int PHONE_TYPE_CDMA = RILConstants.CDMA_PHONE;
+ static final int PHONE_TYPE_SIP = RILConstants.SIP_PHONE;
// Used for preferred network type
// Note NT_* substitute RILConstants.NETWORK_MODE_* above the Phone
diff --git a/telephony/java/com/android/internal/telephony/PhoneFactory.java b/telephony/java/com/android/internal/telephony/PhoneFactory.java
index 803b736..2e391cb 100644
--- a/telephony/java/com/android/internal/telephony/PhoneFactory.java
+++ b/telephony/java/com/android/internal/telephony/PhoneFactory.java
@@ -24,6 +24,8 @@ import android.util.Log;
import com.android.internal.telephony.cdma.CDMAPhone;
import com.android.internal.telephony.gsm.GSMPhone;
+import com.android.internal.telephony.sip.SipPhone;
+import com.android.internal.telephony.sip.SipPhoneFactory;
/**
* {@hide}
@@ -175,4 +177,13 @@ public class PhoneFactory {
return phone;
}
}
+
+ /**
+ * Makes a {@link SipPhone} object.
+ * @param sipUri the local SIP URI the phone runs on
+ * @return the {@code SipPhone} object or null if the SIP URI is not valid
+ */
+ public static SipPhone makeSipPhone(String sipUri) {
+ return SipPhoneFactory.makePhone(sipUri, sContext, sPhoneNotifier);
+ }
}
diff --git a/telephony/java/com/android/internal/telephony/PhoneSubInfo.java b/telephony/java/com/android/internal/telephony/PhoneSubInfo.java
index 4f71bb1..a45cad1 100644
--- a/telephony/java/com/android/internal/telephony/PhoneSubInfo.java
+++ b/telephony/java/com/android/internal/telephony/PhoneSubInfo.java
@@ -21,6 +21,7 @@ import java.io.PrintWriter;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Binder;
+import android.telephony.PhoneNumberUtils;
import android.util.Log;
public class PhoneSubInfo extends IPhoneSubInfo.Stub {
@@ -29,6 +30,9 @@ public class PhoneSubInfo extends IPhoneSubInfo.Stub {
private Context mContext;
private static final String READ_PHONE_STATE =
android.Manifest.permission.READ_PHONE_STATE;
+ private static final String CALL_PRIVILEGED =
+ // TODO Add core/res/AndriodManifest.xml#READ_PRIVILEGED_PHONE_STATE
+ android.Manifest.permission.CALL_PRIVILEGED;
public PhoneSubInfo(Phone phone) {
mPhone = phone;
@@ -101,7 +105,22 @@ public class PhoneSubInfo extends IPhoneSubInfo.Stub {
*/
public String getVoiceMailNumber() {
mContext.enforceCallingOrSelfPermission(READ_PHONE_STATE, "Requires READ_PHONE_STATE");
- return (String) mPhone.getVoiceMailNumber();
+ String number = PhoneNumberUtils.extractNetworkPortion(mPhone.getVoiceMailNumber());
+ Log.d(LOG_TAG, "VM: PhoneSubInfo.getVoiceMailNUmber: "); // + number);
+ return number;
+ }
+
+ /**
+ * Retrieves the complete voice mail number.
+ *
+ * @hide
+ */
+ public String getCompleteVoiceMailNumber() {
+ mContext.enforceCallingOrSelfPermission(CALL_PRIVILEGED,
+ "Requires CALL_PRIVILEGED");
+ String number = mPhone.getVoiceMailNumber();
+ Log.d(LOG_TAG, "VM: PhoneSubInfo.getCompleteVoiceMailNUmber: "); // + number);
+ return number;
}
/**
diff --git a/telephony/java/com/android/internal/telephony/PhoneSubInfoProxy.java b/telephony/java/com/android/internal/telephony/PhoneSubInfoProxy.java
index 202ded2..7009893 100644
--- a/telephony/java/com/android/internal/telephony/PhoneSubInfoProxy.java
+++ b/telephony/java/com/android/internal/telephony/PhoneSubInfoProxy.java
@@ -82,6 +82,13 @@ public class PhoneSubInfoProxy extends IPhoneSubInfo.Stub {
}
/**
+ * Retrieves the complete voice mail number.
+ */
+ public String getCompleteVoiceMailNumber() {
+ return mPhoneSubInfo.getCompleteVoiceMailNumber();
+ }
+
+ /**
* Retrieves the alpha identifier associated with the voice mail number.
*/
public String getVoiceMailAlphaTag() {
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index 71a80e0..888f721 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -79,6 +79,7 @@ public interface RILConstants {
int NO_PHONE = 0;
int GSM_PHONE = 1;
int CDMA_PHONE = 2;
+ int SIP_PHONE = 3;
int CDM_TTY_MODE_DISABLED = 0;
int CDM_TTY_MODE_ENABLED = 1;
diff --git a/telephony/java/com/android/internal/telephony/SMSDispatcher.java b/telephony/java/com/android/internal/telephony/SMSDispatcher.java
index ca526a5..606b52d 100644
--- a/telephony/java/com/android/internal/telephony/SMSDispatcher.java
+++ b/telephony/java/com/android/internal/telephony/SMSDispatcher.java
@@ -44,10 +44,6 @@ import android.util.Config;
import android.util.Log;
import android.view.WindowManager;
-import com.android.internal.telephony.CommandsInterface;
-import com.android.internal.telephony.SmsMessageBase;
-import com.android.internal.telephony.SmsResponse;
-import com.android.internal.telephony.WapPushOverSms;
import com.android.internal.util.HexDump;
import java.io.ByteArrayOutputStream;
@@ -75,7 +71,7 @@ public abstract class SMSDispatcher extends Handler {
private static final int DEFAULT_SMS_MAX_COUNT = 100;
/** Default timeout for SMS sent query */
- private static final int DEFAULT_SMS_TIMOUEOUT = 6000;
+ private static final int DEFAULT_SMS_TIMEOUT = 6000;
protected static final String[] RAW_PROJECTION = new String[] {
"pdu",
@@ -83,8 +79,6 @@ public abstract class SMSDispatcher extends Handler {
"destination_port",
};
- static final int MAIL_SEND_SMS = 1;
-
static final protected int EVENT_NEW_SMS = 1;
static final protected int EVENT_SEND_SMS_COMPLETE = 2;
@@ -143,7 +137,7 @@ public abstract class SMSDispatcher extends Handler {
private SmsCounter mCounter;
- private ArrayList mSTrackers = new ArrayList(MO_MSG_QUEUE_LIMIT);
+ private ArrayList<SmsTracker> mSTrackers = new ArrayList<SmsTracker>(MO_MSG_QUEUE_LIMIT);
/** Wake lock to ensure device stays awake while dispatching the SMS intent. */
private PowerManager.WakeLock mWakeLock;
@@ -154,10 +148,6 @@ public abstract class SMSDispatcher extends Handler {
*/
private final int WAKE_LOCK_TIMEOUT = 5000;
- private static SmsMessage mSmsMessage;
- private static SmsMessageBase mSmsMessageBase;
- private SmsMessageBase.SubmitPduBase mSubmitPduBase;
-
protected boolean mStorageAvailable = true;
protected boolean mReportMemoryStatusPending = false;
@@ -345,7 +335,7 @@ public abstract class SMSDispatcher extends Handler {
msg.obj = null;
if (mSTrackers.isEmpty() == false) {
try {
- SmsTracker sTracker = (SmsTracker)mSTrackers.remove(0);
+ SmsTracker sTracker = mSTrackers.remove(0);
sTracker.mSentIntent.send(RESULT_ERROR_LIMIT_EXCEEDED);
} catch (CanceledException ex) {
Log.e(TAG, "failed to send back RESULT_ERROR_LIMIT_EXCEEDED");
@@ -358,7 +348,7 @@ public abstract class SMSDispatcher extends Handler {
case EVENT_SEND_CONFIRMED_SMS:
if (mSTrackers.isEmpty() == false) {
- SmsTracker sTracker = (SmsTracker)mSTrackers.remove(mSTrackers.size() - 1);
+ SmsTracker sTracker = mSTrackers.remove(mSTrackers.size() - 1);
if (isMultipartTracker(sTracker)) {
sendMultipartSms(sTracker);
} else {
@@ -372,7 +362,7 @@ public abstract class SMSDispatcher extends Handler {
if (mSTrackers.isEmpty() == false) {
// Remove the latest one.
try {
- SmsTracker sTracker = (SmsTracker)mSTrackers.remove(mSTrackers.size() - 1);
+ SmsTracker sTracker = mSTrackers.remove(mSTrackers.size() - 1);
sTracker.mSentIntent.send(RESULT_ERROR_LIMIT_EXCEEDED);
} catch (CanceledException ex) {
Log.e(TAG, "failed to send back RESULT_ERROR_LIMIT_EXCEEDED");
@@ -679,7 +669,7 @@ public abstract class SMSDispatcher extends Handler {
* @param destPort the port to deliver the message to
* @param data the body of the message to send
* @param sentIntent if not NULL this <code>PendingIntent</code> is
- * broadcast when the message is sucessfully sent, or failed.
+ * broadcast when the message is successfully sent, or failed.
* The result code will be <code>Activity.RESULT_OK<code> for success,
* or one of these errors:<br>
* <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
@@ -706,7 +696,7 @@ public abstract class SMSDispatcher extends Handler {
* the current default SMSC
* @param text the body of the message to send
* @param sentIntent if not NULL this <code>PendingIntent</code> is
- * broadcast when the message is sucessfully sent, or failed.
+ * broadcast when the message is successfully sent, or failed.
* The result code will be <code>Activity.RESULT_OK<code> for success,
* or one of these errors:<br>
* <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
@@ -761,7 +751,7 @@ public abstract class SMSDispatcher extends Handler {
* defatult SMSC
* @param pdu the raw PDU to send
* @param sentIntent if not NULL this <code>Intent</code> is
- * broadcast when the message is sucessfully sent, or failed.
+ * broadcast when the message is successfully sent, or failed.
* The result code will be <code>Activity.RESULT_OK<code> for success,
* or one of these errors:
* <code>RESULT_ERROR_GENERIC_FAILURE</code>
@@ -837,7 +827,7 @@ public abstract class SMSDispatcher extends Handler {
mSTrackers.add(tracker);
sendMessageDelayed ( obtainMessage(EVENT_ALERT_TIMEOUT, d),
- DEFAULT_SMS_TIMOUEOUT);
+ DEFAULT_SMS_TIMEOUT);
}
protected String getAppNameByIntent(PendingIntent intent) {
@@ -931,7 +921,7 @@ public abstract class SMSDispatcher extends Handler {
}
/**
- * Keeps track of an SMS that has been sent to the RIL, until it it has
+ * Keeps track of an SMS that has been sent to the RIL, until it has
* successfully been sent, or we're done trying.
*
*/
@@ -972,27 +962,26 @@ public abstract class SMSDispatcher extends Handler {
}
};
- private BroadcastReceiver mResultReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (intent.getAction().equals(Intent.ACTION_DEVICE_STORAGE_LOW)) {
- mStorageAvailable = false;
- mCm.reportSmsMemoryStatus(false, obtainMessage(EVENT_REPORT_MEMORY_STATUS_DONE));
- } else if (intent.getAction().equals(Intent.ACTION_DEVICE_STORAGE_OK)) {
- mStorageAvailable = true;
- mCm.reportSmsMemoryStatus(true, obtainMessage(EVENT_REPORT_MEMORY_STATUS_DONE));
- } else {
- // Assume the intent is one of the SMS receive intents that
- // was sent as an ordered broadcast. Check result and ACK.
- int rc = getResultCode();
- boolean success = (rc == Activity.RESULT_OK)
- || (rc == Intents.RESULT_SMS_HANDLED);
-
- // For a multi-part message, this only ACKs the last part.
- // Previous parts were ACK'd as they were received.
- acknowledgeLastIncomingSms(success, rc, null);
- }
+ private BroadcastReceiver mResultReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent.getAction().equals(Intent.ACTION_DEVICE_STORAGE_LOW)) {
+ mStorageAvailable = false;
+ mCm.reportSmsMemoryStatus(false, obtainMessage(EVENT_REPORT_MEMORY_STATUS_DONE));
+ } else if (intent.getAction().equals(Intent.ACTION_DEVICE_STORAGE_OK)) {
+ mStorageAvailable = true;
+ mCm.reportSmsMemoryStatus(true, obtainMessage(EVENT_REPORT_MEMORY_STATUS_DONE));
+ } else {
+ // Assume the intent is one of the SMS receive intents that
+ // was sent as an ordered broadcast. Check result and ACK.
+ int rc = getResultCode();
+ boolean success = (rc == Activity.RESULT_OK)
+ || (rc == Intents.RESULT_SMS_HANDLED);
+
+ // For a multi-part message, this only ACKs the last part.
+ // Previous parts were ACK'd as they were received.
+ acknowledgeLastIncomingSms(success, rc, null);
}
-
- };
+ }
+ };
}
diff --git a/telephony/java/com/android/internal/telephony/SipPhoneNotifier.java b/telephony/java/com/android/internal/telephony/SipPhoneNotifier.java
new file mode 100644
index 0000000..4092c69
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/SipPhoneNotifier.java
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+
+import com.android.internal.telephony.ITelephonyRegistry;
+
+/**
+ * Temporary. Will be removed after integrating with CallManager.
+ * 100% copy from DefaultPhoneNotifier. Cannot access its package level
+ * constructor; thus the copy.
+ * @hide
+ */
+public class SipPhoneNotifier implements PhoneNotifier {
+
+ static final String LOG_TAG = "GSM";
+ private static final boolean DBG = true;
+ private ITelephonyRegistry mRegistry;
+
+ public SipPhoneNotifier() {
+ mRegistry = ITelephonyRegistry.Stub.asInterface(ServiceManager.getService(
+ "telephony.registry"));
+ }
+
+ public void notifyPhoneState(Phone sender) {
+ Call ringingCall = sender.getRingingCall();
+ String incomingNumber = "";
+ if (ringingCall != null && ringingCall.getEarliestConnection() != null){
+ incomingNumber = ringingCall.getEarliestConnection().getAddress();
+ }
+ try {
+ mRegistry.notifyCallState(convertCallState(sender.getState()), incomingNumber);
+ } catch (RemoteException ex) {
+ // system process is dead
+ }
+ }
+
+ public void notifyServiceState(Phone sender) {
+ try {
+ mRegistry.notifyServiceState(sender.getServiceState());
+ } catch (RemoteException ex) {
+ // system process is dead
+ }
+ }
+
+ public void notifySignalStrength(Phone sender) {
+ try {
+ mRegistry.notifySignalStrength(sender.getSignalStrength());
+ } catch (RemoteException ex) {
+ // system process is dead
+ }
+ }
+
+ public void notifyMessageWaitingChanged(Phone sender) {
+ try {
+ mRegistry.notifyMessageWaitingChanged(sender.getMessageWaitingIndicator());
+ } catch (RemoteException ex) {
+ // system process is dead
+ }
+ }
+
+ public void notifyCallForwardingChanged(Phone sender) {
+ try {
+ mRegistry.notifyCallForwardingChanged(sender.getCallForwardingIndicator());
+ } catch (RemoteException ex) {
+ // system process is dead
+ }
+ }
+
+ public void notifyDataActivity(Phone sender) {
+ try {
+ mRegistry.notifyDataActivity(convertDataActivityState(sender.getDataActivityState()));
+ } catch (RemoteException ex) {
+ // system process is dead
+ }
+ }
+
+ public void notifyDataConnection(Phone sender, String reason) {
+ TelephonyManager telephony = TelephonyManager.getDefault();
+ try {
+ mRegistry.notifyDataConnection(
+ convertDataState(sender.getDataConnectionState()),
+ sender.isDataConnectivityPossible(), reason,
+ sender.getActiveApn(),
+ sender.getActiveApnTypes(),
+ sender.getInterfaceName(null),
+ ((telephony!=null) ? telephony.getNetworkType() :
+ TelephonyManager.NETWORK_TYPE_UNKNOWN),
+ sender.getGateway(null));
+ } catch (RemoteException ex) {
+ // system process is dead
+ }
+ }
+
+ public void notifyDataConnectionFailed(Phone sender, String reason) {
+ try {
+ mRegistry.notifyDataConnectionFailed(reason);
+ } catch (RemoteException ex) {
+ // system process is dead
+ }
+ }
+
+ public void notifyCellLocation(Phone sender) {
+ Bundle data = new Bundle();
+ sender.getCellLocation().fillInNotifierBundle(data);
+ try {
+ mRegistry.notifyCellLocation(data);
+ } catch (RemoteException ex) {
+ // system process is dead
+ }
+ }
+
+ private void log(String s) {
+ Log.d(LOG_TAG, "[PhoneNotifier] " + s);
+ }
+
+ /**
+ * Convert the {@link State} enum into the TelephonyManager.CALL_STATE_* constants
+ * for the public API.
+ */
+ public static int convertCallState(Phone.State state) {
+ switch (state) {
+ case RINGING:
+ return TelephonyManager.CALL_STATE_RINGING;
+ case OFFHOOK:
+ return TelephonyManager.CALL_STATE_OFFHOOK;
+ default:
+ return TelephonyManager.CALL_STATE_IDLE;
+ }
+ }
+
+ /**
+ * Convert the TelephonyManager.CALL_STATE_* constants into the {@link State} enum
+ * for the public API.
+ */
+ public static Phone.State convertCallState(int state) {
+ switch (state) {
+ case TelephonyManager.CALL_STATE_RINGING:
+ return Phone.State.RINGING;
+ case TelephonyManager.CALL_STATE_OFFHOOK:
+ return Phone.State.OFFHOOK;
+ default:
+ return Phone.State.IDLE;
+ }
+ }
+
+ /**
+ * Convert the {@link DataState} enum into the TelephonyManager.DATA_* constants
+ * for the public API.
+ */
+ public static int convertDataState(Phone.DataState state) {
+ switch (state) {
+ case CONNECTING:
+ return TelephonyManager.DATA_CONNECTING;
+ case CONNECTED:
+ return TelephonyManager.DATA_CONNECTED;
+ case SUSPENDED:
+ return TelephonyManager.DATA_SUSPENDED;
+ default:
+ return TelephonyManager.DATA_DISCONNECTED;
+ }
+ }
+
+ /**
+ * Convert the TelephonyManager.DATA_* constants into {@link DataState} enum
+ * for the public API.
+ */
+ public static Phone.DataState convertDataState(int state) {
+ switch (state) {
+ case TelephonyManager.DATA_CONNECTING:
+ return Phone.DataState.CONNECTING;
+ case TelephonyManager.DATA_CONNECTED:
+ return Phone.DataState.CONNECTED;
+ case TelephonyManager.DATA_SUSPENDED:
+ return Phone.DataState.SUSPENDED;
+ default:
+ return Phone.DataState.DISCONNECTED;
+ }
+ }
+
+ /**
+ * Convert the {@link DataState} enum into the TelephonyManager.DATA_* constants
+ * for the public API.
+ */
+ public static int convertDataActivityState(Phone.DataActivityState state) {
+ switch (state) {
+ case DATAIN:
+ return TelephonyManager.DATA_ACTIVITY_IN;
+ case DATAOUT:
+ return TelephonyManager.DATA_ACTIVITY_OUT;
+ case DATAINANDOUT:
+ return TelephonyManager.DATA_ACTIVITY_INOUT;
+ case DORMANT:
+ return TelephonyManager.DATA_ACTIVITY_DORMANT;
+ default:
+ return TelephonyManager.DATA_ACTIVITY_NONE;
+ }
+ }
+
+ /**
+ * Convert the TelephonyManager.DATA_* constants into the {@link DataState} enum
+ * for the public API.
+ */
+ public static Phone.DataActivityState convertDataActivityState(int state) {
+ switch (state) {
+ case TelephonyManager.DATA_ACTIVITY_IN:
+ return Phone.DataActivityState.DATAIN;
+ case TelephonyManager.DATA_ACTIVITY_OUT:
+ return Phone.DataActivityState.DATAOUT;
+ case TelephonyManager.DATA_ACTIVITY_INOUT:
+ return Phone.DataActivityState.DATAINANDOUT;
+ case TelephonyManager.DATA_ACTIVITY_DORMANT:
+ return Phone.DataActivityState.DORMANT;
+ default:
+ return Phone.DataActivityState.NONE;
+ }
+ }
+}
diff --git a/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java b/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
index 7aecf5b..840d366 100755
--- a/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
@@ -454,9 +454,14 @@ public class CDMAPhone extends PhoneBase {
return mMeid;
}
- //returns MEID in CDMA
+ //returns MEID or ESN in CDMA
public String getDeviceId() {
- return getMeid();
+ String id = getMeid();
+ if ((id == null) || id.matches("^0*$")) {
+ Log.d(LOG_TAG, "getDeviceId(): MEID is not initialized use ESN");
+ id = getEsn();
+ }
+ return id;
}
public String getDeviceSvn() {
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaCall.java b/telephony/java/com/android/internal/telephony/cdma/CdmaCall.java
index c3bb01f..4ad61bb 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaCall.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaCall.java
@@ -75,8 +75,7 @@ public final class CdmaCall extends Call {
public Phone
getPhone() {
- //TODO, see GsmCall
- return null;
+ return owner.phone;
}
public boolean isMultiparty() {
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java b/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
index ed93aea..8eaf4a2 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
@@ -28,21 +28,20 @@ import android.database.SQLException;
import android.os.AsyncResult;
import android.os.Message;
import android.os.SystemProperties;
+import android.preference.PreferenceManager;
import android.provider.Telephony;
import android.provider.Telephony.Sms.Intents;
-import android.preference.PreferenceManager;
-import android.util.Config;
-import android.util.Log;
import android.telephony.SmsManager;
import android.telephony.SmsMessage.MessageClass;
+import android.util.Config;
+import android.util.Log;
-import com.android.internal.telephony.TelephonyProperties;
import com.android.internal.telephony.CommandsInterface;
+import com.android.internal.telephony.SMSDispatcher;
import com.android.internal.telephony.SmsHeader;
import com.android.internal.telephony.SmsMessageBase;
-import com.android.internal.telephony.SMSDispatcher;
import com.android.internal.telephony.SmsMessageBase.TextEncodingDetails;
-import com.android.internal.telephony.cdma.SmsMessage;
+import com.android.internal.telephony.TelephonyProperties;
import com.android.internal.telephony.cdma.sms.SmsEnvelope;
import com.android.internal.telephony.cdma.sms.UserData;
import com.android.internal.util.HexDump;
@@ -51,20 +50,16 @@ import java.io.ByteArrayOutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
-import java.lang.Boolean;
final class CdmaSMSDispatcher extends SMSDispatcher {
private static final String TAG = "CDMA";
- private CDMAPhone mCdmaPhone;
-
private byte[] mLastDispatchedSmsFingerprint;
private byte[] mLastAcknowledgedSmsFingerprint;
CdmaSMSDispatcher(CDMAPhone phone) {
super(phone);
- mCdmaPhone = phone;
}
/**
@@ -129,7 +124,7 @@ final class CdmaSMSDispatcher extends SMSDispatcher {
Log.d(TAG, "Voicemail count=" + voicemailCount);
// Store the voicemail count in preferences.
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(
- ((CDMAPhone) mPhone).getContext());
+ mPhone.getContext());
SharedPreferences.Editor editor = sp.edit();
editor.putInt(CDMAPhone.VM_COUNT_CDMA, voicemailCount);
editor.commit();
@@ -176,7 +171,7 @@ final class CdmaSMSDispatcher extends SMSDispatcher {
* TODO(cleanup): Why are we using a getter method for this
* (and for so many other sms fields)? Trivial getters and
* setters like this are direct violations of the style guide.
- * If the purpose is to protect agaist writes (by not
+ * If the purpose is to protect against writes (by not
* providing a setter) then any protection is illusory (and
* hence bad) for cases where the values are not primitives,
* such as this call for the header. Since this is an issue
@@ -440,7 +435,7 @@ final class CdmaSMSDispatcher extends SMSDispatcher {
protected void sendSms(SmsTracker tracker) {
HashMap map = tracker.mData;
- byte smsc[] = (byte[]) map.get("smsc");
+ // byte smsc[] = (byte[]) map.get("smsc"); // unused for CDMA
byte pdu[] = (byte[]) map.get("pdu");
Message reply = obtainMessage(EVENT_SEND_SMS_COMPLETE, tracker);
diff --git a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
index b50502c..0f3b8ff 100755
--- a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
@@ -287,7 +287,7 @@ public class SmsMessage extends SmsMessageBase {
* @param destAddr Address of the recipient.
* @param message String representation of the message payload.
* @param statusReportRequested Indicates whether a report is requested for this message.
- * @param headerData Array containing the data for the User Data Header, preceded
+ * @param smsHeader Array containing the data for the User Data Header, preceded
* by the Element Identifiers.
* @return a <code>SubmitPdu</code> containing the encoded SC
* address, if applicable, and the encoded message.
@@ -355,7 +355,7 @@ public class SmsMessage extends SmsMessageBase {
* Get an SMS-SUBMIT PDU for a data message to a destination address &amp; port
*
* @param destAddr the address of the destination for the message
- * @param userDara the data for the message
+ * @param userData the data for the message
* @param statusReportRequested Indicates whether a report is requested for this message.
* @return a <code>SubmitPdu</code> containing the encoded SC
* address, if applicable, and the encoded message.
@@ -446,7 +446,7 @@ public class SmsMessage extends SmsMessageBase {
*/
public static TextEncodingDetails calculateLength(CharSequence messageBody,
boolean use7bitOnly) {
- return BearerData.calcTextEncodingDetails(messageBody.toString(), use7bitOnly);
+ return BearerData.calcTextEncodingDetails(messageBody, use7bitOnly);
}
/**
diff --git a/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
index c7032ac..ab79fe9 100644
--- a/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
+++ b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
@@ -405,7 +405,8 @@ public final class BearerData {
/**
* Calculate the message text encoding length, fragmentation, and other details.
*
- * @param force ignore (but still count) illegal characters if true
+ * @param msg message text
+ * @param force7BitEncoding ignore (but still count) illegal characters if true
* @return septet count, or -1 on failure
*/
public static TextEncodingDetails calcTextEncodingDetails(CharSequence msg,
@@ -427,9 +428,10 @@ public final class BearerData {
ted.codeUnitCount = msg.length();
int octets = ted.codeUnitCount * 2;
if (octets > MAX_USER_DATA_BYTES) {
- ted.msgCount = (octets / MAX_USER_DATA_BYTES_WITH_HEADER) + 1;
- ted.codeUnitsRemaining = (MAX_USER_DATA_BYTES_WITH_HEADER
- - (octets % MAX_USER_DATA_BYTES_WITH_HEADER))/2;
+ ted.msgCount = (octets + (MAX_USER_DATA_BYTES_WITH_HEADER - 1)) /
+ MAX_USER_DATA_BYTES_WITH_HEADER;
+ ted.codeUnitsRemaining = ((ted.msgCount *
+ MAX_USER_DATA_BYTES_WITH_HEADER) - octets) / 2;
} else {
ted.msgCount = 1;
ted.codeUnitsRemaining = (MAX_USER_DATA_BYTES - octets)/2;
@@ -802,9 +804,8 @@ public final class BearerData {
* Create serialized representation for BearerData object.
* (See 3GPP2 C.R1001-F, v1.0, section 4.5 for layout details)
*
- * @param bearerData an instance of BearerData.
- *
- * @return data byta array of raw encoded SMS bearer data.
+ * @param bData an instance of BearerData.
+ * @return byte array of raw encoded SMS bearer data.
*/
public static byte[] encode(BearerData bData) {
bData.hasUserDataHeader = ((bData.userData != null) &&
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmCall.java b/telephony/java/com/android/internal/telephony/gsm/GsmCall.java
index 9542d20..58124a2 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmCall.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmCall.java
@@ -70,8 +70,7 @@ class GsmCall extends Call {
public Phone
getPhone() {
- //TODO
- return null;
+ return owner.phone;
}
public boolean
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
index 5f651e7..f6d4491 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
@@ -442,8 +442,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
if ((state == State.IDLE || state == State.SCANNING)
&& (gprsState == ServiceState.STATE_IN_SERVICE || noAutoAttach)
&& mGsmPhone.mSIMRecords.getRecordsLoaded()
- && (mGsmPhone.mSST.isConcurrentVoiceAndData() ||
- phone.getState() == Phone.State.IDLE )
+ && phone.getState() == Phone.State.IDLE
&& isDataAllowed()
&& !mIsPsRestricted
&& desiredPowerState ) {
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java b/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
index d720516..3079a64 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
@@ -27,13 +27,12 @@ import android.telephony.ServiceState;
import android.util.Config;
import android.util.Log;
-import com.android.internal.telephony.IccUtils;
-import com.android.internal.telephony.SmsMessageBase.TextEncodingDetails;
-import com.android.internal.telephony.gsm.SmsMessage;
import com.android.internal.telephony.CommandsInterface;
+import com.android.internal.telephony.IccUtils;
import com.android.internal.telephony.SMSDispatcher;
import com.android.internal.telephony.SmsHeader;
import com.android.internal.telephony.SmsMessageBase;
+import com.android.internal.telephony.SmsMessageBase.TextEncodingDetails;
import java.util.ArrayList;
import java.util.HashMap;
@@ -97,20 +96,20 @@ final class GsmSMSDispatcher extends SMSDispatcher {
if (sms.isTypeZero()) {
// As per 3GPP TS 23.040 9.2.3.9, Type Zero messages should not be
// Displayed/Stored/Notified. They should only be acknowledged.
- Log.d(TAG, "Received short message type 0, Dont display or store it. Send Ack");
+ Log.d(TAG, "Received short message type 0, Don't display or store it. Send Ack");
return Intents.RESULT_SMS_HANDLED;
}
// Special case the message waiting indicator messages
if (sms.isMWISetMessage()) {
mGsmPhone.updateMessageWaitingIndicator(true);
- handled |= sms.isMwiDontStore();
+ handled = sms.isMwiDontStore();
if (Config.LOGD) {
Log.d(TAG, "Received voice mail indicator set SMS shouldStore=" + !handled);
}
} else if (sms.isMWIClearMessage()) {
mGsmPhone.updateMessageWaitingIndicator(false);
- handled |= sms.isMwiDontStore();
+ handled = sms.isMwiDontStore();
if (Config.LOGD) {
Log.d(TAG, "Received voice mail indicator clear SMS shouldStore=" + !handled);
}
@@ -301,7 +300,7 @@ final class GsmSMSDispatcher extends SMSDispatcher {
map.put("smsc", pdus.encodedScAddress);
map.put("pdu", pdus.encodedMessage);
- SmsTracker tracker = SmsTrackerFactory(map, sentIntent, deliveryIntent);
+ SmsTracker tracker = SmsTrackerFactory(map, sentIntent, deliveryIntent);
sendSms(tracker);
}
}
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
index d539f6f..90ecbd7 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
@@ -650,6 +650,7 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
int lac = -1;
int cid = -1;
int regState = -1;
+ int psc = -1;
if (states.length > 0) {
try {
regState = Integer.parseInt(states[0]);
@@ -661,6 +662,11 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
cid = Integer.parseInt(states[2], 16);
}
}
+ if (states.length > 14) {
+ if (states[14] != null && states[14].length() > 0) {
+ psc = Integer.parseInt(states[14], 16);
+ }
+ }
} catch (NumberFormatException ex) {
Log.w(LOG_TAG, "error parsing RegistrationState: " + ex);
}
@@ -677,6 +683,7 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
// LAC and CID are -1 if not avail
newCellLoc.setLacAndCid(lac, cid);
+ newCellLoc.setPsc(psc);
break;
case EVENT_POLL_STATE_GPRS:
diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
index 4fd62fb..9a3c476 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
@@ -800,9 +800,10 @@ public class SmsMessage extends SmsMessageBase{
int septets = GsmAlphabet.countGsmSeptets(msgBody, !use7bitOnly);
ted.codeUnitCount = septets;
if (septets > MAX_USER_DATA_SEPTETS) {
- ted.msgCount = (septets / MAX_USER_DATA_SEPTETS_WITH_HEADER) + 1;
- ted.codeUnitsRemaining = MAX_USER_DATA_SEPTETS_WITH_HEADER
- - (septets % MAX_USER_DATA_SEPTETS_WITH_HEADER);
+ ted.msgCount = (septets + (MAX_USER_DATA_SEPTETS_WITH_HEADER - 1)) /
+ MAX_USER_DATA_SEPTETS_WITH_HEADER;
+ ted.codeUnitsRemaining = (ted.msgCount *
+ MAX_USER_DATA_SEPTETS_WITH_HEADER) - septets;
} else {
ted.msgCount = 1;
ted.codeUnitsRemaining = MAX_USER_DATA_SEPTETS - septets;
@@ -812,9 +813,10 @@ public class SmsMessage extends SmsMessageBase{
int octets = msgBody.length() * 2;
ted.codeUnitCount = msgBody.length();
if (octets > MAX_USER_DATA_BYTES) {
- ted.msgCount = (octets / MAX_USER_DATA_BYTES_WITH_HEADER) + 1;
- ted.codeUnitsRemaining = (MAX_USER_DATA_BYTES_WITH_HEADER
- - (octets % MAX_USER_DATA_BYTES_WITH_HEADER))/2;
+ ted.msgCount = (octets + (MAX_USER_DATA_BYTES_WITH_HEADER - 1)) /
+ MAX_USER_DATA_BYTES_WITH_HEADER;
+ ted.codeUnitsRemaining = ((ted.msgCount *
+ MAX_USER_DATA_BYTES_WITH_HEADER) - octets) / 2;
} else {
ted.msgCount = 1;
ted.codeUnitsRemaining = (MAX_USER_DATA_BYTES - octets)/2;
diff --git a/telephony/java/com/android/internal/telephony/sip/CallFailCause.java b/telephony/java/com/android/internal/telephony/sip/CallFailCause.java
new file mode 100644
index 0000000..58fb408
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/sip/CallFailCause.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.sip;
+
+/**
+ * Call fail causes from TS 24.008 .
+ * These are mostly the cause codes we need to distinguish for the UI.
+ * See 22.001 Annex F.4 for mapping of cause codes to local tones.
+ *
+ * {@hide}
+ *
+ */
+public interface CallFailCause {
+ static final int NORMAL_CLEARING = 16;
+ // Busy Tone
+ static final int USER_BUSY = 17;
+
+ // No Tone
+ static final int NUMBER_CHANGED = 22;
+ static final int STATUS_ENQUIRY = 30;
+ static final int NORMAL_UNSPECIFIED = 31;
+
+ // Congestion Tone
+ static final int NO_CIRCUIT_AVAIL = 34;
+ static final int TEMPORARY_FAILURE = 41;
+ static final int SWITCHING_CONGESTION = 42;
+ static final int CHANNEL_NOT_AVAIL = 44;
+ static final int QOS_NOT_AVAIL = 49;
+ static final int BEARER_NOT_AVAIL = 58;
+
+ // others
+ static final int ACM_LIMIT_EXCEEDED = 68;
+ static final int CALL_BARRED = 240;
+ static final int FDN_BLOCKED = 241;
+ static final int ERROR_UNSPECIFIED = 0xffff;
+}
diff --git a/telephony/java/com/android/internal/telephony/sip/CallProxy.java b/telephony/java/com/android/internal/telephony/sip/CallProxy.java
new file mode 100644
index 0000000..fad9663
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/sip/CallProxy.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.sip;
+
+import com.android.internal.telephony.*;
+import java.util.List;
+
+// TODO: remove this class after integrating with CallManager
+class CallProxy extends Call {
+ private Call mTarget;
+
+ void setTarget(Call target) {
+ mTarget = target;
+ }
+
+ @Override
+ public List<Connection> getConnections() {
+ return mTarget.getConnections();
+ }
+
+ @Override
+ public Phone getPhone() {
+ return mTarget.getPhone();
+ }
+
+ @Override
+ public boolean isMultiparty() {
+ return mTarget.isMultiparty();
+ }
+
+ @Override
+ public void hangup() throws CallStateException {
+ mTarget.hangup();
+ }
+
+ @Override
+ public boolean hasConnection(Connection c) {
+ return mTarget.hasConnection(c);
+ }
+
+ @Override
+ public boolean hasConnections() {
+ return mTarget.hasConnections();
+ }
+
+ @Override
+ public State getState() {
+ return mTarget.getState();
+ }
+
+ @Override
+ public boolean isIdle() {
+ return mTarget.isIdle();
+ }
+
+ @Override
+ public Connection getEarliestConnection() {
+ return mTarget.getEarliestConnection();
+ }
+
+ @Override
+ public long getEarliestCreateTime() {
+ return mTarget.getEarliestCreateTime();
+ }
+
+ @Override
+ public long getEarliestConnectTime() {
+ return mTarget.getEarliestConnectTime();
+ }
+
+ @Override
+ public boolean isDialingOrAlerting() {
+ return mTarget.isDialingOrAlerting();
+ }
+
+ @Override
+ public boolean isRinging() {
+ return mTarget.isRinging();
+ }
+
+ @Override
+ public Connection getLatestConnection() {
+ return mTarget.getLatestConnection();
+ }
+
+ @Override
+ public boolean isGeneric() {
+ return mTarget.isGeneric();
+ }
+
+ @Override
+ public void setGeneric(boolean generic) {
+ mTarget.setGeneric(generic);
+ }
+
+ @Override
+ public void hangupIfAlive() {
+ mTarget.hangupIfAlive();
+ }
+}
diff --git a/telephony/java/com/android/internal/telephony/sip/SipCallBase.java b/telephony/java/com/android/internal/telephony/sip/SipCallBase.java
new file mode 100644
index 0000000..e7eda4f
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/sip/SipCallBase.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.sip;
+
+import com.android.internal.telephony.Call;
+import com.android.internal.telephony.CallStateException;
+import com.android.internal.telephony.Connection;
+import com.android.internal.telephony.DriverCall;
+import com.android.internal.telephony.Phone;
+
+import android.net.sip.SipManager;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import javax.sip.SipException;
+
+abstract class SipCallBase extends Call {
+ private static final int MAX_CONNECTIONS_PER_CALL = 5;
+
+ protected List<Connection> connections = new ArrayList<Connection>();
+
+ protected abstract void setState(State newState);
+
+ public void dispose() {
+ }
+
+ public List<Connection> getConnections() {
+ // FIXME should return Collections.unmodifiableList();
+ return connections;
+ }
+
+ public boolean isMultiparty() {
+ return connections.size() > 1;
+ }
+
+ public String toString() {
+ return state.toString();
+ }
+
+ /**
+ * Called by SipConnection when it has disconnected
+ */
+ void connectionDisconnected(Connection conn) {
+ if (state != State.DISCONNECTED) {
+ /* If only disconnected connections remain, we are disconnected*/
+
+ boolean hasOnlyDisconnectedConnections = true;
+
+ for (int i = 0, s = connections.size() ; i < s; i ++) {
+ if (connections.get(i).getState()
+ != State.DISCONNECTED
+ ) {
+ hasOnlyDisconnectedConnections = false;
+ break;
+ }
+ }
+
+ if (hasOnlyDisconnectedConnections) {
+ state = State.DISCONNECTED;
+ }
+ }
+ }
+
+
+ /*package*/ void detach(Connection conn) {
+ connections.remove(conn);
+
+ if (connections.size() == 0) {
+ state = State.IDLE;
+ }
+ }
+
+ /**
+ * @return true if there's no space in this call for additional
+ * connections to be added via "conference"
+ */
+ /*package*/ boolean isFull() {
+ return connections.size() == MAX_CONNECTIONS_PER_CALL;
+ }
+
+ void clearDisconnected() {
+ for (Iterator<Connection> it = connections.iterator(); it.hasNext(); ) {
+ Connection c = it.next();
+ if (c.getState() == State.DISCONNECTED) it.remove();
+ }
+
+ if (connections.isEmpty()) setState(State.IDLE);
+ }
+}
diff --git a/telephony/java/com/android/internal/telephony/sip/SipCommandInterface.java b/telephony/java/com/android/internal/telephony/sip/SipCommandInterface.java
new file mode 100644
index 0000000..33c89f8
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/sip/SipCommandInterface.java
@@ -0,0 +1,373 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.sip;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.Message;
+
+import com.android.internal.telephony.BaseCommands;
+import com.android.internal.telephony.CommandsInterface;
+import com.android.internal.telephony.UUSInfo;
+import com.android.internal.telephony.gsm.SmsBroadcastConfigInfo;
+
+/**
+ * SIP doesn't need CommandsInterface. The class does nothing but made to work
+ * with PhoneBase's constructor.
+ */
+class SipCommandInterface extends BaseCommands implements CommandsInterface {
+ SipCommandInterface(Context context) {
+ super(context);
+ }
+
+ @Override public void setOnNITZTime(Handler h, int what, Object obj) {
+ }
+
+ public void getIccCardStatus(Message result) {
+ }
+
+ public void supplyIccPin(String pin, Message result) {
+ }
+
+ public void supplyIccPuk(String puk, String newPin, Message result) {
+ }
+
+ public void supplyIccPin2(String pin, Message result) {
+ }
+
+ public void supplyIccPuk2(String puk, String newPin2, Message result) {
+ }
+
+ public void changeIccPin(String oldPin, String newPin, Message result) {
+ }
+
+ public void changeIccPin2(String oldPin2, String newPin2, Message result) {
+ }
+
+ public void changeBarringPassword(String facility, String oldPwd,
+ String newPwd, Message result) {
+ }
+
+ public void supplyNetworkDepersonalization(String netpin, Message result) {
+ }
+
+ public void getCurrentCalls(Message result) {
+ }
+
+ @Deprecated public void getPDPContextList(Message result) {
+ }
+
+ public void getDataCallList(Message result) {
+ }
+
+ public void dial(String address, int clirMode, Message result) {
+ }
+
+ public void dial(String address, int clirMode, UUSInfo uusInfo,
+ Message result) {
+ }
+
+ public void getIMSI(Message result) {
+ }
+
+ public void getIMEI(Message result) {
+ }
+
+ public void getIMEISV(Message result) {
+ }
+
+
+ public void hangupConnection (int gsmIndex, Message result) {
+ }
+
+ public void hangupWaitingOrBackground (Message result) {
+ }
+
+ public void hangupForegroundResumeBackground (Message result) {
+ }
+
+ public void switchWaitingOrHoldingAndActive (Message result) {
+ }
+
+ public void conference (Message result) {
+ }
+
+
+ public void setPreferredVoicePrivacy(boolean enable, Message result) {
+ }
+
+ public void getPreferredVoicePrivacy(Message result) {
+ }
+
+ public void separateConnection (int gsmIndex, Message result) {
+ }
+
+ public void acceptCall (Message result) {
+ }
+
+ public void rejectCall (Message result) {
+ }
+
+ public void explicitCallTransfer (Message result) {
+ }
+
+ public void getLastCallFailCause (Message result) {
+ }
+
+ /** @deprecated */
+ public void getLastPdpFailCause (Message result) {
+ }
+
+ public void getLastDataCallFailCause (Message result) {
+ }
+
+ public void setMute (boolean enableMute, Message response) {
+ }
+
+ public void getMute (Message response) {
+ }
+
+ public void getSignalStrength (Message result) {
+ }
+
+ public void getRegistrationState (Message result) {
+ }
+
+ public void getGPRSRegistrationState (Message result) {
+ }
+
+ public void getOperator(Message result) {
+ }
+
+ public void sendDtmf(char c, Message result) {
+ }
+
+ public void startDtmf(char c, Message result) {
+ }
+
+ public void stopDtmf(Message result) {
+ }
+
+ public void sendBurstDtmf(String dtmfString, int on, int off,
+ Message result) {
+ }
+
+ public void sendSMS (String smscPDU, String pdu, Message result) {
+ }
+
+ public void sendCdmaSms(byte[] pdu, Message result) {
+ }
+
+ public void deleteSmsOnSim(int index, Message response) {
+ }
+
+ public void deleteSmsOnRuim(int index, Message response) {
+ }
+
+ public void writeSmsToSim(int status, String smsc, String pdu, Message response) {
+ }
+
+ public void writeSmsToRuim(int status, String pdu, Message response) {
+ }
+
+ public void setupDefaultPDP(String apn, String user, String password,
+ Message result) {
+ }
+
+ public void deactivateDefaultPDP(int cid, Message result) {
+ }
+
+ public void setupDataCall(String radioTechnology, String profile,
+ String apn, String user, String password, String authType,
+ Message result) {
+ }
+
+ public void deactivateDataCall(int cid, Message result) {
+ }
+
+ public void setRadioPower(boolean on, Message result) {
+ }
+
+ public void setSuppServiceNotifications(boolean enable, Message result) {
+ }
+
+ public void acknowledgeLastIncomingGsmSms(boolean success, int cause,
+ Message result) {
+ }
+
+ public void acknowledgeLastIncomingCdmaSms(boolean success, int cause,
+ Message result) {
+ }
+
+
+ public void iccIO (int command, int fileid, String path, int p1, int p2,
+ int p3, String data, String pin2, Message result) {
+ }
+
+ public void getCLIR(Message result) {
+ }
+
+ public void setCLIR(int clirMode, Message result) {
+ }
+
+ public void queryCallWaiting(int serviceClass, Message response) {
+ }
+
+ public void setCallWaiting(boolean enable, int serviceClass,
+ Message response) {
+ }
+
+ public void setNetworkSelectionModeAutomatic(Message response) {
+ }
+
+ public void setNetworkSelectionModeManual(
+ String operatorNumeric, Message response) {
+ }
+
+ public void getNetworkSelectionMode(Message response) {
+ }
+
+ public void getAvailableNetworks(Message response) {
+ }
+
+ public void setCallForward(int action, int cfReason, int serviceClass,
+ String number, int timeSeconds, Message response) {
+ }
+
+ public void queryCallForwardStatus(int cfReason, int serviceClass,
+ String number, Message response) {
+ }
+
+ public void queryCLIP(Message response) {
+ }
+
+ public void getBasebandVersion (Message response) {
+ }
+
+ public void queryFacilityLock (String facility, String password,
+ int serviceClass, Message response) {
+ }
+
+ public void setFacilityLock (String facility, boolean lockState,
+ String password, int serviceClass, Message response) {
+ }
+
+ public void sendUSSD (String ussdString, Message response) {
+ }
+
+ public void cancelPendingUssd (Message response) {
+ }
+
+ public void resetRadio(Message result) {
+ }
+
+ public void invokeOemRilRequestRaw(byte[] data, Message response) {
+ }
+
+ public void invokeOemRilRequestStrings(String[] strings, Message response) {
+ }
+
+ public void setBandMode (int bandMode, Message response) {
+ }
+
+ public void queryAvailableBandMode (Message response) {
+ }
+
+ public void sendTerminalResponse(String contents, Message response) {
+ }
+
+ public void sendEnvelope(String contents, Message response) {
+ }
+
+ public void handleCallSetupRequestFromSim(
+ boolean accept, Message response) {
+ }
+
+ public void setPreferredNetworkType(int networkType , Message response) {
+ }
+
+ public void getPreferredNetworkType(Message response) {
+ }
+
+ public void getNeighboringCids(Message response) {
+ }
+
+ public void setLocationUpdates(boolean enable, Message response) {
+ }
+
+ public void getSmscAddress(Message result) {
+ }
+
+ public void setSmscAddress(String address, Message result) {
+ }
+
+ public void reportSmsMemoryStatus(boolean available, Message result) {
+ }
+
+ public void reportStkServiceIsRunning(Message result) {
+ }
+
+ public void getGsmBroadcastConfig(Message response) {
+ }
+
+ public void setGsmBroadcastConfig(SmsBroadcastConfigInfo[] config, Message response) {
+ }
+
+ public void setGsmBroadcastActivation(boolean activate, Message response) {
+ }
+
+
+ // ***** Methods for CDMA support
+ public void getDeviceIdentity(Message response) {
+ }
+
+ public void getCDMASubscription(Message response) {
+ }
+
+ public void setPhoneType(int phoneType) { //Set by CDMAPhone and GSMPhone constructor
+ }
+
+ public void queryCdmaRoamingPreference(Message response) {
+ }
+
+ public void setCdmaRoamingPreference(int cdmaRoamingType, Message response) {
+ }
+
+ public void setCdmaSubscription(int cdmaSubscription , Message response) {
+ }
+
+ public void queryTTYMode(Message response) {
+ }
+
+ public void setTTYMode(int ttyMode, Message response) {
+ }
+
+ public void sendCDMAFeatureCode(String FeatureCode, Message response) {
+ }
+
+ public void getCdmaBroadcastConfig(Message response) {
+ }
+
+ public void setCdmaBroadcastConfig(int[] configValuesArray, Message response) {
+ }
+
+ public void setCdmaBroadcastActivation(boolean activate, Message response) {
+ }
+
+ public void exitEmergencyCallbackMode(Message response) {
+ }
+}
diff --git a/telephony/java/com/android/internal/telephony/sip/SipConnectionBase.java b/telephony/java/com/android/internal/telephony/sip/SipConnectionBase.java
new file mode 100644
index 0000000..6c989b4
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/sip/SipConnectionBase.java
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.sip;
+
+import android.content.Context;
+import android.net.sip.SipAudioCall;
+import android.os.Message;
+import android.os.Registrant;
+import android.os.SystemClock;
+import android.util.Log;
+import android.telephony.PhoneNumberUtils;
+import android.telephony.ServiceState;
+
+import com.android.internal.telephony.*;
+
+abstract class SipConnectionBase extends Connection {
+ //***** Event Constants
+ private static final int EVENT_DTMF_DONE = 1;
+ private static final int EVENT_PAUSE_DONE = 2;
+ private static final int EVENT_NEXT_POST_DIAL = 3;
+ private static final int EVENT_WAKE_LOCK_TIMEOUT = 4;
+
+ //***** Constants
+ private static final int PAUSE_DELAY_FIRST_MILLIS = 100;
+ private static final int PAUSE_DELAY_MILLIS = 3 * 1000;
+ private static final int WAKE_LOCK_TIMEOUT_MILLIS = 60*1000;
+
+ private static final String LOG_TAG = "SIP_CONN";
+
+ private SipAudioCall mSipAudioCall;
+
+ private String dialString; // outgoing calls only
+ private String postDialString; // outgoing calls only
+ private int nextPostDialChar; // index into postDialString
+ private boolean isIncoming;
+ private boolean disconnected;
+
+ int index; // index in SipCallTracker.connections[], -1 if unassigned
+ // The Sip index is 1 + this
+
+ /*
+ * These time/timespan values are based on System.currentTimeMillis(),
+ * i.e., "wall clock" time.
+ */
+ private long createTime;
+ private long connectTime;
+ private long disconnectTime;
+
+ /*
+ * These time/timespan values are based on SystemClock.elapsedRealTime(),
+ * i.e., time since boot. They are appropriate for comparison and
+ * calculating deltas.
+ */
+ private long connectTimeReal;
+ private long duration;
+ private long holdingStartTime; // The time when the Connection last transitioned
+ // into HOLDING
+
+ private DisconnectCause mCause = DisconnectCause.NOT_DISCONNECTED;
+ private PostDialState postDialState = PostDialState.NOT_STARTED;
+
+ SipConnectionBase(String calleeSipUri) {
+ dialString = calleeSipUri;
+
+ postDialString = PhoneNumberUtils.extractPostDialPortion(dialString);
+
+ isIncoming = false;
+ createTime = System.currentTimeMillis();
+ }
+
+ protected void setState(Call.State state) {
+ switch (state) {
+ case ACTIVE:
+ if (connectTime == 0) {
+ connectTimeReal = SystemClock.elapsedRealtime();
+ connectTime = System.currentTimeMillis();
+ }
+ break;
+ case DISCONNECTED:
+ duration = SystemClock.elapsedRealtime() - connectTimeReal;
+ disconnectTime = System.currentTimeMillis();
+ break;
+ case HOLDING:
+ holdingStartTime = SystemClock.elapsedRealtime();
+ break;
+ }
+ }
+
+ @Override
+ public long getCreateTime() {
+ return createTime;
+ }
+
+ @Override
+ public long getConnectTime() {
+ return connectTime;
+ }
+
+ @Override
+ public long getDisconnectTime() {
+ return disconnectTime;
+ }
+
+ @Override
+ public long getDurationMillis() {
+ if (connectTimeReal == 0) {
+ return 0;
+ } else if (duration == 0) {
+ return SystemClock.elapsedRealtime() - connectTimeReal;
+ } else {
+ return duration;
+ }
+ }
+
+ @Override
+ public long getHoldDurationMillis() {
+ if (getState() != Call.State.HOLDING) {
+ // If not holding, return 0
+ return 0;
+ } else {
+ return SystemClock.elapsedRealtime() - holdingStartTime;
+ }
+ }
+
+ @Override
+ public DisconnectCause getDisconnectCause() {
+ return mCause;
+ }
+
+ void setDisconnectCause(DisconnectCause cause) {
+ mCause = cause;
+ }
+
+ @Override
+ public PostDialState getPostDialState() {
+ return postDialState;
+ }
+
+ @Override
+ public void proceedAfterWaitChar() {
+ // TODO
+ }
+
+ @Override
+ public void proceedAfterWildChar(String str) {
+ // TODO
+ }
+
+ @Override
+ public void cancelPostDial() {
+ // TODO
+ }
+
+ protected abstract Phone getPhone();
+
+ DisconnectCause disconnectCauseFromCode(int causeCode) {
+ /**
+ * See 22.001 Annex F.4 for mapping of cause codes
+ * to local tones
+ */
+
+ switch (causeCode) {
+ case CallFailCause.USER_BUSY:
+ return DisconnectCause.BUSY;
+
+ case CallFailCause.NO_CIRCUIT_AVAIL:
+ case CallFailCause.TEMPORARY_FAILURE:
+ case CallFailCause.SWITCHING_CONGESTION:
+ case CallFailCause.CHANNEL_NOT_AVAIL:
+ case CallFailCause.QOS_NOT_AVAIL:
+ case CallFailCause.BEARER_NOT_AVAIL:
+ return DisconnectCause.CONGESTION;
+
+ case CallFailCause.ACM_LIMIT_EXCEEDED:
+ return DisconnectCause.LIMIT_EXCEEDED;
+
+ case CallFailCause.CALL_BARRED:
+ return DisconnectCause.CALL_BARRED;
+
+ case CallFailCause.FDN_BLOCKED:
+ return DisconnectCause.FDN_BLOCKED;
+
+ case CallFailCause.ERROR_UNSPECIFIED:
+ case CallFailCause.NORMAL_CLEARING:
+ default:
+ Phone phone = getPhone();
+ int serviceState = phone.getServiceState().getState();
+ if (serviceState == ServiceState.STATE_POWER_OFF) {
+ return DisconnectCause.POWER_OFF;
+ } else if (serviceState == ServiceState.STATE_OUT_OF_SERVICE
+ || serviceState == ServiceState.STATE_EMERGENCY_ONLY ) {
+ return DisconnectCause.OUT_OF_SERVICE;
+ } else if (causeCode == CallFailCause.ERROR_UNSPECIFIED) {
+ return DisconnectCause.ERROR_UNSPECIFIED;
+ } else if (causeCode == CallFailCause.NORMAL_CLEARING) {
+ return DisconnectCause.NORMAL;
+ } else {
+ // If nothing else matches, report unknown call drop reason
+ // to app, not NORMAL call end.
+ return DisconnectCause.ERROR_UNSPECIFIED;
+ }
+ }
+ }
+
+ @Override
+ public String getRemainingPostDialString() {
+ if (postDialState == PostDialState.CANCELLED
+ || postDialState == PostDialState.COMPLETE
+ || postDialString == null
+ || postDialString.length() <= nextPostDialChar) {
+ return "";
+ }
+
+ return postDialString.substring(nextPostDialChar);
+ }
+
+ private void log(String msg) {
+ Log.d(LOG_TAG, "[SipConn] " + msg);
+ }
+
+ @Override
+ public int getNumberPresentation() {
+ // TODO: add PRESENTATION_URL
+ return Connection.PRESENTATION_ALLOWED;
+ }
+
+ /*
+ @Override
+ public UUSInfo getUUSInfo() {
+ // FIXME: what's this for SIP?
+ return null;
+ }
+ */
+}
diff --git a/telephony/java/com/android/internal/telephony/sip/SipPhone.java b/telephony/java/com/android/internal/telephony/sip/SipPhone.java
new file mode 100755
index 0000000..1325dd3
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/sip/SipPhone.java
@@ -0,0 +1,823 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.sip;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.net.Uri;
+import android.net.rtp.AudioGroup;
+import android.net.rtp.AudioStream;
+import android.net.sip.SipAudioCall;
+import android.net.sip.SipManager;
+import android.net.sip.SipProfile;
+import android.net.sip.SipSessionState;
+import android.os.AsyncResult;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Registrant;
+import android.os.RegistrantList;
+import android.os.SystemProperties;
+import android.preference.PreferenceManager;
+import android.provider.Telephony;
+import android.telephony.CellLocation;
+import android.telephony.PhoneNumberUtils;
+import android.telephony.ServiceState;
+import android.telephony.SignalStrength;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.internal.telephony.Call;
+import com.android.internal.telephony.CallerInfo;
+import com.android.internal.telephony.CallStateException;
+import com.android.internal.telephony.CommandsInterface;
+import com.android.internal.telephony.Connection;
+import com.android.internal.telephony.DataConnection;
+import com.android.internal.telephony.IccCard;
+import com.android.internal.telephony.IccFileHandler;
+import com.android.internal.telephony.IccPhoneBookInterfaceManager;
+import com.android.internal.telephony.IccSmsInterfaceManager;
+import com.android.internal.telephony.MmiCode;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneBase;
+import com.android.internal.telephony.PhoneNotifier;
+import com.android.internal.telephony.PhoneProxy;
+import com.android.internal.telephony.PhoneSubInfo;
+import com.android.internal.telephony.TelephonyProperties;
+import com.android.internal.telephony.UUSInfo;
+
+import java.io.IOException;
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.sip.SipException;
+
+/**
+ * {@hide}
+ */
+public class SipPhone extends SipPhoneBase {
+ private static final String LOG_TAG = "SipPhone";
+ private static final boolean LOCAL_DEBUG = true;
+
+ // A call that is ringing or (call) waiting
+ private SipCall ringingCall = new SipCall();
+ private SipCall foregroundCall = new SipCall();
+ private SipCall backgroundCall = new SipCall();
+
+ private SipManager mSipManager;
+ private SipProfile mProfile;
+
+ SipPhone (Context context, PhoneNotifier notifier, SipProfile profile) {
+ super(context, notifier);
+
+ Log.v(LOG_TAG, " +++++++++++++++++++++ new SipPhone: " + profile.getUriString());
+ ringingCall = new SipCall();
+ foregroundCall = new SipCall();
+ backgroundCall = new SipCall();
+ mProfile = profile;
+ mSipManager = SipManager.getInstance(context);
+
+ // FIXME: what's this for SIP?
+ //Change the system property
+ //SystemProperties.set(TelephonyProperties.CURRENT_ACTIVE_PHONE,
+ // new Integer(Phone.PHONE_TYPE_GSM).toString());
+ }
+
+ public String getPhoneName() {
+ return "SIP:" + getUriString(mProfile);
+ }
+
+ public String getSipUri() {
+ return mProfile.getUriString();
+ }
+
+ public boolean canTake(Object incomingCall) {
+ synchronized (SipPhone.class) {
+ if (!(incomingCall instanceof SipAudioCall)) return false;
+ if (ringingCall.getState().isAlive()) return false;
+
+ // FIXME: is it true that we cannot take any incoming call if
+ // both foreground and background are active
+ if (foregroundCall.getState().isAlive()
+ && backgroundCall.getState().isAlive()) {
+ return false;
+ }
+
+ SipAudioCall sipAudioCall = (SipAudioCall) incomingCall;
+ Log.v(LOG_TAG, " ++++++ taking call from: "
+ + sipAudioCall.getPeerProfile().getUriString());
+ String localUri = sipAudioCall.getLocalProfile().getUriString();
+ if (localUri.equals(mProfile.getUriString())) {
+ boolean makeCallWait = foregroundCall.getState().isAlive();
+ ringingCall.initIncomingCall(sipAudioCall, makeCallWait);
+ return true;
+ }
+ return false;
+ }
+ }
+
+ public void acceptCall() throws CallStateException {
+ synchronized (SipPhone.class) {
+ // FIXME if SWITCH fails, should retry with ANSWER
+ // in case the active/holding call disappeared and this
+ // is no longer call waiting
+
+ if (ringingCall.getState() == Call.State.INCOMING) {
+ Log.v(LOG_TAG, "acceptCall");
+ // Always unmute when answering a new call
+ setMute(false);
+ // make ringingCall foreground
+ foregroundCall.switchWith(ringingCall);
+ foregroundCall.acceptCall();
+ } else if (ringingCall.getState() == Call.State.WAITING) {
+ setMute(false);
+ switchHoldingAndActive();
+ // make ringingCall foreground
+ foregroundCall.switchWith(ringingCall);
+ foregroundCall.acceptCall();
+ } else {
+ throw new CallStateException("phone not ringing");
+ }
+ }
+ }
+
+ public void rejectCall() throws CallStateException {
+ synchronized (SipPhone.class) {
+ if (ringingCall.getState().isRinging()) {
+ Log.v(LOG_TAG, "rejectCall");
+ ringingCall.rejectCall();
+ } else {
+ throw new CallStateException("phone not ringing");
+ }
+ }
+ }
+
+ public Connection dial(String dialString, UUSInfo uusinfo) throws CallStateException {
+ return dial(dialString);
+ }
+
+ public Connection dial(String dialString) throws CallStateException {
+ synchronized (SipPhone.class) {
+ return dialInternal(dialString);
+ }
+ }
+
+ private Connection dialInternal(String dialString)
+ throws CallStateException {
+ // TODO: parse SIP URL?
+ // Need to make sure dialString gets parsed properly
+ //String newDialString = PhoneNumberUtils.stripSeparators(dialString);
+ //return mCT.dial(newDialString);
+ clearDisconnected();
+
+ if (!canDial()) {
+ throw new CallStateException("cannot dial in current state");
+ }
+ if (foregroundCall.getState() == SipCall.State.ACTIVE) {
+ switchHoldingAndActive();
+ }
+ if (foregroundCall.getState() != SipCall.State.IDLE) {
+ //we should have failed in !canDial() above before we get here
+ throw new CallStateException("cannot dial in current state");
+ }
+
+ setMute(false);
+ //cm.dial(pendingMO.address, clirMode, obtainCompleteMessage());
+ try {
+ Connection c = foregroundCall.dial(dialString);
+ return c;
+ } catch (SipException e) {
+ Log.e(LOG_TAG, "dial()", e);
+ throw new CallStateException("dial error: " + e);
+ }
+ }
+
+ public void switchHoldingAndActive() throws CallStateException {
+ Log.v(LOG_TAG, " ~~~~~~ switch fg and bg");
+ synchronized (SipPhone.class) {
+ foregroundCall.switchWith(backgroundCall);
+ if (backgroundCall.getState().isAlive()) backgroundCall.hold();
+ if (foregroundCall.getState().isAlive()) foregroundCall.unhold();
+ }
+ }
+
+ public boolean canConference() {
+ return true;
+ }
+
+ public void conference() throws CallStateException {
+ synchronized (SipPhone.class) {
+ if ((foregroundCall.getState() != SipCall.State.ACTIVE)
+ || (foregroundCall.getState() != SipCall.State.ACTIVE)) {
+ throw new CallStateException("wrong state to merge calls: fg="
+ + foregroundCall.getState() + ", bg="
+ + backgroundCall.getState());
+ }
+ foregroundCall.merge(backgroundCall);
+ }
+ }
+
+ public void conference(Call that) throws CallStateException {
+ synchronized (SipPhone.class) {
+ if (!(that instanceof SipCall)) {
+ throw new CallStateException("expect " + SipCall.class
+ + ", cannot merge with " + that.getClass());
+ }
+ foregroundCall.merge((SipCall) that);
+ }
+ }
+
+ public boolean canTransfer() {
+ return false;
+ }
+
+ public void explicitCallTransfer() throws CallStateException {
+ //mCT.explicitCallTransfer();
+ }
+
+ public void clearDisconnected() {
+ synchronized (SipPhone.class) {
+ ringingCall.clearDisconnected();
+ foregroundCall.clearDisconnected();
+ backgroundCall.clearDisconnected();
+
+ updatePhoneState();
+ notifyPreciseCallStateChanged();
+ }
+ }
+
+ public void sendDtmf(char c) {
+ if (!PhoneNumberUtils.is12Key(c)) {
+ Log.e(LOG_TAG,
+ "sendDtmf called with invalid character '" + c + "'");
+ } else if (foregroundCall.getState().isAlive()) {
+ synchronized (SipPhone.class) {
+ foregroundCall.sendDtmf(c);
+ }
+ }
+ }
+
+ public void startDtmf(char c) {
+ if (!PhoneNumberUtils.is12Key(c)) {
+ Log.e(LOG_TAG,
+ "startDtmf called with invalid character '" + c + "'");
+ } else {
+ sendDtmf(c);
+ }
+ }
+
+ public void stopDtmf() {
+ // no op
+ }
+
+ public void sendBurstDtmf(String dtmfString) {
+ Log.e(LOG_TAG, "[SipPhone] sendBurstDtmf() is a CDMA method");
+ }
+
+ public void getOutgoingCallerIdDisplay(Message onComplete) {
+ // FIXME: what to reply?
+ AsyncResult.forMessage(onComplete, null, null);
+ onComplete.sendToTarget();
+ }
+
+ public void setOutgoingCallerIdDisplay(int commandInterfaceCLIRMode,
+ Message onComplete) {
+ // FIXME: what's this for SIP?
+ AsyncResult.forMessage(onComplete, null, null);
+ onComplete.sendToTarget();
+ }
+
+ public void getCallWaiting(Message onComplete) {
+ // FIXME: what to reply?
+ AsyncResult.forMessage(onComplete, null, null);
+ onComplete.sendToTarget();
+ }
+
+ public void setCallWaiting(boolean enable, Message onComplete) {
+ // FIXME: what to reply?
+ Log.e(LOG_TAG, "call waiting not supported");
+ }
+
+ public void setMute(boolean muted) {
+ synchronized (SipPhone.class) {
+ foregroundCall.setMute(muted);
+ }
+ }
+
+ public boolean getMute() {
+ return foregroundCall.getMute();
+ }
+
+ public Call getForegroundCall() {
+ return foregroundCall;
+ }
+
+ public Call getBackgroundCall() {
+ return backgroundCall;
+ }
+
+ public Call getRingingCall() {
+ return ringingCall;
+ }
+
+ public ServiceState getServiceState() {
+ // FIXME: we may need to provide this when data connectivity is lost
+ // or when server is down
+ return super.getServiceState();
+ }
+
+ private String getUriString(SipProfile p) {
+ // SipProfile.getUriString() may contain "SIP:" and port
+ return p.getUserName() + "@" + getSipDomain(p);
+ }
+
+ private String getSipDomain(SipProfile p) {
+ String domain = p.getSipDomain();
+ // TODO: move this to SipProfile
+ if (domain.endsWith(":5060")) {
+ return domain.substring(0, domain.length() - 5);
+ } else {
+ return domain;
+ }
+ }
+
+ private class SipCall extends SipCallBase {
+ void switchWith(SipCall that) {
+ synchronized (SipPhone.class) {
+ SipCall tmp = new SipCall();
+ tmp.takeOver(this);
+ this.takeOver(that);
+ that.takeOver(tmp);
+ }
+ }
+
+ private void takeOver(SipCall that) {
+ connections = that.connections;
+ state = that.state;
+ for (Connection c : connections) {
+ ((SipConnection) c).changeOwner(this);
+ }
+ }
+
+ @Override
+ public Phone getPhone() {
+ return SipPhone.this;
+ }
+
+ @Override
+ public List<Connection> getConnections() {
+ synchronized (SipPhone.class) {
+ // FIXME should return Collections.unmodifiableList();
+ return connections;
+ }
+ }
+
+ private CallerInfo getCallerInfo(String number) {
+ CallerInfo info = CallerInfo.getCallerInfo(mContext, number);
+ if ((info == null) || (info.name == null)) return null;
+ Log.v(LOG_TAG, "++******++ got info from contact:");
+ Log.v(LOG_TAG, " name: " + info.name);
+ Log.v(LOG_TAG, " numb: " + info.phoneNumber);
+ Log.v(LOG_TAG, " pres: " + info.numberPresentation);
+ return info;
+ }
+
+ Connection dial(String calleeSipUri) throws SipException {
+ CallerInfo info = getCallerInfo(calleeSipUri);
+ if (!calleeSipUri.contains("@")) {
+ calleeSipUri += "@" + getSipDomain(mProfile);
+ if (info != null) info.phoneNumber = calleeSipUri;
+ }
+ try {
+ SipProfile callee =
+ new SipProfile.Builder(calleeSipUri).build();
+ SipConnection c = new SipConnection(this, callee, info);
+ connections.add(c);
+ c.dial();
+ setState(Call.State.DIALING);
+ return c;
+ } catch (ParseException e) {
+ // TODO: notify someone
+ throw new SipException("dial", e);
+ }
+ }
+
+ @Override
+ public void hangup() throws CallStateException {
+ synchronized (SipPhone.class) {
+ Log.v(LOG_TAG, "hang up call: " + getState() + ": " + this
+ + " on phone " + getPhone());
+ CallStateException excp = null;
+ for (Connection c : connections) {
+ try {
+ c.hangup();
+ } catch (CallStateException e) {
+ excp = e;
+ }
+ }
+ if (excp != null) throw excp;
+ setState(State.DISCONNECTING);
+ }
+ }
+
+ void initIncomingCall(SipAudioCall sipAudioCall, boolean makeCallWait) {
+ SipProfile callee = sipAudioCall.getPeerProfile();
+ CallerInfo info = getCallerInfo(getUriString(callee));
+ if (info == null) info = getCallerInfo(callee.getUserName());
+ if (info == null) info = getCallerInfo(callee.getDisplayName());
+ SipConnection c = new SipConnection(this, callee, info);
+ connections.add(c);
+
+ Call.State newState = makeCallWait ? State.WAITING : State.INCOMING;
+ c.initIncomingCall(sipAudioCall, newState);
+
+ setState(newState);
+ notifyNewRingingConnectionP(c);
+ }
+
+ void rejectCall() throws CallStateException {
+ hangup();
+ }
+
+ void acceptCall() throws CallStateException {
+ if (this != foregroundCall) {
+ throw new CallStateException("acceptCall() in a non-fg call");
+ }
+ if (connections.size() != 1) {
+ throw new CallStateException("acceptCall() in a conf call");
+ }
+ ((SipConnection) connections.get(0)).acceptCall();
+ }
+
+ void hold() throws CallStateException {
+ setState(State.HOLDING);
+ AudioGroup audioGroup = getAudioGroup();
+ if (audioGroup != null) {
+ audioGroup.setMode(AudioGroup.MODE_ON_HOLD);
+ }
+ for (Connection c : connections) ((SipConnection) c).hold();
+ }
+
+ void unhold() throws CallStateException {
+ setState(State.ACTIVE);
+ AudioGroup audioGroup = new AudioGroup();
+ for (Connection c : connections) {
+ ((SipConnection) c).unhold(audioGroup);
+ }
+ }
+
+ void setMute(boolean muted) {
+ AudioGroup audioGroup = getAudioGroup();
+ if (audioGroup == null) return;
+ audioGroup.setMode(
+ muted ? AudioGroup.MODE_MUTED : AudioGroup.MODE_NORMAL);
+ }
+
+ boolean getMute() {
+ AudioGroup audioGroup = getAudioGroup();
+ if (audioGroup == null) return false;
+ return (audioGroup.getMode() == AudioGroup.MODE_MUTED);
+ }
+
+ void merge(SipCall that) throws CallStateException {
+ AudioGroup audioGroup = getAudioGroup();
+ for (Connection c : that.connections) {
+ SipConnection conn = (SipConnection) c;
+ add(conn);
+ if (conn.getState() == Call.State.HOLDING) {
+ conn.unhold(audioGroup);
+ }
+ }
+ that.setState(Call.State.IDLE);
+ }
+
+ private void add(SipConnection conn) {
+ SipCall call = conn.getCall();
+ if (call == this) return;
+ if (call != null) call.connections.remove(conn);
+
+ connections.add(conn);
+ conn.changeOwner(this);
+ }
+
+ void sendDtmf(char c) {
+ AudioGroup audioGroup = getAudioGroup();
+ if (audioGroup == null) return;
+ audioGroup.sendDtmf(convertDtmf(c));
+ }
+
+ private int convertDtmf(char c) {
+ int code = c - '0';
+ if ((code < 0) || (code > 9)) {
+ switch (c) {
+ case '*': return 10;
+ case '#': return 11;
+ case 'A': return 12;
+ case 'B': return 13;
+ case 'C': return 14;
+ case 'D': return 15;
+ default:
+ throw new IllegalArgumentException(
+ "invalid DTMF char: " + (int) c);
+ }
+ }
+ return code;
+ }
+
+ @Override
+ protected void setState(State newState) {
+ if (state != newState) {
+ Log.v(LOG_TAG, "++******++ call state changed: " + state
+ + " --> " + newState + ": " + this + ": on phone "
+ + getPhone() + " " + connections.size());
+
+ if (newState == Call.State.ALERTING) {
+ state = newState; // need in ALERTING to enable ringback
+ SipPhone.this.startRingbackTone();
+ } else if (state == Call.State.ALERTING) {
+ SipPhone.this.stopRingbackTone();
+ }
+ state = newState;
+ updatePhoneState();
+ notifyPreciseCallStateChanged();
+ }
+ }
+
+ void onConnectionStateChanged(SipConnection conn) {
+ // this can be called back when a conf call is formed
+ if (state != State.ACTIVE) {
+ setState(conn.getState());
+ }
+ }
+
+ void onConnectionEnded(SipConnection conn) {
+ // set state to DISCONNECTED only when all conns are disconnected
+ if (state != State.DISCONNECTED) {
+ boolean allConnectionsDisconnected = true;
+ for (Connection c : connections) {
+ if (c.getState() != State.DISCONNECTED) {
+ allConnectionsDisconnected = false;
+ break;
+ }
+ }
+ if (allConnectionsDisconnected) setState(State.DISCONNECTED);
+ }
+ notifyDisconnectP(conn);
+ }
+
+ private AudioGroup getAudioGroup() {
+ if (connections.isEmpty()) return null;
+ return ((SipConnection) connections.get(0)).getAudioGroup();
+ }
+ }
+
+ private class SipConnection extends SipConnectionBase {
+ private SipCall mOwner;
+ private SipAudioCall mSipAudioCall;
+ private Call.State mState = Call.State.IDLE;
+ private SipProfile mPeer;
+ private boolean mIncoming = false;
+
+ private SipAudioCallAdapter mAdapter = new SipAudioCallAdapter() {
+ @Override
+ protected void onCallEnded(DisconnectCause cause) {
+ if (getDisconnectCause() != DisconnectCause.LOCAL) {
+ setDisconnectCause(cause);
+ }
+ synchronized (SipPhone.class) {
+ setState(Call.State.DISCONNECTED);
+ mOwner.onConnectionEnded(SipConnection.this);
+ Log.v(LOG_TAG, "-------- connection ended: "
+ + mPeer.getUriString() + ": "
+ + mSipAudioCall.getState() + ", cause: "
+ + getDisconnectCause() + ", on phone "
+ + getPhone());
+ }
+ }
+
+ @Override
+ public void onChanged(SipAudioCall call) {
+ synchronized (SipPhone.class) {
+ Call.State newState = getCallStateFrom(call);
+ if (mState == newState) return;
+ if (newState == Call.State.INCOMING) {
+ setState(mOwner.getState()); // INCOMING or WAITING
+ } else {
+ setState(newState);
+ }
+ mOwner.onConnectionStateChanged(SipConnection.this);
+ Log.v(LOG_TAG, "++******++ connection state changed: "
+ + mPeer.getUriString() + ": " + mState
+ + " on phone " + getPhone());
+ }
+ }
+
+ @Override
+ protected void onError(String errorMessage) {
+ Log.w(LOG_TAG, "SIP error: " + errorMessage);
+ if (mSipAudioCall.isInCall()) {
+ // Don't end the call when in call.
+ // TODO: how to deliver the error to PhoneApp
+ return;
+ }
+
+ // FIXME: specify error
+ onCallEnded(DisconnectCause.ERROR_UNSPECIFIED);
+ }
+ };
+
+ public SipConnection(SipCall owner, SipProfile callee,
+ CallerInfo info) {
+ super(getUriString(callee));
+ mOwner = owner;
+ mPeer = callee;
+ if (info == null) info = createCallerInfo();
+ setUserData(info);
+ }
+
+ private CallerInfo createCallerInfo() {
+ SipProfile p = mPeer;
+ String name = p.getDisplayName();
+ if (TextUtils.isEmpty(name)) name = p.getUserName();
+ CallerInfo info = new CallerInfo();
+ info.name = name;
+ info.phoneNumber = getUriString(p);
+ return info;
+ }
+
+ void initIncomingCall(SipAudioCall sipAudioCall, Call.State newState) {
+ setState(newState);
+ mSipAudioCall = sipAudioCall;
+ sipAudioCall.setListener(mAdapter); // call back to set state
+ mIncoming = true;
+ }
+
+ void acceptCall() throws CallStateException {
+ try {
+ mSipAudioCall.answerCall();
+ } catch (SipException e) {
+ throw new CallStateException("acceptCall(): " + e);
+ }
+ }
+
+ void changeOwner(SipCall owner) {
+ mOwner = owner;
+ }
+
+ AudioGroup getAudioGroup() {
+ if (mSipAudioCall == null) return null;
+ return mSipAudioCall.getAudioGroup();
+ }
+
+ void dial() throws SipException {
+ setState(Call.State.DIALING);
+ mSipAudioCall = mSipManager.makeAudioCall(mContext, mProfile,
+ mPeer, null);
+ mSipAudioCall.setRingbackToneEnabled(false);
+ mSipAudioCall.setListener(mAdapter);
+ }
+
+ void hold() throws CallStateException {
+ setState(Call.State.HOLDING);
+ try {
+ mSipAudioCall.holdCall();
+ } catch (SipException e) {
+ throw new CallStateException("hold(): " + e);
+ }
+ }
+
+ void unhold(AudioGroup audioGroup) throws CallStateException {
+ mSipAudioCall.setAudioGroup(audioGroup);
+ setState(Call.State.ACTIVE);
+ try {
+ mSipAudioCall.continueCall();
+ } catch (SipException e) {
+ throw new CallStateException("unhold(): " + e);
+ }
+ }
+
+ @Override
+ protected void setState(Call.State state) {
+ if (state == mState) return;
+ super.setState(state);
+ mState = state;
+ }
+
+ @Override
+ public Call.State getState() {
+ return mState;
+ }
+
+ @Override
+ public boolean isIncoming() {
+ return mIncoming;
+ }
+
+ @Override
+ public String getAddress() {
+ return getUriString(mPeer);
+ }
+
+ @Override
+ public SipCall getCall() {
+ return mOwner;
+ }
+
+ @Override
+ protected Phone getPhone() {
+ return mOwner.getPhone();
+ }
+
+ @Override
+ public void hangup() throws CallStateException {
+ synchronized (SipPhone.class) {
+ Log.v(LOG_TAG, "hangup conn: " + mPeer.getUriString() + ": "
+ + ": on phone " + getPhone().getPhoneName());
+ try {
+ mSipAudioCall.endCall();
+ setState(Call.State.DISCONNECTING);
+ setDisconnectCause(DisconnectCause.LOCAL);
+ } catch (SipException e) {
+ throw new CallStateException("hangup(): " + e);
+ }
+ }
+ }
+
+ @Override
+ public void separate() throws CallStateException {
+ synchronized (SipPhone.class) {
+ SipCall call = (SipCall) SipPhone.this.getBackgroundCall();
+ if (call.getState() != Call.State.IDLE) {
+ throw new CallStateException(
+ "cannot put conn back to a call in non-idle state: "
+ + call.getState());
+ }
+ Log.v(LOG_TAG, "separate conn: " + mPeer.getUriString()
+ + " from " + mOwner + " back to " + call);
+
+ AudioGroup audioGroup = call.getAudioGroup(); // may be null
+ call.add(this);
+ mSipAudioCall.setAudioGroup(audioGroup);
+ call.hold();
+ }
+ }
+
+ @Override
+ public UUSInfo getUUSInfo() {
+ return null;
+ }
+ }
+
+ private static Call.State getCallStateFrom(SipAudioCall sipAudioCall) {
+ if (sipAudioCall.isOnHold()) return Call.State.HOLDING;
+ SipSessionState sessionState = sipAudioCall.getState();
+ switch (sessionState) {
+ case READY_TO_CALL: return Call.State.IDLE;
+ case INCOMING_CALL:
+ case INCOMING_CALL_ANSWERING: return Call.State.INCOMING;
+ case OUTGOING_CALL: return Call.State.DIALING;
+ case OUTGOING_CALL_RING_BACK: return Call.State.ALERTING;
+ case OUTGOING_CALL_CANCELING: return Call.State.DISCONNECTING;
+ case IN_CALL: return Call.State.ACTIVE;
+ default:
+ Log.w(LOG_TAG, "illegal connection state: " + sessionState);
+ return Call.State.DISCONNECTED;
+ }
+ }
+
+ private abstract class SipAudioCallAdapter extends SipAudioCall.Adapter {
+ protected abstract void onCallEnded(Connection.DisconnectCause cause);
+ protected abstract void onError(String errorMessage);
+
+ @Override
+ public void onCallEnded(SipAudioCall call) {
+ onCallEnded(Connection.DisconnectCause.NORMAL);
+ }
+
+ @Override
+ public void onCallBusy(SipAudioCall call) {
+ onCallEnded(Connection.DisconnectCause.BUSY);
+ }
+
+ @Override
+ public void onError(SipAudioCall call, String errorMessage) {
+ onError(errorMessage);
+ }
+ }
+}
diff --git a/telephony/java/com/android/internal/telephony/sip/SipPhoneBase.java b/telephony/java/com/android/internal/telephony/sip/SipPhoneBase.java
new file mode 100755
index 0000000..4b7e991
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/sip/SipPhoneBase.java
@@ -0,0 +1,554 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.sip;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.net.Uri;
+import android.os.AsyncResult;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Registrant;
+import android.os.RegistrantList;
+import android.os.SystemProperties;
+import android.preference.PreferenceManager;
+import android.provider.Telephony;
+import android.telephony.CellLocation;
+import android.telephony.PhoneNumberUtils;
+import android.telephony.ServiceState;
+import android.telephony.SignalStrength;
+import android.text.TextUtils;
+import android.util.Log;
+
+import static com.android.internal.telephony.CommandsInterface.CF_ACTION_DISABLE;
+import static com.android.internal.telephony.CommandsInterface.CF_ACTION_ENABLE;
+import static com.android.internal.telephony.CommandsInterface.CF_ACTION_ERASURE;
+import static com.android.internal.telephony.CommandsInterface.CF_ACTION_REGISTRATION;
+import static com.android.internal.telephony.CommandsInterface.CF_REASON_ALL;
+import static com.android.internal.telephony.CommandsInterface.CF_REASON_ALL_CONDITIONAL;
+import static com.android.internal.telephony.CommandsInterface.CF_REASON_NO_REPLY;
+import static com.android.internal.telephony.CommandsInterface.CF_REASON_NOT_REACHABLE;
+import static com.android.internal.telephony.CommandsInterface.CF_REASON_BUSY;
+import static com.android.internal.telephony.CommandsInterface.CF_REASON_UNCONDITIONAL;
+import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_VOICE;
+import static com.android.internal.telephony.TelephonyProperties.PROPERTY_BASEBAND_VERSION;
+
+import com.android.internal.telephony.Call;
+import com.android.internal.telephony.CallStateException;
+import com.android.internal.telephony.CommandsInterface;
+import com.android.internal.telephony.Connection;
+import com.android.internal.telephony.DataConnection;
+import com.android.internal.telephony.IccCard;
+import com.android.internal.telephony.IccFileHandler;
+import com.android.internal.telephony.IccPhoneBookInterfaceManager;
+import com.android.internal.telephony.IccSmsInterfaceManager;
+import com.android.internal.telephony.MmiCode;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneBase;
+import com.android.internal.telephony.PhoneNotifier;
+import com.android.internal.telephony.PhoneProxy;
+import com.android.internal.telephony.PhoneSubInfo;
+import com.android.internal.telephony.TelephonyProperties;
+//import com.android.internal.telephony.UUSInfo;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+abstract class SipPhoneBase extends PhoneBase {
+ // NOTE that LOG_TAG here is "Sip", which means that log messages
+ // from this file will go into the radio log rather than the main
+ // log. (Use "adb logcat -b radio" to see them.)
+ static final String LOG_TAG = "SipPhone";
+ private static final boolean LOCAL_DEBUG = true;
+
+ //SipCallTracker mCT;
+ PhoneSubInfo mSubInfo;
+
+ Registrant mPostDialHandler;
+
+ final RegistrantList mRingbackRegistrants = new RegistrantList();
+
+ private State state = State.IDLE;
+
+ public SipPhoneBase(Context context, PhoneNotifier notifier) {
+ super(notifier, context, new SipCommandInterface(context), false);
+
+ // FIXME: what's this for SIP?
+ //Change the system property
+ //SystemProperties.set(TelephonyProperties.CURRENT_ACTIVE_PHONE,
+ // new Integer(Phone.PHONE_TYPE_GSM).toString());
+ }
+
+ public abstract Call getForegroundCall();
+
+ public abstract Call getBackgroundCall();
+
+ public abstract Call getRingingCall();
+
+ /*
+ public Connection dial(String dialString, UUSInfo uusInfo)
+ throws CallStateException {
+ // ignore UUSInfo
+ return dial(dialString);
+ }
+ */
+
+ void migrateFrom(SipPhoneBase from) {
+ migrate(mRingbackRegistrants, from.mRingbackRegistrants);
+ migrate(mPreciseCallStateRegistrants, from.mPreciseCallStateRegistrants);
+ migrate(mNewRingingConnectionRegistrants, from.mNewRingingConnectionRegistrants);
+ migrate(mIncomingRingRegistrants, from.mIncomingRingRegistrants);
+ migrate(mDisconnectRegistrants, from.mDisconnectRegistrants);
+ migrate(mServiceStateRegistrants, from.mServiceStateRegistrants);
+ migrate(mMmiCompleteRegistrants, from.mMmiCompleteRegistrants);
+ migrate(mMmiRegistrants, from.mMmiRegistrants);
+ migrate(mUnknownConnectionRegistrants, from.mUnknownConnectionRegistrants);
+ migrate(mSuppServiceFailedRegistrants, from.mSuppServiceFailedRegistrants);
+ }
+
+ static void migrate(RegistrantList to, RegistrantList from) {
+ from.removeCleared();
+ for (int i = 0, n = from.size(); i < n; i++) {
+ to.add((Registrant) from.get(i));
+ }
+ }
+
+ @Override
+ public void registerForRingbackTone(Handler h, int what, Object obj) {
+ mRingbackRegistrants.addUnique(h, what, obj);
+ }
+
+ @Override
+ public void unregisterForRingbackTone(Handler h) {
+ mRingbackRegistrants.remove(h);
+ }
+
+ protected void startRingbackTone() {
+ AsyncResult result = new AsyncResult(null, new Boolean(true), null);
+ mRingbackRegistrants.notifyRegistrants(result);
+ }
+
+ protected void stopRingbackTone() {
+ AsyncResult result = new AsyncResult(null, new Boolean(false), null);
+ mRingbackRegistrants.notifyRegistrants(result);
+ }
+
+ public void dispose() {
+ mIsTheCurrentActivePhone = false;
+ mSubInfo.dispose();
+ }
+
+ public void removeReferences() {
+ mSubInfo = null;
+ }
+
+ public ServiceState getServiceState() {
+ // FIXME: we may need to provide this when data connectivity is lost
+ // or when server is down
+ ServiceState s = new ServiceState();
+ s.setState(ServiceState.STATE_IN_SERVICE);
+ return s;
+ }
+
+ public CellLocation getCellLocation() {
+ return null; //mSST.cellLoc;
+ }
+
+ public State getState() {
+ return state;
+ }
+
+ public int getPhoneType() {
+ // FIXME: add SIP phone type
+ return Phone.PHONE_TYPE_GSM;
+ }
+
+ public SignalStrength getSignalStrength() {
+ return new SignalStrength();
+ }
+
+ public boolean getMessageWaitingIndicator() {
+ return false;
+ }
+
+ public boolean getCallForwardingIndicator() {
+ return false;
+ }
+
+ public List<? extends MmiCode> getPendingMmiCodes() {
+ return new ArrayList<MmiCode>(0);
+ }
+
+ public DataState getDataConnectionState() {
+ return DataState.DISCONNECTED;
+ }
+
+ public DataState getDataConnectionState(String apnType) {
+ return DataState.DISCONNECTED;
+ }
+
+ public DataActivityState getDataActivityState() {
+ return DataActivityState.NONE;
+ }
+
+ /**
+ * Notify any interested party of a Phone state change {@link Phone.State}
+ */
+ void notifyPhoneStateChanged() {
+ mNotifier.notifyPhoneState(this);
+ }
+
+ /**
+ * Notify registrants of a change in the call state. This notifies changes in {@link Call.State}
+ * Use this when changes in the precise call state are needed, else use notifyPhoneStateChanged.
+ */
+ void notifyPreciseCallStateChanged() {
+ /* we'd love it if this was package-scoped*/
+ super.notifyPreciseCallStateChangedP();
+ }
+
+ void notifyNewRingingConnection(Connection c) {
+ /* we'd love it if this was package-scoped*/
+ super.notifyNewRingingConnectionP(c);
+ }
+
+ void notifyDisconnect(Connection cn) {
+ mDisconnectRegistrants.notifyResult(cn);
+ }
+
+ void notifyUnknownConnection() {
+ mUnknownConnectionRegistrants.notifyResult(this);
+ }
+
+ void notifySuppServiceFailed(SuppService code) {
+ mSuppServiceFailedRegistrants.notifyResult(code);
+ }
+
+ void notifyServiceStateChanged(ServiceState ss) {
+ super.notifyServiceStateChangedP(ss);
+ }
+
+ public void notifyCallForwardingIndicator() {
+ mNotifier.notifyCallForwardingChanged(this);
+ }
+
+ public boolean canDial() {
+ int serviceState = getServiceState().getState();
+ Log.v(LOG_TAG, "canDial(): serviceState = " + serviceState);
+ if (serviceState == ServiceState.STATE_POWER_OFF) return false;
+
+ String disableCall = SystemProperties.get(
+ TelephonyProperties.PROPERTY_DISABLE_CALL, "false");
+ Log.v(LOG_TAG, "canDial(): disableCall = " + disableCall);
+ if (disableCall.equals("true")) return false;
+
+ Log.v(LOG_TAG, "canDial(): ringingCall: " + getRingingCall().getState());
+ Log.v(LOG_TAG, "canDial(): foregndCall: " + getForegroundCall().getState());
+ Log.v(LOG_TAG, "canDial(): backgndCall: " + getBackgroundCall().getState());
+ return !getRingingCall().isRinging()
+ && (!getForegroundCall().getState().isAlive()
+ || !getBackgroundCall().getState().isAlive());
+ }
+
+ public boolean handleInCallMmiCommands(String dialString)
+ throws CallStateException {
+ return false;
+ }
+
+ boolean isInCall() {
+ Call.State foregroundCallState = getForegroundCall().getState();
+ Call.State backgroundCallState = getBackgroundCall().getState();
+ Call.State ringingCallState = getRingingCall().getState();
+
+ return (foregroundCallState.isAlive() || backgroundCallState.isAlive()
+ || ringingCallState.isAlive());
+ }
+
+ public boolean handlePinMmi(String dialString) {
+ return false;
+ }
+
+ public void sendUssdResponse(String ussdMessge) {
+ }
+
+ public void registerForSuppServiceNotification(
+ Handler h, int what, Object obj) {
+ }
+
+ public void unregisterForSuppServiceNotification(Handler h) {
+ }
+
+ public void setRadioPower(boolean power) {
+ }
+
+ public String getVoiceMailNumber() {
+ return null;
+ }
+
+ public String getVoiceMailAlphaTag() {
+ return null;
+ }
+
+ public String getDeviceId() {
+ return null;
+ }
+
+ public String getDeviceSvn() {
+ return null;
+ }
+
+ public String getEsn() {
+ Log.e(LOG_TAG, "[SipPhone] getEsn() is a CDMA method");
+ return "0";
+ }
+
+ public String getMeid() {
+ Log.e(LOG_TAG, "[SipPhone] getMeid() is a CDMA method");
+ return "0";
+ }
+
+ public String getSubscriberId() {
+ return null;
+ }
+
+ public String getIccSerialNumber() {
+ return null;
+ }
+
+ public String getLine1Number() {
+ return null;
+ }
+
+ public String getLine1AlphaTag() {
+ return null;
+ }
+
+ public void setLine1Number(String alphaTag, String number, Message onComplete) {
+ // FIXME: what to reply for SIP?
+ AsyncResult.forMessage(onComplete, null, null);
+ onComplete.sendToTarget();
+ }
+
+ public void setVoiceMailNumber(String alphaTag, String voiceMailNumber,
+ Message onComplete) {
+ // FIXME: what to reply for SIP?
+ AsyncResult.forMessage(onComplete, null, null);
+ onComplete.sendToTarget();
+ }
+
+ private boolean isValidCommandInterfaceCFReason(int commandInterfaceCFReason) {
+ switch (commandInterfaceCFReason) {
+ case CF_REASON_UNCONDITIONAL:
+ case CF_REASON_BUSY:
+ case CF_REASON_NO_REPLY:
+ case CF_REASON_NOT_REACHABLE:
+ case CF_REASON_ALL:
+ case CF_REASON_ALL_CONDITIONAL:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ private boolean isValidCommandInterfaceCFAction (int commandInterfaceCFAction) {
+ switch (commandInterfaceCFAction) {
+ case CF_ACTION_DISABLE:
+ case CF_ACTION_ENABLE:
+ case CF_ACTION_REGISTRATION:
+ case CF_ACTION_ERASURE:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ protected boolean isCfEnable(int action) {
+ return (action == CF_ACTION_ENABLE) || (action == CF_ACTION_REGISTRATION);
+ }
+
+ public void getCallForwardingOption(int commandInterfaceCFReason, Message onComplete) {
+ if (isValidCommandInterfaceCFReason(commandInterfaceCFReason)) {
+ // FIXME: what to reply?
+ AsyncResult.forMessage(onComplete, null, null);
+ onComplete.sendToTarget();
+ }
+ }
+
+ public void setCallForwardingOption(int commandInterfaceCFAction,
+ int commandInterfaceCFReason, String dialingNumber,
+ int timerSeconds, Message onComplete) {
+ if (isValidCommandInterfaceCFAction(commandInterfaceCFAction)
+ && isValidCommandInterfaceCFReason(commandInterfaceCFReason)) {
+ // FIXME: what to reply?
+ AsyncResult.forMessage(onComplete, null, null);
+ onComplete.sendToTarget();
+ }
+ }
+
+ public void getOutgoingCallerIdDisplay(Message onComplete) {
+ // FIXME: what to reply?
+ AsyncResult.forMessage(onComplete, null, null);
+ onComplete.sendToTarget();
+ }
+
+ public void setOutgoingCallerIdDisplay(int commandInterfaceCLIRMode,
+ Message onComplete) {
+ // FIXME: what's this for SIP?
+ AsyncResult.forMessage(onComplete, null, null);
+ onComplete.sendToTarget();
+ }
+
+ public void getCallWaiting(Message onComplete) {
+ // FIXME: what to reply?
+ AsyncResult.forMessage(onComplete, null, null);
+ onComplete.sendToTarget();
+ }
+
+ public void setCallWaiting(boolean enable, Message onComplete) {
+ // FIXME: what to reply?
+ Log.e(LOG_TAG, "call waiting not supported");
+ }
+
+ public boolean getIccRecordsLoaded() {
+ return false;
+ }
+
+ public IccCard getIccCard() {
+ return null;
+ }
+
+ public void getAvailableNetworks(Message response) {
+ // FIXME: what to reply?
+ }
+
+ public void setNetworkSelectionModeAutomatic(Message response) {
+ // FIXME: what to reply?
+ }
+
+ public void selectNetworkManually(
+ com.android.internal.telephony.gsm.NetworkInfo network,
+ Message response) {
+ // FIXME: what to reply?
+ }
+
+ public void getNeighboringCids(Message response) {
+ // FIXME: what to reply?
+ }
+
+ public void setOnPostDialCharacter(Handler h, int what, Object obj) {
+ mPostDialHandler = new Registrant(h, what, obj);
+ }
+
+ public void getDataCallList(Message response) {
+ // FIXME: what to reply?
+ }
+
+ public List<DataConnection> getCurrentDataConnectionList () {
+ return null;
+ }
+
+ public void updateServiceLocation() {
+ }
+
+ public void enableLocationUpdates() {
+ }
+
+ public void disableLocationUpdates() {
+ }
+
+ public boolean getDataRoamingEnabled() {
+ return false;
+ }
+
+ public void setDataRoamingEnabled(boolean enable) {
+ }
+
+ public boolean enableDataConnectivity() {
+ return false;
+ }
+
+ public boolean disableDataConnectivity() {
+ return false;
+ }
+
+ public boolean isDataConnectivityPossible() {
+ return false;
+ }
+
+ boolean updateCurrentCarrierInProvider() {
+ return false;
+ }
+
+ public void saveClirSetting(int commandInterfaceCLIRMode) {
+ // FIXME: what's this for SIP?
+ }
+
+ /**
+ * Retrieves the PhoneSubInfo of the SipPhone
+ */
+ public PhoneSubInfo getPhoneSubInfo(){
+ return mSubInfo;
+ }
+
+ /** {@inheritDoc} */
+ public IccSmsInterfaceManager getIccSmsInterfaceManager(){
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ public IccPhoneBookInterfaceManager getIccPhoneBookInterfaceManager(){
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ public IccFileHandler getIccFileHandler(){
+ return null;
+ }
+
+ public void activateCellBroadcastSms(int activate, Message response) {
+ Log.e(LOG_TAG, "Error! This functionality is not implemented for SIP.");
+ }
+
+ public void getCellBroadcastSmsConfig(Message response) {
+ Log.e(LOG_TAG, "Error! This functionality is not implemented for SIP.");
+ }
+
+ public void setCellBroadcastSmsConfig(int[] configValuesArray, Message response){
+ Log.e(LOG_TAG, "Error! This functionality is not implemented for SIP.");
+ }
+
+ void updatePhoneState() {
+ State oldState = state;
+
+ if (getRingingCall().isRinging()) {
+ state = State.RINGING;
+ } else if (getForegroundCall().isIdle()
+ && getBackgroundCall().isIdle()) {
+ state = State.IDLE;
+ } else {
+ state = State.OFFHOOK;
+ }
+ Log.e(LOG_TAG, " ^^^^^^ new phone state: " + state);
+
+ if (state != oldState) {
+ notifyPhoneStateChanged();
+ }
+ }
+}
diff --git a/telephony/java/com/android/internal/telephony/sip/SipPhoneFactory.java b/telephony/java/com/android/internal/telephony/sip/SipPhoneFactory.java
new file mode 100644
index 0000000..611e3ea
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/sip/SipPhoneFactory.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.sip;
+
+import com.android.internal.telephony.PhoneNotifier;
+
+import android.content.Context;
+import android.net.sip.SipProfile;
+import android.util.Log;
+
+import java.text.ParseException;
+
+/**
+ * {@hide}
+ */
+public class SipPhoneFactory {
+ /**
+ * Makes a {@link SipPhone} object.
+ * @param sipUri the local SIP URI the phone runs on
+ * @param context {@code Context} needed to create a Phone object
+ * @param phoneNotifier {@code PhoneNotifier} needed to create a Phone
+ * object
+ * @return the {@code SipPhone} object or null if the SIP URI is not valid
+ */
+ public static SipPhone makePhone(String sipUri, Context context,
+ PhoneNotifier phoneNotifier) {
+ try {
+ SipProfile profile = new SipProfile.Builder(sipUri).build();
+ return new SipPhone(context, phoneNotifier, profile);
+ } catch (ParseException e) {
+ Log.w("SipPhoneFactory", "makePhone", e);
+ return null;
+ }
+ }
+}
diff --git a/telephony/java/com/android/internal/telephony/test/SimulatedCommands.java b/telephony/java/com/android/internal/telephony/test/SimulatedCommands.java
index a120f52..9c72e5a 100644
--- a/telephony/java/com/android/internal/telephony/test/SimulatedCommands.java
+++ b/telephony/java/com/android/internal/telephony/test/SimulatedCommands.java
@@ -844,7 +844,6 @@ public final class SimulatedCommands extends BaseCommands
ret[11] = null;
ret[12] = null;
ret[13] = null;
- ret[14] = null;
resultSuccess(result, ret);
}