summaryrefslogtreecommitdiffstats
path: root/telephony
diff options
context:
space:
mode:
Diffstat (limited to 'telephony')
-rw-r--r--telephony/java/android/telephony/PhoneNumberUtils.java22
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java21
-rw-r--r--telephony/java/android/telephony/gsm/GsmCellLocation.java30
-rw-r--r--telephony/java/android/telephony/gsm/SmsMessage.java12
-rw-r--r--telephony/java/com/android/internal/telephony/CallManager.java1766
-rw-r--r--telephony/java/com/android/internal/telephony/CallerInfo.java36
-rw-r--r--telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java94
-rw-r--r--telephony/java/com/android/internal/telephony/Connection.java6
-rw-r--r--telephony/java/com/android/internal/telephony/DataConnectionTracker.java2
-rw-r--r--telephony/java/com/android/internal/telephony/DefaultPhoneNotifier.java3
-rw-r--r--telephony/java/com/android/internal/telephony/GsmAlphabet.java10
-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/IccSmsInterfaceManager.java8
-rw-r--r--telephony/java/com/android/internal/telephony/MccTable.java28
-rw-r--r--telephony/java/com/android/internal/telephony/Phone.java7
-rw-r--r--telephony/java/com/android/internal/telephony/PhoneBase.java6
-rw-r--r--telephony/java/com/android/internal/telephony/PhoneFactory.java11
-rw-r--r--telephony/java/com/android/internal/telephony/PhoneProxy.java4
-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.java89
-rwxr-xr-xtelephony/java/com/android/internal/telephony/cdma/CDMAPhone.java13
-rw-r--r--telephony/java/com/android/internal/telephony/cdma/CdmaCall.java3
-rw-r--r--telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java23
-rw-r--r--telephony/java/com/android/internal/telephony/cdma/RuimSmsInterfaceManager.java5
-rw-r--r--telephony/java/com/android/internal/telephony/cdma/SmsMessage.java10
-rw-r--r--telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java15
-rw-r--r--telephony/java/com/android/internal/telephony/cdma/sms/UserData.java2
-rw-r--r--telephony/java/com/android/internal/telephony/gsm/GSMPhone.java10
-rw-r--r--telephony/java/com/android/internal/telephony/gsm/GsmCall.java3
-rw-r--r--telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java9
-rw-r--r--telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java13
-rw-r--r--telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java17
-rw-r--r--telephony/java/com/android/internal/telephony/gsm/SimSmsInterfaceManager.java5
-rw-r--r--telephony/java/com/android/internal/telephony/gsm/SmsMessage.java14
-rw-r--r--telephony/java/com/android/internal/telephony/sip/SipCallBase.java55
-rw-r--r--telephony/java/com/android/internal/telephony/sip/SipCommandInterface.java372
-rw-r--r--telephony/java/com/android/internal/telephony/sip/SipConnectionBase.java174
-rwxr-xr-xtelephony/java/com/android/internal/telephony/sip/SipPhone.java872
-rwxr-xr-xtelephony/java/com/android/internal/telephony/sip/SipPhoneBase.java439
-rw-r--r--telephony/java/com/android/internal/telephony/sip/SipPhoneFactory.java49
-rw-r--r--telephony/java/com/android/internal/telephony/test/SimulatedCommands.java1
-rw-r--r--telephony/mockril/Android.mk31
-rw-r--r--telephony/mockril/src/com/android/internal/telephony/mockril/MockRilController.java218
-rw-r--r--telephony/tests/telephonytests/Android.mk2
-rw-r--r--telephony/tests/telephonytests/AndroidManifest.xml7
-rw-r--r--telephony/tests/telephonytests/src/android/telephony/PhoneNumberUtilsTest.java23
-rw-r--r--telephony/tests/telephonytests/src/com/android/frameworks/telephonytests/TelephonyMockRilTestRunner.java93
-rw-r--r--telephony/tests/telephonytests/src/com/android/internal/telephony/CallerInfoTest.java (renamed from telephony/tests/telephonytests/src/com/android/telephonytest/unit/CallerInfoUnitTest.java)5
-rw-r--r--telephony/tests/telephonytests/src/com/android/internal/telephony/SmsMessageBodyTest.java194
-rw-r--r--telephony/tests/telephonytests/src/com/android/internal/telephony/mockril/MockRilTest.java304
-rw-r--r--telephony/tests/telephonytests/src/com/android/telephonytest/unit/PhoneNumberUtilsUnitTest.java63
54 files changed, 4980 insertions, 256 deletions
diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java
index 1d3ad81..8e4f6fc 100644
--- a/telephony/java/android/telephony/PhoneNumberUtils.java
+++ b/telephony/java/android/telephony/PhoneNumberUtils.java
@@ -136,14 +136,14 @@ public class PhoneNumberUtils
Uri uri = intent.getData();
String scheme = uri.getScheme();
- if (scheme.equals("tel")) {
+ if (scheme.equals("tel") || scheme.equals("sip")) {
return uri.getSchemeSpecificPart();
}
// 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,22 @@ public class PhoneNumberUtils
}
/**
+ * Determines if the specified number is actually a URI
+ * (i.e. a SIP address) rather than a regular PSTN phone number,
+ * based on whether or not the number contains an "@" character.
+ *
+ * @hide
+ * @param number
+ * @return true if number contains @
+ */
+ public static boolean isUriNumber(String number) {
+ // Note we allow either "@" or "%40" to indicate a URI, in case
+ // the passed-in string is URI-escaped. (Neither "@" nor "%40"
+ // will ever be found in a legal PSTN number.)
+ return number != null && (number.contains("@") || number.contains("%40"));
+ }
+
+ /**
* 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..313bc82 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,22 @@ public class GsmCellLocation extends CellLocation {
}
/**
+ * On a UMTS network, returns the primary scrambling code of the serving
+ * cell.
+ *
+ * @return primary scrambling code for UMTS, -1 if unknown or GSM
+ */
+ 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 +86,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 +113,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 +141,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..b09df82
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/CallManager.java
@@ -0,0 +1,1766 @@
+/*
+ * 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.os.Registrant;
+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 ="CallManager";
+ private static final boolean DBG = true;
+ private static final boolean VDBG = true;
+
+ private static final int EVENT_DISCONNECT = 100;
+ private static final int EVENT_PRECISE_CALL_STATE_CHANGED = 101;
+ private static final int EVENT_NEW_RINGING_CONNECTION = 102;
+ private static final int EVENT_UNKNOWN_CONNECTION = 103;
+ private static final int EVENT_INCOMING_RING = 104;
+ private static final int EVENT_RINGBACK_TONE = 105;
+ private static final int EVENT_IN_CALL_VOICE_PRIVACY_ON = 106;
+ private static final int EVENT_IN_CALL_VOICE_PRIVACY_OFF = 107;
+ private static final int EVENT_CALL_WAITING = 108;
+ private static final int EVENT_DISPLAY_INFO = 109;
+ private static final int EVENT_SIGNAL_INFO = 110;
+ private static final int EVENT_CDMA_OTA_STATUS_CHANGE = 111;
+ private static final int EVENT_RESEND_INCALL_MUTE = 112;
+ private static final int EVENT_MMI_INITIATE = 113;
+ private static final int EVENT_MMI_COMPLETE = 114;
+ private static final int EVENT_ECM_TIMER_RESET = 115;
+ private static final int EVENT_SUBSCRIPTION_INFO_READY = 116;
+ private static final int EVENT_SUPP_SERVICE_FAILED = 117;
+ private static final int EVENT_SERVICE_STATE_CHANGED = 118;
+ private static final int EVENT_POST_DIAL_CHARACTER = 119;
+
+ // Singleton instance
+ private static final CallManager INSTANCE = new CallManager();
+
+ // list of registered phones, which are PhoneBase objs
+ 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, which is PhoneBase obj
+ private Phone mDefaultPhone;
+
+ // state registrants
+ protected final RegistrantList mPreciseCallStateRegistrants
+ = new RegistrantList();
+
+ protected final RegistrantList mNewRingingConnectionRegistrants
+ = new RegistrantList();
+
+ protected final RegistrantList mIncomingRingRegistrants
+ = new RegistrantList();
+
+ protected final RegistrantList mDisconnectRegistrants
+ = new RegistrantList();
+
+ protected final RegistrantList mMmiRegistrants
+ = new RegistrantList();
+
+ protected final RegistrantList mUnknownConnectionRegistrants
+ = new RegistrantList();
+
+ protected final RegistrantList mRingbackToneRegistrants
+ = new RegistrantList();
+
+ protected final RegistrantList mInCallVoicePrivacyOnRegistrants
+ = new RegistrantList();
+
+ protected final RegistrantList mInCallVoicePrivacyOffRegistrants
+ = new RegistrantList();
+
+ protected final RegistrantList mCallWaitingRegistrants
+ = new RegistrantList();
+
+ protected final RegistrantList mDisplayInfoRegistrants
+ = new RegistrantList();
+
+ protected final RegistrantList mSignalInfoRegistrants
+ = new RegistrantList();
+
+ protected final RegistrantList mCdmaOtaStatusChangeRegistrants
+ = new RegistrantList();
+
+ protected final RegistrantList mResendIncallMuteRegistrants
+ = new RegistrantList();
+
+ protected final RegistrantList mMmiInitiateRegistrants
+ = new RegistrantList();
+
+ protected final RegistrantList mMmiCompleteRegistrants
+ = new RegistrantList();
+
+ protected final RegistrantList mEcmTimerResetRegistrants
+ = new RegistrantList();
+
+ protected final RegistrantList mSubscriptionInfoReadyRegistrants
+ = new RegistrantList();
+
+ protected final RegistrantList mSuppServiceFailedRegistrants
+ = new RegistrantList();
+
+ protected final RegistrantList mServiceStateChangedRegistrants
+ = new RegistrantList();
+
+ protected final RegistrantList mPostDialCharacterRegistrants
+ = 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;
+ }
+
+ /**
+ * Get the corresponding PhoneBase obj
+ *
+ * @param phone a Phone object
+ * @return the corresponding PhoneBase obj in Phone if Phone
+ * is a PhoneProxy obj
+ * or the Phone itself if Phone is not a PhoneProxy obj
+ */
+ private static Phone getPhoneBase(Phone phone) {
+ if (phone instanceof PhoneProxy) {
+ return phone.getForegroundCall().getPhone();
+ }
+ return phone;
+ }
+
+ /**
+ * Check if two phones refer to the same PhoneBase obj
+ *
+ * Note: PhoneBase, not PhoneProxy, is to be used inside of CallManager
+ *
+ * Both PhoneBase and PhoneProxy implement Phone interface, so
+ * they have same phone APIs, such as dial(). The real implementation, for
+ * example in GSM, is in GSMPhone as extend from PhoneBase, so that
+ * foregroundCall.getPhone() returns GSMPhone obj. On the other hand,
+ * PhoneFactory.getDefaultPhone() returns PhoneProxy obj, which has a class
+ * member of GSMPhone.
+ *
+ * So for phone returned by PhoneFacotry, which is used by PhoneApp,
+ * phone.getForegroundCall().getPhone() != phone
+ * but
+ * isSamePhone(phone, phone.getForegroundCall().getPhone()) == true
+ *
+ * @param p1 is the first Phone obj
+ * @param p2 is the second Phone obj
+ * @return true if p1 and p2 refer to the same phone
+ */
+ public static boolean isSamePhone(Phone p1, Phone p2) {
+ return (getPhoneBase(p1) == getPhoneBase(p2));
+ }
+
+ /**
+ * 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) {
+ s = Phone.State.RINGING;
+ } else if (phone.getState() == Phone.State.OFFHOOK) {
+ if (s == Phone.State.IDLE) 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 to be registered
+ * @return true if register successfully
+ */
+ public boolean registerPhone(Phone phone) {
+ Phone basePhone = getPhoneBase(phone);
+
+ if (basePhone != null && !mPhones.contains(basePhone)) {
+
+ if (VDBG) {
+ Log.d(LOG_TAG, "registerPhone(" +
+ phone.getPhoneName() + " " + phone + ")");
+ }
+
+ if (mPhones.isEmpty()) {
+ mDefaultPhone = basePhone;
+ }
+ mPhones.add(basePhone);
+ mRingingCalls.add(basePhone.getRingingCall());
+ mBackgroundCalls.add(basePhone.getBackgroundCall());
+ mForegroundCalls.add(basePhone.getForegroundCall());
+ registerForPhoneStates(basePhone);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * unregister phone from CallManager
+ * @param phone to be unregistered
+ */
+ public void unregisterPhone(Phone phone) {
+ Phone basePhone = getPhoneBase(phone);
+
+ if (basePhone != null && mPhones.contains(basePhone)) {
+
+ if (VDBG) {
+ Log.d(LOG_TAG, "unregisterPhone(" +
+ phone.getPhoneName() + " " + phone + ")");
+ }
+
+ mPhones.remove(basePhone);
+ mRingingCalls.remove(basePhone.getRingingCall());
+ mBackgroundCalls.remove(basePhone.getBackgroundCall());
+ mForegroundCalls.remove(basePhone.getForegroundCall());
+ unregisterForPhoneStates(basePhone);
+ if (basePhone == mDefaultPhone) {
+ if (mPhones.isEmpty()) {
+ mDefaultPhone = null;
+ } else {
+ mDefaultPhone = mPhones.get(0);
+ }
+ }
+ }
+ }
+
+ /**
+ * 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();
+ }
+
+ 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) {
+ // for common events supported by all phones
+ 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.registerForDisplayInfo(mHandler, EVENT_DISPLAY_INFO, null);
+ phone.registerForSignalInfo(mHandler, EVENT_SIGNAL_INFO, null);
+ phone.registerForResendIncallMute(mHandler, EVENT_RESEND_INCALL_MUTE, null);
+ phone.registerForMmiInitiate(mHandler, EVENT_MMI_INITIATE, null);
+ phone.registerForMmiComplete(mHandler, EVENT_MMI_COMPLETE, null);
+ phone.registerForSuppServiceFailed(mHandler, EVENT_SUPP_SERVICE_FAILED, null);
+ phone.registerForServiceStateChanged(mHandler, EVENT_SERVICE_STATE_CHANGED, null);
+
+ // for events supported only by GSM and CDMA phone
+ if (phone.getPhoneType() == Phone.PHONE_TYPE_GSM ||
+ phone.getPhoneType() == Phone.PHONE_TYPE_CDMA) {
+ phone.setOnPostDialCharacter(mHandler, EVENT_POST_DIAL_CHARACTER, null);
+ }
+
+ // for events supported only by CDMA phone
+ if (phone.getPhoneType() == Phone.PHONE_TYPE_CDMA ){
+ phone.registerForCdmaOtaStatusChange(mHandler, EVENT_CDMA_OTA_STATUS_CHANGE, null);
+ phone.registerForSubscriptionInfoReady(mHandler, EVENT_SUBSCRIPTION_INFO_READY, null);
+ phone.registerForCallWaiting(mHandler, EVENT_CALL_WAITING, null);
+ phone.registerForEcmTimerReset(mHandler, EVENT_ECM_TIMER_RESET, null);
+ }
+ }
+
+ private void unregisterForPhoneStates(Phone phone) {
+ // for common events supported by all phones
+ 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.unregisterForDisplayInfo(mHandler);
+ phone.unregisterForSignalInfo(mHandler);
+ phone.unregisterForResendIncallMute(mHandler);
+ phone.unregisterForMmiInitiate(mHandler);
+ phone.unregisterForMmiComplete(mHandler);
+ phone.unregisterForSuppServiceFailed(mHandler);
+ phone.unregisterForServiceStateChanged(mHandler);
+
+ // for events supported only by GSM and CDMA phone
+ if (phone.getPhoneType() == Phone.PHONE_TYPE_GSM ||
+ phone.getPhoneType() == Phone.PHONE_TYPE_CDMA) {
+ phone.setOnPostDialCharacter(null, EVENT_POST_DIAL_CHARACTER, null);
+ }
+
+ // for events supported only by CDMA phone
+ if (phone.getPhoneType() == Phone.PHONE_TYPE_CDMA ){
+ phone.unregisterForCdmaOtaStatusChange(mHandler);
+ phone.unregisterForSubscriptionInfoReady(mHandler);
+ phone.unregisterForCallWaiting(mHandler);
+ phone.unregisterForEcmTimerReset(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 (VDBG) {
+ Log.d(LOG_TAG, "acceptCall(" +ringingCall + " from " + ringingCall.getPhone() + ")");
+ Log.d(LOG_TAG, this.toString());
+ }
+
+ if ( hasActiveFgCall() ) {
+ Phone activePhone = getActiveFgCall().getPhone();
+ boolean hasBgCall = ! (activePhone.getBackgroundCall().isIdle());
+ boolean sameChannel = (activePhone == ringingPhone);
+
+ if (DBG) {
+ 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();
+
+ if (VDBG) {
+ Log.d(LOG_TAG, "End acceptCall(" +ringingCall + ")");
+ Log.d(LOG_TAG, this.toString());
+ }
+ }
+
+ /**
+ * 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 {
+ if (VDBG) {
+ Log.d(LOG_TAG, "rejectCall(" +ringingCall + ")");
+ Log.d(LOG_TAG, this.toString());
+ }
+
+ Phone ringingPhone = ringingCall.getPhone();
+
+ ringingPhone.rejectCall();
+
+ if (VDBG) {
+ Log.d(LOG_TAG, "End rejectCall(" +ringingCall + ")");
+ Log.d(LOG_TAG, this.toString());
+ }
+ }
+
+ /**
+ * 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 (VDBG) {
+ Log.d(LOG_TAG, "switchHoldingAndActive(" +heldCall + ")");
+ Log.d(LOG_TAG, this.toString());
+ }
+
+ if (hasActiveFgCall()) {
+ activePhone = getActiveFgCall().getPhone();
+ }
+
+ if (heldCall != null) {
+ heldPhone = heldCall.getPhone();
+ }
+
+ if (activePhone != null) {
+ activePhone.switchHoldingAndActive();
+ }
+
+ if (heldPhone != null && heldPhone != activePhone) {
+ heldPhone.switchHoldingAndActive();
+ }
+
+ if (VDBG) {
+ Log.d(LOG_TAG, "End switchHoldingAndActive(" +heldCall + ")");
+ Log.d(LOG_TAG, this.toString());
+ }
+ }
+
+ /**
+ * Hangup foreground call and resume the specific background call
+ *
+ * Note: this is noop if there is no foreground call or the heldCall is null
+ *
+ * @param heldCall to become foreground
+ * @throws CallStateException
+ */
+ public void hangupForegroundResumeBackground(Call heldCall) throws CallStateException {
+ Phone foregroundPhone = null;
+ Phone backgroundPhone = null;
+
+ if (VDBG) {
+ Log.d(LOG_TAG, "hangupForegroundResumeBackground(" +heldCall + ")");
+ Log.d(LOG_TAG, this.toString());
+ }
+
+ if (hasActiveFgCall()) {
+ foregroundPhone = getFgPhone();
+ if (heldCall != null) {
+ backgroundPhone = heldCall.getPhone();
+ if (foregroundPhone == backgroundPhone) {
+ getActiveFgCall().hangup();
+ } else {
+ // the call to be hangup and resumed belongs to different phones
+ getActiveFgCall().hangup();
+ switchHoldingAndActive(heldCall);
+ }
+ }
+ }
+
+ if (VDBG) {
+ Log.d(LOG_TAG, "End hangupForegroundResumeBackground(" +heldCall + ")");
+ Log.d(LOG_TAG, this.toString());
+ }
+ }
+
+ /**
+ * 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 {
+
+ if (VDBG) {
+ Log.d(LOG_TAG, "conference(" +heldCall + ")");
+ Log.d(LOG_TAG, this.toString());
+ }
+
+
+ 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"));
+ }
+
+ if (VDBG) {
+ Log.d(LOG_TAG, "End conference(" +heldCall + ")");
+ Log.d(LOG_TAG, this.toString());
+ }
+
+ }
+
+ /**
+ * 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 {
+ Phone basePhone = getPhoneBase(phone);
+ Connection result;
+
+ if (VDBG) {
+ Log.d(LOG_TAG, " dial(" + basePhone + ", "+ dialString + ")");
+ Log.d(LOG_TAG, this.toString());
+ }
+
+ if ( hasActiveFgCall() ) {
+ Phone activePhone = getActiveFgCall().getPhone();
+ boolean hasBgCall = !(activePhone.getBackgroundCall().isIdle());
+
+ if (DBG) {
+ Log.d(LOG_TAG, "hasBgCall: "+ hasBgCall + " sameChannel:" + (activePhone == basePhone));
+ }
+
+ if (activePhone != basePhone) {
+ if (hasBgCall) {
+ Log.d(LOG_TAG, "Hangup");
+ getActiveFgCall().hangup();
+ } else {
+ Log.d(LOG_TAG, "Switch");
+ activePhone.switchHoldingAndActive();
+ }
+ }
+ }
+
+ result = basePhone.dial(dialString);
+
+ if (VDBG) {
+ Log.d(LOG_TAG, "End dial(" + basePhone + ", "+ dialString + ")");
+ Log.d(LOG_TAG, this.toString());
+ }
+
+ return result;
+ }
+
+ /**
+ * 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 (VDBG) {
+ Log.d(LOG_TAG, " explicitCallTransfer(" + heldCall + ")");
+ Log.d(LOG_TAG, this.toString());
+ }
+
+ if (canTransfer(heldCall)) {
+ heldCall.getPhone().explicitCallTransfer();
+ }
+
+ if (VDBG) {
+ Log.d(LOG_TAG, "End explicitCallTransfer(" + heldCall + ")");
+ Log.d(LOG_TAG, this.toString());
+ }
+
+ }
+
+ /**
+ * 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) {
+ Log.e(LOG_TAG, "getPendingMmiCodes not implemented");
+ 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) {
+ Log.e(LOG_TAG, "sendUssdResponse not implemented");
+ 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 (VDBG) {
+ Log.d(LOG_TAG, " setMute(" + muted + ")");
+ Log.d(LOG_TAG, this.toString());
+ }
+
+ if (hasActiveFgCall()) {
+ getActiveFgCall().getPhone().setMute(muted);
+ }
+
+ if (VDBG) {
+ Log.d(LOG_TAG, "End setMute(" + muted + ")");
+ Log.d(LOG_TAG, this.toString());
+ }
+ }
+
+ /**
+ * 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;
+ }
+
+ /**
+ * Enables or disables echo suppression.
+ */
+ public void setEchoSuppressionEnabled(boolean enabled) {
+ if (VDBG) {
+ Log.d(LOG_TAG, " setEchoSuppression(" + enabled + ")");
+ Log.d(LOG_TAG, this.toString());
+ }
+
+ if (hasActiveFgCall()) {
+ getActiveFgCall().getPhone().setEchoSuppressionEnabled(enabled);
+ }
+
+ if (VDBG) {
+ Log.d(LOG_TAG, "End setEchoSuppression(" + enabled + ")");
+ Log.d(LOG_TAG, this.toString());
+ }
+ }
+
+ /**
+ * 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) {
+ boolean result = false;
+
+ if (VDBG) {
+ Log.d(LOG_TAG, " sendDtmf(" + c + ")");
+ Log.d(LOG_TAG, this.toString());
+ }
+
+ if (hasActiveFgCall()) {
+ getActiveFgCall().getPhone().sendDtmf(c);
+ result = true;
+ }
+
+ if (VDBG) {
+ Log.d(LOG_TAG, "End sendDtmf(" + c + ")");
+ Log.d(LOG_TAG, this.toString());
+ }
+ return result;
+ }
+
+ /**
+ * 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) {
+ boolean result = false;
+
+ if (VDBG) {
+ Log.d(LOG_TAG, " startDtmf(" + c + ")");
+ Log.d(LOG_TAG, this.toString());
+ }
+
+ if (hasActiveFgCall()) {
+ getActiveFgCall().getPhone().startDtmf(c);
+ result = true;
+ }
+
+ if (VDBG) {
+ Log.d(LOG_TAG, "End startDtmf(" + c + ")");
+ Log.d(LOG_TAG, this.toString());
+ }
+
+ return result;
+ }
+
+ /**
+ * Stop the playing DTMF tone. Ignored if there is no playing DTMF
+ * tone or no active call.
+ */
+ public void stopDtmf() {
+ if (VDBG) {
+ Log.d(LOG_TAG, " stopDtmf()" );
+ Log.d(LOG_TAG, this.toString());
+ }
+
+ if (hasActiveFgCall()) getFgPhone().stopDtmf();
+
+ if (VDBG) {
+ Log.d(LOG_TAG, "End stopDtmf()");
+ Log.d(LOG_TAG, this.toString());
+ }
+ }
+
+ /**
+ * send burst DTMF tone, it can send the string as single character or multiple character
+ * ignore if there is no active call or not valid digits string.
+ * Valid digit means only includes characters ISO-LATIN characters 0-9, *, #
+ * The difference between sendDtmf and sendBurstDtmf is sendDtmf only sends one character,
+ * this api can send single character and multiple character, also, this api has response
+ * back to caller.
+ *
+ * @param dtmfString is string representing the dialing digit(s) in the active call
+ * @param on the DTMF ON length in milliseconds, or 0 for default
+ * @param off the DTMF OFF length in milliseconds, or 0 for default
+ * @param onComplete is the callback message when the action is processed by BP
+ *
+ */
+ public boolean sendBurstDtmf(String dtmfString, int on, int off, Message onComplete) {
+ if (hasActiveFgCall()) {
+ getActiveFgCall().getPhone().sendBurstDtmf(dtmfString, on, off, onComplete);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Notifies when a voice connection has disconnected, either due to local
+ * or remote hangup or error.
+ *
+ * Messages received from this will have the following members:<p>
+ * <ul><li>Message.obj will be an AsyncResult</li>
+ * <li>AsyncResult.userObj = obj</li>
+ * <li>AsyncResult.result = a Connection object that is
+ * no longer connected.</li></ul>
+ */
+ public void registerForDisconnect(Handler h, int what, Object obj) {
+ mDisconnectRegistrants.addUnique(h, what, obj);
+ }
+
+ /**
+ * Unregisters for voice disconnection notification.
+ * Extraneous calls are tolerated silently
+ */
+ public void unregisterForDisconnect(Handler h){
+ mDisconnectRegistrants.remove(h);
+ }
+
+ /**
+ * Register for getting notifications for change in the Call State {@link Call.State}
+ * This is called PreciseCallState because the call state is more precise than the
+ * {@link Phone.State} which can be obtained using the {@link PhoneStateListener}
+ *
+ * Resulting events will have an AsyncResult in <code>Message.obj</code>.
+ * AsyncResult.userData will be set to the obj argument here.
+ * The <em>h</em> parameter is held only by a weak reference.
+ */
+ public void registerForPreciseCallStateChanged(Handler h, int what, Object obj){
+ mPreciseCallStateRegistrants.addUnique(h, what, obj);
+ }
+
+ /**
+ * Unregisters for voice call state change notifications.
+ * Extraneous calls are tolerated silently.
+ */
+ public void unregisterForPreciseCallStateChanged(Handler h){
+ mPreciseCallStateRegistrants.remove(h);
+ }
+
+ /**
+ * Notifies when a previously untracked non-ringing/waiting connection has appeared.
+ * This is likely due to some other entity (eg, SIM card application) initiating a call.
+ */
+ public void registerForUnknownConnection(Handler h, int what, Object obj){
+ mUnknownConnectionRegistrants.addUnique(h, what, obj);
+ }
+
+ /**
+ * Unregisters for unknown connection notifications.
+ */
+ public void unregisterForUnknownConnection(Handler h){
+ mUnknownConnectionRegistrants.remove(h);
+ }
+
+
+ /**
+ * Notifies when a new ringing or waiting connection has appeared.<p>
+ *
+ * Messages received from this:
+ * Message.obj will be an AsyncResult
+ * AsyncResult.userObj = obj
+ * AsyncResult.result = a Connection. <p>
+ * Please check Connection.isRinging() to make sure the Connection
+ * has not dropped since this message was posted.
+ * If Connection.isRinging() is true, then
+ * Connection.getCall() == Phone.getRingingCall()
+ */
+ public void registerForNewRingingConnection(Handler h, int what, Object obj){
+ mNewRingingConnectionRegistrants.addUnique(h, what, obj);
+ }
+
+ /**
+ * Unregisters for new ringing connection notification.
+ * Extraneous calls are tolerated silently
+ */
+
+ public void unregisterForNewRingingConnection(Handler h){
+ mNewRingingConnectionRegistrants.remove(h);
+ }
+
+ /**
+ * Notifies when an incoming call rings.<p>
+ *
+ * Messages received from this:
+ * Message.obj will be an AsyncResult
+ * AsyncResult.userObj = obj
+ * AsyncResult.result = a Connection. <p>
+ */
+ public void registerForIncomingRing(Handler h, int what, Object obj){
+ mIncomingRingRegistrants.addUnique(h, what, obj);
+ }
+
+ /**
+ * Unregisters for ring notification.
+ * Extraneous calls are tolerated silently
+ */
+
+ public void unregisterForIncomingRing(Handler h){
+ mIncomingRingRegistrants.remove(h);
+ }
+
+ /**
+ * Notifies when out-band ringback tone is needed.<p>
+ *
+ * Messages received from this:
+ * Message.obj will be an AsyncResult
+ * AsyncResult.userObj = obj
+ * AsyncResult.result = boolean, true to start play ringback tone
+ * and false to stop. <p>
+ */
+ public void registerForRingbackTone(Handler h, int what, Object obj){
+ mRingbackToneRegistrants.addUnique(h, what, obj);
+ }
+
+ /**
+ * Unregisters for ringback tone notification.
+ */
+
+ public void unregisterForRingbackTone(Handler h){
+ mRingbackToneRegistrants.remove(h);
+ }
+
+ /**
+ * Registers the handler to reset the uplink mute state to get
+ * uplink audio.
+ */
+ public void registerForResendIncallMute(Handler h, int what, Object obj){
+ mResendIncallMuteRegistrants.addUnique(h, what, obj);
+ }
+
+ /**
+ * Unregisters for resend incall mute notifications.
+ */
+ public void unregisterForResendIncallMute(Handler h){
+ mResendIncallMuteRegistrants.remove(h);
+ }
+
+ /**
+ * Register for notifications of initiation of a new MMI code request.
+ * MMI codes for GSM are discussed in 3GPP TS 22.030.<p>
+ *
+ * Example: If Phone.dial is called with "*#31#", then the app will
+ * be notified here.<p>
+ *
+ * The returned <code>Message.obj</code> will contain an AsyncResult.
+ *
+ * <code>obj.result</code> will be an "MmiCode" object.
+ */
+ public void registerForMmiInitiate(Handler h, int what, Object obj){
+ mMmiInitiateRegistrants.addUnique(h, what, obj);
+ }
+
+ /**
+ * Unregisters for new MMI initiate notification.
+ * Extraneous calls are tolerated silently
+ */
+ public void unregisterForMmiInitiate(Handler h){
+ mMmiInitiateRegistrants.remove(h);
+ }
+
+ /**
+ * Register for notifications that an MMI request has completed
+ * its network activity and is in its final state. This may mean a state
+ * of COMPLETE, FAILED, or CANCELLED.
+ *
+ * <code>Message.obj</code> will contain an AsyncResult.
+ * <code>obj.result</code> will be an "MmiCode" object
+ */
+ public void registerForMmiComplete(Handler h, int what, Object obj){
+ mMmiCompleteRegistrants.addUnique(h, what, obj);
+ }
+
+ /**
+ * Unregisters for MMI complete notification.
+ * Extraneous calls are tolerated silently
+ */
+ public void unregisterForMmiComplete(Handler h){
+ mMmiCompleteRegistrants.remove(h);
+ }
+
+ /**
+ * Registration point for Ecm timer reset
+ * @param h handler to notify
+ * @param what user-defined message code
+ * @param obj placed in Message.obj
+ */
+ public void registerForEcmTimerReset(Handler h, int what, Object obj){
+ mEcmTimerResetRegistrants.addUnique(h, what, obj);
+ }
+
+ /**
+ * Unregister for notification for Ecm timer reset
+ * @param h Handler to be removed from the registrant list.
+ */
+ public void unregisterForEcmTimerReset(Handler h){
+ mEcmTimerResetRegistrants.remove(h);
+ }
+
+ /**
+ * Register for ServiceState changed.
+ * Message.obj will contain an AsyncResult.
+ * AsyncResult.result will be a ServiceState instance
+ */
+ public void registerForServiceStateChanged(Handler h, int what, Object obj){
+ mServiceStateChangedRegistrants.addUnique(h, what, obj);
+ }
+
+ /**
+ * Unregisters for ServiceStateChange notification.
+ * Extraneous calls are tolerated silently
+ */
+ public void unregisterForServiceStateChanged(Handler h){
+ mServiceStateChangedRegistrants.remove(h);
+ }
+
+ /**
+ * Register for notifications when a supplementary service attempt fails.
+ * Message.obj will contain an AsyncResult.
+ *
+ * @param h Handler that receives the notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ */
+ public void registerForSuppServiceFailed(Handler h, int what, Object obj){
+ mSuppServiceFailedRegistrants.addUnique(h, what, obj);
+ }
+
+ /**
+ * Unregister for notifications when a supplementary service attempt fails.
+ * Extraneous calls are tolerated silently
+ *
+ * @param h Handler to be removed from the registrant list.
+ */
+ public void unregisterForSuppServiceFailed(Handler h){
+ mSuppServiceFailedRegistrants.remove(h);
+ }
+
+ /**
+ * Register for notifications when a sInCall VoicePrivacy is enabled
+ *
+ * @param h Handler that receives the notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ */
+ public void registerForInCallVoicePrivacyOn(Handler h, int what, Object obj){
+ mInCallVoicePrivacyOnRegistrants.addUnique(h, what, obj);
+ }
+
+ /**
+ * Unregister for notifications when a sInCall VoicePrivacy is enabled
+ *
+ * @param h Handler to be removed from the registrant list.
+ */
+ public void unregisterForInCallVoicePrivacyOn(Handler h){
+ mInCallVoicePrivacyOnRegistrants.remove(h);
+ }
+
+ /**
+ * Register for notifications when a sInCall VoicePrivacy is disabled
+ *
+ * @param h Handler that receives the notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ */
+ public void registerForInCallVoicePrivacyOff(Handler h, int what, Object obj){
+ mInCallVoicePrivacyOffRegistrants.addUnique(h, what, obj);
+ }
+
+ /**
+ * Unregister for notifications when a sInCall VoicePrivacy is disabled
+ *
+ * @param h Handler to be removed from the registrant list.
+ */
+ public void unregisterForInCallVoicePrivacyOff(Handler h){
+ mInCallVoicePrivacyOffRegistrants.remove(h);
+ }
+
+ /**
+ * Register for notifications when CDMA call waiting comes
+ *
+ * @param h Handler that receives the notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ */
+ public void registerForCallWaiting(Handler h, int what, Object obj){
+ mCallWaitingRegistrants.addUnique(h, what, obj);
+ }
+
+ /**
+ * Unregister for notifications when CDMA Call waiting comes
+ * @param h Handler to be removed from the registrant list.
+ */
+ public void unregisterForCallWaiting(Handler h){
+ mCallWaitingRegistrants.remove(h);
+ }
+
+
+ /**
+ * Register for signal information notifications from the network.
+ * Message.obj will contain an AsyncResult.
+ * AsyncResult.result will be a SuppServiceNotification instance.
+ *
+ * @param h Handler that receives the notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ */
+
+ public void registerForSignalInfo(Handler h, int what, Object obj){
+ mSignalInfoRegistrants.addUnique(h, what, obj);
+ }
+
+ /**
+ * Unregisters for signal information notifications.
+ * Extraneous calls are tolerated silently
+ *
+ * @param h Handler to be removed from the registrant list.
+ */
+ public void unregisterForSignalInfo(Handler h){
+ mSignalInfoRegistrants.remove(h);
+ }
+
+ /**
+ * Register for display information notifications from the network.
+ * Message.obj will contain an AsyncResult.
+ * AsyncResult.result will be a SuppServiceNotification instance.
+ *
+ * @param h Handler that receives the notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ */
+ public void registerForDisplayInfo(Handler h, int what, Object obj){
+ mDisplayInfoRegistrants.addUnique(h, what, obj);
+ }
+
+ /**
+ * Unregisters for display information notifications.
+ * Extraneous calls are tolerated silently
+ *
+ * @param h Handler to be removed from the registrant list.
+ */
+ public void unregisterForDisplayInfo(Handler h) {
+ mDisplayInfoRegistrants.remove(h);
+ }
+
+ /**
+ * Register for notifications when CDMA OTA Provision status change
+ *
+ * @param h Handler that receives the notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ */
+ public void registerForCdmaOtaStatusChange(Handler h, int what, Object obj){
+ mCdmaOtaStatusChangeRegistrants.addUnique(h, what, obj);
+ }
+
+ /**
+ * Unregister for notifications when CDMA OTA Provision status change
+ * @param h Handler to be removed from the registrant list.
+ */
+ public void unregisterForCdmaOtaStatusChange(Handler h){
+ mCdmaOtaStatusChangeRegistrants.remove(h);
+ }
+
+ /**
+ * Registration point for subscription info ready
+ * @param h handler to notify
+ * @param what what code of message when delivered
+ * @param obj placed in Message.obj
+ */
+ public void registerForSubscriptionInfoReady(Handler h, int what, Object obj){
+ mSubscriptionInfoReadyRegistrants.addUnique(h, what, obj);
+ }
+
+ /**
+ * Unregister for notifications for subscription info
+ * @param h Handler to be removed from the registrant list.
+ */
+ public void unregisterForSubscriptionInfoReady(Handler h){
+ mSubscriptionInfoReadyRegistrants.remove(h);
+ }
+
+ /**
+ * Sets an event to be fired when the telephony system processes
+ * a post-dial character on an outgoing call.<p>
+ *
+ * Messages of type <code>what</code> will be sent to <code>h</code>.
+ * The <code>obj</code> field of these Message's will be instances of
+ * <code>AsyncResult</code>. <code>Message.obj.result</code> will be
+ * a Connection object.<p>
+ *
+ * Message.arg1 will be the post dial character being processed,
+ * or 0 ('\0') if end of string.<p>
+ *
+ * If Connection.getPostDialState() == WAIT,
+ * the application must call
+ * {@link com.android.internal.telephony.Connection#proceedAfterWaitChar()
+ * Connection.proceedAfterWaitChar()} or
+ * {@link com.android.internal.telephony.Connection#cancelPostDial()
+ * Connection.cancelPostDial()}
+ * for the telephony system to continue playing the post-dial
+ * DTMF sequence.<p>
+ *
+ * If Connection.getPostDialState() == WILD,
+ * the application must call
+ * {@link com.android.internal.telephony.Connection#proceedAfterWildChar
+ * Connection.proceedAfterWildChar()}
+ * or
+ * {@link com.android.internal.telephony.Connection#cancelPostDial()
+ * Connection.cancelPostDial()}
+ * for the telephony system to continue playing the
+ * post-dial DTMF sequence.<p>
+ *
+ */
+ public void registerForPostDialCharacter(Handler h, int what, Object obj){
+ mPostDialCharacterRegistrants.addUnique(h, what, obj);
+ }
+
+ public void unregisterForPostDialCharacter(Handler h){
+ mPostDialCharacterRegistrants.remove(h);
+ }
+
+ /* APIs to access foregroudCalls, backgroudCalls, and ringingCalls
+ * 1. APIs to access list of calls
+ * 2. APIs to check if any active call, which has connection other than
+ * disconnected ones, pleaser refer to Call.isIdle()
+ * 3. APIs to return first active call
+ * 4. APIs to return the connections of first active call
+ * 5. APIs to return other property of first active call
+ */
+
+ /**
+ * @return list of all ringing calls
+ */
+ public List<Call> getRingingCalls() {
+ return Collections.unmodifiableList(mRingingCalls);
+ }
+
+ /**
+ * @return list of all foreground calls
+ */
+ public List<Call> getForegroundCalls() {
+ return Collections.unmodifiableList(mForegroundCalls);
+ }
+
+ /**
+ * @return list of all background calls
+ */
+ public List<Call> getBackgroundCalls() {
+ return Collections.unmodifiableList(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() {
+ Call call = getFirstNonIdleCall(mForegroundCalls);
+ if (call == null) {
+ call = (mDefaultPhone == null)
+ ? null
+ : mDefaultPhone.getForegroundCall();
+ }
+ return call;
+ }
+
+ // Returns the first call that is not in IDLE state. If both active calls
+ // and disconnecting/disconnected calls exist, return the first active call.
+ private Call getFirstNonIdleCall(List<Call> calls) {
+ Call result = null;
+ for (Call call : calls) {
+ if (!call.isIdle()) {
+ return call;
+ } else if (call.getState() != Call.State.IDLE) {
+ if (result == null) result = call;
+ }
+ }
+ return result;
+ }
+
+ /**
+ * 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() {
+ Call call = getFirstNonIdleCall(mBackgroundCalls);
+ if (call == null) {
+ call = (mDefaultPhone == null)
+ ? null
+ : mDefaultPhone.getBackgroundCall();
+ }
+ return call;
+ }
+
+ /**
+ * 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() {
+ Call call = getFirstNonIdleCall(mRingingCalls);
+ if (call == null) {
+ call = (mDefaultPhone == null)
+ ? null
+ : mDefaultPhone.getRingingCall();
+ }
+ return call;
+ }
+
+ /**
+ * @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 empty list 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 = getFirstActiveBgCall();
+ 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:
+ if (VDBG) Log.d(LOG_TAG, " handleMessage (EVENT_DISCONNECT)");
+ mDisconnectRegistrants.notifyRegistrants((AsyncResult) msg.obj);
+ break;
+ case EVENT_PRECISE_CALL_STATE_CHANGED:
+ if (VDBG) Log.d(LOG_TAG, " handleMessage (EVENT_PRECISE_CALL_STATE_CHANGED)");
+ mPreciseCallStateRegistrants.notifyRegistrants((AsyncResult) msg.obj);
+ break;
+ case EVENT_NEW_RINGING_CONNECTION:
+ if (VDBG) Log.d(LOG_TAG, " handleMessage (EVENT_NEW_RINGING_CONNECTION)");
+ mNewRingingConnectionRegistrants.notifyRegistrants((AsyncResult) msg.obj);
+ break;
+ case EVENT_UNKNOWN_CONNECTION:
+ if (VDBG) Log.d(LOG_TAG, " handleMessage (EVENT_UNKNOWN_CONNECTION)");
+ mUnknownConnectionRegistrants.notifyRegistrants((AsyncResult) msg.obj);
+ break;
+ case EVENT_INCOMING_RING:
+ if (VDBG) Log.d(LOG_TAG, " handleMessage (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:
+ if (VDBG) Log.d(LOG_TAG, " handleMessage (EVENT_RINGBACK_TONE)");
+ mRingbackToneRegistrants.notifyRegistrants((AsyncResult) msg.obj);
+ break;
+ case EVENT_IN_CALL_VOICE_PRIVACY_ON:
+ if (VDBG) Log.d(LOG_TAG, " handleMessage (EVENT_IN_CALL_VOICE_PRIVACY_ON)");
+ mInCallVoicePrivacyOnRegistrants.notifyRegistrants((AsyncResult) msg.obj);
+ break;
+ case EVENT_IN_CALL_VOICE_PRIVACY_OFF:
+ if (VDBG) Log.d(LOG_TAG, " handleMessage (EVENT_IN_CALL_VOICE_PRIVACY_OFF)");
+ mInCallVoicePrivacyOffRegistrants.notifyRegistrants((AsyncResult) msg.obj);
+ break;
+ case EVENT_CALL_WAITING:
+ if (VDBG) Log.d(LOG_TAG, " handleMessage (EVENT_CALL_WAITING)");
+ mCallWaitingRegistrants.notifyRegistrants((AsyncResult) msg.obj);
+ break;
+ case EVENT_DISPLAY_INFO:
+ if (VDBG) Log.d(LOG_TAG, " handleMessage (EVENT_DISPLAY_INFO)");
+ mDisplayInfoRegistrants.notifyRegistrants((AsyncResult) msg.obj);
+ break;
+ case EVENT_SIGNAL_INFO:
+ if (VDBG) Log.d(LOG_TAG, " handleMessage (EVENT_SIGNAL_INFO)");
+ mSignalInfoRegistrants.notifyRegistrants((AsyncResult) msg.obj);
+ break;
+ case EVENT_CDMA_OTA_STATUS_CHANGE:
+ if (VDBG) Log.d(LOG_TAG, " handleMessage (EVENT_CDMA_OTA_STATUS_CHANGE)");
+ mCdmaOtaStatusChangeRegistrants.notifyRegistrants((AsyncResult) msg.obj);
+ break;
+ case EVENT_RESEND_INCALL_MUTE:
+ if (VDBG) Log.d(LOG_TAG, " handleMessage (EVENT_RESEND_INCALL_MUTE)");
+ mResendIncallMuteRegistrants.notifyRegistrants((AsyncResult) msg.obj);
+ break;
+ case EVENT_MMI_INITIATE:
+ if (VDBG) Log.d(LOG_TAG, " handleMessage (EVENT_MMI_INITIATE)");
+ mMmiInitiateRegistrants.notifyRegistrants((AsyncResult) msg.obj);
+ break;
+ case EVENT_MMI_COMPLETE:
+ if (VDBG) Log.d(LOG_TAG, " handleMessage (EVENT_MMI_COMPLETE)");
+ mMmiCompleteRegistrants.notifyRegistrants((AsyncResult) msg.obj);
+ break;
+ case EVENT_ECM_TIMER_RESET:
+ if (VDBG) Log.d(LOG_TAG, " handleMessage (EVENT_ECM_TIMER_RESET)");
+ mEcmTimerResetRegistrants.notifyRegistrants((AsyncResult) msg.obj);
+ break;
+ case EVENT_SUBSCRIPTION_INFO_READY:
+ if (VDBG) Log.d(LOG_TAG, " handleMessage (EVENT_SUBSCRIPTION_INFO_READY)");
+ mSubscriptionInfoReadyRegistrants.notifyRegistrants((AsyncResult) msg.obj);
+ break;
+ case EVENT_SUPP_SERVICE_FAILED:
+ if (VDBG) Log.d(LOG_TAG, " handleMessage (EVENT_SUPP_SERVICE_FAILED)");
+ mSuppServiceFailedRegistrants.notifyRegistrants((AsyncResult) msg.obj);
+ break;
+ case EVENT_SERVICE_STATE_CHANGED:
+ if (VDBG) Log.d(LOG_TAG, " handleMessage (EVENT_SERVICE_STATE_CHANGED)");
+ mServiceStateChangedRegistrants.notifyRegistrants((AsyncResult) msg.obj);
+ break;
+ case EVENT_POST_DIAL_CHARACTER:
+ // we need send the character that is being processed in msg.arg1
+ // so can't use notifyRegistrants()
+ if (VDBG) Log.d(LOG_TAG, " handleMessage (EVENT_POST_DIAL_CHARACTER)");
+ for(int i=0; i < mPostDialCharacterRegistrants.size(); i++) {
+ Message notifyMsg;
+ notifyMsg = ((Registrant)mPostDialCharacterRegistrants.get(i)).messageForRegistrant();
+ notifyMsg.obj = msg.obj;
+ notifyMsg.arg1 = msg.arg1;
+ notifyMsg.sendToTarget();
+ }
+ break;
+ }
+ }
+ };
+
+ @Override
+ public String toString() {
+ Call call;
+ StringBuilder b = new StringBuilder();
+
+ b.append("########### Dump CallManager ############");
+ b.append("\nCallManager state = " + getState());
+ call = getActiveFgCall();
+ b.append("\n - Foreground: " + getActiveFgCallState());
+ b.append(" from " + call.getPhone());
+ b.append("\n Conn: ").append(getFgCallConnections());
+ call = getFirstActiveBgCall();
+ b.append("\n - Background: " + call.getState());
+ b.append(" from " + call.getPhone());
+ b.append("\n Conn: ").append(getBgCallConnections());
+ call = getFirstActiveRingingCall();
+ b.append("\n - Ringing: " +call.getState());
+ b.append(" from " + call.getPhone());
+
+ for (Phone phone : getAllPhones()) {
+ if (phone != null) {
+ b.append("\n Phone: " + phone + ", name = " + phone.getPhoneName()
+ + ", state = " + phone.getState());
+ call = phone.getForegroundCall();
+ b.append("\n - Foreground: ").append(call);
+ call = phone.getBackgroundCall();
+ b.append(" Background: ").append(call);
+ call = phone.getRingingCall();
+ b.append(" Ringing: ").append(call);
+ }
+ }
+ b.append("\n########## End Dump CallManager ##########");
+ return b.toString();
+ }
+}
diff --git a/telephony/java/com/android/internal/telephony/CallerInfo.java b/telephony/java/com/android/internal/telephony/CallerInfo.java
index cf89848..360d35e 100644
--- a/telephony/java/com/android/internal/telephony/CallerInfo.java
+++ b/telephony/java/com/android/internal/telephony/CallerInfo.java
@@ -26,9 +26,9 @@ import static android.provider.ContactsContract.RawContacts;
import android.text.TextUtils;
import android.telephony.TelephonyManager;
import android.telephony.PhoneNumberUtils;
-import android.util.Config;
import android.util.Log;
+
/**
* Looks up caller information for the given phone number.
*
@@ -36,6 +36,7 @@ import android.util.Log;
*/
public class CallerInfo {
private static final String TAG = "CallerInfo";
+ private static final boolean VDBG = Log.isLoggable(TAG, Log.VERBOSE);
public static final String UNKNOWN_NUMBER = "-1";
public static final String PRIVATE_NUMBER = "-2";
@@ -128,7 +129,7 @@ public class CallerInfo {
info.isCachedPhotoCurrent = false;
info.contactExists = false;
- if (Config.LOGV) Log.v(TAG, "construct callerInfo from cursor");
+ if (VDBG) Log.v(TAG, "construct callerInfo from cursor");
if (cursor != null) {
if (cursor.moveToFirst()) {
@@ -166,31 +167,30 @@ public class CallerInfo {
// Look for the person ID.
// TODO: This is pretty ugly now, see bug 2269240 for
- // more details. With tel: URI the contact id is in
- // col "_id" while when we use a
- // content://contacts/data/phones URI, the contact id
- // is col "contact_id". As a work around we use the
- // type of the contact url to figure out which column
- // we should look at to get the contact_id.
-
- final String mimeType = context.getContentResolver().getType(contactRef);
+ // more details. The column to use depends upon the type of URL,
+ // for content://com.android.contacts/data/phones the "contact_id"
+ // column is used. For content/com.andriod.contacts/phone_lookup"
+ // the "_ID" column is used. If it is neither we leave columnIndex
+ // at -1 and no person ID will be available.
columnIndex = -1;
- if (Phone.CONTENT_ITEM_TYPE.equals(mimeType)) {
- // content://com.android.contacts/data/phones URL
+ String url = contactRef.toString();
+ if (url.startsWith("content://com.android.contacts/data/phones")) {
+ if (VDBG) Log.v(TAG,
+ "URL path starts with 'data/phones' using RawContacts.CONTACT_ID");
columnIndex = cursor.getColumnIndex(RawContacts.CONTACT_ID);
- } else {
- // content://com.android.contacts/phone_lookup URL
- // TODO: mime type is null here so we cannot test
- // if we have the right url type. phone_lookup URL
- // should resolve to a mime type.
+ } else if (url.startsWith("content://com.android.contacts/phone_lookup")) {
+ if (VDBG) Log.v(TAG,
+ "URL path starts with 'phone_lookup' using PhoneLookup._ID");
columnIndex = cursor.getColumnIndex(PhoneLookup._ID);
+ } else {
+ Log.e(TAG, "Bad contact URL '" + url + "'");
}
if (columnIndex != -1) {
info.person_id = cursor.getLong(columnIndex);
} else {
- Log.e(TAG, "Column missing for " + contactRef);
+ Log.e(TAG, "person_id column missing for " + contactRef);
}
// look for the custom ringtone, create from the string stored
diff --git a/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java b/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java
index 798a5a5..25ca559 100644
--- a/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java
+++ b/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java
@@ -24,9 +24,10 @@ import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
+import android.provider.ContactsContract.CommonDataKinds.SipAddress;
+import android.provider.ContactsContract.Data;
import android.provider.ContactsContract.PhoneLookup;
import android.telephony.PhoneNumberUtils;
-import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Log;
@@ -36,7 +37,7 @@ import android.util.Log;
public class CallerInfoAsyncQuery {
- private static final boolean DBG = false;
+ private static final boolean DBG = true; // STOPSHIP: disable debugging before ship
private static final String LOG_TAG = "CallerInfoAsyncQuery";
private static final int EVENT_NEW_QUERY = 1;
@@ -189,7 +190,7 @@ public class CallerInfoAsyncQuery {
*/
@Override
protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
- if (DBG) log("query complete for token: " + token);
+ if (DBG) log("##### onQueryComplete() ##### query complete for token: " + token);
//get the cookie and notify the listener.
CookieWrapper cw = (CookieWrapper) cookie;
@@ -227,6 +228,8 @@ public class CallerInfoAsyncQuery {
mCallerInfo = new CallerInfo().markAsVoiceMail();
} else {
mCallerInfo = CallerInfo.getCallerInfo(mQueryContext, mQueryUri, cursor);
+ if (DBG) log("==> Got mCallerInfo: " + mCallerInfo);
+
// Use the number entered by the user for display.
if (!TextUtils.isEmpty(cw.number)) {
mCallerInfo.phoneNumber = PhoneNumberUtils.formatNumber(cw.number);
@@ -238,7 +241,7 @@ public class CallerInfoAsyncQuery {
//notify that we can clean up the queue after this.
CookieWrapper endMarker = new CookieWrapper();
endMarker.event = EVENT_END_OF_QUEUE;
- startQuery (token, endMarker, null, null, null, null, null);
+ startQuery(token, endMarker, null, null, null, null, null);
}
//notify the listener that the query is complete.
@@ -274,24 +277,82 @@ public class CallerInfoAsyncQuery {
cw.cookie = cookie;
cw.event = EVENT_NEW_QUERY;
- c.mHandler.startQuery (token, cw, contactRef, null, null, null, null);
+ c.mHandler.startQuery(token, cw, contactRef, null, null, null, null);
return c;
}
/**
- * Factory method to start query with a number
+ * Factory method to start the query based on a number.
+ *
+ * Note: if the number contains an "@" character we treat it
+ * as a SIP address, and look it up directly in the Data table
+ * rather than using the PhoneLookup table.
+ * TODO: But eventually we should expose two separate methods, one for
+ * numbers and one for SIP addresses, and then have
+ * PhoneUtils.startGetCallerInfo() decide which one to call based on
+ * the phone type of the incoming connection.
*/
public static CallerInfoAsyncQuery startQuery(int token, Context context, String number,
OnQueryCompleteListener listener, Object cookie) {
- //construct the URI object and start Query.
- Uri contactRef = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(number));
+ if (DBG) {
+ log("##### CallerInfoAsyncQuery startQuery()... #####");
+ log("- number: " + number);
+ log("- cookie: " + cookie);
+ }
+
+ // Construct the URI object and query params, and start the query.
+
+ Uri contactRef;
+ String selection;
+ String[] selectionArgs;
+
+ if (PhoneNumberUtils.isUriNumber(number)) {
+ // "number" is really a SIP address.
+ if (DBG) log(" - Treating number as a SIP address: " + number);
+
+ // We look up SIP addresses directly in the Data table:
+ contactRef = Data.CONTENT_URI;
+
+ // Note Data.DATA1 and SipAddress.SIP_ADDRESS are equivalent.
+ //
+ // Also note we use "upper(data1)" in the WHERE clause, and
+ // uppercase the incoming SIP address, in order to do a
+ // case-insensitive match.
+ //
+ // TODO: need to confirm that the use of upper() doesn't
+ // prevent us from using the index! (Linear scan of the whole
+ // contacts DB can be very slow.)
+ //
+ // TODO: May also need to normalize by adding "sip:" as a
+ // prefix, if we start storing SIP addresses that way in the
+ // database.
+
+ selection = "upper(" + Data.DATA1 + ")=?"
+ + " AND "
+ + Data.MIMETYPE + "='" + SipAddress.CONTENT_ITEM_TYPE + "'";
+ selectionArgs = new String[] { number.toUpperCase() };
+
+ } else {
+ // "number" is a regular phone number. Use the PhoneLookup table:
+ contactRef = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(number));
+ selection = null;
+ selectionArgs = null;
+ }
+
+ if (DBG) {
+ log("==> contactRef: " + contactRef);
+ log("==> selection: " + selection);
+ if (selectionArgs != null) {
+ for (int i = 0; i < selectionArgs.length; i++) {
+ log("==> selectionArgs[" + i + "]: " + selectionArgs[i]);
+ }
+ }
+ }
CallerInfoAsyncQuery c = new CallerInfoAsyncQuery();
c.allocate(context, contactRef);
- if (DBG) log("starting query for number: " + number + " handler: " + c.toString());
-
//create cookieWrapper, start query
CookieWrapper cw = new CookieWrapper();
cw.listener = listener;
@@ -307,10 +368,15 @@ public class CallerInfoAsyncQuery {
cw.event = EVENT_NEW_QUERY;
}
- c.mHandler.startQuery (token, cw, contactRef, null, null, null, null);
-
+ c.mHandler.startQuery(token,
+ cw, // cookie
+ contactRef, // uri
+ null, // projection
+ selection, // selection
+ selectionArgs, // selectionArgs
+ null); // orderBy
return c;
- }
+ }
/**
* Method to add listeners to a currently running query
@@ -326,7 +392,7 @@ public class CallerInfoAsyncQuery {
cw.cookie = cookie;
cw.event = EVENT_ADD_LISTENER;
- mHandler.startQuery (token, cw, null, null, null, null, null);
+ mHandler.startQuery(token, cw, null, null, null, null, null);
}
/**
diff --git a/telephony/java/com/android/internal/telephony/Connection.java b/telephony/java/com/android/internal/telephony/Connection.java
index 11d0b1b..07f90cd 100644
--- a/telephony/java/com/android/internal/telephony/Connection.java
+++ b/telephony/java/com/android/internal/telephony/Connection.java
@@ -39,6 +39,12 @@ public abstract class Connection {
CONGESTION, /* outgoing call to congested network */
MMI, /* not presently used; dial() returns null */
INVALID_NUMBER, /* invalid dial string */
+ NUMBER_UNREACHABLE, /* cannot reach the peer */
+ SERVER_UNREACHABLE, /* cannot reach the server */
+ INVALID_CREDENTIALS, /* invalid credentials */
+ OUT_OF_NETWORK, /* calling from out of network is not allowed */
+ SERVER_ERROR, /* server error */
+ TIMED_OUT, /* client timed out */
LOST_SIGNAL,
LIMIT_EXCEEDED, /* eg GSM ACM limit exceeded */
INCOMING_REJECTED, /* an incoming call that was rejected */
diff --git a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
index 3b9e6cc..265bf7e 100644
--- a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
@@ -33,7 +33,7 @@ import java.util.ArrayList;
*
*/
public abstract class DataConnectionTracker extends Handler {
- protected static final boolean DBG = true;
+ protected static final boolean DBG = false;
protected final String LOG_TAG = "DataConnectionTracker";
/**
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..30ee77c 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}
@@ -171,7 +171,7 @@ public class GsmAlphabet {
* array cannot contain more than 255 septets.
*
* @param data The text string to encode.
- * @param header Optional header (includeing length byte) that precedes
+ * @param header Optional header (including length byte) that precedes
* the encoded data, padded to septet boundary.
* @return Byte array containing header and encoded data.
*/
@@ -204,7 +204,7 @@ public class GsmAlphabet {
* the packed septets. The returned array cannot contain more than 255
* septets.
*
- * @param data the data string to endcode
+ * @param data the data string to encode
* @throws EncodeException if String is too large to encode
*/
public static byte[] stringToGsm7BitPacked(String data)
@@ -223,7 +223,7 @@ public class GsmAlphabet {
*
* @param data the text to convert to septets
* @param startingSeptetOffset the number of padding septets to put before
- * the character data at the begining of the array
+ * the character data at the beginning of the array
* @param throwException If true, throws EncodeException on invalid char.
* If false, replaces unencodable char with GSM alphabet space char.
*
@@ -257,7 +257,7 @@ public class GsmAlphabet {
}
/**
- * Pack a 7-bit char into its appropirate place in a byte array
+ * Pack a 7-bit char into its appropriate place in a byte array
*
* @param bitOffset the bit offset that the septet should be packed at
* (septet index * 7)
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/IccSmsInterfaceManager.java b/telephony/java/com/android/internal/telephony/IccSmsInterfaceManager.java
index 8a5a6ae..5fef6de 100644
--- a/telephony/java/com/android/internal/telephony/IccSmsInterfaceManager.java
+++ b/telephony/java/com/android/internal/telephony/IccSmsInterfaceManager.java
@@ -57,7 +57,7 @@ public abstract class IccSmsInterfaceManager extends ISms.Stub {
* @param destPort the port to deliver the message to
* @param data the body of the message to send
* @param sentIntent if not NULL this <code>PendingIntent</code> is
- * broadcast when the message is sucessfully sent, or failed.
+ * broadcast when the message is successfully sent, or failed.
* The result code will be <code>Activity.RESULT_OK<code> for success,
* or one of these errors:<br>
* <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
@@ -67,7 +67,7 @@ public abstract class IccSmsInterfaceManager extends ISms.Stub {
* the extra "errorCode" containing a radio technology specific value,
* generally only useful for troubleshooting.<br>
* The per-application based SMS control checks sentIntent. If sentIntent
- * is NULL the caller will be checked against all unknown applicaitons,
+ * is NULL the caller will be checked against all unknown applications,
* which cause smaller number of SMS to be sent in checking period.
* @param deliveryIntent if not NULL this <code>PendingIntent</code> is
* broadcast when the message is delivered to the recipient. The
@@ -94,7 +94,7 @@ public abstract class IccSmsInterfaceManager extends ISms.Stub {
* the current default SMSC
* @param text the body of the message to send
* @param sentIntent if not NULL this <code>PendingIntent</code> is
- * broadcast when the message is sucessfully sent, or failed.
+ * broadcast when the message is successfully sent, or failed.
* The result code will be <code>Activity.RESULT_OK<code> for success,
* or one of these errors:<br>
* <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
@@ -140,7 +140,7 @@ public abstract class IccSmsInterfaceManager extends ISms.Stub {
* <code>RESULT_ERROR_RADIO_OFF</code>
* <code>RESULT_ERROR_NULL_PDU</code>.
* The per-application based SMS control checks sentIntent. If sentIntent
- * is NULL the caller will be checked against all unknown applicaitons,
+ * is NULL the caller will be checked against all unknown applications,
* which cause smaller number of SMS to be sent in checking period.
* @param deliveryIntents if not null, an <code>ArrayList</code> of
* <code>PendingIntent</code>s (one for each message part) that is
diff --git a/telephony/java/com/android/internal/telephony/MccTable.java b/telephony/java/com/android/internal/telephony/MccTable.java
index b73c2f7..5539057 100644
--- a/telephony/java/com/android/internal/telephony/MccTable.java
+++ b/telephony/java/com/android/internal/telephony/MccTable.java
@@ -174,7 +174,7 @@ mcc_table = [
(438, 'tm', 2, 'Turkmenistan'),
(440, 'jp', 2, 'Asia/Tokyo', 'ja', 14, 'Japan'),
(441, 'jp', 2, 'Asia/Tokyo', 'ja', 14, 'Japan'),
- (450, 'kr', 2, 'Korea (Republic of)'),
+ (450, 'kr', 2, 'Asia/Seoul', 'ko', 13, 'Korea (Republic of)'),
(452, 'vn', 2, 'Viet Nam (Socialist Republic of)'),
(454, 'hk', 2, '"Hong Kong, China"'),
(455, 'mo', 2, '"Macao, China"'),
@@ -378,6 +378,7 @@ public final class MccTable
"",
"Africa/Johannesburg",
"Asia/Beijing",
+ "Asia/Seoul",
"Asia/Singapore",
"Asia/Tokyo",
"Australia/Sydney",
@@ -399,7 +400,8 @@ public final class MccTable
* AUTO GENERATED (by the Python code above)
*/
private static final String[] LANG_STRINGS = {
- "", "cs", "de", "en", "es", "fr", "it", "ja", "nl", "zh"
+ "", "cs", "de", "en", "es", "fr", "it", "ja", "ko", "nl",
+ "zh"
};
/**
@@ -446,13 +448,13 @@ public final class MccTable
* default language 4 bits
*/
private static final int[] IND_CODES = {
- 0x67720400, 0x6e6c6c68, 0x62650400, 0x667204b5, 0x6d630400, 0x61640400,
- 0x657304a4, 0x68750400, 0x62610400, 0x68720400, 0x72730400, 0x697404d6,
- 0x766104d6, 0x726f0400, 0x63680502, 0x637a6cc1, 0x736b0400, 0x61746ce2,
- 0x67626c93, 0x67626c93, 0x646b0400, 0x73650400, 0x6e6f0400, 0x66690400,
+ 0x67720400, 0x6e6c6c79, 0x62650400, 0x667204c5, 0x6d630400, 0x61640400,
+ 0x657304b4, 0x68750400, 0x62610400, 0x68720400, 0x72730400, 0x697404e6,
+ 0x766104e6, 0x726f0400, 0x63680512, 0x637a6cd1, 0x736b0400, 0x61746cf2,
+ 0x67626ca3, 0x67626ca3, 0x646b0400, 0x73650400, 0x6e6f0400, 0x66690400,
0x6c740400, 0x6c760400, 0x65650400, 0x72750400, 0x75610400, 0x62790400,
- 0x6d640400, 0x706c04f0, 0x64656c72, 0x67690400, 0x70740400, 0x6c750400,
- 0x69650483, 0x69730400, 0x616c0400, 0x6d740400, 0x63790400, 0x67650400,
+ 0x6d640400, 0x706c0500, 0x64656c82, 0x67690400, 0x70740400, 0x6c750400,
+ 0x69650493, 0x69730400, 0x616c0400, 0x6d740400, 0x63790400, 0x67650400,
0x616d0400, 0x62670400, 0x74720400, 0x666f0400, 0x67650400, 0x676c0400,
0x736d0400, 0x736c0400, 0x6d6b0400, 0x6c690400, 0x6d650400, 0x63615e00,
0x706d0400, 0x75735e03, 0x75735e03, 0x75735e03, 0x75735e03, 0x75735e03,
@@ -465,11 +467,11 @@ public final class MccTable
0x6c620400, 0x6a6f0400, 0x73790400, 0x69710400, 0x6b770400, 0x73610400,
0x79650400, 0x6f6d0400, 0x70730400, 0x61650400, 0x696c0400, 0x62680400,
0x71610400, 0x6d6e0400, 0x6e700400, 0x61650400, 0x61650400, 0x69720400,
- 0x757a0400, 0x746a0400, 0x6b670400, 0x746d0400, 0x6a707447, 0x6a707447,
- 0x6b720400, 0x766e0400, 0x686b0400, 0x6d6f0400, 0x6b680400, 0x6c610400,
- 0x636e6c29, 0x636e6c29, 0x74770400, 0x6b700400, 0x62640400, 0x6d760400,
- 0x6d790400, 0x61755c53, 0x69640400, 0x746c0400, 0x70680400, 0x74680400,
- 0x73675c33, 0x626e0400, 0x6e7a0513, 0x6d700400, 0x67750400, 0x6e720400,
+ 0x757a0400, 0x746a0400, 0x6b670400, 0x746d0400, 0x6a707457, 0x6a707457,
+ 0x6b726c38, 0x766e0400, 0x686b0400, 0x6d6f0400, 0x6b680400, 0x6c610400,
+ 0x636e6c2a, 0x636e6c2a, 0x74770400, 0x6b700400, 0x62640400, 0x6d760400,
+ 0x6d790400, 0x61755c63, 0x69640400, 0x746c0400, 0x70680400, 0x74680400,
+ 0x73675c43, 0x626e0400, 0x6e7a0523, 0x6d700400, 0x67750400, 0x6e720400,
0x70670400, 0x746f0400, 0x73620400, 0x76750400, 0x666a0400, 0x77660400,
0x61730400, 0x6b690400, 0x6e630400, 0x70660400, 0x636b0400, 0x77730400,
0x666d0400, 0x6d680400, 0x70770400, 0x65670400, 0x647a0400, 0x6d610400,
diff --git a/telephony/java/com/android/internal/telephony/Phone.java b/telephony/java/com/android/internal/telephony/Phone.java
index 3c9a1f8..e426e94 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
@@ -1167,6 +1169,11 @@ public interface Phone {
boolean getMute();
/**
+ * Enables or disables echo suppression.
+ */
+ void setEchoSuppressionEnabled(boolean enabled);
+
+ /**
* Invokes RIL_REQUEST_OEM_HOOK_RAW on RIL implementation.
*
* @param data The data for the request.
diff --git a/telephony/java/com/android/internal/telephony/PhoneBase.java b/telephony/java/com/android/internal/telephony/PhoneBase.java
index c3c8f5e..1674ad6 100644
--- a/telephony/java/com/android/internal/telephony/PhoneBase.java
+++ b/telephony/java/com/android/internal/telephony/PhoneBase.java
@@ -286,7 +286,7 @@ public abstract class PhoneBase extends Handler implements Phone {
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext());
SharedPreferences.Editor editor = sp.edit();
editor.putBoolean(DNS_SERVER_CHECK_DISABLED_KEY, b);
- editor.commit();
+ editor.apply();
}
/**
@@ -505,6 +505,10 @@ public abstract class PhoneBase extends Handler implements Phone {
mCM.unregisterForResendIncallMute(h);
}
+ public void setEchoSuppressionEnabled(boolean enabled) {
+ // no need for regular phone
+ }
+
/**
* Subclasses of Phone probably want to replace this with a
* version scoped to their packages
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/PhoneProxy.java b/telephony/java/com/android/internal/telephony/PhoneProxy.java
index 5e7dcb0..77f1e6c 100644
--- a/telephony/java/com/android/internal/telephony/PhoneProxy.java
+++ b/telephony/java/com/android/internal/telephony/PhoneProxy.java
@@ -568,6 +568,10 @@ public class PhoneProxy extends Handler implements Phone {
return mActivePhone.getMute();
}
+ public void setEchoSuppressionEnabled(boolean enabled) {
+ mActivePhone.setEchoSuppressionEnabled(enabled);
+ }
+
public void invokeOemRilRequestRaw(byte[] data, Message response) {
mActivePhone.invokeOemRilRequestRaw(data, response);
}
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..917e1d8 100644
--- a/telephony/java/com/android/internal/telephony/SMSDispatcher.java
+++ b/telephony/java/com/android/internal/telephony/SMSDispatcher.java
@@ -32,9 +32,11 @@ import android.database.Cursor;
import android.database.SQLException;
import android.net.Uri;
import android.os.AsyncResult;
+import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.os.PowerManager;
+import android.os.StatFs;
import android.provider.Telephony;
import android.provider.Telephony.Sms.Intents;
import android.provider.Settings;
@@ -44,10 +46,6 @@ import android.util.Config;
import android.util.Log;
import android.view.WindowManager;
-import com.android.internal.telephony.CommandsInterface;
-import com.android.internal.telephony.SmsMessageBase;
-import com.android.internal.telephony.SmsResponse;
-import com.android.internal.telephony.WapPushOverSms;
import com.android.internal.util.HexDump;
import java.io.ByteArrayOutputStream;
@@ -75,7 +73,7 @@ public abstract class SMSDispatcher extends Handler {
private static final int DEFAULT_SMS_MAX_COUNT = 100;
/** Default timeout for SMS sent query */
- private static final int DEFAULT_SMS_TIMOUEOUT = 6000;
+ private static final int DEFAULT_SMS_TIMEOUT = 6000;
protected static final String[] RAW_PROJECTION = new String[] {
"pdu",
@@ -83,8 +81,6 @@ public abstract class SMSDispatcher extends Handler {
"destination_port",
};
- static final int MAIL_SEND_SMS = 1;
-
static final protected int EVENT_NEW_SMS = 1;
static final protected int EVENT_SEND_SMS_COMPLETE = 2;
@@ -143,7 +139,7 @@ public abstract class SMSDispatcher extends Handler {
private SmsCounter mCounter;
- private ArrayList mSTrackers = new ArrayList(MO_MSG_QUEUE_LIMIT);
+ private ArrayList<SmsTracker> mSTrackers = new ArrayList<SmsTracker>(MO_MSG_QUEUE_LIMIT);
/** Wake lock to ensure device stays awake while dispatching the SMS intent. */
private PowerManager.WakeLock mWakeLock;
@@ -154,10 +150,6 @@ public abstract class SMSDispatcher extends Handler {
*/
private final int WAKE_LOCK_TIMEOUT = 5000;
- private static SmsMessage mSmsMessage;
- private static SmsMessageBase mSmsMessageBase;
- private SmsMessageBase.SubmitPduBase mSubmitPduBase;
-
protected boolean mStorageAvailable = true;
protected boolean mReportMemoryStatusPending = false;
@@ -250,11 +242,9 @@ public abstract class SMSDispatcher extends Handler {
// Register for device storage intents. Use these to notify the RIL
// that storage for SMS is or is not available.
- // TODO: Revisit this for a later release. Storage reporting should
- // rely more on application indication.
IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_DEVICE_STORAGE_LOW);
- filter.addAction(Intent.ACTION_DEVICE_STORAGE_OK);
+ filter.addAction(Intent.ACTION_DEVICE_STORAGE_FULL);
+ filter.addAction(Intent.ACTION_DEVICE_STORAGE_NOT_FULL);
mContext.registerReceiver(mResultReceiver, filter);
}
@@ -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>
@@ -689,7 +679,7 @@ public abstract class SMSDispatcher extends Handler {
* the extra "errorCode" containing a radio technology specific value,
* generally only useful for troubleshooting.<br>
* The per-application based SMS control checks sentIntent. If sentIntent
- * is NULL the caller will be checked against all unknown applicaitons,
+ * is NULL the caller will be checked against all unknown applications,
* which cause smaller number of SMS to be sent in checking period.
* @param deliveryIntent if not NULL this <code>PendingIntent</code> is
* broadcast when the message is delivered to the recipient. The
@@ -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>
@@ -742,7 +732,7 @@ public abstract class SMSDispatcher extends Handler {
* <code>RESULT_ERROR_RADIO_OFF</code>
* <code>RESULT_ERROR_NULL_PDU</code>.
* The per-application based SMS control checks sentIntent. If sentIntent
- * is NULL the caller will be checked against all unknown applicaitons,
+ * is NULL the caller will be checked against all unknown applications,
* which cause smaller number of SMS to be sent in checking period.
* @param deliveryIntents if not null, an <code>ArrayList</code> of
* <code>PendingIntent</code>s (one for each message part) that is
@@ -758,17 +748,17 @@ public abstract class SMSDispatcher extends Handler {
* Send a SMS
*
* @param smsc the SMSC to send the message through, or NULL for the
- * defatult SMSC
+ * default SMSC
* @param pdu the raw PDU to send
* @param sentIntent if not NULL this <code>Intent</code> is
- * broadcast when the message is sucessfully sent, or failed.
+ * broadcast when the message is successfully sent, or failed.
* The result code will be <code>Activity.RESULT_OK<code> for success,
* or one of these errors:
* <code>RESULT_ERROR_GENERIC_FAILURE</code>
* <code>RESULT_ERROR_RADIO_OFF</code>
* <code>RESULT_ERROR_NULL_PDU</code>.
* The per-application based SMS control checks sentIntent. If sentIntent
- * is NULL the caller will be checked against all unknown applicaitons,
+ * is NULL the caller will be checked against all unknown applications,
* which cause smaller number of SMS to be sent in checking period.
* @param deliveryIntent if not NULL this <code>Intent</code> is
* broadcast when the message is delivered to the recipient. The
@@ -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_FULL)) {
+ mStorageAvailable = false;
+ mCm.reportSmsMemoryStatus(false, obtainMessage(EVENT_REPORT_MEMORY_STATUS_DONE));
+ } else if (intent.getAction().equals(Intent.ACTION_DEVICE_STORAGE_NOT_FULL)) {
+ mStorageAvailable = true;
+ mCm.reportSmsMemoryStatus(true, obtainMessage(EVENT_REPORT_MEMORY_STATUS_DONE));
+ } else {
+ // Assume the intent is one of the SMS receive intents that
+ // was sent as an ordered broadcast. Check result and ACK.
+ int rc = getResultCode();
+ boolean success = (rc == Activity.RESULT_OK)
+ || (rc == Intents.RESULT_SMS_HANDLED);
+
+ // For a multi-part message, this only ACKs the last part.
+ // Previous parts were ACK'd as they were received.
+ acknowledgeLastIncomingSms(success, rc, null);
}
-
- };
+ }
+ };
}
diff --git a/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java b/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
index 1c81a07..f31bf24 100755
--- a/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
@@ -158,7 +158,7 @@ public class CDMAPhone extends PhoneBase {
mDataConnection = new CdmaDataConnectionTracker (this);
mRuimCard = new RuimCard(this);
mRuimPhoneBookInterfaceManager = new RuimPhoneBookInterfaceManager(this);
- mRuimSmsInterfaceManager = new RuimSmsInterfaceManager(this);
+ mRuimSmsInterfaceManager = new RuimSmsInterfaceManager(this, mSMS);
mSubInfo = new PhoneSubInfo(this);
mEriManager = new EriManager(this, context, EriManager.ERI_FROM_XML);
mCcatService = CatService.getInstance(mCM, mRuimRecords, mContext,
@@ -453,9 +453,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() {
@@ -1406,7 +1411,7 @@ public class CDMAPhone extends PhoneBase {
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext());
SharedPreferences.Editor editor = sp.edit();
editor.putString(VM_NUMBER_CDMA, number);
- editor.commit();
+ editor.apply();
}
/**
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..dceff2a 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
@@ -28,21 +28,20 @@ import android.database.SQLException;
import android.os.AsyncResult;
import android.os.Message;
import android.os.SystemProperties;
+import android.preference.PreferenceManager;
import android.provider.Telephony;
import android.provider.Telephony.Sms.Intents;
-import android.preference.PreferenceManager;
-import android.util.Config;
-import android.util.Log;
import android.telephony.SmsManager;
import android.telephony.SmsMessage.MessageClass;
+import android.util.Config;
+import android.util.Log;
-import com.android.internal.telephony.TelephonyProperties;
import com.android.internal.telephony.CommandsInterface;
+import com.android.internal.telephony.SMSDispatcher;
import com.android.internal.telephony.SmsHeader;
import com.android.internal.telephony.SmsMessageBase;
-import com.android.internal.telephony.SMSDispatcher;
import com.android.internal.telephony.SmsMessageBase.TextEncodingDetails;
-import com.android.internal.telephony.cdma.SmsMessage;
+import com.android.internal.telephony.TelephonyProperties;
import com.android.internal.telephony.cdma.sms.SmsEnvelope;
import com.android.internal.telephony.cdma.sms.UserData;
import com.android.internal.util.HexDump;
@@ -51,20 +50,16 @@ import java.io.ByteArrayOutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
-import java.lang.Boolean;
final class CdmaSMSDispatcher extends SMSDispatcher {
private static final String TAG = "CDMA";
- private CDMAPhone mCdmaPhone;
-
private byte[] mLastDispatchedSmsFingerprint;
private byte[] mLastAcknowledgedSmsFingerprint;
CdmaSMSDispatcher(CDMAPhone phone) {
super(phone);
- mCdmaPhone = phone;
}
/**
@@ -129,10 +124,10 @@ final class CdmaSMSDispatcher extends SMSDispatcher {
Log.d(TAG, "Voicemail count=" + voicemailCount);
// Store the voicemail count in preferences.
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(
- ((CDMAPhone) mPhone).getContext());
+ mPhone.getContext());
SharedPreferences.Editor editor = sp.edit();
editor.putInt(CDMAPhone.VM_COUNT_CDMA, voicemailCount);
- editor.commit();
+ editor.apply();
((CDMAPhone) mPhone).updateMessageWaitingIndicator(voicemailCount);
handled = true;
} else if (((SmsEnvelope.TELESERVICE_WMT == teleService) ||
@@ -176,7 +171,7 @@ final class CdmaSMSDispatcher extends SMSDispatcher {
* TODO(cleanup): Why are we using a getter method for this
* (and for so many other sms fields)? Trivial getters and
* setters like this are direct violations of the style guide.
- * If the purpose is to protect agaist writes (by not
+ * If the purpose is to protect against writes (by not
* providing a setter) then any protection is illusory (and
* hence bad) for cases where the values are not primitives,
* such as this call for the header. Since this is an issue
@@ -440,7 +435,7 @@ final class CdmaSMSDispatcher extends SMSDispatcher {
protected void sendSms(SmsTracker tracker) {
HashMap map = tracker.mData;
- byte smsc[] = (byte[]) map.get("smsc");
+ // byte smsc[] = (byte[]) map.get("smsc"); // unused for CDMA
byte pdu[] = (byte[]) map.get("pdu");
Message reply = obtainMessage(EVENT_SEND_SMS_COMPLETE, tracker);
diff --git a/telephony/java/com/android/internal/telephony/cdma/RuimSmsInterfaceManager.java b/telephony/java/com/android/internal/telephony/cdma/RuimSmsInterfaceManager.java
index cfcfd98..e97549d 100644
--- a/telephony/java/com/android/internal/telephony/cdma/RuimSmsInterfaceManager.java
+++ b/telephony/java/com/android/internal/telephony/cdma/RuimSmsInterfaceManager.java
@@ -27,6 +27,7 @@ import com.android.internal.telephony.IccConstants;
import com.android.internal.telephony.IccSmsInterfaceManager;
import com.android.internal.telephony.IccUtils;
import com.android.internal.telephony.PhoneProxy;
+import com.android.internal.telephony.SMSDispatcher;
import com.android.internal.telephony.SmsRawData;
import java.util.ArrayList;
@@ -81,9 +82,9 @@ public class RuimSmsInterfaceManager extends IccSmsInterfaceManager {
}
};
- public RuimSmsInterfaceManager(CDMAPhone phone) {
+ public RuimSmsInterfaceManager(CDMAPhone phone, SMSDispatcher dispatcher) {
super(phone);
- mDispatcher = new CdmaSMSDispatcher(phone);
+ mDispatcher = dispatcher;
}
public void dispose() {
diff --git a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
index f869dbd..d563dc8 100644
--- a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
@@ -59,7 +59,7 @@ import static android.telephony.SmsMessage.MessageClass;
/**
* TODO(cleanup): internally returning null in many places makes
* debugging very hard (among many other reasons) and should be made
- * more meaningful (replaced with execptions for example). Null
+ * more meaningful (replaced with exceptions for example). Null
* returns should only occur at the very outside of the module/class
* scope.
*/
@@ -299,7 +299,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.
@@ -367,7 +367,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.
@@ -458,7 +458,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);
}
/**
@@ -626,7 +626,7 @@ public class SmsMessage extends SmsMessageBase {
* incrementing within the range 1..65535 remembering the state
* via a persistent system property. (See C.S0015-B, v2.0,
* 4.3.1.5) Since this routine is expected to be accessed via via
- * binder-call, and hence should be threadsafe, it has been
+ * binder-call, and hence should be thread-safe, it has been
* synchronized.
*/
private synchronized static int getNextMessageId() {
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/cdma/sms/UserData.java b/telephony/java/com/android/internal/telephony/cdma/sms/UserData.java
index d93852c..189d97db 100644
--- a/telephony/java/com/android/internal/telephony/cdma/sms/UserData.java
+++ b/telephony/java/com/android/internal/telephony/cdma/sms/UserData.java
@@ -56,7 +56,7 @@ public class UserData {
* 0, with the resulting code of 0x20.
*
* Note this mapping is also equivalent to that used by both the
- * IS5 and the IS-91 encodings. For the former this is defined
+ * IA5 and the IS-91 encodings. For the former this is defined
* using CCITT Rec. T.50 Tables 1 and 3. For the latter IS 637 B,
* Table 4.3.1.4.1-1 -- and note the encoding uses only 6 bits,
* and hence only maps entries up to the '_' character.
diff --git a/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java b/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java
index 49de5f9..3959c67 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java
@@ -117,10 +117,6 @@ public class GSMPhone extends PhoneBase {
Thread debugPortThread;
ServerSocket debugSocket;
- private int mReportedRadioResets;
- private int mReportedAttemptedConnects;
- private int mReportedSuccessfulConnects;
-
private String mImei;
private String mImeiSv;
private String mVmNumber;
@@ -151,7 +147,7 @@ public class GSMPhone extends PhoneBase {
mSimCard = new SimCard(this);
if (!unitTestMode) {
mSimPhoneBookIntManager = new SimPhoneBookInterfaceManager(this);
- mSimSmsIntManager = new SimSmsInterfaceManager(this);
+ mSimSmsIntManager = new SimSmsInterfaceManager(this, mSMS);
mSubInfo = new PhoneSubInfo(this);
}
mStkService = CatService.getInstance(mCM, mSIMRecords, mContext,
@@ -807,7 +803,7 @@ public class GSMPhone extends PhoneBase {
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext());
SharedPreferences.Editor editor = sp.edit();
editor.putString(VM_NUMBER, number);
- editor.commit();
+ editor.apply();
setVmSimImsi(getSubscriberId());
}
@@ -830,7 +826,7 @@ public class GSMPhone extends PhoneBase {
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext());
SharedPreferences.Editor editor = sp.edit();
editor.putString(VM_SIM_IMSI, imsi);
- editor.commit();
+ editor.apply();
}
public String getVoiceMailAlphaTag() {
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmCall.java b/telephony/java/com/android/internal/telephony/gsm/GsmCall.java
index 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..e7d57bc 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 ) {
@@ -1158,10 +1157,8 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
// No try for permanent failure
if (cause.isPermanentFail()) {
notifyNoData(cause);
- if (!mRequestedApnType.equals(Phone.APN_TYPE_DEFAULT)) {
- phone.notifyDataConnection(Phone.REASON_APN_FAILED);
- onEnableApn(apnTypeToId(mRequestedApnType), DISABLED);
- }
+ phone.notifyDataConnection(Phone.REASON_APN_FAILED);
+ onEnableApn(apnTypeToId(mRequestedApnType), DISABLED);
return;
}
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..6ddb312 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
@@ -358,8 +358,14 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
EVENT_SIM_RECORDS_LOADED, null);
mNeedToRegForSimLoaded = false;
}
- // restore the previous network selection.
- phone.restoreSavedNetworkSelection(null);
+
+ boolean skipRestoringSelection = phone.getContext().getResources().getBoolean(
+ com.android.internal.R.bool.skip_restoring_network_selection);
+
+ if (!skipRestoringSelection) {
+ // restore the previous network selection.
+ phone.restoreSavedNetworkSelection(null);
+ }
pollState();
// Signal strength polling stops when radio is off
queueNextSignalStrengthPoll();
@@ -650,6 +656,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 +668,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 +689,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/SimSmsInterfaceManager.java b/telephony/java/com/android/internal/telephony/gsm/SimSmsInterfaceManager.java
index 2028ca4..67ecc77 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SimSmsInterfaceManager.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SimSmsInterfaceManager.java
@@ -25,6 +25,7 @@ import android.util.Log;
import com.android.internal.telephony.IccConstants;
import com.android.internal.telephony.IccSmsInterfaceManager;
import com.android.internal.telephony.IccUtils;
+import com.android.internal.telephony.SMSDispatcher;
import com.android.internal.telephony.SmsRawData;
import java.util.ArrayList;
@@ -78,9 +79,9 @@ public class SimSmsInterfaceManager extends IccSmsInterfaceManager {
}
};
- public SimSmsInterfaceManager(GSMPhone phone) {
+ public SimSmsInterfaceManager(GSMPhone phone, SMSDispatcher dispatcher) {
super(phone);
- mDispatcher = new GsmSMSDispatcher(phone);
+ mDispatcher = dispatcher;
}
public void dispose() {
diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
index 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/SipCallBase.java b/telephony/java/com/android/internal/telephony/sip/SipCallBase.java
new file mode 100644
index 0000000..9050a43
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/sip/SipCallBase.java
@@ -0,0 +1,55 @@
+/*
+ * 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.Phone;
+
+import android.net.sip.SipManager;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+abstract class SipCallBase extends Call {
+ protected List<Connection> connections = new ArrayList<Connection>();
+
+ protected abstract void setState(State newState);
+
+ public List<Connection> getConnections() {
+ // FIXME should return Collections.unmodifiableList();
+ return connections;
+ }
+
+ public boolean isMultiparty() {
+ return connections.size() > 1;
+ }
+
+ public String toString() {
+ return state.toString() + ":" + super.toString();
+ }
+
+ 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..ed578c8
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/sip/SipCommandInterface.java
@@ -0,0 +1,372 @@
+/*
+ * 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..dc4b27b
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/sip/SipConnectionBase.java
@@ -0,0 +1,174 @@
+/*
+ * 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.Connection;
+import com.android.internal.telephony.Phone;
+
+import android.net.sip.SipAudioCall;
+import android.os.SystemClock;
+import android.util.Log;
+import android.telephony.PhoneNumberUtils;
+
+abstract class SipConnectionBase extends Connection {
+ 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;
+
+ /*
+ * 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();
+
+ @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;
+ }
+}
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..67f13bd
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/sip/SipPhone.java
@@ -0,0 +1,872 @@
+/*
+ * 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.rtp.AudioGroup;
+import android.net.sip.SipAudioCall;
+import android.net.sip.SipErrorCode;
+import android.net.sip.SipException;
+import android.net.sip.SipManager;
+import android.net.sip.SipProfile;
+import android.net.sip.SipSession;
+import android.os.AsyncResult;
+import android.os.Message;
+import android.telephony.PhoneNumberUtils;
+import android.telephony.ServiceState;
+import android.util.Log;
+
+import com.android.internal.telephony.Call;
+import com.android.internal.telephony.CallStateException;
+import com.android.internal.telephony.Connection;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneNotifier;
+import com.android.internal.telephony.UUSInfo;
+
+import java.text.ParseException;
+import java.util.List;
+
+/**
+ * {@hide}
+ */
+public class SipPhone extends SipPhoneBase {
+ private static final String LOG_TAG = "SipPhone";
+ private static final boolean DEBUG = true;
+ private static final int TIMEOUT_MAKE_CALL = 15; // in seconds
+ private static final int TIMEOUT_ANSWER_CALL = 8; // in seconds
+ private static final int TIMEOUT_HOLD_CALL = 15; // in seconds
+
+ // 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);
+
+ if (DEBUG) Log.d(LOG_TAG, "new SipPhone: " + profile.getUriString());
+ ringingCall = new SipCall();
+ foregroundCall = new SipCall();
+ backgroundCall = new SipCall();
+ mProfile = profile;
+ mSipManager = SipManager.newInstance(context);
+ }
+
+ public String getPhoneName() {
+ return "SIP:" + getUriString(mProfile);
+ }
+
+ public String getSipUri() {
+ return mProfile.getUriString();
+ }
+
+ public boolean equals(SipPhone phone) {
+ return getSipUri().equals(phone.getSipUri());
+ }
+
+ public boolean canTake(Object incomingCall) {
+ synchronized (SipPhone.class) {
+ if (!(incomingCall instanceof SipAudioCall)) return false;
+ if (ringingCall.getState().isAlive()) return false;
+
+ // FIXME: is it true that we cannot take any incoming call if
+ // both foreground and background are active
+ if (foregroundCall.getState().isAlive()
+ && backgroundCall.getState().isAlive()) {
+ return false;
+ }
+
+ try {
+ SipAudioCall sipAudioCall = (SipAudioCall) incomingCall;
+ if (DEBUG) Log.d(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);
+ if (sipAudioCall.getState()
+ != SipSession.State.INCOMING_CALL) {
+ // Peer cancelled the call!
+ if (DEBUG) Log.d(LOG_TAG, " call cancelled !!");
+ ringingCall.reset();
+ }
+ return true;
+ }
+ } catch (Exception e) {
+ // Peer may cancel the call at any time during the time we hook
+ // up ringingCall with sipAudioCall. Clean up ringingCall when
+ // that happens.
+ ringingCall.reset();
+ }
+ return false;
+ }
+ }
+
+ public void acceptCall() throws CallStateException {
+ synchronized (SipPhone.class) {
+ if ((ringingCall.getState() == Call.State.INCOMING) ||
+ (ringingCall.getState() == Call.State.WAITING)) {
+ if (DEBUG) Log.d(LOG_TAG, "acceptCall");
+ // Always unmute when answering a new call
+ setMute(false);
+ ringingCall.acceptCall();
+ } else {
+ throw new CallStateException("phone not ringing");
+ }
+ }
+ }
+
+ public void rejectCall() throws CallStateException {
+ synchronized (SipPhone.class) {
+ if (ringingCall.getState().isRinging()) {
+ if (DEBUG) Log.d(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 {
+ 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);
+ 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 {
+ if (DEBUG) Log.d(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");
+ }
+
+ @Override
+ public void setEchoSuppressionEnabled(boolean enabled) {
+ synchronized (SipPhone.class) {
+ AudioGroup audioGroup = foregroundCall.getAudioGroup();
+ if (audioGroup == null) return;
+ int mode = audioGroup.getMode();
+ audioGroup.setMode(enabled
+ ? AudioGroup.MODE_ECHO_SUPPRESSION
+ : AudioGroup.MODE_NORMAL);
+ if (DEBUG) Log.d(LOG_TAG, String.format(
+ "audioGroup mode change: %d --> %d", mode,
+ audioGroup.getMode()));
+ }
+ }
+
+ 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 reset() {
+ connections.clear();
+ setState(Call.State.IDLE);
+ }
+
+ 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;
+ }
+ }
+
+ Connection dial(String originalNumber) throws SipException {
+ String calleeSipUri = originalNumber;
+ if (!calleeSipUri.contains("@")) {
+ calleeSipUri += "@" + getSipDomain(mProfile);
+ }
+ try {
+ SipProfile callee =
+ new SipProfile.Builder(calleeSipUri).build();
+ SipConnection c = new SipConnection(this, callee);
+ 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) {
+ if (state.isAlive()) {
+ if (DEBUG) Log.d(LOG_TAG, "hang up call: " + getState()
+ + ": " + this + " on phone " + getPhone());
+ setState(State.DISCONNECTING);
+ CallStateException excp = null;
+ for (Connection c : connections) {
+ try {
+ c.hangup();
+ } catch (CallStateException e) {
+ excp = e;
+ }
+ }
+ if (excp != null) throw excp;
+ } else {
+ if (DEBUG) Log.d(LOG_TAG, "hang up dead call: " + getState()
+ + ": " + this + " on phone " + getPhone());
+ }
+ }
+ }
+
+ void initIncomingCall(SipAudioCall sipAudioCall, boolean makeCallWait) {
+ SipProfile callee = sipAudioCall.getPeerProfile();
+ SipConnection c = new SipConnection(this, callee);
+ 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 != ringingCall) {
+ throw new CallStateException("acceptCall() in a non-ringing 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) {
+ if (DEBUG) 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;
+ if (DEBUG) Log.d(LOG_TAG, "---check connections: "
+ + connections.size());
+ for (Connection c : connections) {
+ if (DEBUG) Log.d(LOG_TAG, " state=" + c.getState() + ": "
+ + c);
+ 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);
+ SipAudioCall sipAudioCall = mSipAudioCall;
+ mSipAudioCall = null;
+ String sessionState = (sipAudioCall == null)
+ ? ""
+ : (sipAudioCall.getState() + ", ");
+ if (DEBUG) Log.d(LOG_TAG, "--- connection ended: "
+ + mPeer.getUriString() + ": " + sessionState
+ + "cause: " + getDisconnectCause() + ", on phone "
+ + getPhone());
+ if (sipAudioCall != null) {
+ sipAudioCall.setListener(null);
+ sipAudioCall.close();
+ }
+ mOwner.onConnectionEnded(SipConnection.this);
+ }
+ }
+
+ @Override
+ public void onCallEstablished(SipAudioCall call) {
+ onChanged(call);
+ if (mState == Call.State.ACTIVE) call.startAudio();
+ }
+
+ @Override
+ public void onCallHeld(SipAudioCall call) {
+ onChanged(call);
+ if (mState == Call.State.HOLDING) call.startAudio();
+ }
+
+ @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 {
+ if (mOwner == ringingCall) {
+ if (ringingCall.getState() == Call.State.WAITING) {
+ try {
+ switchHoldingAndActive();
+ } catch (CallStateException e) {
+ // disconnect the call.
+ onCallEnded(DisconnectCause.LOCAL);
+ return;
+ }
+ }
+ foregroundCall.switchWith(ringingCall);
+ }
+ setState(newState);
+ }
+ mOwner.onConnectionStateChanged(SipConnection.this);
+ if (DEBUG) Log.v(LOG_TAG, "+***+ connection state changed: "
+ + mPeer.getUriString() + ": " + mState
+ + " on phone " + getPhone());
+ }
+ }
+
+ @Override
+ protected void onError(DisconnectCause cause) {
+ if (DEBUG) Log.d(LOG_TAG, "SIP error: " + cause);
+ if (mSipAudioCall.isInCall()
+ && (cause != DisconnectCause.LOST_SIGNAL)) {
+ // Don't end the call when in a call.
+ return;
+ }
+
+ onCallEnded(cause);
+ }
+ };
+
+ public SipConnection(SipCall owner, SipProfile callee) {
+ super(getUriString(callee));
+ mOwner = owner;
+ mPeer = callee;
+ }
+
+ 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(TIMEOUT_ANSWER_CALL);
+ } 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(mProfile, mPeer, null,
+ TIMEOUT_MAKE_CALL);
+ mSipAudioCall.setRingbackToneEnabled(false);
+ mSipAudioCall.setListener(mAdapter);
+ }
+
+ void hold() throws CallStateException {
+ setState(Call.State.HOLDING);
+ try {
+ mSipAudioCall.holdCall(TIMEOUT_HOLD_CALL);
+ } catch (SipException e) {
+ throw new CallStateException("hold(): " + e);
+ }
+ }
+
+ void unhold(AudioGroup audioGroup) throws CallStateException {
+ mSipAudioCall.setAudioGroup(audioGroup);
+ setState(Call.State.ACTIVE);
+ try {
+ mSipAudioCall.continueCall(TIMEOUT_HOLD_CALL);
+ } 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) {
+ if (DEBUG) Log.d(LOG_TAG, "hangup conn: " + mPeer.getUriString()
+ + ": " + mState + ": on phone "
+ + getPhone().getPhoneName());
+ if (!mState.isAlive()) return;
+ try {
+ SipAudioCall sipAudioCall = mSipAudioCall;
+ if (sipAudioCall != null) {
+ sipAudioCall.setListener(null);
+ sipAudioCall.endCall();
+ }
+ } catch (SipException e) {
+ throw new CallStateException("hangup(): " + e);
+ } finally {
+ mAdapter.onCallEnded(((mState == Call.State.INCOMING)
+ || (mState == Call.State.WAITING))
+ ? DisconnectCause.INCOMING_REJECTED
+ : DisconnectCause.LOCAL);
+ }
+ }
+ }
+
+ @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());
+ }
+ if (DEBUG) Log.d(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;
+ int sessionState = sipAudioCall.getState();
+ switch (sessionState) {
+ case SipSession.State.READY_TO_CALL: return Call.State.IDLE;
+ case SipSession.State.INCOMING_CALL:
+ case SipSession.State.INCOMING_CALL_ANSWERING: return Call.State.INCOMING;
+ case SipSession.State.OUTGOING_CALL: return Call.State.DIALING;
+ case SipSession.State.OUTGOING_CALL_RING_BACK: return Call.State.ALERTING;
+ case SipSession.State.OUTGOING_CALL_CANCELING: return Call.State.DISCONNECTING;
+ case SipSession.State.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.Listener {
+ protected abstract void onCallEnded(Connection.DisconnectCause cause);
+ protected abstract void onError(Connection.DisconnectCause cause);
+
+ @Override
+ public void onCallEnded(SipAudioCall call) {
+ onCallEnded(call.isInCall()
+ ? Connection.DisconnectCause.NORMAL
+ : Connection.DisconnectCause.INCOMING_MISSED);
+ }
+
+ @Override
+ public void onCallBusy(SipAudioCall call) {
+ onCallEnded(Connection.DisconnectCause.BUSY);
+ }
+
+ @Override
+ public void onError(SipAudioCall call, int errorCode,
+ String errorMessage) {
+ switch (errorCode) {
+ case SipErrorCode.SERVER_UNREACHABLE:
+ onError(Connection.DisconnectCause.SERVER_UNREACHABLE);
+ break;
+ case SipErrorCode.PEER_NOT_REACHABLE:
+ onError(Connection.DisconnectCause.NUMBER_UNREACHABLE);
+ break;
+ case SipErrorCode.INVALID_REMOTE_URI:
+ onError(Connection.DisconnectCause.INVALID_NUMBER);
+ break;
+ case SipErrorCode.TIME_OUT:
+ case SipErrorCode.TRANSACTION_TERMINTED:
+ onError(Connection.DisconnectCause.TIMED_OUT);
+ break;
+ case SipErrorCode.DATA_CONNECTION_LOST:
+ onError(Connection.DisconnectCause.LOST_SIGNAL);
+ break;
+ case SipErrorCode.INVALID_CREDENTIALS:
+ onError(Connection.DisconnectCause.INVALID_CREDENTIALS);
+ break;
+ case SipErrorCode.CROSS_DOMAIN_AUTHENTICATION:
+ onError(Connection.DisconnectCause.OUT_OF_NETWORK);
+ break;
+ case SipErrorCode.SERVER_ERROR:
+ onError(Connection.DisconnectCause.SERVER_ERROR);
+ break;
+ case SipErrorCode.SOCKET_ERROR:
+ case SipErrorCode.CLIENT_ERROR:
+ default:
+ Log.w(LOG_TAG, "error: " + SipErrorCode.toString(errorCode)
+ + ": " + errorMessage);
+ onError(Connection.DisconnectCause.ERROR_UNSPECIFIED);
+ }
+ }
+ }
+}
diff --git a/telephony/java/com/android/internal/telephony/sip/SipPhoneBase.java b/telephony/java/com/android/internal/telephony/sip/SipPhoneBase.java
new file mode 100755
index 0000000..5499966
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/sip/SipPhoneBase.java
@@ -0,0 +1,439 @@
+/*
+ * 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.AsyncResult;
+import android.os.Handler;
+import android.os.Message;
+import android.os.Registrant;
+import android.os.RegistrantList;
+import android.os.SystemProperties;
+import android.telephony.CellLocation;
+import android.telephony.ServiceState;
+import android.telephony.SignalStrength;
+import android.util.Log;
+
+import com.android.internal.telephony.Call;
+import com.android.internal.telephony.CallStateException;
+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.PhoneSubInfo;
+import com.android.internal.telephony.TelephonyProperties;
+
+import java.util.ArrayList;
+import java.util.List;
+
+abstract class SipPhoneBase extends PhoneBase {
+ private static final String LOG_TAG = "SipPhone";
+
+ private RegistrantList mRingbackRegistrants = new RegistrantList();
+ private State state = State.IDLE;
+
+ public SipPhoneBase(Context context, PhoneNotifier notifier) {
+ super(notifier, context, new SipCommandInterface(context), false);
+ }
+
+ public abstract Call getForegroundCall();
+
+ public abstract Call getBackgroundCall();
+
+ public abstract Call getRingingCall();
+
+ 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 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;
+ }
+
+ public State getState() {
+ return state;
+ }
+
+ public int getPhoneType() {
+ return Phone.PHONE_TYPE_SIP;
+ }
+
+ public SignalStrength getSignalStrength() {
+ return new SignalStrength();
+ }
+
+ public boolean getMessageWaitingIndicator() {
+ return false;
+ }
+
+ public boolean getCallForwardingIndicator() {
+ return false;
+ }
+
+ public List<? extends MmiCode> getPendingMmiCodes() {
+ return new ArrayList<MmiCode>(0);
+ }
+
+ public DataState getDataConnectionState() {
+ return DataState.DISCONNECTED;
+ }
+
+ public DataState getDataConnectionState(String apnType) {
+ return DataState.DISCONNECTED;
+ }
+
+ public DataActivityState getDataActivityState() {
+ return DataActivityState.NONE;
+ }
+
+ /**
+ * Notify any interested party of a Phone state change {@link Phone.State}
+ */
+ void notifyPhoneStateChanged() {
+ mNotifier.notifyPhoneState(this);
+ }
+
+ /**
+ * Notify registrants of a change in the call state. This notifies changes in {@link Call.State}
+ * Use this when changes in the precise call state are needed, else use notifyPhoneStateChanged.
+ */
+ void notifyPreciseCallStateChanged() {
+ /* we'd love it if this was package-scoped*/
+ super.notifyPreciseCallStateChangedP();
+ }
+
+ void notifyNewRingingConnection(Connection c) {
+ 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();
+ }
+
+ public void getCallForwardingOption(int commandInterfaceCFReason, Message onComplete) {
+ }
+
+ public void setCallForwardingOption(int commandInterfaceCFAction,
+ int commandInterfaceCFReason, String dialingNumber,
+ int timerSeconds, Message onComplete) {
+ }
+
+ 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) {
+ AsyncResult.forMessage(onComplete, null, null);
+ onComplete.sendToTarget();
+ }
+
+ public void setCallWaiting(boolean enable, Message onComplete) {
+ Log.e(LOG_TAG, "call waiting not supported");
+ }
+
+ public boolean getIccRecordsLoaded() {
+ return false;
+ }
+
+ public IccCard getIccCard() {
+ return null;
+ }
+
+ public void getAvailableNetworks(Message response) {
+ }
+
+ public void setNetworkSelectionModeAutomatic(Message response) {
+ }
+
+ public void selectNetworkManually(
+ com.android.internal.telephony.gsm.NetworkInfo network,
+ Message response) {
+ }
+
+ public void getNeighboringCids(Message response) {
+ }
+
+ public void setOnPostDialCharacter(Handler h, int what, Object obj) {
+ }
+
+ public void getDataCallList(Message response) {
+ }
+
+ 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) {
+ }
+
+ public PhoneSubInfo getPhoneSubInfo(){
+ return null;
+ }
+
+ public IccSmsInterfaceManager getIccSmsInterfaceManager(){
+ return null;
+ }
+
+ public IccPhoneBookInterfaceManager getIccPhoneBookInterfaceManager(){
+ return null;
+ }
+
+ 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;
+ }
+
+ if (state != oldState) {
+ Log.d(LOG_TAG, " ^^^ new phone state: " + state);
+ 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);
}
diff --git a/telephony/mockril/Android.mk b/telephony/mockril/Android.mk
new file mode 100644
index 0000000..7c39cb1
--- /dev/null
+++ b/telephony/mockril/Android.mk
@@ -0,0 +1,31 @@
+#
+# 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.
+#
+#
+
+LOCAL_PATH:=$(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_JAVA_LIBRARIES := core framework
+
+LOCAL_STATIC_JAVA_LIBRARIES := librilproto-java
+
+LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE := mockrilcontroller
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/telephony/mockril/src/com/android/internal/telephony/mockril/MockRilController.java b/telephony/mockril/src/com/android/internal/telephony/mockril/MockRilController.java
new file mode 100644
index 0000000..99f0abe
--- /dev/null
+++ b/telephony/mockril/src/com/android/internal/telephony/mockril/MockRilController.java
@@ -0,0 +1,218 @@
+/*
+ * 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.mockril;
+
+import android.os.Bundle;
+import android.util.Log;
+import android.telephony.PhoneNumberUtils;
+
+import com.android.internal.communication.MsgHeader;
+import com.android.internal.communication.Msg;
+import com.android.internal.telephony.RilChannel;
+import com.android.internal.telephony.ril_proto.RilCtrlCmds;
+import com.android.internal.telephony.ril_proto.RilCmds;
+import com.google.protobuf.micro.MessageMicro;
+
+import java.io.IOException;
+
+/**
+ * Contain a list of commands to control Mock RIL. Before using these commands the devices
+ * needs to be set with Mock RIL. Refer to hardware/ril/mockril/README.txt for details.
+ *
+ */
+public class MockRilController {
+ private static final String TAG = "MockRILController";
+ private RilChannel mRilChannel = null;
+ private Msg mMessage = null;
+
+ public MockRilController() throws IOException {
+ mRilChannel = RilChannel.makeRilChannel();
+ }
+
+ /**
+ * Close the channel after the communication is done.
+ * This method has to be called after the test is finished.
+ */
+ public void closeChannel() {
+ mRilChannel.close();
+ }
+
+ /**
+ * Send commands and return true on success
+ * @param cmd for MsgHeader
+ * @param token for MsgHeader
+ * @param status for MsgHeader
+ * @param pbData for Msg data
+ * @return true if command is sent successfully, false if it fails
+ */
+ private boolean sendCtrlCommand(int cmd, long token, int status, MessageMicro pbData) {
+ try {
+ Msg.send(mRilChannel, cmd, token, status, pbData);
+ } catch (IOException e) {
+ Log.v(TAG, "send command : %d failed: " + e.getStackTrace());
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Get control response
+ * @return Msg if response is received, else return null.
+ */
+ private Msg getCtrlResponse() {
+ Msg response = null;
+ try {
+ response = Msg.recv(mRilChannel);
+ } catch (IOException e) {
+ Log.v(TAG, "receive response for getRadioState() error: " + e.getStackTrace());
+ return null;
+ }
+ return response;
+ }
+
+ /**
+ * @return the radio state if it is valid, otherwise return -1
+ */
+ public int getRadioState() {
+ if (!sendCtrlCommand(RilCtrlCmds.CTRL_CMD_GET_RADIO_STATE, 0, 0, null)) {
+ return -1;
+ }
+ Msg response = getCtrlResponse();
+ if (response == null) {
+ Log.v(TAG, "failed to get response");
+ return -1;
+ }
+ response.printHeader(TAG);
+ RilCtrlCmds.CtrlRspRadioState resp =
+ response.getDataAs(RilCtrlCmds.CtrlRspRadioState.class);
+ int state = resp.getState();
+ if ((state >= RilCmds.RADIOSTATE_OFF) && (state <= RilCmds.RADIOSTATE_NV_READY))
+ return state;
+ else
+ return -1;
+ }
+
+ /**
+ * Set the radio state of mock ril to the given state
+ * @param state for given radio state
+ * @return true if the state is set successful, false if it fails
+ */
+ public boolean setRadioState(int state) {
+ RilCtrlCmds.CtrlReqRadioState req = new RilCtrlCmds.CtrlReqRadioState();
+ if (state < 0 || state > RilCmds.RADIOSTATE_NV_READY) {
+ Log.v(TAG, "the give radio state is not valid.");
+ return false;
+ }
+ req.setState(state);
+ if (!sendCtrlCommand(RilCtrlCmds.CTRL_CMD_SET_RADIO_STATE, 0, 0, req)) {
+ Log.v(TAG, "send set radio state request failed.");
+ return false;
+ }
+ Msg response = getCtrlResponse();
+ if (response == null) {
+ Log.v(TAG, "failed to get response for setRadioState");
+ return false;
+ }
+ response.printHeader(TAG);
+ RilCtrlCmds.CtrlRspRadioState resp =
+ response.getDataAs(RilCtrlCmds.CtrlRspRadioState.class);
+ int curstate = resp.getState();
+ return curstate == state;
+ }
+
+ /**
+ * Start an incoming call for the given phone number
+ *
+ * @param phoneNumber is the number to show as incoming call
+ * @return true if the incoming call is started successfully, false if it fails.
+ */
+ public boolean startIncomingCall(String phoneNumber) {
+ RilCtrlCmds.CtrlReqSetMTCall req = new RilCtrlCmds.CtrlReqSetMTCall();
+
+ req.setPhoneNumber(phoneNumber);
+ if (!sendCtrlCommand(RilCtrlCmds.CTRL_CMD_SET_MT_CALL, 0, 0, req)) {
+ Log.v(TAG, "send CMD_SET_MT_CALL request failed");
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Hang up a connection remotelly for the given call fail cause
+ *
+ * @param connectionID is the connection to be hung up
+ * @param failCause is the call fail cause defined in ril.h
+ * @return true if the hangup is successful, false if it fails
+ */
+ public boolean hangupRemote(int connectionId, int failCause) {
+ RilCtrlCmds.CtrlHangupConnRemote req = new RilCtrlCmds.CtrlHangupConnRemote();
+ req.setConnectionId(connectionId);
+ req.setCallFailCause(failCause);
+
+ if (!sendCtrlCommand(RilCtrlCmds.CTRL_CMD_HANGUP_CONN_REMOTE, 0, 0, req)) {
+ Log.v(TAG, "send CTRL_CMD_HANGUP_CONN_REMOTE request failed");
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Set call transition flag to the Mock Ril
+ *
+ * @param flag is a boolean value for the call transiton flag
+ * true: call transition: dialing->alert, alert->active is controlled
+ * false: call transition is automatically handled by Mock Ril
+ * @return true if the request is successful, false if it failed to set the flag
+ */
+ public boolean setCallTransitionFlag(boolean flag) {
+ RilCtrlCmds.CtrlSetCallTransitionFlag req = new RilCtrlCmds.CtrlSetCallTransitionFlag();
+
+ req.setFlag(flag);
+
+ if (!sendCtrlCommand(RilCtrlCmds.CTRL_CMD_SET_CALL_TRANSITION_FLAG, 0, 0, req)) {
+ Log.v(TAG, "send CTRL_CMD_SET_CALL_TRANSITION_FLAG request failed");
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Set the dialing call to alert if the call transition flag is true
+ *
+ * @return true if the call transition is successful, false if it fails
+ */
+ public boolean setDialCallToAlert() {
+ if (!sendCtrlCommand(RilCtrlCmds.CTRL_CMD_SET_CALL_ALERT, 0, 0, null)) {
+ Log.v(TAG, "send CTRL_CMD_SET_CALL_ALERT request failed");
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Set the alert call to active if the call transition flag is true
+ *
+ * @return true if the call transition is successful, false if it fails
+ */
+ public boolean setAlertCallToActive() {
+ if (!sendCtrlCommand(RilCtrlCmds.CTRL_CMD_SET_CALL_ACTIVE, 0, 0, null)) {
+ Log.v(TAG, "send CTRL_CMD_SET_CALL_ACTIVE request failed");
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/telephony/tests/telephonytests/Android.mk b/telephony/tests/telephonytests/Android.mk
index 45e265a..98e4403 100644
--- a/telephony/tests/telephonytests/Android.mk
+++ b/telephony/tests/telephonytests/Android.mk
@@ -5,6 +5,8 @@ LOCAL_MODULE_TAGS := tests
LOCAL_SRC_FILES := $(call all-subdir-java-files)
+LOCAL_STATIC_JAVA_LIBRARIES := librilproto-java
+
LOCAL_JAVA_LIBRARIES := android.test.runner
LOCAL_PACKAGE_NAME := FrameworksTelephonyTests
diff --git a/telephony/tests/telephonytests/AndroidManifest.xml b/telephony/tests/telephonytests/AndroidManifest.xml
index 6a97423..ba1d957 100644
--- a/telephony/tests/telephonytests/AndroidManifest.xml
+++ b/telephony/tests/telephonytests/AndroidManifest.xml
@@ -32,6 +32,13 @@
android:targetPackage="com.android.frameworks.telephonytests"
android:label="Frameworks Telephony Tests">
</instrumentation>
+
+ <instrumentation android:name=".TelephonyMockRilTestRunner"
+ android:targetPackage="com.android.frameworks.telephonytests"
+ android:label="Test Runner for Mock Ril Tests"
+ />
+
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
+ <uses-permission android:name="android.permission.INTERNET" />
</manifest>
diff --git a/telephony/tests/telephonytests/src/android/telephony/PhoneNumberUtilsTest.java b/telephony/tests/telephonytests/src/android/telephony/PhoneNumberUtilsTest.java
index 2165023..67df510 100644
--- a/telephony/tests/telephonytests/src/android/telephony/PhoneNumberUtilsTest.java
+++ b/telephony/tests/telephonytests/src/android/telephony/PhoneNumberUtilsTest.java
@@ -16,13 +16,16 @@
package android.telephony;
+import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest;
import android.text.SpannableStringBuilder;
import android.telephony.PhoneNumberUtils;
+import android.telephony.TelephonyManager;
+import android.content.Context;
import junit.framework.TestCase;
-public class PhoneNumberUtilsTest extends TestCase {
+public class PhoneNumberUtilsTest extends AndroidTestCase {
@SmallTest
public void testExtractNetworkPortion() throws Exception {
@@ -482,4 +485,22 @@ public class PhoneNumberUtilsTest extends TestCase {
PhoneNumberUtils.cdmaCheckAndProcessPlusCodeByNumberFormat("+18475797000",
PhoneNumberUtils.FORMAT_UNKNOWN,PhoneNumberUtils.FORMAT_UNKNOWN));
}
+
+ /**
+ * Basic checks for the VoiceMail number.
+ */
+ @SmallTest
+ public void testWithNumberNotEqualToVoiceMail() throws Exception {
+ assertFalse(PhoneNumberUtils.isVoiceMailNumber("911"));
+ assertFalse(PhoneNumberUtils.isVoiceMailNumber("tel:911"));
+ assertFalse(PhoneNumberUtils.isVoiceMailNumber("+18001234567"));
+ assertFalse(PhoneNumberUtils.isVoiceMailNumber(""));
+ assertFalse(PhoneNumberUtils.isVoiceMailNumber(null));
+ // This test fails on a device without a sim card
+ /*TelephonyManager mTelephonyManager =
+ (TelephonyManager)getContext().getSystemService(Context.TELEPHONY_SERVICE);
+ String mVoiceMailNumber = mTelephonyManager.getDefault().getVoiceMailNumber();
+ assertTrue(PhoneNumberUtils.isVoiceMailNumber(mVoiceMailNumber));
+ */
+ }
}
diff --git a/telephony/tests/telephonytests/src/com/android/frameworks/telephonytests/TelephonyMockRilTestRunner.java b/telephony/tests/telephonytests/src/com/android/frameworks/telephonytests/TelephonyMockRilTestRunner.java
new file mode 100644
index 0000000..9192f57
--- /dev/null
+++ b/telephony/tests/telephonytests/src/com/android/frameworks/telephonytests/TelephonyMockRilTestRunner.java
@@ -0,0 +1,93 @@
+/*
+ * 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.frameworks.telephonytests;
+
+import android.os.Bundle;
+
+import android.test.InstrumentationTestRunner;
+import android.test.InstrumentationTestSuite;
+import android.util.Log;
+
+import java.io.IOException;
+
+import com.android.internal.telephony.RilChannel;
+import com.android.internal.telephony.mockril.MockRilTest;
+
+import junit.framework.TestSuite;
+
+public class TelephonyMockRilTestRunner extends InstrumentationTestRunner {
+
+ public RilChannel mMockRilChannel;
+
+ @Override
+ public TestSuite getAllTests() {
+ log("getAllTests E");
+ TestSuite suite = new InstrumentationTestSuite(this);
+ suite.addTestSuite(MockRilTest.class);
+ log("getAllTests X");
+ return suite;
+ }
+
+ @Override
+ public ClassLoader getLoader() {
+ log("getLoader EX");
+ return TelephonyMockRilTestRunner.class.getClassLoader();
+ }
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ log("onCreate E");
+ try {
+ mMockRilChannel = RilChannel.makeRilChannel();
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ log("onCreate X");
+
+ super.onCreate(icicle);
+ }
+
+ @Override
+ public void onDestroy() {
+ // I've not seen this called
+ log("onDestroy EX");
+ super.onDestroy();
+ }
+
+ @Override
+ public void onStart() {
+ // Called when the instrumentation thread is started.
+ // At the moment we don't need the thread so return
+ // which will shut down this unused thread.
+ log("onStart EX");
+ super.onStart();
+ }
+
+ @Override
+ public void finish(int resultCode, Bundle results) {
+ // Called when complete so I ask the mMockRilChannel to quit.
+ log("finish E");
+ mMockRilChannel.close();
+ log("finish X");
+ super.finish(resultCode, results);
+ }
+
+ private void log(String s) {
+ Log.e("TelephonyMockRilTestRunner", s);
+ }
+}
diff --git a/telephony/tests/telephonytests/src/com/android/telephonytest/unit/CallerInfoUnitTest.java b/telephony/tests/telephonytests/src/com/android/internal/telephony/CallerInfoTest.java
index 0f24f15..1e5dafb 100644
--- a/telephony/tests/telephonytests/src/com/android/telephonytest/unit/CallerInfoUnitTest.java
+++ b/telephony/tests/telephonytests/src/com/android/internal/telephony/CallerInfoTest.java
@@ -14,7 +14,8 @@
* limitations under the License.
*/
-package com.android.telephonytest.unit;
+package com.android.internal.telephony;
+
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest;
@@ -33,7 +34,7 @@ import android.util.StringBuilderPrinter;
*
*/
-public class CallerInfoUnitTest extends AndroidTestCase {
+public class CallerInfoTest extends AndroidTestCase {
private CallerInfo mInfo;
private Context mContext;
diff --git a/telephony/tests/telephonytests/src/com/android/internal/telephony/SmsMessageBodyTest.java b/telephony/tests/telephonytests/src/com/android/internal/telephony/SmsMessageBodyTest.java
new file mode 100644
index 0000000..b214887
--- /dev/null
+++ b/telephony/tests/telephonytests/src/com/android/internal/telephony/SmsMessageBodyTest.java
@@ -0,0 +1,194 @@
+/*
+ * 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.telephony.SmsMessage;
+import android.telephony.TelephonyManager;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import static android.telephony.SmsMessage.MAX_USER_DATA_SEPTETS;
+
+public class SmsMessageBodyTest extends AndroidTestCase {
+
+ private static final String sAsciiChars = "@$_ !\"#%&'()*+,-./0123456789" +
+ ":;<=>?ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\n\r";
+ private static final String sGsmBasicChars = "\u00a3\u00a5\u00e8\u00e9" +
+ "\u00f9\u00ec\u00f2\u00c7\u00d8\u00f8\u00c5\u00e5\u0394\u03a6" +
+ "\u0393\u039b\u03a9\u03a0\u03a8\u03a3\u0398\u00c6\u00e6" +
+ "\u00df\u00c9\u00a4\u00a1\u00c4\u00d6\u00d1\u00dc\u00a7\u00bf" +
+ "\u00e4\u00f6\u00f1\u00fc\u00e0";
+ private static final String sGsmExtendedAsciiChars = "{|}\\[~]^\f";
+ private static final String sGsmExtendedEuroSymbol = "\u20ac";
+ private static final String sUnicodeChars = "\u4e00\u4e01\u4e02\u4e03" +
+ "\u4e04\u4e05\u4e06\u4e07\u4e08\u4e09\u4e0a\u4e0b\u4e0c\u4e0d" +
+ "\u4e0e\u4e0f\u3041\u3042\u3043\u3044\u3045\u3046\u3047\u3048" +
+ "\u30a1\u30a2\u30a3\u30a4\u30a5\u30a6\u30a7\u30a8" +
+ "\uff10\uff11\uff12\uff13\uff14\uff15\uff16\uff17\uff18" +
+ "\uff70\uff71\uff72\uff73\uff74\uff75\uff76\uff77\uff78" +
+ "\u0400\u0401\u0402\u0403\u0404\u0405\u0406\u0407\u0408" +
+ "\u00a2\u00a9\u00ae\u2122";
+
+ private static final int sTestLengthCount = 12;
+
+ private static final int[] sSeptetTestLengths =
+ { 0, 1, 2, 80, 159, 160, 161, 240, 305, 306, 307, 320};
+
+ private static final int[] sUnicodeTestLengths =
+ { 0, 1, 2, 35, 69, 70, 71, 100, 133, 134, 135, 160};
+
+ private static final int[] sTestMsgCounts =
+ { 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3};
+
+ private static final int[] sSeptetUnitsRemaining =
+ {160, 159, 158, 80, 1, 0, 145, 66, 1, 0, 152, 139};
+
+ private static final int[] sUnicodeUnitsRemaining =
+ { 70, 69, 68, 35, 1, 0, 63, 34, 1, 0, 66, 41};
+
+
+ @SmallTest
+ public void testCalcLengthAscii() throws Exception {
+ StringBuilder sb = new StringBuilder(320);
+ int[] values = {0, 0, 0, SmsMessage.ENCODING_7BIT};
+ int startPos = 0;
+ int asciiCharsLen = sAsciiChars.length();
+
+ for (int i = 0; i < sTestLengthCount; i++) {
+ int len = sSeptetTestLengths[i];
+ assertTrue(sb.length() <= len);
+
+ while (sb.length() < len) {
+ int addCount = len - sb.length();
+ int endPos = (asciiCharsLen - startPos > addCount) ?
+ (startPos + addCount) : asciiCharsLen;
+ sb.append(sAsciiChars, startPos, endPos);
+ startPos = (endPos == asciiCharsLen) ? 0 : endPos;
+ }
+ assertEquals(len, sb.length());
+
+ String testStr = sb.toString();
+ values[0] = sTestMsgCounts[i];
+ values[1] = len;
+ values[2] = sSeptetUnitsRemaining[i];
+
+ callGsmLengthMethods(testStr, false, values);
+ callGsmLengthMethods(testStr, true, values);
+ callCdmaLengthMethods(testStr, false, values);
+ callCdmaLengthMethods(testStr, true, values);
+ }
+ }
+
+ @SmallTest
+ public void testCalcLength7bitGsm() throws Exception {
+ // TODO
+ }
+
+ @SmallTest
+ public void testCalcLength7bitGsmExtended() throws Exception {
+ // TODO
+ }
+
+ @SmallTest
+ public void testCalcLengthUnicode() throws Exception {
+ StringBuilder sb = new StringBuilder(160);
+ int[] values = {0, 0, 0, SmsMessage.ENCODING_16BIT};
+ int[] values7bit = {1, 0, 0, SmsMessage.ENCODING_7BIT};
+ int startPos = 0;
+ int unicodeCharsLen = sUnicodeChars.length();
+
+ // start with length 1: empty string uses ENCODING_7BIT
+ for (int i = 1; i < sTestLengthCount; i++) {
+ int len = sUnicodeTestLengths[i];
+ assertTrue(sb.length() <= len);
+
+ while (sb.length() < len) {
+ int addCount = len - sb.length();
+ int endPos = (unicodeCharsLen - startPos > addCount) ?
+ (startPos + addCount) : unicodeCharsLen;
+ sb.append(sUnicodeChars, startPos, endPos);
+ startPos = (endPos == unicodeCharsLen) ? 0 : endPos;
+ }
+ assertEquals(len, sb.length());
+
+ String testStr = sb.toString();
+ values[0] = sTestMsgCounts[i];
+ values[1] = len;
+ values[2] = sUnicodeUnitsRemaining[i];
+ values7bit[1] = len;
+ values7bit[2] = MAX_USER_DATA_SEPTETS - len;
+
+ callGsmLengthMethods(testStr, false, values);
+ callCdmaLengthMethods(testStr, false, values);
+ callGsmLengthMethods(testStr, true, values7bit);
+ callCdmaLengthMethods(testStr, true, values7bit);
+ }
+ }
+
+ private void callGsmLengthMethods(CharSequence msgBody, boolean use7bitOnly,
+ int[] expectedValues)
+ {
+ // deprecated GSM-specific method
+ int[] values = android.telephony.gsm.SmsMessage.calculateLength(msgBody, use7bitOnly);
+ assertEquals("msgCount", expectedValues[0], values[0]);
+ assertEquals("codeUnitCount", expectedValues[1], values[1]);
+ assertEquals("codeUnitsRemaining", expectedValues[2], values[2]);
+ assertEquals("codeUnitSize", expectedValues[3], values[3]);
+
+ int activePhone = TelephonyManager.getDefault().getPhoneType();
+ if (TelephonyManager.PHONE_TYPE_GSM == activePhone) {
+ values = android.telephony.SmsMessage.calculateLength(msgBody, use7bitOnly);
+ assertEquals("msgCount", expectedValues[0], values[0]);
+ assertEquals("codeUnitCount", expectedValues[1], values[1]);
+ assertEquals("codeUnitsRemaining", expectedValues[2], values[2]);
+ assertEquals("codeUnitSize", expectedValues[3], values[3]);
+ }
+
+ SmsMessageBase.TextEncodingDetails ted =
+ com.android.internal.telephony.gsm.SmsMessage.calculateLength(msgBody, use7bitOnly);
+ assertEquals("msgCount", expectedValues[0], ted.msgCount);
+ assertEquals("codeUnitCount", expectedValues[1], ted.codeUnitCount);
+ assertEquals("codeUnitsRemaining", expectedValues[2], ted.codeUnitsRemaining);
+ assertEquals("codeUnitSize", expectedValues[3], ted.codeUnitSize);
+ }
+
+ private void callCdmaLengthMethods(CharSequence msgBody, boolean use7bitOnly,
+ int[] expectedValues)
+ {
+ int activePhone = TelephonyManager.getDefault().getPhoneType();
+ if (TelephonyManager.PHONE_TYPE_CDMA == activePhone) {
+ int[] values = android.telephony.SmsMessage.calculateLength(msgBody, use7bitOnly);
+ assertEquals("msgCount", expectedValues[0], values[0]);
+ assertEquals("codeUnitCount", expectedValues[1], values[1]);
+ assertEquals("codeUnitsRemaining", expectedValues[2], values[2]);
+ assertEquals("codeUnitSize", expectedValues[3], values[3]);
+ }
+
+ SmsMessageBase.TextEncodingDetails ted =
+ com.android.internal.telephony.cdma.SmsMessage.calculateLength(msgBody, use7bitOnly);
+ assertEquals("msgCount", expectedValues[0], ted.msgCount);
+ assertEquals("codeUnitCount", expectedValues[1], ted.codeUnitCount);
+ assertEquals("codeUnitsRemaining", expectedValues[2], ted.codeUnitsRemaining);
+ assertEquals("codeUnitSize", expectedValues[3], ted.codeUnitSize);
+
+ ted = com.android.internal.telephony.cdma.sms.BearerData.calcTextEncodingDetails(msgBody, use7bitOnly);
+ assertEquals("msgCount", expectedValues[0], ted.msgCount);
+ assertEquals("codeUnitCount", expectedValues[1], ted.codeUnitCount);
+ assertEquals("codeUnitsRemaining", expectedValues[2], ted.codeUnitsRemaining);
+ assertEquals("codeUnitSize", expectedValues[3], ted.codeUnitSize);
+ }
+}
diff --git a/telephony/tests/telephonytests/src/com/android/internal/telephony/mockril/MockRilTest.java b/telephony/tests/telephonytests/src/com/android/internal/telephony/mockril/MockRilTest.java
new file mode 100644
index 0000000..3149ee1
--- /dev/null
+++ b/telephony/tests/telephonytests/src/com/android/internal/telephony/mockril/MockRilTest.java
@@ -0,0 +1,304 @@
+/*
+ * 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.mockril;
+
+import android.util.Log;
+import android.test.InstrumentationTestCase;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+import com.android.internal.communication.MsgHeader;
+import com.android.internal.communication.Msg;
+import com.android.internal.telephony.RilChannel;
+import com.android.internal.telephony.ril_proto.RilCtrlCmds;
+import com.android.internal.telephony.ril_proto.RilCmds;
+
+import com.android.frameworks.telephonytests.TelephonyMockRilTestRunner;
+import com.google.protobuf.micro.InvalidProtocolBufferMicroException;
+
+// Test suite for test ril
+public class MockRilTest extends InstrumentationTestCase {
+ private static final String TAG = "MockRilTest";
+
+ RilChannel mMockRilChannel;
+ TelephonyMockRilTestRunner mRunner;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mRunner = (TelephonyMockRilTestRunner)getInstrumentation();
+ mMockRilChannel = mRunner.mMockRilChannel;
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ static void log(String s) {
+ Log.v(TAG, s);
+ }
+
+ /**
+ * Test Case 1: Test protobuf serialization and deserialization
+ * @throws InvalidProtocolBufferMicroException
+ */
+ public void testProtobufSerDes() throws InvalidProtocolBufferMicroException {
+ log("testProtobufSerdes E");
+
+ RilCtrlCmds.CtrlRspRadioState rs = new RilCtrlCmds.CtrlRspRadioState();
+ assertTrue(String.format("expected rs.state == 0 was %d", rs.getState()),
+ rs.getState() == 0);
+ rs.setState(1);
+ assertTrue(String.format("expected rs.state == 1 was %d", rs.getState()),
+ rs.getState() == 1);
+
+ byte[] rs_ser = rs.toByteArray();
+ RilCtrlCmds.CtrlRspRadioState rsNew = RilCtrlCmds.CtrlRspRadioState.parseFrom(rs_ser);
+ assertTrue(String.format("expected rsNew.state == 1 was %d", rs.getState()),
+ rs.getState() == 1);
+
+ log("testProtobufSerdes X");
+ }
+
+ /**
+ * Test case 2: Test echo command works using writeMsg & readMsg
+ */
+ public void testEchoMsg() throws IOException {
+ log("testEchoMsg E");
+
+ MsgHeader mh = new MsgHeader();
+ mh.setCmd(0);
+ mh.setToken(1);
+ mh.setStatus(2);
+ ByteBuffer data = ByteBuffer.allocate(3);
+ data.put((byte)3);
+ data.put((byte)4);
+ data.put((byte)5);
+ Msg.send(mMockRilChannel, mh, data);
+
+ Msg respMsg = Msg.recv(mMockRilChannel);
+ assertTrue(String.format("expected mhd.header.cmd == 0 was %d",respMsg.getCmd()),
+ respMsg.getCmd() == 0);
+ assertTrue(String.format("expected mhd.header.token == 1 was %d",respMsg.getToken()),
+ respMsg.getToken() == 1);
+ assertTrue(String.format("expected mhd.header.status == 2 was %d", respMsg.getStatus()),
+ respMsg.getStatus() == 2);
+ assertTrue(String.format("expected mhd.data[0] == 3 was %d", respMsg.getData(0)),
+ respMsg.getData(0) == 3);
+ assertTrue(String.format("expected mhd.data[1] == 4 was %d", respMsg.getData(1)),
+ respMsg.getData(1) == 4);
+ assertTrue(String.format("expected mhd.data[2] == 5 was %d", respMsg.getData(2)),
+ respMsg.getData(2) == 5);
+
+ log("testEchoMsg X");
+ }
+
+ /**
+ * Test case 3: Test get as
+ */
+ public void testGetAs() {
+ log("testGetAs E");
+
+ // Use a message header as the protobuf data content
+ MsgHeader mh = new MsgHeader();
+ mh.setCmd(12345);
+ mh.setToken(9876);
+ mh.setStatus(7654);
+ mh.setLengthData(4321);
+ byte[] data = mh.toByteArray();
+ MsgHeader mhResult = Msg.getAs(MsgHeader.class, data);
+
+ assertTrue(String.format("expected cmd == 12345 was %d", mhResult.getCmd()),
+ mhResult.getCmd() == 12345);
+ assertTrue(String.format("expected token == 9876 was %d", mhResult.getToken()),
+ mhResult.getToken() == 9876);
+ assertTrue(String.format("expected status == 7654 was %d", mhResult.getStatus()),
+ mhResult.getStatus() == 7654);
+ assertTrue(String.format("expected lengthData == 4321 was %d", mhResult.getLengthData()),
+ mhResult.getLengthData() == 4321);
+
+ Msg msg = Msg.obtain();
+ msg.setData(ByteBuffer.wrap(data));
+
+ mhResult = msg.getDataAs(MsgHeader.class);
+
+ assertTrue(String.format("expected cmd == 12345 was %d", mhResult.getCmd()),
+ mhResult.getCmd() == 12345);
+ assertTrue(String.format("expected token == 9876 was %d", mhResult.getToken()),
+ mhResult.getToken() == 9876);
+ assertTrue(String.format("expected status == 7654 was %d", mhResult.getStatus()),
+ mhResult.getStatus() == 7654);
+ assertTrue(String.format("expected lengthData == 4321 was %d", mhResult.getLengthData()),
+ mhResult.getLengthData() == 4321);
+
+ log("testGetAs X");
+ }
+
+ /**
+ * Test case 3: test get radio state
+ */
+ public void testGetRadioState() throws IOException {
+ log("testGetRadioState E");
+
+ Msg.send(mMockRilChannel, 1, 9876, 0, null);
+
+ Msg resp = Msg.recv(mMockRilChannel);
+ //resp.printHeader("testGetRadioState");
+
+ assertTrue(String.format("expected cmd == 1 was %d", resp.getCmd()),
+ resp.getCmd() == 1);
+ assertTrue(String.format("expected token == 9876 was %d", resp.getToken()),
+ resp.getToken() == 9876);
+ assertTrue(String.format("expected status == 0 was %d", resp.getStatus()),
+ resp.getStatus() == 0);
+
+ RilCtrlCmds.CtrlRspRadioState rsp = resp.getDataAs(RilCtrlCmds.CtrlRspRadioState.class);
+
+ int state = rsp.getState();
+ log("testGetRadioState state=" + state);
+ assertTrue(String.format("expected RadioState >= 0 && RadioState <= 9 was %d", state),
+ ((state >= 0) && (state <= 9)));
+
+ log("testGetRadioState X");
+ }
+
+ /**
+ * Test case 5: test set radio state
+ */
+ public void testSetRadioState() throws IOException {
+ log("testSetRadioState E");
+
+ RilCtrlCmds.CtrlReqRadioState cmdrs = new RilCtrlCmds.CtrlReqRadioState();
+ assertEquals(0, cmdrs.getState());
+
+ cmdrs.setState(RilCmds.RADIOSTATE_SIM_NOT_READY);
+ assertEquals(2, cmdrs.getState());
+
+ Msg.send(mMockRilChannel, RilCtrlCmds.CTRL_CMD_SET_RADIO_STATE, 0, 0, cmdrs);
+
+ Msg resp = Msg.recv(mMockRilChannel);
+ log("get response status :" + resp.getStatus());
+ log("get response for command: " + resp.getCmd());
+ log("get command token: " + resp.getToken());
+
+ RilCtrlCmds.CtrlRspRadioState rsp = resp.getDataAs(RilCtrlCmds.CtrlRspRadioState.class);
+
+ int state = rsp.getState();
+ log("get response for testSetRadioState: " + state);
+ assertTrue(RilCmds.RADIOSTATE_SIM_NOT_READY == state);
+ }
+
+ /**
+ * Test case 6: test start incoming call and hangup it.
+ */
+ public void testStartIncomingCallAndHangup() throws IOException {
+ log("testStartIncomingCallAndHangup");
+ RilCtrlCmds.CtrlReqSetMTCall cmd = new RilCtrlCmds.CtrlReqSetMTCall();
+ String incomingCall = "6502889108";
+ // set the MT call
+ cmd.setPhoneNumber(incomingCall);
+ Msg.send(mMockRilChannel, RilCtrlCmds.CTRL_CMD_SET_MT_CALL, 0, 0, cmd);
+ // get response
+ Msg resp = Msg.recv(mMockRilChannel);
+ log("Get response status: " + resp.getStatus());
+ assertTrue("The ril is not in a proper state to set MT calls.",
+ resp.getStatus() == RilCtrlCmds.CTRL_STATUS_OK);
+
+ // allow the incoming call alerting for some time
+ try {
+ Thread.sleep(5000);
+ } catch (InterruptedException e) {}
+
+ // we are playing a trick to assume the current is 1
+ RilCtrlCmds.CtrlHangupConnRemote hangupCmd = new RilCtrlCmds.CtrlHangupConnRemote();
+ hangupCmd.setConnectionId(1);
+ hangupCmd.setCallFailCause(16); // normal hangup
+ Msg.send(mMockRilChannel, RilCtrlCmds.CTRL_CMD_HANGUP_CONN_REMOTE, 0, 0, hangupCmd);
+
+ // get response
+ resp = Msg.recv(mMockRilChannel);
+ log("Get response for hangup connection: " + resp.getStatus());
+ assertTrue("CTRL_CMD_HANGUP_CONN_REMOTE failed",
+ resp.getStatus() == RilCtrlCmds.CTRL_STATUS_OK);
+ }
+
+ /**
+ * Test case 7: test set call transition flag
+ */
+ public void testSetCallTransitionFlag() throws IOException {
+ log("testSetCallTransitionFlag");
+ // Set flag to true:
+ RilCtrlCmds.CtrlSetCallTransitionFlag cmd = new RilCtrlCmds.CtrlSetCallTransitionFlag();
+ cmd.setFlag(true);
+ Msg.send(mMockRilChannel, RilCtrlCmds.CTRL_CMD_SET_CALL_TRANSITION_FLAG, 0, 0, cmd);
+
+ Msg resp = Msg.recv(mMockRilChannel);
+ log("Get response status: " + resp.getStatus());
+ assertTrue("Set call transition flag failed",
+ resp.getStatus() == RilCtrlCmds.CTRL_STATUS_OK);
+
+ // add a dialing call
+ RilCtrlCmds.CtrlReqAddDialingCall cmdDialCall = new RilCtrlCmds.CtrlReqAddDialingCall();
+ String phoneNumber = "5102345678";
+ cmdDialCall.setPhoneNumber(phoneNumber);
+ Msg.send(mMockRilChannel, RilCtrlCmds.CTRL_CMD_ADD_DIALING_CALL, 0, 0, cmdDialCall);
+ resp = Msg.recv(mMockRilChannel);
+ log("Get response status for adding a dialing call: " + resp.getStatus());
+ assertTrue("add dialing call failed",
+ resp.getStatus() == RilCtrlCmds.CTRL_STATUS_OK);
+ try {
+ Thread.sleep(5000);
+ } catch (InterruptedException e) {}
+
+ // send command to force call state change
+ Msg.send(mMockRilChannel, RilCtrlCmds.CTRL_CMD_SET_CALL_ALERT, 0, 0, null);
+ resp = Msg.recv(mMockRilChannel);
+ log("Get response status: " + resp.getStatus());
+ assertTrue("Set call alert failed",
+ resp.getStatus() == RilCtrlCmds.CTRL_STATUS_OK);
+
+ try {
+ Thread.sleep(2000);
+ } catch (InterruptedException e) {}
+
+ // send command to force call state change
+ Msg.send(mMockRilChannel, RilCtrlCmds.CTRL_CMD_SET_CALL_ACTIVE, 0, 0, null);
+ resp = Msg.recv(mMockRilChannel);
+ log("Get response status: " + resp.getStatus());
+ assertTrue("Set call active failed",
+ resp.getStatus() == RilCtrlCmds.CTRL_STATUS_OK);
+
+ // hangup the active all remotely
+ RilCtrlCmds.CtrlHangupConnRemote hangupCmd = new RilCtrlCmds.CtrlHangupConnRemote();
+ hangupCmd.setConnectionId(1);
+ hangupCmd.setCallFailCause(16); // normal hangup
+ Msg.send(mMockRilChannel, RilCtrlCmds.CTRL_CMD_HANGUP_CONN_REMOTE, 0, 0, hangupCmd);
+ resp = Msg.recv(mMockRilChannel);
+ log("Get response for hangup connection: " + resp.getStatus());
+ assertTrue("CTRL_CMD_HANGUP_CONN_REMOTE failed",
+ resp.getStatus() == RilCtrlCmds.CTRL_STATUS_OK);
+
+ // set the flag to false
+ cmd.setFlag(false);
+ Msg.send(mMockRilChannel, RilCtrlCmds.CTRL_CMD_SET_CALL_TRANSITION_FLAG, 0, 0, cmd);
+ resp = Msg.recv(mMockRilChannel);
+ assertTrue("Set call transition flag failed",
+ resp.getStatus() == RilCtrlCmds.CTRL_STATUS_OK);
+ }
+}
diff --git a/telephony/tests/telephonytests/src/com/android/telephonytest/unit/PhoneNumberUtilsUnitTest.java b/telephony/tests/telephonytests/src/com/android/telephonytest/unit/PhoneNumberUtilsUnitTest.java
deleted file mode 100644
index 2d3c548..0000000
--- a/telephony/tests/telephonytests/src/com/android/telephonytest/unit/PhoneNumberUtilsUnitTest.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2009 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.telephonytest.unit;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.util.Log;
-
-import android.telephony.PhoneNumberUtils;
-import android.telephony.TelephonyManager;
-
-/*
- * Check the PhoneNumberUtils utility class works as expected.
- *
- */
-
-public class PhoneNumberUtilsUnitTest extends AndroidTestCase {
- private String mVoiceMailNumber;
- private static final String TAG = "PhoneNumberUtilsUnitTest";
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- // FIXME: Why are we getting a security exception here? The
- // permission is declared in the manifest....
- // mVoiceMailNumber = TelephonyManager.getDefault().getVoiceMailNumber();
- }
-
- @Override
- protected void tearDown() throws Exception {
- super.tearDown();
- }
-
- /**
- * Basic checks for the VoiceMail number.
- * Assumes READ_PHONE_STATE permission and we don't have it.
- */
- // TODO: Figure out why we don't have the permission declared in the manifest.
- @SmallTest
- public void testWithNumberNotEqualToVoiceMail() throws Exception {
- assertFalse(PhoneNumberUtils.isVoiceMailNumber("911"));
- assertFalse(PhoneNumberUtils.isVoiceMailNumber("tel:911"));
- assertFalse(PhoneNumberUtils.isVoiceMailNumber("+18001234567"));
- assertFalse(PhoneNumberUtils.isVoiceMailNumber(""));
- assertFalse(PhoneNumberUtils.isVoiceMailNumber(null));
- // FIXME:
- // assertTrue(PhoneNumberUtils.isVoiceMailNumber(mVoiceMailNumber));
- }
-
-}