Merge kwd to master
Change-Id: Idb607c0aa32f80fe4fe1539aedea7a221e9e7f04
diff --git a/Android.mk b/Android.mk
index b0ab1d0..449faa0 100644
--- a/Android.mk
+++ b/Android.mk
@@ -24,7 +24,7 @@
$(call all-Iaidl-files-under, src/java) \
$(call all-logtags-files-under, src/java)
-LOCAL_JAVA_LIBRARIES := voip-common
+LOCAL_JAVA_LIBRARIES := voip-common ims-common
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE := telephony-common
diff --git a/CleanSpec.mk b/CleanSpec.mk
index c5b95a1..4f5f260 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -44,6 +44,7 @@
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/telephony-common_intermediates)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/telephony-common_intermediates)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/telephony-common_intermediates)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/telephony-common_intermediates)
# ************************************************
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
diff --git a/src/java/android/provider/Telephony.java b/src/java/android/provider/Telephony.java
index 82ad215..ae15b3a 100644
--- a/src/java/android/provider/Telephony.java
+++ b/src/java/android/provider/Telephony.java
@@ -27,10 +27,12 @@
import android.database.sqlite.SqliteWrapper;
import android.net.Uri;
import android.telephony.SmsMessage;
+import android.telephony.SubscriptionManager;
import android.text.TextUtils;
import android.telephony.Rlog;
import android.util.Patterns;
+import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.SmsApplication;
@@ -238,7 +240,14 @@
public static final String LOCKED = "locked";
/**
- * Error code associated with sending or receiving this message.
+ * The sub_id to which the message belongs to
+ * <p>Type: INTEGER (long) </p>
+ * @hide
+ */
+ public static final String SUB_ID = "sub_id";
+
+ /**
+ * Error code associated with sending or receiving this message
* <P>Type: INTEGER</P>
*/
public static final String ERROR_CODE = "error_code";
@@ -314,7 +323,29 @@
public static Uri addMessageToUri(ContentResolver resolver,
Uri uri, String address, String body, String subject,
Long date, boolean read, boolean deliveryReport) {
- return addMessageToUri(resolver, uri, address, body, subject,
+ return addMessageToUri(SubscriptionManager.getPreferredSmsSubId(),
+ resolver, uri, address, body, subject, date, read, deliveryReport, -1L);
+ }
+
+ /**
+ * Add an SMS to the given URI.
+ *
+ * @param resolver the content resolver to use
+ * @param uri the URI to add the message to
+ * @param address the address of the sender
+ * @param body the body of the message
+ * @param subject the psuedo-subject of the message
+ * @param date the timestamp for the message
+ * @param read true if the message has been read, false if not
+ * @param deliveryReport true if a delivery report was requested, false if not
+ * @param subId the sub_id which the message belongs to
+ * @return the URI for the new message
+ * @hide
+ */
+ public static Uri addMessageToUri(long subId, ContentResolver resolver,
+ Uri uri, String address, String body, String subject,
+ Long date, boolean read, boolean deliveryReport) {
+ return addMessageToUri(subId, resolver, uri, address, body, subject,
date, read, deliveryReport, -1L);
}
@@ -336,8 +367,34 @@
public static Uri addMessageToUri(ContentResolver resolver,
Uri uri, String address, String body, String subject,
Long date, boolean read, boolean deliveryReport, long threadId) {
- ContentValues values = new ContentValues(7);
+ return addMessageToUri(SubscriptionManager.getPreferredSmsSubId(),
+ resolver, uri, address, body, subject,
+ date, read, deliveryReport, threadId);
+ }
+ /**
+ * Add an SMS to the given URI with thread_id specified.
+ *
+ * @param resolver the content resolver to use
+ * @param uri the URI to add the message to
+ * @param address the address of the sender
+ * @param body the body of the message
+ * @param subject the psuedo-subject of the message
+ * @param date the timestamp for the message
+ * @param read true if the message has been read, false if not
+ * @param deliveryReport true if a delivery report was requested, false if not
+ * @param threadId the thread_id of the message
+ * @param subId the sub_id which the message belongs to
+ * @return the URI for the new message
+ * @hide
+ */
+ public static Uri addMessageToUri(long subId, ContentResolver resolver,
+ Uri uri, String address, String body, String subject,
+ Long date, boolean read, boolean deliveryReport, long threadId) {
+ ContentValues values = new ContentValues(8);
+ Rlog.v(TAG,"Telephony addMessageToUri sub id: " + subId);
+
+ values.put(SUB_ID, subId);
values.put(ADDRESS, address);
if (date != null) {
values.put(DATE, date);
@@ -450,7 +507,26 @@
public static Uri addMessage(ContentResolver resolver,
String address, String body, String subject, Long date,
boolean read) {
- return addMessageToUri(resolver, CONTENT_URI, address, body,
+ return addMessageToUri(SubscriptionManager.getPreferredSmsSubId(),
+ resolver, CONTENT_URI, address, body, subject, date, read, false);
+ }
+
+ /**
+ * Add an SMS to the Draft box.
+ *
+ * @param resolver the content resolver to use
+ * @param address the address of the sender
+ * @param body the body of the message
+ * @param subject the psuedo-subject of the message
+ * @param date the timestamp for the message
+ * @param read true if the message has been read, false if not
+ * @param subId the sub_id which the message belongs to
+ * @return the URI for the new message
+ * @hide
+ */
+ public static Uri addMessage(long subId, ContentResolver resolver,
+ String address, String body, String subject, Long date, boolean read) {
+ return addMessageToUri(subId, resolver, CONTENT_URI, address, body,
subject, date, read, false);
}
}
@@ -490,7 +566,25 @@
*/
public static Uri addMessage(ContentResolver resolver,
String address, String body, String subject, Long date) {
- return addMessageToUri(resolver, CONTENT_URI, address, body,
+ return addMessageToUri(SubscriptionManager.getPreferredSmsSubId(),
+ resolver, CONTENT_URI, address, body, subject, date, true, false);
+ }
+
+ /**
+ * Add an SMS to the Draft box.
+ *
+ * @param resolver the content resolver to use
+ * @param address the address of the sender
+ * @param body the body of the message
+ * @param subject the psuedo-subject of the message
+ * @param date the timestamp for the message
+ * @param subId the sub_id which the message belongs to
+ * @return the URI for the new message
+ * @hide
+ */
+ public static Uri addMessage(long subId, ContentResolver resolver,
+ String address, String body, String subject, Long date) {
+ return addMessageToUri(subId, resolver, CONTENT_URI, address, body,
subject, date, true, false);
}
}
@@ -512,6 +606,33 @@
*/
public static final Uri CONTENT_URI = Uri.parse("content://sms/draft");
+ /**
+ * @hide
+ */
+ public static Uri addMessage(ContentResolver resolver,
+ String address, String body, String subject, Long date) {
+ return addMessageToUri(SubscriptionManager.getPreferredSmsSubId(),
+ resolver, CONTENT_URI, address, body, subject, date, true, false);
+ }
+
+ /**
+ * Add an SMS to the Draft box.
+ *
+ * @param resolver the content resolver to use
+ * @param address the address of the sender
+ * @param body the body of the message
+ * @param subject the psuedo-subject of the message
+ * @param date the timestamp for the message
+ * @param subId the sub_id which the message belongs to
+ * @return the URI for the new message
+ * @hide
+ */
+ public static Uri addMessage(long subId, ContentResolver resolver,
+ String address, String body, String subject, Long date) {
+ return addMessageToUri(subId, resolver, CONTENT_URI, address, body,
+ subject, date, true, false);
+ }
+
/**
* The default sort order for this table.
*/
@@ -555,7 +676,28 @@
public static Uri addMessage(ContentResolver resolver,
String address, String body, String subject, Long date,
boolean deliveryReport, long threadId) {
- return addMessageToUri(resolver, CONTENT_URI, address, body,
+ return addMessageToUri(SubscriptionManager.getPreferredSmsSubId(),
+ resolver, CONTENT_URI, address, body, subject, date,
+ true, deliveryReport, threadId);
+ }
+
+ /**
+ * Add an SMS to the Out box.
+ *
+ * @param resolver the content resolver to use
+ * @param address the address of the sender
+ * @param body the body of the message
+ * @param subject the psuedo-subject of the message
+ * @param date the timestamp for the message
+ * @param deliveryReport whether a delivery report was requested for the message
+ * @param subId the sub_id which the message belongs to
+ * @return the URI for the new message
+ * @hide
+ */
+ public static Uri addMessage(long subId, ContentResolver resolver,
+ String address, String body, String subject, Long date,
+ boolean deliveryReport, long threadId) {
+ return addMessageToUri(subId, resolver, CONTENT_URI, address, body,
subject, date, true, deliveryReport, threadId);
}
}
@@ -888,6 +1030,9 @@
public static SmsMessage[] getMessagesFromIntent(Intent intent) {
Object[] messages = (Object[]) intent.getSerializableExtra("pdus");
String format = intent.getStringExtra("format");
+ long subId = intent.getLongExtra(PhoneConstants.SUBSCRIPTION_KEY, 0);
+
+ Rlog.v(TAG, " getMessagesFromIntent sub_id : " + subId);
int pduCount = messages.length;
SmsMessage[] msgs = new SmsMessage[pduCount];
@@ -895,6 +1040,7 @@
for (int i = 0; i < pduCount; i++) {
byte[] pdu = (byte[]) messages[i];
msgs[i] = SmsMessage.createFromPdu(pdu, format);
+ msgs[i].setSubId(subId);
}
return msgs;
}
@@ -1452,6 +1598,14 @@
* <P>Type: INTEGER (boolean)</P>
*/
public static final String LOCKED = "locked";
+
+ /**
+ * The sub id to which message belongs to
+ * <p>Type: INTEGER</p>
+ * @hide
+ */
+ public static final String SUB_ID = "sub_id";
+
}
/**
@@ -2181,6 +2335,13 @@
* <P>Type: INTEGER (long)</P>
*/
public static final String LAST_TRY = "last_try";
+
+ /**
+ * The sub_id to which the pending message belongs to
+ * <p>Type: INTEGER (long) </p>
+ * @hide
+ */
+ public static final String SUB_ID = "pending_sub_id";
}
/**
@@ -2387,6 +2548,14 @@
* <P>Type: TEXT</P>
*/
public static final String MVNO_MATCH_DATA = "mvno_match_data";
+
+ /**
+ * The sub_id to which the APN belongs to
+ * <p>Type: INTEGER (long) </p>
+ * @hide
+ */
+ public static final String SUB_ID = "sub_id";
+
}
/**
diff --git a/src/java/android/telephony/CellBroadcastMessage.java b/src/java/android/telephony/CellBroadcastMessage.java
index af11bc4..1d30608 100644
--- a/src/java/android/telephony/CellBroadcastMessage.java
+++ b/src/java/android/telephony/CellBroadcastMessage.java
@@ -49,6 +49,31 @@
private final long mDeliveryTime;
private boolean mIsRead;
+ /**
+ * Indicates the subId
+ *
+ * @hide
+ */
+ private long mSubId = 0;
+
+ /**
+ * set Subscription information
+ *
+ * @hide
+ */
+ public void setSubId(long subId) {
+ mSubId = subId;
+ }
+
+ /**
+ * get Subscription information
+ *
+ * @hide
+ */
+ public long getSubId() {
+ return mSubId;
+ }
+
public CellBroadcastMessage(SmsCbMessage message) {
mSmsCbMessage = message;
mDeliveryTime = System.currentTimeMillis();
diff --git a/src/java/android/telephony/SmsManager.java b/src/java/android/telephony/SmsManager.java
index 3af756e..8fd48f4 100644
--- a/src/java/android/telephony/SmsManager.java
+++ b/src/java/android/telephony/SmsManager.java
@@ -47,6 +47,7 @@
public final class SmsManager {
/** Singleton object constructed during class initialization. */
private static final SmsManager sInstance = new SmsManager();
+ private static final int DEFAULT_SUB = 0;
/**
* Send a text based SMS.
@@ -87,6 +88,42 @@
public void sendTextMessage(
String destinationAddress, String scAddress, String text,
PendingIntent sentIntent, PendingIntent deliveryIntent) {
+ sendTextMessage(getPreferredSmsSubscription(), destinationAddress, scAddress, text,
+ sentIntent, deliveryIntent);
+ }
+
+ /**
+ * Send a text based SMS.
+ *
+ * @param destinationAddress the address to send the message to
+ * @param scAddress is the service center address or null to use
+ * 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 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>
+ * <code>RESULT_ERROR_RADIO_OFF</code><br>
+ * <code>RESULT_ERROR_NULL_PDU</code><br>
+ * For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include
+ * 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 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
+ * raw pdu of the status report is in the extended data ("pdu").
+ * @param subId on which the SMS has to be sent.
+ *
+ * @throws IllegalArgumentException if destinationAddress or text are empty
+ *
+ */
+ /** @hide */
+ public void sendTextMessage(
+ long subId, String destinationAddress, String scAddress, String text,
+ PendingIntent sentIntent, PendingIntent deliveryIntent) {
if (TextUtils.isEmpty(destinationAddress)) {
throw new IllegalArgumentException("Invalid destinationAddress");
}
@@ -165,6 +202,47 @@
public void sendMultipartTextMessage(
String destinationAddress, String scAddress, ArrayList<String> parts,
ArrayList<PendingIntent> sentIntents, ArrayList<PendingIntent> deliveryIntents) {
+ sendMultipartTextMessage(getPreferredSmsSubscription(), destinationAddress, scAddress, parts, sentIntents,
+ deliveryIntents);
+ }
+
+ /**
+ * Send a multi-part text based SMS. The callee should have already
+ * divided the message into correctly sized parts by calling
+ * <code>divideMessage</code>.
+ *
+ * @param destinationAddress the address to send the message to
+ * @param scAddress is the service center address or null to use
+ * the current default SMSC
+ * @param parts an <code>ArrayList</code> of strings that, in order,
+ * comprise the original message
+ * @param sentIntents if not null, an <code>ArrayList</code> of
+ * <code>PendingIntent</code>s (one for each message part) that is
+ * broadcast when the corresponding message part has been sent.
+ * 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>
+ * <code>RESULT_ERROR_RADIO_OFF</code><br>
+ * <code>RESULT_ERROR_NULL_PDU</code><br>
+ * For <code>RESULT_ERROR_GENERIC_FAILURE</code> each sentIntent may include
+ * 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 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
+ * broadcast when the corresponding message part has been delivered
+ * to the recipient. The raw pdu of the status report is in the
+ * extended data ("pdu").
+ * @param subId on which the SMS has to be sent.
+ *
+ * @throws IllegalArgumentException if destinationAddress or data are empty
+ */
+ /** @hide */
+ public void sendMultipartTextMessage(long subId, String destinationAddress, String scAddress,
+ ArrayList<String> parts, ArrayList<PendingIntent> sentIntents,
+ ArrayList<PendingIntent> deliveryIntents) {
if (TextUtils.isEmpty(destinationAddress)) {
throw new IllegalArgumentException("Invalid destinationAddress");
}
@@ -228,6 +306,43 @@
public void sendDataMessage(
String destinationAddress, String scAddress, short destinationPort,
byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) {
+ sendDataMessage(getPreferredSmsSubscription(),
+ destinationAddress, scAddress, destinationPort,
+ data, sentIntent, deliveryIntent);
+ }
+
+ /**
+ * Send a data based SMS to a specific application port.
+ *
+ * @param destinationAddress the address to send the message to
+ * @param scAddress is the service center address or null to use
+ * the current default SMSC
+ * @param destinationPort 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 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>
+ * <code>RESULT_ERROR_RADIO_OFF</code><br>
+ * <code>RESULT_ERROR_NULL_PDU</code><br>
+ * For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include
+ * 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 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
+ * raw pdu of the status report is in the extended data ("pdu").
+ * @param subId on which the SMS has to be sent.
+ *
+ * @throws IllegalArgumentException if destinationAddress or data are empty
+ */
+ /** @hide */
+ public void sendDataMessage(long subId,
+ String destinationAddress, String scAddress, short destinationPort,
+ byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) {
if (TextUtils.isEmpty(destinationAddress)) {
throw new IllegalArgumentException("Invalid destinationAddress");
}
@@ -289,7 +404,28 @@
* @throws IllegalArgumentException if pdu is NULL
* {@hide}
*/
- public boolean copyMessageToIcc(byte[] smsc, byte[] pdu, int status) {
+ public boolean copyMessageToIcc(byte[] smsc, byte[] pdu,int status) {
+ return copyMessageToIcc(getPreferredSmsSubscription(), smsc, pdu, status);
+ }
+
+ /**
+ * Copy a raw SMS PDU to the ICC on subId.
+ * ICC (Integrated Circuit Card) is the card of the device.
+ * For example, this can be the SIM or USIM for GSM.
+ *
+ * @param smsc the SMSC for this message, or NULL for the default SMSC
+ * @param pdu the raw PDU to store
+ * @param status message status (STATUS_ON_ICC_READ, STATUS_ON_ICC_UNREAD,
+ * STATUS_ON_ICC_SENT, STATUS_ON_ICC_UNSENT)
+ * @param subId from which SMS has to be copied.
+ * @return true for success
+ *
+ * @throws IllegalArgumentException if pdu is NULL
+ * {@hide}
+ */
+
+ /** @hide */
+ public boolean copyMessageToIcc(long subId, byte[] smsc, byte[] pdu, int status) {
boolean success = false;
if (null == pdu) {
@@ -320,6 +456,22 @@
*/
public boolean
deleteMessageFromIcc(int messageIndex) {
+ return deleteMessageFromIcc(getPreferredSmsSubscription(), messageIndex);
+ }
+
+ /**
+ * Delete the specified message from the ICC on subId.
+ * ICC (Integrated Circuit Card) is the card of the device.
+ * For example, this can be the SIM or USIM for GSM.
+ *
+ * @param messageIndex is the record index of the message on ICC
+ * @param subId from which SMS has to be deleted.
+ * @return true for success
+ *
+ */
+ /** @hide */
+ public boolean
+ deleteMessageFromIcc(long subId, int messageIndex) {
boolean success = false;
byte[] pdu = new byte[IccConstants.SMS_RECORD_LENGTH-1];
Arrays.fill(pdu, (byte)0xff);
@@ -352,6 +504,26 @@
* {@hide}
*/
public boolean updateMessageOnIcc(int messageIndex, int newStatus, byte[] pdu) {
+ return updateMessageOnIcc(getPreferredSmsSubscription(), messageIndex, newStatus, pdu);
+ }
+
+ /**
+ * Update the specified message on the ICC on subId.
+ * ICC (Integrated Circuit Card) is the card of the device.
+ * For example, this can be the SIM or USIM for GSM.
+ *
+ * @param messageIndex record index of message to update
+ * @param newStatus new message status (STATUS_ON_ICC_READ,
+ * STATUS_ON_ICC_UNREAD, STATUS_ON_ICC_SENT,
+ * STATUS_ON_ICC_UNSENT, STATUS_ON_ICC_FREE)
+ * @param pdu the raw PDU to store
+ * @param subId on which the SMS had to be updated.
+ * @return true for success
+ *
+ */
+ /** @hide */
+ public boolean updateMessageOnIcc(long subId, int messageIndex, int newStatus,
+ byte[] pdu) {
boolean success = false;
try {
@@ -377,6 +549,21 @@
* {@hide}
*/
public static ArrayList<SmsMessage> getAllMessagesFromIcc() {
+ return getAllMessagesFromIcc(getPreferredSmsSubscription());
+ }
+
+ /**
+ * Retrieves all messages currently stored on ICC on the default
+ * subId.
+ * ICC (Integrated Circuit Card) is the card of the device.
+ * For example, this can be the SIM or USIM for GSM.
+ *
+ * @param subId from which the messages had to be retrieved.
+ * @return <code>ArrayList</code> of <code>SmsMessage</code> objects
+ *
+ */
+ /** @hide */
+ public static ArrayList<SmsMessage> getAllMessagesFromIcc(long subId) {
List<SmsRawData> records = null;
try {
@@ -408,6 +595,28 @@
* {@hide}
*/
public boolean enableCellBroadcast(int messageIdentifier) {
+ return enableCellBroadcast(getPreferredSmsSubscription(), messageIdentifier);
+ }
+
+ /**
+ * Enable reception of cell broadcast (SMS-CB) messages with the given
+ * message identifier on a particular subId.
+ * Note that if two different clients enable the same
+ * message identifier, they must both disable it for the device to stop
+ * receiving those messages. All received messages will be broadcast in an
+ * intent with the action "android.provider.telephony.SMS_CB_RECEIVED".
+ * Note: This call is blocking, callers may want to avoid calling it from
+ * the main thread of an application.
+ *
+ * @param messageIdentifier Message identifier as specified in TS 23.041 (3GPP)
+ * or C.R1001-G (3GPP2)
+ * @param subId for which the broadcast has to be enabled
+ * @return true if successful, false otherwise
+ * @see #disableCellBroadcast(int)
+ *
+ */
+ /** @hide */
+ public boolean enableCellBroadcast(long subId, int messageIdentifier) {
boolean success = false;
try {
@@ -439,6 +648,27 @@
* {@hide}
*/
public boolean disableCellBroadcast(int messageIdentifier) {
+ return disableCellBroadcast(getPreferredSmsSubscription(), messageIdentifier);
+ }
+
+ /**
+ * Disable reception of cell broadcast (SMS-CB) messages with the given
+ * message identifier on a particular subId.
+ * Note that if two different clients enable the same
+ * message identifier, they must both disable it for the device to stop
+ * receiving those messages.
+ * Note: This call is blocking, callers may want to avoid calling it from
+ * the main thread of an application.
+ *
+ * @param messageIdentifier Message identifier as specified in TS 23.041
+ * @param subId for which the broadcast has to be disabled
+ * @return true if successful, false otherwise
+ *
+ * @see #enableCellBroadcast(int)
+ *
+ */
+ /** @hide */
+ public boolean disableCellBroadcast(long subId, int messageIdentifier) {
boolean success = false;
try {
@@ -473,6 +703,30 @@
* {@hide}
*/
public boolean enableCellBroadcastRange(int startMessageId, int endMessageId) {
+ return enableCellBroadcastRange(getPreferredSmsSubscription(), startMessageId,
+ endMessageId);
+ }
+
+ /**
+ * Enable reception of cell broadcast (SMS-CB) messages with the given
+ * message identifier range on a particular subId.
+ * Note that if two different clients enable the same
+ * message identifier, they must both disable it for the device to stop
+ * receiving those messages. All received messages will be broadcast in an
+ * intent with the action "android.provider.Telephony.SMS_CB_RECEIVED".
+ * Note: This call is blocking, callers may want to avoid calling it from
+ * the main thread of an application.
+ *
+ * @param startMessageId first message identifier as specified in TS 23.041
+ * @param endMessageId last message identifier as specified in TS 23.041
+ * @return true if successful, false otherwise
+ * @see #disableCellBroadcastRange(int, int)
+ * @throws IllegalArgumentException if endMessageId < startMessageId
+ *
+ */
+ /** @hide */
+ public boolean enableCellBroadcastRange(long subId, int startMessageId,
+ int endMessageId) {
boolean success = false;
if (endMessageId < startMessageId) {
@@ -510,6 +764,30 @@
* {@hide}
*/
public boolean disableCellBroadcastRange(int startMessageId, int endMessageId) {
+ return disableCellBroadcastRange(getPreferredSmsSubscription(), startMessageId,
+ endMessageId);
+ }
+
+ /**
+ * Disable reception of cdma broadcast messages with the given
+ * message identifier range on a particular subId.
+ * Note that if two different clients enable the same
+ * message identifier range, they must both disable it for the device to stop
+ * receiving those messages.
+ * Note: This call is blocking, callers may want to avoid calling it from
+ * the main thread of an application.
+ *
+ * @param startMessageId first message identifier as specified in TS 23.041
+ * @param endMessageId last message identifier as specified in TS 23.041
+ * @return true if successful, false otherwise
+ *
+ * @see #enableCellBroadcastRange(int, int)
+ * @throws IllegalArgumentException if endMessageId < startMessageId
+ *
+ */
+ /** @hide */
+ public boolean disableCellBroadcastRange(long subId, int startMessageId,
+ int endMessageId) {
boolean success = false;
if (endMessageId < startMessageId) {
@@ -564,6 +842,11 @@
* @hide
*/
boolean isImsSmsSupported() {
+ return isImsSmsSupported(getPreferredSmsSubscription());
+ }
+
+ /** @hide */
+ boolean isImsSmsSupported(long subId) {
boolean boSupported = false;
try {
ISms iccISms = getISmsService();
@@ -589,6 +872,11 @@
* @hide
*/
String getImsSmsFormat() {
+ return getImsSmsFormat(getPreferredSmsSubscription());
+ }
+
+ /** @hide */
+ String getImsSmsFormat(long subId) {
String format = com.android.internal.telephony.SmsConstants.FORMAT_UNKNOWN;
try {
ISms iccISms = getISmsService();
@@ -601,6 +889,42 @@
return format;
}
+ /**
+ * Get the preferred sms subId
+ *
+ * @return the preferred subId
+ * @hide
+ */
+ public static long getPreferredSmsSubscription() {
+ ISms iccISms = null;
+ try {
+ iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
+ return (long) iccISms.getPreferredSmsSubscription();
+ } catch (RemoteException ex) {
+ return DEFAULT_SUB;
+ } catch (NullPointerException ex) {
+ return DEFAULT_SUB;
+ }
+ }
+
+ /**
+ * Get SMS prompt property, enabled or not
+ *
+ * @return true if enabled, false otherwise
+ * @hide
+ */
+ public boolean isSMSPromptEnabled() {
+ ISms iccISms = null;
+ try {
+ iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
+ return iccISms.isSMSPromptEnabled();
+ } catch (RemoteException ex) {
+ return false;
+ } catch (NullPointerException ex) {
+ return false;
+ }
+ }
+
// see SmsMessage.getStatusOnIcc
/** Free space (TS 51.011 10.5.3 / 3GPP2 C.S0023 3.4.27). */
diff --git a/src/java/android/telephony/SmsMessage.java b/src/java/android/telephony/SmsMessage.java
index 12d6949..64368aa 100644
--- a/src/java/android/telephony/SmsMessage.java
+++ b/src/java/android/telephony/SmsMessage.java
@@ -96,6 +96,28 @@
*/
public SmsMessageBase mWrappedSmsMessage;
+ /** Indicates the subId
+ *
+ * @hide
+ */
+ private long mSubId = 0;
+
+ /** set Subscription information
+ *
+ * @hide
+ */
+ public void setSubId(long subId) {
+ mSubId = subId;
+ }
+
+ /** get Subscription information
+ *
+ * @hide
+ */
+ public long getSubId() {
+ return mSubId;
+ }
+
public static class SubmitPdu {
public byte[] encodedScAddress; // Null if not applicable.
diff --git a/src/java/com/android/internal/telephony/BaseCommands.java b/src/java/com/android/internal/telephony/BaseCommands.java
index 19ff4ed..47dcc99 100644
--- a/src/java/com/android/internal/telephony/BaseCommands.java
+++ b/src/java/com/android/internal/telephony/BaseCommands.java
@@ -18,6 +18,7 @@
import android.content.Context;
+import android.os.Message;
import android.os.RegistrantList;
import android.os.Registrant;
import android.os.Handler;
@@ -64,6 +65,9 @@
protected RegistrantList mRilConnectedRegistrants = new RegistrantList();
protected RegistrantList mIccRefreshRegistrants = new RegistrantList();
protected RegistrantList mRilCellInfoListRegistrants = new RegistrantList();
+ protected RegistrantList mSubscriptionStatusRegistrants = new RegistrantList();
+ protected RegistrantList mSrvccStateRegistrants = new RegistrantList();
+ protected RegistrantList mHardwareConfigChangeRegistrants = new RegistrantList();
protected Registrant mGsmSmsRegistrant;
protected Registrant mCdmaSmsRegistrant;
@@ -636,6 +640,17 @@
mExitEmergencyCallbackModeRegistrants.remove(h);
}
+ @Override
+ public void registerForHardwareConfigChanged(Handler h, int what, Object obj) {
+ Registrant r = new Registrant (h, what, obj);
+ mHardwareConfigChangeRegistrants.add(r);
+ }
+
+ @Override
+ public void unregisterForHardwareConfigChanged(Handler h) {
+ mHardwareConfigChangeRegistrants.remove(h);
+ }
+
/**
* {@inheritDoc}
*/
@@ -653,6 +668,15 @@
mRilConnectedRegistrants.remove(h);
}
+ public void registerForSubscriptionStatusChanged(Handler h, int what, Object obj) {
+ Registrant r = new Registrant (h, what, obj);
+ mSubscriptionStatusRegistrants.add(r);
+ }
+
+ public void unregisterForSubscriptionStatusChanged(Handler h) {
+ mSubscriptionStatusRegistrants.remove(h);
+ }
+
//***** Protected Methods
/**
* Store new RadioState and send notification based on the changes
@@ -724,10 +748,29 @@
}
@Override
+ public void registerForSrvccStateChanged(Handler h, int what, Object obj) {
+ Registrant r = new Registrant (h, what, obj);
+
+ mSrvccStateRegistrants.add(r);
+ }
+
+ @Override
+ public void unregisterForSrvccStateChanged(Handler h) {
+ mSrvccStateRegistrants.remove(h);
+ }
+
+ @Override
public void testingEmergencyCall() {}
@Override
public int getRilVersion() {
return mRilVersion;
}
+
+ public void setUiccSubscription(int slotId, int appIndex, int subId, int subStatus,
+ Message response) {
+ }
+
+ public void setDataAllowed(boolean allowed, Message response) {
+ }
}
diff --git a/src/java/com/android/internal/telephony/Call.java b/src/java/com/android/internal/telephony/Call.java
index b83d33e..46df399 100644
--- a/src/java/com/android/internal/telephony/Call.java
+++ b/src/java/com/android/internal/telephony/Call.java
@@ -45,6 +45,9 @@
}
}
+ public enum SrvccState {
+ NONE, STARTED, COMPLETED, FAILED, CANCELED;
+ }
/* Instance Variables */
diff --git a/src/java/com/android/internal/telephony/CallManager.java b/src/java/com/android/internal/telephony/CallManager.java
index aa4cca8..637b6eb 100644
--- a/src/java/com/android/internal/telephony/CallManager.java
+++ b/src/java/com/android/internal/telephony/CallManager.java
@@ -26,6 +26,8 @@
import android.os.RegistrantList;
import android.os.Registrant;
import android.telephony.PhoneNumberUtils;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
import android.telephony.PhoneStateListener;
import android.telephony.ServiceState;
import android.telephony.Rlog;
@@ -79,6 +81,7 @@
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;
+ private static final int EVENT_ONHOLD_TONE = 120;
// Singleton instance
private static final CallManager INSTANCE = new CallManager();
@@ -125,6 +128,9 @@
protected final RegistrantList mRingbackToneRegistrants
= new RegistrantList();
+ protected final RegistrantList mOnHoldToneRegistrants
+ = new RegistrantList();
+
protected final RegistrantList mInCallVoicePrivacyOnRegistrants
= new RegistrantList();
@@ -232,6 +238,21 @@
}
/**
+ * get Phone object corresponds to subId
+ * @return Phone
+ */
+ private Phone getPhone(long subId) {
+ Phone p = null;
+ for (Phone phone : mPhones) {
+ if (phone.getSubId() == subId && !(phone instanceof VoicePhone)) {
+ p = phone;
+ break;
+ }
+ }
+ return p;
+ }
+
+ /**
* 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
@@ -251,6 +272,27 @@
}
/**
+ * Get current coarse-grained voice call state on a subId.
+ * If the Call Manager has an active call and call waiting occurs,
+ * then the phone state is RINGING not OFFHOOK
+ *
+ */
+ public PhoneConstants.State getState(long subId) {
+ PhoneConstants.State s = PhoneConstants.State.IDLE;
+
+ for (Phone phone : mPhones) {
+ if (phone.getSubId() == subId) {
+ if (phone.getState() == PhoneConstants.State.RINGING) {
+ s = PhoneConstants.State.RINGING;
+ } else if (phone.getState() == PhoneConstants.State.OFFHOOK) {
+ if (s == PhoneConstants.State.IDLE) s = PhoneConstants.State.OFFHOOK;
+ }
+ }
+ }
+ return s;
+ }
+
+ /**
* @return the service state of CallManager, which represents the
* highest priority state of all the service states of phones
*
@@ -286,6 +328,65 @@
}
/**
+ * @return the Phone service state corresponds to subId
+ */
+ public int getServiceState(long subId) {
+ int resultState = ServiceState.STATE_OUT_OF_SERVICE;
+
+ for (Phone phone : mPhones) {
+ if (phone.getSubId() == subId) {
+ 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;
+ }
+
+ /**
+ * @return the phone associated with any call
+ */
+ public Phone getPhoneInCall() {
+ Phone phone = null;
+ if (!getFirstActiveRingingCall().isIdle()) {
+ phone = getFirstActiveRingingCall().getPhone();
+ } else if (!getActiveFgCall().isIdle()) {
+ phone = getActiveFgCall().getPhone();
+ } else {
+ // If BG call is idle, we return default phone
+ phone = getFirstActiveBgCall().getPhone();
+ }
+ return phone;
+ }
+
+ public Phone getPhoneInCall(long subId) {
+ Phone phone = null;
+ if (!getFirstActiveRingingCall(subId).isIdle()) {
+ phone = getFirstActiveRingingCall(subId).getPhone();
+ } else if (!getActiveFgCall(subId).isIdle()) {
+ phone = getActiveFgCall(subId).getPhone();
+ } else {
+ // If BG call is idle, we return default phone
+ phone = getFirstActiveBgCall(subId).getPhone();
+ }
+ return phone;
+ }
+
+ /**
* Register phone to CallManager
* @param phone to be registered
* @return true if register successfully
@@ -327,6 +428,11 @@
phone.getPhoneName() + " " + phone + ")");
}
+ Phone vPhone = basePhone.getVoicePhone();
+ if (vPhone != null) {
+ unregisterPhone(vPhone);
+ }
+
mPhones.remove(basePhone);
mRingingCalls.remove(basePhone.getRingingCall());
mBackgroundCalls.remove(basePhone.getBackgroundCall());
@@ -357,6 +463,14 @@
}
/**
+ * @return the phone associated with the foreground call
+ * of a particular subId
+ */
+ public Phone getFgPhone(long subId) {
+ return getActiveFgCall(subId).getPhone();
+ }
+
+ /**
* @return the phone associated with the background call
*/
public Phone getBgPhone() {
@@ -364,12 +478,28 @@
}
/**
+ * @return the phone associated with the background call
+ * of a particular subId
+ */
+ public Phone getBgPhone(long subId) {
+ return getFirstActiveBgCall(subId).getPhone();
+ }
+
+ /**
* @return the phone associated with the ringing call
*/
public Phone getRingingPhone() {
return getFirstActiveRingingCall().getPhone();
}
+ /**
+ * @return the phone associated with the ringing call
+ * of a particular subId
+ */
+ public Phone getRingingPhone(long subId) {
+ return getFirstActiveRingingCall(subId).getPhone();
+ }
+
public void setAudioMode() {
Context context = getContext();
if (context == null) return;
@@ -407,14 +537,18 @@
int newAudioMode = AudioManager.MODE_IN_CALL;
if (offhookPhone instanceof SipPhone) {
+ Rlog.d(LOG_TAG, "setAudioMode Set audio mode for SIP call!");
// enable IN_COMMUNICATION audio mode instead for sipPhone
newAudioMode = AudioManager.MODE_IN_COMMUNICATION;
}
- if (audioManager.getMode() != newAudioMode || mSpeedUpAudioForMtCall) {
+ int currMode = audioManager.getMode();
+ if (currMode != newAudioMode || mSpeedUpAudioForMtCall) {
// request audio focus before setting the new mode
if (VDBG) Rlog.d(LOG_TAG, "requestAudioFocus on STREAM_VOICE_CALL");
audioManager.requestAudioFocusForCall(AudioManager.STREAM_VOICE_CALL,
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
+ Rlog.d(LOG_TAG, "setAudioMode Setting audio mode from "
+ + currMode + " to " + newAudioMode);
audioManager.setMode(newAudioMode);
}
mSpeedUpAudioForMtCall = false;
@@ -429,6 +563,7 @@
mSpeedUpAudioForMtCall = false;
break;
}
+ Rlog.d(LOG_TAG, "setAudioMode state = " + getState());
}
private Context getContext() {
@@ -454,9 +589,10 @@
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
+ // for events supported only by GSM, CDMA and IMS phone
if (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_GSM ||
- phone.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA) {
+ phone.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA ||
+ phone.getPhoneType() == PhoneConstants.PHONE_TYPE_IMS) {
phone.setOnPostDialCharacter(mHandler, EVENT_POST_DIAL_CHARACTER, null);
}
@@ -467,6 +603,11 @@
phone.registerForCallWaiting(mHandler, EVENT_CALL_WAITING, null);
phone.registerForEcmTimerReset(mHandler, EVENT_ECM_TIMER_RESET, null);
}
+
+ // for events supported only by IMS phone
+ if (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_IMS) {
+ phone.registerForOnHoldTone(mHandler, EVENT_ONHOLD_TONE, null);
+ }
}
private void unregisterForPhoneStates(Phone phone) {
@@ -487,9 +628,10 @@
phone.unregisterForSuppServiceFailed(mHandler);
phone.unregisterForServiceStateChanged(mHandler);
- // for events supported only by GSM and CDMA phone
+ // for events supported only by GSM, CDMA and IMS phone
if (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_GSM ||
- phone.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA) {
+ phone.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA ||
+ phone.getPhoneType() == PhoneConstants.PHONE_TYPE_IMS) {
phone.setOnPostDialCharacter(null, EVENT_POST_DIAL_CHARACTER, null);
}
@@ -500,6 +642,11 @@
phone.unregisterForCallWaiting(mHandler);
phone.unregisterForEcmTimerReset(mHandler);
}
+
+ // for events supported only by IMS phone
+ if (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_IMS) {
+ phone.unregisterForOnHoldTone(mHandler);
+ }
}
/**
@@ -550,7 +697,8 @@
AudioManager audioManager = (AudioManager)
context.getSystemService(Context.AUDIO_SERVICE);
int currMode = audioManager.getMode();
- if ((currMode != AudioManager.MODE_IN_CALL) && !(ringingPhone instanceof SipPhone)) {
+ if ((currMode != AudioManager.MODE_IN_CALL)
+ && !(ringingPhone instanceof SipPhone)) {
Rlog.d(LOG_TAG, "setAudioMode Setting audio mode from " +
currMode + " to " + AudioManager.MODE_IN_CALL);
audioManager.setMode(AudioManager.MODE_IN_CALL);
@@ -698,6 +846,28 @@
}
/**
+ * Whether or not the phone can conference in the current phone
+ * state--that is, one call holding and one call active.
+ * This method consider the phone object which is specific
+ * to the provided subId.
+ * @return true if the phone can conference; false otherwise.
+ */
+ public boolean canConference(Call heldCall, long subId) {
+ Phone activePhone = null;
+ Phone heldPhone = null;
+
+ if (hasActiveFgCall(subId)) {
+ activePhone = getActiveFgCall(subId).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,
@@ -707,20 +877,24 @@
* In these cases, this operation may not be performed.
*/
public void conference(Call heldCall) throws CallStateException {
+ long subId = heldCall.getPhone().getSubId();
if (VDBG) {
Rlog.d(LOG_TAG, "conference(" +heldCall + ")");
Rlog.d(LOG_TAG, toString());
}
-
- Phone fgPhone = getFgPhone();
- if (fgPhone instanceof SipPhone) {
- ((SipPhone) fgPhone).conference(heldCall);
- } else if (canConference(heldCall)) {
- fgPhone.conference();
+ Phone fgPhone = getFgPhone(subId);
+ if (fgPhone != null) {
+ 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"));
+ }
} else {
- throw(new CallStateException("Can't conference foreground and selected background call"));
+ Rlog.d(LOG_TAG, "conference: fgPhone=null");
}
if (VDBG) {
@@ -742,10 +916,12 @@
*/
public Connection dial(Phone phone, String dialString) throws CallStateException {
Phone basePhone = getPhoneBase(phone);
+ long subId = phone.getSubId();
Connection result;
if (VDBG) {
- Rlog.d(LOG_TAG, " dial(" + basePhone + ", "+ dialString + ")");
+ Rlog.d(LOG_TAG, " dial(" + basePhone + ", "+ dialString + ")" +
+ " subId = " + subId);
Rlog.d(LOG_TAG, toString());
}
@@ -763,18 +939,22 @@
}
}
- if ( hasActiveFgCall() ) {
- Phone activePhone = getActiveFgCall().getPhone();
+ if ( hasActiveFgCall(subId) ) {
+ Phone activePhone = getActiveFgCall(subId).getPhone();
boolean hasBgCall = !(activePhone.getBackgroundCall().isIdle());
if (DBG) {
Rlog.d(LOG_TAG, "hasBgCall: "+ hasBgCall + " sameChannel:" + (activePhone == basePhone));
}
- if (activePhone != basePhone) {
+ // Manipulation between IMS phone and its owner
+ // will be treated in GSM/CDMA phone.
+ Phone vPhone = basePhone.getVoicePhone();
+ if (activePhone != basePhone
+ && (vPhone == null || vPhone != activePhone)) {
if (hasBgCall) {
Rlog.d(LOG_TAG, "Hangup");
- getActiveFgCall().hangup();
+ getActiveFgCall(subId).hangup();
} else {
Rlog.d(LOG_TAG, "Switch");
activePhone.switchHoldingAndActive();
@@ -816,6 +996,18 @@
}
/**
+ * clear disconnect connection for a phone specific
+ * to the provided subId
+ */
+ public void clearDisconnected(long subId) {
+ for(Phone phone : mPhones) {
+ if (phone.getSubId() == subId) {
+ phone.clearDisconnected();
+ }
+ }
+ }
+
+ /**
* Phone can make a call only if ALL of the following are true:
* - Phone is not powered off
* - There's no incoming or waiting call
@@ -826,14 +1018,19 @@
*/
private boolean canDial(Phone phone) {
int serviceState = phone.getServiceState().getState();
+ long subId = phone.getSubId();
boolean hasRingingCall = hasActiveRingingCall();
- Call.State fgCallState = getActiveFgCallState();
+ Call.State fgCallState = getActiveFgCallState(subId);
boolean result = (serviceState != ServiceState.STATE_POWER_OFF
&& !hasRingingCall
&& ((fgCallState == Call.State.ACTIVE)
|| (fgCallState == Call.State.IDLE)
- || (fgCallState == Call.State.DISCONNECTED)));
+ || (fgCallState == Call.State.DISCONNECTED)
+ /*As per 3GPP TS 51.010-1 section 31.13.1.4
+ call should be alowed when the foreground
+ call is in ALERTING state*/
+ || (fgCallState == Call.State.ALERTING)));
if (result == false) {
Rlog.d(LOG_TAG, "canDial serviceState=" + serviceState
@@ -864,6 +1061,26 @@
}
/**
+ * Whether or not the phone specific to subId 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, long subId) {
+ Phone activePhone = null;
+ Phone heldPhone = null;
+
+ if (hasActiveFgCall(subId)) {
+ activePhone = getActiveFgCall(subId).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
*
@@ -1206,6 +1423,27 @@
}
/**
+ * Notifies when out-band on-hold tone is needed.<p>
+ *
+ * Messages received from this:
+ * Message.obj will be an AsyncResult
+ * AsyncResult.userObj = obj
+ * AsyncResult.result = boolean, true to start play on-hold tone
+ * and false to stop. <p>
+ */
+ public void registerForOnHoldTone(Handler h, int what, Object obj){
+ mOnHoldToneRegistrants.addUnique(h, what, obj);
+ }
+
+ /**
+ * Unregisters for on-hold tone notification.
+ */
+
+ public void unregisterForOnHoldTone(Handler h){
+ mOnHoldToneRegistrants.remove(h);
+ }
+
+ /**
* Registers the handler to reset the uplink mute state to get
* uplink audio.
*/
@@ -1447,7 +1685,7 @@
}
/**
- * Registration point for subscription info ready
+ * Registration point for subcription info ready
* @param h handler to notify
* @param what what code of message when delivered
* @param obj placed in Message.obj
@@ -1542,6 +1780,14 @@
}
/**
+ * Return true if there is at least one active foreground call
+ * on a particular subId or an active sip call
+ */
+ public boolean hasActiveFgCall(long subId) {
+ return (getFirstActiveCall(mForegroundCalls, subId) != null);
+ }
+
+ /**
* Return true if there is at least one active background call
*/
public boolean hasActiveBgCall() {
@@ -1551,6 +1797,16 @@
}
/**
+ * Return true if there is at least one active background call
+ * on a particular subId or an active sip call
+ */
+ public boolean hasActiveBgCall(long subId) {
+ // TODO since hasActiveBgCall may get called often
+ // better to cache it to improve performance
+ return (getFirstActiveCall(mBackgroundCalls, subId) != null);
+ }
+
+ /**
* Return true if there is at least one active ringing call
*
*/
@@ -1559,6 +1815,13 @@
}
/**
+ * Return true if there is at least one active ringing call
+ */
+ public boolean hasActiveRingingCall(long subId) {
+ return (getFirstActiveCall(mRingingCalls, subId) != null);
+ }
+
+ /**
* return the active foreground call from foreground calls
*
* Active call means the call is NOT in Call.State.IDLE
@@ -1579,6 +1842,17 @@
return call;
}
+ public Call getActiveFgCall(long subId) {
+ Call call = getFirstNonIdleCall(mForegroundCalls, subId);
+ if (call == null) {
+ Phone phone = getPhone(subId);
+ call = (phone == null)
+ ? null
+ : phone.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) {
@@ -1593,6 +1867,23 @@
return result;
}
+ // 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, long subId) {
+ Call result = null;
+ for (Call call : calls) {
+ if ((call.getPhone().getSubId() == subId) ||
+ (call.getPhone() instanceof SipPhone)) {
+ 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
*
@@ -1617,6 +1908,35 @@
}
/**
+ * return one active background call from background calls of the
+ * requested subId.
+ *
+ * Active call means the call is NOT idle defined by Call.isIdle()
+ *
+ * 1. If there is only one active background call on given sub or
+ * on SIP Phone, return it
+ * 2. If there is more than one active background call, return the background call
+ * associated with the active sub.
+ * 3. If there is no background call at all, return null.
+ *
+ * Complete background calls list can be get by getBackgroundCalls()
+ */
+ public Call getFirstActiveBgCall(long subId) {
+ Phone phone = getPhone(subId);
+ if (hasMoreThanOneHoldingCall(subId)) {
+ return phone.getBackgroundCall();
+ } else {
+ Call call = getFirstNonIdleCall(mBackgroundCalls, subId);
+ if (call == null) {
+ call = (phone == null)
+ ? null
+ : phone.getBackgroundCall();
+ }
+ return call;
+ }
+ }
+
+ /**
* return one active ringing call from ringing calls
*
* Active call means the call is NOT idle defined by Call.isIdle()
@@ -1639,6 +1959,17 @@
return call;
}
+ public Call getFirstActiveRingingCall(long subId) {
+ Phone phone = getPhone(subId);
+ Call call = getFirstNonIdleCall(mRingingCalls, subId);
+ if (call == null) {
+ call = (phone == null)
+ ? null
+ : phone.getRingingCall();
+ }
+ return call;
+ }
+
/**
* @return the state of active foreground call
* return IDLE if there is no active foreground call
@@ -1653,6 +1984,16 @@
return Call.State.IDLE;
}
+ public Call.State getActiveFgCallState(long subId) {
+ Call fgCall = getActiveFgCall(subId);
+
+ 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
@@ -1666,6 +2007,18 @@
}
/**
+ * @return the connections of active foreground call
+ * return empty list if there is no active foreground call
+ */
+ public List<Connection> getFgCallConnections(long subId) {
+ Call fgCall = getActiveFgCall(subId);
+ if ( fgCall != null) {
+ return fgCall.getConnections();
+ }
+ return mEmptyConnections;
+ }
+
+ /**
* @return the connections of active background call
* return empty list if there is no active background call
*/
@@ -1678,6 +2031,18 @@
}
/**
+ * @return the connections of active background call
+ * return empty list if there is no active background call
+ */
+ public List<Connection> getBgCallConnections(long subId) {
+ Call bgCall = getFirstActiveBgCall(subId);
+ if ( bgCall != null) {
+ return bgCall.getConnections();
+ }
+ return mEmptyConnections;
+ }
+
+ /**
* @return the latest connection of active foreground call
* return null if there is no active foreground call
*/
@@ -1690,6 +2055,18 @@
}
/**
+ * @return the latest connection of active foreground call
+ * return null if there is no active foreground call
+ */
+ public Connection getFgCallLatestConnection(long subId) {
+ Call fgCall = getActiveFgCall(subId);
+ if ( fgCall != null) {
+ return fgCall.getLatestConnection();
+ }
+ return null;
+ }
+
+ /**
* @return true if there is at least one Foreground call in disconnected state
*/
public boolean hasDisconnectedFgCall() {
@@ -1697,6 +2074,14 @@
}
/**
+ * @return true if there is at least one Foreground call in disconnected state
+ */
+ public boolean hasDisconnectedFgCall(long subId) {
+ return (getFirstCallOfState(mForegroundCalls, Call.State.DISCONNECTED,
+ subId) != null);
+ }
+
+ /**
* @return true if there is at least one background call in disconnected state
*/
public boolean hasDisconnectedBgCall() {
@@ -1704,6 +2089,15 @@
}
/**
+ * @return true if there is at least one background call in disconnected state
+ */
+ public boolean hasDisconnectedBgCall(long subId) {
+ return (getFirstCallOfState(mBackgroundCalls, Call.State.DISCONNECTED,
+ subId) != null);
+ }
+
+
+ /**
* @return the first active call from a call list
*/
private Call getFirstActiveCall(ArrayList<Call> calls) {
@@ -1716,6 +2110,19 @@
}
/**
+ * @return the first active call from a call list
+ */
+ private Call getFirstActiveCall(ArrayList<Call> calls, long subId) {
+ for (Call call : calls) {
+ if ((!call.isIdle()) && ((call.getPhone().getSubId() == subId) ||
+ (call.getPhone() instanceof SipPhone))) {
+ 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) {
@@ -1727,6 +2134,20 @@
return null;
}
+ /**
+ * @return the first call in a the Call.state from a call list
+ */
+ private Call getFirstCallOfState(ArrayList<Call> calls, Call.State state,
+ long subId) {
+ for (Call call : calls) {
+ if ((call.getState() == state) ||
+ ((call.getPhone().getSubId() == subId) ||
+ (call.getPhone() instanceof SipPhone))) {
+ return call;
+ }
+ }
+ return null;
+ }
private boolean hasMoreThanOneRingingCall() {
int count = 0;
@@ -1738,6 +2159,44 @@
return false;
}
+ /**
+ * @return true if more than one active ringing call exists on
+ * the active subId.
+ * This checks for the active calls on provided
+ * subId and also active calls on SIP Phone.
+ *
+ */
+ private boolean hasMoreThanOneRingingCall(long subId) {
+ int count = 0;
+ for (Call call : mRingingCalls) {
+ if ((call.getState().isRinging()) &&
+ ((call.getPhone().getSubId() == subId) ||
+ (call.getPhone() instanceof SipPhone))) {
+ if (++count > 1) return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * @return true if more than one active background call exists on
+ * the provided subId.
+ * This checks for the background calls on provided
+ * subId and also background calls on SIP Phone.
+ *
+ */
+ private boolean hasMoreThanOneHoldingCall(long subId) {
+ int count = 0;
+ for (Call call : mBackgroundCalls) {
+ if ((call.getState() == Call.State.HOLDING) &&
+ ((call.getPhone().getSubId() == subId) ||
+ (call.getPhone() instanceof SipPhone))) {
+ if (++count > 1) return true;
+ }
+ }
+ return false;
+ }
+
private Handler mHandler = new Handler() {
@Override
@@ -1754,8 +2213,9 @@
break;
case EVENT_NEW_RINGING_CONNECTION:
if (VDBG) Rlog.d(LOG_TAG, " handleMessage (EVENT_NEW_RINGING_CONNECTION)");
- if (getActiveFgCallState().isDialing() || hasMoreThanOneRingingCall()) {
- Connection c = (Connection) ((AsyncResult) msg.obj).result;
+ Connection c = (Connection) ((AsyncResult) msg.obj).result;
+ long subId = c.getCall().getPhone().getSubId();
+ if (getActiveFgCallState(subId).isDialing() || hasMoreThanOneRingingCall()) {
try {
Rlog.d(LOG_TAG, "silently drop incoming call: " + c.getCall());
c.getCall().hangup();
@@ -1845,6 +2305,10 @@
notifyMsg.sendToTarget();
}
break;
+ case EVENT_ONHOLD_TONE:
+ if (VDBG) Rlog.d(LOG_TAG, " handleMessage (EVENT_ONHOLD_TONE)");
+ mOnHoldToneRegistrants.notifyRegistrants((AsyncResult) msg.obj);
+ break;
}
}
};
@@ -1853,20 +2317,21 @@
public String toString() {
Call call;
StringBuilder b = new StringBuilder();
-
- b.append("CallManager {");
- b.append("\nstate = " + 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 (int i = 0; i < TelephonyManager.getDefault().getPhoneCount(); i++) {
+ b.append("CallManager {");
+ b.append("\nstate = " + getState(i));
+ call = getActiveFgCall(i);
+ b.append("\n- Foreground: " + getActiveFgCallState(i));
+ b.append(" from " + call.getPhone());
+ b.append("\n Conn: ").append(getFgCallConnections(i));
+ call = getFirstActiveBgCall(i);
+ b.append("\n- Background: " + call.getState());
+ b.append(" from " + call.getPhone());
+ b.append("\n Conn: ").append(getBgCallConnections(i));
+ call = getFirstActiveRingingCall(i);
+ b.append("\n- Ringing: " +call.getState());
+ b.append(" from " + call.getPhone());
+ }
for (Phone phone : getAllPhones()) {
if (phone != null) {
diff --git a/src/java/com/android/internal/telephony/CellBroadcastHandler.java b/src/java/com/android/internal/telephony/CellBroadcastHandler.java
index f7e0e3a..36f9521 100644
--- a/src/java/com/android/internal/telephony/CellBroadcastHandler.java
+++ b/src/java/com/android/internal/telephony/CellBroadcastHandler.java
@@ -23,6 +23,7 @@
import android.content.Intent;
import android.os.Message;
import android.provider.Telephony;
+import android.telephony.SubscriptionManager;
import android.telephony.SmsCbMessage;
/**
@@ -88,6 +89,7 @@
appOp = AppOpsManager.OP_RECEIVE_SMS;
}
intent.putExtra("message", message);
+ SubscriptionManager.putPhoneIdAndSubIdExtra(intent, mPhone.getPhoneId());
mContext.sendOrderedBroadcast(intent, receiverPermission, appOp, mReceiver,
getHandler(), Activity.RESULT_OK, null, null);
}
diff --git a/src/java/com/android/internal/telephony/CommandException.java b/src/java/com/android/internal/telephony/CommandException.java
index 80338ae..bf24689 100644
--- a/src/java/com/android/internal/telephony/CommandException.java
+++ b/src/java/com/android/internal/telephony/CommandException.java
@@ -44,6 +44,7 @@
ILLEGAL_SIM_OR_ME,
MISSING_RESOURCE,
NO_SUCH_ELEMENT,
+ SUBSCRIPTION_NOT_SUPPORTED,
}
public CommandException(Error e) {
@@ -89,6 +90,8 @@
return new CommandException(Error.MISSING_RESOURCE);
case RILConstants.NO_SUCH_ELEMENT:
return new CommandException(Error.NO_SUCH_ELEMENT);
+ case RILConstants.SUBSCRIPTION_NOT_SUPPORTED:
+ return new CommandException(Error.SUBSCRIPTION_NOT_SUPPORTED);
default:
Rlog.e("GSM", "Unrecognized RIL errno " + ril_errno);
return new CommandException(Error.INVALID_RESPONSE);
diff --git a/src/java/com/android/internal/telephony/CommandsInterface.java b/src/java/com/android/internal/telephony/CommandsInterface.java
index d292753..e75de0b 100644
--- a/src/java/com/android/internal/telephony/CommandsInterface.java
+++ b/src/java/com/android/internal/telephony/CommandsInterface.java
@@ -191,6 +191,26 @@
void registerForInCallVoicePrivacyOff(Handler h, int what, Object obj);
void unregisterForInCallVoicePrivacyOff(Handler h);
+ /** Single Radio Voice Call State progress notifications */
+ void registerForSrvccStateChanged(Handler h, int what, Object obj);
+ void unregisterForSrvccStateChanged(Handler h);
+
+ /**
+ * Handlers for subscription status change indications.
+ *
+ * @param h Handler for subscription status change messages.
+ * @param what User-defined message code.
+ * @param obj User object.
+ */
+ void registerForSubscriptionStatusChanged(Handler h, int what, Object obj);
+ void unregisterForSubscriptionStatusChanged(Handler h);
+
+ /**
+ * fires on any change in hardware configuration.
+ */
+ void registerForHardwareConfigChanged(Handler h, int what, Object obj);
+ void unregisterForHardwareConfigChanged(Handler h);
+
/**
* unlike the register* methods, there's only one new 3GPP format SMS handler.
* if you need to unregister, you should also tell the radio to stop
@@ -1624,10 +1644,23 @@
*
* @param nonce the nonce string to pass with the ISIM authentication request
* @param response a callback message with the String response in the obj field
+ * @deprecated
+ * @see requestIccSimAuthentication
*/
public void requestIsimAuthentication(String nonce, Message response);
/**
+ * Request the SIM application on the UICC to perform authentication
+ * challenge/response algorithm. The data string and challenge response are
+ * Base64 encoded Strings.
+ * Can support EAP-SIM, EAP-AKA with results encoded per 3GPP TS 31.102.
+ *
+ * @param data authentication challenge data
+ * @param response a callback message with the String response in the obj field
+ */
+ public void requestIccSimAuthentication(String data, Message response);
+
+ /**
* Get the current Voice Radio Technology.
*
* AsyncResult.result is an int array with the first value
@@ -1650,11 +1683,11 @@
* Sets the minimum time in milli-seconds between when RIL_UNSOL_CELL_INFO_LIST
* should be invoked.
*
- * The default, 0, means invoke RIL_UNSOL_CELL_INFO_LIST when any of the reported
+ * The default, 0, means invoke RIL_UNSOL_CELL_INFO_LIST when any of the reported
* information changes. Setting the value to INT_MAX(0x7fffffff) means never issue
* A RIL_UNSOL_CELL_INFO_LIST.
*
- *
+ *
* @param rateInMillis is sent back to handler and result.obj is a AsyncResult
* @param response.obj is AsyncResult ar when sent to associated handler
@@ -1793,7 +1826,45 @@
void nvResetConfig(int resetType, Message response);
/**
+ * returned message
+ * retMsg.obj = AsyncResult ar
+ * ar.exception carries exception on failure
+ * ar.userObject contains the orignal value of result.obj
+ * ar.result contains a List of HardwareConfig
+ */
+ void getHardwareConfig (Message result);
+
+ /**
* @return version of the ril.
*/
int getRilVersion();
+
+ /**
+ * Sets user selected subscription at Modem.
+ *
+ * @param slotId
+ * Slot.
+ * @param appIndex
+ * Application index in the card.
+ * @param subId
+ * Indicates subscription 0 or subscription 1.
+ * @param subStatus
+ * Activation status, 1 = activate and 0 = deactivate.
+ * @param result
+ * Callback message contains the information of SUCCESS/FAILURE.
+ */
+ // FIXME Update the doc and consider modifying the request to make more generic.
+ public void setUiccSubscription(int slotId, int appIndex, int subId, int subStatus,
+ Message result);
+
+ /**
+ * Tells the modem if data is allowed or not.
+ *
+ * @param allowed
+ * true = allowed, false = not alowed
+ * @param result
+ * Callback message contains the information of SUCCESS/FAILURE.
+ */
+ // FIXME We may need to pass AID and slotid also
+ public void setDataAllowed(boolean allowed, Message result);
}
diff --git a/src/java/com/android/internal/telephony/Connection.java b/src/java/com/android/internal/telephony/Connection.java
index 59a5586..e1ef562 100644
--- a/src/java/com/android/internal/telephony/Connection.java
+++ b/src/java/com/android/internal/telephony/Connection.java
@@ -99,6 +99,14 @@
public abstract long getConnectTime();
/**
+ * Connection connect time in elapsedRealtime() format.
+ * For outgoing calls: Begins at (DIALING|ALERTING) -> ACTIVE transition.
+ * For incoming calls: Begins at (INCOMING|WAITING) -> ACTIVE transition.
+ * Returns 0 before then.
+ */
+ public abstract long getConnectTimeReal();
+
+ /**
* Disconnect time in currentTimeMillis() format.
* The time when this Connection makes a transition into ENDED or FAIL.
* Returns 0 before then.
@@ -114,6 +122,13 @@
public abstract long getDurationMillis();
/**
+ * The time when this Connection last transitioned into HOLDING
+ * in elapsedRealtime() format.
+ * Returns 0, if it has never made a transition into HOLDING.
+ */
+ public abstract long getHoldingStartTime();
+
+ /**
* If this connection is HOLDING, return the number of milliseconds
* that it has been on hold for (approximately).
* If this connection is in any other state, return 0.
@@ -282,6 +297,19 @@
public abstract int getPreciseDisconnectCause();
/**
+ * Returns the original Connection instance associated with
+ * this Connection
+ */
+ public abstract Connection getOrigConnection();
+
+ /**
+ * Returns whether the original ImsPhoneConnection was a member
+ * of a conference call
+ * @return valid only when getOrigConnection() is not null
+ */
+ public abstract boolean isMultiparty();
+
+ /**
* Build a human representation of a connection instance, suitable for debugging.
* Don't log personal stuff unless in debug mode.
* @return a string representing the internal state of this connection.
diff --git a/src/java/com/android/internal/telephony/DefaultPhoneNotifier.java b/src/java/com/android/internal/telephony/DefaultPhoneNotifier.java
index c001b79..d1cc618 100644
--- a/src/java/com/android/internal/telephony/DefaultPhoneNotifier.java
+++ b/src/java/com/android/internal/telephony/DefaultPhoneNotifier.java
@@ -23,12 +23,19 @@
import android.os.ServiceManager;
import android.telephony.CellInfo;
import android.telephony.DataConnectionRealTimeInfo;
+import android.telephony.Rlog;
+import android.telephony.VoLteServiceState;
import android.telephony.ServiceState;
+import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.telephony.PreciseCallState;
import android.telephony.DisconnectCause;
+import com.android.internal.telephony.Call;
+import com.android.internal.telephony.CallManager;
+import com.android.internal.telephony.Phone;
import com.android.internal.telephony.ITelephonyRegistry;
+import com.android.internal.telephony.PhoneConstants;
import java.util.List;
@@ -36,11 +43,12 @@
* broadcast intents
*/
public class DefaultPhoneNotifier implements PhoneNotifier {
+ static final String LOG_TAG = "DefaultPhoneNotifier";
- private ITelephonyRegistry mRegistry;
+ protected ITelephonyRegistry mRegistry;
/*package*/
- DefaultPhoneNotifier() {
+ protected DefaultPhoneNotifier() {
mRegistry = ITelephonyRegistry.Stub.asInterface(ServiceManager.getService(
"telephony.registry"));
}
@@ -48,13 +56,56 @@
@Override
public void notifyPhoneState(Phone sender) {
Call ringingCall = sender.getRingingCall();
+ long subId = sender.getSubId();
String incomingNumber = "";
if (ringingCall != null && ringingCall.getEarliestConnection() != null){
incomingNumber = ringingCall.getEarliestConnection().getAddress();
}
try {
if (mRegistry != null) {
- mRegistry.notifyCallState(convertCallState(sender.getState()), incomingNumber);
+ mRegistry.notifyCallStateUsingSubId(subId,
+ convertCallState(sender.getState()), incomingNumber);
+ }
+ } catch (RemoteException ex) {
+ // system process is dead
+ }
+ notifyCallStateToTelephonyRegistry(sender);
+ }
+
+ /*
+ * Suppose, some third party app e.g. FM app registers for a call state changed indication
+ * through TelephonyManager/PhoneStateListener and an incoming call is received on sub1 or
+ * sub2. Then ir-respective of sub1/sub2 FM app should be informed of call state
+ * changed(onCallStateChanged()) indication so that FM app can be paused.
+ * Hence send consolidated call state information to apps. (i.e. sub1 or sub2 active
+ * call state, in priority order RINGING > OFFHOOK > IDLE)
+ */
+ public void notifyCallStateToTelephonyRegistry(Phone sender) {
+ Call ringingCall = null;
+ CallManager cm = CallManager.getInstance();
+ PhoneConstants.State state = sender.getState();
+ String incomingNumber = "";
+ for (Phone phone : cm.getAllPhones()) {
+ if (phone.getState() == PhoneConstants.State.RINGING) {
+ ringingCall = phone.getRingingCall();
+ if (ringingCall != null && ringingCall.getEarliestConnection() != null) {
+ incomingNumber = ringingCall.getEarliestConnection().getAddress();
+ }
+ sender = phone;
+ state = PhoneConstants.State.RINGING;
+ break;
+ } else if (phone.getState() == PhoneConstants.State.OFFHOOK) {
+ if (state == PhoneConstants.State.IDLE) {
+ state = PhoneConstants.State.OFFHOOK;
+ sender = phone;
+ }
+ }
+ }
+ Rlog.d(LOG_TAG, "notifyCallStateToTelephonyRegistry, subId = " + sender.getSubId()
+ + " state = " + state);
+ try {
+ if (mRegistry != null) {
+ mRegistry.notifyCallState(convertCallState(state), incomingNumber);
}
} catch (RemoteException ex) {
// system process is dead
@@ -64,13 +115,16 @@
@Override
public void notifyServiceState(Phone sender) {
ServiceState ss = sender.getServiceState();
+ long subId = sender.getSubId();
+ Rlog.d(LOG_TAG, "nofityServiceState: mRegistry=" + mRegistry + " ss=" + ss
+ + " sender=" + sender);
if (ss == null) {
ss = new ServiceState();
ss.setStateOutOfService();
}
try {
if (mRegistry != null) {
- mRegistry.notifyServiceState(ss);
+ mRegistry.notifyServiceStateUsingSubId(subId, ss);
}
} catch (RemoteException ex) {
// system process is dead
@@ -79,9 +133,12 @@
@Override
public void notifySignalStrength(Phone sender) {
+ long subId = sender.getSubId();
+ Rlog.d(LOG_TAG, "notifySignalStrength: mRegistry=" + mRegistry
+ + " ss=" + sender.getSignalStrength() + " sender=" + sender);
try {
if (mRegistry != null) {
- mRegistry.notifySignalStrength(sender.getSignalStrength());
+ mRegistry.notifySignalStrengthUsingSubId(subId, sender.getSignalStrength());
}
} catch (RemoteException ex) {
// system process is dead
@@ -90,9 +147,11 @@
@Override
public void notifyMessageWaitingChanged(Phone sender) {
+ long subId = sender.getSubId();
try {
if (mRegistry != null) {
- mRegistry.notifyMessageWaitingChanged(sender.getMessageWaitingIndicator());
+ mRegistry.notifyMessageWaitingChangedUsingSubId(subId,
+ sender.getMessageWaitingIndicator());
}
} catch (RemoteException ex) {
// system process is dead
@@ -101,9 +160,11 @@
@Override
public void notifyCallForwardingChanged(Phone sender) {
+ long subId = sender.getSubId();
try {
if (mRegistry != null) {
- mRegistry.notifyCallForwardingChanged(sender.getCallForwardingIndicator());
+ mRegistry.notifyCallForwardingChangedUsingSubId(subId,
+ sender.getCallForwardingIndicator());
}
} catch (RemoteException ex) {
// system process is dead
@@ -112,9 +173,11 @@
@Override
public void notifyDataActivity(Phone sender) {
+ long subId = sender.getSubId();
try {
if (mRegistry != null) {
- mRegistry.notifyDataActivity(convertDataActivityState(sender.getDataActivityState()));
+ mRegistry.notifyDataActivityUsingSubId(subId,
+ convertDataActivityState(sender.getDataActivityState()));
}
} catch (RemoteException ex) {
// system process is dead
@@ -129,6 +192,10 @@
private void doNotifyDataConnection(Phone sender, String reason, String apnType,
PhoneConstants.DataState state) {
+ long subId = sender.getSubId();
+ long dds = SubscriptionManager.getDefaultDataSubId();
+ Rlog.d(LOG_TAG, "subId = " + subId + ", DDS = " + dds);
+
// TODO
// use apnType as the key to which connection we're talking about.
// pass apnType back up to fetch particular for this one.
@@ -146,16 +213,16 @@
try {
if (mRegistry != null) {
- mRegistry.notifyDataConnection(
- convertDataState(state),
- sender.isDataConnectivityPossible(apnType), reason,
- sender.getActiveApnHost(apnType),
- apnType,
- linkProperties,
- networkCapabilities,
- ((telephony!=null) ? telephony.getNetworkType() :
- TelephonyManager.NETWORK_TYPE_UNKNOWN),
- roaming);
+ mRegistry.notifyDataConnectionUsingSubId(subId,
+ convertDataState(state),
+ sender.isDataConnectivityPossible(apnType), reason,
+ sender.getActiveApnHost(apnType),
+ apnType,
+ linkProperties,
+ networkCapabilities,
+ ((telephony!=null) ? telephony.getNetworkType() :
+ TelephonyManager.NETWORK_TYPE_UNKNOWN),
+ roaming);
}
} catch (RemoteException ex) {
// system process is dead
@@ -164,9 +231,10 @@
@Override
public void notifyDataConnectionFailed(Phone sender, String reason, String apnType) {
+ long subId = sender.getSubId();
try {
if (mRegistry != null) {
- mRegistry.notifyDataConnectionFailed(reason, apnType);
+ mRegistry.notifyDataConnectionFailedUsingSubId(subId, reason, apnType);
}
} catch (RemoteException ex) {
// system process is dead
@@ -175,11 +243,12 @@
@Override
public void notifyCellLocation(Phone sender) {
+ long subId = sender.getSubId();
Bundle data = new Bundle();
sender.getCellLocation().fillInNotifierBundle(data);
try {
if (mRegistry != null) {
- mRegistry.notifyCellLocation(data);
+ mRegistry.notifyCellLocationUsingSubId(subId, data);
}
} catch (RemoteException ex) {
// system process is dead
@@ -188,9 +257,10 @@
@Override
public void notifyCellInfo(Phone sender, List<CellInfo> cellInfo) {
+ long subId = sender.getSubId();
try {
if (mRegistry != null) {
- mRegistry.notifyCellInfo(cellInfo);
+ mRegistry.notifyCellInfoUsingSubId(subId, cellInfo);
}
} catch (RemoteException ex) {
@@ -209,6 +279,7 @@
@Override
public void notifyOtaspChanged(Phone sender, int otaspMode) {
+ // FIXME: subId?
try {
if (mRegistry != null) {
mRegistry.notifyOtaspChanged(otaspMode);
@@ -219,6 +290,7 @@
}
public void notifyPreciseCallState(Phone sender) {
+ // FIXME: subId?
Call ringingCall = sender.getRingingCall();
Call foregroundCall = sender.getForegroundCall();
Call backgroundCall = sender.getBackgroundCall();
@@ -235,6 +307,7 @@
}
public void notifyDisconnectCause(int cause, int preciseCause) {
+ // FIXME: subId?
try {
mRegistry.notifyDisconnectCause(cause, preciseCause);
} catch (RemoteException ex) {
@@ -244,6 +317,7 @@
public void notifyPreciseDataConnectionFailed(Phone sender, String reason, String apnType,
String apn, String failCause) {
+ // FIXME: subId?
try {
mRegistry.notifyPreciseDataConnectionFailed(reason, apnType, apn, failCause);
} catch (RemoteException ex) {
@@ -251,6 +325,16 @@
}
}
+ @Override
+ public void notifyVoLteServiceStateChanged(Phone sender, VoLteServiceState lteState) {
+ // FIXME: subID
+ try {
+ mRegistry.notifyVoLteServiceStateChanged(lteState);
+ } catch (RemoteException ex) {
+ // system process is dead
+ }
+ }
+
/**
* Convert the {@link PhoneConstants.State} enum into the TelephonyManager.CALL_STATE_*
* constants for the public API.
@@ -406,4 +490,9 @@
return Call.State.IDLE;
}
}
+
+ public interface IDataStateChangedCallback {
+ void onDataStateChanged(long subId, String state, String reason, String apnName,
+ String apnType, boolean unavailable);
+ }
}
diff --git a/src/java/com/android/internal/telephony/HardwareConfig.java b/src/java/com/android/internal/telephony/HardwareConfig.java
new file mode 100644
index 0000000..0f855de
--- /dev/null
+++ b/src/java/com/android/internal/telephony/HardwareConfig.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2014 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.Rlog;
+import java.util.BitSet;
+import android.telephony.ServiceState;
+
+/**
+ * {@hide}
+ *
+ * hardware configuration information reported by the ril layer and for
+ * use by the telephone framework.
+ *
+ * the hardware configuration is managed by the TelephonyDevController
+ * (aka: the 'TDC').
+ *
+ * the hardware resources are:
+ * - modem: physical entity providing acces technology.
+ * - sim: physicaly entity providing a slot interface.
+ */
+public class HardwareConfig {
+ static final String LOG_TAG = "HardwareConfig";
+
+ /**
+ * hardware configuration kind.
+ */
+ public static final int DEV_HARDWARE_TYPE_MODEM = 0;
+ public static final int DEV_HARDWARE_TYPE_SIM = 1;
+ /**
+ * ril attachment model. if single, there is a one-to-one
+ * relationship between a modem hardware and a ril daemon.
+ * if multiple, there is a one-to-many relatioship between a
+ * modem hardware and several ril simultaneous ril daemons.
+ */
+ public static final int DEV_MODEM_RIL_MODEL_SINGLE = 0;
+ public static final int DEV_MODEM_RIL_MODEL_MULTIPLE = 1;
+ /**
+ * hardware state of the resource.
+ *
+ * enabled: the resource can be used by the msim-framework,
+ * call activity can be handled on it.
+ * standby: the resource can be used by the msim-framework but
+ * only for non call related activity. as example:
+ * reading the address book from a sim device. attempt
+ * to use this resource for call activity leads to
+ * undetermined results.
+ * disabled: the resource cannot be used and attempt to use
+ * it leads to undetermined results.
+ *
+ * by default, all resources are 'enabled', what causes a resource
+ * to be marked otherwise is a property of the underlying hardware
+ * knowledge and implementation and it is out of scope of the TDC.
+ */
+ public static final int DEV_HARDWARE_STATE_ENABLED = 0;
+ public static final int DEV_HARDWARE_STATE_STANDBY = 1;
+ public static final int DEV_HARDWARE_STATE_DISABLED = 2;
+
+ /**
+ * common hardware configuration.
+ *
+ * type - see DEV_HARDWARE_TYPE_
+ * uuid - unique identifier for this hardware.
+ * state - see DEV_HARDWARE_STATE_
+ */
+ public int type;
+ public String uuid;
+ public int state;
+ /**
+ * following is some specific hardware configuration based on the hardware type.
+ */
+ /**
+ * DEV_HARDWARE_TYPE_MODEM.
+ *
+ * rilModel - see DEV_MODEM_RIL_MODEL_
+ * rat - BitSet value, based on android.telephony.ServiceState
+ * maxActiveVoiceCall - maximum number of concurent active voice calls.
+ * maxActiveDataCall - maximum number of concurent active data calls.
+ * maxStandby - maximum number of concurent standby connections.
+ *
+ * note: the maxStandby is not necessarily an equal sum of the maxActiveVoiceCall
+ * and maxActiveDataCall (nor a derivative of it) since it really depends on the
+ * modem capability, hence it is left for the hardware to define.
+ */
+ public int rilModel;
+ public BitSet rat;
+ public int maxActiveVoiceCall;
+ public int maxActiveDataCall;
+ public int maxStandby;
+ /**
+ * DEV_HARDWARE_TYPE_SIM.
+ *
+ * modemUuid - unique association to a modem for a sim.
+ */
+ public String modemUuid;
+
+ /**
+ * default constructor.
+ */
+ public HardwareConfig(int type) {
+ type = type;
+ }
+
+ /**
+ * create from a resource string format.
+ */
+ public HardwareConfig(String res) {
+ String split[] = res.split(",");
+
+ type = Integer.parseInt(split[0]);
+
+ switch (type) {
+ case DEV_HARDWARE_TYPE_MODEM: {
+ assignModem(
+ split[1].trim(), /* uuid */
+ Integer.parseInt(split[2]), /* state */
+ Integer.parseInt(split[3]), /* ril-model */
+ Integer.parseInt(split[4]), /* rat */
+ Integer.parseInt(split[5]), /* max-voice */
+ Integer.parseInt(split[6]), /* max-data */
+ Integer.parseInt(split[7]) /* max-standby */
+ );
+ break;
+ }
+ case DEV_HARDWARE_TYPE_SIM: {
+ assignSim(
+ split[1].trim(), /* uuid */
+ Integer.parseInt(split[2]), /* state */
+ split[3].trim() /* modem-uuid */
+ );
+ break;
+ }
+ }
+ }
+
+ public void assignModem(String id, int state, int model, int ratBits,
+ int maxV, int maxD, int maxS) {
+ if (type == DEV_HARDWARE_TYPE_MODEM) {
+ char[] bits = Integer.toBinaryString(ratBits).toCharArray();
+ uuid = id;
+ state = state;
+ rilModel = model;
+ rat = new BitSet(bits.length);
+ for (int i = 0 ; i < bits.length ; i++) {
+ rat.set(i, (bits[i] == '1' ? true : false));
+ }
+ maxActiveVoiceCall = maxV;
+ maxActiveDataCall = maxD;
+ maxStandby = maxS;
+ }
+ }
+
+ public void assignSim(String id, int state, String link) {
+ if (type == DEV_HARDWARE_TYPE_SIM) {
+ uuid = id;
+ modemUuid = link;
+ state = state;
+ }
+ }
+
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ if (type == DEV_HARDWARE_TYPE_MODEM) {
+ builder.append("Modem ");
+ builder.append("{ uuid=" + uuid);
+ builder.append(", state=" + state);
+ builder.append(", rilModel=" + rilModel);
+ builder.append(", rat=" + rat.toString());
+ builder.append(", maxActiveVoiceCall=" + maxActiveVoiceCall);
+ builder.append(", maxActiveDataCall=" + maxActiveDataCall);
+ builder.append(", maxStandby=" + maxStandby);
+ builder.append(" }");
+ } else if (type == DEV_HARDWARE_TYPE_SIM) {
+ builder.append("Sim ");
+ builder.append("{ uuid=" + uuid);
+ builder.append(", modemUuid=" + modemUuid);
+ builder.append(", state=" + state);
+ builder.append(" }");
+ } else {
+ builder.append("Invalid Configration");
+ }
+ return builder.toString();
+ }
+
+ public int compareTo(HardwareConfig hw) {
+ String one = this.toString();
+ String two = hw.toString();
+
+ return (one.compareTo(two));
+ }
+}
diff --git a/src/java/com/android/internal/telephony/IIccPhoneBook.aidl b/src/java/com/android/internal/telephony/IIccPhoneBook.aidl
index 95b3efb..0f802cc 100644
--- a/src/java/com/android/internal/telephony/IIccPhoneBook.aidl
+++ b/src/java/com/android/internal/telephony/IIccPhoneBook.aidl
@@ -46,6 +46,16 @@
List<AdnRecord> getAdnRecordsInEf(int efid);
/**
+ * Loads the AdnRecords in efid and returns them as a
+ * List of AdnRecords
+ *
+ * @param efid the EF id of a ADN-like SIM
+ * @param subId user preferred subId
+ * @return List of AdnRecord
+ */
+ List<AdnRecord> getAdnRecordsInEfUsingSubId(long subId, int efid);
+
+ /**
* Replace oldAdn with newAdn in ADN-like record in EF
*
* getAdnRecordsInEf must be called at least once before this function,
@@ -68,6 +78,31 @@
String newTag, String newPhoneNumber,
String pin2);
+
+
+ /**
+ * Replace oldAdn with newAdn in ADN-like record in EF
+ *
+ * getAdnRecordsInEf must be called at least once before this function,
+ * otherwise an error will be returned
+ *
+ * @param efid must be one among EF_ADN, EF_FDN, and EF_SDN
+ * @param oldTag adn tag to be replaced
+ * @param oldPhoneNumber adn number to be replaced
+ * Set both oldTag and oldPhoneNubmer to "" means to replace an
+ * empty record, aka, insert new record
+ * @param newTag adn tag to be stored
+ * @param newPhoneNumber adn number ot be stored
+ * Set both newTag and newPhoneNubmer to "" means to replace the old
+ * record with empty one, aka, delete old record
+ * @param pin2 required to update EF_FDN, otherwise must be null
+ * @param subId user preferred subId
+ * @return true for success
+ */
+ boolean updateAdnRecordsInEfBySearchUsingSubId(long subId, int efid,
+ String oldTag, String oldPhoneNumber,
+ String newTag, String newPhoneNumber,
+ String pin2);
/**
* Update an ADN-like EF record by record index
*
@@ -88,6 +123,26 @@
String pin2);
/**
+ * Update an ADN-like EF record by record index
+ *
+ * This is useful for iteration the whole ADN file, such as write the whole
+ * phone book or erase/format the whole phonebook
+ *
+ * @param efid must be one among EF_ADN, EF_FDN, and EF_SDN
+ * @param newTag adn tag to be stored
+ * @param newPhoneNumber adn number to be stored
+ * Set both newTag and newPhoneNubmer to "" means to replace the old
+ * record with empty one, aka, delete old record
+ * @param index is 1-based adn record index to be updated
+ * @param pin2 required to update EF_FDN, otherwise must be null
+ * @param subId user preferred subId
+ * @return true for success
+ */
+ boolean updateAdnRecordsInEfByIndexUsingSubId(long subId, int efid, String newTag,
+ String newPhoneNumber, int index,
+ String pin2);
+
+ /**
* Get the max munber of records in efid
*
* @param efid the EF id of a ADN-like SIM
@@ -98,4 +153,16 @@
*/
int[] getAdnRecordsSize(int efid);
+ /**
+ * Get the max munber of records in efid
+ *
+ * @param efid the EF id of a ADN-like SIM
+ * @param subId user preferred subId
+ * @return int[3] array
+ * recordSizes[0] is the single record length
+ * recordSizes[1] is the total length of the EF file
+ * recordSizes[2] is the number of records in the EF file
+ */
+ int[] getAdnRecordsSizeUsingSubId(long subId, int efid);
+
}
diff --git a/src/java/com/android/internal/telephony/IccPhoneBookInterfaceManager.java b/src/java/com/android/internal/telephony/IccPhoneBookInterfaceManager.java
index d4ae7fb..8699148 100644
--- a/src/java/com/android/internal/telephony/IccPhoneBookInterfaceManager.java
+++ b/src/java/com/android/internal/telephony/IccPhoneBookInterfaceManager.java
@@ -28,6 +28,8 @@
import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppType;
import com.android.internal.telephony.uicc.IccConstants;
import com.android.internal.telephony.uicc.IccRecords;
+import com.android.internal.telephony.uicc.UiccCard;
+import com.android.internal.telephony.uicc.UiccCardApplication;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -36,16 +38,19 @@
* SimPhoneBookInterfaceManager to provide an inter-process communication to
* access ADN-like SIM records.
*/
-public abstract class IccPhoneBookInterfaceManager extends IIccPhoneBook.Stub {
+public abstract class IccPhoneBookInterfaceManager {
protected static final boolean DBG = true;
protected PhoneBase mPhone;
+ private UiccCardApplication mCurrentApp = null;
protected AdnRecordCache mAdnCache;
protected final Object mLock = new Object();
protected int mRecordSize[];
protected boolean mSuccess;
+ private boolean mIs3gCard = false; // flag to determine if card is 3G or 2G
protected List<AdnRecord> mRecords;
+
protected static final boolean ALLOW_SIM_OP_IN_UI_THREAD = false;
protected static final int EVENT_GET_SIZE_DONE = 1;
@@ -126,11 +131,6 @@
}
}
- protected void publish() {
- //NOTE service "simphonebook" added by IccSmsInterfaceManagerProxy
- ServiceManager.addService("simphonebook", this);
- }
-
protected abstract void logd(String msg);
protected abstract void loge(String msg);
@@ -155,7 +155,6 @@
* @param pin2 required to update EF_FDN, otherwise must be null
* @return true for success
*/
- @Override
public boolean
updateAdnRecordsInEfBySearch (int efid,
String oldTag, String oldPhoneNumber,
@@ -210,7 +209,6 @@
* @param pin2 required to update EF_FDN, otherwise must be null
* @return true for success
*/
- @Override
public boolean
updateAdnRecordsInEfByIndex(int efid, String newTag,
String newPhoneNumber, int index, String pin2) {
@@ -250,7 +248,6 @@
* recordSizes[1] is the total length of the EF file
* recordSizes[2] is the number of records in the EF file
*/
- @Override
public abstract int[] getAdnRecordsSize(int efid);
/**
@@ -262,7 +259,6 @@
* @param efid the EF id of a ADN-like ICC
* @return List of AdnRecord
*/
- @Override
public List<AdnRecord> getAdnRecordsInEf(int efid) {
if (mPhone.getContext().checkCallingOrSelfPermission(
diff --git a/src/java/com/android/internal/telephony/IccPhoneBookInterfaceManagerProxy.java b/src/java/com/android/internal/telephony/IccPhoneBookInterfaceManagerProxy.java
index 3a2fbc5..b2ccbfe 100644
--- a/src/java/com/android/internal/telephony/IccPhoneBookInterfaceManagerProxy.java
+++ b/src/java/com/android/internal/telephony/IccPhoneBookInterfaceManagerProxy.java
@@ -27,15 +27,12 @@
* SimPhoneBookInterfaceManager to provide an inter-process communication to
* access ADN-like SIM records.
*/
-public class IccPhoneBookInterfaceManagerProxy extends IIccPhoneBook.Stub {
+public class IccPhoneBookInterfaceManagerProxy {
private IccPhoneBookInterfaceManager mIccPhoneBookInterfaceManager;
public IccPhoneBookInterfaceManagerProxy(IccPhoneBookInterfaceManager
iccPhoneBookInterfaceManager) {
mIccPhoneBookInterfaceManager = iccPhoneBookInterfaceManager;
- if(ServiceManager.getService("simphonebook") == null) {
- ServiceManager.addService("simphonebook", this);
- }
}
public void setmIccPhoneBookInterfaceManager(
@@ -43,7 +40,6 @@
mIccPhoneBookInterfaceManager = iccPhoneBookInterfaceManager;
}
- @Override
public boolean
updateAdnRecordsInEfBySearch (int efid,
String oldTag, String oldPhoneNumber,
@@ -53,7 +49,6 @@
efid, oldTag, oldPhoneNumber, newTag, newPhoneNumber, pin2);
}
- @Override
public boolean
updateAdnRecordsInEfByIndex(int efid, String newTag,
String newPhoneNumber, int index, String pin2) {
@@ -61,12 +56,10 @@
newTag, newPhoneNumber, index, pin2);
}
- @Override
public int[] getAdnRecordsSize(int efid) {
return mIccPhoneBookInterfaceManager.getAdnRecordsSize(efid);
}
- @Override
public List<AdnRecord> getAdnRecordsInEf(int efid) {
return mIccPhoneBookInterfaceManager.getAdnRecordsInEf(efid);
}
diff --git a/src/java/com/android/internal/telephony/IccProvider.java b/src/java/com/android/internal/telephony/IccProvider.java
old mode 100644
new mode 100755
index 6f2c4ed..79d2eba
--- a/src/java/com/android/internal/telephony/IccProvider.java
+++ b/src/java/com/android/internal/telephony/IccProvider.java
@@ -20,10 +20,14 @@
import android.content.UriMatcher;
import android.content.ContentValues;
import android.database.Cursor;
+import android.database.MergeCursor;
import android.database.MatrixCursor;
import android.net.Uri;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.telephony.SubInfoRecord;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.telephony.Rlog;
@@ -39,7 +43,7 @@
*/
public class IccProvider extends ContentProvider {
private static final String TAG = "IccProvider";
- private static final boolean DBG = false;
+ private static final boolean DBG = true;
private static final String[] ADDRESS_BOOK_COLUMN_NAMES = new String[] {
@@ -49,25 +53,31 @@
"_id"
};
- private static final int ADN = 1;
- private static final int FDN = 2;
- private static final int SDN = 3;
+ protected static final int ADN = 1;
+ protected static final int ADN_SUB = 2;
+ protected static final int FDN = 3;
+ protected static final int FDN_SUB = 4;
+ protected static final int SDN = 5;
+ protected static final int SDN_SUB = 6;
+ protected static final int ADN_ALL = 7;
- private static final String STR_TAG = "tag";
- private static final String STR_NUMBER = "number";
- private static final String STR_EMAILS = "emails";
- private static final String STR_PIN2 = "pin2";
+ protected static final String STR_TAG = "tag";
+ protected static final String STR_NUMBER = "number";
+ protected static final String STR_EMAILS = "emails";
+ protected static final String STR_PIN2 = "pin2";
private static final UriMatcher URL_MATCHER =
new UriMatcher(UriMatcher.NO_MATCH);
static {
URL_MATCHER.addURI("icc", "adn", ADN);
+ URL_MATCHER.addURI("icc", "adn/subId/#", ADN_SUB);
URL_MATCHER.addURI("icc", "fdn", FDN);
+ URL_MATCHER.addURI("icc", "fdn/subId/#", FDN_SUB);
URL_MATCHER.addURI("icc", "sdn", SDN);
+ URL_MATCHER.addURI("icc", "sdn/subId/#", SDN_SUB);
}
-
@Override
public boolean onCreate() {
return true;
@@ -76,27 +86,66 @@
@Override
public Cursor query(Uri url, String[] projection, String selection,
String[] selectionArgs, String sort) {
+ if (DBG) log("query");
+
switch (URL_MATCHER.match(url)) {
case ADN:
- return loadFromEf(IccConstants.EF_ADN);
+ return loadFromEf(IccConstants.EF_ADN, SubscriptionManager.getDefaultSubId());
+
+ case ADN_SUB:
+ return loadFromEf(IccConstants.EF_ADN, getRequestSubId(url));
case FDN:
- return loadFromEf(IccConstants.EF_FDN);
+ return loadFromEf(IccConstants.EF_FDN, SubscriptionManager.getDefaultSubId());
+
+ case FDN_SUB:
+ return loadFromEf(IccConstants.EF_FDN, getRequestSubId(url));
case SDN:
- return loadFromEf(IccConstants.EF_SDN);
+ return loadFromEf(IccConstants.EF_SDN, SubscriptionManager.getDefaultSubId());
+
+ case SDN_SUB:
+ return loadFromEf(IccConstants.EF_SDN, getRequestSubId(url));
+
+ case ADN_ALL:
+ return loadAllSimContacts(IccConstants.EF_ADN);
default:
throw new IllegalArgumentException("Unknown URL " + url);
}
}
+ private Cursor loadAllSimContacts(int efType) {
+ Cursor [] result;
+ List<SubInfoRecord> subInfoList = SubscriptionManager.getActivatedSubInfoList(null);
+
+ if ((subInfoList == null) || (subInfoList.size() == 0)) {
+ result = new Cursor[0];
+ } else {
+ int subIdCount = subInfoList.size();
+ result = new Cursor[subIdCount];
+ long subId;
+
+ for (int i = 0; i < subIdCount; i++) {
+ subId = subInfoList.get(i).mSubId;
+ result[i] = loadFromEf(efType, subId);
+ Rlog.i(TAG,"ADN Records loaded for Subscription ::" + subId);
+ }
+ }
+
+ return new MergeCursor(result);
+ }
+
@Override
public String getType(Uri url) {
switch (URL_MATCHER.match(url)) {
case ADN:
+ case ADN_SUB:
case FDN:
+ case FDN_SUB:
case SDN:
+ case SDN_SUB:
+ case ADN_ALL:
return "vnd.android.cursor.dir/sim-contact";
default:
@@ -109,6 +158,7 @@
Uri resultUri;
int efType;
String pin2 = null;
+ long subId;
if (DBG) log("insert");
@@ -116,10 +166,23 @@
switch (match) {
case ADN:
efType = IccConstants.EF_ADN;
+ subId = SubscriptionManager.getDefaultSubId();
+ break;
+
+ case ADN_SUB:
+ efType = IccConstants.EF_ADN;
+ subId = getRequestSubId(url);
break;
case FDN:
efType = IccConstants.EF_FDN;
+ subId = SubscriptionManager.getDefaultSubId();
+ pin2 = initialValues.getAsString("pin2");
+ break;
+
+ case FDN_SUB:
+ efType = IccConstants.EF_FDN;
+ subId = getRequestSubId(url);
pin2 = initialValues.getAsString("pin2");
break;
@@ -131,7 +194,7 @@
String tag = initialValues.getAsString("tag");
String number = initialValues.getAsString("number");
// TODO(): Read email instead of sending null.
- boolean success = addIccRecordToEf(efType, tag, number, null, pin2);
+ boolean success = addIccRecordToEf(efType, tag, number, null, pin2, subId);
if (!success) {
return null;
@@ -143,9 +206,17 @@
buf.append("adn/");
break;
+ case ADN_SUB:
+ buf.append("adn/subId/");
+ break;
+
case FDN:
buf.append("fdn/");
break;
+
+ case FDN_SUB:
+ buf.append("fdn/subId/");
+ break;
}
// TODO: we need to find out the rowId for the newly added record
@@ -153,6 +224,7 @@
resultUri = Uri.parse(buf.toString());
+ getContext().getContentResolver().notifyChange(url, null);
/*
// notify interested parties that an insertion happened
getContext().getContentResolver().notifyInsert(
@@ -164,6 +236,11 @@
private String normalizeValue(String inVal) {
int len = inVal.length();
+ // If name is empty in contact return null to avoid crash.
+ if (len == 0) {
+ if (DBG) log("len of input String is 0");
+ return inVal;
+ }
String retVal = inVal;
if (inVal.charAt(0) == '\'' && inVal.charAt(len-1) == '\'') {
@@ -176,17 +253,28 @@
@Override
public int delete(Uri url, String where, String[] whereArgs) {
int efType;
-
- if (DBG) log("delete");
+ long subId;
int match = URL_MATCHER.match(url);
switch (match) {
case ADN:
efType = IccConstants.EF_ADN;
+ subId = SubscriptionManager.getDefaultSubId();
+ break;
+
+ case ADN_SUB:
+ efType = IccConstants.EF_ADN;
+ subId = getRequestSubId(url);
break;
case FDN:
efType = IccConstants.EF_FDN;
+ subId = SubscriptionManager.getDefaultSubId();
+ break;
+
+ case FDN_SUB:
+ efType = IccConstants.EF_FDN;
+ subId = getRequestSubId(url);
break;
default:
@@ -194,6 +282,8 @@
"Cannot insert into URL: " + url);
}
+ if (DBG) log("delete");
+
// parse where clause
String tag = null;
String number = null;
@@ -207,8 +297,12 @@
String param = tokens[n];
if (DBG) log("parsing '" + param + "'");
- String[] pair = param.split("=", 2);
+ String[] pair = param.split("=");
+ if (pair.length != 2) {
+ Rlog.e(TAG, "resolve: bad whereClause parameter: " + param);
+ continue;
+ }
String key = pair[0].trim();
String val = pair[1].trim();
@@ -224,26 +318,24 @@
}
}
- if (TextUtils.isEmpty(number)) {
+ if (efType == FDN && TextUtils.isEmpty(pin2)) {
return 0;
}
- if (efType == IccConstants.EF_FDN && TextUtils.isEmpty(pin2)) {
- return 0;
- }
-
- boolean success = deleteIccRecordFromEf(efType, tag, number, emails, pin2);
+ boolean success = deleteIccRecordFromEf(efType, tag, number, emails, pin2, subId);
if (!success) {
return 0;
}
+ getContext().getContentResolver().notifyChange(url, null);
return 1;
}
@Override
public int update(Uri url, ContentValues values, String where, String[] whereArgs) {
- int efType;
String pin2 = null;
+ int efType;
+ long subId;
if (DBG) log("update");
@@ -251,10 +343,23 @@
switch (match) {
case ADN:
efType = IccConstants.EF_ADN;
+ subId = SubscriptionManager.getDefaultSubId();
+ break;
+
+ case ADN_SUB:
+ efType = IccConstants.EF_ADN;
+ subId = getRequestSubId(url);
break;
case FDN:
efType = IccConstants.EF_FDN;
+ subId = SubscriptionManager.getDefaultSubId();
+ pin2 = values.getAsString("pin2");
+ break;
+
+ case FDN_SUB:
+ efType = IccConstants.EF_FDN;
+ subId = getRequestSubId(url);
pin2 = values.getAsString("pin2");
break;
@@ -271,24 +376,25 @@
String[] newEmails = null;
// TODO(): Update for email.
boolean success = updateIccRecordInEf(efType, tag, number,
- newTag, newNumber, pin2);
+ newTag, newNumber, pin2, subId);
if (!success) {
return 0;
}
+ getContext().getContentResolver().notifyChange(url, null);
return 1;
}
- private MatrixCursor loadFromEf(int efType) {
- if (DBG) log("loadFromEf: efType=" + efType);
+ private MatrixCursor loadFromEf(int efType, long subId) {
+ if (DBG) log("loadFromEf: efType=" + efType + ", subscription=" + subId);
List<AdnRecord> adnRecords = null;
try {
IIccPhoneBook iccIpb = IIccPhoneBook.Stub.asInterface(
ServiceManager.getService("simphonebook"));
if (iccIpb != null) {
- adnRecords = iccIpb.getAdnRecordsInEf(efType);
+ adnRecords = iccIpb.getAdnRecordsInEfUsingSubId(subId, efType);
}
} catch (RemoteException ex) {
// ignore it
@@ -313,9 +419,10 @@
}
private boolean
- addIccRecordToEf(int efType, String name, String number, String[] emails, String pin2) {
+ addIccRecordToEf(int efType, String name, String number, String[] emails,
+ String pin2, long subId) {
if (DBG) log("addIccRecordToEf: efType=" + efType + ", name=" + name +
- ", number=" + number + ", emails=" + emails);
+ ", number=" + number + ", emails=" + emails + ", subscription=" + subId);
boolean success = false;
@@ -328,8 +435,8 @@
IIccPhoneBook iccIpb = IIccPhoneBook.Stub.asInterface(
ServiceManager.getService("simphonebook"));
if (iccIpb != null) {
- success = iccIpb.updateAdnRecordsInEfBySearch(efType, "", "",
- name, number, pin2);
+ success = iccIpb.updateAdnRecordsInEfBySearchUsingSubId(subId, efType,
+ "", "", name, number, pin2);
}
} catch (RemoteException ex) {
// ignore it
@@ -342,18 +449,20 @@
private boolean
updateIccRecordInEf(int efType, String oldName, String oldNumber,
- String newName, String newNumber, String pin2) {
+ String newName, String newNumber, String pin2, long subId) {
if (DBG) log("updateIccRecordInEf: efType=" + efType +
", oldname=" + oldName + ", oldnumber=" + oldNumber +
- ", newname=" + newName + ", newnumber=" + newNumber);
+ ", newname=" + newName + ", newnumber=" + newNumber +
+ ", subscription=" + subId);
+
boolean success = false;
try {
IIccPhoneBook iccIpb = IIccPhoneBook.Stub.asInterface(
ServiceManager.getService("simphonebook"));
if (iccIpb != null) {
- success = iccIpb.updateAdnRecordsInEfBySearch(efType,
- oldName, oldNumber, newName, newNumber, pin2);
+ success = iccIpb.updateAdnRecordsInEfBySearchUsingSubId(subId, efType, oldName,
+ oldNumber, newName, newNumber, pin2);
}
} catch (RemoteException ex) {
// ignore it
@@ -366,9 +475,10 @@
private boolean deleteIccRecordFromEf(int efType, String name, String number, String[] emails,
- String pin2) {
+ String pin2, long subId) {
if (DBG) log("deleteIccRecordFromEf: efType=" + efType +
- ", name=" + name + ", number=" + number + ", emails=" + emails + ", pin2=" + pin2);
+ ", name=" + name + ", number=" + number + ", emails=" + emails +
+ ", pin2=" + pin2 + ", subscription=" + subId);
boolean success = false;
@@ -376,8 +486,8 @@
IIccPhoneBook iccIpb = IIccPhoneBook.Stub.asInterface(
ServiceManager.getService("simphonebook"));
if (iccIpb != null) {
- success = iccIpb.updateAdnRecordsInEfBySearch(efType,
- name, number, "", "", pin2);
+ success = iccIpb.updateAdnRecordsInEfBySearchUsingSubId(subId, efType,
+ name, number, "", "", pin2);
}
} catch (RemoteException ex) {
// ignore it
@@ -423,4 +533,13 @@
Rlog.d(TAG, "[IccProvider] " + msg);
}
+ private long getRequestSubId(Uri url) {
+ if (DBG) log("getRequestSubId url: " + url);
+
+ try {
+ return Long.parseLong(url.getLastPathSegment());
+ } catch (NumberFormatException ex) {
+ throw new IllegalArgumentException("Unknown URL " + url);
+ }
+ }
}
diff --git a/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java b/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java
index 938fd38..acef542 100644
--- a/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java
+++ b/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java
@@ -47,7 +47,7 @@
* IccSmsInterfaceManager to provide an inter-process communication to
* access Sms in Icc.
*/
-public class IccSmsInterfaceManager extends ISms.Stub {
+public class IccSmsInterfaceManager {
static final String LOG_TAG = "IccSmsInterfaceManager";
static final boolean DBG = true;
@@ -120,9 +120,6 @@
mAppOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
mDispatcher = new ImsSMSDispatcher(phone,
phone.mSmsStorageMonitor, phone.mSmsUsageMonitor);
- if (ServiceManager.getService("isms") == null) {
- ServiceManager.addService("isms", this);
- }
}
protected void markMessagesAsRead(ArrayList<byte[]> messages) {
@@ -181,7 +178,7 @@
* @return success or not
*
*/
- @Override
+
public boolean
updateMessageOnIccEf(String callingPackage, int index, int status, byte[] pdu) {
if (DBG) log("updateMessageOnIccEf: index=" + index +
@@ -236,7 +233,6 @@
* @return success or not
*
*/
- @Override
public boolean copyMessageToIccEf(String callingPackage, int status, byte[] pdu, byte[] smsc) {
//NOTE smsc not used in RUIM
if (DBG) log("copyMessageToIccEf: status=" + status + " ==> " +
@@ -274,7 +270,7 @@
*
* @return list of SmsRawData of all sms on Icc
*/
- @Override
+
public List<SmsRawData> getAllMessagesFromIccEf(String callingPackage) {
if (DBG) log("getAllMessagesFromEF");
@@ -333,7 +329,7 @@
* broadcast when the message is delivered to the recipient. The
* raw pdu of the status report is in the extended data ("pdu").
*/
- @Override
+
public void sendData(String callingPackage, String destAddr, String scAddr, int destPort,
byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) {
mPhone.getContext().enforceCallingPermission(
@@ -375,7 +371,7 @@
* broadcast when the message is delivered to the recipient. The
* raw pdu of the status report is in the extended data ("pdu").
*/
- @Override
+
public void sendText(String callingPackage, String destAddr, String scAddr,
String text, PendingIntent sentIntent, PendingIntent deliveryIntent) {
mPhone.getContext().enforceCallingPermission(
@@ -418,7 +414,7 @@
* to the recipient. The raw pdu of the status report is in the
* extended data ("pdu").
*/
- @Override
+
public void sendMultipartText(String callingPackage, String destAddr, String scAddr,
List<String> parts, List<PendingIntent> sentIntents,
List<PendingIntent> deliveryIntents) {
@@ -440,12 +436,12 @@
(ArrayList<PendingIntent>) sentIntents, (ArrayList<PendingIntent>) deliveryIntents);
}
- @Override
+
public int getPremiumSmsPermission(String packageName) {
return mDispatcher.getPremiumSmsPermission(packageName);
}
- @Override
+
public void setPremiumSmsPermission(String packageName, int permission) {
mDispatcher.setPremiumSmsPermission(packageName, permission);
}
diff --git a/src/java/com/android/internal/telephony/InboundSmsHandler.java b/src/java/com/android/internal/telephony/InboundSmsHandler.java
index 95a650f..709bfc2 100644
--- a/src/java/com/android/internal/telephony/InboundSmsHandler.java
+++ b/src/java/com/android/internal/telephony/InboundSmsHandler.java
@@ -37,8 +37,10 @@
import android.provider.Telephony.Sms.Intents;
import android.telephony.Rlog;
import android.telephony.SmsMessage;
+import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
+import com.android.internal.telephony.PhoneBase;
import com.android.internal.util.HexDump;
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
@@ -219,6 +221,11 @@
}
}
+ // CAF_MSIM Is this used anywhere ? if not remove it
+ public PhoneBase getPhone() {
+ return mPhone;
+ }
+
/**
* This parent state throws an exception (for debug builds) or prints an error for unhandled
* message types.
@@ -699,9 +706,10 @@
* @param permission receivers are required to have this permission
* @param appOp app op that is being performed when dispatching to a receiver
*/
- void dispatchIntent(Intent intent, String permission, int appOp,
+ protected void dispatchIntent(Intent intent, String permission, int appOp,
BroadcastReceiver resultReceiver) {
intent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT);
+ SubscriptionManager.putPhoneIdAndSubIdExtra(intent, mPhone.getPhoneId());
mContext.sendOrderedBroadcast(intent, permission, appOp, resultReceiver,
getHandler(), Activity.RESULT_OK, null, null);
}
diff --git a/src/java/com/android/internal/telephony/MccTable.java b/src/java/com/android/internal/telephony/MccTable.java
index a0c8349..c303219 100644
--- a/src/java/com/android/internal/telephony/MccTable.java
+++ b/src/java/com/android/internal/telephony/MccTable.java
@@ -24,6 +24,7 @@
import android.os.Build;
import android.os.RemoteException;
import android.os.SystemProperties;
+import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Slog;
@@ -175,9 +176,18 @@
*/
public static void updateMccMncConfiguration(Context context, String mccmnc,
boolean fromServiceState) {
+ Slog.d(LOG_TAG, "updateMccMncConfiguration mccmnc='" + mccmnc + "' fromServiceState=" + fromServiceState);
if (!TextUtils.isEmpty(mccmnc)) {
int mcc, mnc;
+ String defaultMccMnc = TelephonyManager.getDefault().getSimOperator();
+ Slog.d(LOG_TAG, "updateMccMncConfiguration defaultMccMnc=" + defaultMccMnc);
+ //Update mccmnc only for default subscription in case of MultiSim.
+// if (!defaultMccMnc.equals(mccmnc)) {
+// Slog.d(LOG_TAG, "Not a Default subscription, ignoring mccmnc config update.");
+// return;
+// }
+
try {
mcc = Integer.parseInt(mccmnc.substring(0,3));
mnc = Integer.parseInt(mccmnc.substring(3));
@@ -237,7 +247,8 @@
* @return Locale or null if no appropriate value
* {@hide}
*/
- public static Locale getLocaleForLanguageCountry(Context context, String language, String country) {
+ public static Locale getLocaleForLanguageCountry(Context context, String language,
+ String country) {
String l = SystemProperties.get("persist.sys.language");
String c = SystemProperties.get("persist.sys.country");
diff --git a/src/java/com/android/internal/telephony/MmiCode.java b/src/java/com/android/internal/telephony/MmiCode.java
index c71ff77..feab370 100644
--- a/src/java/com/android/internal/telephony/MmiCode.java
+++ b/src/java/com/android/internal/telephony/MmiCode.java
@@ -45,6 +45,11 @@
public CharSequence getMessage();
/**
+ * @return Phone associated with the MMI/USSD message
+ */
+ public Phone getPhone();
+
+ /**
* Cancels pending MMI request.
* State becomes CANCELLED unless already COMPLETE or FAILED
*/
diff --git a/src/java/com/android/internal/telephony/Phone.java b/src/java/com/android/internal/telephony/Phone.java
index 5b7b005..706332c 100644
--- a/src/java/com/android/internal/telephony/Phone.java
+++ b/src/java/com/android/internal/telephony/Phone.java
@@ -29,6 +29,7 @@
import com.android.internal.telephony.test.SimulatedRadioControl;
import com.android.internal.telephony.uicc.IsimRecords;
+import com.android.internal.telephony.uicc.UiccCard;
import com.android.internal.telephony.uicc.UsimServiceTable;
import com.android.internal.telephony.PhoneConstants.*; // ????
@@ -104,6 +105,7 @@
static final String REASON_LOST_DATA_CONNECTION = "lostDataConnection";
static final String REASON_CONNECTED = "connected";
static final String REASON_SINGLE_PDN_ARBITRATION = "SinglePdnArbitration";
+ static final String REASON_DATA_SPECIFIC_DISABLED = "specificDisabled";
// Used for band mode selection methods
static final int BM_UNSPECIFIED = 0; // selected by baseband automatically
@@ -384,6 +386,23 @@
void unregisterForRingbackTone(Handler h);
/**
+ * Notifies when out-band on-hold tone is needed.<p>
+ *
+ * Messages received from this:
+ * Message.obj will be an AsyncResult
+ * AsyncResult.userObj = obj
+ * AsyncResult.result = boolean, true to start play on-hold tone
+ * and false to stop. <p>
+ */
+ void registerForOnHoldTone(Handler h, int what, Object obj);
+
+ /**
+ * Unregisters for on-hold tone notification.
+ */
+
+ void unregisterForOnHoldTone(Handler h);
+
+ /**
* Registers the handler to reset the uplink mute state to get
* uplink audio.
*/
@@ -592,6 +611,20 @@
public void unregisterForSubscriptionInfoReady(Handler h);
/**
+ * Registration point for Sim records loaded
+ * @param h handler to notify
+ * @param what what code of message when delivered
+ * @param obj placed in Message.obj
+ */
+ public void registerForSimRecordsLoaded(Handler h, int what, Object obj);
+
+ /**
+ * Unregister for notifications for Sim records loaded
+ * @param h Handler to be removed from the registrant list.
+ */
+ public void unregisterForSimRecordsLoaded(Handler h);
+
+ /**
* Returns SIM record load state. Use
* <code>getSimCard().registerForReady()</code> for change notification.
*
@@ -1693,16 +1726,6 @@
IsimRecords getIsimRecords();
/**
- * Request the ISIM application on the UICC to perform the AKA
- * challenge/response algorithm for IMS authentication. The nonce string
- * and challenge response are Base64 encoded Strings.
- *
- * @param nonce the nonce string to pass with the ISIM authentication request
- * @param response a callback message with the String response in the obj field
- */
- void requestIsimAuthentication(String nonce, Message response);
-
- /**
* Sets the SIM voice message waiting indicator records.
* @param line GSM Subscriber Profile Number, one-based. Only '1' is supported
* @param countWaiting The number of messages waiting, if known. Use
@@ -1718,6 +1741,12 @@
UsimServiceTable getUsimServiceTable();
/**
+ * Gets the Uicc card corresponding to this phone.
+ * @return the UiccCard object corresponding to the phone ID.
+ */
+ UiccCard getUiccCard();
+
+ /**
* Unregister from all events it registered for and dispose objects
* created by this object.
*/
@@ -1772,4 +1801,36 @@
* @param response Callback message.
*/
void nvResetConfig(int resetType, Message response);
+
+ /*
+ * Returns the subscription id.
+ */
+ public long getSubId();
+
+ /*
+ * Returns the phone id.
+ */
+ public int getPhoneId();
+
+ /**
+ * Get P-CSCF address from PCO after data connection is established or modified.
+ */
+ public String[] getPcscfAddress();
+
+ /**
+ * Set IMS registration state
+ */
+ public void setImsRegistrationState(boolean registered);
+
+ /**
+ * Return an instance of a ImsPhone phone
+ * @return an interface of a ImsPhone phone
+ */
+ Phone getVoicePhone();
+
+ /**
+ * Return the service state of mVoicePhone if it is STATE_IN_SERVICE
+ * otherwise return the current voice service state
+ */
+ int getVoiceServiceState();
}
diff --git a/src/java/com/android/internal/telephony/PhoneBase.java b/src/java/com/android/internal/telephony/PhoneBase.java
index 2955880..a46934f 100644
--- a/src/java/com/android/internal/telephony/PhoneBase.java
+++ b/src/java/com/android/internal/telephony/PhoneBase.java
@@ -16,7 +16,10 @@
package com.android.internal.telephony;
+import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.net.LinkProperties;
import android.net.NetworkCapabilities;
@@ -34,11 +37,13 @@
import android.telephony.CellInfo;
import android.telephony.CellInfoCdma;
import android.telephony.DataConnectionRealTimeInfo;
+import android.telephony.VoLteServiceState;
import android.telephony.Rlog;
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
import android.text.TextUtils;
+import com.android.ims.ImsManager;
import com.android.internal.R;
import com.android.internal.telephony.dataconnection.DcTrackerBase;
import com.android.internal.telephony.test.SimulatedRadioControl;
@@ -46,6 +51,7 @@
import com.android.internal.telephony.uicc.IccFileHandler;
import com.android.internal.telephony.uicc.IccRecords;
import com.android.internal.telephony.uicc.IsimRecords;
+import com.android.internal.telephony.uicc.UiccCard;
import com.android.internal.telephony.uicc.UiccCardApplication;
import com.android.internal.telephony.uicc.UiccController;
import com.android.internal.telephony.uicc.UsimServiceTable;
@@ -56,6 +62,7 @@
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
+import static com.android.internal.telephony.PhoneConstants.DEFAULT_SUBSCRIPTION;
/**
* (<em>Not for SDK use</em>)
@@ -72,6 +79,19 @@
public abstract class PhoneBase extends Handler implements Phone {
private static final String LOG_TAG = "PhoneBase";
+ private BroadcastReceiver mImsIntentReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent.getAction().equals(ImsManager.ACTION_IMS_SERVICE_UP)) {
+ mImsServiceReady = true;
+ updateVoicePhone();
+ } else if (intent.getAction().equals(ImsManager.ACTION_IMS_SERVICE_DOWN)) {
+ mImsServiceReady = false;
+ updateVoicePhone();
+ }
+ }
+ };
+
// Key used to read and write the saved network selection numeric value
public static final String NETWORK_SELECTION_KEY = "network_selection_key";
// Key used to read and write the saved network selection operator name
@@ -118,6 +138,10 @@
protected static final int EVENT_SET_NETWORK_AUTOMATIC = 28;
protected static final int EVENT_ICC_RECORD_EVENTS = 29;
protected static final int EVENT_ICC_CHANGED = 30;
+ // Single Radio Voice Call Continuity
+ protected static final int EVENT_SRVCC_STATE_CHANGED = 31;
+ protected static final int EVENT_INITIATE_SILENT_REDIAL = 32;
+ protected static final int EVENT_LAST = EVENT_INITIATE_SILENT_REDIAL;
// Key used to read/write current CLIR setting
public static final String CLIR_KEY = "clir_key";
@@ -158,6 +182,13 @@
private final String mActionDetached;
private final String mActionAttached;
+ // Holds the subscription information
+ protected Subscription mSubscriptionData = null;
+ protected int mPhoneId;
+
+ protected boolean mImsServiceReady = false;
+ protected Phone mVoicePhone = null;
+
@Override
public String getPhoneName() {
return mName;
@@ -188,6 +219,7 @@
/**
* Set a system property, unless we're in unit test mode
*/
+ // CAF_MSIM TODO this need to be replated with TelephonyManager API ?
public void setSystemProperty(String property, String value) {
if(getUnitTestMode()) {
return;
@@ -223,6 +255,9 @@
protected final RegistrantList mSuppServiceFailedRegistrants
= new RegistrantList();
+ protected final RegistrantList mSimRecordsLoadedRegistrants
+ = new RegistrantList();
+
protected Looper mLooper; /* to insure registrants are in correct thread*/
protected final Context mContext;
@@ -262,6 +297,23 @@
*/
protected PhoneBase(String name, PhoneNotifier notifier, Context context, CommandsInterface ci,
boolean unitTestMode) {
+ this(name, notifier, context, ci, unitTestMode, DEFAULT_SUBSCRIPTION);
+ }
+
+ /**
+ * Constructs a PhoneBase in normal (non-unit test) mode.
+ *
+ * @param notifier An instance of DefaultPhoneNotifier,
+ * @param context Context object from hosting application
+ * unless unit testing.
+ * @param ci is CommandsInterface
+ * @param unitTestMode when true, prevents notifications
+ * of state change events
+ * @param subscription is current phone subscription
+ */
+ protected PhoneBase(String name, PhoneNotifier notifier, Context context, CommandsInterface ci,
+ boolean unitTestMode, int phoneId) {
+ mPhoneId = phoneId;
mName = name;
mNotifier = notifier;
mContext = context;
@@ -274,8 +326,6 @@
mTelephonyTester = new TelephonyTester(this);
}
- setPropertiesByCarrier();
-
setUnitTestMode(unitTestMode);
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
@@ -309,16 +359,27 @@
TelephonyProperties.PROPERTY_CALL_RING_DELAY, 3000);
Rlog.d(LOG_TAG, "mCallRingDelay=" + mCallRingDelay);
+ if (getPhoneType() == PhoneConstants.PHONE_TYPE_IMS) return;
+
+ setPropertiesByCarrier();
+
// Initialize device storage and outgoing SMS usage monitors for SMSDispatchers.
mSmsStorageMonitor = new SmsStorageMonitor(this);
mSmsUsageMonitor = new SmsUsageMonitor(context);
mUiccController = UiccController.getInstance();
mUiccController.registerForIccChanged(this, EVENT_ICC_CHANGED, null);
+
+ // Monitor IMS service
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(ImsManager.ACTION_IMS_SERVICE_UP);
+ filter.addAction(ImsManager.ACTION_IMS_SERVICE_DOWN);
+ mContext.registerReceiver(mImsIntentReceiver, filter);
}
@Override
public void dispose() {
synchronized(PhoneProxy.lockForRadioTechnologyChange) {
+ mContext.unregisterReceiver(mImsIntentReceiver);
mCi.unSetOnCallRing(this);
// Must cleanup all connectionS and needs to use sendMessage!
mDcTracker.cleanUpAllConnections(null);
@@ -331,6 +392,11 @@
if (mTelephonyTester != null) {
mTelephonyTester.dispose();
}
+
+ if (mVoicePhone != null) {
+ ((VoicePhone)mVoicePhone).unregisterForSilentRedial(this);
+ mVoicePhone.dispose();
+ }
}
}
@@ -342,6 +408,11 @@
mUiccApplication.set(null);
mDcTracker = null;
mUiccController = null;
+
+ if (mVoicePhone != null) {
+ mVoicePhone.removeReferences();
+ mVoicePhone = null;
+ }
}
/**
@@ -394,6 +465,20 @@
handleSetSelectNetwork((AsyncResult) msg.obj);
break;
+ case EVENT_INITIATE_SILENT_REDIAL:
+ Rlog.d(LOG_TAG, "Event EVENT_INITIATE_SILENT_REDIAL Received");
+ ar = (AsyncResult) msg.obj;
+ if ((ar.exception == null) && (ar.result != null)) {
+ String dialString = (String) ar.result;
+ if (TextUtils.isEmpty(dialString)) return;
+ try {
+ dialInternal(dialString, null);
+ } catch (CallStateException e) {
+ Rlog.e(LOG_TAG, "silent redial failed: " + e);
+ }
+ }
+ break;
+
default:
throw new RuntimeException("unexpected event not handled");
}
@@ -581,6 +666,14 @@
mMmiCompleteRegistrants.remove(h);
}
+ public void registerForSimRecordsLoaded(Handler h, int what, Object obj) {
+ logUnexpectedCdmaMethodCall("registerForSimRecordsLoaded");
+ }
+
+ public void unregisterForSimRecordsLoaded(Handler h) {
+ logUnexpectedCdmaMethodCall("unregisterForSimRecordsLoaded");
+ }
+
@Override
public void setNetworkSelectionModeAutomatic(Message response) {
// wrap the response message in our own message along with
@@ -719,6 +812,16 @@
// Inherited documentation suffices.
@Override
+ public void registerForOnHoldTone(Handler h, int what, Object obj) {
+ }
+
+ // Inherited documentation suffices.
+ @Override
+ public void unregisterForOnHoldTone(Handler h) {
+ }
+
+ // Inherited documentation suffices.
+ @Override
public void registerForResendIncallMute(Handler h, int what, Object obj) {
mCi.registerForResendIncallMute(h, what, obj);
}
@@ -1105,6 +1208,10 @@
mNotifier.notifyDataConnectionRealTimeInfo(this, dcRtInfo);
}
+ public void notifyVoLteServiceStateChanged(VoLteServiceState lteState) {
+ mNotifier.notifyVoLteServiceStateChanged(this, lteState);
+ }
+
/**
* @return true if a mobile originating emergency call is active
*/
@@ -1437,11 +1544,6 @@
}
@Override
- public void requestIsimAuthentication(String nonce, Message result) {
- Rlog.e(LOG_TAG, "requestIsimAuthentication() is only supported on LTE devices");
- }
-
- @Override
public String getMsisdn() {
logUnexpectedGsmMethodCall("getMsisdn");
return null;
@@ -1517,6 +1619,64 @@
return (r != null) ? r.getUsimServiceTable() : null;
}
+ /**
+ * Gets the Uicc card corresponding to this phone.
+ * @return the UiccCard object corresponding to the phone ID.
+ */
+ @Override
+ public UiccCard getUiccCard() {
+ return mUiccController.getUiccCard(mPhoneId);
+ }
+
+ /**
+ * Get P-CSCF address from PCO after data connection is established or modified.
+ */
+ @Override
+ public String[] getPcscfAddress() {
+ return mDcTracker.getPcscfAddress();
+ }
+
+ /**
+ * Set IMS registration state
+ */
+ @Override
+ public void setImsRegistrationState(boolean registered) {
+ mDcTracker.setImsRegistrationState(registered);
+ }
+
+ /**
+ * Return an instance of a IMS phone
+ */
+ @Override
+ public Phone getVoicePhone() {
+ return mVoicePhone;
+ }
+
+ protected void updateVoicePhone() {
+ Rlog.d(LOG_TAG, "updateVoicePhone"
+ + " mImsServiceReady=" + mImsServiceReady);
+
+ if (mImsServiceReady && (mVoicePhone == null)) {
+ mVoicePhone = PhoneFactory.makeImsPhone(mNotifier, this);
+ CallManager.getInstance().registerPhone(mVoicePhone);
+ ((VoicePhone)mVoicePhone).registerForSilentRedial(
+ this, EVENT_INITIATE_SILENT_REDIAL, null);
+ } else if (!mImsServiceReady && (mVoicePhone != null)) {
+ CallManager.getInstance().unregisterPhone(mVoicePhone);
+ ((VoicePhone)mVoicePhone).unregisterForSilentRedial(this);
+
+ mVoicePhone.dispose();
+ mVoicePhone.removeReferences();
+ mVoicePhone = null;
+ }
+ }
+
+ protected Connection dialInternal(String dialString, UUSInfo uusInfo)
+ throws CallStateException {
+ // dialInternal shall be overriden by GSMPhone and CDMAPhone
+ return null;
+ }
+
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("PhoneBase:");
pw.println(" mCi=" + mCi);
@@ -1554,4 +1714,37 @@
pw.println(" isDataConnectivityPossible()=" + isDataConnectivityPossible());
pw.println(" needsOtaServiceProvisioning=" + needsOtaServiceProvisioning());
}
+
+ /**
+ * Returns the subscription id.
+ */
+ public long getSubId() {
+ long [] subId = SubscriptionController.getInstance().getSubId(mPhoneId);
+ return subId[0];
+ }
+
+ /**
+ * Returns the phone id.
+ */
+ public int getPhoneId() {
+ return mPhoneId;
+ }
+
+ //Gets Subscription information in the Phone Object
+ public Subscription getSubscriptionInfo() {
+ return mSubscriptionData;
+ }
+
+ /**
+ * Return the service state of mVoicePhone if it is STATE_IN_SERVICE
+ * otherwise return the current voice service state
+ */
+ @Override
+ public int getVoiceServiceState() {
+ if (mVoicePhone != null
+ && mVoicePhone.getServiceState().getState() == ServiceState.STATE_IN_SERVICE) {
+ return ServiceState.STATE_IN_SERVICE;
+ }
+ return getServiceState().getState();
+ }
}
diff --git a/src/java/com/android/internal/telephony/PhoneFactory.java b/src/java/com/android/internal/telephony/PhoneFactory.java
index 33b21d4..5d20052 100644
--- a/src/java/com/android/internal/telephony/PhoneFactory.java
+++ b/src/java/com/android/internal/telephony/PhoneFactory.java
@@ -18,10 +18,15 @@
import android.content.ComponentName;
import android.content.Context;
+import android.content.Intent;
import android.net.LocalServerSocket;
import android.os.Looper;
+import android.os.SystemProperties;
+import android.os.UserHandle;
import android.provider.Settings;
+import android.provider.Settings.SettingNotFoundException;
import android.telephony.Rlog;
+import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import com.android.internal.telephony.cdma.CDMALTEPhone;
@@ -31,6 +36,9 @@
import com.android.internal.telephony.sip.SipPhone;
import com.android.internal.telephony.sip.SipPhoneFactory;
import com.android.internal.telephony.uicc.UiccController;
+import com.android.internal.telephony.imsphone.ImsPhoneFactory;
+
+import static com.android.internal.telephony.TelephonyProperties.PROPERTY_DEFAULT_SUBSCRIPTION;
/**
* {@hide}
@@ -42,8 +50,15 @@
//***** Class Variables
+ static private Phone[] sProxyPhones = null;
+ static private CommandsInterface[] sCommandsInterfaces = null;
+
+ static private ProxyController mProxyController;
+ static private UiccController mUiccController;
+
static private Phone sProxyPhone = null;
static private CommandsInterface sCommandsInterface = null;
+ static private SubInfoRecordUpdater sSubInfoRecordUpdater = null;
static private boolean sMadeDefaults = false;
static private PhoneNotifier sPhoneNotifier;
@@ -71,6 +86,9 @@
"PhoneFactory.makeDefaultPhone must be called from Looper thread");
}
+ // create the telephony device controller.
+ TelephonyDevController.create();
+
int retryCount = 0;
for(;;) {
boolean hasException = false;
@@ -98,39 +116,71 @@
sPhoneNotifier = new DefaultPhoneNotifier();
- // Get preferred network type.
- int networkType = calculatePreferredNetworkType(context);
- Rlog.i(LOG_TAG, "Network Type set to " + Integer.toString(networkType));
+ // Get preferred network mode
+ int preferredNetworkMode = RILConstants.PREFERRED_NETWORK_MODE;
+ if (TelephonyManager.getLteOnCdmaModeStatic() == PhoneConstants.LTE_ON_CDMA_TRUE) {
+ preferredNetworkMode = Phone.NT_MODE_GLOBAL;
+ }
int cdmaSubscription = CdmaSubscriptionSourceManager.getDefault(context);
Rlog.i(LOG_TAG, "Cdma Subscription set to " + cdmaSubscription);
- //reads the system properties and makes commandsinterface
- sCommandsInterface = new RIL(context, networkType, cdmaSubscription);
+ /* In case of multi SIM mode two instances of PhoneProxy, RIL are created,
+ where as in single SIM mode only instance. isMultiSimEnabled() function checks
+ whether it is single SIM or multi SIM mode */
+ int numPhones = TelephonyManager.getDefault().getPhoneCount();
+ int[] networkModes = new int[numPhones];
+ sProxyPhones = new PhoneProxy[numPhones];
+ sCommandsInterfaces = new RIL[numPhones];
- // Instantiate UiccController so that all other classes can just call getInstance()
- UiccController.make(context, sCommandsInterface);
+ for (int i = 0; i < numPhones; i++) {
+ //reads the system properties and makes commandsinterface
+ try {
+// // Get preferred network type.
+// TODO: Sishir added this code to but we need a new technique for MSim
+// int networkType = calculatePreferredNetworkType(context);
+// Rlog.i(LOG_TAG, "Network Type set to " + Integer.toString(networkType));
- int phoneType = TelephonyManager.getPhoneType(networkType);
- if (phoneType == PhoneConstants.PHONE_TYPE_GSM) {
- Rlog.i(LOG_TAG, "Creating GSMPhone");
- sProxyPhone = new PhoneProxy(new GSMPhone(context,
- sCommandsInterface, sPhoneNotifier));
- } else if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
- switch (TelephonyManager.getLteOnCdmaModeStatic()) {
- case PhoneConstants.LTE_ON_CDMA_TRUE:
- Rlog.i(LOG_TAG, "Creating CDMALTEPhone");
- sProxyPhone = new PhoneProxy(new CDMALTEPhone(context,
- sCommandsInterface, sPhoneNotifier));
- break;
- case PhoneConstants.LTE_ON_CDMA_FALSE:
- default:
- Rlog.i(LOG_TAG, "Creating CDMAPhone");
- sProxyPhone = new PhoneProxy(new CDMAPhone(context,
- sCommandsInterface, sPhoneNotifier));
- break;
+ networkModes[i] = TelephonyManager.getIntAtIndex(
+ context.getContentResolver(),
+ Settings.Global.PREFERRED_NETWORK_MODE, i);
+ } catch (SettingNotFoundException snfe) {
+ Rlog.e(LOG_TAG, "Settings Exception Reading Value At Index for"+
+ " Settings.Global.PREFERRED_NETWORK_MODE");
+ networkModes[i] = preferredNetworkMode;
}
+
+ Rlog.i(LOG_TAG, "Network Mode set to " + Integer.toString(networkModes[i]));
+ sCommandsInterfaces[i] = new RIL(context, networkModes[i],
+ cdmaSubscription, i);
}
+ Rlog.i(LOG_TAG, "Creating SubscriptionController");
+ SubscriptionController.init(context, sCommandsInterfaces);
+
+ // Instantiate UiccController so that all other classes can just
+ // call getInstance()
+ mUiccController = UiccController.make(context, sCommandsInterfaces);
+
+ for (int i = 0; i < numPhones; i++) {
+ PhoneBase phone = null;
+ int phoneType = TelephonyManager.getPhoneType(networkModes[i]);
+ if (phoneType == PhoneConstants.PHONE_TYPE_GSM) {
+ phone = new GSMPhone(context,
+ sCommandsInterfaces[i], sPhoneNotifier, i);
+ } else if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
+ phone = new CDMALTEPhone(context,
+ sCommandsInterfaces[i], sPhoneNotifier, i);
+ }
+ Rlog.i(LOG_TAG, "Creating Phone with type = " + phoneType + " sub = " + i);
+
+ sProxyPhones[i] = new PhoneProxy(phone);
+ }
+ mProxyController = ProxyController.getInstance(context, sProxyPhones,
+ mUiccController, sCommandsInterfaces);
+
+ // Set the default phone in base class
+ sProxyPhone = sProxyPhones[PhoneConstants.DEFAULT_SUBSCRIPTION];
+ sCommandsInterface = sCommandsInterfaces[PhoneConstants.DEFAULT_SUBSCRIPTION];
// Ensure that we have a default SMS app. Requesting the app with
// updateIfNeeded set to true is enough to configure a default SMS app.
@@ -146,10 +196,31 @@
SmsApplication.initSmsPackageMonitor(context);
sMadeDefaults = true;
+
+ Rlog.i(LOG_TAG, "Creating SubInfoRecordUpdater ");
+ sSubInfoRecordUpdater = new SubInfoRecordUpdater(context,
+ sProxyPhones, sCommandsInterfaces);
}
}
}
+ public static Phone getCdmaPhone(int phoneId) {
+ Phone phone;
+ synchronized(PhoneProxy.lockForRadioTechnologyChange) {
+ phone = new CDMALTEPhone(sContext, sCommandsInterfaces[phoneId],
+ sPhoneNotifier, phoneId);
+ }
+ return phone;
+ }
+
+ public static Phone getGsmPhone(int phoneId) {
+ synchronized(PhoneProxy.lockForRadioTechnologyChange) {
+ Phone phone = new GSMPhone(sContext, sCommandsInterfaces[phoneId],
+ sPhoneNotifier, phoneId);
+ return phone;
+ }
+ }
+
public static Phone getDefaultPhone() {
if (sLooper != Looper.myLooper()) {
throw new RuntimeException(
@@ -162,6 +233,31 @@
return sProxyPhone;
}
+ public static Phone getPhone(int phoneId) {
+ if (sLooper != Looper.myLooper()) {
+ throw new RuntimeException(
+ "PhoneFactory.getPhone must be called from Looper thread");
+ }
+ if (!sMadeDefaults) {
+ throw new IllegalStateException("Default phones haven't been made yet!");
+ // CAF_MSIM FIXME need to introduce default phone id ?
+ } else if (phoneId == PhoneConstants.DEFAULT_SUBSCRIPTION) {
+ return sProxyPhone;
+ }
+ return sProxyPhones[phoneId];
+ }
+
+ public static Phone [] getPhones() {
+ if (sLooper != Looper.myLooper()) {
+ throw new RuntimeException(
+ "PhoneFactory.getPhone must be called from Looper thread");
+ }
+ if (!sMadeDefaults) {
+ throw new IllegalStateException("Default phones haven't been made yet!");
+ }
+ return sProxyPhones;
+ }
+
public static Phone getCdmaPhone() {
Phone phone;
synchronized(PhoneProxy.lockForRadioTechnologyChange) {
@@ -182,10 +278,11 @@
}
public static Phone getGsmPhone() {
- synchronized(PhoneProxy.lockForRadioTechnologyChange) {
- Phone phone = new GSMPhone(sContext, sCommandsInterface, sPhoneNotifier);
- return phone;
+ int phoneId = SubscriptionController.getInstance().getPhoneId(getDefaultSubscription());
+ if (phoneId < 0 || phoneId >= TelephonyManager.getDefault().getPhoneCount()) {
+ phoneId = 0;
}
+ return getGsmPhone(phoneId);
}
/**
@@ -197,12 +294,42 @@
return SipPhoneFactory.makePhone(sipUri, sContext, sPhoneNotifier);
}
+ /* Sets the default subscription. If only one phone instance is active that
+ * subscription is set as default subscription. If both phone instances
+ * are active the first instance "0" is set as default subscription
+ */
+ public static void setDefaultSubscription(int subId) {
+ SystemProperties.set(PROPERTY_DEFAULT_SUBSCRIPTION, Integer.toString(subId));
+ int phoneId = SubscriptionController.getInstance().getPhoneId(subId);
+
+ // Set the default phone in base class
+ if (phoneId >= 0 && phoneId < sProxyPhones.length) {
+ sProxyPhone = sProxyPhones[phoneId];
+ sCommandsInterface = sCommandsInterfaces[phoneId];
+ sMadeDefaults = true;
+ }
+
+ // Update MCC MNC device configuration information
+ String defaultMccMnc = TelephonyManager.getDefault().getSimOperator(phoneId);
+ Rlog.d(LOG_TAG, "update mccmnc=" + defaultMccMnc);
+ MccTable.updateMccMncConfiguration(sContext, defaultMccMnc, false);
+
+ // Broadcast an Intent for default sub change
+ Intent intent = new Intent(TelephonyIntents.ACTION_DEFAULT_SUBSCRIPTION_CHANGED);
+ intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
+ SubscriptionManager.putPhoneIdAndSubIdExtra(intent, phoneId);
+ Rlog.d(LOG_TAG, "setDefaultSubscription : " + subId
+ + " Broadcasting Default Subscription Changed...");
+ sContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
+ }
+
/**
* Returns the preferred network type that should be set in the modem.
*
* @param context The current {@link Context}.
* @return the preferred network mode that should be set.
*/
+ // TODO: Fix when we "properly" have TelephonyDevController/SubscriptionController ..
public static int calculatePreferredNetworkType(Context context) {
int preferredNetworkType = RILConstants.PREFERRED_NETWORK_MODE;
if (TelephonyManager.getLteOnCdmaModeStatic() == PhoneConstants.LTE_ON_CDMA_TRUE) {
@@ -212,4 +339,171 @@
Settings.Global.PREFERRED_NETWORK_MODE, preferredNetworkType);
return networkType;
}
+
+ /* Gets the default subscription */
+ public static long getDefaultSubscription() {
+ return SubscriptionController.getInstance().getDefaultSubId();
+ }
+
+ /* Gets User preferred Voice subscription setting*/
+ public static int getVoiceSubscription() {
+ int subId = 0;
+
+ try {
+ subId = Settings.Global.getInt(sContext.getContentResolver(),
+ Settings.Global.MULTI_SIM_VOICE_CALL_SUBSCRIPTION);
+ } catch (SettingNotFoundException snfe) {
+ Rlog.e(LOG_TAG, "Settings Exception Reading Dual Sim Voice Call Values");
+ }
+
+ int phoneId = SubscriptionController.getInstance().getPhoneId(subId);
+ // Set subscription to 0 if current subscription is invalid.
+ // Ex: multisim.config property is TSTS and subscription is 2.
+ // If user is trying to set multisim.config to DSDS and reboots
+ // in this case index 2 is invalid so need to set to 0.
+ if (phoneId < 0 || phoneId >= TelephonyManager.getDefault().getPhoneCount()) {
+ Rlog.i(LOG_TAG, "Subscription is invalid..." + subId + " Set to 0");
+ subId = 0;
+ setVoiceSubscription(subId);
+ }
+
+ return subId;
+ }
+
+ /* Returns User Prompt property, enabed or not */
+ public static boolean isPromptEnabled() {
+ boolean prompt = false;
+ int value = 0;
+ try {
+ value = Settings.Global.getInt(sContext.getContentResolver(),
+ Settings.Global.MULTI_SIM_VOICE_PROMPT);
+ } catch (SettingNotFoundException snfe) {
+ Rlog.e(LOG_TAG, "Settings Exception Reading Dual Sim Voice Prompt Values");
+ }
+ prompt = (value == 0) ? false : true ;
+ Rlog.d(LOG_TAG, "Prompt option:" + prompt);
+
+ return prompt;
+ }
+
+ /*Sets User Prompt property, enabed or not */
+ public static void setPromptEnabled(boolean enabled) {
+ int value = (enabled == false) ? 0 : 1;
+ Settings.Global.putInt(sContext.getContentResolver(),
+ Settings.Global.MULTI_SIM_VOICE_PROMPT, value);
+ Rlog.d(LOG_TAG, "setVoicePromptOption to " + enabled);
+ }
+
+ /* Returns User SMS Prompt property, enabled or not */
+ public static boolean isSMSPromptEnabled() {
+ boolean prompt = false;
+ int value = 0;
+ try {
+ value = Settings.Global.getInt(sContext.getContentResolver(),
+ Settings.Global.MULTI_SIM_SMS_PROMPT);
+ } catch (SettingNotFoundException snfe) {
+ Rlog.e(LOG_TAG, "Settings Exception Reading Dual Sim SMS Prompt Values");
+ }
+ prompt = (value == 0) ? false : true ;
+ Rlog.d(LOG_TAG, "SMS Prompt option:" + prompt);
+
+ return prompt;
+ }
+
+ /*Sets User SMS Prompt property, enable or not */
+ public static void setSMSPromptEnabled(boolean enabled) {
+ int value = (enabled == false) ? 0 : 1;
+ Settings.Global.putInt(sContext.getContentResolver(),
+ Settings.Global.MULTI_SIM_SMS_PROMPT, value);
+ Rlog.d(LOG_TAG, "setSMSPromptOption to " + enabled);
+ }
+
+ /* Gets User preferred Data subscription setting*/
+ public static long getDataSubscription() {
+ long subId = 1;
+
+ try {
+ subId = Settings.Global.getLong(sContext.getContentResolver(),
+ Settings.Global.MULTI_SIM_DATA_CALL_SUBSCRIPTION);
+ } catch (SettingNotFoundException snfe) {
+ Rlog.e(LOG_TAG, "Settings Exception Reading Dual Sim Data Call Values");
+ }
+
+ int phoneId = SubscriptionController.getInstance().getPhoneId(subId);
+ if (phoneId < 0 || phoneId >= TelephonyManager.getDefault().getPhoneCount()) {
+ subId = 1;
+ Rlog.i(LOG_TAG, "Subscription is invalid..." + subId + " Set to 0");
+ setDataSubscription(subId);
+ }
+
+ return subId;
+ }
+
+ /* Gets User preferred SMS subscription setting*/
+ public static int getSMSSubscription() {
+ int subId = 0;
+ try {
+ subId = Settings.Global.getInt(sContext.getContentResolver(),
+ Settings.Global.MULTI_SIM_SMS_SUBSCRIPTION);
+ } catch (SettingNotFoundException snfe) {
+ Rlog.e(LOG_TAG, "Settings Exception Reading Dual Sim SMS Values");
+ }
+
+ int phoneId = SubscriptionController.getInstance().getPhoneId(subId);
+ if (phoneId < 0 || phoneId >= TelephonyManager.getDefault().getPhoneCount()) {
+ Rlog.i(LOG_TAG, "Subscription is invalid..." + subId + " Set to 0");
+ subId = 0;
+ setSMSSubscription(subId);
+ }
+
+ return subId;
+ }
+
+ static public void setVoiceSubscription(int subId) {
+ Settings.Global.putInt(sContext.getContentResolver(),
+ Settings.Global.MULTI_SIM_VOICE_CALL_SUBSCRIPTION, subId);
+ Rlog.d(LOG_TAG, "setVoiceSubscription : " + subId);
+ }
+
+ static public void setDataSubscription(long subId) {
+ boolean enabled;
+
+ Settings.Global.putLong(sContext.getContentResolver(),
+ Settings.Global.MULTI_SIM_DATA_CALL_SUBSCRIPTION, subId);
+ Rlog.d(LOG_TAG, "setDataSubscription: " + subId);
+
+ // Update the current mobile data flag
+ enabled = Settings.Global.getInt(sContext.getContentResolver(),
+ Settings.Global.MOBILE_DATA + subId, 0) != 0;
+ Settings.Global.putInt(sContext.getContentResolver(),
+ Settings.Global.MOBILE_DATA, enabled ? 1 : 0);
+ Rlog.d(LOG_TAG, "set mobile_data: " + enabled);
+
+ // Update the current data roaming flag
+ enabled = Settings.Global.getInt(sContext.getContentResolver(),
+ Settings.Global.DATA_ROAMING + subId, 0) != 0;
+ Settings.Global.putInt(sContext.getContentResolver(),
+ Settings.Global.DATA_ROAMING, enabled ? 1 : 0);
+ Rlog.d(LOG_TAG, "set data_roaming: " + enabled);
+ }
+
+ static public void setSMSSubscription(int subId) {
+ Settings.Global.putInt(sContext.getContentResolver(),
+ Settings.Global.MULTI_SIM_SMS_SUBSCRIPTION, subId);
+
+ Intent intent = new Intent("com.android.mms.transaction.SEND_MESSAGE");
+ sContext.sendBroadcast(intent);
+
+ // Change occured in SMS preferred sub, update the default
+ // SMS interface Manager object with the new SMS preferred subscription.
+ Rlog.d(LOG_TAG, "setSMSSubscription : " + subId);
+ }
+
+ /**
+ * Makes a {@link ImsPhone} object.
+ * @return the {@code ImsPhone} object or null if the exception occured
+ */
+ public static Phone makeImsPhone(PhoneNotifier phoneNotifier, Phone defaultPhone) {
+ return ImsPhoneFactory.makePhone(sContext, phoneNotifier, defaultPhone);
+ }
}
diff --git a/src/java/com/android/internal/telephony/PhoneNotifier.java b/src/java/com/android/internal/telephony/PhoneNotifier.java
index aebf694..da3e305 100644
--- a/src/java/com/android/internal/telephony/PhoneNotifier.java
+++ b/src/java/com/android/internal/telephony/PhoneNotifier.java
@@ -18,6 +18,7 @@
import android.telephony.CellInfo;
import android.telephony.DataConnectionRealTimeInfo;
+import android.telephony.VoLteServiceState;
import java.util.List;
@@ -58,4 +59,6 @@
String apn, String failCause);
public void notifyDataConnectionRealTimeInfo(Phone sender, DataConnectionRealTimeInfo dcRtInfo);
+
+ public void notifyVoLteServiceStateChanged(Phone sender, VoLteServiceState lteState);
}
diff --git a/src/java/com/android/internal/telephony/PhoneProxy.java b/src/java/com/android/internal/telephony/PhoneProxy.java
index 7c232a1..3b5a562 100644
--- a/src/java/com/android/internal/telephony/PhoneProxy.java
+++ b/src/java/com/android/internal/telephony/PhoneProxy.java
@@ -32,11 +32,19 @@
import android.telephony.CellLocation;
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
+import android.telephony.SubscriptionManager;
import android.telephony.Rlog;
+import com.android.internal.telephony.cdma.CDMAPhone;
+import com.android.internal.telephony.gsm.GSMPhone;
+import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.test.SimulatedRadioControl;
+import com.android.internal.telephony.cdma.CDMALTEPhone;
+import com.android.internal.telephony.gsm.GSMPhone;
import com.android.internal.telephony.uicc.IccCardProxy;
+import com.android.internal.telephony.uicc.IccFileHandler;
import com.android.internal.telephony.uicc.IsimRecords;
+import com.android.internal.telephony.uicc.UiccCard;
import com.android.internal.telephony.uicc.UsimServiceTable;
import com.android.internal.telephony.CallManager;
@@ -62,6 +70,8 @@
private static final int EVENT_RIL_CONNECTED = 4;
private static final int EVENT_UPDATE_PHONE_OBJECT = 5;
+ private int mPhoneId = 0;
+
private static final String LOG_TAG = "PhoneProxy";
//***** Class Methods
@@ -69,8 +79,6 @@
mActivePhone = phone;
mResetModemOnRadioTechnologyChange = SystemProperties.getBoolean(
TelephonyProperties.PROPERTY_RESET_ON_RADIO_TECH_CHANGE, false);
- mIccSmsInterfaceManager =
- new IccSmsInterfaceManager((PhoneBase) this.mActivePhone);
mIccPhoneBookInterfaceManagerProxy = new IccPhoneBookInterfaceManagerProxy(
phone.getIccPhoneBookInterfaceManager());
mPhoneSubInfoProxy = new PhoneSubInfoProxy(phone.getPhoneSubInfo());
@@ -80,7 +88,11 @@
mCommandsInterface.registerForOn(this, EVENT_RADIO_ON, null);
mCommandsInterface.registerForVoiceRadioTechChanged(
this, EVENT_VOICE_RADIO_TECH_CHANGED, null);
- mIccCardProxy = new IccCardProxy(phone.getContext(), mCommandsInterface);
+ mPhoneId = phone.getPhoneId();
+ mIccSmsInterfaceManager =
+ new IccSmsInterfaceManager((PhoneBase)this.mActivePhone);
+ mIccCardProxy = new IccCardProxy(mActivePhone.getContext(), mCommandsInterface, mActivePhone.getPhoneId());
+
if (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_GSM) {
// For the purpose of IccCardProxy we only care about the technology family
mIccCardProxy.setVoiceRadioTech(ServiceState.RIL_RADIO_TECHNOLOGY_UMTS);
@@ -226,6 +238,7 @@
Intent intent = new Intent(TelephonyIntents.ACTION_RADIO_TECHNOLOGY_CHANGED);
intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
intent.putExtra(PhoneConstants.PHONE_NAME_KEY, mActivePhone.getPhoneName());
+ SubscriptionManager.putPhoneIdAndSubIdExtra(intent, mPhoneId);
ActivityManagerNative.broadcastStickyIntent(intent, null, UserHandle.USER_ALL);
}
@@ -256,9 +269,9 @@
// System.gc();
if (ServiceState.isCdma(newVoiceRadioTech)) {
- mActivePhone = PhoneFactory.getCdmaPhone();
+ mActivePhone = PhoneFactory.getCdmaPhone(mPhoneId);
} else if (ServiceState.isGsm(newVoiceRadioTech)) {
- mActivePhone = PhoneFactory.getGsmPhone();
+ mActivePhone = PhoneFactory.getGsmPhone(mPhoneId);
}
if (oldPhone != null) {
@@ -272,6 +285,22 @@
oldPhone = null;
}
+ public IccSmsInterfaceManager getIccSmsInterfaceManager(){
+ return mIccSmsInterfaceManager;
+ }
+
+ public PhoneSubInfoProxy getPhoneSubInfoProxy(){
+ return mPhoneSubInfoProxy;
+ }
+
+ public IccPhoneBookInterfaceManagerProxy getIccPhoneBookInterfaceManagerProxy() {
+ return mIccPhoneBookInterfaceManagerProxy;
+ }
+
+ public IccFileHandler getIccFileHandler() {
+ return ((PhoneBase)mActivePhone).getIccFileHandler();
+ }
+
@Override
public void updatePhoneObject(int voiceRadioTech) {
logd("updatePhoneObject: radioTechnology=" + voiceRadioTech);
@@ -545,6 +574,16 @@
}
@Override
+ public void registerForOnHoldTone(Handler h, int what, Object obj) {
+ mActivePhone.registerForOnHoldTone(h,what,obj);
+ }
+
+ @Override
+ public void unregisterForOnHoldTone(Handler h) {
+ mActivePhone.unregisterForOnHoldTone(h);
+ }
+
+ @Override
public void registerForResendIncallMute(Handler h, int what, Object obj) {
mActivePhone.registerForResendIncallMute(h,what,obj);
}
@@ -555,6 +594,15 @@
}
@Override
+ public void registerForSimRecordsLoaded(Handler h, int what, Object obj) {
+ mActivePhone.registerForSimRecordsLoaded(h,what,obj);
+ }
+
+ public void unregisterForSimRecordsLoaded(Handler h) {
+ mActivePhone.unregisterForSimRecordsLoaded(h);
+ }
+
+ @Override
public boolean getIccRecordsLoaded() {
return mIccCardProxy.getIccRecordsLoaded();
}
@@ -1162,11 +1210,9 @@
return mActivePhone.getIsimRecords();
}
- @Override
- public void requestIsimAuthentication(String nonce, Message response) {
- mActivePhone.requestIsimAuthentication(nonce, response);
- }
-
+ /**
+ * {@inheritDoc}
+ */
@Override
public int getLteOnCdmaMode() {
return mActivePhone.getLteOnCdmaMode();
@@ -1183,6 +1229,11 @@
}
@Override
+ public UiccCard getUiccCard() {
+ return mActivePhone.getUiccCard();
+ }
+
+ @Override
public void nvReadItem(int itemID, Message response) {
mActivePhone.nvReadItem(itemID, response);
}
@@ -1214,4 +1265,111 @@
mActivePhone = null;
mCommandsInterface = null;
}
+
+ public boolean updateCurrentCarrierInProvider() {
+ if (mActivePhone instanceof CDMALTEPhone) {
+ return ((CDMALTEPhone)mActivePhone).updateCurrentCarrierInProvider();
+ } else if (mActivePhone instanceof GSMPhone) {
+ return ((GSMPhone)mActivePhone).updateCurrentCarrierInProvider();
+ } else {
+ loge("Phone object is not MultiSim. This should not hit!!!!");
+ return false;
+ }
+ }
+
+ public void updateDataConnectionTracker() {
+ logd("Updating Data Connection Tracker");
+ if (mActivePhone instanceof CDMALTEPhone) {
+ ((CDMALTEPhone)mActivePhone).updateDataConnectionTracker();
+ } else if (mActivePhone instanceof GSMPhone) {
+ ((GSMPhone)mActivePhone).updateDataConnectionTracker();
+ } else {
+ loge("Phone object is not MultiSim. This should not hit!!!!");
+ }
+ }
+
+ public void setInternalDataEnabled(boolean enable) {
+ setInternalDataEnabled(enable, null);
+ }
+
+ public boolean setInternalDataEnabledFlag(boolean enable) {
+ boolean flag = false;
+ if (mActivePhone instanceof CDMALTEPhone) {
+ flag = ((CDMALTEPhone)mActivePhone).setInternalDataEnabledFlag(enable);
+ } else if (mActivePhone instanceof GSMPhone) {
+ flag = ((GSMPhone)mActivePhone).setInternalDataEnabledFlag(enable);
+ } else {
+ loge("Phone object is not MultiSim. This should not hit!!!!");
+ }
+ return flag;
+ }
+
+ public void setInternalDataEnabled(boolean enable, Message onCompleteMsg) {
+ if (mActivePhone instanceof CDMALTEPhone) {
+ ((CDMALTEPhone)mActivePhone).setInternalDataEnabled(enable, onCompleteMsg);
+ } else if (mActivePhone instanceof GSMPhone) {
+ ((GSMPhone)mActivePhone).setInternalDataEnabled(enable, onCompleteMsg);
+ } else {
+ loge("Phone object is not MultiSim. This should not hit!!!!");
+ }
+ }
+
+ public void registerForAllDataDisconnected(Handler h, int what, Object obj) {
+ if (mActivePhone instanceof CDMALTEPhone) {
+ ((CDMALTEPhone)mActivePhone).registerForAllDataDisconnected(h, what, obj);
+ } else if (mActivePhone instanceof GSMPhone) {
+ ((GSMPhone)mActivePhone).registerForAllDataDisconnected(h, what, obj);
+ } else {
+ loge("Phone object is not MultiSim. This should not hit!!!!");
+ }
+ }
+
+ public void unregisterForAllDataDisconnected(Handler h) {
+ if (mActivePhone instanceof CDMALTEPhone) {
+ ((CDMALTEPhone)mActivePhone).unregisterForAllDataDisconnected(h);
+ } else if (mActivePhone instanceof GSMPhone) {
+ ((GSMPhone)mActivePhone).unregisterForAllDataDisconnected(h);
+ } else {
+ loge("Phone object is not MultiSim. This should not hit!!!!");
+ }
+ }
+
+
+ public long getSubId() {
+ return mActivePhone.getSubId();
+ }
+
+ public int getPhoneId() {
+ return mActivePhone.getPhoneId();
+ }
+
+ @Override
+ public String[] getPcscfAddress() {
+ return mActivePhone.getPcscfAddress();
+ }
+
+ @Override
+ public void setImsRegistrationState(boolean registered){
+ logd("setImsRegistrationState - registered: " + registered);
+
+ mActivePhone.setImsRegistrationState(registered);
+
+ if ((mActivePhone.getPhoneName()).equals("GSM")) {
+ GSMPhone GP = (GSMPhone)mActivePhone;
+ GP.getServiceStateTracker().setImsRegistrationState(registered);
+ } else if ((mActivePhone.getPhoneName()).equals("CDMA")) {
+ CDMAPhone CP = (CDMAPhone)mActivePhone;
+ CP.getServiceStateTracker().setImsRegistrationState(registered);
+ }
+ }
+
+ @Override
+ public Phone getVoicePhone() {
+ return null;
+ }
+
+ @Override
+ public int getVoiceServiceState() {
+ return mActivePhone.getVoiceServiceState();
+ }
}
diff --git a/src/java/com/android/internal/telephony/PhoneSubInfo.java b/src/java/com/android/internal/telephony/PhoneSubInfo.java
index 185814d..4da8e9e 100755
--- a/src/java/com/android/internal/telephony/PhoneSubInfo.java
+++ b/src/java/com/android/internal/telephony/PhoneSubInfo.java
@@ -21,12 +21,15 @@
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Binder;
+import android.os.RemoteException;
import android.telephony.PhoneNumberUtils;
import android.telephony.Rlog;
import com.android.internal.telephony.uicc.IsimRecords;
+import com.android.internal.telephony.uicc.UiccCard;
+import com.android.internal.telephony.uicc.UiccCardApplication;
-public class PhoneSubInfo extends IPhoneSubInfo.Stub {
+public class PhoneSubInfo {
static final String LOG_TAG = "PhoneSubInfo";
private static final boolean DBG = true;
private static final boolean VDBG = false; // STOPSHIP if true
@@ -62,7 +65,6 @@
/**
* Retrieves the unique device ID, e.g., IMEI for GSM phones and MEID for CDMA phones.
*/
- @Override
public String getDeviceId() {
mContext.enforceCallingOrSelfPermission(READ_PHONE_STATE, "Requires READ_PHONE_STATE");
return mPhone.getDeviceId();
@@ -72,7 +74,6 @@
* Retrieves the software version number for the device, e.g., IMEI/SV
* for GSM phones.
*/
- @Override
public String getDeviceSvn() {
mContext.enforceCallingOrSelfPermission(READ_PHONE_STATE, "Requires READ_PHONE_STATE");
return mPhone.getDeviceSvn();
@@ -81,7 +82,6 @@
/**
* Retrieves the unique subscriber ID, e.g., IMSI for GSM phones.
*/
- @Override
public String getSubscriberId() {
mContext.enforceCallingOrSelfPermission(READ_PHONE_STATE, "Requires READ_PHONE_STATE");
return mPhone.getSubscriberId();
@@ -98,7 +98,6 @@
/**
* Retrieves the serial number of the ICC, if applicable.
*/
- @Override
public String getIccSerialNumber() {
mContext.enforceCallingOrSelfPermission(READ_PHONE_STATE, "Requires READ_PHONE_STATE");
return mPhone.getIccSerialNumber();
@@ -107,7 +106,6 @@
/**
* Retrieves the phone number string for line 1.
*/
- @Override
public String getLine1Number() {
mContext.enforceCallingOrSelfPermission(READ_PHONE_STATE, "Requires READ_PHONE_STATE");
return mPhone.getLine1Number();
@@ -116,7 +114,6 @@
/**
* Retrieves the alpha identifier for line 1.
*/
- @Override
public String getLine1AlphaTag() {
mContext.enforceCallingOrSelfPermission(READ_PHONE_STATE, "Requires READ_PHONE_STATE");
return mPhone.getLine1AlphaTag();
@@ -125,7 +122,6 @@
/**
* Retrieves the MSISDN string.
*/
- @Override
public String getMsisdn() {
mContext.enforceCallingOrSelfPermission(READ_PHONE_STATE, "Requires READ_PHONE_STATE");
return mPhone.getMsisdn();
@@ -134,7 +130,6 @@
/**
* Retrieves the voice mail number.
*/
- @Override
public String getVoiceMailNumber() {
mContext.enforceCallingOrSelfPermission(READ_PHONE_STATE, "Requires READ_PHONE_STATE");
String number = PhoneNumberUtils.extractNetworkPortion(mPhone.getVoiceMailNumber());
@@ -147,7 +142,6 @@
*
* @hide
*/
- @Override
public String getCompleteVoiceMailNumber() {
mContext.enforceCallingOrSelfPermission(CALL_PRIVILEGED,
"Requires CALL_PRIVILEGED");
@@ -159,7 +153,6 @@
/**
* Retrieves the alpha identifier associated with the voice mail number.
*/
- @Override
public String getVoiceMailAlphaTag() {
mContext.enforceCallingOrSelfPermission(READ_PHONE_STATE, "Requires READ_PHONE_STATE");
return mPhone.getVoiceMailAlphaTag();
@@ -169,7 +162,6 @@
* Returns the IMS private user identity (IMPI) that was loaded from the ISIM.
* @return the IMPI, or null if not present or not loaded
*/
- @Override
public String getIsimImpi() {
mContext.enforceCallingOrSelfPermission(READ_PRIVILEGED_PHONE_STATE,
"Requires READ_PRIVILEGED_PHONE_STATE");
@@ -185,7 +177,6 @@
* Returns the IMS home network domain name that was loaded from the ISIM.
* @return the IMS domain name, or null if not present or not loaded
*/
- @Override
public String getIsimDomain() {
mContext.enforceCallingOrSelfPermission(READ_PRIVILEGED_PHONE_STATE,
"Requires READ_PRIVILEGED_PHONE_STATE");
@@ -202,7 +193,6 @@
* @return an array of IMPU strings, with one IMPU per string, or null if
* not present or not loaded
*/
- @Override
public String[] getIsimImpu() {
mContext.enforceCallingOrSelfPermission(READ_PRIVILEGED_PHONE_STATE,
"Requires READ_PRIVILEGED_PHONE_STATE");
@@ -214,6 +204,84 @@
}
}
+ /**
+ * Returns the IMS Service Table (IST) that was loaded from the ISIM.
+ * @return IMS Service Table or null if not present or not loaded
+ */
+ public String getIsimIst(){
+ mContext.enforceCallingOrSelfPermission(READ_PRIVILEGED_PHONE_STATE,
+ "Requires READ_PRIVILEGED_PHONE_STATE");
+ IsimRecords isim = mPhone.getIsimRecords();
+ if (isim != null) {
+ return isim.getIsimIst();
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Returns the IMS Proxy Call Session Control Function(PCSCF) that were loaded from the ISIM.
+ * @return an array of PCSCF strings with one PCSCF per string, or null if
+ * not present or not loaded
+ */
+ public String[] getIsimPcscf() {
+ mContext.enforceCallingOrSelfPermission(READ_PRIVILEGED_PHONE_STATE,
+ "Requires READ_PRIVILEGED_PHONE_STATE");
+ IsimRecords isim = mPhone.getIsimRecords();
+ if (isim != null) {
+ return isim.getIsimPcscf();
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Returns the response of ISIM Authetification through RIL.
+ * Returns null if the Authentification hasn't been successed or isn't present iphonesubinfo.
+ * @return the response of ISIM Authetification, or null if not available
+ */
+ public String getIsimChallengeResponse(String nonce){
+ mContext.enforceCallingOrSelfPermission(READ_PRIVILEGED_PHONE_STATE,
+ "Requires READ_PRIVILEGED_PHONE_STATE");
+ IsimRecords isim = mPhone.getIsimRecords();
+ if (isim != null) {
+ return isim.getIsimChallengeResponse(nonce);
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Returns the response of the SIM application on the UICC to authentication
+ * challenge/response algorithm. The data string and challenge response are
+ * Base64 encoded Strings.
+ * Can support EAP-SIM, EAP-AKA with results encoded per 3GPP TS 31.102.
+ *
+ * @param appType ICC application family (@see com.android.internal.telephony.PhoneConstants#APPTYPE_xxx)
+ * @param data authentication challenge data
+ * @return challenge response
+ */
+ public String getIccSimChallengeResponse(long subId, int appType, String data) {
+ // FIXME: use subId!!
+ mContext.enforceCallingOrSelfPermission(READ_PRIVILEGED_PHONE_STATE,
+ "Requires READ_PRIVILEGED_PHONE_STATE");
+
+ UiccCard uiccCard = mPhone.getUiccCard();
+ if (uiccCard == null) {
+ Rlog.e(LOG_TAG, "getIccSimChallengeResponse() UiccCard is null");
+ return null;
+ }
+
+ UiccCardApplication uiccApp = uiccCard.getApplicationByType(appType);
+ if (uiccApp == null) {
+ Rlog.e(LOG_TAG, "getIccSimChallengeResponse() no app with specified type -- " +
+ appType);
+ return null;
+ }
+
+ return uiccApp.getIccRecords().getIccSimChallengeResponse(data);
+ }
+
private void log(String s) {
Rlog.d(LOG_TAG, s);
}
@@ -222,7 +290,6 @@
Rlog.e(LOG_TAG, s, e);
}
- @Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
!= PackageManager.PERMISSION_GRANTED) {
diff --git a/src/java/com/android/internal/telephony/PhoneSubInfoController.java b/src/java/com/android/internal/telephony/PhoneSubInfoController.java
new file mode 100644
index 0000000..5e5606b
--- /dev/null
+++ b/src/java/com/android/internal/telephony/PhoneSubInfoController.java
@@ -0,0 +1,272 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ * Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
+ * Not a Contribution.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.telephony.SubscriptionManager;
+import android.telephony.Rlog;
+import android.telephony.TelephonyManager;
+
+import java.lang.NullPointerException;
+import java.lang.ArrayIndexOutOfBoundsException;
+
+import com.android.internal.telephony.IPhoneSubInfo;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneSubInfoProxy;
+
+public class PhoneSubInfoController extends IPhoneSubInfo.Stub {
+ private static final String TAG = "PhoneSubInfoController";
+ private Phone[] mPhone;
+
+ public PhoneSubInfoController(Phone[] phone) {
+ mPhone = phone;
+ if (ServiceManager.getService("iphonesubinfo") == null) {
+ ServiceManager.addService("iphonesubinfo", this);
+ }
+ }
+
+
+ public String getDeviceId() {
+ return getDeviceIdUsingSubId(getDefaultSubscription());
+ }
+
+ public String getDeviceIdUsingSubId(long subId) {
+ PhoneSubInfoProxy phoneSubInfoProxy = getPhoneSubInfoProxy(subId);
+ if (phoneSubInfoProxy != null) {
+ return phoneSubInfoProxy.getDeviceId();
+ } else {
+ Rlog.e(TAG,"getDeviceId phoneSubInfoProxy is null" +
+ " for Subscription:" + subId);
+ return null;
+ }
+ }
+
+ public String getDeviceSvn() {
+ PhoneSubInfoProxy phoneSubInfoProxy = getPhoneSubInfoProxy(getDefaultSubscription());
+ if (phoneSubInfoProxy != null) {
+ return phoneSubInfoProxy.getDeviceSvn();
+ } else {
+ Rlog.e(TAG,"getDeviceSvn phoneSubInfoProxy is null");
+ return null;
+ }
+ }
+
+ public String getSubscriberId() {
+ return getSubscriberIdUsingSubId(getDefaultSubscription());
+ }
+
+ public String getSubscriberIdUsingSubId(long subId) {
+ PhoneSubInfoProxy phoneSubInfoProxy = getPhoneSubInfoProxy(subId);
+ if (phoneSubInfoProxy != null) {
+ return phoneSubInfoProxy.getSubscriberId();
+ } else {
+ Rlog.e(TAG,"getSubscriberId phoneSubInfoProxy is" +
+ " null for Subscription:" + subId);
+ return null;
+ }
+ }
+
+ /**
+ * Retrieves the serial number of the ICC, if applicable.
+ */
+ public String getIccSerialNumber() {
+ return getIccSerialNumberUsingSubId(getDefaultSubscription());
+ }
+
+ public String getIccSerialNumberUsingSubId(long subId) {
+ PhoneSubInfoProxy phoneSubInfoProxy = getPhoneSubInfoProxy(subId);
+ if (phoneSubInfoProxy != null) {
+ return phoneSubInfoProxy.getIccSerialNumber();
+ } else {
+ Rlog.e(TAG,"getIccSerialNumber phoneSubInfoProxy is" +
+ " null for Subscription:" + subId);
+ return null;
+ }
+ }
+
+ public String getLine1Number() {
+ return getLine1NumberUsingSubId(getDefaultSubscription());
+ }
+
+ public String getLine1NumberUsingSubId(long subId) {
+ PhoneSubInfoProxy phoneSubInfoProxy = getPhoneSubInfoProxy(subId);
+ if (phoneSubInfoProxy != null) {
+ return phoneSubInfoProxy.getLine1Number();
+ } else {
+ Rlog.e(TAG,"getLine1Number phoneSubInfoProxy is" +
+ " null for Subscription:" + subId);
+ return null;
+ }
+ }
+
+ public String getLine1AlphaTag() {
+ return getLine1AlphaTagUsingSubId(getDefaultSubscription());
+ }
+
+ public String getLine1AlphaTagUsingSubId(long subId) {
+ PhoneSubInfoProxy phoneSubInfoProxy = getPhoneSubInfoProxy(subId);
+ if (phoneSubInfoProxy != null) {
+ return phoneSubInfoProxy.getLine1AlphaTag();
+ } else {
+ Rlog.e(TAG,"getLine1AlphaTag phoneSubInfoProxy is" +
+ " null for Subscription:" + subId);
+ return null;
+ }
+ }
+
+ public String getMsisdn() {
+ return getMsisdnUsingSubId(getDefaultSubscription());
+ }
+
+ public String getMsisdnUsingSubId(long subId) {
+ PhoneSubInfoProxy phoneSubInfoProxy = getPhoneSubInfoProxy(subId);
+ if (phoneSubInfoProxy != null) {
+ return phoneSubInfoProxy.getMsisdn();
+ } else {
+ Rlog.e(TAG,"getMsisdn phoneSubInfoProxy is" +
+ " null for Subscription:" + subId);
+ return null;
+ }
+ }
+
+ public String getVoiceMailNumber() {
+ return getVoiceMailNumberUsingSubId(getDefaultSubscription());
+ }
+
+ public String getVoiceMailNumberUsingSubId(long subId) {
+ PhoneSubInfoProxy phoneSubInfoProxy = getPhoneSubInfoProxy(subId);
+ if (phoneSubInfoProxy != null) {
+ return phoneSubInfoProxy.getVoiceMailNumber();
+ } else {
+ Rlog.e(TAG,"getVoiceMailNumber phoneSubInfoProxy is" +
+ " null for Subscription:" + subId);
+ return null;
+ }
+ }
+
+ public String getCompleteVoiceMailNumber() {
+ return getCompleteVoiceMailNumberUsingSubId(getDefaultSubscription());
+ }
+
+ public String getCompleteVoiceMailNumberUsingSubId(long subId) {
+ PhoneSubInfoProxy phoneSubInfoProxy = getPhoneSubInfoProxy(subId);
+ if (phoneSubInfoProxy != null) {
+ return phoneSubInfoProxy.getCompleteVoiceMailNumber();
+ } else {
+ Rlog.e(TAG,"getCompleteVoiceMailNumber phoneSubInfoProxy" +
+ " is null for Subscription:" + subId);
+ return null;
+ }
+ }
+
+ public String getVoiceMailAlphaTag() {
+ return getVoiceMailAlphaTagUsingSubId(getDefaultSubscription());
+ }
+
+ public String getVoiceMailAlphaTagUsingSubId(long subId) {
+ PhoneSubInfoProxy phoneSubInfoProxy = getPhoneSubInfoProxy(subId);
+ if (phoneSubInfoProxy != null) {
+ return phoneSubInfoProxy.getVoiceMailAlphaTag();
+ } else {
+ Rlog.e(TAG,"getVoiceMailAlphaTag phoneSubInfoProxy is" +
+ " null for Subscription:" + subId);
+ return null;
+ }
+ }
+
+ /**
+ * get Phone sub info proxy object based on subId.
+ **/
+ private PhoneSubInfoProxy getPhoneSubInfoProxy(long subId) {
+
+ long phoneId = SubscriptionManager.getPhoneId(subId);
+ if (phoneId < 0 || phoneId >= TelephonyManager.getDefault().getPhoneCount()) {
+ phoneId = 0;
+ }
+
+ try {
+ return ((PhoneProxy)mPhone[(int)phoneId]).getPhoneSubInfoProxy();
+ } catch (NullPointerException e) {
+ Rlog.e(TAG, "Exception is :" + e.toString() + " For subId :" + subId);
+ e.printStackTrace();
+ return null;
+ } catch (ArrayIndexOutOfBoundsException e) {
+ Rlog.e(TAG, "Exception is :" + e.toString() + " For subId :" + subId);
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ private long getDefaultSubscription() {
+ return PhoneFactory.getDefaultSubscription();
+ }
+
+
+ public String getIsimImpi() {
+ PhoneSubInfoProxy phoneSubInfoProxy = getPhoneSubInfoProxy(getDefaultSubscription());
+ return phoneSubInfoProxy.getIsimImpi();
+ }
+
+ public String getIsimDomain() {
+ PhoneSubInfoProxy phoneSubInfoProxy = getPhoneSubInfoProxy(getDefaultSubscription());
+ return phoneSubInfoProxy.getIsimDomain();
+ }
+
+ public String[] getIsimImpu() {
+ PhoneSubInfoProxy phoneSubInfoProxy = getPhoneSubInfoProxy(getDefaultSubscription());
+ return phoneSubInfoProxy.getIsimImpu();
+ }
+
+ public String getIsimIst() throws RemoteException {
+ PhoneSubInfoProxy phoneSubInfoProxy = getPhoneSubInfoProxy(getDefaultSubscription());
+ return phoneSubInfoProxy.getIsimIst();
+ }
+
+ public String[] getIsimPcscf() throws RemoteException {
+ PhoneSubInfoProxy phoneSubInfoProxy = getPhoneSubInfoProxy(getDefaultSubscription());
+ return phoneSubInfoProxy.getIsimPcscf();
+ }
+
+ public String getIsimChallengeResponse(String nonce) throws RemoteException {
+ PhoneSubInfoProxy phoneSubInfoProxy = getPhoneSubInfoProxy(getDefaultSubscription());
+ return phoneSubInfoProxy.getIsimChallengeResponse(nonce);
+ }
+
+ public String getIccSimChallengeResponse(long subId, int appType, String data)
+ throws RemoteException {
+ PhoneSubInfoProxy phoneSubInfoProxy = getPhoneSubInfoProxy(subId);
+ return phoneSubInfoProxy.getIccSimChallengeResponse(subId, appType, data);
+ }
+
+ public String getGroupIdLevel1() {
+ return getGroupIdLevel1UsingSubId(getDefaultSubscription());
+ }
+
+ public String getGroupIdLevel1UsingSubId(long subId) {
+ PhoneSubInfoProxy phoneSubInfoProxy = getPhoneSubInfoProxy(subId);
+ if (phoneSubInfoProxy != null) {
+ return phoneSubInfoProxy.getGroupIdLevel1();
+ } else {
+ Rlog.e(TAG,"getGroupIdLevel1 phoneSubInfoProxy is" +
+ " null for Subscription:" + subId);
+ return null;
+ }
+ }
+}
diff --git a/src/java/com/android/internal/telephony/PhoneSubInfoProxy.java b/src/java/com/android/internal/telephony/PhoneSubInfoProxy.java
index 1974ff8..4a8e7d6 100755
--- a/src/java/com/android/internal/telephony/PhoneSubInfoProxy.java
+++ b/src/java/com/android/internal/telephony/PhoneSubInfoProxy.java
@@ -19,6 +19,7 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import android.os.RemoteException;
import android.os.ServiceManager;
@@ -146,6 +147,110 @@
}
@Override
+ public String getDeviceIdUsingSubId(long subId) throws RemoteException {
+ // FIXME: getDeviceIdUsingSubId
+ return null;
+ }
+
+ @Override
+ public String getSubscriberIdUsingSubId(long subId) throws RemoteException {
+ // FIXME: getSubscriberIdUsingSubId
+ return null;
+ }
+
+ @Override
+ public String getGroupIdLevel1UsingSubId(long subId) throws RemoteException {
+ // FIXME: getGroupIdLevel1UsingSubId
+ return null;
+ }
+
+ @Override
+ public String getIccSerialNumberUsingSubId(long subId) throws RemoteException {
+ // FIXME: getIccSerialNumberUsingSubId
+ return null;
+ }
+
+ @Override
+ public String getLine1NumberUsingSubId(long subId) throws RemoteException {
+ // FIXME: getLine1NumberUsingSubId
+ return null;
+ }
+
+ @Override
+ public String getLine1AlphaTagUsingSubId(long subId) throws RemoteException {
+ // FIXME: getLine1AlphaTagUsingSubId
+ return null;
+ }
+
+ @Override
+ public String getMsisdnUsingSubId(long subId) throws RemoteException {
+ // FIXME: getMsisdnUsingSubId
+ return null;
+ }
+
+ @Override
+ public String getVoiceMailNumberUsingSubId(long subId) throws RemoteException {
+ // FIXME: getVoiceMailNumberUsingSubId
+ return null;
+ }
+
+ @Override
+ public String getCompleteVoiceMailNumberUsingSubId(long subId) throws RemoteException {
+ // FIXME: getCompleteVoiceMailNumberUsingSubId
+ return null;
+ }
+
+ @Override
+ public String getVoiceMailAlphaTagUsingSubId(long subId) throws RemoteException {
+ // FIXME: getVoiceMailAlphaTagUsingSubId
+ return null;
+ }
+
+ /**
+ * Returns the IMS Service Table (IST) that was loaded from the ISIM.
+ * @return IMS Service Table or null if not present or not loaded
+ */
+ @Override
+ public String getIsimIst() {
+ return mPhoneSubInfo.getIsimIst();
+ }
+
+ /**
+ * Returns the IMS Proxy Call Session Control Function(PCSCF) that were loaded from the ISIM.
+ * @return an array of PCSCF strings with one PCSCF per string, or null if
+ * not present or not loaded
+ */
+ @Override
+ public String[] getIsimPcscf() {
+ return mPhoneSubInfo.getIsimPcscf();
+ }
+
+ /**
+ * Returns the response of ISIM Authetification through RIL.
+ * Returns null if the Authentification hasn't been successed or isn't present iphonesubinfo.
+ * @return the response of ISIM Authetification, or null if not available
+ * @deprecated
+ * @see getIccSimChallengeResponse
+ */
+ public String getIsimChallengeResponse(String nonce) {
+ return mPhoneSubInfo.getIsimChallengeResponse(nonce);
+ }
+
+ /**
+ * Returns the response of the SIM application on the UICC to authentication
+ * challenge/response algorithm. The data string and challenge response are
+ * Base64 encoded Strings.
+ * Can support EAP-SIM, EAP-AKA with results encoded per 3GPP TS 31.102.
+ *
+ * @param appType ICC application type (@see com.android.internal.telephony.PhoneConstants#APPTYPE_xxx)
+ * @param data authentication challenge data
+ * @return challenge response
+ */
+ public String getIccSimChallengeResponse(long subId, int appType, String data) {
+ return mPhoneSubInfo.getIccSimChallengeResponse(subId, appType, data);
+ }
+
+ @Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
mPhoneSubInfo.dump(fd, pw, args);
}
diff --git a/src/java/com/android/internal/telephony/ProxyController.java b/src/java/com/android/internal/telephony/ProxyController.java
new file mode 100644
index 0000000..80612b7
--- /dev/null
+++ b/src/java/com/android/internal/telephony/ProxyController.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of The Linux Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.internal.telephony;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.Message;
+
+import android.telephony.Rlog;
+import android.telephony.ServiceState;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+
+import com.android.internal.telephony.CommandsInterface;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneBase;
+import com.android.internal.telephony.PhoneProxy;
+import com.android.internal.telephony.dataconnection.DctController;
+import com.android.internal.telephony.uicc.UiccController;
+
+public class ProxyController {
+ static final String LOG_TAG = "ProxyController";
+
+ //***** Class Variables
+ private static ProxyController sProxyController;
+
+ private Phone[] mProxyPhones;
+
+ private UiccController mUiccController;
+
+ private CommandsInterface[] mCi;
+
+ private Context mContext;
+
+ private static DctController mDctController;
+
+ //UiccPhoneBookController to use proper IccPhoneBookInterfaceManagerProxy object
+ private UiccPhoneBookController mUiccPhoneBookController;
+
+ //PhoneSubInfoController to use proper PhoneSubInfoProxy object
+ private PhoneSubInfoController mPhoneSubInfoController;
+
+ //UiccSmsController to use proper IccSmsInterfaceManager object
+ private UiccSmsController mUiccSmsController;
+
+ // private SubscriptionManager mSubscriptionManager;
+
+ //***** Class Methods
+ public static ProxyController getInstance(Context context, Phone[] phoneProxy,
+ UiccController uiccController, CommandsInterface[] ci) {
+ if (sProxyController == null) {
+ sProxyController = new ProxyController(context, phoneProxy, uiccController, ci);
+ }
+ return sProxyController;
+ }
+
+ static public ProxyController getInstance() {
+ return sProxyController;
+ }
+
+ private ProxyController(Context context, Phone[] phoneProxy, UiccController uiccController,
+ CommandsInterface[] ci) {
+ logd("Constructor - Enter");
+
+ mContext = context;
+ mProxyPhones = phoneProxy;
+ mUiccController = uiccController;
+ mCi = ci;
+
+ mDctController = DctController.makeDctController((PhoneProxy[])phoneProxy);
+ mUiccPhoneBookController = new UiccPhoneBookController(mProxyPhones);
+ mPhoneSubInfoController = new PhoneSubInfoController(mProxyPhones);
+ mUiccSmsController = new UiccSmsController(mProxyPhones);
+ // mSubscriptionManager = SubscriptionManager.getInstance(context, uiccController, ci);
+
+ logd("Constructor - Exit");
+ }
+
+ public void updateDataConnectionTracker(int sub) {
+ ((PhoneProxy) mProxyPhones[sub]).updateDataConnectionTracker();
+ }
+
+ public void enableDataConnectivity(int sub) {
+ ((PhoneProxy) mProxyPhones[sub]).setInternalDataEnabled(true);
+ }
+
+ public void disableDataConnectivity(int sub,
+ Message dataCleanedUpMsg) {
+ ((PhoneProxy) mProxyPhones[sub]).setInternalDataEnabled(false, dataCleanedUpMsg);
+ }
+
+ public boolean enableDataConnectivityFlag(int sub) {
+ return ((PhoneProxy) mProxyPhones[sub]).setInternalDataEnabledFlag(true);
+ }
+
+ public boolean disableDataConnectivityFlag(int sub) {
+ return ((PhoneProxy) mProxyPhones[sub]).setInternalDataEnabledFlag(false);
+ }
+
+ public void updateCurrentCarrierInProvider(int sub) {
+ ((PhoneProxy) mProxyPhones[sub]).updateCurrentCarrierInProvider();
+ }
+
+ public void checkAndUpdatePhoneObject(Subscription userSub) {
+ int subId = userSub.subId;
+ if ((userSub.appType.equals("SIM")
+ || userSub.appType.equals("USIM"))
+ && (!mProxyPhones[subId].getPhoneName().equals("GSM"))) {
+ logd("gets New GSM phone" );
+ ((PhoneProxy) mProxyPhones[subId])
+ .updatePhoneObject(ServiceState.RIL_RADIO_TECHNOLOGY_GSM);
+ } else if ((userSub.appType.equals("RUIM")
+ || userSub.appType.equals("CSIM")
+ || userSub.appType.equals("GLOBAL"))
+ && (!mProxyPhones[subId].getPhoneName().equals("CDMA"))) {
+ logd("gets New CDMA phone" );
+ ((PhoneProxy) mProxyPhones[subId])
+ .updatePhoneObject(ServiceState.RIL_RADIO_TECHNOLOGY_1xRTT);
+ }
+ }
+
+ public void registerForAllDataDisconnected(long subId, Handler h, int what, Object obj) {
+ int phoneId = SubscriptionController.getInstance().getPhoneId(subId);
+
+ if (phoneId >= 0 && phoneId < TelephonyManager.getDefault().getPhoneCount()) {
+ ((PhoneProxy) mProxyPhones[phoneId]).registerForAllDataDisconnected(h, what, obj);
+ }
+ }
+
+ public void unregisterForAllDataDisconnected(long subId, Handler h) {
+ int phoneId = SubscriptionController.getInstance().getPhoneId(subId);
+
+ if (phoneId >= 0 && phoneId < TelephonyManager.getDefault().getPhoneCount()) {
+ ((PhoneProxy) mProxyPhones[phoneId]).unregisterForAllDataDisconnected(h);
+ }
+ }
+
+ public boolean isDataDisconnected(long subId) {
+ int phoneId = SubscriptionController.getInstance().getPhoneId(subId);
+
+ if (phoneId >= 0 && phoneId < TelephonyManager.getDefault().getPhoneCount()) {
+ Phone activePhone = ((PhoneProxy) mProxyPhones[phoneId]).getActivePhone();
+ return ((PhoneBase) activePhone).mDcTracker.isDisconnected();
+ } else {
+ return false;
+ }
+ }
+
+ private void logd(String string) {
+ Rlog.d(LOG_TAG, string);
+ }
+}
diff --git a/src/java/com/android/internal/telephony/RIL.java b/src/java/com/android/internal/telephony/RIL.java
index cc71b86..be9b1c9 100644
--- a/src/java/com/android/internal/telephony/RIL.java
+++ b/src/java/com/android/internal/telephony/RIL.java
@@ -41,6 +41,7 @@
import android.os.PowerManager;
import android.os.SystemProperties;
import android.os.PowerManager.WakeLock;
+import android.provider.Settings.SettingNotFoundException;
import android.telephony.CellInfo;
import android.telephony.NeighboringCellInfo;
import android.telephony.PhoneNumberUtils;
@@ -63,6 +64,8 @@
import com.android.internal.telephony.cdma.CdmaSmsBroadcastConfigInfo;
import com.android.internal.telephony.dataconnection.DcFailCause;
import com.android.internal.telephony.dataconnection.DataCallResponse;
+import com.android.internal.telephony.TelephonyDevController;
+import com.android.internal.telephony.HardwareConfig;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
@@ -89,6 +92,7 @@
private static RILRequest sPool = null;
private static int sPoolSize = 0;
private static final int MAX_POOL_SIZE = 4;
+ private Context mContext;
//***** Instance Variables
int mSerial;
@@ -243,6 +247,8 @@
// When we are testing emergency calls
AtomicBoolean mTestingEmergencyCall = new AtomicBoolean(false);
+ private Integer mInstanceId;
+
//***** Events
static final int EVENT_SEND = 1;
@@ -255,7 +261,7 @@
static final int RESPONSE_SOLICITED = 0;
static final int RESPONSE_UNSOLICITED = 1;
- static final String SOCKET_NAME_RIL = "rild";
+ static final String[] SOCKET_NAME_RIL = {"rild", "rild2", "rild3"};
static final int SOCKET_OPEN_RETRY_MILLIS = 4 * 1000;
@@ -464,14 +470,21 @@
public void
run() {
int retryCount = 0;
+ String rilSocket = "rild";
try {for (;;) {
LocalSocket s = null;
LocalSocketAddress l;
+ if (mInstanceId == null || mInstanceId == 0 ) {
+ rilSocket = SOCKET_NAME_RIL[0];
+ } else {
+ rilSocket = SOCKET_NAME_RIL[mInstanceId];
+ }
+
try {
s = new LocalSocket();
- l = new LocalSocketAddress(SOCKET_NAME_RIL,
+ l = new LocalSocketAddress(rilSocket,
LocalSocketAddress.Namespace.RESERVED);
s.connect(l);
} catch (IOException ex){
@@ -488,12 +501,12 @@
if (retryCount == 8) {
Rlog.e (RILJ_LOG_TAG,
- "Couldn't find '" + SOCKET_NAME_RIL
+ "Couldn't find '" + rilSocket
+ "' socket after " + retryCount
+ " times, continuing to retry silently");
} else if (retryCount > 0 && retryCount < 8) {
Rlog.i (RILJ_LOG_TAG,
- "Couldn't find '" + SOCKET_NAME_RIL
+ "Couldn't find '" + rilSocket
+ "' socket; retrying after timeout");
}
@@ -509,7 +522,7 @@
retryCount = 0;
mSocket = s;
- Rlog.i(RILJ_LOG_TAG, "Connected to '" + SOCKET_NAME_RIL + "' socket");
+ Rlog.i(RILJ_LOG_TAG, "Connected to '" + rilSocket + "' socket");
int length = 0;
try {
@@ -535,14 +548,14 @@
p.recycle();
}
} catch (java.io.IOException ex) {
- Rlog.i(RILJ_LOG_TAG, "'" + SOCKET_NAME_RIL + "' socket closed",
+ Rlog.i(RILJ_LOG_TAG, "'" + rilSocket + "' socket closed",
ex);
} catch (Throwable tr) {
Rlog.e(RILJ_LOG_TAG, "Uncaught exception read length=" + length +
"Exception:" + tr.toString());
}
- Rlog.i(RILJ_LOG_TAG, "Disconnected from '" + SOCKET_NAME_RIL
+ Rlog.i(RILJ_LOG_TAG, "Disconnected from '" + rilSocket
+ "' socket");
setRadioState (RadioState.RADIO_UNAVAILABLE);
@@ -571,15 +584,22 @@
//***** Constructors
public RIL(Context context, int preferredNetworkType, int cdmaSubscription) {
+ this(context, preferredNetworkType, cdmaSubscription, null);
+ }
+
+ public RIL(Context context, int preferredNetworkType,
+ int cdmaSubscription, Integer instanceId) {
super(context);
if (RILJ_LOGD) {
riljLog("RIL(context, preferredNetworkType=" + preferredNetworkType +
" cdmaSubscription=" + cdmaSubscription + ")");
}
+ mContext = context;
mCdmaSubscription = cdmaSubscription;
mPreferredNetworkType = preferredNetworkType;
mPhoneType = RILConstants.NO_PHONE;
+ mInstanceId = instanceId;
PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, RILJ_LOG_TAG);
@@ -609,6 +629,9 @@
filter.addAction(Intent.ACTION_SCREEN_OFF);
context.registerReceiver(mIntentReceiver, filter);
}
+
+ TelephonyDevController tdc = TelephonyDevController.getInstance();
+ tdc.registerRIL(this);
}
//***** CommandsInterface implementation
@@ -657,6 +680,33 @@
send(rr);
}
+ public void setUiccSubscription(int slotId, int appIndex, int subId,
+ int subStatus, Message result) {
+ //Note: This RIL request is also valid for SIM and RUIM (ICC card)
+ RILRequest rr = RILRequest.obtain(RIL_REQUEST_SET_UICC_SUBSCRIPTION, result);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+ + " slot: " + slotId + " appIndex: " + appIndex
+ + " subId: " + subId + " subStatus: " + subStatus);
+
+ rr.mParcel.writeInt(slotId);
+ rr.mParcel.writeInt(appIndex);
+ rr.mParcel.writeInt(subId);
+ rr.mParcel.writeInt(subStatus);
+
+ send(rr);
+ }
+
+ // FIXME This API should take an AID and slot ID
+ public void setDataAllowed(boolean allowed, Message result) {
+ RILRequest rr = RILRequest.obtain(RIL_REQUEST_ALLOW_DATA, result);
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+ rr.mParcel.writeInt(1);
+ rr.mParcel.writeInt(allowed ? 1 : 0);
+ send(rr);
+ }
+
@Override public void
supplyIccPin(String pin, Message result) {
supplyIccPinForApp(pin, null, result);
@@ -1141,6 +1191,16 @@
@Override
public void
+ getHardwareConfig (Message result) {
+ RILRequest rr = RILRequest.obtain(RIL_REQUEST_GET_HARDWARE_CONFIG, result);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+ send(rr);
+ }
+
+ @Override
+ public void
sendDtmf(char c, Message result) {
RILRequest rr
= RILRequest.obtain(RIL_REQUEST_DTMF, result);
@@ -2433,6 +2493,10 @@
case RIL_REQUEST_NV_WRITE_ITEM: ret = responseVoid(p); break;
case RIL_REQUEST_NV_WRITE_CDMA_PRL: ret = responseVoid(p); break;
case RIL_REQUEST_NV_RESET_CONFIG: ret = responseVoid(p); break;
+ case RIL_REQUEST_SET_UICC_SUBSCRIPTION: ret = responseVoid(p); break;
+ case RIL_REQUEST_ALLOW_DATA: ret = responseVoid(p); break;
+ case RIL_REQUEST_GET_HARDWARE_CONFIG: ret = responseHardwareConfig(p); break;
+ case RIL_REQUEST_ICC_SIM_AUTHENTICATION: ret = responseString(p); break;
default:
throw new RuntimeException("Unrecognized solicited response: " + rr.mRequest);
//break;
@@ -2556,6 +2620,13 @@
sb.append(cell).append(" ");
}
s = sb.toString();
+ } else if (req == RIL_REQUEST_GET_HARDWARE_CONFIG) {
+ ArrayList<HardwareConfig> hwcfgs = (ArrayList<HardwareConfig>) ret;
+ sb = new StringBuilder(" ");
+ for (HardwareConfig hwcfg : hwcfgs) {
+ sb.append("[").append(hwcfg).append("] ");
+ }
+ s = sb.toString();
} else {
s = ret.toString();
}
@@ -2613,6 +2684,9 @@
case RIL_UNSOL_VOICE_RADIO_TECH_CHANGED: ret = responseInts(p); break;
case RIL_UNSOL_CELL_INFO_LIST: ret = responseCellInfoList(p); break;
case RIL_UNSOL_RESPONSE_IMS_NETWORK_STATE_CHANGED: ret = responseVoid(p); break;
+ case RIL_UNSOL_UICC_SUBSCRIPTION_STATUS_CHANGED: ret = responseInts(p); break;
+ case RIL_UNSOL_SRVCC_STATE_NOTIFY: ret = responseInts(p); break;
+ case RIL_UNSOL_HARDWARE_CONFIG_CHANGED: ret = responseHardwareConfig(p); break;
default:
throw new RuntimeException("Unrecognized unsol response: " + response);
@@ -2986,6 +3060,32 @@
}
break;
}
+ case RIL_UNSOL_UICC_SUBSCRIPTION_STATUS_CHANGED: {
+ if (RILJ_LOGD) unsljLogRet(response, ret);
+
+ if (mSubscriptionStatusRegistrants != null) {
+ mSubscriptionStatusRegistrants.notifyRegistrants(
+ new AsyncResult (null, ret, null));
+ }
+ break;
+ }
+ case RIL_UNSOL_SRVCC_STATE_NOTIFY: {
+ if (RILJ_LOGD) unsljLogRet(response, ret);
+
+ if (mSrvccStateRegistrants != null) {
+ mSrvccStateRegistrants
+ .notifyRegistrants(new AsyncResult(null, ret, null));
+ }
+ break;
+ }
+ case RIL_UNSOL_HARDWARE_CONFIG_CHANGED:
+ if (RILJ_LOGD) unsljLogRet(response, ret);
+
+ if (mHardwareConfigChangeRegistrants != null) {
+ mHardwareConfigChangeRegistrants.notifyRegistrants(
+ new AsyncResult (null, ret, null));
+ }
+ break;
}
}
@@ -3284,6 +3384,10 @@
if (!TextUtils.isEmpty(gateways)) {
dataCall.gateways = gateways.split(" ");
}
+ String pcscf = p.readString();
+ if (!TextUtils.isEmpty(pcscf)) {
+ dataCall.pcscf = pcscf.split(" ");
+ }
}
return dataCall;
}
@@ -3339,6 +3443,13 @@
dataCall.gateways = gateways.split(" ");
}
}
+ if (num >= 6) {
+ String pcscf = p.readString();
+ if (RILJ_LOGD) riljLog("responseSetupDataCall got pcscf=" + pcscf);
+ if (!TextUtils.isEmpty(pcscf)) {
+ dataCall.pcscf = pcscf.split(" ");
+ }
+ }
} else {
if (num != 1) {
throw new RuntimeException(
@@ -3617,6 +3728,44 @@
return response;
}
+ private Object
+ responseHardwareConfig(Parcel p) {
+ int num;
+ ArrayList<HardwareConfig> response;
+ HardwareConfig hw;
+
+ num = p.readInt();
+ response = new ArrayList<HardwareConfig>(num);
+
+ if (RILJ_LOGV) {
+ riljLog("responseHardwareConfig: num=" + num);
+ }
+ for (int i = 0 ; i < num ; i++) {
+ int type = p.readInt();
+ switch(type) {
+ case HardwareConfig.DEV_HARDWARE_TYPE_MODEM: {
+ hw = new HardwareConfig(type);
+ hw.assignModem(p.readString(), p.readInt(), p.readInt(),
+ p.readInt(), p.readInt(), p.readInt(), p.readInt());
+ break;
+ }
+ case HardwareConfig.DEV_HARDWARE_TYPE_SIM: {
+ hw = new HardwareConfig(type);
+ hw.assignSim(p.readString(), p.readInt(), p.readString());
+ break;
+ }
+ default: {
+ throw new RuntimeException(
+ "RIL_REQUEST_GET_HARDWARE_CONFIG invalid hardward type:" + type);
+ }
+ }
+
+ response.add(hw);
+ }
+
+ return response;
+ }
+
static String
requestToString(int request) {
/*
@@ -3746,6 +3895,10 @@
case RIL_REQUEST_NV_WRITE_ITEM: return "RIL_REQUEST_NV_WRITE_ITEM";
case RIL_REQUEST_NV_WRITE_CDMA_PRL: return "RIL_REQUEST_NV_WRITE_CDMA_PRL";
case RIL_REQUEST_NV_RESET_CONFIG: return "RIL_REQUEST_NV_RESET_CONFIG";
+ case RIL_REQUEST_SET_UICC_SUBSCRIPTION: return "RIL_REQUEST_SET_UICC_SUBSCRIPTION";
+ case RIL_REQUEST_ALLOW_DATA: return "RIL_REQUEST_ALLOW_DATA";
+ case RIL_REQUEST_GET_HARDWARE_CONFIG: return "GET_HARDWARE_CONFIG";
+ case RIL_REQUEST_ICC_SIM_AUTHENTICATION: return "RIL_REQUEST_SIM_AUTHENTICATION";
default: return "<unknown request>";
}
}
@@ -3798,16 +3951,23 @@
case RIL_UNSOL_CELL_INFO_LIST: return "UNSOL_CELL_INFO_LIST";
case RIL_UNSOL_RESPONSE_IMS_NETWORK_STATE_CHANGED:
return "UNSOL_RESPONSE_IMS_NETWORK_STATE_CHANGED";
+ case RIL_UNSOL_UICC_SUBSCRIPTION_STATUS_CHANGED:
+ return "RIL_UNSOL_UICC_SUBSCRIPTION_STATUS_CHANGED";
+ case RIL_UNSOL_SRVCC_STATE_NOTIFY:
+ return "UNSOL_SRVCC_STATE_NOTIFY";
+ case RIL_UNSOL_HARDWARE_CONFIG_CHANGED: return "RIL_UNSOL_HARDWARE_CONFIG_CHANGED";
default: return "<unknown response>";
}
}
private void riljLog(String msg) {
- Rlog.d(RILJ_LOG_TAG, msg);
+ Rlog.d(RILJ_LOG_TAG, msg
+ + (mInstanceId != null ? (" [SUB" + mInstanceId + "]") : ""));
}
private void riljLogv(String msg) {
- Rlog.v(RILJ_LOG_TAG, msg);
+ Rlog.v(RILJ_LOG_TAG, msg
+ + (mInstanceId != null ? (" [SUB" + mInstanceId + "]") : ""));
}
private void unsljLog(int response) {
@@ -4037,6 +4197,17 @@
send(rr);
}
+ @Override
+ public void requestIccSimAuthentication(String data, Message response) {
+ RILRequest rr = RILRequest.obtain(RIL_REQUEST_ICC_SIM_AUTHENTICATION, response);
+
+ rr.mParcel.writeString(data);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+ send(rr);
+ }
+
/**
* {@inheritDoc}
*/
diff --git a/src/java/com/android/internal/telephony/ServiceStateTracker.java b/src/java/com/android/internal/telephony/ServiceStateTracker.java
index f737ab8..8462427 100644
--- a/src/java/com/android/internal/telephony/ServiceStateTracker.java
+++ b/src/java/com/android/internal/telephony/ServiceStateTracker.java
@@ -16,7 +16,9 @@
package com.android.internal.telephony;
+import android.app.PendingIntent;
import android.content.Context;
+import android.content.IntentFilter;
import android.os.AsyncResult;
import android.os.Handler;
import android.os.Message;
@@ -26,6 +28,7 @@
import android.telephony.CellInfo;
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
+import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Pair;
import android.util.TimeUtils;
@@ -106,8 +109,8 @@
protected RegistrantList mPsRestrictDisabledRegistrants = new RegistrantList();
/* Radio power off pending flag and tag counter */
- private boolean mPendingRadioPowerOffAfterDataOff = false;
- private int mPendingRadioPowerOffAfterDataOffTag = 0;
+ protected boolean mPendingRadioPowerOffAfterDataOff = false;
+ protected int mPendingRadioPowerOffAfterDataOffTag = 0;
/** Signal strength poll rate. */
protected static final int POLL_PERIOD_MILLIS = 20 * 1000;
@@ -156,9 +159,10 @@
protected static final int EVENT_CDMA_SUBSCRIPTION_SOURCE_CHANGED = 39;
protected static final int EVENT_CDMA_PRL_VERSION_CHANGED = 40;
protected static final int EVENT_RADIO_ON = 41;
- protected static final int EVENT_ICC_CHANGED = 42;
+ public static final int EVENT_ICC_CHANGED = 42;
protected static final int EVENT_GET_CELL_INFO_LIST = 43;
protected static final int EVENT_UNSOL_CELL_INFO_LIST = 44;
+ protected static final int EVENT_CHANGE_IMS_STATE = 45;
protected static final String TIMEZONE_PROPERTY = "persist.sys.timezone";
@@ -200,6 +204,13 @@
protected static final String REGISTRATION_DENIED_GEN = "General";
protected static final String REGISTRATION_DENIED_AUTH = "Authentication Failure";
+ protected boolean mImsRegistrationOnOff = false;
+ protected boolean mAlarmSwitch = false;
+ protected IntentFilter mIntentFilter = null;
+ protected PendingIntent mRadioOffIntent = null;
+ protected static final String ACTION_RADIO_OFF = "android.intent.action.ACTION_RADIO_OFF";
+ protected boolean mPowerOffDelayNeed = true;
+
protected ServiceStateTracker(PhoneBase phoneBase, CommandsInterface ci, CellInfo cellInfo) {
mPhoneBase = phoneBase;
mCellInfo = cellInfo;
@@ -455,6 +466,8 @@
public abstract int getCurrentDataConnectionState();
public abstract boolean isConcurrentVoiceAndDataAllowed();
+ public abstract void setImsRegistrationState(boolean registered);
+
/**
* Registration point for transition into DataConnection attached.
* @param h handler to notify
@@ -707,6 +720,10 @@
return retVal;
}
+ public String getSystemProperty(String property, String defValue) {
+ return TelephonyManager.getTelephonyProperty(property, mPhoneBase.getSubId(), defValue);
+ }
+
/**
* @return all available cell information or null if none.
*/
@@ -798,6 +815,7 @@
// if we have a change in operator, notify wifi (even to/from none)
if (((newOp == null) && (TextUtils.isEmpty(oldOp) == false)) ||
((newOp != null) && (newOp.equals(oldOp) == false))) {
+ log("update mccmnc=" + newOp + " fromServiceState=true");
MccTable.updateMccMncConfiguration(context, newOp, true);
}
}
diff --git a/src/java/com/android/internal/telephony/SmsStorageMonitor.java b/src/java/com/android/internal/telephony/SmsStorageMonitor.java
old mode 100644
new mode 100755
index 820c408..ca5d015
--- a/src/java/com/android/internal/telephony/SmsStorageMonitor.java
+++ b/src/java/com/android/internal/telephony/SmsStorageMonitor.java
@@ -26,6 +26,7 @@
import android.os.PowerManager;
import android.provider.Telephony.Sms.Intents;
import android.telephony.Rlog;
+import android.telephony.SubscriptionManager;
/**
* Monitors the device and ICC storage, and sends the appropriate events.
@@ -54,6 +55,9 @@
private boolean mReportMemoryStatusPending;
+ /** it is use to put in to extra value for SIM_FULL_ACTION and SMS_REJECTED_ACTION */
+ PhoneBase mPhone;
+
final CommandsInterface mCi; // accessed from inner class
boolean mStorageAvailable = true; // accessed from inner class
@@ -68,6 +72,7 @@
* @param phone the Phone to use
*/
public SmsStorageMonitor(PhoneBase phone) {
+ mPhone = phone;
mContext = phone.getContext();
mCi = phone.mCi;
@@ -139,6 +144,7 @@
// broadcast SIM_FULL intent
Intent intent = new Intent(Intents.SIM_FULL_ACTION);
mWakeLock.acquire(WAKE_LOCK_TIMEOUT);
+ SubscriptionManager.putPhoneIdAndSubIdExtra(intent, mPhone.getPhoneId());
mContext.sendBroadcast(intent, android.Manifest.permission.RECEIVE_SMS);
}
diff --git a/src/java/com/android/internal/telephony/SubInfoRecordUpdater.java b/src/java/com/android/internal/telephony/SubInfoRecordUpdater.java
new file mode 100644
index 0000000..9e3d417
--- /dev/null
+++ b/src/java/com/android/internal/telephony/SubInfoRecordUpdater.java
@@ -0,0 +1,451 @@
+/*
+* Copyright (C) 2011-2014 MediaTek Inc.
+*
+* 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 static android.Manifest.permission.READ_PHONE_STATE;
+import android.app.ActivityManagerNative;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.AsyncResult;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.SystemProperties;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.telephony.Rlog;
+import android.telephony.SubscriptionManager;
+import android.telephony.SubInfoRecord;
+import android.telephony.TelephonyManager;
+import com.android.internal.telephony.CommandsInterface;
+import com.android.internal.telephony.IccCardConstants;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.telephony.PhoneFactory;
+import com.android.internal.telephony.PhoneProxy;
+import com.android.internal.telephony.TelephonyIntents;
+import com.android.internal.telephony.TelephonyProperties;
+import com.android.internal.telephony.uicc.IccConstants;
+import com.android.internal.telephony.uicc.IccFileHandler;
+import com.android.internal.telephony.uicc.IccUtils;
+
+import java.util.List;
+
+/**
+ *@hide
+ */
+public class SubInfoRecordUpdater extends Handler {
+ private static final String LOG_TAG = "SUB";
+ private static final int PROJECT_SIM_NUM = TelephonyManager.getDefault().getPhoneCount();
+ private static final int EVENT_OFFSET = 8;
+ private static final int EVENT_QUERY_ICCID_DONE = 1;
+ private static final String ICCID_STRING_FOR_NO_SIM = "";
+ private static final int ICCID_WAIT_TIMER = 90;
+
+ /**
+ * int[] sInsertSimState maintains all slots' SIM inserted status currently,
+ * it may contain 4 kinds of values:
+ * SIM_NOT_INSERT : no SIM inserted in slot i now
+ * SIM_CHANGED : a valid SIM insert in slot i and is different SIM from last time
+ * it will later become SIM_NEW or SIM_REPOSITION during update procedure
+ * SIM_NOT_CHANGE : a valid SIM insert in slot i and is the same SIM as last time
+ * SIM_NEW : a valid SIM insert in slot i and is a new SIM
+ * SIM_REPOSITION : a valid SIM insert in slot i and is inserted in different slot last time
+ * positive integer #: index to distinguish SIM cards with the same IccId
+ */
+ public static final int SIM_NOT_CHANGE = 0;
+ public static final int SIM_CHANGED = -1;
+ public static final int SIM_NEW = -2;
+ public static final int SIM_REPOSITION = -3;
+ public static final int SIM_NOT_INSERT = -99;
+
+ public static final int STATUS_NO_SIM_INSERTED = 0x00;
+ public static final int STATUS_SIM1_INSERTED = 0x01;
+ public static final int STATUS_SIM2_INSERTED = 0x02;
+ public static final int STATUS_SIM3_INSERTED = 0x04;
+ public static final int STATUS_SIM4_INSERTED = 0x08;
+
+ private static Phone[] sPhone;
+ private static Context sContext = null;
+ private static CommandsInterface[] sCi;
+ private static IccFileHandler[] sFh = new IccFileHandler[PROJECT_SIM_NUM];
+ private static String sIccId[] = new String[PROJECT_SIM_NUM];
+ private static int[] sInsertSimState = new int[PROJECT_SIM_NUM];
+ private static TelephonyManager sTelephonyMgr = null;
+ // To prevent repeatedly update flow every time receiver SIM_STATE_CHANGE
+ private static boolean sNeedUpdate = true;
+
+ public SubInfoRecordUpdater(Context context, Phone[] phoneProxy, CommandsInterface[] ci) {
+ logd("Constructor invoked");
+
+ sContext = context;
+ sPhone = phoneProxy;
+ sCi = ci;
+ IntentFilter intentFilter = new IntentFilter(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
+ sContext.registerReceiver(sReceiver, intentFilter);
+ }
+
+ private static int encodeEventId(int event, int slotId) {
+ return event << (slotId * EVENT_OFFSET);
+ }
+
+ private final BroadcastReceiver sReceiver = new BroadcastReceiver() {
+ public void onReceive(Context context, Intent intent) {
+ logd("[Receiver]+");
+ String action = intent.getAction();
+ int slotId;
+ logd("Action: " + action);
+ if (action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED)) {
+ String simStatus = intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE);
+ slotId = intent.getIntExtra(PhoneConstants.SLOT_KEY, 0);
+ logd("slotId: " + slotId + " simStatus: " + simStatus);
+ if (IccCardConstants.INTENT_VALUE_ICC_READY.equals(simStatus)
+ || IccCardConstants.INTENT_VALUE_ICC_LOCKED.equals(simStatus)) {
+ if (sIccId[slotId] != null && sIccId[slotId].equals(ICCID_STRING_FOR_NO_SIM)) {
+ logd("SIM" + (slotId + 1) + " hot plug in");
+ sIccId[slotId] = null;
+ sNeedUpdate = true;
+ }
+ queryIccId(slotId);
+ } else if (IccCardConstants.INTENT_VALUE_ICC_LOADED.equals(simStatus)) {
+ queryIccId(slotId);
+ if (sTelephonyMgr == null) {
+ sTelephonyMgr = TelephonyManager.from(sContext);
+ }
+ //setDisplayNameForNewSim(sTelephonyMgr.getSimOperatorName(slotId), slotId, SimInfoManager.SIM_SOURCE);
+ } else if (IccCardConstants.INTENT_VALUE_ICC_ABSENT.equals(simStatus)) {
+ if (sIccId[slotId] != null && !sIccId[slotId].equals(ICCID_STRING_FOR_NO_SIM)) {
+ logd("SIM" + (slotId + 1) + " hot plug out");
+ sNeedUpdate = true;
+ }
+ sFh[slotId] = null;
+ sIccId[slotId] = ICCID_STRING_FOR_NO_SIM;
+ if (isAllIccIdQueryDone() && sNeedUpdate) {
+ updateSimInfoByIccId();
+ }
+ }
+ }
+ logd("[Receiver]-");
+ }
+ };
+
+ private boolean isAllIccIdQueryDone() {
+ for (int i = 0; i < PROJECT_SIM_NUM; i++) {
+ if (sIccId[i] == null) {
+ logd("Wait for SIM" + (i + 1) + " IccId");
+ return false;
+ }
+ }
+ logd("All IccIds query complete");
+
+ return true;
+ }
+
+ public static void setDisplayNameForNewSub(String newSubName, int subId, int newNameSource) {
+ SubInfoRecord subInfo = SubscriptionManager.getSubInfoUsingSubId(sContext, subId);
+ if (subInfo != null) {
+ // overwrite SIM display name if it is not assigned by user
+ int oldNameSource = subInfo.mNameSource;
+ String oldSubName = subInfo.mDisplayName;
+ logd("[setDisplayNameForNewSub] mSubInfoIdx = " + subInfo.mSubId + ", oldSimName = " + oldSubName
+ + ", oldNameSource = " + oldNameSource + ", newSubName = " + newSubName + ", newNameSource = " + newNameSource);
+ if (oldSubName == null ||
+ (oldNameSource == SubscriptionManager.DEFAULT_SOURCE && newSubName != null) ||
+ (oldNameSource == SubscriptionManager.SIM_SOURCE && newSubName != null && !newSubName.equals(oldSubName))) {
+ SubscriptionManager.setDisplayName(sContext, newSubName, subInfo.mSubId, newNameSource);
+ }
+ } else {
+ logd("SUB" + (subId + 1) + " SubInfo not created yet");
+ }
+ }
+
+ public void handleMessage(Message msg) {
+ AsyncResult ar = (AsyncResult)msg.obj;
+ int msgNum = msg.what;
+ int slotId;
+ for (slotId = PhoneConstants.SUB1; slotId <= PhoneConstants.SUB3; slotId++) {
+ int pivot = 1 << (slotId * EVENT_OFFSET);
+ if (msgNum >= pivot) {
+ continue;
+ } else {
+ break;
+ }
+ }
+ slotId--;
+ int event = msgNum >> (slotId * EVENT_OFFSET);
+ switch (event) {
+ case EVENT_QUERY_ICCID_DONE:
+ logd("handleMessage : <EVENT_QUERY_ICCID_DONE> SIM" + (slotId + 1));
+ if (ar.exception == null) {
+ if (ar.result != null) {
+ byte[] data = (byte[])ar.result;
+ sIccId[slotId] = IccUtils.bcdToString(data, 0, data.length);
+ } else {
+ logd("Null ar");
+ sIccId[slotId] = ICCID_STRING_FOR_NO_SIM;
+ }
+ } else {
+ sIccId[slotId] = ICCID_STRING_FOR_NO_SIM;
+ logd("Query IccId fail: " + ar.exception);
+ }
+ logd("sIccId[" + slotId + "] = " + sIccId[slotId]);
+ if (isAllIccIdQueryDone() && sNeedUpdate) {
+ updateSimInfoByIccId();
+ }
+ break;
+ default:
+ logd("Unknown msg:" + msg.what);
+ }
+ }
+
+ private void queryIccId(int slotId) {
+ if (sFh[slotId] == null) {
+ logd("Getting IccFileHandler");
+ sFh[slotId] = ((PhoneProxy)sPhone[slotId]).getIccFileHandler();
+ }
+ if (sFh[slotId] != null) {
+ if (sIccId[slotId] == null) {
+ logd("Querying IccId");
+ sFh[slotId].loadEFTransparent(IccConstants.EF_ICCID, obtainMessage(encodeEventId(EVENT_QUERY_ICCID_DONE, slotId)));
+ }
+ } else {
+ sIccId[slotId] = ICCID_STRING_FOR_NO_SIM;
+ logd("sFh[" + slotId + "] is null, SIM not inserted");
+ }
+ }
+
+ synchronized public void updateSimInfoByIccId() {
+ logd("[updateSimInfoByIccId]+ Start");
+ sNeedUpdate = false;
+
+ SubscriptionManager.clearSubInfo();
+
+ for (int i = 0; i < PROJECT_SIM_NUM; i++) {
+ sInsertSimState[i] = SIM_NOT_CHANGE;
+ }
+
+ int insertedSimCount = PROJECT_SIM_NUM;
+ for (int i = 0; i < PROJECT_SIM_NUM; i++) {
+ if (ICCID_STRING_FOR_NO_SIM.equals(sIccId[i])) {
+ insertedSimCount--;
+ sInsertSimState[i] = SIM_NOT_INSERT;
+ }
+ }
+ logd("insertedSimCount = " + insertedSimCount);
+
+ int index = 0;
+ for (int i = 0; i < PROJECT_SIM_NUM; i++) {
+ if (sInsertSimState[i] == SIM_NOT_INSERT) {
+ continue;
+ }
+ index = 2;
+ for (int j = i + 1; j < PROJECT_SIM_NUM; j++) {
+ if (sInsertSimState[j] == SIM_NOT_CHANGE && sIccId[i].equals(sIccId[j])) {
+ sInsertSimState[i] = 1;
+ sInsertSimState[j] = index;
+ index++;
+ }
+ }
+ }
+
+ ContentResolver contentResolver = sContext.getContentResolver();
+ String[] oldIccId = new String[PROJECT_SIM_NUM];
+ for (int i = 0; i < PROJECT_SIM_NUM; i++) {
+ oldIccId[i] = null;
+ List<SubInfoRecord> oldSubInfo = SubscriptionController.getInstance().getSubInfoUsingSlotIdWithCheck(i, false);
+ if (oldSubInfo != null) {
+ oldIccId[i] = oldSubInfo.get(0).mIccId;
+ logd("oldSubId = " + oldSubInfo.get(0).mSubId);
+ if (sInsertSimState[i] == SIM_NOT_CHANGE && !sIccId[i].equals(oldIccId[i])) {
+ sInsertSimState[i] = SIM_CHANGED;
+ }
+ if (sInsertSimState[i] != SIM_NOT_CHANGE) {
+ ContentValues value = new ContentValues(1);
+ value.put(SubscriptionManager.SIM_ID, SubscriptionManager.SIM_NOT_INSERTED);
+ contentResolver.update(SubscriptionManager.CONTENT_URI, value,
+ SubscriptionManager._ID + "=" + Long.toString(oldSubInfo.get(0).mSubId), null);
+ }
+ } else {
+ if (sInsertSimState[i] == SIM_NOT_CHANGE) {
+ // no SIM inserted last time, but there is one SIM inserted now
+ sInsertSimState[i] = SIM_CHANGED;
+ }
+ oldIccId[i] = ICCID_STRING_FOR_NO_SIM;
+ logd("No SIM in slot " + i + " last time");
+ }
+ }
+
+ for (int i = 0; i < PROJECT_SIM_NUM; i++) {
+ logd("oldIccId[" + i + "] = " + oldIccId[i] + ", sIccId[" + i + "] = " + sIccId[i]);
+ }
+
+ //check if the inserted SIM is new SIM
+ int nNewCardCount = 0;
+ int nNewSimStatus = 0;
+ for (int i = 0; i < PROJECT_SIM_NUM; i++) {
+ if (sInsertSimState[i] == SIM_NOT_INSERT) {
+ logd("No SIM inserted in slot " + i + " this time");
+ } else {
+ if (sInsertSimState[i] > 0) {
+ //some special SIMs may have the same IccIds, add suffix to distinguish them
+ //FIXME: addSubInfoRecord can return an error.
+ SubscriptionManager.addSubInfoRecord(sContext, sIccId[i] + Integer.toString(sInsertSimState[i]), i);
+ logd("SUB" + (i + 1) + " has invalid IccId");
+ } else /*if (sInsertSimState[i] != SIM_NOT_INSERT)*/ {
+ SubscriptionManager.addSubInfoRecord(sContext, sIccId[i], i);
+ }
+ if (isNewSim(sIccId[i], oldIccId)) {
+ nNewCardCount++;
+ switch (i) {
+ case PhoneConstants.SUB1:
+ nNewSimStatus |= STATUS_SIM1_INSERTED;
+ break;
+ case PhoneConstants.SUB2:
+ nNewSimStatus |= STATUS_SIM2_INSERTED;
+ break;
+ case PhoneConstants.SUB3:
+ nNewSimStatus |= STATUS_SIM3_INSERTED;
+ break;
+ //case PhoneConstants.SUB3:
+ // nNewSimStatus |= STATUS_SIM4_INSERTED;
+ // break;
+ }
+
+ sInsertSimState[i] = SIM_NEW;
+ }
+ }
+ }
+
+ for (int i = 0; i < PROJECT_SIM_NUM; i++) {
+ if (sInsertSimState[i] == SIM_CHANGED) {
+ sInsertSimState[i] = SIM_REPOSITION;
+ }
+ logd("sInsertSimState[" + i + "] = " + sInsertSimState[i]);
+ }
+
+ long[] subIdInSlot = {-3, -3, -3, -3};
+ List<SubInfoRecord> subInfos = SubscriptionManager.getActivatedSubInfoList(sContext);
+ int nSubCount = (subInfos == null) ? 0 : subInfos.size();
+ logd("nSubCount = " + nSubCount);
+ for (int i=0; i<nSubCount; i++) {
+ SubInfoRecord temp = subInfos.get(i);
+ subIdInSlot[temp.mSlotId] = temp.mSubId;
+ logd("subIdInSlot[" + temp.mSlotId + "] = " + temp.mSubId);
+ }
+
+ // true if any slot has no SIM this time, but has SIM last time
+ boolean hasSimRemoved = false;
+ for (int i=0; i < PROJECT_SIM_NUM; i++) {
+ if (sIccId[i] != null && sIccId[i].equals(ICCID_STRING_FOR_NO_SIM) && !oldIccId[i].equals("")) {
+ hasSimRemoved = true;
+ break;
+ }
+ }
+
+ if (nNewCardCount == 0) {
+ int i;
+ if (hasSimRemoved) {
+ // no new SIM, at least one SIM is removed, check if any SIM is repositioned first
+ for (i=0; i < PROJECT_SIM_NUM; i++) {
+ if (sInsertSimState[i] == SIM_REPOSITION) {
+ logd("No new SIM detected and SIM repositioned");
+ setUpdatedData(SubscriptionManager.EXTRA_VALUE_REPOSITION_SIM, nSubCount, nNewSimStatus);
+ break;
+ }
+ }
+ if (i == PROJECT_SIM_NUM) {
+ // no new SIM, no SIM is repositioned => at least one SIM is removed
+ logd("No new SIM detected and SIM removed");
+ setUpdatedData(SubscriptionManager.EXTRA_VALUE_REMOVE_SIM, nSubCount, nNewSimStatus);
+ }
+ } else {
+ // no SIM is removed, no new SIM, just check if any SIM is repositioned
+ for (i=0; i< PROJECT_SIM_NUM; i++) {
+ if (sInsertSimState[i] == SIM_REPOSITION) {
+ logd("No new SIM detected and SIM repositioned");
+ setUpdatedData(SubscriptionManager.EXTRA_VALUE_REPOSITION_SIM, nSubCount, nNewSimStatus);
+ break;
+ }
+ }
+ if (i == PROJECT_SIM_NUM) {
+ // all status remain unchanged
+ logd("[updateSimInfoByIccId] All SIM inserted into the same slot");
+ setUpdatedData(SubscriptionManager.EXTRA_VALUE_NOCHANGE, nSubCount, nNewSimStatus);
+ }
+ }
+ } else {
+ logd("New SIM detected");
+ setUpdatedData(SubscriptionManager.EXTRA_VALUE_NEW_SIM, nSubCount, nNewSimStatus);
+ }
+
+ SubscriptionController.getInstance().updateDefaultSubId();
+ logd("[updateSimInfoByIccId]- SimInfo update complete");
+ }
+
+ private static void setUpdatedData(int detectedType, int subCount, int newSimStatus) {
+
+ Intent intent = new Intent(TelephonyIntents.ACTION_SUBINFO_RECORD_UPDATED);
+
+ logd("[setUpdatedData]+ ");
+
+ if (detectedType == SubscriptionManager.EXTRA_VALUE_NEW_SIM ) {
+ intent.putExtra(SubscriptionManager.INTENT_KEY_DETECT_STATUS, SubscriptionManager.EXTRA_VALUE_NEW_SIM);
+ intent.putExtra(SubscriptionManager.INTENT_KEY_SIM_COUNT, subCount);
+ intent.putExtra(SubscriptionManager.INTENT_KEY_NEW_SIM_SLOT, newSimStatus);
+ } else if (detectedType == SubscriptionManager.EXTRA_VALUE_REPOSITION_SIM) {
+ intent.putExtra(SubscriptionManager.INTENT_KEY_DETECT_STATUS, SubscriptionManager.EXTRA_VALUE_REPOSITION_SIM);
+ intent.putExtra(SubscriptionManager.INTENT_KEY_SIM_COUNT, subCount);
+ } else if (detectedType == SubscriptionManager.EXTRA_VALUE_REMOVE_SIM) {
+ intent.putExtra(SubscriptionManager.INTENT_KEY_DETECT_STATUS, SubscriptionManager.EXTRA_VALUE_REMOVE_SIM);
+ intent.putExtra(SubscriptionManager.INTENT_KEY_SIM_COUNT, subCount);
+ } else if (detectedType == SubscriptionManager.EXTRA_VALUE_NOCHANGE) {
+ intent.putExtra(SubscriptionManager.INTENT_KEY_DETECT_STATUS, SubscriptionManager.EXTRA_VALUE_NOCHANGE);
+ }
+
+ logd("broadcast intent ACTION_SUBINFO_RECORD_UPDATED : [" + detectedType + ", " + subCount + ", " + newSimStatus+ "]");
+ ActivityManagerNative.broadcastStickyIntent(intent, READ_PHONE_STATE, UserHandle.USER_ALL);
+ logd("[setUpdatedData]- ");
+ }
+
+ private static boolean isNewSim(String iccId, String[] oldIccId) {
+ boolean newSim = true;
+ for(int i = 0; i < PROJECT_SIM_NUM; i++) {
+ if(iccId.equals(oldIccId[i])) {
+ newSim = false;
+ break;
+ }
+ }
+ logd("newSim = " + newSim);
+
+ return newSim;
+ }
+
+ public void dispose() {
+ logd("[dispose]");
+ sContext.unregisterReceiver(sReceiver);
+ }
+
+ private static void logd(String message) {
+ Rlog.d(LOG_TAG, "[SubInfoRecordUpdater]" + message);
+ }
+}
+
diff --git a/src/java/com/android/internal/telephony/Subscription.java b/src/java/com/android/internal/telephony/Subscription.java
new file mode 100644
index 0000000..86bc201
--- /dev/null
+++ b/src/java/com/android/internal/telephony/Subscription.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright (c) 2010-2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of The Linux Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.internal.telephony;
+
+import android.text.TextUtils;
+
+import android.telephony.Rlog;
+
+/**
+ * Class holding all the information of a subscription from UICC Card.
+ */
+public final class Subscription {
+ private static final String LOG_TAG = "Subscription";
+
+ public int slotId; // Slot id
+ public int m3gppIndex; // Subscription index in the card for GSM
+ public int m3gpp2Index; // Subscription index in the card for CDMA
+ public int subId; // SUB 0 or SUB 1
+ public SubscriptionStatus subStatus; // DEACTIVATE = 0, ACTIVATE = 1,
+ // ACTIVATED = 2, DEACTIVATED = 3, INVALID = 4;
+ public String appId;
+ public String appLabel;
+ public String appType;
+ public String iccId;
+
+ private boolean DEBUG = false;
+
+ /**
+ * Subscription activation status
+ */
+ public enum SubscriptionStatus {
+ SUB_DEACTIVATE,
+ SUB_ACTIVATE,
+ SUB_ACTIVATED,
+ SUB_DEACTIVATED,
+ SUB_INVALID
+ }
+
+ public static final int SUBSCRIPTION_INDEX_INVALID = -1;
+
+ public Subscription() {
+ clear();
+ }
+
+ public String toString() {
+ return "Subscription = { "
+ + "slotId = " + slotId
+ + ", 3gppIndex = " + m3gppIndex
+ + ", 3gpp2Index = " + m3gpp2Index
+ + ", subId = " + subId
+ + ", subStatus = " + subStatus
+ + ", appId = " + appId
+ + ", appLabel = " + appLabel
+ + ", appType = " + appType
+ + ", iccId = " + iccId + " }";
+ }
+
+ public boolean equals(Subscription sub) {
+ if (sub != null) {
+ if ((slotId == sub.slotId) && (m3gppIndex == sub.m3gppIndex)
+ && (m3gpp2Index == sub.m3gpp2Index) && (subId == sub.subId)
+ && (subStatus == sub.subStatus)
+ && ((TextUtils.isEmpty(appId) && TextUtils.isEmpty(sub.appId))
+ || TextUtils.equals(appId, sub.appId))
+ && ((TextUtils.isEmpty(appLabel) && TextUtils.isEmpty(sub.appLabel))
+ || TextUtils.equals(appLabel, sub.appLabel))
+ && ((TextUtils.isEmpty(appType) && TextUtils.isEmpty(sub.appType))
+ || TextUtils.equals(appType, sub.appType))
+ && ((TextUtils.isEmpty(iccId) && TextUtils.isEmpty(sub.iccId))
+ || TextUtils.equals(iccId, sub.iccId))) {
+ return true;
+ }
+ } else {
+ Rlog.d(LOG_TAG, "Subscription.equals: sub == null");
+ }
+ return false;
+ }
+
+ /**
+ * Return true if the appIndex, appId, appLabel and iccId are matching.
+ * @param sub
+ * @return
+ */
+ public boolean isSame(Subscription sub) {
+ // Not checking the subId, subStatus and slotId, which are related to the
+ // activated status
+ if (sub != null) {
+ if (DEBUG) {
+ Rlog.d(LOG_TAG, "isSame(): this = " + m3gppIndex
+ + ":" + m3gpp2Index
+ + ":" + appId
+ + ":" + appType
+ + ":" + iccId);
+ Rlog.d(LOG_TAG, "compare with = " + sub.m3gppIndex
+ + ":" + sub.m3gpp2Index
+ + ":" + sub.appId
+ + ":" + sub.appType
+ + ":" + sub.iccId);
+ }
+ if ((m3gppIndex == sub.m3gppIndex)
+ && (m3gpp2Index == sub.m3gpp2Index)
+ && ((TextUtils.isEmpty(appId) && TextUtils.isEmpty(sub.appId))
+ || TextUtils.equals(appId, sub.appId))
+ && ((TextUtils.isEmpty(appType) && TextUtils.isEmpty(sub.appType))
+ || TextUtils.equals(appType, sub.appType))
+ && ((TextUtils.isEmpty(iccId) && TextUtils.isEmpty(sub.iccId))
+ || TextUtils.equals(iccId, sub.iccId))){
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Reset the subscription
+ */
+ public void clear() {
+ slotId = SUBSCRIPTION_INDEX_INVALID;
+ m3gppIndex = SUBSCRIPTION_INDEX_INVALID;
+ m3gpp2Index = SUBSCRIPTION_INDEX_INVALID;
+ subId = SUBSCRIPTION_INDEX_INVALID;
+ subStatus = SubscriptionStatus.SUB_INVALID;
+ appId = null;
+ appLabel = null;
+ appType = null;
+ iccId = null;
+ }
+
+ /**
+ * Copies the subscription parameters
+ * @param from
+ * @return
+ */
+ public Subscription copyFrom(Subscription from) {
+ if (from != null) {
+ slotId = from.slotId;
+ m3gppIndex = from.m3gppIndex;
+ m3gpp2Index = from.m3gpp2Index;
+ subId = from.subId;
+ subStatus = from.subStatus;
+ if (from.appId != null) {
+ appId = new String(from.appId);
+ }
+ if (from.appLabel != null) {
+ appLabel = new String(from.appLabel);
+ }
+ if (from.appType != null) {
+ appType = new String(from.appType);
+ }
+ if (from.iccId != null) {
+ iccId = new String(from.iccId);
+ }
+ }
+
+ return this;
+ }
+
+ /**
+ * Return the valid app index (either 3gpp or 3gpp2 index)
+ * @return
+ */
+ public int getAppIndex() {
+ if (this.m3gppIndex != SUBSCRIPTION_INDEX_INVALID) {
+ return this.m3gppIndex;
+ } else {
+ return this.m3gpp2Index;
+ }
+ }
+}
diff --git a/src/java/com/android/internal/telephony/SubscriptionController.java b/src/java/com/android/internal/telephony/SubscriptionController.java
new file mode 100644
index 0000000..6ea3425
--- /dev/null
+++ b/src/java/com/android/internal/telephony/SubscriptionController.java
@@ -0,0 +1,1111 @@
+/*
+* Copyright (C) 2011-2014 MediaTek Inc.
+*
+* 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.Manifest;
+import android.app.AppOpsManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.os.AsyncResult;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.Message;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.telephony.Rlog;
+import android.util.Log;
+import android.net.Uri;
+import android.database.Cursor;
+import android.content.Intent;
+import android.provider.BaseColumns;
+import android.provider.Settings;
+import android.provider.Settings.SettingNotFoundException;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+
+import com.android.internal.telephony.ISub;
+import com.android.internal.telephony.dataconnection.DctController;
+import com.android.internal.telephony.uicc.IccConstants;
+import com.android.internal.telephony.uicc.IccFileHandler;
+import android.telephony.SubscriptionManager;
+import android.telephony.SubInfoRecord;
+import android.telephony.TelephonyManager;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map.Entry;
+/**
+ * SubscriptionController to provide an inter-process communication to
+ * access Sms in Icc.
+ */
+public class SubscriptionController extends ISub.Stub {
+ static final String LOG_TAG = "SUB";
+ static final boolean DBG = true;
+ static final boolean VDBG = false;
+
+ protected final Object mLock = new Object();
+ protected boolean mSuccess;
+
+ /** The singleton instance. */
+ private static SubscriptionController sInstance = null;
+ protected static Phone mPhone;
+ protected static Context mContext;
+
+ private DataConnectionHandler mDataConnectionHandler;
+
+ public static final Uri CONTENT_URI =
+ Uri.parse("content://telephony/siminfo");
+
+ public static final int DEFAULT_INT_VALUE = -100;
+
+ public static final String DEFAULT_STRING_VALUE = "N/A";
+
+ private static final int EVENT_SET_DEFAULT_DATA_DONE = 1;
+
+ public static final int EXTRA_VALUE_NEW_SIM = 1;
+ public static final int EXTRA_VALUE_REMOVE_SIM = 2;
+ public static final int EXTRA_VALUE_REPOSITION_SIM = 3;
+ public static final int EXTRA_VALUE_NOCHANGE = 4;
+
+ public static final String INTENT_KEY_DETECT_STATUS = "simDetectStatus";
+ public static final String INTENT_KEY_SIM_COUNT = "simCount";
+ public static final String INTENT_KEY_NEW_SIM_SLOT = "newSIMSlot";
+ public static final String INTENT_KEY_NEW_SIM_STATUS = "newSIMStatus";
+
+ /**
+ * The ICC ID of a SIM.
+ * <P>Type: TEXT (String)</P>
+ */
+ public static final String ICC_ID = "icc_id";
+
+ /**
+ * <P>Type: INTEGER (int)</P>
+ */
+ public static final String SIM_ID = "sim_id";
+
+ public static final int SIM_NOT_INSERTED = -1;
+
+ /**
+ * The display name of a SIM.
+ * <P>Type: TEXT (String)</P>
+ */
+ public static final String DISPLAY_NAME = "display_name";
+
+ public static final int DEFAULT_NAME_RES = com.android.internal.R.string.unknownName;
+
+ /**
+ * The display name source of a SIM.
+ * <P>Type: INT (int)</P>
+ */
+ public static final String NAME_SOURCE = "name_source";
+
+ public static final int DEFAULT_SOURCE = 0;
+
+ public static final int SIM_SOURCE = 1;
+
+ public static final int USER_INPUT = 2;
+
+ /**
+ * The color of a SIM.
+ * <P>Type: INTEGER (int)</P>
+ */
+ public static final String COLOR = "color";
+
+ public static final int COLOR_1 = 0;
+
+ public static final int COLOR_2 = 1;
+
+ public static final int COLOR_3 = 2;
+
+ public static final int COLOR_4 = 3;
+
+ public static final int COLOR_DEFAULT = COLOR_1;
+
+ /**
+ * The phone number of a SIM.
+ * <P>Type: TEXT (String)</P>
+ */
+ public static final String NUMBER = "number";
+
+ /**
+ * The number display format of a SIM.
+ * <P>Type: INTEGER (int)</P>
+ */
+ public static final String DISPLAY_NUMBER_FORMAT = "display_number_format";
+
+ public static final int DISPALY_NUMBER_NONE = 0;
+
+ public static final int DISPLAY_NUMBER_FIRST = 1;
+
+ public static final int DISPLAY_NUMBER_LAST = 2;
+
+ public static final int DISLPAY_NUMBER_DEFAULT = DISPLAY_NUMBER_FIRST;
+
+ /**
+ * Permission for data roaming of a SIM.
+ * <P>Type: INTEGER (int)</P>
+ */
+ public static final String DATA_ROAMING = "data_roaming";
+
+ public static final int DATA_ROAMING_ENABLE = 1;
+
+ public static final int DATA_ROAMING_DISABLE = 0;
+
+ public static final int DATA_ROAMING_DEFAULT = DATA_ROAMING_DISABLE;
+
+ private static final int RES_TYPE_BACKGROUND_DARK = 0;
+
+ private static final int RES_TYPE_BACKGROUND_LIGHT = 1;
+
+ private static final int[] sSimBackgroundDarkRes = setSimResource(RES_TYPE_BACKGROUND_DARK);
+
+ private static final int[] sSimBackgroundLightRes = setSimResource(RES_TYPE_BACKGROUND_LIGHT);
+
+ private static HashMap<Integer, Long> mSimInfo = new HashMap<Integer, Long>();
+ // FIXME define an invalid SUB_ID, and use that below instead of "1".
+ private static long mDefaultVoiceSubId = 1;
+ private static int mDefaultPhoneId = 0;
+
+ public static SubscriptionController init(Phone phone) {
+ synchronized (SubscriptionController.class) {
+ if (sInstance == null) {
+ sInstance = new SubscriptionController(phone);
+ } else {
+ Log.wtf(LOG_TAG, "init() called multiple times! sInstance = " + sInstance);
+ }
+ return sInstance;
+ }
+ }
+
+ public static SubscriptionController init(Context c, CommandsInterface[] ci) {
+ synchronized (SubscriptionController.class) {
+ if (sInstance == null) {
+ sInstance = new SubscriptionController(c);
+ } else {
+ Log.wtf(LOG_TAG, "init() called multiple times! sInstance = " + sInstance);
+ }
+ return sInstance;
+ }
+ }
+
+ public static SubscriptionController getInstance() {
+ if (sInstance == null)
+ {
+ Log.wtf(LOG_TAG, "getInstance null");
+ }
+
+ return sInstance;
+ }
+
+ private SubscriptionController(Context c) {
+ mContext = c;
+
+ if(ServiceManager.getService("isub") == null) {
+ ServiceManager.addService("isub", this);
+ }
+
+ mDataConnectionHandler = new DataConnectionHandler();
+ logd("SubscriptionController init by Context");
+ }
+
+ private boolean isSubInfoReady() {
+ return (mSimInfo.size() > 0) ? true : false;
+ }
+
+ private SubscriptionController(Phone phone) {
+ mContext = phone.getContext();
+
+ if(ServiceManager.getService("isub") == null) {
+ ServiceManager.addService("isub", this);
+ }
+
+ logd("SubscriptionController init by Phone");
+ }
+
+ /**
+ * Broadcast when subinfo settings has chanded
+ * @SubId The unique SubInfoRecord index in database
+ * @param columnName The column that is updated
+ * @param intContent The updated integer value
+ * @param stringContent The updated string value
+ */
+ private void broadcastSimInfoContentChanged(long subId,
+ String columnName, int intContent, String stringContent) {
+ Intent intent = new Intent(TelephonyIntents.ACTION_SUBINFO_CONTENT_CHANGE);
+ intent.putExtra(BaseColumns._ID, subId);
+ intent.putExtra(TelephonyIntents.EXTRA_COLUMN_NAME, columnName);
+ intent.putExtra(TelephonyIntents.EXTRA_INT_CONTENT, intContent);
+ intent.putExtra(TelephonyIntents.EXTRA_STRING_CONTENT, stringContent);
+ if (intContent != DEFAULT_INT_VALUE) {
+ logd("SubInfoRecord" + subId + " changed, " + columnName + " -> " + intContent);
+ } else {
+ logd("SubInfoRecord" + subId + " changed, " + columnName + " -> " + stringContent);
+ }
+ mContext.sendBroadcast(intent);
+ }
+
+
+ /**
+ * New SubInfoRecord instance and fill in detail info
+ * @param cursor
+ * @return the query result of desired SubInfoRecord
+ */
+ private SubInfoRecord getSubInfoRecord(Cursor cursor) {
+ SubInfoRecord info = new SubInfoRecord();
+ info.mSubId = cursor.getLong(cursor.getColumnIndexOrThrow(BaseColumns._ID));
+ info.mIccId = cursor.getString(cursor.getColumnIndexOrThrow(ICC_ID));
+ info.mSlotId = cursor.getInt(cursor.getColumnIndexOrThrow(SIM_ID));
+ info.mDisplayName = cursor.getString(cursor.getColumnIndexOrThrow(DISPLAY_NAME));
+ info.mNameSource = cursor.getInt(cursor.getColumnIndexOrThrow(NAME_SOURCE));
+ info.mColor = cursor.getInt(cursor.getColumnIndexOrThrow(COLOR));
+ info.mNumber = cursor.getString(cursor.getColumnIndexOrThrow(NUMBER));
+ info.mDispalyNumberFormat = cursor.getInt(cursor.getColumnIndexOrThrow(DISPLAY_NUMBER_FORMAT));
+ info.mDataRoaming = cursor.getInt(cursor.getColumnIndexOrThrow(DATA_ROAMING));
+
+ int size = sSimBackgroundDarkRes.length;
+ if (info.mColor >= 0 && info.mColor < size) {
+ info.mSimIconRes[RES_TYPE_BACKGROUND_DARK] = sSimBackgroundDarkRes[info.mColor];
+ info.mSimIconRes[RES_TYPE_BACKGROUND_LIGHT] = sSimBackgroundLightRes[info.mColor];
+ }
+ logd("[getSubInfoRecord] SubId:" + info.mSubId + " iccid:" + info.mIccId + " slotId:" + info.mSlotId
+ + " displayName:" + info.mDisplayName + " color:" + info.mColor);
+
+ return info;
+ }
+
+ /**
+ * Query SubInfoRecord(s) from subinfo database
+ * @param selection A filter declaring which rows to return
+ * @param queryKey query key content
+ * @return Array list of queried result from database
+ */
+ private List<SubInfoRecord> getSubInfo(String selection, Object queryKey) {
+ logd("selection:" + selection + " " + queryKey);
+ String[] selectionArgs = null;
+ if (queryKey != null) {
+ selectionArgs = new String[] {queryKey.toString()};
+ }
+ ArrayList<SubInfoRecord> subList = null;
+ Cursor cursor = mContext.getContentResolver().query(CONTENT_URI,
+ null, selection, selectionArgs, null);
+ try {
+ if (cursor != null) {
+ while (cursor.moveToNext()) {
+ SubInfoRecord subInfo = getSubInfoRecord(cursor);
+ if (subInfo != null)
+ {
+ if (subList == null)
+ {
+ subList = new ArrayList<SubInfoRecord>();
+ }
+ subList.add(subInfo);
+ }
+ }
+ } else {
+ logd("Query fail");
+ }
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+
+ return subList;
+ }
+
+
+
+ /**
+ * Get the SubInfoRecord according to an index
+ * @param subId The unique SubInfoRecord index in database
+ * @return SubInfoRecord, maybe null
+ */
+ @Override
+ public SubInfoRecord getSubInfoUsingSubId(long subId) {
+ logd("[getSubInfoUsingSubIdx]+ subId:" + subId);
+ if (subId <= 0 || !isSubInfoReady()) {
+ logd("[getSubInfoUsingSubIdx]- subId <= 0 or not ready");
+ return null;
+ }
+ Cursor cursor = mContext.getContentResolver().query(CONTENT_URI,
+ null, BaseColumns._ID + "=?", new String[] {Long.toString(subId)}, null);
+ try {
+ if (cursor != null) {
+ if (cursor.moveToFirst()) {
+ logd("[getSubInfoUsingSubIdx]- Info detail:");
+ return getSubInfoRecord(cursor);
+ }
+ }
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+ logd("[getSubInfoUsingSubIdx]- null info return");
+
+ return null;
+ }
+
+ /**
+ * Get the SubInfoRecord according to an IccId
+ * @param iccId the IccId of SIM card
+ * @return SubInfoRecord, maybe null
+ */
+ @Override
+ public List<SubInfoRecord> getSubInfoUsingIccId(String iccId) {
+ logd("[getSubInfoUsingIccId]+ iccId:" + iccId);
+ if (iccId == null || !isSubInfoReady()) {
+ logd("[getSubInfoUsingIccId]- null iccid or not ready");
+ return null;
+ }
+ Cursor cursor = mContext.getContentResolver().query(CONTENT_URI,
+ null, ICC_ID + "=?", new String[] {iccId}, null);
+ ArrayList<SubInfoRecord> subList = null;
+ try {
+ if (cursor != null) {
+ while (cursor.moveToNext()) {
+ SubInfoRecord subInfo = getSubInfoRecord(cursor);
+ if (subInfo != null)
+ {
+ if (subList == null)
+ {
+ subList = new ArrayList<SubInfoRecord>();
+ }
+ subList.add(subInfo);
+ }
+ }
+ } else {
+ logd("Query fail");
+ }
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+
+ return subList;
+ }
+
+ /**
+ * Get the SubInfoRecord according to slotId
+ * @param slotId the slot which the SIM is inserted
+ * @return SubInfoRecord, maybe null
+ */
+ @Override
+ public List<SubInfoRecord> getSubInfoUsingSlotId(int slotId) {
+ return getSubInfoUsingSlotIdWithCheck(slotId, true);
+ }
+
+ /**
+ * Get all the SubInfoRecord(s) in subinfo database
+ * @return Array list of all SubInfoRecords in database, include thsoe that were inserted before
+ */
+ @Override
+ public List<SubInfoRecord> getAllSubInfoList() {
+ logd("[getAllSubInfoList]+");
+ List<SubInfoRecord> subList = null;
+ subList = getSubInfo(null, null);
+ if (subList != null) {
+ logd("[getAllSubInfoList]- " + subList.size() + " infos return");
+ } else {
+ logd("[getAllSubInfoList]- no info return");
+ }
+
+ return subList;
+ }
+
+ /**
+ * Get the SubInfoRecord(s) of the currently inserted SIM(s)
+ * @return Array list of currently inserted SubInfoRecord(s)
+ */
+ @Override
+ public List<SubInfoRecord> getActivatedSubInfoList() {
+ logd("[getActivatedSubInfoList]+");
+ List<SubInfoRecord> subList = null;
+
+ if (!isSubInfoReady()) {
+ logd("[getActivatedSubInfoList] Sub Controller not ready");
+ return subList;
+ }
+
+ subList = getSubInfo(SIM_ID + "!=" + SIM_NOT_INSERTED, null);
+ if (subList != null) {
+ logd("[getActivatedSubInfoList]- " + subList.size() + " infos return");
+ } else {
+ logd("[getActivatedSubInfoList]- no info return");
+ }
+
+ return subList;
+ }
+
+ /**
+ * Get the SUB count of all SUB(s) in subinfo database
+ * @return all SIM count in database, include what was inserted before
+ */
+ @Override
+ public int getAllSubInfoCount() {
+ logd("[getAllSubInfoCount]+");
+ Cursor cursor = mContext.getContentResolver().query(CONTENT_URI,
+ null, null, null, null);
+ try {
+ if (cursor != null) {
+ int count = cursor.getCount();
+ logd("[getAllSubInfoCount]- " + count + " SUB(s) in DB");
+ return count;
+ }
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+ logd("[getAllSubInfoCount]- no SUB in DB");
+
+ return 0;
+ }
+
+ /**
+ * Add a new SubInfoRecord to subinfo database if needed
+ * @param iccId the IccId of the SIM card
+ * @param slotId the slot which the SIM is inserted
+ * @return the URL of the newly created row or the updated row
+ */
+ @Override
+ public int addSubInfoRecord(String iccId, int slotId) {
+ logd("[addSubInfoRecord]+ iccId:" + iccId + " slotId:" + slotId);
+ if (iccId == null) {
+ logd("[addSubInfoRecord]- null iccId");
+ }
+ Uri uri = null;
+ String nameToSet;
+ nameToSet = "SUB 0"+Integer.toString(slotId+1);
+ ContentResolver resolver = mContext.getContentResolver();
+ Cursor cursor = resolver.query(CONTENT_URI, new String[] {BaseColumns._ID, SIM_ID, NAME_SOURCE},
+ ICC_ID + "=?", new String[] {iccId}, null);
+ try {
+ if (cursor == null || !cursor.moveToFirst()) {
+ ContentValues value = new ContentValues();
+ value.put(ICC_ID, iccId);
+ // default SIM color differs between slots
+ value.put(COLOR, slotId);
+ value.put(SIM_ID, slotId);
+ value.put(DISPLAY_NAME, nameToSet);
+ uri = resolver.insert(CONTENT_URI, value);
+ logd("[addSubInfoRecord] New record creating values=" + value);
+ logd("[addSubInfoRecord] New record result uri=" + uri);
+ } else {
+ long subId = cursor.getLong(0);
+ int oldSimInfoId = cursor.getInt(1);
+ int nameSource = cursor.getInt(2);
+ ContentValues value = new ContentValues();
+
+ if (slotId != oldSimInfoId) {
+ value.put(SIM_ID, slotId);
+ }
+
+ if (nameSource != USER_INPUT) {
+ value.put(DISPLAY_NAME, nameToSet);
+ }
+
+ if (value.size() > 0) {
+ String where = BaseColumns._ID + "=" + Long.toString(subId);
+ logd("[addSubInfoRecord] resolver.update value=" + value + " where=" + where);
+ resolver.update(CONTENT_URI, value, where, null);
+ }
+
+ logd("[addSubInfoRecord] Record already exist");
+ }
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+
+ cursor = resolver.query(CONTENT_URI,
+ null, SIM_ID + "=?", new String[] {String.valueOf(slotId)}, null);
+ if (cursor == null) logd("[addSubInfoRecord] 1 cursor is null");
+
+ try {
+ if (cursor != null && cursor.moveToFirst()) {
+ do {
+ long subId = cursor.getLong(cursor.getColumnIndexOrThrow(BaseColumns._ID));
+ logd("[addSubInfoRecord] subId=" + subId + " mSimInfo.size" + mSimInfo.size());
+ // If mSinInfo is already having a valid subId for a slotId/phoneId,
+ // do not add another subId for same slotId/phoneId.
+ long[] sub = getSubId(slotId);
+
+ if ((mSimInfo.size() == 0) || (sub[0] <= 0)) {
+ // set the first entry as default sub
+ // TODO While two subs active, if user deactivats first
+ // one, need to update the default subId with second
+ // one.
+ if (mSimInfo.size() == 0) {
+ logd("[addSubInfoRecord] call setDefaultSubId subId=" + subId);
+ setDefaultSubId(subId);
+ }
+ mSimInfo.put(slotId, subId);
+ logd("[addSubInfoRecord] mSimInfo.size=" + mSimInfo.size()
+ + " slotId = " + slotId + " subId = " + subId);
+ } else {
+ logd("[addSubInfoRecord] size != 0 && sub[0] > 0, IGNORE");
+ }
+ } while (cursor.moveToNext());
+ } else {
+ logd("[addSubInfoRecord] no records for " + BaseColumns._ID);
+ }
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+
+ int size = mSimInfo.size();
+ logd("[addSubInfoRecord]- info size=" + size);
+
+ return 1;
+ }
+
+ /**
+ * Set SIM color by simInfo index
+ * @param color the color of the SIM
+ * @param subId the unique SubInfoRecord index in database
+ * @return the number of records updated
+ */
+ @Override
+ public int setColor(int color, long subId) {
+ logd("[setColor]+ color:" + color + " subId:" + subId);
+ int size = sSimBackgroundDarkRes.length;
+ if (subId <= 0 || color < 0 || color >= size) {
+ logd("[setColor]- fail");
+ return -1;
+ }
+ ContentValues value = new ContentValues(1);
+ value.put(COLOR, color);
+ logd("[setColor]- color:" + color + " set");
+
+ int result = mContext.getContentResolver().update(CONTENT_URI, value,
+ BaseColumns._ID + "=" + Long.toString(subId), null);
+ broadcastSimInfoContentChanged(subId, COLOR, color, DEFAULT_STRING_VALUE);
+
+ return result;
+ }
+
+ /**
+ * Set display name by simInfo index
+ * @param displayName the display name of SIM card
+ * @param subId the unique SubInfoRecord index in database
+ * @return the number of records updated
+ */
+ @Override
+ public int setDisplayName(String displayName, long subId) {
+ return setDisplayNameUsingSrc(displayName, subId, -1);
+ }
+
+ /**
+ * Set display name by simInfo index with name source
+ * @param displayName the display name of SIM card
+ * @param subId the unique SubInfoRecord index in database
+ * @param nameSource 0: DEFAULT_SOURCE, 1: SIM_SOURCE, 2: USER_INPUT
+ * @return the number of records updated
+ */
+ @Override
+ public int setDisplayNameUsingSrc(String displayName, long subId, long nameSource) {
+ logd("[setDisplayName]+ displayName:" + displayName + " subId:" + subId + " nameSource:" + nameSource);
+ if (subId <= 0) {
+ logd("[setDisplayName]- fail");
+ return -1;
+ }
+ String nameToSet;
+ if (displayName == null) {
+ nameToSet = mContext.getString(DEFAULT_NAME_RES);
+ } else {
+ nameToSet = displayName;
+ }
+ ContentValues value = new ContentValues(1);
+ value.put(DISPLAY_NAME, nameToSet);
+ if (nameSource >= DEFAULT_SOURCE) {
+ logd("Set nameSource=" + nameSource);
+ value.put(NAME_SOURCE, nameSource);
+ }
+ logd("[setDisplayName]- mDisplayName:" + nameToSet + " set");
+
+ int result = mContext.getContentResolver().update(CONTENT_URI, value,
+ BaseColumns._ID + "=" + Long.toString(subId), null);
+ broadcastSimInfoContentChanged(subId, DISPLAY_NAME, DEFAULT_INT_VALUE, nameToSet);
+
+ return result;
+ }
+
+ /**
+ * Set phone number by subId
+ * @param number the phone number of the SIM
+ * @param subId the unique SubInfoRecord index in database
+ * @return the number of records updated
+ */
+ @Override
+ public int setDispalyNumber(String number, long subId) {
+ logd("[setDispalyNumber]+ number:" + number + " subId:" + subId);
+ if (number == null || subId <= 0) {
+ logd("[setDispalyNumber]- fail");
+ return -1;
+ }
+ ContentValues value = new ContentValues(1);
+ value.put(NUMBER, number);
+ logd("[setDispalyNumber]- number:" + number + " set");
+
+ int result = mContext.getContentResolver().update(CONTENT_URI, value,
+ BaseColumns._ID + "=" + Long.toString(subId), null);
+ broadcastSimInfoContentChanged(subId, NUMBER, DEFAULT_INT_VALUE, number);
+
+ return result;
+ }
+
+ /**
+ * Set number display format. 0: none, 1: the first four digits, 2: the last four digits
+ * @param format the display format of phone number
+ * @param subId the unique SubInfoRecord index in database
+ * @return the number of records updated
+ */
+ @Override
+ public int setDisplayNumberFormat(int format, long subId) {
+ logd("[setDisplayNumberFormat]+ format:" + format + " subId:" + subId);
+ if (format < 0 || subId <= 0) {
+ logd("[setDisplayNumberFormat]- fail, return -1");
+ return -1;
+ }
+ ContentValues value = new ContentValues(1);
+ value.put(DISPLAY_NUMBER_FORMAT, format);
+ logd("[setDisplayNumberFormat]- format:" + format + " set");
+
+ int result = mContext.getContentResolver().update(CONTENT_URI, value,
+ BaseColumns._ID + "=" + Long.toString(subId), null);
+ broadcastSimInfoContentChanged(subId, DISPLAY_NUMBER_FORMAT, format, DEFAULT_STRING_VALUE);
+
+ return result;
+ }
+
+ /**
+ * Set data roaming by simInfo index
+ * @param roaming 0:Don't allow data when roaming, 1:Allow data when roaming
+ * @param subId the unique SubInfoRecord index in database
+ * @return the number of records updated
+ */
+ @Override
+ public int setDataRoaming(int roaming, long subId) {
+ logd("[setDataRoaming]+ roaming:" + roaming + " subId:" + subId);
+ if (roaming < 0 || subId <= 0) {
+ logd("[setDataRoaming]- fail");
+ return -1;
+ }
+ ContentValues value = new ContentValues(1);
+ value.put(DATA_ROAMING, roaming);
+ logd("[setDataRoaming]- roaming:" + roaming + " set");
+
+ int result = mContext.getContentResolver().update(CONTENT_URI, value,
+ BaseColumns._ID + "=" + Long.toString(subId), null);
+ broadcastSimInfoContentChanged(subId, DATA_ROAMING, roaming, DEFAULT_STRING_VALUE);
+
+ return result;
+ }
+
+ @Override
+ public int getSlotId(long subId) {
+ logd("[getSlotId]+ subId:" + subId);
+ if (subId <= 0) {
+ logd("[getSlotId]- subId <= 0");
+ return SIM_NOT_INSERTED;
+ }
+
+ int size = mSimInfo.size();
+ logd("[getSlotId]- info size="+size);
+
+ if (size == 0)
+ {
+ return SIM_NOT_INSERTED;
+ }
+
+ for (Entry<Integer, Long> entry: mSimInfo.entrySet()) {
+ int sim = entry.getKey();
+ long sub = entry.getValue();
+ logd("[getSlotId]- entry, sim ="+sim+", sub="+ sub);
+
+ if (subId == sub)
+ {
+ logd("[getSlotId]- return ="+sim);
+ return sim;
+ }
+ }
+
+ logd("[getSlotId]- return fail");
+ return (int)(subId-1);
+
+ }
+
+ /**
+ * Return the subId for specified sim Id.
+ * @deprecated
+ */
+ @Override
+ @Deprecated
+ public long[] getSubId(int slotId) {
+ if (VDBG) logd("[getSubId]+ slotId:" + slotId);
+
+ // FIXME this should return the subIds associated with the PhoneIds
+ // using {1, 2} for now, to workaround problem with data classes
+ // not being able to find a subscription that has value set to
+ // default data during bootup, since SubscriptionController has not
+ // discovered all the subs when this query is made.
+
+ long[] subId = new long[] {-1-slotId, -1-slotId};
+
+ if (slotId < 0) {
+ logd("[getSubId]- slotId < 0, return dummy instead");
+ return subId;
+ }
+
+ int size = mSimInfo.size();
+ if (VDBG) logd("[getSubId]- info size="+size);
+
+ if (size == 0)
+ {
+ logd("[getSubId]- size == 0, return dummy instead");
+ return subId;
+ }
+
+ int subIdx = 0;
+
+ for (Entry<Integer, Long> entry: mSimInfo.entrySet()) {
+ int sim = entry.getKey();
+ long sub = entry.getValue();
+
+ if (VDBG) logd("[getSubId]- entry, sim ="+sim+", sub="+ sub);
+ if (slotId == sim)
+ {
+ subId[subIdx] = sub;
+ if (VDBG) logd("[getSubId]- subId["+subIdx+"] = "+subId[subIdx]);
+ subIdx++;
+ }
+ }
+
+ if (VDBG) logd("[getSubId]-, subId = "+subId[0]);
+ return subId;
+ }
+
+ @Override
+ public int getPhoneId(long subId) {
+ if (VDBG) logd("[getPhoneId]+ subId:" + subId);
+ if (subId <= 0) {
+ // FIXME Do not auto map subId to phoneId
+ // May be we shoud add dummy/default subId's during
+ // initialization of subscription controller ??
+ if (subId == -1) {
+ logd("[getPhoneId]- subId == -1 return =" + 0);
+ return 0;
+ } else if (subId == -2) {
+ logd("[getPhoneId]- subId == -2 return =" + 1);
+ return 1;
+ }
+ }
+
+ int size = mSimInfo.size();
+
+ if (size == 0) {
+ if (VDBG) logd("[getPhoneId]- returning defaultPhoneId=" + mDefaultPhoneId);
+ return mDefaultPhoneId;
+ }
+
+ for (Entry<Integer, Long> entry: mSimInfo.entrySet()) {
+ int sim = entry.getKey();
+ long sub = entry.getValue();
+ if (VDBG) logd("[getPhoneId]- entry, sim="+sim+", sub="+ sub);
+
+ if (subId == sub)
+ {
+ if (VDBG) logd("[getPhoneId]- return="+sim);
+ return sim;
+ }
+ }
+
+ if (VDBG) logd("[getPhoneId]- return=" + (int)(subId-1));
+ return (int)(subId-1);
+
+ }
+
+ @Override
+ public int clearSubInfo()
+ {
+ if (VDBG) logd("[clearSubInfo]+");
+
+ int size = mSimInfo.size();
+ if (VDBG) logd("[getSubId]- info size="+size);
+
+ if (size == 0)
+ {
+ return 0;
+ }
+
+ mSimInfo.clear();
+ if (VDBG) logd("[clearSubInfo]-");
+ return 0;
+
+ }
+
+ private static int[] setSimResource(int type) {
+ int[] simResource = null;
+
+ switch (type) {
+ case RES_TYPE_BACKGROUND_DARK:
+ simResource = new int[] {
+ com.android.internal.R.drawable.sim_dark_blue,
+ com.android.internal.R.drawable.sim_dark_orange,
+ com.android.internal.R.drawable.sim_dark_green,
+ com.android.internal.R.drawable.sim_dark_purple
+ };
+ break;
+ case RES_TYPE_BACKGROUND_LIGHT:
+ simResource = new int[] {
+ com.android.internal.R.drawable.sim_light_blue,
+ com.android.internal.R.drawable.sim_light_orange,
+ com.android.internal.R.drawable.sim_light_green,
+ com.android.internal.R.drawable.sim_light_purple
+ };
+ break;
+ }
+
+ return simResource;
+ }
+
+ private void logd(String msg) {
+ Rlog.d(LOG_TAG, "[SubController]" + msg);
+ }
+
+ private void loge(String msg) {
+ Rlog.e(LOG_TAG, "[SubController]" + msg);
+ }
+
+ @Override
+ public long getDefaultSubId() {
+ if (VDBG) logd("getDefaultSubId: value=" + mDefaultVoiceSubId);
+ return mDefaultVoiceSubId;
+ }
+
+ @Override
+ public void setDefaultVoiceSubId(long subId) {
+ Settings.Global.putLong(mContext.getContentResolver(),
+ Settings.Global.MULTI_SIM_VOICE_CALL_SUBSCRIPTION, subId);
+ broadcastDefaultVoiceSubIdChanged(subId);
+ if (VDBG) logd("setDefaultVoiceSubId, subId=" + subId);
+ }
+
+ private static void broadcastDefaultVoiceSubIdChanged(long subId) {
+ // Broadcast an Intent for default voice sub change
+ Intent intent = new Intent(TelephonyIntents.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED);
+ intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
+ intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId);
+ mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
+ }
+
+ @Override
+ public long getDefaultVoiceSubId() {
+ long subId = SubscriptionManager.INVALID_SUB_ID;
+
+ try {
+ subId = Settings.Global.getLong(mContext.getContentResolver(),
+ Settings.Global.MULTI_SIM_VOICE_CALL_SUBSCRIPTION);
+ } catch (SettingNotFoundException snfe) {
+ loge("Settings Exception Reading Dual Sim Voice Call Values");
+ }
+
+ if (VDBG) logd("getDefaultVoiceSubId, value = " + subId);
+ return subId;
+ }
+
+ @Override
+ public long getDefaultDataSubId() {
+ long subId = SubscriptionManager.INVALID_SUB_ID;
+ try {
+ subId = Settings.Global.getLong(mContext.getContentResolver(),
+ Settings.Global.MULTI_SIM_DATA_CALL_SUBSCRIPTION);
+ } catch (SettingNotFoundException snfe) {
+ loge("Settings Exception Reading Dual Sim Data Call Values");
+ }
+
+ return subId;
+ }
+
+ public void setDefaultDataSubId(long subId) {
+ loge("setDataSubId: subId=" + subId + " FIXME NOP right now");
+ DctController dctController = DctController.getInstance();
+ dctController.setDataSubId(subId);
+ dctController.registerForDataSwitchInfo(mDataConnectionHandler,
+ EVENT_SET_DEFAULT_DATA_DONE, null);
+ }
+
+ private void updateDataSubId(long subId) {
+ if (VDBG) logd(" updateDataSubId, subId=" + subId);
+ Settings.Global.putLong(mContext.getContentResolver(),
+ Settings.Global.MULTI_SIM_DATA_CALL_SUBSCRIPTION, subId);
+ broadcastDefaultDataSubIdChanged(subId);
+ }
+
+ private static void broadcastDefaultDataSubIdChanged(long subId) {
+ // Broadcast an Intent for default data sub change
+ Intent intent = new Intent(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
+ intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
+ intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId);
+ mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
+ }
+
+ /* Sets the default subscription. If only one sub is active that
+ * sub is set as default subId. If two or more sub's are active
+ * the first sub is set as default subscription
+ */
+ // FIXME Modify/rename this method name as part of
+ // refactoring other subscription changes
+ public void setDefaultSubId(long subId) {
+ if (subId > 0 && subId != SubscriptionManager.INVALID_SUB_ID
+ && subId != SubscriptionManager.DEFAULT_SUB_ID) {
+ int phoneId = getPhoneId(subId);
+ if (phoneId >= 0 && phoneId < TelephonyManager.getDefault().getPhoneCount()) {
+ mDefaultVoiceSubId = subId; // returned by getFirstActiveSubId()
+ if (VDBG) logd("setDefaultSubId: mDefaultVoiceSubId=subId=" + subId);
+ // Update MCC MNC device configuration information
+ String defaultMccMnc = TelephonyManager.getDefault().getSimOperator(phoneId);
+ if (VDBG) logd("setDefaultSubId: call update mccmnc=" + defaultMccMnc);
+ MccTable.updateMccMncConfiguration(mContext, defaultMccMnc, false);
+
+ // Broadcast an Intent for default sub change
+ Intent intent = new Intent(TelephonyIntents.ACTION_DEFAULT_SUBSCRIPTION_CHANGED);
+ intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
+ SubscriptionManager.putPhoneIdAndSubIdExtra(intent, phoneId);
+ if (VDBG) logd("setDefaultSubId: " + subId + " Broadcasting Default SubId Changed");
+ mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
+ } else {
+ if (VDBG) logd("setDefaultSubId: not set invalid phoneId=" + phoneId + " subId=" + subId);
+ }
+ } else {
+ if (VDBG) logd("setDefaultSubId: not set invalid subId=" + subId);
+ }
+ }
+
+ // FIXME SubscriptionController should register (perhaps using Registrants)
+ // with SimInfoUpdater for any updates to the subscriptions. When it is
+ // notified of any updates, this API should be called. Rather than
+ // SimInfoUpdater calling into updateDefaultSubId.
+ public void updateDefaultSubId() {
+ if (!isSubActive(getDefaultDataSubId())) {
+ updateDataSubId(getFirstActiveSubId());
+ }
+
+ if (!isSubActive(getDefaultVoiceSubId())) {
+ setDefaultVoiceSubId(getFirstActiveSubId());
+ }
+ }
+
+ // FIXME As part of getDefaultSubId method cleanup, modify
+ // the mDefaultVoiceSubId to mFirstActiveSubId.
+ private long getFirstActiveSubId() {
+ if (VDBG) logd("getFirstActiveSubId, value = " + mDefaultVoiceSubId);
+ return mDefaultVoiceSubId;
+ }
+
+ private boolean isSubActive(long subId) {
+ boolean subActive = false;
+ List<SubInfoRecord> activeSubList = getActivatedSubInfoList();
+
+ if (activeSubList != null) {
+ for (SubInfoRecord subInfoRecord : activeSubList) {
+ if (subInfoRecord.mSubId == subId) {
+ if (VDBG) logd("isSubActive, found active sub " + subId);
+ subActive = true;
+ break;
+ }
+ }
+ }
+ return subActive;
+ }
+
+ private class DataConnectionHandler extends Handler {
+ @Override
+ public void handleMessage(Message msg) {
+ AsyncResult ar = (AsyncResult) msg.obj;
+ switch (msg.what) {
+ case EVENT_SET_DEFAULT_DATA_DONE:
+ Long subId = (Long)ar.result;
+ if (VDBG) logd("EVENT_SET_DEFAULT_DATA_DONE subId:" + subId);
+ updateDataSubId(subId);
+ break;
+ }
+ }
+ }
+
+ /* This should return long and not long [] since each phone has
+ * exactly 1 sub id for now, it could return the 0th element
+ * returned from getSubId()
+ */
+ // FIXME will design a mechanism to manage the relationship between PhoneId/SlotId/SubId
+ // since phoneId = SlotId is not always true
+ public long getSubIdUsingPhoneId(int phoneId) {
+ long[] subId = getSubId(phoneId);
+ return subId[0];
+ }
+
+ public long[] getSubIdUsingSlotId(int slotId) {
+ return getSubId(slotId);
+ }
+
+ public List<SubInfoRecord> getSubInfoUsingSlotIdWithCheck(int slotId, boolean needCheck) {
+ if (VDBG) logd("[getSubInfoUsingSlotIdWithCheck]+ slotId=" + slotId);
+ if (slotId < 0 ) {
+ if (VDBG) logd("[getSubInfoUsingSlotIdWithCheck]- return null, slotId < 0");
+ return null;
+ }
+
+ if (needCheck && !isSubInfoReady()) {
+ if (VDBG) logd("[getSubInfoUsingSlotIdWithCheck]- not ready return null");
+ return null;
+ }
+
+ Cursor cursor = mContext.getContentResolver().query(CONTENT_URI,
+ null, SIM_ID + "=?", new String[] {String.valueOf(slotId)}, null);
+ ArrayList<SubInfoRecord> subList = null;
+ try {
+ if (cursor != null) {
+ while (cursor.moveToNext()) {
+ SubInfoRecord subInfo = getSubInfoRecord(cursor);
+ if (subInfo != null)
+ {
+ if (subList == null)
+ {
+ subList = new ArrayList<SubInfoRecord>();
+ }
+ subList.add(subInfo);
+ }
+ }
+ }
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+ if (VDBG) logd("[getSubInfoUsingSlotId]- subList=" + subList);
+ return subList;
+
+ }
+}
diff --git a/src/java/com/android/internal/telephony/SubscriptionData.java b/src/java/com/android/internal/telephony/SubscriptionData.java
new file mode 100644
index 0000000..2ba8e90
--- /dev/null
+++ b/src/java/com/android/internal/telephony/SubscriptionData.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2010-2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of The Linux Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.internal.telephony;
+
+import java.util.Arrays;
+
+/**
+ * Class holding a list of subscriptions
+ */
+public class SubscriptionData {
+ public Subscription [] subscription;
+
+ public SubscriptionData(int numSub) {
+ subscription = new Subscription[numSub];
+ for (int i = 0; i < numSub; i++) {
+ subscription[i] = new Subscription();
+ }
+ }
+
+ public int getLength() {
+ if (subscription != null) {
+ return subscription.length;
+ }
+ return 0;
+ }
+
+ public SubscriptionData copyFrom(SubscriptionData from) {
+ if (from != null) {
+ subscription = new Subscription[from.getLength()];
+ for (int i = 0; i < from.getLength(); i++) {
+ subscription[i] = new Subscription();
+ subscription[i].copyFrom(from.subscription[i]);
+ }
+ }
+ return this;
+ }
+
+ public String getIccId() {
+ if (subscription.length > 0 && subscription[0] != null) {
+ return subscription[0].iccId;
+ }
+ return null;
+ }
+
+ public boolean hasSubscription(Subscription sub){
+ for (int i = 0; i < subscription.length; i++) {
+ if (subscription[i].isSame(sub)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public Subscription getSubscription(Subscription sub){
+ for (int i = 0; i < subscription.length; i++) {
+ if (subscription[i].isSame(sub)) {
+ return subscription[i];
+ }
+ }
+ return null;
+ }
+
+ public String toString() {
+ return Arrays.toString(subscription);
+ }
+}
diff --git a/src/java/com/android/internal/telephony/TelephonyCapabilities.java b/src/java/com/android/internal/telephony/TelephonyCapabilities.java
index bbef33e..bc1288a 100644
--- a/src/java/com/android/internal/telephony/TelephonyCapabilities.java
+++ b/src/java/com/android/internal/telephony/TelephonyCapabilities.java
@@ -136,7 +136,8 @@
*/
public static boolean supportsHoldAndUnhold(Phone phone) {
return ((phone.getPhoneType() == PhoneConstants.PHONE_TYPE_GSM)
- || (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_SIP));
+ || (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_SIP)
+ || (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_IMS));
}
/**
diff --git a/src/java/com/android/internal/telephony/TelephonyDevController.java b/src/java/com/android/internal/telephony/TelephonyDevController.java
new file mode 100644
index 0000000..448e5bd
--- /dev/null
+++ b/src/java/com/android/internal/telephony/TelephonyDevController.java
@@ -0,0 +1,343 @@
+/*
+ * Copyright (C) 2014 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.content.res.Resources;
+import com.android.internal.telephony.*;
+import android.telephony.TelephonyManager;
+
+import android.os.AsyncResult;
+import android.telephony.Rlog;
+import java.util.BitSet;
+import java.util.List;
+import java.util.ArrayList;
+import android.text.TextUtils;
+import android.os.Handler;
+import android.os.Message;
+import android.os.Registrant;
+import android.os.RegistrantList;
+import android.telephony.ServiceState;
+
+/**
+ * TelephonyDevController - provides a unified view of the
+ * telephony hardware resources on a device.
+ *
+ * manages the set of HardwareConfig for the framework.
+ */
+public class TelephonyDevController extends Handler {
+ private static final String LOG_TAG = "TDC";
+ private static final boolean DBG = true;
+ private static final Object mLock = new Object();
+
+ private static final int EVENT_HARDWARE_CONFIG_CHANGED = 1;
+
+ private static TelephonyDevController sTelephonyDevController;
+ private static ArrayList<HardwareConfig> mModems = new ArrayList<HardwareConfig>();
+ private static ArrayList<HardwareConfig> mSims = new ArrayList<HardwareConfig>();
+
+ private static Message sRilHardwareConfig;
+
+ private static void logd(String s) {
+ Rlog.d(LOG_TAG, s);
+ }
+
+ private static void loge(String s) {
+ Rlog.e(LOG_TAG, s);
+ }
+
+ public static TelephonyDevController create() {
+ synchronized (mLock) {
+ if (sTelephonyDevController != null) {
+ throw new RuntimeException("TelephonyDevController already created!?!");
+ }
+ sTelephonyDevController = new TelephonyDevController();
+ return sTelephonyDevController;
+ }
+ }
+
+ public static TelephonyDevController getInstance() {
+ synchronized (mLock) {
+ if (sTelephonyDevController == null) {
+ throw new RuntimeException("TelephonyDevController not yet created!?!");
+ }
+ return sTelephonyDevController;
+ }
+ }
+
+ private void initFromResource() {
+ Resources resource = Resources.getSystem();
+ String[] hwStrings = resource.getStringArray(
+ com.android.internal.R.array.config_telephonyHardware);
+ if (hwStrings != null) {
+ for (String hwString : hwStrings) {
+ HardwareConfig hw = new HardwareConfig(hwString);
+ if (hw != null) {
+ if (hw.type == HardwareConfig.DEV_HARDWARE_TYPE_MODEM) {
+ updateOrInsert(hw, mModems);
+ } else if (hw.type == HardwareConfig.DEV_HARDWARE_TYPE_SIM) {
+ updateOrInsert(hw, mSims);
+ }
+ }
+ }
+ }
+ }
+
+ private TelephonyDevController() {
+ initFromResource();
+
+ mModems.trimToSize();
+ mSims.trimToSize();
+ }
+
+ /**
+ * each RIL call this interface to register/unregister the unsolicited hardware
+ * configuration callback data it can provide.
+ */
+ public static void registerRIL(CommandsInterface cmdsIf) {
+ /* get the current configuration from this ril... */
+ cmdsIf.getHardwareConfig(sRilHardwareConfig);
+ /* ... process it ... */
+ if (sRilHardwareConfig != null) {
+ AsyncResult ar = (AsyncResult) sRilHardwareConfig.obj;
+ if (ar.exception == null) {
+ handleGetHardwareConfigChanged(ar);
+ }
+ }
+ /* and register for async device configuration change. */
+ cmdsIf.registerForHardwareConfigChanged(sTelephonyDevController, EVENT_HARDWARE_CONFIG_CHANGED, null);
+ }
+
+ public static void unregisterRIL(CommandsInterface cmdsIf) {
+ cmdsIf.unregisterForHardwareConfigChanged(sTelephonyDevController);
+ }
+
+ /**
+ * handle callbacks from RIL.
+ */
+ public void handleMessage(Message msg) {
+ AsyncResult ar;
+ switch (msg.what) {
+ case EVENT_HARDWARE_CONFIG_CHANGED:
+ if (DBG) logd("handleMessage: received EVENT_HARDWARE_CONFIG_CHANGED");
+ ar = (AsyncResult) msg.obj;
+ handleGetHardwareConfigChanged(ar);
+ break;
+ default:
+ loge("handleMessage: Unknown Event " + msg.what);
+ }
+ }
+
+ /**
+ * hardware configuration update or insert.
+ */
+ private static void updateOrInsert(HardwareConfig hw, ArrayList<HardwareConfig> list) {
+ int size;
+ HardwareConfig item;
+ synchronized (mLock) {
+ size = list.size();
+ for (int i = 0 ; i < size ; i++) {
+ item = list.get(i);
+ if (item.uuid.compareTo(hw.uuid) == 0) {
+ if (DBG) logd("updateOrInsert: removing: " + item);
+ list.remove(i);
+ }
+ }
+ if (DBG) logd("updateOrInsert: inserting: " + hw);
+ list.add(hw);
+ }
+ }
+
+ /**
+ * hardware configuration changed.
+ */
+ private static void handleGetHardwareConfigChanged(AsyncResult ar) {
+ if ((ar.exception == null) && (ar.result != null)) {
+ List hwcfg = (List)ar.result;
+ for (int i = 0 ; i < hwcfg.size() ; i++) {
+ HardwareConfig hw = null;
+
+ hw = (HardwareConfig) hwcfg.get(i);
+ if (hw != null) {
+ if (hw.type == HardwareConfig.DEV_HARDWARE_TYPE_MODEM) {
+ updateOrInsert(hw, mModems);
+ } else if (hw.type == HardwareConfig.DEV_HARDWARE_TYPE_SIM) {
+ updateOrInsert(hw, mSims);
+ }
+ }
+ }
+ } else {
+ /* error detected, ignore. are we missing some real time configutation
+ * at this point? what to do...
+ */
+ loge("handleGetHardwareConfigChanged - returned an error.");
+ }
+ }
+
+ /**
+ * get total number of registered modem.
+ */
+ public static int getModemCount() {
+ synchronized (mLock) {
+ int count = mModems.size();
+ if (DBG) logd("getModemCount: " + count);
+ return count;
+ }
+ }
+
+ /**
+ * get modem at index 'index'.
+ */
+ public HardwareConfig getModem(int index) {
+ synchronized (mLock) {
+ if (mModems.isEmpty()) {
+ loge("getModem: no registered modem device?!?");
+ return null;
+ }
+
+ if (index > getModemCount()) {
+ loge("getModem: out-of-bounds access for modem device " + index + " max: " + getModemCount());
+ return null;
+ }
+
+ if (DBG) logd("getModem: " + index);
+ return mModems.get(index);
+ }
+ }
+
+ /**
+ * get total number of registered sims.
+ */
+ public int getSimCount() {
+ synchronized (mLock) {
+ int count = mSims.size();
+ if (DBG) logd("getSimCount: " + count);
+ return count;
+ }
+ }
+
+ /**
+ * get sim at index 'index'.
+ */
+ public HardwareConfig getSim(int index) {
+ synchronized (mLock) {
+ if (mSims.isEmpty()) {
+ loge("getSim: no registered sim device?!?");
+ return null;
+ }
+
+ if (index > getSimCount()) {
+ loge("getSim: out-of-bounds access for sim device " + index + " max: " + getSimCount());
+ return null;
+ }
+
+ if (DBG) logd("getSim: " + index);
+ return mSims.get(index);
+ }
+ }
+
+ /**
+ * get modem associated with sim index 'simIndex'.
+ */
+ public HardwareConfig getModemForSim(int simIndex) {
+ synchronized (mLock) {
+ if (mModems.isEmpty() || mSims.isEmpty()) {
+ loge("getModemForSim: no registered modem/sim device?!?");
+ return null;
+ }
+
+ if (simIndex > getSimCount()) {
+ loge("getModemForSim: out-of-bounds access for sim device " + simIndex + " max: " + getSimCount());
+ return null;
+ }
+
+ if (DBG) logd("getModemForSim " + simIndex);
+
+ HardwareConfig sim = getSim(simIndex);
+ for (HardwareConfig modem: mModems) {
+ if (modem.uuid.equals(sim.modemUuid)) {
+ return modem;
+ }
+ }
+
+ return null;
+ }
+ }
+
+ /**
+ * get all sim's associated with modem at index 'modemIndex'.
+ */
+ public ArrayList<HardwareConfig> getAllSimsForModem(int modemIndex) {
+ synchronized (mLock) {
+ if (mSims.isEmpty()) {
+ loge("getAllSimsForModem: no registered sim device?!?");
+ return null;
+ }
+
+ if (modemIndex > getModemCount()) {
+ loge("getAllSimsForModem: out-of-bounds access for modem device " + modemIndex + " max: " + getModemCount());
+ return null;
+ }
+
+ if (DBG) logd("getAllSimsForModem " + modemIndex);
+
+ ArrayList<HardwareConfig> result = new ArrayList<HardwareConfig>();
+ HardwareConfig modem = getModem(modemIndex);
+ for (HardwareConfig sim: mSims) {
+ if (sim.modemUuid.equals(modem.uuid)) {
+ result.add(sim);
+ }
+ }
+ return result;
+ }
+ }
+
+ /**
+ * get all modem's registered.
+ */
+ public ArrayList<HardwareConfig> getAllModems() {
+ synchronized (mLock) {
+ ArrayList<HardwareConfig> modems = new ArrayList<HardwareConfig>();
+ if (mModems.isEmpty()) {
+ if (DBG) logd("getAllModems: empty list.");
+ } else {
+ for (HardwareConfig modem: mModems) {
+ modems.add(modem);
+ }
+ }
+
+ return modems;
+ }
+ }
+
+ /**
+ * get all sim's registered.
+ */
+ public ArrayList<HardwareConfig> getAllSims() {
+ synchronized (mLock) {
+ ArrayList<HardwareConfig> sims = new ArrayList<HardwareConfig>();
+ if (mSims.isEmpty()) {
+ if (DBG) logd("getAllSims: empty list.");
+ } else {
+ for (HardwareConfig sim: mSims) {
+ sims.add(sim);
+ }
+ }
+
+ return sims;
+ }
+ }
+}
diff --git a/src/java/com/android/internal/telephony/UiccPhoneBookController.java b/src/java/com/android/internal/telephony/UiccPhoneBookController.java
new file mode 100644
index 0000000..7cc582d
--- /dev/null
+++ b/src/java/com/android/internal/telephony/UiccPhoneBookController.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
+ * Not a Contribution.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import android.os.ServiceManager;
+import android.os.RemoteException;
+import android.telephony.Rlog;
+
+import com.android.internal.telephony.IccPhoneBookInterfaceManagerProxy;
+import com.android.internal.telephony.IIccPhoneBook;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.uicc.AdnRecord;
+
+import java.lang.ArrayIndexOutOfBoundsException;
+import java.lang.NullPointerException;
+import java.util.List;
+
+public class UiccPhoneBookController extends IIccPhoneBook.Stub {
+ private static final String TAG = "UiccPhoneBookController";
+ private Phone[] mPhone;
+
+ /* only one UiccPhoneBookController exists */
+ public UiccPhoneBookController(Phone[] phone) {
+ if (ServiceManager.getService("simphonebook") == null) {
+ ServiceManager.addService("simphonebook", this);
+ }
+ mPhone = phone;
+ }
+
+ public boolean
+ updateAdnRecordsInEfBySearch (int efid, String oldTag, String oldPhoneNumber,
+ String newTag, String newPhoneNumber, String pin2) throws android.os.RemoteException {
+ return updateAdnRecordsInEfBySearchUsingSubId(getDefaultSubscription(), efid, oldTag,
+ oldPhoneNumber, newTag, newPhoneNumber, pin2);
+ }
+
+ public boolean
+ updateAdnRecordsInEfBySearchUsingSubId(long subId, int efid, String oldTag,
+ String oldPhoneNumber, String newTag, String newPhoneNumber,
+ String pin2) throws android.os.RemoteException {
+ IccPhoneBookInterfaceManagerProxy iccPbkIntMgrProxy =
+ getIccPhoneBookInterfaceManagerProxy(subId);
+ if (iccPbkIntMgrProxy != null) {
+ return iccPbkIntMgrProxy.updateAdnRecordsInEfBySearch(efid, oldTag,
+ oldPhoneNumber, newTag, newPhoneNumber, pin2);
+ } else {
+ Rlog.e(TAG,"updateAdnRecordsInEfBySearch iccPbkIntMgrProxy is" +
+ " null for Subscription:"+subId);
+ return false;
+ }
+ }
+
+ public boolean
+ updateAdnRecordsInEfByIndex(int efid, String newTag,
+ String newPhoneNumber, int index, String pin2) throws android.os.RemoteException {
+ return updateAdnRecordsInEfByIndexUsingSubId(getDefaultSubscription(), efid, newTag,
+ newPhoneNumber, index, pin2);
+ }
+
+ public boolean
+ updateAdnRecordsInEfByIndexUsingSubId(long subId, int efid, String newTag,
+ String newPhoneNumber, int index, String pin2) throws android.os.RemoteException {
+ IccPhoneBookInterfaceManagerProxy iccPbkIntMgrProxy =
+ getIccPhoneBookInterfaceManagerProxy(subId);
+ if (iccPbkIntMgrProxy != null) {
+ return iccPbkIntMgrProxy.updateAdnRecordsInEfByIndex(efid, newTag,
+ newPhoneNumber, index, pin2);
+ } else {
+ Rlog.e(TAG,"updateAdnRecordsInEfByIndex iccPbkIntMgrProxy is" +
+ " null for Subscription:"+subId);
+ return false;
+ }
+ }
+
+ public int[] getAdnRecordsSize(int efid) throws android.os.RemoteException {
+ return getAdnRecordsSizeUsingSubId(getDefaultSubscription(), efid);
+ }
+
+ public int[]
+ getAdnRecordsSizeUsingSubId(long subId, int efid) throws android.os.RemoteException {
+ IccPhoneBookInterfaceManagerProxy iccPbkIntMgrProxy =
+ getIccPhoneBookInterfaceManagerProxy(subId);
+ if (iccPbkIntMgrProxy != null) {
+ return iccPbkIntMgrProxy.getAdnRecordsSize(efid);
+ } else {
+ Rlog.e(TAG,"getAdnRecordsSize iccPbkIntMgrProxy is" +
+ " null for Subscription:"+subId);
+ return null;
+ }
+ }
+
+ public List<AdnRecord> getAdnRecordsInEf(int efid) throws android.os.RemoteException {
+ return getAdnRecordsInEfUsingSubId(getDefaultSubscription(), efid);
+ }
+
+ public List<AdnRecord> getAdnRecordsInEfUsingSubId(long subId, int efid)
+ throws android.os.RemoteException {
+ IccPhoneBookInterfaceManagerProxy iccPbkIntMgrProxy =
+ getIccPhoneBookInterfaceManagerProxy(subId);
+ if (iccPbkIntMgrProxy != null) {
+ return iccPbkIntMgrProxy.getAdnRecordsInEf(efid);
+ } else {
+ Rlog.e(TAG,"getAdnRecordsInEf iccPbkIntMgrProxy is" +
+ "null for Subscription:"+subId);
+ return null;
+ }
+ }
+
+ /**
+ * get phone book interface manager proxy object based on subscription.
+ **/
+ private IccPhoneBookInterfaceManagerProxy
+ getIccPhoneBookInterfaceManagerProxy(long subId) {
+
+ long phoneId = SubscriptionController.getInstance().getPhoneId(subId);
+ try {
+ return ((PhoneProxy)mPhone[(int)phoneId]).getIccPhoneBookInterfaceManagerProxy();
+ } catch (NullPointerException e) {
+ Rlog.e(TAG, "Exception is :"+e.toString()+" For subscription :"+subId );
+ e.printStackTrace(); //To print stack trace
+ return null;
+ } catch (ArrayIndexOutOfBoundsException e) {
+ Rlog.e(TAG, "Exception is :"+e.toString()+" For subscription :"+subId );
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ private long getDefaultSubscription() {
+ return PhoneFactory.getDefaultSubscription();
+ }
+}
diff --git a/src/java/com/android/internal/telephony/UiccSmsController.java b/src/java/com/android/internal/telephony/UiccSmsController.java
new file mode 100755
index 0000000..d5b4c88
--- /dev/null
+++ b/src/java/com/android/internal/telephony/UiccSmsController.java
@@ -0,0 +1,308 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
+ * Not a Contribution.
+ *
+ * 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.app.PendingIntent;
+import android.os.ServiceManager;
+import android.telephony.Rlog;
+
+import com.android.internal.telephony.ISms;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.SmsRawData;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * UiccSmsController to provide an inter-process communication to
+ * access Sms in Icc.
+ */
+public class UiccSmsController extends ISms.Stub {
+ static final String LOG_TAG = "RIL_UiccSmsController";
+
+ protected Phone[] mPhone;
+
+ protected UiccSmsController(Phone[] phone){
+ mPhone = phone;
+
+ if (ServiceManager.getService("isms") == null) {
+ ServiceManager.addService("isms", this);
+ }
+ }
+
+ public boolean
+ updateMessageOnIccEf(String callingPackage, int index, int status, byte[] pdu)
+ throws android.os.RemoteException {
+ return updateMessageOnIccEfUsingSubId(getPreferredSmsSubscription(), callingPackage,
+ index, status, pdu);
+ }
+
+ public boolean
+ updateMessageOnIccEfUsingSubId(long subId, String callingPackage, int index, int status,
+ byte[] pdu) throws android.os.RemoteException {
+ IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
+ if (iccSmsIntMgr != null) {
+ return iccSmsIntMgr.updateMessageOnIccEf(callingPackage, index, status, pdu);
+ } else {
+ Rlog.e(LOG_TAG,"updateMessageOnIccEf iccSmsIntMgr is null" +
+ " for Subscription: " + subId);
+ return false;
+ }
+ }
+
+ public boolean copyMessageToIccEf(String callingPackage, int status, byte[] pdu, byte[] smsc)
+ throws android.os.RemoteException {
+ return copyMessageToIccEfUsingSubId(getPreferredSmsSubscription(), callingPackage, status,
+ pdu, smsc);
+ }
+
+ public boolean copyMessageToIccEfUsingSubId(long subId, String callingPackage, int status,
+ byte[] pdu, byte[] smsc) throws android.os.RemoteException {
+ IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
+ if (iccSmsIntMgr != null) {
+ return iccSmsIntMgr.copyMessageToIccEf(callingPackage, status, pdu, smsc);
+ } else {
+ Rlog.e(LOG_TAG,"copyMessageToIccEf iccSmsIntMgr is null" +
+ " for Subscription: " + subId);
+ return false;
+ }
+ }
+
+ public List<SmsRawData> getAllMessagesFromIccEf(String callingPackage)
+ throws android.os.RemoteException {
+ return getAllMessagesFromIccEfUsingSubId(getPreferredSmsSubscription(), callingPackage);
+ }
+
+ public List<SmsRawData> getAllMessagesFromIccEfUsingSubId(long subId, String callingPackage)
+ throws android.os.RemoteException {
+ IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
+ if (iccSmsIntMgr != null) {
+ return iccSmsIntMgr.getAllMessagesFromIccEf(callingPackage);
+ } else {
+ Rlog.e(LOG_TAG,"getAllMessagesFromIccEf iccSmsIntMgr is" +
+ " null for Subscription: " + subId);
+ return null;
+ }
+ }
+
+ public void sendData(String callingPackage, String destAddr, String scAddr, int destPort,
+ byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) {
+ sendDataUsingSubId(getPreferredSmsSubscription(), callingPackage, destAddr, scAddr,
+ destPort, data, sentIntent, deliveryIntent);
+ }
+
+ public void sendDataUsingSubId(long subId, String callingPackage, String destAddr,
+ String scAddr, int destPort, byte[] data, PendingIntent sentIntent,
+ PendingIntent deliveryIntent) {
+ IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
+ if (iccSmsIntMgr != null) {
+ iccSmsIntMgr.sendData(callingPackage, destAddr, scAddr, destPort, data,
+ sentIntent, deliveryIntent);
+ } else {
+ Rlog.e(LOG_TAG,"sendText iccSmsIntMgr is null for" +
+ " Subscription: " + subId);
+ }
+ }
+
+ public void sendText(String callingPackage, String destAddr, String scAddr,
+ String text, PendingIntent sentIntent, PendingIntent deliveryIntent) {
+ sendTextUsingSubId(getPreferredSmsSubscription(), callingPackage, destAddr, scAddr,
+ text, sentIntent, deliveryIntent);
+ }
+
+ public void sendTextUsingSubId(long subId, String callingPackage, String destAddr,
+ String scAddr, String text, PendingIntent sentIntent, PendingIntent deliveryIntent) {
+ IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
+ if (iccSmsIntMgr != null) {
+ iccSmsIntMgr.sendText(callingPackage, destAddr, scAddr, text, sentIntent,
+ deliveryIntent);
+ } else {
+ Rlog.e(LOG_TAG,"sendText iccSmsIntMgr is null for" +
+ " Subscription: " + subId);
+ }
+ }
+
+ public void sendMultipartText(String callingPackage, String destAddr, String scAddr,
+ List<String> parts, List<PendingIntent> sentIntents,
+ List<PendingIntent> deliveryIntents) throws android.os.RemoteException {
+ sendMultipartTextUsingSubId(getPreferredSmsSubscription(), callingPackage, destAddr,
+ scAddr, parts, sentIntents, deliveryIntents);
+ }
+
+ public void sendMultipartTextUsingSubId(long subId, String callingPackage, String destAddr,
+ String scAddr, List<String> parts, List<PendingIntent> sentIntents,
+ List<PendingIntent> deliveryIntents)
+ throws android.os.RemoteException {
+ IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
+ if (iccSmsIntMgr != null ) {
+ iccSmsIntMgr.sendMultipartText(callingPackage, destAddr, scAddr, parts, sentIntents,
+ deliveryIntents);
+ } else {
+ Rlog.e(LOG_TAG,"sendMultipartText iccSmsIntMgr is null for" +
+ " Subscription: " + subId);
+ }
+ }
+
+ public boolean enableCellBroadcast(int messageIdentifier) throws android.os.RemoteException {
+ return enableCellBroadcastUsingSubId(getPreferredSmsSubscription(), messageIdentifier);
+ }
+
+ public boolean enableCellBroadcastUsingSubId(long subId, int messageIdentifier)
+ throws android.os.RemoteException {
+ return enableCellBroadcastRangeUsingSubId(subId, messageIdentifier, messageIdentifier);
+ }
+
+ public boolean enableCellBroadcastRange(int startMessageId, int endMessageId)
+ throws android.os.RemoteException {
+ return enableCellBroadcastRangeUsingSubId(getPreferredSmsSubscription(), startMessageId,
+ endMessageId);
+ }
+
+ public boolean enableCellBroadcastRangeUsingSubId(long subId, int startMessageId,
+ int endMessageId) throws android.os.RemoteException {
+ IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
+ if (iccSmsIntMgr != null ) {
+ return iccSmsIntMgr.enableCellBroadcastRange(startMessageId, endMessageId);
+ } else {
+ Rlog.e(LOG_TAG,"enableCellBroadcast iccSmsIntMgr is null for" +
+ " Subscription: " + subId);
+ }
+ return false;
+ }
+
+ public boolean disableCellBroadcast(int messageIdentifier) throws android.os.RemoteException {
+ return disableCellBroadcastUsingSubId(getPreferredSmsSubscription(), messageIdentifier);
+ }
+
+ public boolean disableCellBroadcastUsingSubId(long subId, int messageIdentifier)
+ throws android.os.RemoteException {
+ return disableCellBroadcastRangeUsingSubId(subId, messageIdentifier, messageIdentifier);
+ }
+
+ public boolean disableCellBroadcastRange(int startMessageId, int endMessageId)
+ throws android.os.RemoteException {
+ return disableCellBroadcastRangeUsingSubId(getPreferredSmsSubscription(), startMessageId,
+ endMessageId);
+ }
+
+ public boolean disableCellBroadcastRangeUsingSubId(long subId, int startMessageId,
+ int endMessageId) throws android.os.RemoteException {
+ IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
+ if (iccSmsIntMgr != null ) {
+ return iccSmsIntMgr.disableCellBroadcastRange(startMessageId, endMessageId);
+ } else {
+ Rlog.e(LOG_TAG,"disableCellBroadcast iccSmsIntMgr is null for" +
+ " Subscription:"+subId);
+ }
+ return false;
+ }
+
+ public int getPremiumSmsPermission(String packageName) {
+ return getPremiumSmsPermissionUsingSubId(getPreferredSmsSubscription(), packageName);
+ }
+
+ @Override
+ public int getPremiumSmsPermissionUsingSubId(long subId, String packageName) {
+ IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
+ if (iccSmsIntMgr != null ) {
+ return iccSmsIntMgr.getPremiumSmsPermission(packageName);
+ } else {
+ Rlog.e(LOG_TAG, "getPremiumSmsPermission iccSmsIntMgr is null");
+ }
+ //TODO Rakesh
+ return 0;
+ }
+
+ public void setPremiumSmsPermission(String packageName, int permission) {
+ setPremiumSmsPermissionUsingSubId(getPreferredSmsSubscription(), packageName, permission);
+ }
+
+ @Override
+ public void setPremiumSmsPermissionUsingSubId(long subId, String packageName, int permission) {
+ IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
+ if (iccSmsIntMgr != null ) {
+ iccSmsIntMgr.setPremiumSmsPermission(packageName, permission);
+ } else {
+ Rlog.e(LOG_TAG, "setPremiumSmsPermission iccSmsIntMgr is null");
+ }
+ }
+
+ public boolean isImsSmsSupported() {
+ return isImsSmsSupportedUsingSubId(getPreferredSmsSubscription());
+ }
+
+ @Override
+ public boolean isImsSmsSupportedUsingSubId(long subId) {
+ IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
+ if (iccSmsIntMgr != null ) {
+ return iccSmsIntMgr.isImsSmsSupported();
+ } else {
+ Rlog.e(LOG_TAG, "isImsSmsSupported iccSmsIntMgr is null");
+ }
+ return false;
+ }
+
+ public String getImsSmsFormat() {
+ return getImsSmsFormatUsingSubId(getPreferredSmsSubscription());
+ }
+
+ @Override
+ public String getImsSmsFormatUsingSubId(long subId) {
+ IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
+ if (iccSmsIntMgr != null ) {
+ return iccSmsIntMgr.getImsSmsFormat();
+ } else {
+ Rlog.e(LOG_TAG, "getImsSmsFormat iccSmsIntMgr is null");
+ }
+ return null;
+ }
+
+ /**
+ * get sms interface manager object based on subscription.
+ **/
+ private IccSmsInterfaceManager getIccSmsInterfaceManager(long subId) {
+ long phoneId = SubscriptionController.getInstance().getPhoneId(subId);
+ try {
+ return (IccSmsInterfaceManager)
+ ((PhoneProxy)mPhone[(int)phoneId]).getIccSmsInterfaceManager();
+ } catch (NullPointerException e) {
+ Rlog.e(LOG_TAG, "Exception is :"+e.toString()+" For subscription :"+subId );
+ e.printStackTrace(); //This will print stact trace
+ return null;
+ } catch (ArrayIndexOutOfBoundsException e) {
+ Rlog.e(LOG_TAG, "Exception is :"+e.toString()+" For subscription :"+subId );
+ e.printStackTrace(); //This will print stack trace
+ return null;
+ }
+ }
+
+ /**
+ Gets User preferred SMS subscription */
+ public long getPreferredSmsSubscription() {
+ return PhoneFactory.getDefaultSubscription();
+ }
+
+ /**
+ * Get SMS prompt property, enabled or not
+ **/
+ public boolean isSMSPromptEnabled() {
+ return PhoneFactory.isSMSPromptEnabled();
+ }
+
+}
diff --git a/src/java/com/android/internal/telephony/VoicePhone.java b/src/java/com/android/internal/telephony/VoicePhone.java
new file mode 100644
index 0000000..2589f00
--- /dev/null
+++ b/src/java/com/android/internal/telephony/VoicePhone.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import android.os.Handler;
+import android.os.RegistrantList;
+
+/**
+ * Internal interface used to control the IMS phone from GSM/CDMA Phone
+ *
+ * {@hide}
+ *
+ */
+public interface VoicePhone {
+
+ public static final String CS_FALLBACK = "cs_fallback";
+
+ Connection getHandoverConnection();
+
+ void notifySrvccState(Call.SrvccState state);
+
+ // To redial through GSM or CDMA when dialing IMS call fails
+ void registerForSilentRedial(Handler h, int what, Object obj);
+ void unregisterForSilentRedial(Handler h);
+}
diff --git a/src/java/com/android/internal/telephony/WapPushOverSms.java b/src/java/com/android/internal/telephony/WapPushOverSms.java
index 2f22794..bf43698 100755
--- a/src/java/com/android/internal/telephony/WapPushOverSms.java
+++ b/src/java/com/android/internal/telephony/WapPushOverSms.java
@@ -26,9 +26,11 @@
import android.os.IBinder;
import android.os.RemoteException;
import android.provider.Telephony.Sms.Intents;
+import android.telephony.SubscriptionManager;
import android.telephony.Rlog;
import com.android.internal.telephony.uicc.IccUtils;
+import com.android.internal.telephony.PhoneConstants;
/**
* WAP push handler class.
@@ -95,6 +97,9 @@
int transactionId = pdu[index++] & 0xFF;
int pduType = pdu[index++] & 0xFF;
+ // Should we "abort" if no subId for now just no supplying extra param below
+ int phoneId = handler.getPhone().getPhoneId();
+
if ((pduType != WspTypeDecoder.PDU_TYPE_PUSH) &&
(pduType != WspTypeDecoder.PDU_TYPE_CONFIRMED_PUSH)) {
index = mContext.getResources().getInteger(
@@ -201,6 +206,7 @@
intent.putExtra("data", intentData);
intent.putExtra("contentTypeParameters",
pduDecoder.getContentParameters());
+ SubscriptionManager.putPhoneIdAndSubIdExtra(intent, phoneId);
int procRet = wapPushMan.processMessage(wapAppId, contentType, intent);
if (DBG) Rlog.v(TAG, "procRet:" + procRet);
@@ -241,6 +247,7 @@
intent.putExtra("header", header);
intent.putExtra("data", intentData);
intent.putExtra("contentTypeParameters", pduDecoder.getContentParameters());
+ SubscriptionManager.putPhoneIdAndSubIdExtra(intent, phoneId);
// Direct the intent to only the default MMS app. If we can't find a default MMS app
// then sent it to all broadcast receivers.
diff --git a/src/java/com/android/internal/telephony/cat/CatService.java b/src/java/com/android/internal/telephony/cat/CatService.java
index ec61889..16a0853 100644
--- a/src/java/com/android/internal/telephony/cat/CatService.java
+++ b/src/java/com/android/internal/telephony/cat/CatService.java
@@ -28,6 +28,8 @@
import android.os.SystemProperties;
import com.android.internal.telephony.CommandsInterface;
+import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.telephony.SubscriptionController;
import com.android.internal.telephony.uicc.IccFileHandler;
import com.android.internal.telephony.uicc.IccRecords;
import com.android.internal.telephony.uicc.IccUtils;
@@ -82,14 +84,17 @@
private boolean mStkAppInstalled = false;
// Service constants.
- static final int MSG_ID_SESSION_END = 1;
- static final int MSG_ID_PROACTIVE_COMMAND = 2;
- static final int MSG_ID_EVENT_NOTIFY = 3;
- static final int MSG_ID_CALL_SETUP = 4;
+ protected static final int MSG_ID_SESSION_END = 1;
+ protected static final int MSG_ID_PROACTIVE_COMMAND = 2;
+ protected static final int MSG_ID_EVENT_NOTIFY = 3;
+ protected static final int MSG_ID_CALL_SETUP = 4;
static final int MSG_ID_REFRESH = 5;
static final int MSG_ID_RESPONSE = 6;
static final int MSG_ID_SIM_READY = 7;
+ protected static final int MSG_ID_ICC_CHANGED = 8;
+ protected static final int MSG_ID_ALPHA_NOTIFY = 9;
+
static final int MSG_ID_RIL_MSG_DECODED = 10;
// Events to signal SIM presence or absent in the device.
@@ -102,9 +107,12 @@
static final String STK_DEFAULT = "Default Message";
- /* Intentionally private for singleton */
+ private HandlerThread mHandlerThread;
+ private int mSlotId;
+
+ /* For multisim catservice should not be singleton */
private CatService(CommandsInterface ci, UiccCardApplication ca, IccRecords ir,
- Context context, IccFileHandler fh, UiccCard ic) {
+ Context context, IccFileHandler fh, UiccCard ic, int slotId) {
if (ci == null || ca == null || ir == null || context == null || fh == null
|| ic == null) {
throw new NullPointerException(
@@ -112,9 +120,17 @@
}
mCmdIf = ci;
mContext = context;
+ mSlotId = slotId;
+ mHandlerThread = new HandlerThread("Cat Telephony service" + slotId);
+ mHandlerThread.start();
// Get the RilMessagesDecoder for decoding the messages.
- mMsgDecoder = RilMessageDecoder.getInstance(this, fh);
+ mMsgDecoder = RilMessageDecoder.getInstance(this, fh, slotId);
+ if (null == mMsgDecoder) {
+ CatLog.d(this, "Null RilMessageDecoder instance");
+ return;
+ }
+ mMsgDecoder.start();
// Register ril events handling.
mCmdIf.setOnCatSessionEnd(this, MSG_ID_SESSION_END, null);
@@ -127,23 +143,62 @@
mUiccApplication = ca;
// Register for SIM ready event.
+ CatLog.d(this, "registerForReady slotid: " + mSlotId + "instance : " + this);
mUiccApplication.registerForReady(this, MSG_ID_SIM_READY, null);
mIccRecords.registerForRecordsLoaded(this, MSG_ID_ICC_RECORDS_LOADED, null);
// Check if STK application is availalbe
mStkAppInstalled = isStkAppInstalled();
- CatLog.d(this, "Running CAT service. STK app installed:" + mStkAppInstalled);
+ CatLog.d(this, "Running CAT service on Slotid: " + mSlotId +
+ ". STK app installed:" + mStkAppInstalled);
+ }
+
+ /**
+ * Used for instantiating the Service from the Card.
+ *
+ * @param ci CommandsInterface object
+ * @param context phone app context
+ * @param ic Icc card
+ * @param slotId to know the index of card
+ * @return The only Service object in the system
+ */
+ public static CatService getInstance(CommandsInterface ci,
+ Context context, UiccCard ic, int slotId) {
+ UiccCardApplication ca = null;
+ IccFileHandler fh = null;
+ IccRecords ir = null;
+ if (ic != null) {
+ /* Since Cat is not tied to any application, but rather is Uicc application
+ * in itself - just get first FileHandler and IccRecords object
+ */
+ ca = ic.getApplicationIndex(0);
+ if (ca != null) {
+ fh = ca.getIccFileHandler();
+ ir = ca.getIccRecords();
+ }
+ }
+
+ if (ci == null || ca == null || ir == null || context == null || fh == null
+ || ic == null) {
+ return null;
+ }
+
+ return new CatService(ci, ca, ir, context, fh, ic, slotId);
}
public void dispose() {
- mIccRecords.unregisterForRecordsLoaded(this);
- mCmdIf.unSetOnCatSessionEnd(this);
- mCmdIf.unSetOnCatProactiveCmd(this);
- mCmdIf.unSetOnCatEvent(this);
- mCmdIf.unSetOnCatCallSetUp(this);
+ synchronized (sInstanceLock) {
+ CatLog.d(this, "Disposing CatService object");
+ mIccRecords.unregisterForRecordsLoaded(this);
- removeCallbacksAndMessages(null);
+ mCmdIf.unSetOnCatSessionEnd(this);
+ mCmdIf.unSetOnCatProactiveCmd(this);
+ mCmdIf.unSetOnCatEvent(this);
+ mCmdIf.unSetOnCatCallSetUp(this);
+
+ removeCallbacksAndMessages(null);
+ }
}
@Override
@@ -346,8 +401,15 @@
return;
}
mCurrntCmd = cmdMsg;
+ broadcastCatCmdIntent(cmdMsg);
+ }
+
+
+ private void broadcastCatCmdIntent(CatCmdMessage cmdMsg) {
Intent intent = new Intent(AppInterface.CAT_CMD_ACTION);
intent.putExtra("STK CMD", cmdMsg);
+ intent.putExtra("SLOT_ID", mSlotId);
+ CatLog.d(this, "Sending CmdMsg: " + cmdMsg+ " on slotid:" + mSlotId);
mContext.sendBroadcast(intent);
}
@@ -356,13 +418,15 @@
*
*/
private void handleSessionEnd() {
- CatLog.d(this, "SESSION END");
+ CatLog.d(this, "SESSION END on "+ mSlotId);
mCurrntCmd = mMenuCmd;
Intent intent = new Intent(AppInterface.CAT_SESSION_END_ACTION);
+ intent.putExtra("SLOT_ID", mSlotId);
mContext.sendBroadcast(intent);
}
+
private void sendTerminalResponse(CommandDetails cmdDet,
ResultCode resultCode, boolean includeAdditionalInfo,
int additionalInfo, ResponseData resp) {
@@ -527,60 +591,18 @@
}
/**
- * Used for instantiating/updating the Service from the GsmPhone or CdmaPhone constructor.
+ * Used by application to get an AppInterface object.
*
- * @param ci CommandsInterface object
- * @param context phone app context
- * @param ic Icc card
* @return The only Service object in the system
*/
- public static CatService getInstance(CommandsInterface ci,
- Context context, UiccCard ic) {
- UiccCardApplication ca = null;
- IccFileHandler fh = null;
- IccRecords ir = null;
- if (ic != null) {
- /* Since Cat is not tied to any application, but rather is Uicc application
- * in itself - just get first FileHandler and IccRecords object
- */
- ca = ic.getApplicationIndex(0);
- if (ca != null) {
- fh = ca.getIccFileHandler();
- ir = ca.getIccRecords();
- }
+ //TODO Need to take care for MSIM
+ public static AppInterface getInstance() {
+ int slotId = PhoneConstants.DEFAULT_CARD_INDEX;
+ SubscriptionController sControl = SubscriptionController.getInstance();
+ if (sControl != null) {
+ slotId = sControl.getSlotId(sControl.getDefaultSubId());
}
- synchronized (sInstanceLock) {
- if (sInstance == null) {
- if (ci == null || ca == null || ir == null || context == null || fh == null
- || ic == null) {
- return null;
- }
- HandlerThread thread = new HandlerThread("Cat Telephony service");
- thread.start();
- sInstance = new CatService(ci, ca, ir, context, fh, ic);
- CatLog.d(sInstance, "NEW sInstance");
- } else if ((ir != null) && (mIccRecords != ir)) {
- if (mIccRecords != null) {
- mIccRecords.unregisterForRecordsLoaded(sInstance);
- }
-
- if (mUiccApplication != null) {
- mUiccApplication.unregisterForReady(sInstance);
- }
- CatLog.d(sInstance,
- "Reinitialize the Service with SIMRecords and UiccCardApplication");
- mIccRecords = ir;
- mUiccApplication = ca;
-
- // re-Register for SIM ready event.
- mIccRecords.registerForRecordsLoaded(sInstance, MSG_ID_ICC_RECORDS_LOADED, null);
- mUiccApplication.registerForReady(sInstance, MSG_ID_SIM_READY, null);
- CatLog.d(sInstance, "sr changed reinitialize and return current sInstance");
- } else {
- CatLog.d(sInstance, "Return current sInstance");
- }
- return sInstance;
- }
+ return getInstance(null, null, null, slotId);
}
/**
@@ -588,19 +610,20 @@
*
* @return The only Service object in the system
*/
- public static AppInterface getInstance() {
- return getInstance(null, null, null);
+ public static AppInterface getInstance(int slotId) {
+ return getInstance(null, null, null, slotId);
}
@Override
public void handleMessage(Message msg) {
+ CatLog.d(this, "handleMessage[" + msg.what + "]");
switch (msg.what) {
case MSG_ID_SESSION_END:
case MSG_ID_PROACTIVE_COMMAND:
case MSG_ID_EVENT_NOTIFY:
case MSG_ID_REFRESH:
- CatLog.d(this, "ril message arrived");
+ CatLog.d(this, "ril message arrived,slotid:" + mSlotId);
String data = null;
if (msg.obj != null) {
AsyncResult ar = (AsyncResult) msg.obj;
@@ -771,4 +794,41 @@
return (numReceiver > 0);
}
+
+ public void update(CommandsInterface ci,
+ Context context, UiccCard ic) {
+ UiccCardApplication ca = null;
+ IccRecords ir = null;
+
+ if (ic != null) {
+ /* Since Cat is not tied to any application, but rather is Uicc application
+ * in itself - just get first FileHandler and IccRecords object
+ */
+ ca = ic.getApplicationIndex(0);
+ if (ca != null) {
+ ir = ca.getIccRecords();
+ }
+ }
+
+ synchronized (sInstanceLock) {
+ if ((ir != null) && (mIccRecords != ir)) {
+ if (mIccRecords != null) {
+ mIccRecords.unregisterForRecordsLoaded(this);
+ }
+
+ if (mUiccApplication != null) {
+ CatLog.d(this, "unregisterForReady slotid: " + mSlotId + "instance : " + this);
+ mUiccApplication.unregisterForReady(this);
+ }
+ CatLog.d(this,
+ "Reinitialize the Service with SIMRecords and UiccCardApplication");
+ mIccRecords = ir;
+ mUiccApplication = ca;
+
+ // re-Register for SIM ready event.
+ mIccRecords.registerForRecordsLoaded(this, MSG_ID_ICC_RECORDS_LOADED, null);
+ CatLog.d(this, "registerForReady slotid: " + mSlotId + "instance : " + this);
+ }
+ }
+ }
}
diff --git a/src/java/com/android/internal/telephony/cat/RilMessageDecoder.java b/src/java/com/android/internal/telephony/cat/RilMessageDecoder.java
old mode 100644
new mode 100755
index d75c63d..dad8347
--- a/src/java/com/android/internal/telephony/cat/RilMessageDecoder.java
+++ b/src/java/com/android/internal/telephony/cat/RilMessageDecoder.java
@@ -18,11 +18,13 @@
import com.android.internal.telephony.uicc.IccFileHandler;
import com.android.internal.telephony.uicc.IccUtils;
+import com.android.internal.telephony.PhoneConstants;
import android.os.Handler;
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
import android.os.Message;
+import android.telephony.TelephonyManager;
/**
* Class used for queuing raw ril messages, decoding them into CommanParams
@@ -35,10 +37,11 @@
private static final int CMD_PARAMS_READY = 2;
// members
- private static RilMessageDecoder sInstance = null;
private CommandParamsFactory mCmdParamsFactory = null;
private RilMessage mCurrentRilMessage = null;
private Handler mCaller = null;
+ private static int mSimCount = 0;
+ private static RilMessageDecoder[] mInstance = null;
// States
private StateStart mStateStart = new StateStart();
@@ -51,12 +54,26 @@
* @param fh
* @return RilMesssageDecoder
*/
- public static synchronized RilMessageDecoder getInstance(Handler caller, IccFileHandler fh) {
- if (sInstance == null) {
- sInstance = new RilMessageDecoder(caller, fh);
- sInstance.start();
+ public static synchronized RilMessageDecoder getInstance(Handler caller, IccFileHandler fh,
+ int slotId) {
+ if (null == mInstance) {
+ mSimCount = TelephonyManager.getDefault().getSimCount();
+ mInstance = new RilMessageDecoder[mSimCount];
+ for (int i = 0; i < mSimCount; i++) {
+ mInstance[i] = null;
+ }
}
- return sInstance;
+
+ if (slotId < mSimCount) {
+ if (null == mInstance[slotId]) {
+ mInstance[slotId] = new RilMessageDecoder(caller, fh);
+ }
+ } else {
+ CatLog.d("RilMessageDecoder", "invaild slot id: " + slotId);
+ return null;
+ }
+
+ return mInstance[slotId];
}
/**
@@ -101,6 +118,10 @@
mCmdParamsFactory = CommandParamsFactory.getInstance(this, fh);
}
+ private RilMessageDecoder() {
+ super("RilMessageDecoder");
+ }
+
private class StateStart extends State {
@Override
public boolean processMessage(Message msg) {
diff --git a/src/java/com/android/internal/telephony/cdma/CDMALTEPhone.java b/src/java/com/android/internal/telephony/cdma/CDMALTEPhone.java
index 8e60f53..dd07281 100644
--- a/src/java/com/android/internal/telephony/cdma/CDMALTEPhone.java
+++ b/src/java/com/android/internal/telephony/cdma/CDMALTEPhone.java
@@ -16,25 +16,39 @@
package com.android.internal.telephony.cdma;
+import android.app.ActivityManagerNative;
import android.content.ContentValues;
import android.content.Context;
+import android.content.Intent;
import android.content.SharedPreferences;
import android.database.SQLException;
import android.net.Uri;
import android.os.AsyncResult;
+import android.os.Handler;
import android.os.Message;
+import android.os.UserHandle;
import android.preference.PreferenceManager;
+import android.os.PowerManager;
+import android.os.SystemProperties;
import android.provider.Telephony;
+import android.text.TextUtils;
+import android.telephony.SubscriptionManager;
import android.telephony.Rlog;
import com.android.internal.telephony.CommandsInterface;
+import android.telephony.TelephonyManager;
+import com.android.internal.telephony.dataconnection.DcTracker;
+import com.android.internal.telephony.MccTable;
import com.android.internal.telephony.OperatorInfo;
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.PhoneNotifier;
import com.android.internal.telephony.PhoneProxy;
+import com.android.internal.telephony.PhoneFactory;
+import com.android.internal.telephony.PhoneSubInfo;
import com.android.internal.telephony.SMSDispatcher;
import com.android.internal.telephony.SmsBroadcastUndelivered;
+import com.android.internal.telephony.Subscription;
import com.android.internal.telephony.gsm.GsmSMSDispatcher;
import com.android.internal.telephony.gsm.SmsMessage;
import com.android.internal.telephony.uicc.IsimRecords;
@@ -42,10 +56,20 @@
import com.android.internal.telephony.uicc.SIMRecords;
import com.android.internal.telephony.uicc.UiccCardApplication;
import com.android.internal.telephony.uicc.UiccController;
+import com.android.internal.telephony.ServiceStateTracker;
+import com.android.internal.telephony.TelephonyIntents;
+import com.android.internal.telephony.TelephonyProperties;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_ALPHA;
+import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_ISO_COUNTRY;
+import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC;
+
+import static com.android.internal.telephony.PhoneConstants.EVENT_SUBSCRIPTION_ACTIVATED;
+import static com.android.internal.telephony.PhoneConstants.EVENT_SUBSCRIPTION_DEACTIVATED;
+
public class CDMALTEPhone extends CDMAPhone {
static final String LOG_LTE_TAG = "CDMALTEPhone";
private static final boolean DBG = true;
@@ -57,11 +81,45 @@
private IsimUiccRecords mIsimUiccRecords;
// Constructors
+ public CDMALTEPhone(Context context, CommandsInterface ci, PhoneNotifier notifier,
+ int phoneId) {
+ this(context, ci, notifier, false, phoneId);
+ }
+
+ public CDMALTEPhone(Context context, CommandsInterface ci, PhoneNotifier notifier,
+ boolean unitTestMode, int phoneId) {
+ super(context, ci, notifier, phoneId);
+
+ Rlog.d(LOG_TAG, "CDMALTEPhone: constructor: sub = " + mPhoneId);
+
+ mDcTracker = new DcTracker(this);
+
+ }
+
+ // Constructors
public CDMALTEPhone(Context context, CommandsInterface ci, PhoneNotifier notifier) {
super(context, ci, notifier, false);
}
@Override
+ public void handleMessage (Message msg) {
+ switch (msg.what) {
+ case EVENT_SUBSCRIPTION_ACTIVATED:
+ log("EVENT_SUBSCRIPTION_ACTIVATED");
+ onSubscriptionActivated();
+ break;
+
+ case EVENT_SUBSCRIPTION_DEACTIVATED:
+ log("EVENT_SUBSCRIPTION_DEACTIVATED");
+ onSubscriptionDeactivated();
+ break;
+
+ default:
+ super.handleMessage(msg);
+ }
+ }
+
+ @Override
protected void initSstIcc() {
mSST = new CdmaLteServiceStateTracker(this);
}
@@ -127,7 +185,7 @@
@Override
boolean updateCurrentCarrierInProvider(String operatorNumeric) {
boolean retVal;
- if (mUiccController.getUiccCardApplication(UiccController.APP_FAM_3GPP) == null) {
+ if (mUiccController.getUiccCardApplication(mPhoneId, UiccController.APP_FAM_3GPP) == null) {
if (DBG) log("updateCurrentCarrierInProvider APP_FAM_3GPP == null");
retVal = super.updateCurrentCarrierInProvider(operatorNumeric);
} else {
@@ -140,21 +198,22 @@
@Override
public boolean updateCurrentCarrierInProvider() {
- if (mSimRecords != null) {
+ long currentDds = SubscriptionManager.getDefaultDataSubId();
+ String operatorNumeric = getOperatorNumeric();
+
+ Rlog.d(LOG_TAG, "updateCurrentCarrierInProvider: mSubscription = " + getSubId()
+ + " currentDds = " + currentDds + " operatorNumeric = " + operatorNumeric);
+
+ if (!TextUtils.isEmpty(operatorNumeric) && (getSubId() == currentDds)) {
try {
Uri uri = Uri.withAppendedPath(Telephony.Carriers.CONTENT_URI, "current");
ContentValues map = new ContentValues();
- String operatorNumeric = mSimRecords.getOperatorNumeric();
map.put(Telephony.Carriers.NUMERIC, operatorNumeric);
- if (DBG) log("updateCurrentCarrierInProvider from UICC: numeric=" +
- operatorNumeric);
mContext.getContentResolver().insert(uri, map);
return true;
} catch (SQLException e) {
- loge("Can't store current operator ret false", e);
+ Rlog.e(LOG_TAG, "Can't store current operator", e);
}
- } else {
- if (DBG) log("updateCurrentCarrierInProvider mIccRecords == null ret false");
}
return false;
}
@@ -197,40 +256,28 @@
}
@Override
- public void requestIsimAuthentication(String nonce, Message result) {
- mCi.requestIsimAuthentication(nonce, result);
- }
-
- @Override
protected void onUpdateIccAvailability() {
if (mUiccController == null ) {
return;
}
- // Update IsimRecords
- UiccCardApplication newUiccApplication =
- mUiccController.getUiccCardApplication(UiccController.APP_FAM_IMS);
- IsimUiccRecords newIsimUiccRecords = null;
+ UiccCardApplication newUiccApplication = getUiccCardApplication();
- if (newUiccApplication != null) {
- newIsimUiccRecords = (IsimUiccRecords)newUiccApplication.getIccRecords();
- }
- mIsimUiccRecords = newIsimUiccRecords;
-
- // Update UsimRecords
- newUiccApplication = mUiccController.getUiccCardApplication(UiccController.APP_FAM_3GPP);
- SIMRecords newSimRecords = null;
- if (newUiccApplication != null) {
- newSimRecords = (SIMRecords)newUiccApplication.getIccRecords();
- }
- if (mSimRecords != newSimRecords) {
- if (mSimRecords != null) {
- log("Removing stale SIMRecords object.");
- mSimRecords = null;
+ UiccCardApplication app = mUiccApplication.get();
+ if (app != newUiccApplication) {
+ if (app != null) {
+ log("Removing stale icc objects.");
+ if (mIccRecords.get() != null) {
+ unregisterForRuimRecordEvents();
+ }
+ mIccRecords.set(null);
+ mUiccApplication.set(null);
}
- if (newSimRecords != null) {
- log("New SIMRecords found");
- mSimRecords = newSimRecords;
+ if (newUiccApplication != null) {
+ log("New Uicc application found");
+ mUiccApplication.set(newUiccApplication);
+ mIccRecords.set(newUiccApplication.getIccRecords());
+ registerForRuimRecordEvents();
}
}
@@ -238,6 +285,159 @@
}
@Override
+ protected void init(Context context, PhoneNotifier notifier) {
+ mCi.setPhoneType(PhoneConstants.PHONE_TYPE_CDMA);
+ mCT = new CdmaCallTracker(this);
+ mCdmaSSM = CdmaSubscriptionSourceManager.getInstance(context, mCi, this,
+ EVENT_CDMA_SUBSCRIPTION_SOURCE_CHANGED, null);
+ mRuimPhoneBookInterfaceManager = new RuimPhoneBookInterfaceManager(this);
+ mSubInfo = new PhoneSubInfo(this);
+ mEriManager = new EriManager(this, context, EriManager.ERI_FROM_XML);
+
+ mCi.registerForAvailable(this, EVENT_RADIO_AVAILABLE, null);
+ mCi.registerForOffOrNotAvailable(this, EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null);
+ mCi.registerForOn(this, EVENT_RADIO_ON, null);
+ mCi.setOnSuppServiceNotification(this, EVENT_SSN, null);
+ mSST.registerForNetworkAttached(this, EVENT_REGISTERED_TO_NETWORK, null);
+ mCi.setEmergencyCallbackMode(this, EVENT_EMERGENCY_CALLBACK_MODE_ENTER, null);
+ mCi.registerForExitEmergencyCallbackMode(this, EVENT_EXIT_EMERGENCY_CALLBACK_RESPONSE,
+ null);
+
+ PowerManager pm
+ = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
+ mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,LOG_TAG);
+
+ // This is needed to handle phone process crashes
+ String inEcm = SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE, "false");
+ mIsPhoneInEcmState = inEcm.equals("true");
+ if (mIsPhoneInEcmState) {
+ // Send a message which will invoke handleExitEmergencyCallbackMode
+ mCi.exitEmergencyCallbackMode(obtainMessage(EVENT_EXIT_EMERGENCY_CALLBACK_RESPONSE));
+ }
+
+ // get the string that specifies the carrier OTA Sp number
+ mCarrierOtaSpNumSchema = SystemProperties.get(
+ TelephonyProperties.PROPERTY_OTASP_NUM_SCHEMA,"");
+
+ // Notify voicemails.
+ notifier.notifyMessageWaitingChanged(this);
+ setProperties();
+ }
+
+ private void onSubscriptionActivated() {
+// mSubscriptionData = SubscriptionManager.getCurrentSubscription(mSubscription);
+
+ log("SUBSCRIPTION ACTIVATED : slotId : " + mSubscriptionData.slotId
+ + " appid : " + mSubscriptionData.m3gpp2Index
+ + " subId : " + mSubscriptionData.subId
+ + " subStatus : " + mSubscriptionData.subStatus);
+
+ // Make sure properties are set for proper subscription.
+ setProperties();
+
+ onUpdateIccAvailability();
+ mSST.sendMessage(mSST.obtainMessage(ServiceStateTracker.EVENT_ICC_CHANGED));
+ ((CdmaLteServiceStateTracker)mSST).updateCdmaSubscription();
+ ((DcTracker)mDcTracker).updateRecords();
+ }
+
+ private void onSubscriptionDeactivated() {
+ log("SUBSCRIPTION DEACTIVATED");
+ // resetSubSpecifics
+ mSubscriptionData = null;
+ }
+
+ // Set the properties per subscription
+ private void setProperties() {
+ //Change the system property
+ setSystemProperty(TelephonyProperties.CURRENT_ACTIVE_PHONE,
+ new Integer(PhoneConstants.PHONE_TYPE_CDMA).toString());
+ // Sets operator alpha property by retrieving from build-time system property
+ String operatorAlpha = SystemProperties.get("ro.cdma.home.operator.alpha");
+ setSystemProperty(PROPERTY_ICC_OPERATOR_ALPHA, operatorAlpha);
+
+ // Sets operator numeric property by retrieving from build-time system property
+ String operatorNumeric = SystemProperties.get(PROPERTY_CDMA_HOME_OPERATOR_NUMERIC);
+ log("update icc_operator_numeric=" + operatorNumeric);
+ setSystemProperty(PROPERTY_ICC_OPERATOR_NUMERIC, operatorNumeric);
+ // Sets iso country property by retrieving from build-time system property
+ setIsoCountryProperty(operatorNumeric);
+ // Updates MCC MNC device configuration information
+ log("update mccmnc=" + operatorNumeric);
+ MccTable.updateMccMncConfiguration(mContext, operatorNumeric, false);
+ // Sets current entry in the telephony carrier table
+ updateCurrentCarrierInProvider();
+ }
+
+ @Override
+ protected UiccCardApplication getUiccCardApplication() {
+ return mUiccController.getUiccCardApplication(mPhoneId, UiccController.APP_FAM_3GPP2);
+ }
+
+ @Override
+ public void setSystemProperty(String property, String value) {
+ if(getUnitTestMode()) {
+ return;
+ }
+ TelephonyManager.setTelephonyProperty(property, getSubId(), value);
+ }
+
+ public String getSystemProperty(String property, String defValue) {
+ if(getUnitTestMode()) {
+ return null;
+ }
+ return TelephonyManager.getTelephonyProperty(property, getSubId(), defValue);
+ }
+
+ public void updateDataConnectionTracker() {
+ ((DcTracker)mDcTracker).update();
+ }
+
+ public void setInternalDataEnabled(boolean enable, Message onCompleteMsg) {
+ ((DcTracker)mDcTracker)
+ .setInternalDataEnabled(enable, onCompleteMsg);
+ }
+
+ public boolean setInternalDataEnabledFlag(boolean enable) {
+ return ((DcTracker)mDcTracker)
+ .setInternalDataEnabledFlag(enable);
+ }
+
+ /**
+ * @return operator numeric.
+ */
+ public String getOperatorNumeric() {
+ String operatorNumeric = null;
+
+ if (mCdmaSubscriptionSource == CDMA_SUBSCRIPTION_NV) {
+ operatorNumeric = SystemProperties.get("ro.cdma.home.operator.numeric");
+ } else if (mCdmaSubscriptionSource == CDMA_SUBSCRIPTION_RUIM_SIM
+ && mIccRecords != null && mIccRecords.get() != null) {
+ operatorNumeric = mIccRecords.get().getOperatorNumeric();
+ } else {
+ Rlog.e(LOG_TAG, "getOperatorNumeric: Cannot retrieve operatorNumeric:"
+ + " mCdmaSubscriptionSource = " + mCdmaSubscriptionSource + " mIccRecords = "
+ + ((mIccRecords != null) && (mIccRecords.get() != null)
+ ? mIccRecords.get().getRecordsLoaded()
+ : null));
+ }
+
+ Rlog.d(LOG_TAG, "getOperatorNumeric: mCdmaSubscriptionSource = " + mCdmaSubscriptionSource
+ + " operatorNumeric = " + operatorNumeric);
+
+ return operatorNumeric;
+ }
+ public void registerForAllDataDisconnected(Handler h, int what, Object obj) {
+ ((DcTracker)mDcTracker)
+ .registerForAllDataDisconnected(h, what, obj);
+ }
+
+ public void unregisterForAllDataDisconnected(Handler h) {
+ ((DcTracker)mDcTracker)
+ .unregisterForAllDataDisconnected(h);
+ }
+
+ @Override
protected void log(String s) {
Rlog.d(LOG_LTE_TAG, s);
}
diff --git a/src/java/com/android/internal/telephony/cdma/CDMAPhone.java b/src/java/com/android/internal/telephony/cdma/CDMAPhone.java
index fb177ca..b656e40 100644
--- a/src/java/com/android/internal/telephony/cdma/CDMAPhone.java
+++ b/src/java/com/android/internal/telephony/cdma/CDMAPhone.java
@@ -38,6 +38,7 @@
import android.telephony.CellLocation;
import android.telephony.PhoneNumberUtils;
import android.telephony.ServiceState;
+import android.telephony.SubscriptionManager;
import android.telephony.cdma.CdmaCellLocation;
import android.text.TextUtils;
import android.telephony.Rlog;
@@ -62,10 +63,12 @@
import com.android.internal.telephony.TelephonyIntents;
import com.android.internal.telephony.TelephonyProperties;
import com.android.internal.telephony.UUSInfo;
+import com.android.internal.telephony.VoicePhone;
import com.android.internal.telephony.dataconnection.DcTracker;
import com.android.internal.telephony.uicc.IccException;
import com.android.internal.telephony.uicc.IccRecords;
import com.android.internal.telephony.uicc.RuimRecords;
+import com.android.internal.telephony.uicc.UiccCard;
import com.android.internal.telephony.uicc.UiccCardApplication;
import com.android.internal.telephony.uicc.UiccController;
import java.io.FileDescriptor;
@@ -75,6 +78,7 @@
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import static com.android.internal.telephony.PhoneConstants.SUBSCRIPTION_KEY;
import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_ALPHA;
import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_ISO_COUNTRY;
import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC;
@@ -103,7 +107,8 @@
CdmaSubscriptionSourceManager mCdmaSSM;
ArrayList <CdmaMmiCode> mPendingMmis = new ArrayList<CdmaMmiCode>();
RuimPhoneBookInterfaceManager mRuimPhoneBookInterfaceManager;
- int mCdmaSubscriptionSource = CdmaSubscriptionSourceManager.SUBSCRIPTION_SOURCE_UNKNOWN;
+ int mCdmaSubscriptionSource =
+ CdmaSubscriptionSourceManager.SUBSCRIPTION_SOURCE_UNKNOWN;
PhoneSubInfo mSubInfo;
EriManager mEriManager;
WakeLock mWakeLock;
@@ -117,14 +122,14 @@
// mEcmExitRespRegistrant is informed after the phone has been exited
//the emergency callback mode
//keep track of if phone is in emergency callback mode
- private boolean mIsPhoneInEcmState;
+ protected boolean mIsPhoneInEcmState;
private Registrant mEcmExitRespRegistrant;
protected String mImei;
protected String mImeiSv;
private String mEsn;
private String mMeid;
// string to define how the carrier specifies its own ota sp number
- private String mCarrierOtaSpNumSchema;
+ protected String mCarrierOtaSpNumSchema;
// A runnable which is used to automatically exit from Ecm after a period of time.
private Runnable mExitEcmRunnable = new Runnable() {
@@ -146,6 +151,13 @@
}
public CDMAPhone(Context context, CommandsInterface ci, PhoneNotifier notifier,
+ int phoneId) {
+ super("CDMA", notifier, context, ci, false, phoneId);
+ initSstIcc();
+ init(context, notifier);
+ }
+
+ public CDMAPhone(Context context, CommandsInterface ci, PhoneNotifier notifier,
boolean unitTestMode) {
super("CDMA", notifier, context, ci, unitTestMode);
initSstIcc();
@@ -200,7 +212,7 @@
String operatorNumeric = SystemProperties.get(PROPERTY_CDMA_HOME_OPERATOR_NUMERIC);
log("init: operatorAlpha='" + operatorAlpha
+ "' operatorNumeric='" + operatorNumeric + "'");
- if (mUiccController.getUiccCardApplication(UiccController.APP_FAM_3GPP) == null) {
+ if (mUiccController.getUiccCardApplication(mPhoneId, UiccController.APP_FAM_3GPP) == null) {
log("init: APP_FAM_3GPP == NULL");
if (!TextUtils.isEmpty(operatorAlpha)) {
log("init: set 'gsm.sim.operator.alpha' to operator='" + operatorAlpha + "'");
@@ -208,6 +220,7 @@
}
if (!TextUtils.isEmpty(operatorNumeric)) {
log("init: set 'gsm.sim.operator.numeric' to operator='" + operatorNumeric + "'");
+ log("update icc_operator_numeric=" + operatorNumeric);
setSystemProperty(PROPERTY_ICC_OPERATOR_NUMERIC, operatorNumeric);
}
setIsoCountryProperty(operatorNumeric);
@@ -258,6 +271,7 @@
mSST = null;
mEriManager = null;
mExitEcmRunnable = null;
+
super.removeReferences();
}
@@ -371,6 +385,26 @@
@Override
public Connection
dial (String dialString) throws CallStateException {
+ if (mVoicePhone != null
+ && mVoicePhone.getServiceState().getState() == ServiceState.STATE_IN_SERVICE
+ && !PhoneNumberUtils.isEmergencyNumber(dialString)) {
+ try {
+ return mVoicePhone.dial(dialString);
+ } catch (CallStateException e) {
+ if (!VoicePhone.CS_FALLBACK.equals(e.getMessage())) {
+ CallStateException ce = new CallStateException(e.getMessage());
+ ce.setStackTrace(e.getStackTrace());
+ throw ce;
+ }
+ }
+ }
+
+ return dialInternal(dialString, null);
+ }
+
+ @Override
+ protected Connection
+ dialInternal (String dialString, UUSInfo uusInfo) throws CallStateException {
// Need to make sure dialString gets parsed properly
String newDialString = PhoneNumberUtils.stripSeparators(dialString);
return mCT.dial(newDialString);
@@ -447,7 +481,7 @@
IccRecords r = mIccRecords.get();
if (r == null) {
// to get ICCID form SIMRecords because it is on MF.
- r = mUiccController.getIccRecords(UiccController.APP_FAM_3GPP);
+ r = mUiccController.getIccRecords(mPhoneId, UiccController.APP_FAM_3GPP);
}
return (r != null) ? r.getIccId() : null;
}
@@ -828,9 +862,9 @@
// Read platform settings for dynamic voicemail number
if (getContext().getResources().getBoolean(com.android.internal
.R.bool.config_telephony_use_own_number_for_voicemail)) {
- number = sp.getString(VM_NUMBER_CDMA, getLine1Number());
+ number = sp.getString(VM_NUMBER_CDMA + getPhoneId(), getLine1Number());
} else {
- number = sp.getString(VM_NUMBER_CDMA, "*86");
+ number = sp.getString(VM_NUMBER_CDMA + getPhoneId(), "*86");
}
return number;
}
@@ -960,6 +994,7 @@
//Send an Intent
Intent intent = new Intent(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED);
intent.putExtra(PhoneConstants.PHONE_IN_ECM_STATE, mIsPhoneInEcmState);
+ SubscriptionManager.putPhoneIdAndSubIdExtra(intent, getPhoneId());
ActivityManagerNative.broadcastStickyIntent(intent,null,UserHandle.USER_ALL);
if (DBG) Rlog.d(LOG_TAG, "sendEmergencyCallbackModeChange");
}
@@ -1125,12 +1160,18 @@
case EVENT_RADIO_OFF_OR_NOT_AVAILABLE:{
Rlog.d(LOG_TAG, "Event EVENT_RADIO_OFF_OR_NOT_AVAILABLE Received");
+ if (mVoicePhone != null) {
+ mVoicePhone.getServiceState().setStateOff();
+ }
}
break;
case EVENT_RADIO_ON:{
Rlog.d(LOG_TAG, "Event EVENT_RADIO_ON Received");
handleCdmaSubscriptionSource(mCdmaSSM.getCdmaSubscriptionSource());
+ if (mVoicePhone != null) {
+ mVoicePhone.getServiceState().setStateOutOfService();
+ }
}
break;
@@ -1176,19 +1217,22 @@
}
}
+ protected UiccCardApplication getUiccCardApplication() {
+ return mUiccController.getUiccCardApplication(mPhoneId, UiccController.APP_FAM_3GPP2);
+ }
+
@Override
protected void onUpdateIccAvailability() {
if (mUiccController == null ) {
return;
}
- UiccCardApplication newUiccApplication =
- mUiccController.getUiccCardApplication(UiccController.APP_FAM_3GPP2);
+ UiccCardApplication newUiccApplication = getUiccCardApplication();
if (newUiccApplication == null) {
log("can't find 3GPP2 application; trying APP_FAM_3GPP");
- newUiccApplication = mUiccController
- .getUiccCardApplication(UiccController.APP_FAM_3GPP);
+ newUiccApplication =
+ mUiccController.getUiccCardApplication(mPhoneId, UiccController.APP_FAM_3GPP);
}
UiccCardApplication app = mUiccApplication.get();
@@ -1197,7 +1241,6 @@
log("Removing stale icc objects.");
if (mIccRecords.get() != null) {
unregisterForRuimRecordEvents();
- mRuimPhoneBookInterfaceManager.updateIccRecords(null);
}
mIccRecords.set(null);
mUiccApplication.set(null);
@@ -1207,7 +1250,6 @@
mUiccApplication.set(newUiccApplication);
mIccRecords.set(newUiccApplication.getIccRecords());
registerForRuimRecordEvents();
- mRuimPhoneBookInterfaceManager.updateIccRecords(mIccRecords.get());
}
}
}
@@ -1269,7 +1311,7 @@
* {@inheritDoc}
*/
@Override
- public final void setSystemProperty(String property, String value) {
+ public void setSystemProperty(String property, String value) {
super.setSystemProperty(property, value);
}
@@ -1534,7 +1576,7 @@
// Update the preference value of voicemail number
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext());
SharedPreferences.Editor editor = sp.edit();
- editor.putString(VM_NUMBER_CDMA, number);
+ editor.putString(VM_NUMBER_CDMA + getPhoneId(), number);
editor.apply();
}
@@ -1542,7 +1584,7 @@
* Sets PROPERTY_ICC_OPERATOR_ISO_COUNTRY property
*
*/
- private void setIsoCountryProperty(String operatorNumeric) {
+ protected void setIsoCountryProperty(String operatorNumeric) {
if (TextUtils.isEmpty(operatorNumeric)) {
log("setIsoCountryProperty: clear 'gsm.sim.operator.iso-country'");
setSystemProperty(PROPERTY_ICC_OPERATOR_ISO_COUNTRY, "");
@@ -1579,6 +1621,7 @@
getContext().getContentResolver().insert(uri, map);
// Updates MCC MNC device configuration information
+ log("update mccmnc=" + operatorNumeric);
MccTable.updateMccMncConfiguration(mContext, operatorNumeric, false);
return true;
@@ -1600,6 +1643,10 @@
}
public void prepareEri() {
+ if (mEriManager == null) {
+ Rlog.e(LOG_TAG, "PrepareEri: Trying to access stale objects");
+ return;
+ }
mEriManager.loadEriFile();
if(mEriManager.isEriFileLoaded()) {
// when the ERI file is loaded
diff --git a/src/java/com/android/internal/telephony/cdma/CdmaConnection.java b/src/java/com/android/internal/telephony/cdma/CdmaConnection.java
index f1d6bcb..6571f85 100755
--- a/src/java/com/android/internal/telephony/cdma/CdmaConnection.java
+++ b/src/java/com/android/internal/telephony/cdma/CdmaConnection.java
@@ -251,6 +251,11 @@
}
@Override
+ public long getConnectTimeReal() {
+ return mConnectTimeReal;
+ }
+
+ @Override
public long getDisconnectTime() {
return mDisconnectTime;
}
@@ -267,6 +272,11 @@
}
@Override
+ public long getHoldingStartTime() {
+ return mHoldingStartTime;
+ }
+
+ @Override
public long getHoldDurationMillis() {
if (getState() != CdmaCall.State.HOLDING) {
// If not holding, return 0
@@ -963,4 +973,13 @@
return mPreciseCause;
}
+ @Override
+ public Connection getOrigConnection() {
+ return null;
+ }
+
+ @Override
+ public boolean isMultiparty() {
+ return false;
+ }
}
diff --git a/src/java/com/android/internal/telephony/cdma/CdmaInboundSmsHandler.java b/src/java/com/android/internal/telephony/cdma/CdmaInboundSmsHandler.java
index 9352639..355de31 100644
--- a/src/java/com/android/internal/telephony/cdma/CdmaInboundSmsHandler.java
+++ b/src/java/com/android/internal/telephony/cdma/CdmaInboundSmsHandler.java
@@ -255,7 +255,7 @@
// Store the voicemail count in preferences.
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
SharedPreferences.Editor editor = sp.edit();
- editor.putInt(CDMAPhone.VM_COUNT_CDMA, voicemailCount);
+ editor.putInt(CDMAPhone.VM_COUNT_CDMA + mPhone.getPhoneId(), voicemailCount);
editor.apply();
mPhone.setVoiceMessageWaiting(1, voicemailCount);
}
diff --git a/src/java/com/android/internal/telephony/cdma/CdmaLteServiceStateTracker.java b/src/java/com/android/internal/telephony/cdma/CdmaLteServiceStateTracker.java
index 7c46004..0015cd7 100644
--- a/src/java/com/android/internal/telephony/cdma/CdmaLteServiceStateTracker.java
+++ b/src/java/com/android/internal/telephony/cdma/CdmaLteServiceStateTracker.java
@@ -16,11 +16,15 @@
package com.android.internal.telephony.cdma;
+import android.content.Intent;
import com.android.internal.telephony.TelephonyProperties;
import com.android.internal.telephony.MccTable;
import com.android.internal.telephony.EventLogTags;
import com.android.internal.telephony.uicc.RuimRecords;
import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppState;
+import com.android.internal.telephony.TelephonyIntents;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneFactory;
import android.telephony.CellInfo;
import android.telephony.CellInfoLte;
@@ -29,15 +33,23 @@
import android.telephony.SignalStrength;
import android.telephony.ServiceState;
import android.telephony.cdma.CdmaCellLocation;
+import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.os.AsyncResult;
import android.os.Message;
+import android.os.UserHandle;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.telephony.Rlog;
import android.util.EventLog;
+import com.android.internal.telephony.dataconnection.DcTrackerBase;
+import com.android.internal.telephony.ProxyController;
+import android.telephony.SubscriptionManager;
+import com.android.internal.telephony.uicc.UiccCardApplication;
+import com.android.internal.telephony.uicc.UiccController;
+
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -46,6 +58,7 @@
public class CdmaLteServiceStateTracker extends CdmaServiceStateTracker {
private CDMALTEPhone mCdmaLtePhone;
private final CellInfoLte mCellInfoLte;
+ private static final int EVENT_ALL_DATA_DISCONNECTED = 1001;
private CellIdentityLte mNewCellIdentityLte = new CellIdentityLte();
private CellIdentityLte mLasteCellIdentityLte = new CellIdentityLte();
@@ -95,6 +108,19 @@
// the latest variables.
pollState();
break;
+ case EVENT_ALL_DATA_DISCONNECTED:
+ long dds = SubscriptionManager.getDefaultDataSubId();
+ ProxyController.getInstance().unregisterForAllDataDisconnected(dds, this);
+ synchronized(this) {
+ if (mPendingRadioPowerOffAfterDataOff) {
+ if (DBG) log("EVENT_ALL_DATA_DISCONNECTED, turn radio off now.");
+ hangupAndPowerOff();
+ mPendingRadioPowerOffAfterDataOff = false;
+ } else {
+ log("EVENT_ALL_DATA_DISCONNECTED is stale");
+ }
+ }
+ break;
default:
super.handleMessage(msg);
}
@@ -570,6 +596,63 @@
}
@Override
+ protected UiccCardApplication getUiccCardApplication() {
+ return mUiccController.getUiccCardApplication(((CDMALTEPhone)mPhone).
+ getPhoneId(), UiccController.APP_FAM_3GPP2);
+ }
+
+ protected void updateCdmaSubscription() {
+ mCi.getCDMASubscription(obtainMessage(EVENT_POLL_STATE_CDMA_SUBSCRIPTION));
+ }
+
+ /**
+ * Clean up existing voice and data connection then turn off radio power.
+ *
+ * Hang up the existing voice calls to decrease call drop rate.
+ */
+ @Override
+ public void powerOffRadioSafely(DcTrackerBase dcTracker) {
+ synchronized (this) {
+ if (!mPendingRadioPowerOffAfterDataOff) {
+ long dds = SubscriptionManager.getDefaultDataSubId();
+ // To minimize race conditions we call cleanUpAllConnections on
+ // both if else paths instead of before this isDisconnected test.
+ if (dcTracker.isDisconnected()
+ && (dds == mPhone.getSubId()
+ || (dds != mPhone.getSubId()
+ && ProxyController.getInstance().isDataDisconnected(dds)))) {
+ // To minimize race conditions we do this after isDisconnected
+ dcTracker.cleanUpAllConnections(Phone.REASON_RADIO_TURNED_OFF);
+ if (DBG) log("Data disconnected, turn off radio right away.");
+ hangupAndPowerOff();
+ } else {
+ dcTracker.cleanUpAllConnections(Phone.REASON_RADIO_TURNED_OFF);
+ if (dds != mPhone.getSubId()
+ && !ProxyController.getInstance().isDataDisconnected(dds)) {
+ if (DBG) log("Data is active on DDS. Wait for all data disconnect");
+ // Data is not disconnected on DDS. Wait for the data disconnect complete
+ // before sending the RADIO_POWER off.
+ ProxyController.getInstance().registerForAllDataDisconnected(dds, this,
+ EVENT_ALL_DATA_DISCONNECTED, null);
+ mPendingRadioPowerOffAfterDataOff = true;
+ }
+ Message msg = Message.obtain(this);
+ msg.what = EVENT_SET_RADIO_POWER_OFF;
+ msg.arg1 = ++mPendingRadioPowerOffAfterDataOffTag;
+ if (sendMessageDelayed(msg, 30000)) {
+ if (DBG) log("Wait upto 30s for data to disconnect, then turn off radio.");
+ mPendingRadioPowerOffAfterDataOff = true;
+ } else {
+ log("Cannot send delayed Msg, turn off radio right away.");
+ hangupAndPowerOff();
+ mPendingRadioPowerOffAfterDataOff = false;
+ }
+ }
+ }
+ }
+ }
+
+ @Override
protected void log(String s) {
Rlog.d(LOG_TAG, "[CdmaLteSST] " + s);
}
diff --git a/src/java/com/android/internal/telephony/cdma/CdmaMmiCode.java b/src/java/com/android/internal/telephony/cdma/CdmaMmiCode.java
index 3cb8bc7..9ff9200 100644
--- a/src/java/com/android/internal/telephony/cdma/CdmaMmiCode.java
+++ b/src/java/com/android/internal/telephony/cdma/CdmaMmiCode.java
@@ -22,6 +22,7 @@
import com.android.internal.telephony.uicc.UiccCardApplication;
import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppState;
import com.android.internal.telephony.MmiCode;
+import com.android.internal.telephony.Phone;
import android.os.AsyncResult;
import android.os.Handler;
@@ -162,6 +163,11 @@
return mMessage;
}
+ public Phone
+ getPhone() {
+ return ((Phone) mPhone);
+ }
+
// inherited javadoc suffices
@Override
public void
diff --git a/src/java/com/android/internal/telephony/cdma/CdmaServiceCategoryProgramHandler.java b/src/java/com/android/internal/telephony/cdma/CdmaServiceCategoryProgramHandler.java
index bb9f9d5..81413c6 100644
--- a/src/java/com/android/internal/telephony/cdma/CdmaServiceCategoryProgramHandler.java
+++ b/src/java/com/android/internal/telephony/cdma/CdmaServiceCategoryProgramHandler.java
@@ -26,10 +26,12 @@
import android.os.Message;
import android.provider.Telephony.Sms.Intents;
import android.telephony.PhoneNumberUtils;
+import android.telephony.SubscriptionManager;
import android.telephony.cdma.CdmaSmsCbProgramData;
import android.telephony.cdma.CdmaSmsCbProgramResults;
import com.android.internal.telephony.CommandsInterface;
+import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.WakeLockStateMachine;
import com.android.internal.telephony.cdma.sms.BearerData;
import com.android.internal.telephony.cdma.sms.CdmaSmsAddress;
@@ -103,7 +105,7 @@
Intent intent = new Intent(Intents.SMS_SERVICE_CATEGORY_PROGRAM_DATA_RECEIVED_ACTION);
intent.putExtra("sender", sms.getOriginatingAddress());
intent.putParcelableArrayListExtra("program_data", programDataList);
-
+ SubscriptionManager.putPhoneIdAndSubIdExtra(intent, mPhone.getPhoneId());
mContext.sendOrderedBroadcast(intent, Manifest.permission.RECEIVE_SMS,
AppOpsManager.OP_RECEIVE_SMS, mScpResultsReceiver,
getHandler(), Activity.RESULT_OK, null, null);
diff --git a/src/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java b/src/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
index 8281b7f..ca38aab 100644
--- a/src/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
+++ b/src/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
@@ -38,6 +38,8 @@
import android.telephony.Rlog;
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
+import android.telephony.TelephonyManager;
+import android.telephony.SubscriptionManager;
import android.telephony.cdma.CdmaCellLocation;
import android.text.TextUtils;
import android.util.EventLog;
@@ -500,6 +502,11 @@
}
break;
+ case EVENT_CHANGE_IMS_STATE:
+ if (DBG) log("EVENT_CHANGE_IMS_STATE");
+ setPowerStateToDesired();
+ break;
+
default:
super.handleMessage(msg);
break;
@@ -537,6 +544,7 @@
protected void updateSpnDisplay() {
// mOperatorAlphaLong contains the ERI text
String plmn = mSS.getOperatorAlphaLong();
+
if (!TextUtils.equals(plmn, mCurPlmn)) {
// Allow A blank plmn, "" to set showPlmn to true. Previously, we
// would set showPlmn to true only if plmn was not empty, i.e. was not
@@ -553,6 +561,7 @@
intent.putExtra(TelephonyIntents.EXTRA_SPN, "");
intent.putExtra(TelephonyIntents.EXTRA_SHOW_PLMN, showPlmn);
intent.putExtra(TelephonyIntents.EXTRA_PLMN, plmn);
+ SubscriptionManager.putPhoneIdAndSubIdExtra(intent, mPhone.getPhoneId());
mPhone.getContext().sendStickyBroadcastAsUser(intent, UserHandle.ALL);
}
@@ -1291,7 +1300,7 @@
*/
private
boolean isRoamingBetweenOperators(boolean cdmaRoaming, ServiceState s) {
- String spn = SystemProperties.get(TelephonyProperties.PROPERTY_ICC_OPERATOR_ALPHA, "empty");
+ String spn = getSystemProperty(TelephonyProperties.PROPERTY_ICC_OPERATOR_ALPHA, "empty");
// NOTE: in case of RUIM we should completely ignore the ERI data file and
// mOperatorAlphaLong is set from RIL_REQUEST_OPERATOR response 0 (alpha ONS)
@@ -1377,7 +1386,7 @@
zone = TimeZone.getTimeZone( tzname );
}
- String iso = SystemProperties.get(TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY);
+ String iso = getSystemProperty(TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY, "");
if (zone == null) {
if (mGotCountryCode) {
@@ -1634,7 +1643,7 @@
*/
String getImsi() {
// TODO: When RUIM is enabled, IMSI will come from RUIM not build-time props.
- String operatorNumeric = SystemProperties.get(
+ String operatorNumeric = getSystemProperty(
TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC, "");
if (!TextUtils.isEmpty(operatorNumeric) && getCdmaMin() != null) {
@@ -1730,14 +1739,18 @@
}
}
+ protected UiccCardApplication getUiccCardApplication() {
+ return mUiccController.getUiccCardApplication(mPhone.getPhoneId(),
+ UiccController.APP_FAM_3GPP2);
+ }
+
@Override
protected void onUpdateIccAvailability() {
if (mUiccController == null ) {
return;
}
- UiccCardApplication newUiccApplication =
- mUiccController.getUiccCardApplication(UiccController.APP_FAM_3GPP2);
+ UiccCardApplication newUiccApplication = getUiccCardApplication();
if (mUiccApplcation != newUiccApplication) {
if (mUiccApplcation != null) {
@@ -1810,4 +1823,24 @@
pw.println(" mRegistrationDeniedReason=" + mRegistrationDeniedReason);
pw.println(" mCurrentCarrier=" + mCurrentCarrier);
}
+
+ @Override
+ public void setImsRegistrationState(boolean registered) {
+ log("ImsRegistrationState - registered : " + registered);
+
+ if (mImsRegistrationOnOff && !registered) {
+ if (mAlarmSwitch) {
+ mImsRegistrationOnOff = registered;
+
+ Context context = mPhone.getContext();
+ AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
+ am.cancel(mRadioOffIntent);
+ mAlarmSwitch = false;
+
+ sendMessage(obtainMessage(EVENT_CHANGE_IMS_STATE));
+ return;
+ }
+ }
+ mImsRegistrationOnOff = registered;
+ }
}
diff --git a/src/java/com/android/internal/telephony/cdma/CdmaSubscriptionSourceManager.java b/src/java/com/android/internal/telephony/cdma/CdmaSubscriptionSourceManager.java
index 0388f57..f7b8936 100644
--- a/src/java/com/android/internal/telephony/cdma/CdmaSubscriptionSourceManager.java
+++ b/src/java/com/android/internal/telephony/cdma/CdmaSubscriptionSourceManager.java
@@ -36,6 +36,10 @@
private static final int EVENT_CDMA_SUBSCRIPTION_SOURCE_CHANGED = 1;
private static final int EVENT_GET_CDMA_SUBSCRIPTION_SOURCE = 2;
private static final int EVENT_RADIO_ON = 3;
+ private static final int EVENT_SUBSCRIPTION_STATUS_CHANGED = 4;
+
+ // To know subscription is activated
+ private static final int SUBSCRIPTION_ACTIVATED = 1;
public static final int SUBSCRIPTION_SOURCE_UNKNOWN = -1;
public static final int SUBSCRIPTION_FROM_RUIM = 0; /* CDMA subscription from RUIM */
@@ -62,6 +66,7 @@
mCi.registerForOn(this, EVENT_RADIO_ON, null);
int subscriptionSource = getDefault(context);
mCdmaSubscriptionSource.set(subscriptionSource);
+ mCi.registerForSubscriptionStatusChanged(this, EVENT_SUBSCRIPTION_STATUS_CHANGED, null);
}
/**
@@ -91,6 +96,7 @@
if (sReferenceCount <= 0) {
mCi.unregisterForCdmaSubscriptionChanged(this);
mCi.unregisterForOn(this);
+ mCi.unregisterForSubscriptionStatusChanged(this);
sInstance = null;
}
}
@@ -116,6 +122,24 @@
mCi.getCdmaSubscriptionSource(obtainMessage(EVENT_GET_CDMA_SUBSCRIPTION_SOURCE));
}
break;
+ case EVENT_SUBSCRIPTION_STATUS_CHANGED: {
+ log("EVENT_SUBSCRIPTION_STATUS_CHANGED");
+ ar = (AsyncResult)msg.obj;
+ if (ar.exception == null) {
+ int actStatus = ((int[])ar.result)[0];
+ log("actStatus = " + actStatus);
+ if (actStatus == SUBSCRIPTION_ACTIVATED) { // Subscription Activated
+ // In case of multi-SIM, framework should wait for the subscription ready
+ // to send any request to RIL. Otherwise it will return failure.
+ Rlog.v(LOG_TAG,"get Cdma Subscription Source");
+ mCi.getCdmaSubscriptionSource(
+ obtainMessage(EVENT_GET_CDMA_SUBSCRIPTION_SOURCE));
+ }
+ } else {
+ logw("EVENT_SUBSCRIPTION_STATUS_CHANGED, Exception:" + ar.exception);
+ }
+ }
+ break;
default:
super.handleMessage(msg);
}
diff --git a/src/java/com/android/internal/telephony/cdma/SmsMessage.java b/src/java/com/android/internal/telephony/cdma/SmsMessage.java
index 62ad42a..5c99a43 100644
--- a/src/java/com/android/internal/telephony/cdma/SmsMessage.java
+++ b/src/java/com/android/internal/telephony/cdma/SmsMessage.java
@@ -1033,8 +1033,9 @@
/**
* Returns the list of service category program data, if present.
* @return a list of CdmaSmsCbProgramData objects, or null if not present
+ * @hide
*/
- ArrayList<CdmaSmsCbProgramData> getSmsCbProgramData() {
+ public ArrayList<CdmaSmsCbProgramData> getSmsCbProgramData() {
return mBearerData.serviceCategoryProgramData;
}
}
diff --git a/src/java/com/android/internal/telephony/dataconnection/DataCallResponse.java b/src/java/com/android/internal/telephony/dataconnection/DataCallResponse.java
index 75dd327..5d3a556 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DataCallResponse.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DataCallResponse.java
@@ -47,6 +47,7 @@
public String [] dnses = new String[0];
public String[] gateways = new String[0];
public int suggestedRetryTime = -1;
+ public String [] pcscf = new String[0];
/**
* Class returned by onSetupConnectionCompleted.
@@ -100,6 +101,12 @@
sb.append(",");
}
if (gateways.length > 0) sb.deleteCharAt(sb.length()-1);
+ sb.append("] pcscf=[");
+ for (String addr : pcscf) {
+ sb.append(addr);
+ sb.append(",");
+ }
+ if (pcscf.length > 0) sb.deleteCharAt(sb.length()-1);
sb.append("]}");
return sb.toString();
}
diff --git a/src/java/com/android/internal/telephony/dataconnection/DataConnection.java b/src/java/com/android/internal/telephony/dataconnection/DataConnection.java
index 616fef7..abe0afe 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DataConnection.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DataConnection.java
@@ -58,6 +58,9 @@
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
+import java.net.InetAddress;
+import java.util.Collection;
+
/**
* {@hide}
*
@@ -104,6 +107,8 @@
// The DCT that's talking to us, we only support one!
private DcTrackerBase mDct = null;
+ protected String[] mPcscfAddr;
+
/**
* Used internally for saving connecting parameters.
*/
@@ -291,6 +296,40 @@
}
}
+ public boolean isIpv4Connected() {
+ boolean ret = false;
+ Collection <InetAddress> addresses = mLinkProperties.getAddresses();
+
+ for (InetAddress addr: addresses) {
+ if (addr instanceof java.net.Inet4Address) {
+ java.net.Inet4Address i4addr = (java.net.Inet4Address) addr;
+ if (!i4addr.isAnyLocalAddress() && !i4addr.isLinkLocalAddress() &&
+ !i4addr.isLoopbackAddress() && !i4addr.isMulticastAddress()) {
+ ret = true;
+ break;
+ }
+ }
+ }
+ return ret;
+ }
+
+ public boolean isIpv6Connected() {
+ boolean ret = false;
+ Collection <InetAddress> addresses = mLinkProperties.getAddresses();
+
+ for (InetAddress addr: addresses) {
+ if (addr instanceof java.net.Inet6Address) {
+ java.net.Inet6Address i6addr = (java.net.Inet6Address) addr;
+ if (!i6addr.isAnyLocalAddress() && !i6addr.isLinkLocalAddress() &&
+ !i6addr.isLoopbackAddress() && !i6addr.isMulticastAddress()) {
+ ret = true;
+ break;
+ }
+ }
+ }
+ return ret;
+ }
+
UpdateLinkPropertyResult updateLinkProperty(DataCallResponse newState) {
UpdateLinkPropertyResult result = new UpdateLinkPropertyResult(mLinkProperties);
@@ -434,6 +473,7 @@
response.gateways = new String[0];
response.suggestedRetryTime =
mDcTesterFailBringUpAll.getDcFailBringUp().mSuggestedRetryTime;
+ response.pcscf = new String[0];
Message msg = obtainMessage(EVENT_SETUP_DATA_CONNECTION_DONE, cp);
AsyncResult.forMessage(msg, response, null);
@@ -645,6 +685,8 @@
mLastFailCause = DcFailCause.NONE;
mCid = -1;
+ mPcscfAddr = new String[5];
+
mLinkProperties = new LinkProperties();
mApnContexts.clear();
mApnSetting = null;
@@ -690,6 +732,9 @@
} else {
if (DBG) log("onSetupConnectionCompleted received DataCallResponse: " + response);
mCid = response.cid;
+
+ mPcscfAddr = response.pcscf;
+
result = updateLinkProperty(response).setupResult;
}
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcAsyncChannel.java b/src/java/com/android/internal/telephony/dataconnection/DcAsyncChannel.java
index 178f18d..9e77e1b 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DcAsyncChannel.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DcAsyncChannel.java
@@ -429,4 +429,8 @@
private void log(String s) {
android.telephony.Rlog.d(mLogTag, "DataConnectionAc " + s);
}
+
+ public String[] getPcscfAddr() {
+ return mDc.mPcscfAddr;
+ }
}
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcController.java b/src/java/com/android/internal/telephony/dataconnection/DcController.java
index dff6777..3d45ced 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DcController.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DcController.java
@@ -236,17 +236,22 @@
if (DBG) log("onDataStateChanged: Found ConnId=" + newState.cid
+ " newState=" + newState.toString());
if (newState.active == DATA_CONNECTION_ACTIVE_PH_LINK_INACTIVE) {
- DcFailCause failCause = DcFailCause.fromInt(newState.status);
- if (DBG) log("onDataStateChanged: inactive failCause=" + failCause);
- if (failCause.isRestartRadioFail()) {
- if (DBG) log("onDataStateChanged: X restart radio");
- mDct.sendRestartRadio();
- } else if (failCause.isPermanentFail()) {
- if (DBG) log("onDataStateChanged: inactive, add to cleanup list");
+ if (mDct.mIsCleanupRequired) {
apnsToCleanup.addAll(dc.mApnContexts);
+ mDct.mIsCleanupRequired = false;
} else {
- if (DBG) log("onDataStateChanged: inactive, add to retry list");
- dcsToRetry.add(dc);
+ DcFailCause failCause = DcFailCause.fromInt(newState.status);
+ if (DBG) log("onDataStateChanged: inactive failCause=" + failCause);
+ if (failCause.isRestartRadioFail()) {
+ if (DBG) log("onDataStateChanged: X restart radio");
+ mDct.sendRestartRadio();
+ } else if (failCause.isPermanentFail()) {
+ if (DBG) log("onDataStateChanged: inactive, add to cleanup list");
+ apnsToCleanup.addAll(dc.mApnContexts);
+ } else {
+ if (DBG) log("onDataStateChanged: inactive, add to retry list");
+ dcsToRetry.add(dc);
+ }
}
} else {
// Its active so update the DataConnections link properties
@@ -288,6 +293,7 @@
apnsToCleanup.addAll(dc.mApnContexts);
} else {
if (DBG) log("onDataStateChanged: simple change");
+
for (ApnContext apnContext : dc.mApnContexts) {
mPhone.notifyDataConnection(
PhoneConstants.REASON_LINK_PROPERTIES_CHANGED,
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcSwitchAsyncChannel.java b/src/java/com/android/internal/telephony/dataconnection/DcSwitchAsyncChannel.java
new file mode 100644
index 0000000..9bd4582
--- /dev/null
+++ b/src/java/com/android/internal/telephony/dataconnection/DcSwitchAsyncChannel.java
@@ -0,0 +1,164 @@
+/*
+* Copyright (C) 2011-2014 MediaTek Inc.
+*
+* 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.dataconnection;
+
+import com.android.internal.util.AsyncChannel;
+import com.android.internal.util.Protocol;
+import com.android.internal.telephony.PhoneConstants;
+
+import android.os.Message;
+import android.util.Log;
+
+public class DcSwitchAsyncChannel extends AsyncChannel {
+ private static final boolean DBG = true;
+ private static final boolean VDBG = false;
+ private static final String LOG_TAG = "DcSwitchAsyncChannel";
+
+ private int tagId = 0;
+ private DcSwitchState mDcSwitchState;
+
+ // ***** Event codes for driving the state machine
+ private static final int BASE = Protocol.BASE_DATA_CONNECTION_TRACKER + 0x00002000;
+ static final int REQ_CONNECT = BASE + 0;
+ static final int RSP_CONNECT = BASE + 1;
+ static final int REQ_DISCONNECT = BASE + 2;
+ static final int RSP_DISCONNECT = BASE + 3;
+ static final int REQ_IS_IDLE_STATE = BASE + 4;
+ static final int RSP_IS_IDLE_STATE = BASE + 5;
+ static final int REQ_IS_IDLE_OR_DEACTING_STATE = BASE + 6;
+ static final int RSP_IS_IDLE_OR_DEACTING_STATE = BASE + 7;
+
+ private static final int CMD_TO_STRING_COUNT = RSP_IS_IDLE_OR_DEACTING_STATE - BASE + 1;
+ private static String[] sCmdToString = new String[CMD_TO_STRING_COUNT];
+ static {
+ sCmdToString[REQ_CONNECT - BASE] = "REQ_CONNECT";
+ sCmdToString[RSP_CONNECT - BASE] = "RSP_CONNECT";
+ sCmdToString[REQ_DISCONNECT - BASE] = "REQ_DISCONNECT";
+ sCmdToString[RSP_DISCONNECT - BASE] = "RSP_DISCONNECT";
+ sCmdToString[REQ_IS_IDLE_STATE - BASE] = "REQ_IS_IDLE_STATE";
+ sCmdToString[RSP_IS_IDLE_STATE - BASE] = "RSP_IS_IDLE_STATE";
+ sCmdToString[REQ_IS_IDLE_OR_DEACTING_STATE - BASE] = "REQ_IS_IDLE_OR_DEACTING_STATE";
+ sCmdToString[RSP_IS_IDLE_OR_DEACTING_STATE - BASE] = "RSP_IS_IDLE_OR_DEACTING_STATE";
+ }
+
+ protected static String cmdToString(int cmd) {
+ cmd -= BASE;
+ if ((cmd >= 0) && (cmd < sCmdToString.length)) {
+ return sCmdToString[cmd];
+ } else {
+ return AsyncChannel.cmdToString(cmd + BASE);
+ }
+ }
+
+ public DcSwitchAsyncChannel(DcSwitchState dcSwitchState, int id) {
+ mDcSwitchState = dcSwitchState;
+ tagId = id;
+ }
+
+ public void reqConnect(String type) {
+ sendMessage(REQ_CONNECT, type);
+ if (DBG) log("reqConnect");
+ }
+
+ public int rspConnect(Message response) {
+ int retVal = response.arg1;
+ if (DBG) log("rspConnect=" + retVal);
+ return retVal;
+ }
+
+ public int connectSync(String type) {
+ Message response = sendMessageSynchronously(REQ_CONNECT, type);
+ if ((response != null) && (response.what == RSP_CONNECT)) {
+ return rspConnect(response);
+ } else {
+ log("rspConnect error response=" + response);
+ return PhoneConstants.APN_REQUEST_FAILED;
+ }
+ }
+
+ public void reqDisconnect(String type) {
+ sendMessage(REQ_DISCONNECT, type);
+ if (DBG) log("reqDisconnect");
+ }
+
+ public int rspDisconnect(Message response) {
+ int retVal = response.arg1;
+ if (DBG) log("rspDisconnect=" + retVal);
+ return retVal;
+ }
+
+ public int disconnectSync(String type) {
+ Message response = sendMessageSynchronously(REQ_DISCONNECT, type);
+ if ((response != null) && (response.what == RSP_DISCONNECT)) {
+ return rspDisconnect(response);
+ } else {
+ log("rspDisconnect error response=" + response);
+ return PhoneConstants.APN_REQUEST_FAILED;
+ }
+ }
+
+ public void reqIsIdle() {
+ sendMessage(REQ_IS_IDLE_STATE);
+ if (DBG) log("reqIsIdle");
+ }
+
+ public boolean rspIsIdle(Message response) {
+ boolean retVal = response.arg1 == 1;
+ if (DBG) log("rspIsIdle=" + retVal);
+ return retVal;
+ }
+
+ public boolean isIdleSync() {
+ Message response = sendMessageSynchronously(REQ_IS_IDLE_STATE);
+ if ((response != null) && (response.what == RSP_IS_IDLE_STATE)) {
+ return rspIsIdle(response);
+ } else {
+ log("rspIsIndle error response=" + response);
+ return false;
+ }
+ }
+
+ public void reqIsIdleOrDeacting() {
+ sendMessage(REQ_IS_IDLE_OR_DEACTING_STATE);
+ if (DBG) log("reqIsIdleOrDeacting");
+ }
+
+ public boolean rspIsIdleOrDeacting(Message response) {
+ boolean retVal = response.arg1 == 1;
+ if (DBG) log("rspIsIdleOrDeacting=" + retVal);
+ return retVal;
+ }
+
+ public boolean isIdleOrDeactingSync() {
+ Message response = sendMessageSynchronously(REQ_IS_IDLE_OR_DEACTING_STATE);
+ if ((response != null) && (response.what == RSP_IS_IDLE_OR_DEACTING_STATE)) {
+ return rspIsIdleOrDeacting(response);
+ } else {
+ log("rspIsIndleOrDeacting error response=" + response);
+ return false;
+ }
+ }
+
+ @Override
+ public String toString() {
+ return mDcSwitchState.getName();
+ }
+
+ private void log(String s) {
+ Log.d(LOG_TAG, "[DcSwitchAsyncChannel-" + tagId + "]: " + s);
+ }
+}
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcSwitchState.java b/src/java/com/android/internal/telephony/dataconnection/DcSwitchState.java
new file mode 100644
index 0000000..47740ef
--- /dev/null
+++ b/src/java/com/android/internal/telephony/dataconnection/DcSwitchState.java
@@ -0,0 +1,462 @@
+/*
+* Copyright (C) 2011-2014 MediaTek Inc.
+*
+* 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.dataconnection;
+
+import com.android.internal.util.AsyncChannel;
+import com.android.internal.util.Protocol;
+import com.android.internal.util.State;
+import com.android.internal.util.StateMachine;
+
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.telephony.PhoneBase;
+import com.android.internal.telephony.PhoneProxy;
+
+import android.os.AsyncResult;
+import android.os.Handler;
+import android.os.Message;
+import android.os.Registrant;
+import android.os.RegistrantList;
+import android.text.TextUtils;
+
+import android.util.Log;
+
+import java.util.HashSet;
+import java.util.Iterator;
+
+public class DcSwitchState extends StateMachine {
+ private static final boolean DBG = true;
+ private static final boolean VDBG = false;
+ private static final String LOG_TAG = "DcSwitchState";
+
+ // ***** Event codes for driving the state machine
+ private static final int BASE = Protocol.BASE_DATA_CONNECTION_TRACKER + 0x00001000;
+ private static final int EVENT_CONNECT = BASE + 0;
+ private static final int EVENT_DISCONNECT = BASE + 1;
+ private static final int EVENT_CLEANUP_ALL = BASE + 2;
+ private static final int EVENT_CONNECTED = BASE + 3;
+ private static final int EVENT_DETACH_DONE = BASE + 4;
+ private static final int EVENT_TO_IDLE_DIRECTLY = BASE + 5;
+ private static final int EVENT_TO_ACTING_DIRECTLY = BASE + 6;
+
+ private int mId;
+ private Phone mPhone;
+ private AsyncChannel mAc;
+ private RegistrantList mIdleRegistrants = new RegistrantList();
+ private HashSet<String> mApnTypes = new HashSet<String>();
+
+ private IdleState mIdleState = new IdleState();
+ private ActingState mActingState = new ActingState();
+ private ActedState mActedState = new ActedState();
+ private DeactingState mDeactingState = new DeactingState();
+ private DefaultState mDefaultState = new DefaultState();
+
+ protected DcSwitchState(Phone phone, String name, int id) {
+ super(name);
+ if (DBG) log("DcSwitchState constructor E");
+ mPhone = phone;
+ mId = id;
+
+ addState(mDefaultState);
+ addState(mIdleState, mDefaultState);
+ addState(mActingState, mDefaultState);
+ addState(mActedState, mDefaultState);
+ addState(mDeactingState, mDefaultState);
+ setInitialState(mIdleState);
+
+ if (DBG) log("DcSwitchState constructor X");
+ }
+
+ private int setupConnection(String type) {
+ mApnTypes.add(type);
+ log("DcSwitchState:setupConnection type = " + type);
+ return mPhone.enableApnType(type);
+ }
+
+ private int teardownConnection(String type) {
+ mApnTypes.remove(type);
+ if (mApnTypes.isEmpty()) {
+ log("No APN is using, then clean up all");
+ // Since last type is removed from mApnTypes and will not be disabled in requestDataIdle()
+ mPhone.disableApnType(type);
+ requestDataIdle();
+ transitionTo(mDeactingState);
+ return PhoneConstants.APN_REQUEST_STARTED;
+ } else {
+ return mPhone.disableApnType(type);
+ }
+ }
+
+ private void requestDataIdle() {
+ if (DBG) log("requestDataIdle is triggered");
+ Iterator<String> itrType = mApnTypes.iterator();
+ while (itrType.hasNext()) {
+ mPhone.disableApnType(itrType.next());
+ }
+ mApnTypes.clear();
+ PhoneBase pb = (PhoneBase)((PhoneProxy)mPhone).getActivePhone();
+ pb.mCi.setDataAllowed(false, obtainMessage(EVENT_DETACH_DONE));
+ }
+
+ public void notifyDataConnection(int phoneId, String state, String reason,
+ String apnName, String apnType, boolean unavailable) {
+ if (phoneId == mId &&
+ TextUtils.equals(state, PhoneConstants.DataState.CONNECTED.toString())) {
+ sendMessage(obtainMessage(EVENT_CONNECTED));
+ }
+ }
+
+ public void cleanupAllConnection() {
+ sendMessage(obtainMessage(EVENT_CLEANUP_ALL));
+ }
+
+ public void registerForIdle(Handler h, int what, Object obj) {
+ Registrant r = new Registrant(h, what, obj);
+ mIdleRegistrants.add(r);
+ }
+
+ public void unregisterForIdle(Handler h) {
+ mIdleRegistrants.remove(h);
+ }
+
+ public void transitToIdleState() {
+ sendMessage(obtainMessage(EVENT_TO_IDLE_DIRECTLY));
+ }
+
+ public void transitToActingState() {
+ sendMessage(obtainMessage(EVENT_TO_ACTING_DIRECTLY));
+ }
+
+ private class IdleState extends State {
+ @Override
+ public void enter() {
+ mIdleRegistrants.notifyRegistrants();
+ }
+
+ @Override
+ public boolean processMessage(Message msg) {
+ boolean retVal;
+
+ switch (msg.what) {
+ case DcSwitchAsyncChannel.REQ_CONNECT:
+ case EVENT_CONNECT: {
+ String type = (String)msg.obj;
+ if (DBG) {
+ log("IdleState: REQ_CONNECT/EVENT_CONNECT(" +
+ msg.what + ") type=" + type);
+ }
+
+ PhoneBase pb = (PhoneBase)((PhoneProxy)mPhone).getActivePhone();
+ pb.mCi.setDataAllowed(true, null);
+
+ int result = setupConnection(type);
+ if (msg.what == DcSwitchAsyncChannel.REQ_CONNECT) {
+ mAc.replyToMessage(msg, DcSwitchAsyncChannel.RSP_CONNECT, result);
+ }
+ transitionTo(mActingState);
+ retVal = HANDLED;
+ break;
+ }
+ case DcSwitchAsyncChannel.REQ_DISCONNECT: {
+ String type = (String)msg.obj;
+ if (DBG) {
+ log("IdleState: DcSwitchAsyncChannel.REQ_DISCONNECT type=" + type);
+ }
+ mAc.replyToMessage(msg, DcSwitchAsyncChannel.RSP_DISCONNECT,
+ PhoneConstants.APN_ALREADY_INACTIVE);
+ retVal = HANDLED;
+ break;
+ }
+ case EVENT_CLEANUP_ALL: {
+ if (DBG) {
+ log("IdleState: EVENT_CLEANUP_ALL" );
+ }
+ requestDataIdle();
+ retVal = HANDLED;
+ break;
+ }
+ case EVENT_CONNECTED: {
+ if (DBG) {
+ log("IdleState: Receive invalid event EVENT_CONNECTED!");
+ }
+ retVal = HANDLED;
+ break;
+ }
+ default:
+ if (VDBG) {
+ log("IdleState: nothandled msg.what=0x" +
+ Integer.toHexString(msg.what));
+ }
+ retVal = NOT_HANDLED;
+ break;
+ }
+ return retVal;
+ }
+ }
+
+ private class ActingState extends State {
+ @Override
+ public boolean processMessage(Message msg) {
+ boolean retVal;
+
+ switch (msg.what) {
+ case DcSwitchAsyncChannel.REQ_CONNECT:
+ case EVENT_CONNECT: {
+ String type = (String)msg.obj;
+ if (DBG) {
+ log("ActingState: REQ_CONNECT/EVENT_CONNECT(" + msg.what +
+ ") type=" + type);
+ }
+ int result = setupConnection(type);
+ if (msg.what == DcSwitchAsyncChannel.REQ_CONNECT) {
+ mAc.replyToMessage(msg, DcSwitchAsyncChannel.RSP_CONNECT, result);
+ }
+ retVal = HANDLED;
+ break;
+ }
+ case DcSwitchAsyncChannel.REQ_DISCONNECT: {
+ String type = (String)msg.obj;
+ if (DBG) {
+ log("ActingState: DcSwitchAsyncChannel.REQ_DISCONNECT type=" + type);
+ }
+ int result = teardownConnection(type);
+ mAc.replyToMessage(msg, DcSwitchAsyncChannel.RSP_DISCONNECT, result);
+ retVal = HANDLED;
+ break;
+ }
+ case EVENT_CONNECTED: {
+ if (DBG) {
+ log("ActingState: EVENT_CONNECTED");
+ }
+ transitionTo(mActedState);
+ retVal = HANDLED;
+ break;
+ }
+ case EVENT_CLEANUP_ALL: {
+ if (DBG) {
+ log("ActingState: EVENT_CLEANUP_ALL" );
+ }
+ requestDataIdle();
+ transitionTo(mDeactingState);
+ retVal = HANDLED;
+ break;
+ }
+ default:
+ if (VDBG) {
+ log("ActingState: nothandled msg.what=0x" +
+ Integer.toHexString(msg.what));
+ }
+ retVal = NOT_HANDLED;
+ break;
+ }
+ return retVal;
+ }
+ }
+
+ private class ActedState extends State {
+ @Override
+ public boolean processMessage(Message msg) {
+ boolean retVal;
+
+ switch (msg.what) {
+ case DcSwitchAsyncChannel.REQ_CONNECT:
+ case EVENT_CONNECT: {
+ String type = (String)msg.obj;
+ if (DBG) {
+ log("ActedState: REQ_CONNECT/EVENT_CONNECT(" + msg.what + ") type=" + type);
+ }
+ int result = setupConnection(type);
+ if (msg.what == DcSwitchAsyncChannel.REQ_CONNECT) {
+ mAc.replyToMessage(msg, DcSwitchAsyncChannel.RSP_CONNECT, result);
+ }
+ retVal = HANDLED;
+ break;
+ }
+ case DcSwitchAsyncChannel.REQ_DISCONNECT: {
+ String type = (String)msg.obj;
+ if (DBG) {
+ log("ActedState: DcSwitchAsyncChannel.REQ_DISCONNECT type=" + type);
+ }
+ int result = teardownConnection(type);
+ mAc.replyToMessage(msg, DcSwitchAsyncChannel.RSP_DISCONNECT, result);
+ retVal = HANDLED;
+ break;
+ }
+ case EVENT_CONNECTED: {
+ if (DBG) {
+ log("ActedState: EVENT_CONNECTED");
+ }
+ retVal = HANDLED;
+ break;
+ }
+ case EVENT_CLEANUP_ALL: {
+ if (DBG) {
+ log("ActedState: EVENT_CLEANUP_ALL" );
+ }
+ requestDataIdle();
+ transitionTo(mDeactingState);
+ retVal = HANDLED;
+ break;
+ }
+ default:
+ if (VDBG) {
+ log("ActingState: nothandled msg.what=0x" +
+ Integer.toHexString(msg.what));
+ }
+ retVal = NOT_HANDLED;
+ break;
+ }
+ return retVal;
+ }
+ }
+
+ private class DeactingState extends State {
+ @Override
+ public boolean processMessage(Message msg) {
+ boolean retVal;
+
+ switch (msg.what) {
+ case DcSwitchAsyncChannel.REQ_CONNECT:
+ case EVENT_CONNECT: {
+ String type = (String)msg.obj;
+ if (DBG) {
+ log("DeactingState: REQ_CONNECT/EVENT_CONNECT(" +
+ msg.what + ") type=" + type + ", request is defered.");
+ }
+ deferMessage(obtainMessage(EVENT_CONNECT, type));
+ if (msg.what == DcSwitchAsyncChannel.REQ_CONNECT) {
+ mAc.replyToMessage(msg, DcSwitchAsyncChannel.RSP_CONNECT,
+ PhoneConstants.APN_REQUEST_STARTED);
+ }
+ retVal = HANDLED;
+ break;
+ }
+ case DcSwitchAsyncChannel.REQ_DISCONNECT: {
+ String type = (String)msg.obj;
+ if (DBG) {
+ log("DeactingState: DcSwitchAsyncChannel.REQ_DISCONNECT type=" + type);
+ }
+ mAc.replyToMessage(msg, DcSwitchAsyncChannel.RSP_DISCONNECT,
+ PhoneConstants.APN_ALREADY_INACTIVE);
+ retVal = HANDLED;
+ break;
+ }
+ case EVENT_DETACH_DONE: {
+ if (DBG) {
+ log("DeactingState: EVENT_DETACH_DONE");
+ }
+ transitionTo(mIdleState);
+ retVal = HANDLED;
+ break;
+ }
+ case EVENT_CONNECTED: {
+ if (DBG) {
+ log("DeactingState: Receive invalid event EVENT_CONNECTED!");
+ }
+ retVal = HANDLED;
+ break;
+ }
+ case EVENT_CLEANUP_ALL: {
+ if (DBG) {
+ log("DeactingState: EVENT_CLEANUP_ALL, already deacting." );
+ }
+ retVal = HANDLED;
+ break;
+ }
+ default:
+ if (VDBG) {
+ log("DeactingState: nothandled msg.what=0x" +
+ Integer.toHexString(msg.what));
+ }
+ retVal = NOT_HANDLED;
+ break;
+ }
+ return retVal;
+ }
+ }
+
+ private class DefaultState extends State {
+ @Override
+ public boolean processMessage(Message msg) {
+ AsyncResult ar;
+
+ switch (msg.what) {
+ case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION: {
+ if (mAc != null) {
+ if (VDBG) log("Disconnecting to previous connection mAc=" + mAc);
+ mAc.replyToMessage(msg, AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED,
+ AsyncChannel.STATUS_FULL_CONNECTION_REFUSED_ALREADY_CONNECTED);
+ } else {
+ mAc = new AsyncChannel();
+ mAc.connected(null, getHandler(), msg.replyTo);
+ if (VDBG) log("DcDefaultState: FULL_CONNECTION reply connected");
+ mAc.replyToMessage(msg, AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED,
+ AsyncChannel.STATUS_SUCCESSFUL, mId, "hi");
+ }
+ break;
+ }
+ case AsyncChannel.CMD_CHANNEL_DISCONNECT: {
+ if (VDBG) log("CMD_CHANNEL_DISCONNECT");
+ mAc.disconnect();
+ break;
+ }
+ case AsyncChannel.CMD_CHANNEL_DISCONNECTED: {
+ if (VDBG) log("CMD_CHANNEL_DISCONNECTED");
+ mAc = null;
+ break;
+ }
+ case DcSwitchAsyncChannel.REQ_IS_IDLE_STATE: {
+ boolean val = getCurrentState() == mIdleState;
+ if (VDBG) log("REQ_IS_IDLE_STATE isIdle=" + val);
+ mAc.replyToMessage(msg, DcSwitchAsyncChannel.RSP_IS_IDLE_STATE, val ? 1 : 0);
+ break;
+ }
+ case DcSwitchAsyncChannel.REQ_IS_IDLE_OR_DEACTING_STATE: {
+ boolean val = (getCurrentState() == mIdleState || getCurrentState() == mDeactingState);
+ if (VDBG) log("REQ_IS_IDLE_OR_DEACTING_STATE isIdleDeacting=" + val);
+ mAc.replyToMessage(msg, DcSwitchAsyncChannel.RSP_IS_IDLE_OR_DEACTING_STATE, val ? 1 : 0);
+ break;
+ }
+ case EVENT_TO_ACTING_DIRECTLY: {
+ log("Just transit to Acting state");
+ transitionTo(mActingState);
+ break;
+ }
+ case EVENT_TO_IDLE_DIRECTLY: {
+ log("Just transit to Idle state");
+ Iterator<String> itrType = mApnTypes.iterator();
+ while (itrType.hasNext()) {
+ mPhone.disableApnType(itrType.next());
+ }
+ mApnTypes.clear();
+ transitionTo(mIdleState);
+ }
+ default:
+ if (DBG) {
+ log("DefaultState: shouldn't happen but ignore msg.what=0x" +
+ Integer.toHexString(msg.what));
+ }
+ break;
+ }
+ return HANDLED;
+ }
+ }
+
+ protected void log(String s) {
+ Log.d(LOG_TAG, "[" + getName() + "] " + s);
+ }
+}
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcTracker.java b/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
index 1144737..db299cb 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
@@ -38,9 +38,11 @@
import android.net.Uri;
import android.os.AsyncResult;
import android.os.Build;
+import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.Messenger;
+import android.os.RegistrantList;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
@@ -49,12 +51,14 @@
import android.telephony.CellLocation;
import android.telephony.ServiceState;
import android.telephony.TelephonyManager;
+import android.telephony.SubscriptionManager;
import android.telephony.cdma.CdmaCellLocation;
import android.telephony.gsm.GsmCellLocation;
import android.text.TextUtils;
import android.util.EventLog;
import android.telephony.Rlog;
+import com.android.internal.telephony.cdma.CDMALTEPhone;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneBase;
import com.android.internal.telephony.DctConstants;
@@ -73,6 +77,9 @@
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.HashMap;
+import android.provider.Settings;
+
+import com.android.internal.telephony.ServiceStateTracker;
/**
* {@hide}
*/
@@ -80,6 +87,16 @@
protected final String LOG_TAG = "DCT";
/**
+ * List of messages that are waiting to be posted, when data call disconnect
+ * is complete
+ */
+ private ArrayList<Message> mDisconnectAllCompleteMsgList = new ArrayList<Message>();
+
+ private RegistrantList mAllDataDisconnectedRegistrants = new RegistrantList();
+
+ protected int mDisconnectPendingCount = 0;
+
+ /**
* Handles changes to the APN db.
*/
private class ApnChangeObserver extends ContentObserver {
@@ -122,34 +139,18 @@
private NetworkFactory mNetworkFactory;
private NetworkCapabilities mNetworkFilter;
+ public boolean mImsRegistrationState = false;
+ private ApnContext mWaitCleanUpApnContext = null;
+ private boolean mDeregistrationAlarmState = false;
+ private PendingIntent mImsDeregistrationDelayIntent = null;
+
//***** Constructor
public DcTracker(PhoneBase p) {
super(p);
if (DBG) log("GsmDCT.constructor");
- p.mCi.registerForAvailable (this, DctConstants.EVENT_RADIO_AVAILABLE, null);
- p.mCi.registerForOffOrNotAvailable(this, DctConstants.EVENT_RADIO_OFF_OR_NOT_AVAILABLE,
- null);
-
- p.getCallTracker().registerForVoiceCallEnded (this, DctConstants.EVENT_VOICE_CALL_ENDED,
- null);
- p.getCallTracker().registerForVoiceCallStarted (this, DctConstants.EVENT_VOICE_CALL_STARTED,
- null);
- p.getServiceStateTracker().registerForDataConnectionAttached(this,
- DctConstants.EVENT_DATA_CONNECTION_ATTACHED, null);
- p.getServiceStateTracker().registerForDataConnectionDetached(this,
- DctConstants.EVENT_DATA_CONNECTION_DETACHED, null);
- p.getServiceStateTracker().registerForRoamingOn(this, DctConstants.EVENT_ROAMING_ON, null);
- p.getServiceStateTracker().registerForRoamingOff(this, DctConstants.EVENT_ROAMING_OFF,
- null);
- p.getServiceStateTracker().registerForPsRestrictedEnabled(this,
- DctConstants.EVENT_PS_RESTRICT_ENABLED, null);
- p.getServiceStateTracker().registerForPsRestrictedDisabled(this,
- DctConstants.EVENT_PS_RESTRICT_DISABLED, null);
- p.getServiceStateTracker().registerForDataRegStateOrRatChanged(this,
- DctConstants.EVENT_DATA_RAT_CHANGED, null);
mDataConnectionTracker = this;
-
+ update();
mApnObserver = new ApnChangeObserver();
p.getContext().getContentResolver().registerContentObserver(
Telephony.Carriers.CONTENT_URI, true, mApnObserver);
@@ -179,6 +180,33 @@
cm.registerNetworkFactory(mNetworkFactoryMessenger, "Telephony");
}
+ protected void registerForAllEvents() {
+ mPhone.mCi.registerForAvailable(this, DctConstants.EVENT_RADIO_AVAILABLE, null);
+ mPhone.mCi.registerForOffOrNotAvailable(this,
+ DctConstants.EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null);
+ mPhone.mCi.registerForDataNetworkStateChanged(this,
+ DctConstants.EVENT_DATA_STATE_CHANGED, null);
+ mPhone.getCallTracker().registerForVoiceCallEnded (this,
+ DctConstants.EVENT_VOICE_CALL_ENDED, null);
+ mPhone.getCallTracker().registerForVoiceCallStarted (this,
+ DctConstants.EVENT_VOICE_CALL_STARTED, null);
+ mPhone.getServiceStateTracker().registerForDataConnectionAttached(this,
+ DctConstants.EVENT_DATA_CONNECTION_ATTACHED, null);
+ mPhone.getServiceStateTracker().registerForDataConnectionDetached(this,
+ DctConstants.EVENT_DATA_CONNECTION_DETACHED, null);
+ mPhone.getServiceStateTracker().registerForRoamingOn(this,
+ DctConstants.EVENT_ROAMING_ON, null);
+ mPhone.getServiceStateTracker().registerForRoamingOff(this,
+ DctConstants.EVENT_ROAMING_OFF, null);
+ mPhone.getServiceStateTracker().registerForPsRestrictedEnabled(this,
+ DctConstants.EVENT_PS_RESTRICT_ENABLED, null);
+ mPhone.getServiceStateTracker().registerForPsRestrictedDisabled(this,
+ DctConstants.EVENT_PS_RESTRICT_DISABLED, null);
+ // SubscriptionManager.registerForDdsSwitch(this,
+ // DctConstants.EVENT_CLEAN_UP_ALL_CONNECTIONS, null);
+ mPhone.getServiceStateTracker().registerForDataRegStateOrRatChanged(this,
+ DctConstants.EVENT_DATA_RAT_CHANGED, null);
+ }
@Override
public void dispose() {
if (DBG) log("GsmDCT.dispose");
@@ -192,11 +220,21 @@
super.dispose();
- //Unregister for all events
+ mPhone.getContext().getContentResolver().unregisterContentObserver(mApnObserver);
+ mApnContexts.clear();
+ mPrioritySortedApnContexts.clear();
+
+ destroyDataConnections();
+ }
+ protected void unregisterForAllEvents() {
+ //Unregister for all events
mPhone.mCi.unregisterForAvailable(this);
mPhone.mCi.unregisterForOffOrNotAvailable(this);
IccRecords r = mIccRecords.get();
- if (r != null) { r.unregisterForRecordsLoaded(this);}
+ if (r != null) {
+ r.unregisterForRecordsLoaded(this);
+ mIccRecords.set(null);
+ }
mPhone.mCi.unregisterForDataNetworkStateChanged(this);
mPhone.getCallTracker().unregisterForVoiceCallEnded(this);
mPhone.getCallTracker().unregisterForVoiceCallStarted(this);
@@ -206,12 +244,7 @@
mPhone.getServiceStateTracker().unregisterForRoamingOff(this);
mPhone.getServiceStateTracker().unregisterForPsRestrictedEnabled(this);
mPhone.getServiceStateTracker().unregisterForPsRestrictedDisabled(this);
-
- mPhone.getContext().getContentResolver().unregisterContentObserver(mApnObserver);
- mApnContexts.clear();
- mPrioritySortedApnContexts.clear();
-
- destroyDataConnections();
+ //SubscriptionManager.unregisterForDdsSwitch(this);
}
private class TelephonyNetworkFactory extends NetworkFactory {
@@ -365,6 +398,25 @@
if(DBG) log("finalize");
}
+ protected void supplyMessenger() {
+ // Supply the data connection tracker messenger only if
+ // this is corresponding to the current DDS.
+ if (!isActiveDataSubscription()) {
+ return;
+ }
+
+ ConnectivityManager cm = (ConnectivityManager)mPhone.getContext().getSystemService(
+ Context.CONNECTIVITY_SERVICE);
+ cm.supplyMessenger(ConnectivityManager.TYPE_MOBILE, new Messenger(this));
+ cm.supplyMessenger(ConnectivityManager.TYPE_MOBILE_MMS, new Messenger(this));
+ cm.supplyMessenger(ConnectivityManager.TYPE_MOBILE_SUPL, new Messenger(this));
+ cm.supplyMessenger(ConnectivityManager.TYPE_MOBILE_DUN, new Messenger(this));
+ cm.supplyMessenger(ConnectivityManager.TYPE_MOBILE_HIPRI, new Messenger(this));
+ cm.supplyMessenger(ConnectivityManager.TYPE_MOBILE_FOTA, new Messenger(this));
+ cm.supplyMessenger(ConnectivityManager.TYPE_MOBILE_IMS, new Messenger(this));
+ cm.supplyMessenger(ConnectivityManager.TYPE_MOBILE_CBS, new Messenger(this));
+ }
+
private ApnContext addApnContext(String type, NetworkConfig networkConfig) {
ApnContext apnContext = new ApnContext(mPhone.getContext(), type, LOG_TAG, networkConfig,
this);
@@ -525,7 +577,15 @@
*/
@Override
public synchronized int enableApnType(String apnType) {
- ApnContext apnContext = mApnContexts.get(apnType);
+ ApnContext apnContext = mApnContexts.get(apnType);
+ if (!isActiveDataSubscription()) {
+ if(apnType.equals(PhoneConstants.APN_TYPE_DEFAULT)) {
+ log("enableApnType(): NOT active DDS, apnContext setEnabled as true for default");
+ apnContext.setEnabled(true);
+ }
+ return PhoneConstants.APN_REQUEST_FAILED;
+ }
+
if (apnContext == null || !isApnTypeAvailable(apnType)) {
if (DBG) log("enableApnType: " + apnType + " is type not available");
return PhoneConstants.APN_TYPE_NOT_AVAILABLE;
@@ -550,6 +610,13 @@
public synchronized int disableApnType(String type) {
if (DBG) log("disableApnType:" + type);
ApnContext apnContext = mApnContexts.get(type);
+ if (!isActiveDataSubscription()) {
+ if (type.equals(PhoneConstants.APN_TYPE_DEFAULT)) {
+ log("disableApnType(): NOT active DDS, apnContext setEnabled as false for default");
+ apnContext.setEnabled(false);
+ }
+ return PhoneConstants.APN_REQUEST_FAILED;
+ }
if (apnContext != null) {
setEnabled(apnTypeToId(type), false);
@@ -606,6 +673,23 @@
}
}
+ public boolean getAnyDataEnabled(boolean checkUserDataEnabled) {
+ synchronized (mDataEnabledLock) {
+ if (!(mInternalDataEnabled && (!checkUserDataEnabled || mUserDataEnabled)
+ && (!checkUserDataEnabled || sPolicyDataEnabled)))
+ return false;
+
+ for (ApnContext apnContext : mApnContexts.values()) {
+ // Make sure we dont have a context that going down
+ // and is explicitly disabled.
+ if (isDataAllowed(apnContext)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
private boolean isDataAllowed(ApnContext apnContext) {
return apnContext.isReady() && isDataAllowed();
}
@@ -655,6 +739,14 @@
IccRecords r = mIccRecords.get();
boolean recordsLoaded = (r != null) ? r.getRecordsLoaded() : false;
+ //FIXME always attach
+ boolean psRestricted = mIsPsRestricted;
+ int phoneNum = TelephonyManager.getDefault().getPhoneCount();
+ if (phoneNum > 1) {
+ attachedState = true;
+ psRestricted = false;
+ }
+
boolean allowed =
(attachedState || mAutoAttachOnCreation) &&
recordsLoaded &&
@@ -662,7 +754,8 @@
mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) &&
internalDataEnabled &&
(!mPhone.getServiceState().getRoaming() || getDataOnRoamingEnabled()) &&
- !mIsPsRestricted &&
+ //!mIsPsRestricted &&
+ !psRestricted &&
desiredPowerState;
if (!allowed && DBG) {
String reason = "";
@@ -720,9 +813,12 @@
}
boolean desiredPowerState = mPhone.getServiceStateTracker().getDesiredPowerState();
+ boolean checkUserDataEnabled =
+ !(apnContext.getApnType().equals(PhoneConstants.APN_TYPE_IMS));
if (apnContext.isConnectable() &&
- isDataAllowed(apnContext) && getAnyDataEnabled() && !isEmergency()) {
+ isDataAllowed(apnContext) &&
+ getAnyDataEnabled(checkUserDataEnabled) && !isEmergency()) {
if (apnContext.getState() == DctConstants.State.FAILED) {
if (DBG) log("trySetupData: make a FAILED ApnContext IDLE so its reusable");
apnContext.setState(DctConstants.State.IDLE);
@@ -798,12 +894,25 @@
protected boolean cleanUpAllConnections(boolean tearDown, String reason) {
if (DBG) log("cleanUpAllConnections: tearDown=" + tearDown + " reason=" + reason);
boolean didDisconnect = false;
+ boolean specificdisable = false;
+
+ if (!TextUtils.isEmpty(reason)) {
+ specificdisable = reason.equals(Phone.REASON_DATA_SPECIFIC_DISABLED);
+ }
for (ApnContext apnContext : mApnContexts.values()) {
if (apnContext.isDisconnected() == false) didDisconnect = true;
- // TODO - only do cleanup if not disconnected
- apnContext.setReason(reason);
- cleanUpConnection(tearDown, apnContext);
+ if (specificdisable) {
+ if (!apnContext.getApnType().equals(PhoneConstants.APN_TYPE_IMS)) {
+ if (DBG) log("ApnConextType: " + apnContext.getApnType());
+ apnContext.setReason(reason);
+ cleanUpConnection(tearDown, apnContext);
+ }
+ } else {
+ // TODO - only do cleanup if not disconnected
+ apnContext.setReason(reason);
+ cleanUpConnection(tearDown, apnContext);
+ }
}
stopNetStatPoll();
@@ -811,6 +920,13 @@
// TODO: Do we need mRequestedApnType?
mRequestedApnType = PhoneConstants.APN_TYPE_DEFAULT;
+
+ log("cleanUpConnection: mDisconnectPendingCount = " + mDisconnectPendingCount);
+ if (tearDown && mDisconnectPendingCount == 0) {
+ notifyDataDisconnectComplete();
+ notifyAllDataDisconnected();
+ }
+
return didDisconnect;
}
@@ -829,7 +945,7 @@
cleanUpAllConnections(true, cause);
}
- private void cleanUpConnection(boolean tearDown, ApnContext apnContext) {
+ protected void cleanUpConnection(boolean tearDown, ApnContext apnContext) {
if (apnContext == null) {
if (DBG) log("cleanUpConnection: apn context is null");
@@ -858,6 +974,8 @@
if (apnContext.getState() != DctConstants.State.DISCONNECTING) {
boolean disconnectAll = false;
if (PhoneConstants.APN_TYPE_DUN.equals(apnContext.getApnType())) {
+ // CAF_MSIM is this below condition required.
+ // if (PhoneConstants.APN_TYPE_DUN.equals(PhoneConstants.APN_TYPE_DEFAULT)) {
ApnSetting dunSetting = fetchDunApn();
if (dunSetting != null &&
dunSetting.equals(apnContext.getApnSetting())) {
@@ -880,6 +998,7 @@
.tearDown(apnContext, apnContext.getReason(), msg);
}
apnContext.setState(DctConstants.State.DISCONNECTING);
+ mDisconnectPendingCount++;
}
} else {
// apn is connected but no reference to dcac.
@@ -1288,6 +1407,10 @@
intent.putExtra(INTENT_RECONNECT_ALARM_EXTRA_REASON, apnContext.getReason());
intent.putExtra(INTENT_RECONNECT_ALARM_EXTRA_TYPE, apnType);
+ // Get current sub id.
+ long subId = SubscriptionManager.getDefaultDataSubId();
+ intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId);
+
if (DBG) {
log("startAlarmForReconnect: delay=" + delay + " action=" + intent.getAction()
+ " apn=" + apnContext);
@@ -1425,8 +1548,10 @@
ApnContext potentialApnCtx = null;
for (ApnContext curApnCtx : mApnContexts.values()) {
DcAsyncChannel curDcac = curApnCtx.getDcAc();
+ log("curDcac: " + curDcac);
if (curDcac != null) {
ApnSetting apnSetting = curApnCtx.getApnSetting();
+ log("apnSetting: " + apnSetting);
if (dunSetting != null) {
if (dunSetting.equals(apnSetting)) {
switch (curApnCtx.getState()) {
@@ -1760,6 +1885,14 @@
if (handleError) {
onDataSetupCompleteError(ar);
}
+
+ /* If flag is set to false after SETUP_DATA_CALL is invoked, we need
+ * to clean data connections.
+ */
+ if (!mInternalDataEnabled) {
+ cleanUpAllConnections(null);
+ }
+
}
/**
@@ -1846,6 +1979,16 @@
// Radio will be turned off. No need to retry data setup
apnContext.setApnSetting(null);
apnContext.setDataConnectionAc(null);
+
+ // Need to notify disconnect as well, in the case of switching Airplane mode.
+ // Otherwise, it would cause 30s delayed to turn on Airplane mode.
+ if (mDisconnectPendingCount > 0)
+ mDisconnectPendingCount--;
+
+ if (mDisconnectPendingCount == 0) {
+ notifyDataDisconnectComplete();
+ notifyAllDataDisconnected();
+ }
return;
}
}
@@ -1875,6 +2018,15 @@
if(DBG) log("onDisconnectDone: not retrying");
}
}
+
+ if (mDisconnectPendingCount > 0)
+ mDisconnectPendingCount--;
+
+ if (mDisconnectPendingCount == 0) {
+ notifyDataDisconnectComplete();
+ notifyAllDataDisconnected();
+ }
+
}
/**
@@ -2218,6 +2370,11 @@
return;
}
+ if (!isActiveDataSubscription()) {
+ loge("Ignore msgs since phone is not the current DDS");
+ return;
+ }
+
switch (msg.what) {
case DctConstants.EVENT_RECORDS_LOADED:
onRecordsLoaded();
@@ -2301,6 +2458,18 @@
super.handleMessage(msg);
}
break;
+ case DctConstants.EVENT_SET_INTERNAL_DATA_ENABLE:
+ boolean enabled = (msg.arg1 == DctConstants.ENABLED) ? true : false;
+ onSetInternalDataEnabled(enabled, (Message) msg.obj);
+ break;
+
+ case DctConstants.EVENT_CLEAN_UP_ALL_CONNECTIONS:
+ Message mCause = obtainMessage(DctConstants.EVENT_CLEAN_UP_ALL_CONNECTIONS, null);
+ if ((msg.obj != null) && (msg.obj instanceof String)) {
+ mCause.obj = msg.obj;
+ }
+ super.handleMessage(mCause);
+ break;
case DctConstants.EVENT_DATA_RAT_CHANGED:
//May new Network allow setupData, so try it here
@@ -2344,13 +2513,18 @@
return cid;
}
+ private IccRecords getUiccRecords(int appFamily) {
+ return mUiccController.getIccRecords(mPhone.getPhoneId(), appFamily);
+ }
+
+
@Override
protected void onUpdateIcc() {
if (mUiccController == null ) {
return;
}
- IccRecords newIccRecords = mUiccController.getIccRecords(UiccController.APP_FAM_3GPP);
+ IccRecords newIccRecords = getUiccRecords(UiccController.APP_FAM_3GPP);
IccRecords r = mIccRecords.get();
if (r != newIccRecords) {
@@ -2368,14 +2542,157 @@
}
}
+ // setAsCurrentDataConnectionTracker
+ public void update() {
+ log("update sub = " + mPhone.getSubId());
+ if (isActiveDataSubscription()) {
+ log("update(): Active DDS, register for all events now!");
+ registerForAllEvents();
+ onUpdateIcc();
+
+ mUserDataEnabled = Settings.Global.getInt(mPhone.getContext().getContentResolver(),
+ Settings.Global.MOBILE_DATA, 1) == 1;
+
+ if (mPhone instanceof CDMALTEPhone) {
+ ((CDMALTEPhone)mPhone).updateCurrentCarrierInProvider();
+ supplyMessenger();
+ } else if (mPhone instanceof GSMPhone) {
+ ((GSMPhone)mPhone).updateCurrentCarrierInProvider();
+ supplyMessenger();
+ } else {
+ log("Phone object is not MultiSim. This should not hit!!!!");
+ }
+
+ } else {
+ unregisterForAllEvents();
+ log("update(): NOT the active DDS, unregister for all events!");
+ }
+ }
+
+ @Override
+ public void cleanUpAllConnections(String cause) {
+ cleanUpAllConnections(cause, null);
+ }
+
+ public void updateRecords() {
+ if (isActiveDataSubscription()) {
+ onUpdateIcc();
+ }
+ }
+
+ public void cleanUpAllConnections(String cause, Message disconnectAllCompleteMsg) {
+ log("cleanUpAllConnections");
+ if (disconnectAllCompleteMsg != null) {
+ mDisconnectAllCompleteMsgList.add(disconnectAllCompleteMsg);
+ }
+
+ Message msg = obtainMessage(DctConstants.EVENT_CLEAN_UP_ALL_CONNECTIONS);
+ msg.obj = cause;
+ sendMessage(msg);
+ }
+
+ protected void notifyDataDisconnectComplete() {
+ log("notifyDataDisconnectComplete");
+ for (Message m: mDisconnectAllCompleteMsgList) {
+ m.sendToTarget();
+ }
+ mDisconnectAllCompleteMsgList.clear();
+ }
+
+
+ protected void notifyAllDataDisconnected() {
+ sEnableFailFastRefCounter = 0;
+ mFailFast = false;
+ mAllDataDisconnectedRegistrants.notifyRegistrants();
+ }
+
+ public void registerForAllDataDisconnected(Handler h, int what, Object obj) {
+ mAllDataDisconnectedRegistrants.addUnique(h, what, obj);
+
+ if (isDisconnected()) {
+ log("notify All Data Disconnected");
+ notifyAllDataDisconnected();
+ }
+ }
+
+ public void unregisterForAllDataDisconnected(Handler h) {
+ mAllDataDisconnectedRegistrants.remove(h);
+ }
+
+
+ @Override
+ protected void onSetInternalDataEnabled(boolean enable) {
+ onSetInternalDataEnabled(enable, null);
+ }
+
+ protected void onSetInternalDataEnabled(boolean enabled, Message onCompleteMsg) {
+ boolean sendOnComplete = true;
+
+ synchronized (mDataEnabledLock) {
+ mInternalDataEnabled = enabled;
+ if (enabled) {
+ log("onSetInternalDataEnabled: changed to enabled, try to setup data call");
+ onTrySetupData(Phone.REASON_DATA_ENABLED);
+ } else {
+ sendOnComplete = false;
+ log("onSetInternalDataEnabled: changed to disabled, cleanUpAllConnections");
+ cleanUpAllConnections(null, onCompleteMsg);
+ }
+ }
+
+ if (sendOnComplete) {
+ if (onCompleteMsg != null) {
+ onCompleteMsg.sendToTarget();
+ }
+ }
+ }
+
+ public boolean setInternalDataEnabledFlag(boolean enable) {
+ if (DBG)
+ log("setInternalDataEnabledFlag(" + enable + ")");
+
+ if (mInternalDataEnabled != enable) {
+ mInternalDataEnabled = enable;
+ }
+ return true;
+ }
+
+ @Override
+ public boolean setInternalDataEnabled(boolean enable) {
+ return setInternalDataEnabled(enable, null);
+ }
+
+ public boolean setInternalDataEnabled(boolean enable, Message onCompleteMsg) {
+ if (DBG)
+ log("setInternalDataEnabled(" + enable + ")");
+
+ Message msg = obtainMessage(DctConstants.EVENT_SET_INTERNAL_DATA_ENABLE, onCompleteMsg);
+ msg.arg1 = (enable ? DctConstants.ENABLED : DctConstants.DISABLED);
+ sendMessage(msg);
+ return true;
+ }
+
+ /** Returns true if this is current DDS. */
+ protected boolean isActiveDataSubscription() {
+ // FIXME This should have code like
+ // return (mPhone.getSubId() == SubscriptionManager.getDefaultDataSubId());
+ return true;
+ }
+
+ public void setDataAllowed(boolean enable, Message response) {
+ mIsCleanupRequired = !enable;
+ mPhone.mCi.setDataAllowed(enable, response);
+ mInternalDataEnabled = enable;
+ }
+
@Override
protected void log(String s) {
- Rlog.d(LOG_TAG, s);
+ Rlog.d(LOG_TAG, "[" + mPhone.getPhoneId() + "]" + s);
}
@Override
protected void loge(String s) {
- Rlog.e(LOG_TAG, s);
+ Rlog.e(LOG_TAG, "[" + mPhone.getPhoneId() + "]" + s);
}
@Override
@@ -2389,4 +2706,42 @@
pw.println(" mDataConnectionAsyncChannels=%s\n" + mDataConnectionAcHashMap);
pw.println(" mAttached=" + mAttached.get());
}
+
+ @Override
+ public String[] getPcscfAddress() {
+ log("getPcscfAddress()");
+
+ ApnContext apnContext = mApnContexts.get(PhoneConstants.APN_TYPE_IMS);
+
+ if (apnContext == null) {
+ log("apnContext is null, return null");
+ return null;
+ }
+
+ DcAsyncChannel dcac = apnContext.getDcAc();
+ String[] result = null;
+
+ if (dcac != null) {
+ result = dcac.getPcscfAddr();
+
+ for (int i = 0; i < result.length; i++) {
+ log("Pcscf[" + i + "]: " + result[i]);
+ }
+ return result;
+ }
+ return null;
+ }
+
+ @Override
+ public void setImsRegistrationState(boolean registered) {
+ log("setImsRegistrationState - mImsRegistrationState(before): "+ mImsRegistrationState
+ + ", registered(current) : " + registered);
+
+ if (mPhone == null) return;
+
+ ServiceStateTracker sst = mPhone.getServiceStateTracker();
+ if (sst == null) return;
+
+ sst.setImsRegistrationState(registered);
+ }
}
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcTrackerBase.java b/src/java/com/android/internal/telephony/dataconnection/DcTrackerBase.java
index b3f6d3f..a4e7c7c 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DcTrackerBase.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DcTrackerBase.java
@@ -43,6 +43,7 @@
import android.preference.PreferenceManager;
import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
+import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.EventLog;
@@ -81,6 +82,7 @@
protected static final boolean VDBG_STALL = true; // STOPSHIP if true
protected static final boolean RADIO_TESTS = false;
+ static boolean mIsCleanupRequired = false;
/**
* Constants for the data connection activity:
* physical link down/up
@@ -469,6 +471,17 @@
String reason = intent.getStringExtra(INTENT_RECONNECT_ALARM_EXTRA_REASON);
String apnType = intent.getStringExtra(INTENT_RECONNECT_ALARM_EXTRA_TYPE);
+ long phoneSubId = mPhone.getSubId();
+ long currSubId = intent.getLongExtra(PhoneConstants.SUBSCRIPTION_KEY,
+ SubscriptionManager.INVALID_SUB_ID);
+ log("onActionIntentReconnectAlarm: currSubId = " + currSubId + " phoneSubId=" + phoneSubId);
+
+ // Stop reconnect if not current subId is not correct.
+ if ((currSubId == SubscriptionManager.INVALID_SUB_ID) || (currSubId != phoneSubId)) {
+ log("receive ReconnectAlarm but subId incorrect, ignore");
+ return;
+ }
+
ApnContext apnContext = mApnContexts.get(apnType);
if (DBG) {
@@ -530,8 +543,8 @@
*/
protected DcTrackerBase(PhoneBase phone) {
super();
- if (DBG) log("DCT.constructor");
mPhone = phone;
+ if (DBG) log("DCT.constructor");
mResolver = mPhone.getContext().getContentResolver();
mUiccController = UiccController.getInstance();
mUiccController.registerForIccChanged(this, DctConstants.EVENT_ICC_CHANGED, null);
@@ -727,6 +740,9 @@
public abstract boolean isDataPossible(String apnType);
protected abstract void onUpdateIcc();
protected abstract void completeConnection(ApnContext apnContext);
+ public abstract void setDataAllowed(boolean enable, Message response);
+ public abstract String[] getPcscfAddress();
+ public abstract void setImsRegistrationState(boolean registered);
@Override
public void handleMessage(Message msg) {
@@ -1345,7 +1361,7 @@
if (!prevEnabled) {
onTrySetupData(Phone.REASON_DATA_ENABLED);
} else {
- onCleanUpAllConnections(Phone.REASON_DATA_DISABLED);
+ onCleanUpAllConnections(Phone.REASON_DATA_SPECIFIC_DISABLED);
}
}
}
@@ -1364,7 +1380,7 @@
if (!prevEnabled) {
onTrySetupData(Phone.REASON_DATA_ENABLED);
} else {
- onCleanUpAllConnections(Phone.REASON_DATA_DISABLED);
+ onCleanUpAllConnections(Phone.REASON_DATA_SPECIFIC_DISABLED);
}
}
}
diff --git a/src/java/com/android/internal/telephony/dataconnection/DctController.java b/src/java/com/android/internal/telephony/dataconnection/DctController.java
new file mode 100644
index 0000000..e5631eb
--- /dev/null
+++ b/src/java/com/android/internal/telephony/dataconnection/DctController.java
@@ -0,0 +1,472 @@
+/*
+ * Copyright (C) 2014 MediaTek Inc.
+ *
+ * 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.dataconnection;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Handler;
+import android.os.Message;
+import android.os.AsyncResult;
+import android.os.SystemProperties;
+import android.telephony.ServiceState;
+import android.telephony.TelephonyManager;
+import android.telephony.SubscriptionManager;
+
+import com.android.internal.telephony.IccCardConstants;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.telephony.PhoneBase;
+import com.android.internal.telephony.PhoneProxy;
+import com.android.internal.telephony.TelephonyIntents;
+import com.android.internal.util.AsyncChannel;
+import com.android.internal.telephony.PhoneFactory;
+import com.android.internal.telephony.TelephonyProperties;
+import com.android.internal.telephony.DefaultPhoneNotifier;
+import com.android.internal.telephony.SubscriptionController;
+
+import android.util.Log;
+import java.util.HashSet;
+import java.util.Iterator;
+import android.os.Registrant;
+import android.os.RegistrantList;
+import android.telephony.Rlog;
+
+public class DctController extends Handler {
+ private static final String LOG_TAG = "DctController";
+ private static final boolean DBG = true;
+
+ private static final int EVENT_PHONE1_DETACH = 1;
+ private static final int EVENT_PHONE2_DETACH = 2;
+ private static final int EVENT_PHONE3_DETACH = 3;
+ private static final int EVENT_PHONE4_DETACH = 4;
+ private static final int EVENT_PHONE1_RADIO_OFF = 5;
+ private static final int EVENT_PHONE2_RADIO_OFF = 6;
+ private static final int EVENT_PHONE3_RADIO_OFF = 7;
+ private static final int EVENT_PHONE4_RADIO_OFF = 8;
+
+ private static final int PHONE_NONE = -1;
+
+ private static DctController sDctController;
+
+ private static final int EVENT_ALL_DATA_DISCONNECTED = 1;
+ private static final int EVENT_SET_DATA_ALLOW_DONE = 2;
+
+ private RegistrantList mNotifyDataSwitchInfo = new RegistrantList();
+ private SubscriptionController mSubController = SubscriptionController.getInstance();
+
+ private Phone mActivePhone;
+ private int mPhoneNum;
+ private boolean[] mServicePowerOffFlag;
+ private PhoneProxy[] mPhones;
+ private DcSwitchState[] mDcSwitchState;
+ private DcSwitchAsyncChannel[] mDcSwitchAsyncChannel;
+ private Handler[] mDcSwitchStateHandler;
+
+ private HashSet<String> mApnTypes = new HashSet<String>();
+
+ private BroadcastReceiver mDataStateReceiver;
+ private Context mContext;
+
+ private int mCurrentDataPhone = PHONE_NONE;
+ private int mRequestedDataPhone = PHONE_NONE;
+
+ private Handler mRspHander = new Handler() {
+ public void handleMessage(Message msg){
+ AsyncResult ar;
+ switch(msg.what) {
+ case EVENT_PHONE1_DETACH:
+ case EVENT_PHONE2_DETACH:
+ case EVENT_PHONE3_DETACH:
+ case EVENT_PHONE4_DETACH:
+ logd("EVENT_PHONE" + msg.what +
+ "_DETACH: mRequestedDataPhone=" + mRequestedDataPhone);
+ mCurrentDataPhone = PHONE_NONE;
+ if (mRequestedDataPhone != PHONE_NONE) {
+ mCurrentDataPhone = mRequestedDataPhone;
+ mRequestedDataPhone = PHONE_NONE;
+
+ Iterator<String> itrType = mApnTypes.iterator();
+ while (itrType.hasNext()) {
+ mDcSwitchAsyncChannel[mCurrentDataPhone].connectSync(itrType.next());
+ }
+ mApnTypes.clear();
+ }
+ break;
+
+ case EVENT_PHONE1_RADIO_OFF:
+ case EVENT_PHONE2_RADIO_OFF:
+ case EVENT_PHONE3_RADIO_OFF:
+ case EVENT_PHONE4_RADIO_OFF:
+ logd("EVENT_PHONE" + (msg.what - EVENT_PHONE1_RADIO_OFF + 1) + "_RADIO_OFF.");
+ mServicePowerOffFlag[msg.what - EVENT_PHONE1_RADIO_OFF] = true;
+ break;
+
+ default:
+ break;
+ }
+ }
+ };
+
+ private DefaultPhoneNotifier.IDataStateChangedCallback mDataStateChangedCallback =
+ new DefaultPhoneNotifier.IDataStateChangedCallback() {
+ public void onDataStateChanged(long subId, String state, String reason,
+ String apnName, String apnType, boolean unavailable) {
+ logd("[DataStateChanged]:" + "state=" + state + ",reason=" + reason
+ + ",apnName=" + apnName + ",apnType=" + apnType + ",from subId=" + subId);
+ int phoneId = SubscriptionManager.getPhoneId(subId);
+ mDcSwitchState[phoneId].notifyDataConnection(phoneId, state, reason,
+ apnName, apnType, unavailable);
+ }
+ };
+
+ private class DataStateReceiver extends BroadcastReceiver {
+ public void onReceive(Context context, Intent intent) {
+ synchronized(this) {
+ if (intent.getAction().equals(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED)) {
+ ServiceState ss = ServiceState.newFromBundle(intent.getExtras());
+
+ long subId = intent.getLongExtra(PhoneConstants.SUBSCRIPTION_KEY, PhoneConstants.SUB1);
+ int phoneId = SubscriptionManager.getPhoneId(subId);
+ logd("DataStateReceiver phoneId= " + phoneId);
+
+ // for the case of network out of service when bootup
+ if (subId == -1 || subId == -2) {
+ logd("Network out of service and return");
+ return;
+ }
+
+ boolean prevPowerOff = mServicePowerOffFlag[phoneId];
+ if (ss != null) {
+ int state = ss.getState();
+ switch (state) {
+ case ServiceState.STATE_POWER_OFF:
+ mServicePowerOffFlag[phoneId] = true;
+ logd("Recv STATE_POWER_OFF Intent from phoneId=" + phoneId);
+ break;
+ case ServiceState.STATE_IN_SERVICE:
+ mServicePowerOffFlag[phoneId] = false;
+ logd("Recv STATE_IN_SERVICE Intent from phoneId=" + phoneId);
+ break;
+ case ServiceState.STATE_OUT_OF_SERVICE:
+ logd("Recv STATE_OUT_OF_SERVICE Intent from phoneId=" + phoneId);
+ if (mServicePowerOffFlag[phoneId]) {
+ mServicePowerOffFlag[phoneId] = false;
+ }
+ break;
+ case ServiceState.STATE_EMERGENCY_ONLY:
+ logd("Recv STATE_EMERGENCY_ONLY Intent from phoneId=" + phoneId);
+ break;
+ default:
+ logd("Recv SERVICE_STATE_CHANGED invalid state");
+ break;
+ }
+
+ if (prevPowerOff && mServicePowerOffFlag[phoneId] == false &&
+ mCurrentDataPhone == PHONE_NONE &&
+ phoneId == getDataConnectionFromSetting()) {
+ logd("Current Phone is none and default Phone is " +
+ phoneId + ", then enableApnType()");
+ enableApnType(subId, PhoneConstants.APN_TYPE_DEFAULT);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ public DefaultPhoneNotifier.IDataStateChangedCallback getDataStateChangedCallback() {
+ return mDataStateChangedCallback;
+ }
+
+ public static DctController getInstance() {
+ if (sDctController == null) {
+ throw new RuntimeException(
+ "DCTrackerController.getInstance can't be called before makeDCTController()");
+ }
+ return sDctController;
+ }
+
+ public static DctController makeDctController(PhoneProxy[] phones) {
+ if (sDctController == null) {
+ sDctController = new DctController(phones);
+ }
+ return sDctController;
+ }
+
+ private DctController(PhoneProxy[] phones) {
+ mPhoneNum = TelephonyManager.getDefault().getPhoneCount();
+ mServicePowerOffFlag = new boolean[mPhoneNum];
+ mPhones = phones;
+
+ mDcSwitchState = new DcSwitchState[mPhoneNum];
+ mDcSwitchAsyncChannel = new DcSwitchAsyncChannel[mPhoneNum];
+ mDcSwitchStateHandler = new Handler[mPhoneNum];
+
+ mActivePhone = mPhones[0];
+
+ for (int i = 0; i < mPhoneNum; ++i) {
+ int phoneId = i;
+ mServicePowerOffFlag[i] = true;
+ mDcSwitchState[i] = new DcSwitchState(mPhones[i], "DcSwitchState-" + phoneId, phoneId);
+ mDcSwitchState[i].start();
+ mDcSwitchAsyncChannel[i] = new DcSwitchAsyncChannel(mDcSwitchState[i], phoneId);
+ mDcSwitchStateHandler[i] = new Handler();
+
+ int status = mDcSwitchAsyncChannel[i].fullyConnectSync(mPhones[i].getContext(),
+ mDcSwitchStateHandler[i], mDcSwitchState[i].getHandler());
+
+ if (status == AsyncChannel.STATUS_SUCCESSFUL) {
+ logd("Connect success: " + i);
+ } else {
+ loge("Could not connect to " + i);
+ }
+
+ mDcSwitchState[i].registerForIdle(mRspHander, EVENT_PHONE1_DETACH + i, null);
+
+ // Register for radio state change
+ PhoneBase phoneBase = (PhoneBase)((PhoneProxy)mPhones[i]).getActivePhone();
+ phoneBase.mCi.registerForOffOrNotAvailable(mRspHander, EVENT_PHONE1_RADIO_OFF + i, null);
+ }
+
+ mContext = mActivePhone.getContext();
+
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(TelephonyIntents.ACTION_DATA_CONNECTION_FAILED);
+ filter.addAction(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED);
+
+ mDataStateReceiver = new DataStateReceiver();
+ Intent intent = mContext.registerReceiver(mDataStateReceiver, filter);
+ }
+
+ private IccCardConstants.State getIccCardState(int phoneId) {
+ return mPhones[phoneId].getIccCard().getState();
+ }
+
+ /**
+ * Enable PDP interface by apn type and phone id
+ *
+ * @param type enable pdp interface by apn type, such as PhoneConstants.APN_TYPE_MMS, etc.
+ * @param subId Indicate which sub to query
+ * @return PhoneConstants.APN_REQUEST_STARTED: action is already started
+ * PhoneConstants.APN_ALREADY_ACTIVE: interface has already active
+ * PhoneConstants.APN_TYPE_NOT_AVAILABLE: invalid APN type
+ * PhoneConstants.APN_REQUEST_FAILED: request failed
+ * PhoneConstants.APN_REQUEST_FAILED_DUE_TO_RADIO_OFF: readio turn off
+ * @see #disableApnType()
+ */
+ public synchronized int enableApnType(long subId, String type) {
+ int phoneId = SubscriptionManager.getPhoneId(subId);
+
+ if (phoneId == PHONE_NONE || !isValidphoneId(phoneId)) {
+ logw("enableApnType(): with PHONE_NONE or Invalid PHONE ID");
+ return PhoneConstants.APN_REQUEST_FAILED;
+ }
+
+ logd("enableApnType():type=" + type + ",phoneId=" + phoneId +
+ ",powerOff=" + mServicePowerOffFlag[phoneId]);
+
+ if (!PhoneConstants.APN_TYPE_DEFAULT.equals(type)) {
+ for (int peerphoneId =0; peerphoneId < mPhoneNum; peerphoneId++) {
+ // check peer Phone has non default APN activated as receiving non default APN request.
+ if (phoneId == peerphoneId) {
+ continue;
+ }
+
+ String[] activeApnTypes = mPhones[peerphoneId].getActiveApnTypes();
+ if (activeApnTypes != null && activeApnTypes.length != 0) {
+ for (int i=0; i<activeApnTypes.length; i++) {
+ if (!PhoneConstants.APN_TYPE_DEFAULT.equals(activeApnTypes[i]) &&
+ mPhones[peerphoneId].getDataConnectionState(activeApnTypes[i]) !=
+ PhoneConstants.DataState.DISCONNECTED) {
+ logd("enableApnType:Peer Phone still have non-default active APN type:"+
+ "activeApnTypes[" + i + "]=" + activeApnTypes[i]);
+ return PhoneConstants.APN_REQUEST_FAILED;
+ }
+ }
+ }
+ }
+ }
+
+ logd("enableApnType(): CurrentDataPhone=" +
+ mCurrentDataPhone + ", RequestedDataPhone=" + mRequestedDataPhone);
+
+ if (phoneId == mCurrentDataPhone &&
+ !mDcSwitchAsyncChannel[mCurrentDataPhone].isIdleOrDeactingSync()) {
+ mRequestedDataPhone = PHONE_NONE;
+ logd("enableApnType(): mRequestedDataPhone equals request PHONE ID.");
+ return mDcSwitchAsyncChannel[phoneId].connectSync(type);
+ } else {
+ // Only can switch data when mCurrentDataPhone is PHONE_NONE,
+ // it is set to PHONE_NONE only as receiving EVENT_PHONEX_DETACH
+ if (mCurrentDataPhone == PHONE_NONE) {
+ mCurrentDataPhone = phoneId;
+ mRequestedDataPhone = PHONE_NONE;
+ logd("enableApnType(): current PHONE is NONE or IDLE, mCurrentDataPhone=" +
+ mCurrentDataPhone);
+ return mDcSwitchAsyncChannel[phoneId].connectSync(type);
+ } else {
+ logd("enableApnType(): current PHONE:" + mCurrentDataPhone + " is active.");
+ if (phoneId != mRequestedDataPhone) {
+ mApnTypes.clear();
+ }
+ mApnTypes.add(type);
+ mRequestedDataPhone = phoneId;
+ mDcSwitchState[mCurrentDataPhone].cleanupAllConnection();
+ }
+ }
+ return PhoneConstants.APN_REQUEST_STARTED;
+ }
+
+ /**
+ * disable PDP interface by apn type and sub id
+ *
+ * @param type enable pdp interface by apn type, such as PhoneConstants.APN_TYPE_MMS, etc.
+ * @param subId Indicate which sub to query
+ * @return PhoneConstants.APN_REQUEST_STARTED: action is already started
+ * PhoneConstants.APN_ALREADY_ACTIVE: interface has already active
+ * PhoneConstants.APN_TYPE_NOT_AVAILABLE: invalid APN type
+ * PhoneConstants.APN_REQUEST_FAILED: request failed
+ * PhoneConstants.APN_REQUEST_FAILED_DUE_TO_RADIO_OFF: readio turn off
+ * @see #enableApnTypeGemini()
+ */
+ public synchronized int disableApnType(long subId, String type) {
+
+ int phoneId = SubscriptionManager.getPhoneId(subId);
+
+ if (phoneId == PHONE_NONE || !isValidphoneId(phoneId)) {
+ logw("disableApnType(): with PHONE_NONE or Invalid PHONE ID");
+ return PhoneConstants.APN_REQUEST_FAILED;
+ }
+ logd("disableApnType():type=" + type + ",phoneId=" + phoneId +
+ ",powerOff=" + mServicePowerOffFlag[phoneId]);
+ return mDcSwitchAsyncChannel[phoneId].disconnectSync(type);
+ }
+
+ public boolean isDataConnectivityPossible(String type, int phoneId) {
+ if (phoneId == PHONE_NONE || !isValidphoneId(phoneId)) {
+ logw("isDataConnectivityPossible(): with PHONE_NONE or Invalid PHONE ID");
+ return false;
+ } else {
+ return mPhones[phoneId].isDataConnectivityPossible(type);
+ }
+ }
+
+ public boolean isIdleOrDeacting(int phoneId) {
+ if (mDcSwitchAsyncChannel[phoneId].isIdleOrDeactingSync()) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ private boolean isValidphoneId(int phoneId) {
+ return phoneId >= 0 && phoneId <= mPhoneNum;
+ }
+
+ private boolean isValidApnType(String apnType) {
+ if (apnType.equals(PhoneConstants.APN_TYPE_DEFAULT)
+ || apnType.equals(PhoneConstants.APN_TYPE_MMS)
+ || apnType.equals(PhoneConstants.APN_TYPE_SUPL)
+ || apnType.equals(PhoneConstants.APN_TYPE_DUN)
+ || apnType.equals(PhoneConstants.APN_TYPE_HIPRI)
+ || apnType.equals(PhoneConstants.APN_TYPE_FOTA)
+ || apnType.equals(PhoneConstants.APN_TYPE_IMS)
+ || apnType.equals(PhoneConstants.APN_TYPE_CBS))
+ {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ private int getDataConnectionFromSetting(){
+ long [] subId = SubscriptionManager.getSubId(PhoneConstants.SIM_ID_1);
+ int phoneId = SubscriptionManager.getPhoneId(subId[0]);
+ return phoneId;
+ }
+
+ private static void logv(String s) {
+ Log.v(LOG_TAG, "[DctController] " + s);
+ }
+
+ private static void logd(String s) {
+ Log.d(LOG_TAG, "[DctController] " + s);
+ }
+
+ private static void logw(String s) {
+ Log.w(LOG_TAG, "[DctController] " + s);
+ }
+
+ private static void loge(String s) {
+ Log.e(LOG_TAG, "[DctController] " + s);
+ }
+
+
+ public void setDataSubId(long subId) {
+ //FIXME This should rework
+ //FIXME Need to have a StateMachine logic to handle this api considering various clients
+ Rlog.d(LOG_TAG, "setDataAllowed subId :" + subId);
+ int phoneId = mSubController.getPhoneId(subId);
+ int prefPhoneId = mSubController.getPhoneId(mSubController.getDefaultDataSubId());
+ Phone phone = mPhones[prefPhoneId].getActivePhone();
+ DcTrackerBase dcTracker =((PhoneBase)phone).mDcTracker;
+ dcTracker.setDataAllowed(false, null);
+ mPhones[prefPhoneId].registerForAllDataDisconnected(
+ this, EVENT_ALL_DATA_DISCONNECTED, new Integer(phoneId));
+
+ }
+
+ public void registerForDataSwitchInfo(Handler h, int what, Object obj) {
+ //FIXME This should rework
+ Registrant r = new Registrant (h, what, obj);
+ synchronized (mNotifyDataSwitchInfo) {
+ mNotifyDataSwitchInfo.add(r);
+ }
+ }
+
+ @Override
+ public void handleMessage (Message msg) {
+ //FIXME This should rework
+ AsyncResult ar = (AsyncResult)msg.obj;
+ Rlog.d(LOG_TAG, "handleMessage msg=" + msg);
+
+ switch (msg.what) {
+ case EVENT_ALL_DATA_DISCONNECTED:
+ Integer phoneId = (Integer)ar.userObj;
+ int prefPhoneId = mSubController.getPhoneId(
+ mSubController.getDefaultDataSubId());
+ Rlog.d(LOG_TAG, "EVENT_ALL_DATA_DISCONNECTED phoneId :" + phoneId);
+ mPhones[prefPhoneId].unregisterForAllDataDisconnected(this);
+ Message alllowedDataDone = Message.obtain(this, EVENT_SET_DATA_ALLOW_DONE,
+ new Integer(phoneId));
+ Phone phone = mPhones[phoneId].getActivePhone();
+ DcTrackerBase dcTracker =((PhoneBase)phone).mDcTracker;
+ dcTracker.setDataAllowed(true, alllowedDataDone);
+ break;
+
+ case EVENT_SET_DATA_ALLOW_DONE:
+ phoneId = (Integer)ar.userObj;
+ long[] subId = mSubController.getSubId(phoneId);
+ Rlog.d(LOG_TAG, "EVENT_SET_DATA_ALLOWED_DONE phoneId :" + subId[0]);
+ mNotifyDataSwitchInfo.notifyRegistrants(new AsyncResult(null, subId[0], null));
+ mPhones[phoneId].updateDataConnectionTracker();
+ break;
+ }
+ }
+}
diff --git a/src/java/com/android/internal/telephony/gsm/GSMPhone.java b/src/java/com/android/internal/telephony/gsm/GSMPhone.java
index b169936..24015b2 100644
--- a/src/java/com/android/internal/telephony/gsm/GSMPhone.java
+++ b/src/java/com/android/internal/telephony/gsm/GSMPhone.java
@@ -32,6 +32,8 @@
import android.telephony.CellLocation;
import android.telephony.PhoneNumberUtils;
import android.telephony.ServiceState;
+import android.telephony.TelephonyManager;
+import android.telephony.VoLteServiceState;
import com.android.internal.telephony.CallTracker;
import android.text.TextUtils;
import android.telephony.Rlog;
@@ -51,6 +53,7 @@
import com.android.internal.telephony.SmsBroadcastUndelivered;
import com.android.internal.telephony.dataconnection.DcTracker;
+import com.android.internal.telephony.Call;
import com.android.internal.telephony.CallForwardInfo;
import com.android.internal.telephony.CallStateException;
import com.android.internal.telephony.CommandsInterface;
@@ -61,18 +64,25 @@
import com.android.internal.telephony.OperatorInfo;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneBase;
+import com.android.internal.telephony.PhoneFactory;
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.PhoneNotifier;
import com.android.internal.telephony.PhoneProxy;
import com.android.internal.telephony.PhoneSubInfo;
+import com.android.internal.telephony.Subscription;
+import android.telephony.SubscriptionManager;
import com.android.internal.telephony.TelephonyProperties;
import com.android.internal.telephony.UUSInfo;
+import com.android.internal.telephony.VoicePhone;
import com.android.internal.telephony.test.SimulatedRadioControl;
import com.android.internal.telephony.uicc.IccRecords;
import com.android.internal.telephony.uicc.IccVmNotSupportedException;
import com.android.internal.telephony.uicc.UiccCardApplication;
import com.android.internal.telephony.uicc.UiccController;
import com.android.internal.telephony.ServiceStateTracker;
+import com.android.internal.telephony.uicc.IsimRecords;
+import com.android.internal.telephony.uicc.IsimUiccRecords;
+
import java.io.FileDescriptor;
import java.io.IOException;
@@ -83,6 +93,10 @@
import java.util.ArrayList;
import java.util.List;
+import static com.android.internal.telephony.PhoneConstants.EVENT_SUBSCRIPTION_ACTIVATED;
+import static com.android.internal.telephony.PhoneConstants.EVENT_SUBSCRIPTION_DEACTIVATED;
+
+
/**
* {@hide}
*/
@@ -118,6 +132,8 @@
private String mImeiSv;
private String mVmNumber;
+ private IsimUiccRecords mIsimUiccRecords;
+
// Create Cfu (Call forward unconditional) so that dialling number &
// mOnComplete (Message object passed by client) can be packed &
// given as a single Cfu object as user data to RIL.
@@ -148,9 +164,45 @@
mCi.setPhoneType(PhoneConstants.PHONE_TYPE_GSM);
mCT = new GsmCallTracker(this);
- mSST = new GsmServiceStateTracker (this);
+ mSST = new GsmServiceStateTracker(this);
mDcTracker = new DcTracker(this);
+
+ if (!unitTestMode) {
+ mSimPhoneBookIntManager = new SimPhoneBookInterfaceManager(this);
+ mSubInfo = new PhoneSubInfo(this);
+ }
+
+ mCi.registerForAvailable(this, EVENT_RADIO_AVAILABLE, null);
+ mCi.registerForOffOrNotAvailable(this, EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null);
+ mCi.registerForOn(this, EVENT_RADIO_ON, null);
+ mCi.setOnUSSD(this, EVENT_USSD, null);
+ mCi.setOnSuppServiceNotification(this, EVENT_SSN, null);
+ mCi.registerForSrvccStateChanged(this, EVENT_SRVCC_STATE_CHANGED, null);
+ mSST.registerForNetworkAttached(this, EVENT_REGISTERED_TO_NETWORK, null);
+ setProperties();
+ }
+
+ public
+ GSMPhone (Context context, CommandsInterface ci, PhoneNotifier notifier, int phoneId) {
+ this(context, ci, notifier, false, phoneId);
+ }
+
+ public
+ GSMPhone (Context context, CommandsInterface ci,
+ PhoneNotifier notifier, boolean unitTestMode, int phoneId) {
+ super("GSM", notifier, context, ci, unitTestMode, phoneId);
+
+ if (ci instanceof SimulatedRadioControl) {
+ mSimulatedRadioControl = (SimulatedRadioControl) ci;
+ }
+
+ mCi.setPhoneType(PhoneConstants.PHONE_TYPE_GSM);
+ mCT = new GsmCallTracker(this);
+
+ mSST = new GsmServiceStateTracker(this);
+ mDcTracker = new DcTracker(this);
+
if (!unitTestMode) {
mSimPhoneBookIntManager = new SimPhoneBookInterfaceManager(this);
mSubInfo = new PhoneSubInfo(this);
@@ -162,10 +214,16 @@
mCi.setOnUSSD(this, EVENT_USSD, null);
mCi.setOnSuppServiceNotification(this, EVENT_SSN, null);
mSST.registerForNetworkAttached(this, EVENT_REGISTERED_TO_NETWORK, null);
+ setProperties();
- //Change the system property
- SystemProperties.set(TelephonyProperties.CURRENT_ACTIVE_PHONE,
- new Integer(PhoneConstants.PHONE_TYPE_GSM).toString());
+ log("GSMPhone: constructor: sub = " + mPhoneId);
+
+ setProperties();
+ }
+
+ protected void setProperties() {
+ TelephonyManager.setTelephonyProperty(TelephonyProperties.CURRENT_ACTIVE_PHONE,
+ getSubId(), new Integer(PhoneConstants.PHONE_TYPE_GSM).toString());
}
@Override
@@ -181,6 +239,7 @@
mSST.unregisterForNetworkAttached(this); //EVENT_REGISTERED_TO_NETWORK
mCi.unSetOnUSSD(this);
mCi.unSetOnSuppServiceNotification(this);
+ mCi.unregisterForSrvccStateChanged(this);
mPendingMMIs.clear();
@@ -201,6 +260,7 @@
mSubInfo = null;
mCT = null;
mSST = null;
+
super.removeReferences();
}
@@ -210,6 +270,28 @@
}
+ private void onSubscriptionActivated() {
+ //mSubscriptionData = SubscriptionManager.getCurrentSubscription(mSubscription);
+
+ log("SUBSCRIPTION ACTIVATED : slotId : " + mSubscriptionData.slotId
+ + " appid : " + mSubscriptionData.m3gppIndex
+ + " subId : " + mSubscriptionData.subId
+ + " subStatus : " + mSubscriptionData.subStatus);
+
+ // Make sure properties are set for proper subscription.
+ setProperties();
+
+ onUpdateIccAvailability();
+ mSST.sendMessage(mSST.obtainMessage(ServiceStateTracker.EVENT_ICC_CHANGED));
+ ((DcTracker)mDcTracker).updateRecords();
+ }
+
+ private void onSubscriptionDeactivated() {
+ log("SUBSCRIPTION DEACTIVATED");
+ mSubscriptionData = null;
+ resetSubSpecifics();
+ }
+
@Override
public ServiceState
getServiceState() {
@@ -391,9 +473,9 @@
* {@inheritDoc}
*/
@Override
- public final void
+ public void
setSystemProperty(String property, String value) {
- super.setSystemProperty(property, value);
+ TelephonyManager.setTelephonyProperty(property, getSubId(), value);
}
@Override
@@ -410,6 +492,16 @@
}
@Override
+ public void registerForSimRecordsLoaded(Handler h, int what, Object obj) {
+ mSimRecordsLoadedRegistrants.addUnique(h, what, obj);
+ }
+
+ @Override
+ public void unregisterForSimRecordsLoaded(Handler h) {
+ mSimRecordsLoadedRegistrants.remove(h);
+ }
+
+ @Override
public void
acceptCall() throws CallStateException {
mCT.acceptCall();
@@ -627,7 +719,12 @@
}
@Override
- public boolean handleInCallMmiCommands(String dialString) {
+ public boolean handleInCallMmiCommands(String dialString) throws CallStateException {
+ if (mVoicePhone != null
+ && mVoicePhone.getServiceState().getState() == ServiceState.STATE_IN_SERVICE) {
+ return mVoicePhone.handleInCallMmiCommands(dialString);
+ }
+
if (!isInCall()) {
return false;
}
@@ -685,6 +782,26 @@
@Override
public Connection
dial (String dialString, UUSInfo uusInfo) throws CallStateException {
+ if (mVoicePhone != null
+ && mVoicePhone.getServiceState().getState() == ServiceState.STATE_IN_SERVICE
+ && !PhoneNumberUtils.isEmergencyNumber(dialString)) {
+ try {
+ return mVoicePhone.dial(dialString);
+ } catch (CallStateException e) {
+ if (!VoicePhone.CS_FALLBACK.equals(e.getMessage())) {
+ CallStateException ce = new CallStateException(e.getMessage());
+ ce.setStackTrace(e.getStackTrace());
+ throw ce;
+ }
+ }
+ }
+
+ return dialInternal(dialString, null);
+ }
+
+ @Override
+ protected Connection
+ dialInternal (String dialString, UUSInfo uusInfo) throws CallStateException {
// Need to make sure dialString gets parsed properly
String newDialString = PhoneNumberUtils.stripSeparators(dialString);
@@ -780,7 +897,7 @@
private void storeVoiceMailNumber(String number) {
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext());
SharedPreferences.Editor editor = sp.edit();
- editor.putString(VM_NUMBER, number);
+ editor.putString(VM_NUMBER + getPhoneId(), number);
editor.apply();
setVmSimImsi(getSubscriberId());
}
@@ -792,20 +909,20 @@
String number = (r != null) ? r.getVoiceMailNumber() : "";
if (TextUtils.isEmpty(number)) {
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext());
- number = sp.getString(VM_NUMBER, null);
+ number = sp.getString(VM_NUMBER + getPhoneId(), null);
}
return number;
}
private String getVmSimImsi() {
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext());
- return sp.getString(VM_SIM_IMSI, null);
+ return sp.getString(VM_SIM_IMSI + getPhoneId(), null);
}
private void setVmSimImsi(String imsi) {
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext());
SharedPreferences.Editor editor = sp.edit();
- editor.putString(VM_SIM_IMSI, imsi);
+ editor.putString(VM_SIM_IMSI + getPhoneId(), imsi);
editor.apply();
}
@@ -835,6 +952,11 @@
}
@Override
+ public IsimRecords getIsimRecords() {
+ return mIsimUiccRecords;
+ }
+
+ @Override
public String getImei() {
return mImei;
}
@@ -917,6 +1039,13 @@
}
}
+ public String getSystemProperty(String property, String defValue) {
+ if(getUnitTestMode()) {
+ return null;
+ }
+ return TelephonyManager.getTelephonyProperty(property, getSubId(), defValue);
+ }
+
private boolean isValidCommandInterfaceCFAction (int commandInterfaceCFAction) {
switch (commandInterfaceCFAction) {
case CF_ACTION_DISABLE:
@@ -929,12 +1058,22 @@
}
}
+ public void updateDataConnectionTracker() {
+ ((DcTracker)mDcTracker).update();
+ }
+
protected boolean isCfEnable(int action) {
return (action == CF_ACTION_ENABLE) || (action == CF_ACTION_REGISTRATION);
}
@Override
public void getCallForwardingOption(int commandInterfaceCFReason, Message onComplete) {
+ if ((mVoicePhone != null)
+ && (mVoicePhone.getServiceState().getState() == ServiceState.STATE_IN_SERVICE)) {
+ mVoicePhone.getCallForwardingOption(commandInterfaceCFReason, onComplete);
+ return;
+ }
+
if (isValidCommandInterfaceCFReason(commandInterfaceCFReason)) {
if (LOCAL_DEBUG) Rlog.d(LOG_TAG, "requesting call forwarding query.");
Message resp;
@@ -953,6 +1092,13 @@
String dialingNumber,
int timerSeconds,
Message onComplete) {
+ if ((mVoicePhone != null)
+ && (mVoicePhone.getServiceState().getState() == ServiceState.STATE_IN_SERVICE)) {
+ mVoicePhone.setCallForwardingOption(commandInterfaceCFAction,
+ commandInterfaceCFReason, dialingNumber, timerSeconds, onComplete);
+ return;
+ }
+
if ( (isValidCommandInterfaceCFAction(commandInterfaceCFAction)) &&
(isValidCommandInterfaceCFReason(commandInterfaceCFReason))) {
@@ -987,6 +1133,12 @@
@Override
public void getCallWaiting(Message onComplete) {
+ if ((mVoicePhone != null)
+ && (mVoicePhone.getServiceState().getState() == ServiceState.STATE_IN_SERVICE)) {
+ mVoicePhone.getCallWaiting(onComplete);
+ return;
+ }
+
//As per 3GPP TS 24.083, section 1.6 UE doesn't need to send service
//class parameter in call waiting interrogation to network
mCi.queryCallWaiting(CommandsInterface.SERVICE_CLASS_NONE, onComplete);
@@ -994,6 +1146,12 @@
@Override
public void setCallWaiting(boolean enable, Message onComplete) {
+ if ((mVoicePhone != null)
+ && (mVoicePhone.getServiceState().getState() == ServiceState.STATE_IN_SERVICE)) {
+ mVoicePhone.setCallWaiting(enable, onComplete);
+ return;
+ }
+
mCi.setCallWaiting(enable, CommandsInterface.SERVICE_CLASS_VOICE, onComplete);
}
@@ -1143,12 +1301,45 @@
*/
protected void syncClirSetting() {
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext());
- int clirSetting = sp.getInt(CLIR_KEY, -1);
+ int clirSetting = sp.getInt(CLIR_KEY + getPhoneId(), -1);
if (clirSetting >= 0) {
mCi.setCLIR(clirSetting, null);
}
}
+ private void handleSrvccStateChanged(int[] ret) {
+ Rlog.d(LOG_TAG, "handleSrvccStateChanged");
+
+ Connection conn = null;
+ Call.SrvccState srvccState = Call.SrvccState.NONE;
+ if (ret != null && ret.length != 0) {
+ int state = ret[0];
+ switch(state) {
+ case VoLteServiceState.HANDOVER_STARTED:
+ srvccState = Call.SrvccState.STARTED;
+ conn = ((VoicePhone)mVoicePhone).getHandoverConnection();
+ break;
+ case VoLteServiceState.HANDOVER_COMPLETED:
+ srvccState = Call.SrvccState.COMPLETED;
+ ((VoicePhone) mVoicePhone).notifySrvccState(srvccState);
+ break;
+ case VoLteServiceState.HANDOVER_FAILED:
+ case VoLteServiceState.HANDOVER_CANCELED:
+ srvccState = Call.SrvccState.FAILED;
+ break;
+
+ default:
+ //ignore invalid state
+ return;
+ }
+
+ mCT.notifySrvccState(srvccState, conn);
+
+ VoLteServiceState lteState = new VoLteServiceState(state);
+ notifyVoLteServiceStateChanged(lteState);
+ }
+ }
+
@Override
public void handleMessage (Message msg) {
AsyncResult ar;
@@ -1170,6 +1361,9 @@
break;
case EVENT_RADIO_ON:
+ if (mVoicePhone != null) {
+ mVoicePhone.getServiceState().setStateOutOfService();
+ }
break;
case EVENT_REGISTERED_TO_NETWORK:
@@ -1188,6 +1382,7 @@
setVmSimImsi(null);
}
+ mSimRecordsLoadedRegistrants.notifyRegistrants();
break;
case EVENT_GET_BASEBAND_VERSION_DONE:
@@ -1246,6 +1441,9 @@
mPendingMMIs.get(i).onUssdFinishedError();
}
}
+ if (mVoicePhone != null) {
+ mVoicePhone.getServiceState().setStateOff();
+ }
break;
case EVENT_SSN:
@@ -1322,11 +1520,33 @@
}
break;
+ case EVENT_SUBSCRIPTION_ACTIVATED:
+ log("EVENT_SUBSCRIPTION_ACTIVATED");
+ onSubscriptionActivated();
+ break;
+
+ case EVENT_SUBSCRIPTION_DEACTIVATED:
+ log("EVENT_SUBSCRIPTION_DEACTIVATED");
+ onSubscriptionDeactivated();
+ break;
+
+ case EVENT_SRVCC_STATE_CHANGED:
+ ar = (AsyncResult)msg.obj;
+ if (ar.exception == null) {
+ handleSrvccStateChanged((int[]) ar.result);
+ }
+ break;
+
default:
super.handleMessage(msg);
}
}
+ protected UiccCardApplication getUiccCardApplication() {
+ return ((UiccController) mUiccController).getUiccCardApplication(mPhoneId,
+ UiccController.APP_FAM_3GPP);
+ }
+
@Override
protected void onUpdateIccAvailability() {
if (mUiccController == null ) {
@@ -1334,7 +1554,16 @@
}
UiccCardApplication newUiccApplication =
- mUiccController.getUiccCardApplication(UiccController.APP_FAM_3GPP);
+ mUiccController.getUiccCardApplication(mPhoneId, UiccController.APP_FAM_IMS);
+ IsimUiccRecords newIsimUiccRecords = null;
+
+ if (newUiccApplication != null) {
+ newIsimUiccRecords = (IsimUiccRecords)newUiccApplication.getIccRecords();
+ if (LOCAL_DEBUG) log("New ISIM application found");
+ }
+ mIsimUiccRecords = newIsimUiccRecords;
+
+ newUiccApplication = getUiccCardApplication();
UiccCardApplication app = mUiccApplication.get();
if (app != newUiccApplication) {
@@ -1374,12 +1603,17 @@
* @return true for success; false otherwise.
*/
public boolean updateCurrentCarrierInProvider() {
- IccRecords r = mIccRecords.get();
- if (r != null) {
+ long currentDds = SubscriptionManager.getDefaultDataSubId();
+ String operatorNumeric = getOperatorNumeric();
+
+ log("updateCurrentCarrierInProvider: mSubId = " + getSubId()
+ + " currentDds = " + currentDds + " operatorNumeric = " + operatorNumeric);
+
+ if (!TextUtils.isEmpty(operatorNumeric) && (getSubId() == currentDds)) {
try {
Uri uri = Uri.withAppendedPath(Telephony.Carriers.CONTENT_URI, "current");
ContentValues map = new ContentValues();
- map.put(Telephony.Carriers.NUMERIC, r.getOperatorNumeric());
+ map.put(Telephony.Carriers.NUMERIC, operatorNumeric);
mContext.getContentResolver().insert(uri, map);
return true;
} catch (SQLException e) {
@@ -1397,7 +1631,7 @@
// open the shared preferences editor, and write the value.
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext());
SharedPreferences.Editor editor = sp.edit();
- editor.putInt(CLIR_KEY, commandInterfaceCLIRMode);
+ editor.putInt(CLIR_KEY + getPhoneId(), commandInterfaceCLIRMode);
// commit and log the result.
if (! editor.commit()) {
@@ -1516,6 +1750,40 @@
pw.println(" mVmNumber=" + mVmNumber);
}
+ /**
+ * @return operator numeric.
+ */
+ public String getOperatorNumeric() {
+ String operatorNumeric = null;
+ IccRecords r = mIccRecords.get();
+ if (r != null) {
+ operatorNumeric = r.getOperatorNumeric();
+ }
+ return operatorNumeric;
+ }
+
+ public void registerForAllDataDisconnected(Handler h, int what, Object obj) {
+ ((DcTracker)mDcTracker)
+ .registerForAllDataDisconnected(h, what, obj);
+ }
+
+ public void unregisterForAllDataDisconnected(Handler h) {
+ ((DcTracker)mDcTracker).unregisterForAllDataDisconnected(h);
+ }
+
+ public void setInternalDataEnabled(boolean enable, Message onCompleteMsg) {
+ ((DcTracker)mDcTracker)
+ .setInternalDataEnabled(enable, onCompleteMsg);
+ }
+
+
+ public boolean setInternalDataEnabledFlag(boolean enable) {
+ return ((DcTracker)mDcTracker)
+ .setInternalDataEnabledFlag(enable);
+ }
+
+ public void resetSubSpecifics() {
+ }
protected void log(String s) {
Rlog.d(LOG_TAG, "[GSMPhone] " + s);
}
diff --git a/src/java/com/android/internal/telephony/gsm/GsmCallTracker.java b/src/java/com/android/internal/telephony/gsm/GsmCallTracker.java
index 4a2566d..516520b 100644
--- a/src/java/com/android/internal/telephony/gsm/GsmCallTracker.java
+++ b/src/java/com/android/internal/telephony/gsm/GsmCallTracker.java
@@ -30,6 +30,7 @@
import android.util.EventLog;
import android.telephony.Rlog;
+import com.android.internal.telephony.Call;
import com.android.internal.telephony.CallStateException;
import com.android.internal.telephony.CallTracker;
import com.android.internal.telephony.CommandsInterface;
@@ -88,6 +89,8 @@
PhoneConstants.State mState = PhoneConstants.State.IDLE;
+ Call.SrvccState mSrvccState = Call.SrvccState.NONE;
+ Connection mHandoverConnection;
//***** Events
@@ -504,6 +507,10 @@
// it's a ringing call
if (mConnections[i].getCall() == mRingingCall) {
newRinging = mConnections[i];
+ } else if (mHandoverConnection != null) {
+ // Single Radio Voice Call Continuity (SRVCC) completed
+ mConnections[i].migrateFrom(mHandoverConnection);
+ mHandoverConnection = null;
} else {
// Something strange happened: a call appeared
// which is neither a ringing call or one we created.
@@ -849,6 +856,15 @@
return Phone.SuppService.UNKNOWN;
}
+ /* package */
+ void notifySrvccState(Call.SrvccState state, Connection c) {
+ if (state == Call.SrvccState.STARTED) {
+ mHandoverConnection = c;
+ } else if (state != Call.SrvccState.COMPLETED) {
+ mHandoverConnection = null;
+ }
+ }
+
//****** Overridden from Handler
@Override
diff --git a/src/java/com/android/internal/telephony/gsm/GsmConnection.java b/src/java/com/android/internal/telephony/gsm/GsmConnection.java
index 9f6af51..c78d442 100644
--- a/src/java/com/android/internal/telephony/gsm/GsmConnection.java
+++ b/src/java/com/android/internal/telephony/gsm/GsmConnection.java
@@ -81,6 +81,8 @@
UUSInfo mUusInfo;
int mPreciseCause = 0;
+ Connection mOrigConnection;
+
Handler mHandler;
private PowerManager.WakeLock mPartialWakeLock;
@@ -186,6 +188,11 @@
// and therefore don't need to compare the phone number anyway.
if (! (mIsIncoming || c.isMT)) return true;
+ // A new call appearing by SRVCC may have invalid number
+ // if IMS service is not tightly coupled with cellular modem stack.
+ // Thus we prefer the preexisting handover connection instance.
+ if (mOrigConnection != null) return true;
+
// ... but we can compare phone numbers on MT calls, and we have
// no control over when they begin, so we might as well
@@ -214,6 +221,11 @@
}
@Override
+ public long getConnectTimeReal() {
+ return mConnectTimeReal;
+ }
+
+ @Override
public long getDisconnectTime() {
return mDisconnectTime;
}
@@ -230,6 +242,11 @@
}
@Override
+ public long getHoldingStartTime() {
+ return mHoldingStartTime;
+ }
+
+ @Override
public long getHoldDurationMillis() {
if (getState() != GsmCall.State.HOLDING) {
// If not holding, return 0
@@ -375,9 +392,7 @@
default:
GSMPhone phone = mOwner.mPhone;
int serviceState = phone.getServiceState().getState();
- UiccCardApplication cardApp = UiccController
- .getInstance()
- .getUiccCardApplication(UiccController.APP_FAM_3GPP);
+ UiccCardApplication cardApp = phone.getUiccCardApplication();
AppState uiccAppState = (cardApp != null) ? cardApp.getState() :
AppState.APPSTATE_UNKNOWN;
if (serviceState == ServiceState.STATE_POWER_OFF) {
@@ -436,6 +451,8 @@
if (mParent != null) {
changed = mParent.connectionDisconnected(this);
}
+
+ mOrigConnection = null;
}
clearPostDialListeners();
releaseWakeLock();
@@ -452,10 +469,15 @@
newParent = parentFromDCState(dc.state);
- if (!equalsHandlesNulls(mAddress, dc.number)) {
- if (Phone.DEBUG_PHONE) log("update: phone # changed!");
- mAddress = dc.number;
- changed = true;
+ //Ignore dc.number and dc.name in case of a handover connection
+ if (mOrigConnection != null) {
+ if (Phone.DEBUG_PHONE) log("update: mOrigConnection is not null");
+ } else {
+ if (!equalsHandlesNulls(mAddress, dc.number)) {
+ if (Phone.DEBUG_PHONE) log("update: phone # changed!");
+ mAddress = dc.number;
+ changed = true;
+ }
}
// A null cnapName should be the same as ""
@@ -775,4 +797,45 @@
public int getPreciseDisconnectCause() {
return mPreciseCause;
}
+
+ /* package */ void
+ migrateFrom(Connection c) {
+ if (c == null) return;
+
+ this.mAddress = c.getAddress();
+ this.mNumberPresentation = c.getNumberPresentation();
+
+ this.mDialString = c.getOrigDialString();
+
+ this.mCnapName = c.getCnapName();
+ this.mCnapNamePresentation = c.getCnapNamePresentation();
+
+ this.mIsIncoming = c.isIncoming();
+
+ this.mCreateTime = c.getCreateTime();
+ this.mConnectTime = c.getConnectTime();
+ this.mConnectTimeReal = c.getConnectTimeReal();
+
+ this.mHoldingStartTime = c.getHoldingStartTime();
+
+ this.mUusInfo = c.getUUSInfo();
+
+ this.setUserData(c.getUserData());
+
+ this.mOrigConnection = c;
+ }
+
+ @Override
+ public Connection getOrigConnection() {
+ return mOrigConnection;
+ }
+
+ @Override
+ public boolean isMultiparty() {
+ if (mOrigConnection != null) {
+ return mOrigConnection.isMultiparty();
+ }
+
+ return false;
+ }
}
diff --git a/src/java/com/android/internal/telephony/gsm/GsmMmiCode.java b/src/java/com/android/internal/telephony/gsm/GsmMmiCode.java
index 3e1b1e8..377cc85 100644
--- a/src/java/com/android/internal/telephony/gsm/GsmMmiCode.java
+++ b/src/java/com/android/internal/telephony/gsm/GsmMmiCode.java
@@ -432,6 +432,11 @@
return mMessage;
}
+ public Phone
+ getPhone() {
+ return ((Phone) mPhone);
+ }
+
// inherited javadoc suffices
@Override
public void
diff --git a/src/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java b/src/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
index 345abba..b732d44 100644
--- a/src/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
+++ b/src/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
@@ -25,6 +25,7 @@
import android.provider.Telephony.Sms;
import android.provider.Telephony.Sms.Intents;
import android.telephony.Rlog;
+import android.telephony.TelephonyManager;
import com.android.internal.telephony.GsmAlphabet;
import com.android.internal.telephony.ImsSMSDispatcher;
@@ -35,6 +36,7 @@
import com.android.internal.telephony.SmsHeader;
import com.android.internal.telephony.SmsStorageMonitor;
import com.android.internal.telephony.SmsUsageMonitor;
+import android.telephony.SubscriptionManager;
import com.android.internal.telephony.TelephonyProperties;
import com.android.internal.telephony.uicc.IccRecords;
import com.android.internal.telephony.uicc.IccUtils;
@@ -264,7 +266,10 @@
}
protected UiccCardApplication getUiccCardApplication() {
- return mUiccController.getUiccCardApplication(UiccController.APP_FAM_3GPP);
+ Rlog.d(TAG, "GsmSMSDispatcher: subId = " + mPhone.getSubId()
+ + " slotId = " + mPhone.getPhoneId());
+ return mUiccController.getUiccCardApplication(mPhone.getPhoneId(),
+ UiccController.APP_FAM_3GPP);
}
private void onUpdateIccAvailability() {
diff --git a/src/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java b/src/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
index 5d0d8fb..f69942f 100644
--- a/src/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
+++ b/src/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
@@ -49,6 +49,7 @@
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
import android.telephony.gsm.GsmCellLocation;
+import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.EventLog;
import android.util.TimeUtils;
@@ -57,11 +58,14 @@
import com.android.internal.telephony.CommandsInterface;
import com.android.internal.telephony.EventLogTags;
import com.android.internal.telephony.MccTable;
+import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.telephony.ProxyController;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneFactory;
import com.android.internal.telephony.RILConstants;
import com.android.internal.telephony.RestrictedState;
import com.android.internal.telephony.ServiceStateTracker;
+import android.telephony.SubscriptionManager;
import com.android.internal.telephony.TelephonyIntents;
import com.android.internal.telephony.TelephonyProperties;
import com.android.internal.telephony.dataconnection.DcTrackerBase;
@@ -84,10 +88,11 @@
* {@hide}
*/
final class GsmServiceStateTracker extends ServiceStateTracker {
- private static final String LOG_TAG = "GsmSST";
- private static final boolean VDBG = false;
-
- GSMPhone mPhone;
+ static final String LOG_TAG = "GsmSST";
+ static final boolean VDBG = false;
+ //CAF_MSIM make it private ??
+ private static final int EVENT_ALL_DATA_DISCONNECTED = 1001;
+ private GSMPhone mPhone;
GsmCellLocation mCellLoc;
GsmCellLocation mNewCellLoc;
int mPreferredNetworkType;
@@ -178,6 +183,10 @@
if (intent.getAction().equals(Intent.ACTION_LOCALE_CHANGED)) {
// update emergency string whenever locale changed
updateSpnDisplay();
+ } else if (intent.getAction().equals(ACTION_RADIO_OFF)) {
+ mAlarmSwitch = false;
+ DcTrackerBase dcTracker = mPhone.mDcTracker;
+ powerOffRadioSafely(dcTracker);
}
}
};
@@ -237,6 +246,11 @@
filter.addAction(Intent.ACTION_LOCALE_CHANGED);
phone.getContext().registerReceiver(mIntentReceiver, filter);
+ filter = new IntentFilter();
+ Context context = phone.getContext();
+ filter.addAction(ACTION_RADIO_OFF);
+ context.registerReceiver(mIntentReceiver, filter);
+
// Gsm doesn't support OTASP so its not needed
phone.notifyOtaspChanged(OTASP_NOT_NEEDED);
}
@@ -466,6 +480,26 @@
onRestrictedStateChanged(ar);
break;
+ case EVENT_ALL_DATA_DISCONNECTED:
+ long dds = SubscriptionManager.getDefaultDataSubId();
+ ProxyController.getInstance().unregisterForAllDataDisconnected(dds, this);
+ synchronized(this) {
+ if (mPendingRadioPowerOffAfterDataOff) {
+ if (DBG) log("EVENT_ALL_DATA_DISCONNECTED, turn radio off now.");
+ hangupAndPowerOff();
+ mPendingRadioPowerOffAfterDataOff = false;
+ } else {
+ log("EVENT_ALL_DATA_DISCONNECTED is stale");
+ }
+ }
+ break;
+
+ case EVENT_CHANGE_IMS_STATE:
+ if (DBG) log("EVENT_CHANGE_IMS_STATE:");
+
+ setPowerStateToDesired();
+ break;
+
default:
super.handleMessage(msg);
break;
@@ -474,15 +508,50 @@
@Override
protected void setPowerStateToDesired() {
+
+ if (DBG) {
+ log("mDesiredPowerState = " + mDesiredPowerState);
+ log("getRadioState = " + mCi.getRadioState());
+ log("mPowerOffDelayNeed = " + mPowerOffDelayNeed);
+ log("mAlarmSwitch = " + mAlarmSwitch);
+ }
+
+ if (mAlarmSwitch) {
+ if(DBG) log("mAlarmSwitch == true");
+ Context context = mPhone.getContext();
+ AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
+ am.cancel(mRadioOffIntent);
+ mAlarmSwitch = false;
+ }
+
// If we want it on and it's off, turn it on
if (mDesiredPowerState
- && mCi.getRadioState() == CommandsInterface.RadioState.RADIO_OFF) {
+ && mCi.getRadioState() == CommandsInterface.RadioState.RADIO_OFF) {
mCi.setRadioPower(true, null);
} else if (!mDesiredPowerState && mCi.getRadioState().isOn()) {
// If it's on and available and we want it off gracefully
- DcTrackerBase dcTracker = mPhone.mDcTracker;
- powerOffRadioSafely(dcTracker);
- } // Otherwise, we're in the desired state
+ if (mPowerOffDelayNeed) {
+ if (mImsRegistrationOnOff && !mAlarmSwitch) {
+ if(DBG) log("mImsRegistrationOnOff == true");
+ Context context = mPhone.getContext();
+ AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
+
+ Intent intent = new Intent(ACTION_RADIO_OFF);
+ mRadioOffIntent = PendingIntent.getBroadcast(context, 0, intent, 0);
+
+ mAlarmSwitch = true;
+ if (DBG) log("Alarm setting");
+ am.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+ SystemClock.elapsedRealtime() + 3000, mRadioOffIntent);
+ } else {
+ DcTrackerBase dcTracker = mPhone.mDcTracker;
+ powerOffRadioSafely(dcTracker);
+ }
+ } else {
+ DcTrackerBase dcTracker = mPhone.mDcTracker;
+ powerOffRadioSafely(dcTracker);
+ }
+ }
}
@Override
@@ -572,6 +641,7 @@
intent.putExtra(TelephonyIntents.EXTRA_SPN, spn);
intent.putExtra(TelephonyIntents.EXTRA_SHOW_PLMN, showPlmn);
intent.putExtra(TelephonyIntents.EXTRA_PLMN, plmn);
+ SubscriptionManager.putPhoneIdAndSubIdExtra(intent, mPhone.getPhoneId());
mPhone.getContext().sendStickyBroadcastAsUser(intent, UserHandle.ALL);
}
@@ -1318,7 +1388,7 @@
* @return true if same operator
*/
private boolean isSameNamedOperators(ServiceState s) {
- String spn = SystemProperties.get(TelephonyProperties.PROPERTY_ICC_OPERATOR_ALPHA, "empty");
+ String spn = getSystemProperty(TelephonyProperties.PROPERTY_ICC_OPERATOR_ALPHA, "empty");
String onsl = s.getOperatorAlphaLong();
String onss = s.getOperatorAlphaShort();
@@ -1336,7 +1406,7 @@
* @return true if both are same
*/
private boolean currentMccEqualsSimMcc(ServiceState s) {
- String simNumeric = SystemProperties.get(
+ String simNumeric = getSystemProperty(
TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC, "");
String operatorNumeric = s.getOperatorNumeric();
boolean equalsMcc = true;
@@ -1549,7 +1619,7 @@
zone = TimeZone.getTimeZone( tzname );
}
- String iso = SystemProperties.get(TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY);
+ String iso = getSystemProperty(TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY, "");
if (zone == null) {
@@ -1785,7 +1855,6 @@
mNotification.tickerText = title;
mNotification.setLatestEventInfo(context, title, details,
mNotification.contentIntent);
- mNotification.visibility = Notification.VISIBILITY_PUBLIC;
NotificationManager notificationManager = (NotificationManager)
context.getSystemService(Context.NOTIFICATION_SERVICE);
@@ -1799,14 +1868,18 @@
}
}
+ private UiccCardApplication getUiccCardApplication() {
+ return mUiccController.getUiccCardApplication(mPhone.getPhoneId(),
+ UiccController.APP_FAM_3GPP);
+ }
+
@Override
protected void onUpdateIccAvailability() {
if (mUiccController == null ) {
return;
}
- UiccCardApplication newUiccApplication =
- mUiccController.getUiccCardApplication(UiccController.APP_FAM_3GPP);
+ UiccCardApplication newUiccApplication = getUiccCardApplication();
if (mUiccApplcation != newUiccApplication) {
if (mUiccApplcation != null) {
@@ -1874,4 +1947,70 @@
pw.println(" mCurPlmn=" + mCurPlmn);
pw.println(" mCurShowPlmn=" + mCurShowPlmn);
}
+
+
+ /**
+ * Clean up existing voice and data connection then turn off radio power.
+ *
+ * Hang up the existing voice calls to decrease call drop rate.
+ */
+ @Override
+ public void powerOffRadioSafely(DcTrackerBase dcTracker) {
+ synchronized (this) {
+ if (!mPendingRadioPowerOffAfterDataOff) {
+ long dds = SubscriptionManager.getDefaultDataSubId();
+ // To minimize race conditions we call cleanUpAllConnections on
+ // both if else paths instead of before this isDisconnected test.
+ if (dcTracker.isDisconnected()
+ && (dds == mPhone.getSubId()
+ || (dds != mPhone.getSubId()
+ && ProxyController.getInstance().isDataDisconnected(dds)))) {
+ // To minimize race conditions we do this after isDisconnected
+ dcTracker.cleanUpAllConnections(Phone.REASON_RADIO_TURNED_OFF);
+ if (DBG) log("Data disconnected, turn off radio right away.");
+ hangupAndPowerOff();
+ } else {
+ dcTracker.cleanUpAllConnections(Phone.REASON_RADIO_TURNED_OFF);
+ if (dds != mPhone.getSubId()
+ && !ProxyController.getInstance().isDataDisconnected(dds)) {
+ if (DBG) log("Data is active on DDS. Wait for all data disconnect");
+ // Data is not disconnected on DDS. Wait for the data disconnect complete
+ // before sending the RADIO_POWER off.
+ ProxyController.getInstance().registerForAllDataDisconnected(dds, this,
+ EVENT_ALL_DATA_DISCONNECTED, null);
+ mPendingRadioPowerOffAfterDataOff = true;
+ }
+ Message msg = Message.obtain(this);
+ msg.what = EVENT_SET_RADIO_POWER_OFF;
+ msg.arg1 = ++mPendingRadioPowerOffAfterDataOffTag;
+ if (sendMessageDelayed(msg, 30000)) {
+ if (DBG) log("Wait upto 30s for data to disconnect, then turn off radio.");
+ mPendingRadioPowerOffAfterDataOff = true;
+ } else {
+ log("Cannot send delayed Msg, turn off radio right away.");
+ hangupAndPowerOff();
+ mPendingRadioPowerOffAfterDataOff = false;
+ }
+ }
+ }
+ }
+
+ }
+
+ public void setImsRegistrationState(boolean registered){
+ if (mImsRegistrationOnOff && !registered) {
+ if (mAlarmSwitch) {
+ mImsRegistrationOnOff = registered;
+
+ Context context = mPhone.getContext();
+ AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
+ am.cancel(mRadioOffIntent);
+ mAlarmSwitch = false;
+
+ sendMessage(obtainMessage(EVENT_CHANGE_IMS_STATE));
+ return;
+ }
+ }
+ mImsRegistrationOnOff = registered;
+ }
}
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhone.java b/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
new file mode 100644
index 0000000..5833393
--- /dev/null
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
@@ -0,0 +1,1033 @@
+/*
+ * Copyright (C) 2013 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.imsphone;
+
+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.telephony.PhoneNumberUtils;
+import android.telephony.ServiceState;
+import android.telephony.Rlog;
+import android.text.TextUtils;
+
+import com.android.ims.ImsCallForwardInfo;
+import com.android.ims.ImsException;
+import com.android.ims.ImsReasonInfo;
+import com.android.ims.ImsSsInfo;
+import com.android.ims.ImsUtInterface;
+
+import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BAOC;
+import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BAOIC;
+import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BAOICxH;
+import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BAIC;
+import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BAICr;
+import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BA_ALL;
+import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BA_MO;
+import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BA_MT;
+
+import static com.android.internal.telephony.CommandsInterface.CF_ACTION_DISABLE;
+import static com.android.internal.telephony.CommandsInterface.CF_ACTION_ENABLE;
+import static com.android.internal.telephony.CommandsInterface.CF_ACTION_ERASURE;
+import static com.android.internal.telephony.CommandsInterface.CF_ACTION_REGISTRATION;
+import static com.android.internal.telephony.CommandsInterface.CF_REASON_ALL;
+import static com.android.internal.telephony.CommandsInterface.CF_REASON_ALL_CONDITIONAL;
+import static com.android.internal.telephony.CommandsInterface.CF_REASON_NO_REPLY;
+import static com.android.internal.telephony.CommandsInterface.CF_REASON_NOT_REACHABLE;
+import static com.android.internal.telephony.CommandsInterface.CF_REASON_BUSY;
+import static com.android.internal.telephony.CommandsInterface.CF_REASON_UNCONDITIONAL;
+import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_VOICE;
+import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_NONE;
+
+import com.android.internal.telephony.Call;
+import com.android.internal.telephony.CallForwardInfo;
+import com.android.internal.telephony.CallStateException;
+import com.android.internal.telephony.CallTracker;
+import com.android.internal.telephony.CommandException;
+import com.android.internal.telephony.CommandsInterface;
+import com.android.internal.telephony.Connection;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneBase;
+import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.telephony.PhoneNotifier;
+import com.android.internal.telephony.Subscription;
+import com.android.internal.telephony.VoicePhone;
+import com.android.internal.telephony.uicc.IccRecords;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * {@hide}
+ */
+public class ImsPhone extends ImsPhoneBase implements VoicePhone {
+ private static final String LOG_TAG = "ImsPhone";
+ private static final boolean DBG = true;
+ private static final boolean VDBG = false; // STOPSHIP if true
+
+ protected static final int EVENT_SET_CALL_BARRING_DONE = EVENT_LAST + 1;
+ protected static final int EVENT_GET_CALL_BARRING_DONE = EVENT_LAST + 2;
+ protected static final int EVENT_SET_CALL_WAITING_DONE = EVENT_LAST + 3;
+ protected static final int EVENT_GET_CALL_WAITING_DONE = EVENT_LAST + 4;
+
+ // Instance Variables
+ PhoneBase mDefaultPhone;
+ ImsPhoneCallTracker mCT;
+ ArrayList <ImsPhoneMmiCode> mPendingMMIs = new ArrayList<ImsPhoneMmiCode>();
+
+ Registrant mPostDialHandler;
+ ServiceState mSS = new ServiceState();
+
+ // To redial silently through GSM or CDMA when dialing through IMS fails
+ private String mLastDialString;
+
+ private final RegistrantList mSilentRedialRegistrants
+ = new RegistrantList();
+
+ // Create Cf (Call forward) so that dialling number &
+ // mIsCfu (true if reason is call forward unconditional)
+ // mOnComplete (Message object passed by client) can be packed &
+ // given as a single Cf object as user data to UtInterface.
+ private static class Cf {
+ final String mSetCfNumber;
+ final Message mOnComplete;
+ final boolean mIsCfu;
+
+ Cf(String cfNumber, boolean isCfu, Message onComplete) {
+ mSetCfNumber = cfNumber;
+ mIsCfu = isCfu;
+ mOnComplete = onComplete;
+ }
+ }
+
+ // Constructors
+
+ ImsPhone(Context context, PhoneNotifier notifier, Phone defaultPhone) {
+ super("ImsPhone", context, notifier);
+
+ mDefaultPhone = (PhoneBase) defaultPhone;
+ mCT = new ImsPhoneCallTracker(this);
+ mSS.setStateOff();
+
+ mPhoneId = mDefaultPhone.getPhoneId();
+ }
+
+ @Override
+ public void dispose() {
+ Rlog.d(LOG_TAG, "dispose");
+ // Nothing to dispose in PhoneBase
+ //super.dispose();
+ mPendingMMIs.clear();
+ mCT.dispose();
+
+ //Force all referenced classes to unregister their former registered events
+ }
+
+ @Override
+ public void removeReferences() {
+ Rlog.d(LOG_TAG, "removeReferences");
+ super.removeReferences();
+
+ mCT = null;
+ mSS = null;
+ }
+
+ @Override
+ public ServiceState
+ getServiceState() {
+ return mSS;
+ }
+
+ /* package */ void setServiceState(int state) {
+ mSS.setState(state);
+ }
+
+ @Override
+ public CallTracker getCallTracker() {
+ return mCT;
+ }
+
+ @Override
+ public List<? extends ImsPhoneMmiCode>
+ getPendingMmiCodes() {
+ return mPendingMMIs;
+ }
+
+
+ @Override
+ public void
+ acceptCall() throws CallStateException {
+ mCT.acceptCall();
+ }
+
+ @Override
+ public void
+ rejectCall() throws CallStateException {
+ mCT.rejectCall();
+ }
+
+ @Override
+ public void
+ switchHoldingAndActive() throws CallStateException {
+ mCT.switchWaitingOrHoldingAndActive();
+ }
+
+ @Override
+ public boolean canConference() {
+ return mCT.canConference();
+ }
+
+ public boolean canDial() {
+ return mCT.canDial();
+ }
+
+ @Override
+ public void conference() {
+ mCT.conference();
+ }
+
+ @Override
+ public void clearDisconnected() {
+ mCT.clearDisconnected();
+ }
+
+ @Override
+ public boolean canTransfer() {
+ return mCT.canTransfer();
+ }
+
+ @Override
+ public void explicitCallTransfer() {
+ mCT.explicitCallTransfer();
+ }
+
+ @Override
+ public ImsPhoneCall
+ getForegroundCall() {
+ return mCT.mForegroundCall;
+ }
+
+ @Override
+ public ImsPhoneCall
+ getBackgroundCall() {
+ return mCT.mBackgroundCall;
+ }
+
+ @Override
+ public ImsPhoneCall
+ getRingingCall() {
+ return mCT.mRingingCall;
+ }
+
+ private boolean handleCallDeflectionIncallSupplementaryService(
+ String dialString) {
+ if (dialString.length() > 1) {
+ return false;
+ }
+
+ if (getRingingCall().getState() != ImsPhoneCall.State.IDLE) {
+ if (DBG) Rlog.d(LOG_TAG, "MmiCode 0: rejectCall");
+ try {
+ mCT.rejectCall();
+ } catch (CallStateException e) {
+ if (DBG) Rlog.d(LOG_TAG, "reject failed", e);
+ notifySuppServiceFailed(Phone.SuppService.REJECT);
+ }
+ } else if (getBackgroundCall().getState() != ImsPhoneCall.State.IDLE) {
+ if (DBG) Rlog.d(LOG_TAG, "MmiCode 0: hangupWaitingOrBackground");
+ try {
+ mCT.hangup(getBackgroundCall());
+ } catch (CallStateException e) {
+ if (DBG) Rlog.d(LOG_TAG, "hangup failed", e);
+ }
+ }
+
+ return true;
+ }
+
+
+ private boolean handleCallWaitingIncallSupplementaryService(
+ String dialString) {
+ int len = dialString.length();
+
+ if (len > 2) {
+ return false;
+ }
+
+ ImsPhoneCall call = getForegroundCall();
+
+ try {
+ if (len > 1) {
+ if (DBG) Rlog.d(LOG_TAG, "not support 1X SEND");
+ notifySuppServiceFailed(Phone.SuppService.HANGUP);
+ } else {
+ if (call.getState() != ImsPhoneCall.State.IDLE) {
+ if (DBG) Rlog.d(LOG_TAG, "MmiCode 1: hangup foreground");
+ mCT.hangup(call);
+ } else {
+ if (DBG) Rlog.d(LOG_TAG, "MmiCode 1: switchWaitingOrHoldingAndActive");
+ mCT.switchWaitingOrHoldingAndActive();
+ }
+ }
+ } catch (CallStateException e) {
+ if (DBG) Rlog.d(LOG_TAG, "hangup failed", e);
+ notifySuppServiceFailed(Phone.SuppService.HANGUP);
+ }
+
+ return true;
+ }
+
+ private boolean handleCallHoldIncallSupplementaryService(String dialString) {
+ int len = dialString.length();
+
+ if (len > 2) {
+ return false;
+ }
+
+ ImsPhoneCall call = getForegroundCall();
+
+ if (len > 1) {
+ if (DBG) Rlog.d(LOG_TAG, "separate not supported");
+ notifySuppServiceFailed(Phone.SuppService.SEPARATE);
+ } else {
+ try {
+ if (getRingingCall().getState() != ImsPhoneCall.State.IDLE) {
+ if (DBG) Rlog.d(LOG_TAG, "MmiCode 2: accept ringing call");
+ mCT.acceptCall();
+ } else {
+ if (DBG) Rlog.d(LOG_TAG, "MmiCode 2: switchWaitingOrHoldingAndActive");
+ mCT.switchWaitingOrHoldingAndActive();
+ }
+ } catch (CallStateException e) {
+ if (DBG) Rlog.d(LOG_TAG, "switch failed", e);
+ notifySuppServiceFailed(Phone.SuppService.SWITCH);
+ }
+ }
+
+ return true;
+ }
+
+ private boolean handleMultipartyIncallSupplementaryService(
+ String dialString) {
+ if (dialString.length() > 1) {
+ return false;
+ }
+
+ if (DBG) Rlog.d(LOG_TAG, "MmiCode 3: merge calls");
+ conference();
+ return true;
+ }
+
+ private boolean handleEctIncallSupplementaryService(String dialString) {
+
+ int len = dialString.length();
+
+ if (len != 1) {
+ return false;
+ }
+
+ if (DBG) Rlog.d(LOG_TAG, "MmiCode 4: not support explicit call transfer");
+ notifySuppServiceFailed(Phone.SuppService.TRANSFER);
+ return true;
+ }
+
+ private boolean handleCcbsIncallSupplementaryService(String dialString) {
+ if (dialString.length() > 1) {
+ return false;
+ }
+
+ Rlog.i(LOG_TAG, "MmiCode 5: CCBS not supported!");
+ // Treat it as an "unknown" service.
+ notifySuppServiceFailed(Phone.SuppService.UNKNOWN);
+ return true;
+ }
+
+ @Override
+ public boolean handleInCallMmiCommands(String dialString) {
+ if (!isInCall()) {
+ return false;
+ }
+
+ if (TextUtils.isEmpty(dialString)) {
+ return false;
+ }
+
+ boolean result = false;
+ char ch = dialString.charAt(0);
+ switch (ch) {
+ case '0':
+ result = handleCallDeflectionIncallSupplementaryService(
+ dialString);
+ break;
+ case '1':
+ result = handleCallWaitingIncallSupplementaryService(
+ dialString);
+ break;
+ case '2':
+ result = handleCallHoldIncallSupplementaryService(dialString);
+ break;
+ case '3':
+ result = handleMultipartyIncallSupplementaryService(dialString);
+ break;
+ case '4':
+ result = handleEctIncallSupplementaryService(dialString);
+ break;
+ case '5':
+ result = handleCcbsIncallSupplementaryService(dialString);
+ break;
+ default:
+ break;
+ }
+
+ return result;
+ }
+
+ boolean isInCall() {
+ ImsPhoneCall.State foregroundCallState = getForegroundCall().getState();
+ ImsPhoneCall.State backgroundCallState = getBackgroundCall().getState();
+ ImsPhoneCall.State ringingCallState = getRingingCall().getState();
+
+ return (foregroundCallState.isAlive() ||
+ backgroundCallState.isAlive() ||
+ ringingCallState.isAlive());
+ }
+
+ @Override
+ public Connection
+ dial(String dialString) throws CallStateException {
+ // Need to make sure dialString gets parsed properly
+ String newDialString = PhoneNumberUtils.stripSeparators(dialString);
+
+ // handle in-call MMI first if applicable
+ if (handleInCallMmiCommands(newDialString)) {
+ return null;
+ }
+
+ if (mDefaultPhone.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA) {
+ return mCT.dial(dialString);
+ }
+
+ // Only look at the Network portion for mmi
+ String networkPortion = PhoneNumberUtils.extractNetworkPortionAlt(newDialString);
+ ImsPhoneMmiCode mmi =
+ ImsPhoneMmiCode.newFromDialString(networkPortion, this);
+ if (DBG) Rlog.d(LOG_TAG,
+ "dialing w/ mmi '" + mmi + "'...");
+
+ if (mmi == null) {
+ return mCT.dial(dialString);
+ } else if (mmi.isTemporaryModeCLIR()) {
+ return mCT.dial(mmi.getDialingNumber(), mmi.getCLIRMode());
+ } else if (!mmi.isSupportedOverImsPhone()) {
+ // If the mmi is not supported by IMS service,
+ // try to initiate dialing with default phone
+ throw new CallStateException(VoicePhone.CS_FALLBACK);
+ } else {
+ mPendingMMIs.add(mmi);
+ mMmiRegistrants.notifyRegistrants(new AsyncResult(null, mmi, null));
+ mmi.processCode();
+
+ return null;
+ }
+ }
+
+
+ @Override
+ public void
+ sendDtmf(char c) {
+ if (!PhoneNumberUtils.is12Key(c)) {
+ Rlog.e(LOG_TAG,
+ "sendDtmf called with invalid character '" + c + "'");
+ } else {
+ if (mCT.mState == PhoneConstants.State.OFFHOOK) {
+ mCT.sendDtmf(c);
+ }
+ }
+ }
+
+ @Override
+ public void
+ startDtmf(char c) {
+ if (!PhoneNumberUtils.is12Key(c)) {
+ Rlog.e(LOG_TAG,
+ "startDtmf called with invalid character '" + c + "'");
+ } else {
+ sendDtmf(c);
+ }
+ }
+
+ @Override
+ public void
+ stopDtmf() {
+ // no op
+ }
+
+ @Override
+ public void setOnPostDialCharacter(Handler h, int what, Object obj) {
+ mPostDialHandler = new Registrant(h, what, obj);
+ }
+
+ /*package*/ void notifyIncomingRing() {
+ if (DBG) Rlog.d(LOG_TAG, "notifyIncomingRing");
+ AsyncResult ar = new AsyncResult(null, null, null);
+ sendMessage(obtainMessage(EVENT_CALL_RING, ar));
+ }
+
+ @Override
+ public void setMute(boolean muted) {
+ mCT.setMute(muted);
+ }
+
+ @Override
+ public boolean getMute() {
+ return mCT.getMute();
+ }
+
+ @Override
+ public PhoneConstants.State getState() {
+ return mCT.mState;
+ }
+
+ private boolean isValidCommandInterfaceCFReason (int commandInterfaceCFReason) {
+ switch (commandInterfaceCFReason) {
+ case CF_REASON_UNCONDITIONAL:
+ case CF_REASON_BUSY:
+ case CF_REASON_NO_REPLY:
+ case CF_REASON_NOT_REACHABLE:
+ case CF_REASON_ALL:
+ case CF_REASON_ALL_CONDITIONAL:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ private boolean isValidCommandInterfaceCFAction (int commandInterfaceCFAction) {
+ switch (commandInterfaceCFAction) {
+ case CF_ACTION_DISABLE:
+ case CF_ACTION_ENABLE:
+ case CF_ACTION_REGISTRATION:
+ case CF_ACTION_ERASURE:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ private boolean isCfEnable(int action) {
+ return (action == CF_ACTION_ENABLE) || (action == CF_ACTION_REGISTRATION);
+ }
+
+ private int getConditionFromCFReason(int reason) {
+ switch(reason) {
+ case CF_REASON_UNCONDITIONAL: return ImsUtInterface.CDIV_CF_UNCONDITIONAL;
+ case CF_REASON_BUSY: return ImsUtInterface.CDIV_CF_BUSY;
+ case CF_REASON_NO_REPLY: return ImsUtInterface.CDIV_CF_NO_REPLY;
+ case CF_REASON_NOT_REACHABLE: return ImsUtInterface.CDIV_CF_NOT_REACHABLE;
+ default:
+ break;
+ }
+
+ return ImsUtInterface.INVALID;
+ }
+
+ private int getCFReasonFromCondition(int condition) {
+ switch(condition) {
+ case ImsUtInterface.CDIV_CF_UNCONDITIONAL: return CF_REASON_UNCONDITIONAL;
+ case ImsUtInterface.CDIV_CF_BUSY: return CF_REASON_BUSY;
+ case ImsUtInterface.CDIV_CF_NO_REPLY: return CF_REASON_NO_REPLY;
+ case ImsUtInterface.CDIV_CF_NOT_REACHABLE: return CF_REASON_NOT_REACHABLE;
+ default:
+ break;
+ }
+
+ return CF_REASON_NOT_REACHABLE;
+ }
+
+ private int getActionFromCFAction(int action) {
+ switch(action) {
+ case CF_ACTION_DISABLE: return ImsUtInterface.ACTION_DEACTIVATION;
+ case CF_ACTION_ENABLE: return ImsUtInterface.ACTION_ACTIVATION;
+ case CF_ACTION_ERASURE: return ImsUtInterface.ACTION_ERASURE;
+ case CF_ACTION_REGISTRATION: return ImsUtInterface.ACTION_REGISTRATION;
+ default:
+ break;
+ }
+
+ return ImsUtInterface.INVALID;
+ }
+
+ @Override
+ public void getCallForwardingOption(int commandInterfaceCFReason,
+ Message onComplete) {
+ if (DBG) Rlog.d(LOG_TAG, "getCallForwardingOption reason=" + commandInterfaceCFReason);
+ if (isValidCommandInterfaceCFReason(commandInterfaceCFReason)) {
+ if (DBG) Rlog.d(LOG_TAG, "requesting call forwarding query.");
+ Message resp;
+ resp = obtainMessage(EVENT_GET_CALL_FORWARD_DONE, onComplete);
+
+ try {
+ ImsUtInterface ut = mCT.getUtInterface();
+ ut.queryCallForward(getConditionFromCFReason(commandInterfaceCFReason),null,resp);
+ } catch (ImsException e) {
+ sendErrorResponse(onComplete, e);
+ }
+ } else if (onComplete != null) {
+ sendErrorResponse(onComplete);
+ }
+ }
+
+ @Override
+ public void setCallForwardingOption(int commandInterfaceCFAction,
+ int commandInterfaceCFReason,
+ String dialingNumber,
+ int timerSeconds,
+ Message onComplete) {
+ if (DBG) Rlog.d(LOG_TAG, "setCallForwardingOption action=" + commandInterfaceCFAction
+ + ", reason=" + commandInterfaceCFReason);
+ if ((isValidCommandInterfaceCFAction(commandInterfaceCFAction)) &&
+ (isValidCommandInterfaceCFReason(commandInterfaceCFReason))) {
+ Message resp;
+ Cf cf = new Cf(dialingNumber,
+ (commandInterfaceCFReason == CF_REASON_UNCONDITIONAL ? true : false),
+ onComplete);
+ resp = obtainMessage(EVENT_SET_CALL_FORWARD_DONE,
+ isCfEnable(commandInterfaceCFAction) ? 1 : 0, 0, cf);
+
+ try {
+ ImsUtInterface ut = mCT.getUtInterface();
+ ut.updateCallForward(getActionFromCFAction(commandInterfaceCFAction),
+ getConditionFromCFReason(commandInterfaceCFReason),
+ dialingNumber,
+ timerSeconds,
+ onComplete);
+ } catch (ImsException e) {
+ sendErrorResponse(onComplete, e);
+ }
+ } else if (onComplete != null) {
+ sendErrorResponse(onComplete);
+ }
+ }
+
+ @Override
+ public void getCallWaiting(Message onComplete) {
+ if (DBG) Rlog.d(LOG_TAG, "getCallWaiting");
+ Message resp;
+ resp = obtainMessage(EVENT_GET_CALL_WAITING_DONE, onComplete);
+
+ try {
+ ImsUtInterface ut = mCT.getUtInterface();
+ ut.queryCallWaiting(resp);
+ } catch (ImsException e) {
+ sendErrorResponse(onComplete, e);
+ }
+ }
+
+ @Override
+ public void setCallWaiting(boolean enable, Message onComplete) {
+ if (DBG) Rlog.d(LOG_TAG, "setCallWaiting enable=" + enable);
+ Message resp;
+ resp = obtainMessage(EVENT_SET_CALL_WAITING_DONE, onComplete);
+
+ try {
+ ImsUtInterface ut = mCT.getUtInterface();
+ ut.updateCallWaiting(enable, resp);
+ } catch (ImsException e) {
+ sendErrorResponse(onComplete, e);
+ }
+ }
+
+ private int getCBTypeFromFacility(String facility) {
+ if (CB_FACILITY_BAOC.equals(facility)) {
+ return ImsUtInterface.CB_BAOC;
+ } else if (CB_FACILITY_BAOIC.equals(facility)) {
+ return ImsUtInterface.CB_BOIC;
+ } else if (CB_FACILITY_BAOICxH.equals(facility)) {
+ return ImsUtInterface.CB_BOIC_EXHC;
+ } else if (CB_FACILITY_BAIC.equals(facility)) {
+ return ImsUtInterface.CB_BAIC;
+ } else if (CB_FACILITY_BAICr.equals(facility)) {
+ return ImsUtInterface.CB_BIC_WR;
+ } else if (CB_FACILITY_BA_ALL.equals(facility)) {
+ return 0;
+ } else if (CB_FACILITY_BA_MO.equals(facility)) {
+ return 0;
+ } else if (CB_FACILITY_BA_MT.equals(facility)) {
+ return 0;
+ }
+
+ return 0;
+ }
+
+ /* package */
+ void getCallBarring(String facility, Message onComplete) {
+ if (DBG) Rlog.d(LOG_TAG, "getCallBarring facility=" + facility);
+ Message resp;
+ resp = obtainMessage(EVENT_GET_CALL_BARRING_DONE, onComplete);
+
+ try {
+ ImsUtInterface ut = mCT.getUtInterface();
+ ut.queryCallBarring(getCBTypeFromFacility(facility), resp);
+ } catch (ImsException e) {
+ sendErrorResponse(onComplete, e);
+ }
+ }
+
+ /* package */
+ void setCallBarring(String facility, boolean lockState, String password, Message onComplete) {
+ if (DBG) Rlog.d(LOG_TAG, "setCallBarring facility=" + facility
+ + ", lockState=" + lockState);
+ Message resp;
+ resp = obtainMessage(EVENT_SET_CALL_BARRING_DONE, onComplete);
+
+ try {
+ ImsUtInterface ut = mCT.getUtInterface();
+ // password is not required with Ut interface
+ ut.updateCallBarring(getCBTypeFromFacility(facility), lockState, resp);
+ } catch (ImsException e) {
+ sendErrorResponse(onComplete, e);
+ }
+ }
+
+ @Override
+ public void sendUssdResponse(String ussdMessge) {
+ Rlog.d(LOG_TAG, "sendUssdResponse");
+ ImsPhoneMmiCode mmi = ImsPhoneMmiCode.newFromUssdUserInput(ussdMessge, this);
+ mPendingMMIs.add(mmi);
+ mMmiRegistrants.notifyRegistrants(new AsyncResult(null, mmi, null));
+ mmi.sendUssd(ussdMessge);
+ }
+
+ /* package */
+ void sendUSSD (String ussdString, Message response) {
+ mCT.sendUSSD(ussdString, response);
+ }
+
+ /* package */
+ void cancelUSSD() {
+ mCT.cancelUSSD();
+ }
+
+ /* package */
+ void sendErrorResponse(Message onComplete) {
+ Rlog.d(LOG_TAG, "sendErrorResponse");
+ if (onComplete != null) {
+ AsyncResult.forMessage(onComplete, null,
+ new CommandException(CommandException.Error.GENERIC_FAILURE));
+ onComplete.sendToTarget();
+ }
+ }
+
+ /* package */
+ void sendErrorResponse(Message onComplete, Throwable e) {
+ Rlog.d(LOG_TAG, "sendErrorResponse");
+ if (onComplete != null) {
+ AsyncResult.forMessage(onComplete, null, getCommandException(e));
+ onComplete.sendToTarget();
+ }
+ }
+
+ /* package */
+ void sendErrorResponse(Message onComplete, ImsReasonInfo reasonInfo) {
+ Rlog.d(LOG_TAG, "sendErrorResponse reasonCode=" + reasonInfo.getCode());
+ if (onComplete != null) {
+ AsyncResult.forMessage(onComplete, null, getCommandException(reasonInfo.getCode()));
+ onComplete.sendToTarget();
+ }
+ }
+
+ /* package */
+ CommandException getCommandException(int code) {
+ Rlog.d(LOG_TAG, "getCommandException code=" + code);
+ CommandException.Error error = CommandException.Error.GENERIC_FAILURE;
+
+ switch(code) {
+ case ImsReasonInfo.CODE_UT_NOT_SUPPORTED:
+ error = CommandException.Error.REQUEST_NOT_SUPPORTED;
+ break;
+ case ImsReasonInfo.CODE_UT_CB_PASSWORD_MISMATCH:
+ error = CommandException.Error.PASSWORD_INCORRECT;
+ break;
+ default:
+ break;
+ }
+
+ return new CommandException(error);
+ }
+
+ /* package */
+ CommandException getCommandException(Throwable e) {
+ CommandException ex = null;
+
+ if (e instanceof ImsException) {
+ ex = getCommandException(((ImsException)e).getCode());
+ } else {
+ Rlog.d(LOG_TAG, "getCommandException generic failure");
+ ex = new CommandException(CommandException.Error.GENERIC_FAILURE);
+ }
+ return ex;
+ }
+
+ private void
+ onNetworkInitiatedUssd(ImsPhoneMmiCode mmi) {
+ Rlog.d(LOG_TAG, "onNetworkInitiatedUssd");
+ mMmiCompleteRegistrants.notifyRegistrants(
+ new AsyncResult(null, mmi, null));
+ }
+
+ /* package */
+ void onIncomingUSSD (int ussdMode, String ussdMessage) {
+ if (DBG) Rlog.d(LOG_TAG, "onIncomingUSSD ussdMode=" + ussdMode);
+
+ boolean isUssdError;
+ boolean isUssdRequest;
+
+ isUssdRequest
+ = (ussdMode == CommandsInterface.USSD_MODE_REQUEST);
+
+ isUssdError
+ = (ussdMode != CommandsInterface.USSD_MODE_NOTIFY
+ && ussdMode != CommandsInterface.USSD_MODE_REQUEST);
+
+ ImsPhoneMmiCode found = null;
+ for (int i = 0, s = mPendingMMIs.size() ; i < s; i++) {
+ if(mPendingMMIs.get(i).isPendingUSSD()) {
+ found = mPendingMMIs.get(i);
+ break;
+ }
+ }
+
+ if (found != null) {
+ // Complete pending USSD
+ if (isUssdError) {
+ found.onUssdFinishedError();
+ } else {
+ found.onUssdFinished(ussdMessage, isUssdRequest);
+ }
+ } else { // pending USSD not found
+ // The network may initiate its own USSD request
+
+ // ignore everything that isnt a Notify or a Request
+ // also, discard if there is no message to present
+ if (!isUssdError && ussdMessage != null) {
+ ImsPhoneMmiCode mmi;
+ mmi = ImsPhoneMmiCode.newNetworkInitiatedUssd(ussdMessage,
+ isUssdRequest,
+ ImsPhone.this);
+ onNetworkInitiatedUssd(mmi);
+ }
+ }
+ }
+
+ /**
+ * Removes the given MMI from the pending list and notifies
+ * registrants that it is complete.
+ * @param mmi MMI that is done
+ */
+ /*package*/ void
+ onMMIDone(ImsPhoneMmiCode mmi) {
+ /* Only notify complete if it's on the pending list.
+ * Otherwise, it's already been handled (eg, previously canceled).
+ * The exception is cancellation of an incoming USSD-REQUEST, which is
+ * not on the list.
+ */
+ if (mPendingMMIs.remove(mmi) || mmi.isUssdRequest()) {
+ mMmiCompleteRegistrants.notifyRegistrants(
+ new AsyncResult(null, mmi, null));
+ }
+ }
+
+ @Override
+ public ImsPhoneConnection getHandoverConnection() {
+ ImsPhoneConnection conn = getForegroundCall().getHandoverConnection();
+
+ if (conn == null) {
+ conn = getBackgroundCall().getHandoverConnection();
+ }
+
+ return conn;
+ }
+
+ @Override
+ public void notifySrvccState(Call.SrvccState state) {
+ mCT.notifySrvccState(state);
+ }
+
+ /* package */ void
+ initiateSilentRedial() {
+ String result = mLastDialString;
+ AsyncResult ar = new AsyncResult(null, result, null);
+ if (ar != null) {
+ mSilentRedialRegistrants.notifyRegistrants(ar);
+ }
+ }
+
+ @Override
+ public void registerForSilentRedial(Handler h, int what, Object obj) {
+ mSilentRedialRegistrants.addUnique(h, what, obj);
+ }
+
+ @Override
+ public void unregisterForSilentRedial(Handler h) {
+ mSilentRedialRegistrants.remove(h);
+ }
+
+ @Override
+ public long getSubId() {
+ return mDefaultPhone.getSubId();
+ }
+
+ @Override
+ public int getPhoneId() {
+ return mDefaultPhone.getPhoneId();
+ }
+
+ @Override
+ public Subscription getSubscriptionInfo() {
+ return mDefaultPhone.getSubscriptionInfo();
+ }
+
+ private IccRecords getIccRecords() {
+ return mDefaultPhone.mIccRecords.get();
+ }
+
+ private CallForwardInfo getCallForwardInfo(ImsCallForwardInfo info) {
+ CallForwardInfo cfInfo = new CallForwardInfo();
+ cfInfo.status = info.mStatus;
+ cfInfo.reason = getCFReasonFromCondition(info.mCondition);
+ cfInfo.serviceClass = SERVICE_CLASS_VOICE;
+ cfInfo.toa = info.mToA;
+ cfInfo.number = info.mNumber;
+ cfInfo.timeSeconds = info.mTimeSeconds;
+ return cfInfo;
+ }
+
+ private CallForwardInfo[] handleCfQueryResult(ImsCallForwardInfo[] infos) {
+ CallForwardInfo[] cfInfos = null;
+
+ if (infos != null && infos.length != 0) {
+ cfInfos = new CallForwardInfo[infos.length];
+ }
+
+ IccRecords r = getIccRecords();
+ if (infos == null || infos.length == 0) {
+ if (r != null) {
+ // Assume the default is not active
+ // Set unconditional CFF in SIM to false
+ r.setVoiceCallForwardingFlag(1, false, null);
+ }
+ } else {
+ for (int i = 0, s = infos.length; i < s; i++) {
+ if (infos[i].mCondition == ImsUtInterface.CDIV_CF_UNCONDITIONAL) {
+ if (r != null) {
+ r.setVoiceCallForwardingFlag(1, (infos[i].mStatus == 1),
+ infos[i].mNumber);
+ }
+ }
+ cfInfos[i] = getCallForwardInfo(infos[i]);
+ }
+ }
+
+ return cfInfos;
+ }
+
+ private int[] handleCbQueryResult(ImsSsInfo[] infos) {
+ int[] cbInfos = new int[1];
+ cbInfos[0] = SERVICE_CLASS_NONE;
+
+ if (infos[0].mStatus == 1) {
+ cbInfos[0] = SERVICE_CLASS_VOICE;
+ }
+
+ return cbInfos;
+ }
+
+ private int[] handleCwQueryResult(ImsSsInfo[] infos) {
+ int[] cwInfos = new int[2];
+ cwInfos[0] = 0;
+
+ if (infos[0].mStatus == 1) {
+ cwInfos[0] = 1;
+ cwInfos[1] = SERVICE_CLASS_VOICE;
+ }
+
+ return cwInfos;
+ }
+
+ private void
+ sendResponse(Message onComplete, Object result, Throwable e) {
+ if (onComplete != null) {
+ CommandException ex = null;
+ if (e != null) {
+ ex = getCommandException(e);
+ }
+ AsyncResult.forMessage(onComplete, result, ex);
+ onComplete.sendToTarget();
+ }
+ }
+
+ @Override
+ public void handleMessage (Message msg) {
+ AsyncResult ar = (AsyncResult) msg.obj;
+ Message onComplete;
+
+ if (DBG) Rlog.d(LOG_TAG, "handleMessage what=" + msg.what);
+ switch (msg.what) {
+ case EVENT_SET_CALL_FORWARD_DONE:
+ IccRecords r = getIccRecords();
+ Cf cf = (Cf) ar.userObj;
+ if (cf.mIsCfu && ar.exception == null && r != null) {
+ r.setVoiceCallForwardingFlag(1, msg.arg1 == 1, cf.mSetCfNumber);
+ }
+ sendResponse(cf.mOnComplete, null, ar.exception);
+ break;
+
+ case EVENT_GET_CALL_FORWARD_DONE:
+ CallForwardInfo[] cfInfos = null;
+ if (ar.exception == null) {
+ cfInfos = handleCfQueryResult((ImsCallForwardInfo[])ar.result);
+ }
+ sendResponse((Message) ar.userObj, cfInfos, ar.exception);
+ break;
+
+ case EVENT_GET_CALL_BARRING_DONE:
+ case EVENT_GET_CALL_WAITING_DONE:
+ int[] ssInfos = null;
+ if (ar.exception == null) {
+ if (msg.what == EVENT_GET_CALL_BARRING_DONE) {
+ ssInfos = handleCbQueryResult((ImsSsInfo[])ar.result);
+ } else if (msg.what == EVENT_GET_CALL_WAITING_DONE) {
+ ssInfos = handleCwQueryResult((ImsSsInfo[])ar.result);
+ }
+ }
+ sendResponse((Message) ar.userObj, ssInfos, ar.exception);
+ break;
+
+ case EVENT_SET_CALL_BARRING_DONE:
+ case EVENT_SET_CALL_WAITING_DONE:
+ sendResponse((Message) ar.userObj, null, ar.exception);
+ break;
+
+ default:
+ super.handleMessage(msg);
+ break;
+ }
+ }
+}
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneBase.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneBase.java
new file mode 100644
index 0000000..0743412
--- /dev/null
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneBase.java
@@ -0,0 +1,559 @@
+/*
+ * Copyright (C) 2013 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.imsphone;
+
+import android.content.Context;
+import android.net.LinkProperties;
+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.CellInfo;
+import android.telephony.CellLocation;
+import android.telephony.ServiceState;
+import android.telephony.SignalStrength;
+import android.telephony.Rlog;
+
+import com.android.internal.telephony.Call;
+import com.android.internal.telephony.CallStateException;
+import com.android.internal.telephony.Connection;
+import com.android.internal.telephony.dataconnection.DataConnection;
+import com.android.internal.telephony.IccCard;
+import com.android.internal.telephony.IccPhoneBookInterfaceManager;
+import com.android.internal.telephony.MmiCode;
+import com.android.internal.telephony.OperatorInfo;
+import com.android.internal.telephony.PhoneBase;
+import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.telephony.PhoneNotifier;
+import com.android.internal.telephony.PhoneSubInfo;
+import com.android.internal.telephony.TelephonyProperties;
+import com.android.internal.telephony.UUSInfo;
+import com.android.internal.telephony.uicc.IccFileHandler;
+
+import java.util.ArrayList;
+import java.util.List;
+
+abstract class ImsPhoneBase extends PhoneBase {
+ private static final String LOG_TAG = "ImsPhoneBase";
+
+ private RegistrantList mRingbackRegistrants = new RegistrantList();
+ private RegistrantList mOnHoldRegistrants = new RegistrantList();
+ private PhoneConstants.State mState = PhoneConstants.State.IDLE;
+
+ public ImsPhoneBase(String name, Context context, PhoneNotifier notifier) {
+ super(name, notifier, context, new ImsPhoneCommandInterface(context), false);
+ }
+
+ @Override
+ public Connection dial(String dialString, UUSInfo uusInfo)
+ throws CallStateException {
+ // ignore UUSInfo
+ return dial(dialString);
+ }
+
+ void migrateFrom(ImsPhoneBase 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, Boolean.TRUE, null);
+ mRingbackRegistrants.notifyRegistrants(result);
+ }
+
+ protected void stopRingbackTone() {
+ AsyncResult result = new AsyncResult(null, Boolean.FALSE, null);
+ mRingbackRegistrants.notifyRegistrants(result);
+ }
+
+ @Override
+ public void registerForOnHoldTone(Handler h, int what, Object obj) {
+ mOnHoldRegistrants.addUnique(h, what, obj);
+ }
+
+ @Override
+ public void unregisterForOnHoldTone(Handler h) {
+ mOnHoldRegistrants.remove(h);
+ }
+
+ protected void startOnHoldTone() {
+ AsyncResult result = new AsyncResult(null, Boolean.TRUE, null);
+ mOnHoldRegistrants.notifyRegistrants(result);
+ }
+
+ protected void stopOnHoldTone() {
+ AsyncResult result = new AsyncResult(null, Boolean.FALSE, null);
+ mOnHoldRegistrants.notifyRegistrants(result);
+ }
+
+ @Override
+ 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;
+ }
+
+ /**
+ * @return all available cell information or null if none.
+ */
+ @Override
+ public List<CellInfo> getAllCellInfo() {
+ return getServiceStateTracker().getAllCellInfo();
+ }
+
+ @Override
+ public CellLocation getCellLocation() {
+ return null;
+ }
+
+ @Override
+ public PhoneConstants.State getState() {
+ return mState;
+ }
+
+ @Override
+ public int getPhoneType() {
+ return PhoneConstants.PHONE_TYPE_IMS;
+ }
+
+ @Override
+ public SignalStrength getSignalStrength() {
+ return new SignalStrength();
+ }
+
+ @Override
+ public boolean getMessageWaitingIndicator() {
+ return false;
+ }
+
+ @Override
+ public boolean getCallForwardingIndicator() {
+ return false;
+ }
+
+ @Override
+ public List<? extends MmiCode> getPendingMmiCodes() {
+ return new ArrayList<MmiCode>(0);
+ }
+
+ @Override
+ public PhoneConstants.DataState getDataConnectionState() {
+ return PhoneConstants.DataState.DISCONNECTED;
+ }
+
+ @Override
+ public PhoneConstants.DataState getDataConnectionState(String apnType) {
+ return PhoneConstants.DataState.DISCONNECTED;
+ }
+
+ @Override
+ public DataActivityState getDataActivityState() {
+ return DataActivityState.NONE;
+ }
+
+ /**
+ * Notify any interested party of a Phone state change
+ * {@link com.android.internal.telephony.PhoneConstants.State}
+ */
+ /* package */ void notifyPhoneStateChanged() {
+ mNotifier.notifyPhoneState(this);
+ }
+
+ /**
+ * Notify registrants of a change in the call state. This notifies changes in
+ * {@link com.android.internal.telephony.Call.State}. Use this when changes
+ * in the precise call state are needed, else use notifyPhoneStateChanged.
+ */
+ /* package */ 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);
+ }
+
+ @Override
+ public void notifyCallForwardingIndicator() {
+ mNotifier.notifyCallForwardingChanged(this);
+ }
+
+ public boolean canDial() {
+ int serviceState = getServiceState().getState();
+ Rlog.v(LOG_TAG, "canDial(): serviceState = " + serviceState);
+ if (serviceState == ServiceState.STATE_POWER_OFF) return false;
+
+ String disableCall = SystemProperties.get(
+ TelephonyProperties.PROPERTY_DISABLE_CALL, "false");
+ Rlog.v(LOG_TAG, "canDial(): disableCall = " + disableCall);
+ if (disableCall.equals("true")) return false;
+
+ Rlog.v(LOG_TAG, "canDial(): ringingCall: " + getRingingCall().getState());
+ Rlog.v(LOG_TAG, "canDial(): foregndCall: " + getForegroundCall().getState());
+ Rlog.v(LOG_TAG, "canDial(): backgndCall: " + getBackgroundCall().getState());
+ return !getRingingCall().isRinging()
+ && (!getForegroundCall().getState().isAlive()
+ || !getBackgroundCall().getState().isAlive());
+ }
+
+ @Override
+ public boolean handleInCallMmiCommands(String dialString) {
+ 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());
+ }
+
+ @Override
+ public boolean handlePinMmi(String dialString) {
+ return false;
+ }
+
+ @Override
+ public void sendUssdResponse(String ussdMessge) {
+ }
+
+ @Override
+ public void registerForSuppServiceNotification(
+ Handler h, int what, Object obj) {
+ }
+
+ @Override
+ public void unregisterForSuppServiceNotification(Handler h) {
+ }
+
+ @Override
+ public void setRadioPower(boolean power) {
+ }
+
+ @Override
+ public String getVoiceMailNumber() {
+ return null;
+ }
+
+ @Override
+ public String getVoiceMailAlphaTag() {
+ return null;
+ }
+
+ @Override
+ public String getDeviceId() {
+ return null;
+ }
+
+ @Override
+ public String getDeviceSvn() {
+ return null;
+ }
+
+ @Override
+ public String getImei() {
+ return null;
+ }
+
+ @Override
+ public String getEsn() {
+ Rlog.e(LOG_TAG, "[VoltePhone] getEsn() is a CDMA method");
+ return "0";
+ }
+
+ @Override
+ public String getMeid() {
+ Rlog.e(LOG_TAG, "[VoltePhone] getMeid() is a CDMA method");
+ return "0";
+ }
+
+ @Override
+ public String getSubscriberId() {
+ return null;
+ }
+
+ @Override
+ public String getGroupIdLevel1() {
+ return null;
+ }
+
+ @Override
+ public String getIccSerialNumber() {
+ return null;
+ }
+
+ @Override
+ public String getLine1Number() {
+ return null;
+ }
+
+ @Override
+ public String getLine1AlphaTag() {
+ return null;
+ }
+
+ @Override
+ public void setLine1Number(String alphaTag, String number, Message onComplete) {
+ // FIXME: what to reply for Volte?
+ AsyncResult.forMessage(onComplete, null, null);
+ onComplete.sendToTarget();
+ }
+
+ @Override
+ public void setVoiceMailNumber(String alphaTag, String voiceMailNumber,
+ Message onComplete) {
+ // FIXME: what to reply for Volte?
+ AsyncResult.forMessage(onComplete, null, null);
+ onComplete.sendToTarget();
+ }
+
+ @Override
+ public void getCallForwardingOption(int commandInterfaceCFReason, Message onComplete) {
+ }
+
+ @Override
+ public void setCallForwardingOption(int commandInterfaceCFAction,
+ int commandInterfaceCFReason, String dialingNumber,
+ int timerSeconds, Message onComplete) {
+ }
+
+ @Override
+ public void getOutgoingCallerIdDisplay(Message onComplete) {
+ // FIXME: what to reply?
+ AsyncResult.forMessage(onComplete, null, null);
+ onComplete.sendToTarget();
+ }
+
+ @Override
+ public void setOutgoingCallerIdDisplay(int commandInterfaceCLIRMode,
+ Message onComplete) {
+ // FIXME: what's this for Volte?
+ AsyncResult.forMessage(onComplete, null, null);
+ onComplete.sendToTarget();
+ }
+
+ @Override
+ public void getCallWaiting(Message onComplete) {
+ AsyncResult.forMessage(onComplete, null, null);
+ onComplete.sendToTarget();
+ }
+
+ @Override
+ public void setCallWaiting(boolean enable, Message onComplete) {
+ Rlog.e(LOG_TAG, "call waiting not supported");
+ }
+
+ @Override
+ public boolean getIccRecordsLoaded() {
+ return false;
+ }
+
+ @Override
+ public IccCard getIccCard() {
+ return null;
+ }
+
+ @Override
+ public void getAvailableNetworks(Message response) {
+ }
+
+ @Override
+ public void setNetworkSelectionModeAutomatic(Message response) {
+ }
+
+ @Override
+ public void selectNetworkManually(
+ OperatorInfo network,
+ Message response) {
+ }
+
+ @Override
+ public void getNeighboringCids(Message response) {
+ }
+
+ @Override
+ public void getDataCallList(Message response) {
+ }
+
+ public List<DataConnection> getCurrentDataConnectionList () {
+ return null;
+ }
+
+ @Override
+ public void updateServiceLocation() {
+ }
+
+ @Override
+ public void enableLocationUpdates() {
+ }
+
+ @Override
+ public void disableLocationUpdates() {
+ }
+
+ @Override
+ public boolean getDataRoamingEnabled() {
+ return false;
+ }
+
+ @Override
+ public void setDataRoamingEnabled(boolean enable) {
+ }
+
+ @Override
+ public boolean getDataEnabled() {
+ return false;
+ }
+
+ @Override
+ public void setDataEnabled(boolean enable) {
+ }
+
+
+ public boolean enableDataConnectivity() {
+ return false;
+ }
+
+ public boolean disableDataConnectivity() {
+ return false;
+ }
+
+ @Override
+ public boolean isDataConnectivityPossible() {
+ return false;
+ }
+
+ boolean updateCurrentCarrierInProvider() {
+ return false;
+ }
+
+ public void saveClirSetting(int commandInterfaceCLIRMode) {
+ }
+
+ @Override
+ public PhoneSubInfo getPhoneSubInfo(){
+ return null;
+ }
+
+ @Override
+ public IccPhoneBookInterfaceManager getIccPhoneBookInterfaceManager(){
+ return null;
+ }
+
+ @Override
+ public IccFileHandler getIccFileHandler(){
+ return null;
+ }
+
+ @Override
+ public void activateCellBroadcastSms(int activate, Message response) {
+ Rlog.e(LOG_TAG, "Error! This functionality is not implemented for Volte.");
+ }
+
+ @Override
+ public void getCellBroadcastSmsConfig(Message response) {
+ Rlog.e(LOG_TAG, "Error! This functionality is not implemented for Volte.");
+ }
+
+ @Override
+ public void setCellBroadcastSmsConfig(int[] configValuesArray, Message response){
+ Rlog.e(LOG_TAG, "Error! This functionality is not implemented for Volte.");
+ }
+
+ //@Override
+ @Override
+ public boolean needsOtaServiceProvisioning() {
+ // FIXME: what's this for Volte?
+ return false;
+ }
+
+ //@Override
+ @Override
+ public LinkProperties getLinkProperties(String apnType) {
+ // FIXME: what's this for Volte?
+ return null;
+ }
+
+ @Override
+ protected void onUpdateIccAvailability() {
+ }
+
+ void updatePhoneState() {
+ PhoneConstants.State oldState = mState;
+
+ if (getRingingCall().isRinging()) {
+ mState = PhoneConstants.State.RINGING;
+ } else if (getForegroundCall().isIdle()
+ && getBackgroundCall().isIdle()) {
+ mState = PhoneConstants.State.IDLE;
+ } else {
+ mState = PhoneConstants.State.OFFHOOK;
+ }
+
+ if (mState != oldState) {
+ Rlog.d(LOG_TAG, " ^^^ new phone state: " + mState);
+ notifyPhoneStateChanged();
+ }
+ }
+}
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCall.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCall.java
new file mode 100644
index 0000000..a6cab1e
--- /dev/null
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCall.java
@@ -0,0 +1,298 @@
+/*
+ * Copyright (C) 2013 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.imsphone;
+
+import android.telephony.Rlog;
+import android.telephony.DisconnectCause;
+
+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.ims.ImsCall;
+import com.android.ims.ImsException;
+import com.android.ims.ImsStreamMediaProfile;
+
+import java.util.List;
+
+/**
+ * {@hide}
+ */
+class ImsPhoneCall extends Call {
+ /*************************** Instance Variables **************************/
+
+ private static final String LOG_TAG = "ImsPhoneCall";
+
+ /*package*/ ImsPhoneCallTracker mOwner;
+
+ private boolean mRingbackTonePlayed = false;
+
+ /****************************** Constructors *****************************/
+ /*package*/
+ ImsPhoneCall() {
+ }
+
+ /*package*/
+ ImsPhoneCall(ImsPhoneCallTracker owner) {
+ mOwner = owner;
+ }
+
+ public void dispose() {
+ try {
+ mOwner.hangup(this);
+ } catch (CallStateException ex) {
+ //Rlog.e(LOG_TAG, "dispose: unexpected error on hangup", ex);
+ //while disposing, ignore the exception and clean the connections
+ } finally {
+ for(int i = 0, s = mConnections.size(); i < s; i++) {
+ ImsPhoneConnection c = (ImsPhoneConnection) mConnections.get(i);
+ c.onDisconnect(DisconnectCause.LOST_SIGNAL);
+ }
+ }
+ }
+
+ /************************** Overridden from Call *************************/
+
+ @Override
+ public List<Connection>
+ getConnections() {
+ return mConnections;
+ }
+
+ @Override
+ public Phone
+ getPhone() {
+ return mOwner.mPhone;
+ }
+
+ @Override
+ public boolean
+ isMultiparty() {
+ return mConnections.size() > 1;
+ }
+
+ /** Please note: if this is the foreground call and a
+ * background call exists, the background call will be resumed.
+ */
+ @Override
+ public void
+ hangup() throws CallStateException {
+ mOwner.hangup(this);
+ }
+
+ @Override
+ public String
+ toString() {
+ return mState.toString();
+ }
+
+ //***** Called from ImsPhoneConnection
+
+ /*package*/ void
+ attach(Connection conn) {
+ clearDisconnected();
+ mConnections.add(conn);
+ }
+
+ /*package*/ void
+ attach(Connection conn, State state) {
+ this.attach(conn);
+ mState = state;
+ }
+
+ /*package*/ void
+ attachFake(Connection conn, State state) {
+ attach(conn, state);
+ }
+
+ /**
+ * Called by ImsPhoneConnection when it has disconnected
+ */
+ boolean
+ connectionDisconnected(ImsPhoneConnection conn) {
+ if (mState != State.DISCONNECTED) {
+ /* If only disconnected connections remain, we are disconnected*/
+
+ boolean hasOnlyDisconnectedConnections = true;
+
+ for (int i = 0, s = mConnections.size() ; i < s; i ++) {
+ if (mConnections.get(i).getState() != State.DISCONNECTED) {
+ hasOnlyDisconnectedConnections = false;
+ break;
+ }
+ }
+
+ if (hasOnlyDisconnectedConnections) {
+ mState = State.DISCONNECTED;
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /*package*/ void
+ detach(ImsPhoneConnection conn) {
+ mConnections.remove(conn);
+
+ if (mConnections.size() == 0) {
+ mState = State.IDLE;
+ }
+ }
+
+ /**
+ * @return true if there's no space in this call for additional
+ * connections to be added via "conference"
+ */
+ /*package*/ boolean
+ isFull() {
+ return mConnections.size() == ImsPhoneCallTracker.MAX_CONNECTIONS_PER_CALL;
+ }
+
+ //***** Called from ImsPhoneCallTracker
+ /**
+ * Called when this Call is being hung up locally (eg, user pressed "end")
+ */
+ void
+ onHangupLocal() {
+ for (int i = 0, s = mConnections.size(); i < s; i++) {
+ ImsPhoneConnection cn = (ImsPhoneConnection)mConnections.get(i);
+ cn.onHangupLocal();
+ }
+ mState = State.DISCONNECTING;
+ }
+
+ /**
+ * Called when it's time to clean up disconnected Connection objects
+ */
+ void
+ clearDisconnected() {
+ for (int i = mConnections.size() - 1 ; i >= 0 ; i--) {
+ ImsPhoneConnection cn = (ImsPhoneConnection)mConnections.get(i);
+
+ if (cn.getState() == State.DISCONNECTED) {
+ mConnections.remove(i);
+ }
+ }
+
+ if (mConnections.size() == 0) {
+ mState = State.IDLE;
+ }
+ }
+
+ /*package*/ ImsPhoneConnection
+ getFirstConnection() {
+ if (mConnections.size() == 0) return null;
+
+ return (ImsPhoneConnection) mConnections.get(0);
+ }
+
+ /*package*/ void
+ setMute(boolean mute) {
+ ImsCall imsCall = getFirstConnection() == null ?
+ null : getFirstConnection().getImsCall();
+ if (imsCall != null) {
+ try {
+ imsCall.setMute(mute);
+ } catch (ImsException e) {
+ Rlog.e(LOG_TAG, "setMute failed : " + e.getMessage());
+ }
+ }
+ }
+
+ /* package */ void
+ merge(ImsPhoneCall that, State state) {
+ ImsPhoneConnection[] cc = that.mConnections.toArray(
+ new ImsPhoneConnection[that.mConnections.size()]);
+ for (ImsPhoneConnection c : cc) {
+ c.update(null, state);
+ }
+ }
+
+ /*package*/ ImsCall
+ getImsCall() {
+ return (getFirstConnection() == null) ? null : getFirstConnection().getImsCall();
+ }
+
+ /*package*/ static boolean isLocalTone(ImsCall imsCall) {
+ if ((imsCall == null) || (imsCall.getCallProfile() == null)
+ || (imsCall.getCallProfile().mMediaProfile == null)) {
+ return false;
+ }
+
+ ImsStreamMediaProfile mediaProfile = imsCall.getCallProfile().mMediaProfile;
+
+ return (mediaProfile.mAudioDirection == ImsStreamMediaProfile.DIRECTION_INACTIVE)
+ ? true : false;
+ }
+
+ /*package*/ boolean
+ update (ImsPhoneConnection conn, ImsCall imsCall, State state) {
+ State newState = state;
+ boolean changed = false;
+
+ //ImsCall.Listener.onCallProgressing can be invoked several times
+ //and ringback tone mode can be changed during the call setup procedure
+ if (state == State.ALERTING) {
+ if (mRingbackTonePlayed && !isLocalTone(imsCall)) {
+ mOwner.mPhone.stopRingbackTone();
+ mRingbackTonePlayed = false;
+ } else if (!mRingbackTonePlayed && isLocalTone(imsCall)) {
+ mOwner.mPhone.startRingbackTone();
+ mRingbackTonePlayed = true;
+ }
+ } else {
+ if (mRingbackTonePlayed) {
+ mOwner.mPhone.stopRingbackTone();
+ mRingbackTonePlayed = false;
+ }
+ }
+
+ if ((newState != mState) && (state != State.DISCONNECTED)) {
+ mState = newState;
+ changed = true;
+ } else if (state == State.DISCONNECTED) {
+ changed = true;
+ }
+
+ return changed;
+ }
+
+ /* package */ ImsPhoneConnection
+ getHandoverConnection() {
+ ImsPhoneConnection conn = (ImsPhoneConnection) getEarliestConnection();
+ conn.setMultiparty(isMultiparty());
+ return conn;
+ }
+
+ void switchWith(ImsPhoneCall that) {
+ synchronized (ImsPhoneCall.class) {
+ ImsPhoneCall tmp = new ImsPhoneCall();
+ tmp.takeOver(this);
+ this.takeOver(that);
+ that.takeOver(tmp);
+ }
+ }
+
+ private void takeOver(ImsPhoneCall that) {
+ mConnections = that.mConnections;
+ mState = that.mState;
+ for (Connection c : mConnections) {
+ ((ImsPhoneConnection) c).changeParent(this);
+ }
+ }
+}
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
new file mode 100644
index 0000000..937020a
--- /dev/null
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
@@ -0,0 +1,1193 @@
+/*
+ * Copyright (C) 2013 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.imsphone;
+
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.SharedPreferences;
+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.preference.PreferenceManager;
+import android.telephony.DisconnectCause;
+import android.telephony.PhoneNumberUtils;
+import android.telephony.ServiceState;
+import android.telephony.Rlog;
+
+import com.android.internal.telephony.Call;
+import com.android.internal.telephony.CallStateException;
+import com.android.internal.telephony.CallTracker;
+import com.android.internal.telephony.CommandException;
+import com.android.internal.telephony.CommandsInterface;
+import com.android.internal.telephony.Connection;
+import com.android.internal.telephony.EventLogTags;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneBase;
+import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.telephony.TelephonyProperties;
+
+import com.android.ims.ImsCall;
+import com.android.ims.ImsCallProfile;
+import com.android.ims.ImsConnectionStateListener;
+import com.android.ims.ImsException;
+import com.android.ims.ImsManager;
+import com.android.ims.ImsReasonInfo;
+import com.android.ims.ImsServiceClass;
+import com.android.ims.ImsUtInterface;
+
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * {@hide}
+ */
+public final class ImsPhoneCallTracker extends CallTracker {
+ static final String LOG_TAG = "ImsPhoneCallTracker";
+
+ private static final boolean DBG = true;
+
+ private BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent.getAction().equals(ImsManager.ACTION_IMS_INCOMING_CALL)) {
+ if (DBG) log("onReceive : incoming call intent");
+
+ if (mImsManager == null) return;
+
+ if (mServiceId < 0) return;
+
+ try {
+ // Network initiated USSD will be treated by mImsUssdListener
+ boolean isUssd = intent.getBooleanExtra(ImsManager.EXTRA_USSD, false);
+ if (isUssd) {
+ if (DBG) log("onReceive : USSD");
+ mUssdSession = mImsManager.takeCall(mServiceId, intent, mImsUssdListener);
+ if (mUssdSession != null) {
+ mUssdSession.accept();
+ }
+ return;
+ }
+
+ // Normal MT call
+ ImsCall imsCall = mImsManager.takeCall(mServiceId, intent, mImsCallListener);
+
+ ImsPhoneConnection conn = new ImsPhoneConnection(mPhone.getContext(), imsCall,
+ ImsPhoneCallTracker.this, mRingingCall);
+ addConnection(conn);
+
+ if ((mForegroundCall.getState() != ImsPhoneCall.State.IDLE) ||
+ (mBackgroundCall.getState() != ImsPhoneCall.State.IDLE)) {
+ conn.update(imsCall, ImsPhoneCall.State.WAITING);
+ }
+
+ mPhone.notifyNewRingingConnection(conn);
+ mPhone.notifyIncomingRing();
+
+ updatePhoneState();
+ mPhone.notifyPreciseCallStateChanged();
+ } catch (ImsException e) {
+ loge("onReceive : exception " + e);
+ }
+ }
+ }
+ };
+
+ //***** Constants
+
+ static final int MAX_CONNECTIONS = 7;
+ static final int MAX_CONNECTIONS_PER_CALL = 5;
+
+ private static final int EVENT_HANGUP_PENDINGMO = 18;
+ private static final int EVENT_RESUME_BACKGROUND = 19;
+ private static final int EVENT_DIAL_PENDINGMO = 20;
+
+ private static final int TIMEOUT_HANGUP_PENDINGMO = 500;
+
+ //***** Instance Variables
+ private ArrayList<ImsPhoneConnection> mConnections = new ArrayList<ImsPhoneConnection>();
+ private RegistrantList mVoiceCallEndedRegistrants = new RegistrantList();
+ private RegistrantList mVoiceCallStartedRegistrants = new RegistrantList();
+
+ ImsPhoneCall mRingingCall = new ImsPhoneCall(this);
+ ImsPhoneCall mForegroundCall = new ImsPhoneCall(this);
+ ImsPhoneCall mBackgroundCall = new ImsPhoneCall(this);
+ ImsPhoneCall mHandoverCall = new ImsPhoneCall(this);
+
+ private ImsPhoneConnection mPendingMO;
+ private int mClirMode = CommandsInterface.CLIR_DEFAULT;
+ private Object mSyncHold = new Object();
+
+ private ImsCall mUssdSession = null;
+ private Message mPendingUssd = null;
+
+ ImsPhone mPhone;
+
+ private boolean mDesiredMute = false; // false = mute off
+ private boolean mOnHoldToneStarted = false;
+
+ PhoneConstants.State mState = PhoneConstants.State.IDLE;
+
+ private ImsManager mImsManager;
+ private int mServiceId = -1;
+
+ private Call.SrvccState mSrvccState = Call.SrvccState.NONE;
+
+ //***** Events
+
+
+ //***** Constructors
+
+ ImsPhoneCallTracker(ImsPhone phone) {
+ this.mPhone = phone;
+
+ IntentFilter intentfilter = new IntentFilter();
+ intentfilter.addAction(ImsManager.ACTION_IMS_INCOMING_CALL);
+ mPhone.getContext().registerReceiver(mReceiver, intentfilter);
+
+ Thread t = new Thread() {
+ public void run() {
+ getImsService();
+ }
+ };
+ t.start();
+ }
+
+ private PendingIntent createIncomingCallPendingIntent() {
+ Intent intent = new Intent(ImsManager.ACTION_IMS_INCOMING_CALL);
+ return PendingIntent.getBroadcast(mPhone.getContext(), 0, intent,
+ PendingIntent.FLAG_UPDATE_CURRENT);
+ }
+
+ private void getImsService() {
+ if (DBG) log("getImsService");
+ mImsManager = ImsManager.getInstance(mPhone.getContext());
+ try {
+ // If there are multiple GSM phones,
+ // each GSM phone creates IMS phone for itself.
+ // IMS service needs something to determine appropriate IMS phone.
+ //
+ // TODO: add additional parameter to identity the IMS phone's
+ // subscriber's information.
+ mServiceId = mImsManager.open(ImsServiceClass.MMTEL,
+ createIncomingCallPendingIntent(),
+ mImsConnectionStateListener);
+ } catch (ImsException e) {
+ loge("getImsService: " + e);
+ //Leave mImsManager as null, then CallStateException will be thrown when dialing
+ mImsManager = null;
+ }
+ }
+
+ public void dispose() {
+ if (DBG) log("dispose");
+ mRingingCall.dispose();
+ mBackgroundCall.dispose();
+ mForegroundCall.dispose();
+ mHandoverCall.dispose();
+
+ clearDisconnected();
+ mPhone.getContext().unregisterReceiver(mReceiver);
+ }
+
+ @Override
+ protected void finalize() {
+ log("ImsPhoneCallTracker finalized");
+ }
+
+ //***** Instance Methods
+
+ //***** Public Methods
+ @Override
+ public void registerForVoiceCallStarted(Handler h, int what, Object obj) {
+ Registrant r = new Registrant(h, what, obj);
+ mVoiceCallStartedRegistrants.add(r);
+ }
+
+ @Override
+ public void unregisterForVoiceCallStarted(Handler h) {
+ mVoiceCallStartedRegistrants.remove(h);
+ }
+
+ @Override
+ public void registerForVoiceCallEnded(Handler h, int what, Object obj) {
+ Registrant r = new Registrant(h, what, obj);
+ mVoiceCallEndedRegistrants.add(r);
+ }
+
+ @Override
+ public void unregisterForVoiceCallEnded(Handler h) {
+ mVoiceCallEndedRegistrants.remove(h);
+ }
+
+ Connection
+ dial(String dialString) throws CallStateException {
+ SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mPhone.getContext());
+ int oirMode = sp.getInt(PhoneBase.CLIR_KEY, CommandsInterface.CLIR_DEFAULT);
+ return dial(dialString, oirMode);
+ }
+
+ /**
+ * oirMode is one of the CLIR_ constants
+ */
+ synchronized Connection
+ dial(String dialString, int clirMode) throws CallStateException {
+ if (DBG) log("dial clirMode=" + clirMode);
+
+ // note that this triggers call state changed notif
+ clearDisconnected();
+
+ if (mImsManager == null) {
+ throw new CallStateException("service not available");
+ }
+
+ if (!canDial()) {
+ throw new CallStateException("cannot dial in current state");
+ }
+
+ boolean holdBeforeDial = false;
+
+ // The new call must be assigned to the foreground call.
+ // That call must be idle, so place anything that's
+ // there on hold
+ if (mForegroundCall.getState() == ImsPhoneCall.State.ACTIVE) {
+ if (mBackgroundCall.getState() != ImsPhoneCall.State.IDLE) {
+ //we should have failed in !canDial() above before we get here
+ throw new CallStateException("cannot dial in current state");
+ }
+ // foreground call is empty for the newly dialed connection
+ holdBeforeDial = true;
+ switchWaitingOrHoldingAndActive();
+ }
+
+ ImsPhoneCall.State fgState = ImsPhoneCall.State.IDLE;
+ ImsPhoneCall.State bgState = ImsPhoneCall.State.IDLE;
+
+ mClirMode = clirMode;
+
+ synchronized (mSyncHold) {
+ if (holdBeforeDial) {
+ fgState = mForegroundCall.getState();
+ bgState = mBackgroundCall.getState();
+
+ //holding foreground call failed
+ if (fgState == ImsPhoneCall.State.ACTIVE) {
+ throw new CallStateException("cannot dial in current state");
+ }
+
+ //holding foreground call succeeded
+ if (bgState == ImsPhoneCall.State.HOLDING) {
+ holdBeforeDial = false;
+ }
+ }
+
+ mPendingMO = new ImsPhoneConnection(mPhone.getContext(),
+ checkForTestEmergencyNumber(dialString), this, mForegroundCall);
+ }
+ addConnection(mPendingMO);
+
+ if (!holdBeforeDial) {
+ dialInternal(mPendingMO, clirMode);
+ }
+
+ updatePhoneState();
+ mPhone.notifyPreciseCallStateChanged();
+
+ return mPendingMO;
+ }
+
+ private void dialInternal(ImsPhoneConnection conn, int clirMode) {
+ if (conn == null) {
+ return;
+ }
+
+ if (conn.getAddress()== null || conn.getAddress().length() == 0
+ || conn.getAddress().indexOf(PhoneNumberUtils.WILD) >= 0) {
+ // Phone number is invalid
+ conn.setDisconnectCause(DisconnectCause.INVALID_NUMBER);
+ sendEmptyMessageDelayed(EVENT_HANGUP_PENDINGMO, TIMEOUT_HANGUP_PENDINGMO);
+ return;
+ }
+
+ // Always unmute when initiating a new call
+ setMute(false);
+ int serviceType = PhoneNumberUtils.isEmergencyNumber(conn.getAddress()) ?
+ ImsCallProfile.SERVICE_TYPE_EMERGENCY : ImsCallProfile.SERVICE_TYPE_NORMAL;
+
+ try {
+ String[] callees = new String[] { conn.getAddress() };
+ ImsCallProfile profile = mImsManager.createCallProfile(mServiceId,
+ serviceType, ImsCallProfile.CALL_TYPE_VOICE);
+ profile.setCallExtraInt(ImsCallProfile.EXTRA_OIR, clirMode);
+
+ ImsCall imsCall = mImsManager.makeCall(mServiceId, profile,
+ callees, mImsCallListener);
+ conn.setImsCall(imsCall);
+ } catch (ImsException e) {
+ loge("dialInternal : " + e);
+ conn.setDisconnectCause(DisconnectCause.ERROR_UNSPECIFIED);
+ sendEmptyMessageDelayed(EVENT_HANGUP_PENDINGMO, TIMEOUT_HANGUP_PENDINGMO);
+ }
+ }
+
+ void
+ acceptCall () throws CallStateException {
+ if (DBG) log("acceptCall");
+
+ if (mForegroundCall.getState().isAlive()
+ && mBackgroundCall.getState().isAlive()) {
+ throw new CallStateException("cannot accept call");
+ }
+
+ if ((mRingingCall.getState() == ImsPhoneCall.State.WAITING)
+ && mForegroundCall.getState().isAlive()) {
+ setMute(false);
+ switchWaitingOrHoldingAndActive();
+ } else if (mRingingCall.getState().isRinging()) {
+ if (DBG) log("acceptCall: incoming...");
+ // Always unmute when answering a new call
+ setMute(false);
+ try {
+ ImsCall imsCall = mRingingCall.getImsCall();
+ if (imsCall != null) {
+ imsCall.accept();
+ } else {
+ throw new CallStateException("no valid ims call");
+ }
+ } catch (ImsException e) {
+ throw new CallStateException("cannot accept call");
+ }
+ } else {
+ throw new CallStateException("phone not ringing");
+ }
+ }
+
+ void
+ rejectCall () throws CallStateException {
+ if (DBG) log("rejectCall");
+
+ if (mRingingCall.getState().isRinging()) {
+ hangup(mRingingCall);
+ } else {
+ throw new CallStateException("phone not ringing");
+ }
+ }
+
+ void
+ switchWaitingOrHoldingAndActive() throws CallStateException {
+ if (DBG) log("switchWaitingOrHoldingAndActive");
+
+ if (mRingingCall.getState() == ImsPhoneCall.State.INCOMING) {
+ throw new CallStateException("cannot be in the incoming state");
+ }
+
+ if (mForegroundCall.getState() == ImsPhoneCall.State.ACTIVE) {
+ ImsCall imsCall = mForegroundCall.getImsCall();
+ if (imsCall == null) {
+ throw new CallStateException("no ims call");
+ }
+
+ mForegroundCall.switchWith(mBackgroundCall);
+
+ try {
+ imsCall.hold();
+ } catch (ImsException e) {
+ mForegroundCall.switchWith(mBackgroundCall);
+ throw new CallStateException(e.getMessage());
+ }
+ } else if (mBackgroundCall.getState() == ImsPhoneCall.State.HOLDING) {
+ resumeWaitingOrHolding();
+ }
+ }
+
+ void
+ conference() {
+ if (DBG) log("conference");
+
+ ImsCall fgImsCall = mForegroundCall.getImsCall();
+ if (fgImsCall == null) {
+ log("conference no foreground ims call");
+ return;
+ }
+
+ ImsCall bgImsCall = mBackgroundCall.getImsCall();
+ if (bgImsCall == null) {
+ log("conference no background ims call");
+ return;
+ }
+
+ try {
+ fgImsCall.merge(bgImsCall);
+ } catch (ImsException e) {
+ log("conference " + e.getMessage());
+ }
+ }
+
+ void
+ explicitCallTransfer() {
+ //TODO : implement
+ }
+
+ void
+ clearDisconnected() {
+ if (DBG) log("clearDisconnected");
+
+ internalClearDisconnected();
+
+ updatePhoneState();
+ mPhone.notifyPreciseCallStateChanged();
+ }
+
+ boolean
+ canConference() {
+ return mForegroundCall.getState() == ImsPhoneCall.State.ACTIVE
+ && mBackgroundCall.getState() == ImsPhoneCall.State.HOLDING
+ && !mBackgroundCall.isFull()
+ && !mForegroundCall.isFull();
+ }
+
+ boolean
+ canDial() {
+ boolean ret;
+ int serviceState = mPhone.getServiceState().getState();
+ String disableCall = SystemProperties.get(
+ TelephonyProperties.PROPERTY_DISABLE_CALL, "false");
+
+ ret = (serviceState != ServiceState.STATE_POWER_OFF)
+ && mPendingMO == null
+ && !mRingingCall.isRinging()
+ && !disableCall.equals("true")
+ && (!mForegroundCall.getState().isAlive()
+ || !mBackgroundCall.getState().isAlive());
+
+ return ret;
+ }
+
+ boolean
+ canTransfer() {
+ return mForegroundCall.getState() == ImsPhoneCall.State.ACTIVE
+ && mBackgroundCall.getState() == ImsPhoneCall.State.HOLDING;
+ }
+
+ //***** Private Instance Methods
+
+ private void
+ internalClearDisconnected() {
+ mRingingCall.clearDisconnected();
+ mForegroundCall.clearDisconnected();
+ mBackgroundCall.clearDisconnected();
+ mHandoverCall.clearDisconnected();
+ }
+
+ private void
+ updatePhoneState() {
+ PhoneConstants.State oldState = mState;
+
+ if (mRingingCall.isRinging()) {
+ mState = PhoneConstants.State.RINGING;
+ } else if (mPendingMO != null ||
+ !(mForegroundCall.isIdle() && mBackgroundCall.isIdle())) {
+ mState = PhoneConstants.State.OFFHOOK;
+ } else {
+ mState = PhoneConstants.State.IDLE;
+ }
+
+ if (mState == PhoneConstants.State.IDLE && oldState != mState) {
+ mVoiceCallEndedRegistrants.notifyRegistrants(
+ new AsyncResult(null, null, null));
+ } else if (oldState == PhoneConstants.State.IDLE && oldState != mState) {
+ mVoiceCallStartedRegistrants.notifyRegistrants (
+ new AsyncResult(null, null, null));
+ }
+
+ if (DBG) log("updatePhoneState oldState=" + oldState + ", newState=" + mState);
+
+ if (mState != oldState) {
+ mPhone.notifyPhoneStateChanged();
+ }
+ }
+
+ private void
+ handleRadioNotAvailable() {
+ // handlePollCalls will clear out its
+ // call list when it gets the CommandException
+ // error result from this
+ pollCallsWhenSafe();
+ }
+
+ private void
+ dumpState() {
+ List l;
+
+ log("Phone State:" + mState);
+
+ log("Ringing call: " + mRingingCall.toString());
+
+ l = mRingingCall.getConnections();
+ for (int i = 0, s = l.size(); i < s; i++) {
+ log(l.get(i).toString());
+ }
+
+ log("Foreground call: " + mForegroundCall.toString());
+
+ l = mForegroundCall.getConnections();
+ for (int i = 0, s = l.size(); i < s; i++) {
+ log(l.get(i).toString());
+ }
+
+ log("Background call: " + mBackgroundCall.toString());
+
+ l = mBackgroundCall.getConnections();
+ for (int i = 0, s = l.size(); i < s; i++) {
+ log(l.get(i).toString());
+ }
+
+ }
+
+ //***** Called from ImsPhone
+
+ /*package*/ void
+ setMute(boolean mute) {
+ mDesiredMute = mute;
+ mForegroundCall.setMute(mute);
+ }
+
+ /*package*/ boolean
+ getMute() {
+ return mDesiredMute;
+ }
+
+ /*package*/ void
+ sendDtmf(char c) {
+ if (DBG) log("sendDtmf");
+
+ ImsCall imscall = mForegroundCall.getImsCall();
+ if (imscall != null) {
+ imscall.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;
+ }
+
+ //***** Called from ImsPhoneConnection
+
+ /*package*/ void
+ hangup (ImsPhoneConnection conn) throws CallStateException {
+ if (DBG) log("hangup connection");
+
+ if (conn.getOwner() != this) {
+ throw new CallStateException ("ImsPhoneConnection " + conn
+ + "does not belong to ImsPhoneCallTracker " + this);
+ }
+
+ hangup(conn.getCall());
+ }
+
+ //***** Called from ImsPhoneCall
+
+ /* package */ void
+ hangup (ImsPhoneCall call) throws CallStateException {
+ if (DBG) log("hangup call");
+
+ if (call.getConnections().size() == 0) {
+ throw new CallStateException("no connections");
+ }
+
+ ImsCall imsCall = call.getImsCall();
+ boolean rejectCall = false;
+
+ if (call == mRingingCall) {
+ if (Phone.DEBUG_PHONE) log("(ringing) hangup incoming");
+ rejectCall = true;
+ } else if (call == mForegroundCall) {
+ if (call.isDialingOrAlerting()) {
+ if (Phone.DEBUG_PHONE) {
+ log("(foregnd) hangup dialing or alerting...");
+ }
+ } else {
+ if (Phone.DEBUG_PHONE) {
+ log("(foregnd) hangup foreground");
+ }
+ //held call will be resumed by onCallTerminated
+ }
+ } else if (call == mBackgroundCall) {
+ if (Phone.DEBUG_PHONE) {
+ log("(backgnd) hangup waiting or background");
+ }
+ } else {
+ throw new RuntimeException ("ImsPhoneCall " + call +
+ "does not belong to ImsPhoneCallTracker " + this);
+ }
+
+ call.onHangupLocal();
+
+ try {
+ if (imsCall != null) {
+ if (rejectCall) imsCall.reject(ImsReasonInfo.CODE_USER_DECLINE);
+ else imsCall.terminate(ImsReasonInfo.CODE_USER_TERMINATED);
+ } else if (mPendingMO != null && call == mForegroundCall) {
+ // is holding a foreground call
+ mPendingMO.update(null, ImsPhoneCall.State.DISCONNECTED);
+ mPendingMO.onDisconnect();
+ removeConnection(mPendingMO);
+ mPendingMO = null;
+ updatePhoneState();
+ removeMessages(EVENT_DIAL_PENDINGMO);
+ }
+ } catch (ImsException e) {
+ throw new CallStateException(e.getMessage());
+ }
+
+ mPhone.notifyPreciseCallStateChanged();
+ }
+
+ /* package */
+ void resumeWaitingOrHolding() throws CallStateException {
+ if (DBG) log("resumeWaitingOrHolding");
+
+ try {
+ if (mForegroundCall.getState().isAlive()) {
+ //resume foreground call after holding background call
+ //they were switched before holding
+ ImsCall imsCall = mForegroundCall.getImsCall();
+ if (imsCall != null) imsCall.resume();
+ } else if (mRingingCall.getState() == ImsPhoneCall.State.WAITING) {
+ //accept waiting call after holding background call
+ ImsCall imsCall = mRingingCall.getImsCall();
+ if (imsCall != null) imsCall.accept();
+ } else {
+ //Just resume background call.
+ //To distinguish resuming call with swapping calls
+ //we do not switch calls.here
+ //ImsPhoneConnection.update will chnage the parent when completed
+ ImsCall imsCall = mBackgroundCall.getImsCall();
+ if (imsCall != null) imsCall.resume();
+ }
+ } catch (ImsException e) {
+ throw new CallStateException(e.getMessage());
+ }
+ }
+
+ /* package */
+ void sendUSSD (String ussdString, Message response) {
+ if (DBG) log("sendUSSD");
+
+ try {
+ if (mUssdSession != null) {
+ mUssdSession.sendUssd(ussdString);
+ AsyncResult.forMessage(response, null, null);
+ response.sendToTarget();
+ return;
+ }
+
+ String[] callees = new String[] { ussdString };
+ ImsCallProfile profile = mImsManager.createCallProfile(mServiceId,
+ ImsCallProfile.SERVICE_TYPE_NORMAL, ImsCallProfile.CALL_TYPE_VOICE);
+ profile.setCallExtraInt(ImsCallProfile.EXTRA_DIALSTRING,
+ ImsCallProfile.DIALSTRING_USSD);
+
+ mUssdSession = mImsManager.makeCall(mServiceId, profile,
+ callees, mImsUssdListener);
+ } catch (ImsException e) {
+ loge("sendUSSD : " + e);
+ mPhone.sendErrorResponse(response, e);
+ }
+ }
+
+ /* package */
+ void cancelUSSD() {
+ if (mUssdSession == null) return;
+
+ try {
+ mUssdSession.terminate(ImsReasonInfo.CODE_USER_TERMINATED);
+ } catch (ImsException e) {
+ }
+
+ }
+
+ private synchronized ImsPhoneConnection findConnection(ImsCall imsCall) {
+ for (ImsPhoneConnection conn : mConnections) {
+ if (conn.getImsCall() == imsCall) {
+ return conn;
+ }
+ }
+ return null;
+ }
+
+ private synchronized void removeConnection(ImsPhoneConnection conn) {
+ mConnections.remove(conn);
+ }
+
+ private synchronized void addConnection(ImsPhoneConnection conn) {
+ mConnections.add(conn);
+ }
+
+ private void processCallStateChange(ImsCall imsCall, ImsPhoneCall.State state, int cause) {
+ if (DBG) log("processCallStateChange state=" + state + " cause=" + cause);
+
+ if (imsCall == null) return;
+
+ boolean changed = false;
+ ImsPhoneConnection conn = findConnection(imsCall);
+
+ if (conn == null) {
+ // TODO : what should be done?
+ return;
+ }
+
+ changed = conn.update(imsCall, state);
+
+ if (state == ImsPhoneCall.State.DISCONNECTED) {
+ changed = conn.onDisconnect(cause) || changed;
+ removeConnection(conn);
+ }
+
+ if (changed) {
+ if (conn.getCall() == mHandoverCall) return;
+ updatePhoneState();
+ mPhone.notifyPreciseCallStateChanged();
+ }
+ }
+
+ private int getDisconnectCauseFromReasonInfo(ImsReasonInfo reasonInfo) {
+ int cause = DisconnectCause.ERROR_UNSPECIFIED;
+
+ //int type = reasonInfo.getReasonType();
+ int code = reasonInfo.getCode();
+ switch (code) {
+ case ImsReasonInfo.CODE_SIP_BAD_ADDRESS:
+ case ImsReasonInfo.CODE_SIP_NOT_REACHABLE:
+ return DisconnectCause.NUMBER_UNREACHABLE;
+
+ case ImsReasonInfo.CODE_SIP_BUSY:
+ return DisconnectCause.BUSY;
+
+ case ImsReasonInfo.CODE_USER_TERMINATED:
+ return DisconnectCause.LOCAL;
+
+ case ImsReasonInfo.CODE_USER_TERMINATED_BY_REMOTE:
+ return DisconnectCause.NORMAL;
+
+ case ImsReasonInfo.CODE_SIP_REDIRECTED:
+ case ImsReasonInfo.CODE_SIP_BAD_REQUEST:
+ case ImsReasonInfo.CODE_SIP_FORBIDDEN:
+ case ImsReasonInfo.CODE_SIP_NOT_ACCEPTABLE:
+ case ImsReasonInfo.CODE_SIP_USER_REJECTED:
+ case ImsReasonInfo.CODE_SIP_GLOBAL_ERROR:
+ return DisconnectCause.SERVER_ERROR;
+
+ case ImsReasonInfo.CODE_SIP_SERVICE_UNAVAILABLE:
+ case ImsReasonInfo.CODE_SIP_NOT_FOUND:
+ case ImsReasonInfo.CODE_SIP_SERVER_ERROR:
+ return DisconnectCause.SERVER_UNREACHABLE;
+
+ case ImsReasonInfo.CODE_LOCAL_NETWORK_ROAMING:
+ case ImsReasonInfo.CODE_LOCAL_NETWORK_IP_CHANGED:
+ case ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN:
+ case ImsReasonInfo.CODE_LOCAL_SERVICE_UNAVAILABLE:
+ case ImsReasonInfo.CODE_LOCAL_NOT_REGISTERED:
+ case ImsReasonInfo.CODE_LOCAL_NETWORK_NO_LTE_COVERAGE:
+ case ImsReasonInfo.CODE_LOCAL_NETWORK_NO_SERVICE:
+ case ImsReasonInfo.CODE_LOCAL_CALL_VCC_ON_PROGRESSING:
+ return DisconnectCause.OUT_OF_SERVICE;
+
+ case ImsReasonInfo.CODE_SIP_REQUEST_TIMEOUT:
+ case ImsReasonInfo.CODE_TIMEOUT_1XX_WAITING:
+ case ImsReasonInfo.CODE_TIMEOUT_NO_ANSWER:
+ case ImsReasonInfo.CODE_TIMEOUT_NO_ANSWER_CALL_UPDATE:
+ return DisconnectCause.TIMED_OUT;
+
+ case ImsReasonInfo.CODE_LOCAL_LOW_BATTERY:
+ case ImsReasonInfo.CODE_LOCAL_POWER_OFF:
+ return DisconnectCause.POWER_OFF;
+
+ default:
+ }
+
+ return cause;
+ }
+
+ /**
+ * Listen to the IMS call state change
+ */
+ private ImsCall.Listener mImsCallListener = new ImsCall.Listener() {
+ @Override
+ public void onCallProgressing(ImsCall imsCall) {
+ if (DBG) log("onCallProgressing");
+
+ mPendingMO = null;
+ processCallStateChange(imsCall, ImsPhoneCall.State.ALERTING,
+ DisconnectCause.NOT_DISCONNECTED);
+ }
+
+ @Override
+ public void onCallStarted(ImsCall imsCall) {
+ if (DBG) log("onCallStarted");
+
+ mPendingMO = null;
+ processCallStateChange(imsCall, ImsPhoneCall.State.ACTIVE,
+ DisconnectCause.NOT_DISCONNECTED);
+ }
+
+ /**
+ * onCallStartFailed will be invoked when:
+ * case 1) Dialing fails
+ * case 2) Ringing call is disconnected by local or remote user
+ */
+ @Override
+ public void onCallStartFailed(ImsCall imsCall, ImsReasonInfo reasonInfo) {
+ if (DBG) log("onCallStartFailed reasonCode=" + reasonInfo.getCode());
+
+ if (mPendingMO != null) {
+ // To initiate dialing circuit-switched call
+ if (reasonInfo.getCode() == ImsReasonInfo.CODE_LOCAL_CALL_CS_RETRY_REQUIRED
+ && mBackgroundCall.getState() == ImsPhoneCall.State.IDLE
+ && mRingingCall.getState() == ImsPhoneCall.State.IDLE) {
+ mForegroundCall.detach(mPendingMO);
+ removeConnection(mPendingMO);
+ mPendingMO.finalize();
+ mPendingMO = null;
+ mPhone.initiateSilentRedial();
+ return;
+ }
+ mPendingMO = null;
+ }
+ onCallTerminated(imsCall, reasonInfo);
+ }
+
+ @Override
+ public void onCallTerminated(ImsCall imsCall, ImsReasonInfo reasonInfo) {
+ if (DBG) log("onCallTerminated reasonCode=" + reasonInfo.getCode());
+
+ ImsPhoneCall.State oldState = mForegroundCall.getState();
+
+ processCallStateChange(imsCall, ImsPhoneCall.State.DISCONNECTED,
+ getDisconnectCauseFromReasonInfo(reasonInfo));
+
+ if (reasonInfo.getCode() == ImsReasonInfo.CODE_USER_TERMINATED) {
+ if ((oldState == ImsPhoneCall.State.DISCONNECTING)
+ && (mForegroundCall.getState() == ImsPhoneCall.State.DISCONNECTED)
+ && (mBackgroundCall.getState() == ImsPhoneCall.State.HOLDING)) {
+ sendEmptyMessage(EVENT_RESUME_BACKGROUND);
+ }
+ }
+ }
+
+ @Override
+ public void onCallHeld(ImsCall imsCall) {
+ if (DBG) log("onCallHeld");
+
+ synchronized (mSyncHold) {
+ ImsPhoneCall.State oldState = mBackgroundCall.getState();
+ processCallStateChange(imsCall, ImsPhoneCall.State.HOLDING,
+ DisconnectCause.NOT_DISCONNECTED);
+
+ if (oldState == ImsPhoneCall.State.ACTIVE) {
+ if ((mForegroundCall.getState() == ImsPhoneCall.State.HOLDING)
+ || (mRingingCall.getState() == ImsPhoneCall.State.WAITING)) {
+ sendEmptyMessage(EVENT_RESUME_BACKGROUND);
+ } else {
+ //when multiple connections belong to background call,
+ //only the first callback reaches here
+ //otherwise the oldState is already HOLDING
+ if (mPendingMO != null) {
+ sendEmptyMessage(EVENT_DIAL_PENDINGMO);
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onCallHoldFailed(ImsCall imsCall, ImsReasonInfo reasonInfo) {
+ if (DBG) log("onCallHoldFailed reasonCode=" + reasonInfo.getCode());
+
+ synchronized (mSyncHold) {
+ ImsPhoneCall.State bgState = mBackgroundCall.getState();
+ if (reasonInfo.getCode() == ImsReasonInfo.CODE_LOCAL_CALL_TERMINATED) {
+ // disconnected while processing hold
+ if (mPendingMO != null) {
+ sendEmptyMessage(EVENT_DIAL_PENDINGMO);
+ }
+ } else if (bgState == ImsPhoneCall.State.ACTIVE) {
+ mForegroundCall.switchWith(mBackgroundCall);
+
+ if (mPendingMO != null) {
+ mPendingMO.setDisconnectCause(DisconnectCause.ERROR_UNSPECIFIED);
+ sendEmptyMessageDelayed(EVENT_HANGUP_PENDINGMO, TIMEOUT_HANGUP_PENDINGMO);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onCallResumed(ImsCall imsCall) {
+ if (DBG) log("onCallResumed");
+
+ processCallStateChange(imsCall, ImsPhoneCall.State.ACTIVE,
+ DisconnectCause.NOT_DISCONNECTED);
+ }
+
+ @Override
+ public void onCallResumeFailed(ImsCall imsCall, ImsReasonInfo reasonInfo) {
+ // TODO : What should be done?
+ }
+
+ @Override
+ public void onCallResumeReceived(ImsCall imsCall) {
+ if (DBG) log("onCallResumeReceived");
+
+ if (mOnHoldToneStarted) {
+ mPhone.stopOnHoldTone();
+ mOnHoldToneStarted = false;
+ }
+ }
+
+ @Override
+ public void onCallHoldReceived(ImsCall imsCall) {
+ if (DBG) log("onCallHoldReceived");
+
+ ImsPhoneConnection conn = findConnection(imsCall);
+ if (conn != null && conn.getState() == ImsPhoneCall.State.ACTIVE) {
+ if (!mOnHoldToneStarted && ImsPhoneCall.isLocalTone(imsCall)) {
+ mPhone.startOnHoldTone();
+ mOnHoldToneStarted = true;
+ }
+ }
+ }
+
+ @Override
+ public void onCallMerged(ImsCall call, ImsCall newCall) {
+ if (DBG) log("onCallMerged");
+
+ mForegroundCall.merge(mBackgroundCall, mForegroundCall.getState());
+ updatePhoneState();
+ mPhone.notifyPreciseCallStateChanged();
+ }
+
+ @Override
+ public void onCallMergeFailed(ImsCall call, ImsReasonInfo reasonInfo) {
+ // TODO : What should be done?
+ }
+ };
+
+ /**
+ * Listen to the IMS call state change
+ */
+ private ImsCall.Listener mImsUssdListener = new ImsCall.Listener() {
+ @Override
+ public void onCallStarted(ImsCall imsCall) {
+ if (DBG) log("mImsUssdListener onCallStarted");
+
+ if (imsCall == mUssdSession) {
+ if (mPendingUssd != null) {
+ AsyncResult.forMessage(mPendingUssd);
+ mPendingUssd.sendToTarget();
+ mPendingUssd = null;
+ }
+ }
+ }
+
+ @Override
+ public void onCallStartFailed(ImsCall imsCall, ImsReasonInfo reasonInfo) {
+ if (DBG) log("mImsUssdListener onCallStartFailed reasonCode=" + reasonInfo.getCode());
+
+ onCallTerminated(imsCall, reasonInfo);
+ }
+
+ @Override
+ public void onCallTerminated(ImsCall imsCall, ImsReasonInfo reasonInfo) {
+ if (DBG) log("mImsUssdListener onCallTerminated reasonCode=" + reasonInfo.getCode());
+
+ if (imsCall == mUssdSession) {
+ mUssdSession = null;
+ if (mPendingUssd != null) {
+ CommandException ex =
+ new CommandException(CommandException.Error.GENERIC_FAILURE);
+ AsyncResult.forMessage(mPendingUssd, null, ex);
+ mPendingUssd.sendToTarget();
+ mPendingUssd = null;
+ }
+ }
+ imsCall.close();
+ }
+
+ @Override
+ public void onCallUssdMessageReceived(ImsCall call,
+ int mode, String ussdMessage) {
+ if (DBG) log("mImsUssdListener onCallUssdMessageReceived mode=" + mode);
+
+ int ussdMode = -1;
+
+ switch(mode) {
+ case ImsCall.USSD_MODE_REQUEST:
+ ussdMode = CommandsInterface.USSD_MODE_REQUEST;
+ break;
+
+ case ImsCall.USSD_MODE_NOTIFY:
+ ussdMode = CommandsInterface.USSD_MODE_NOTIFY;
+ break;
+ }
+
+ mPhone.onIncomingUSSD(ussdMode, ussdMessage);
+ }
+ };
+
+ /**
+ * Listen to the IMS service state change
+ *
+ */
+ private ImsConnectionStateListener mImsConnectionStateListener =
+ new ImsConnectionStateListener() {
+ @Override
+ public void onImsConnected() {
+ if (DBG) log("onImsConnected");
+ mPhone.setServiceState(ServiceState.STATE_IN_SERVICE);
+ }
+
+ @Override
+ public void onImsDisconnected() {
+ if (DBG) log("onImsDisconnected");
+ mPhone.setServiceState(ServiceState.STATE_OUT_OF_SERVICE);
+ }
+
+ @Override
+ public void onImsResumed() {
+ if (DBG) log("onImsResumed");
+ mPhone.setServiceState(ServiceState.STATE_IN_SERVICE);
+ }
+
+ @Override
+ public void onImsSuspended() {
+ if (DBG) log("onImsSuspended");
+ mPhone.setServiceState(ServiceState.STATE_OUT_OF_SERVICE);
+ }
+ };
+
+ /* package */
+ ImsUtInterface getUtInterface() throws ImsException {
+ if (mImsManager == null) {
+ throw new ImsException("no ims manager", ImsReasonInfo.CODE_UNSPECIFIED);
+ }
+
+ ImsUtInterface ut = mImsManager.getSupplementaryServiceConfiguration(mServiceId);
+ return ut;
+ }
+
+ /* package */
+ void notifySrvccState(Call.SrvccState state) {
+ if (DBG) log("notifySrvccState state=" + state);
+
+ mSrvccState = state;
+
+ if (mSrvccState == Call.SrvccState.COMPLETED) {
+ if (mForegroundCall.getConnections().size() > 0) {
+ mHandoverCall.switchWith(mForegroundCall);
+ } else if (mBackgroundCall.getConnections().size() > 0) {
+ mHandoverCall.switchWith(mBackgroundCall);
+ }
+ }
+ }
+
+ //****** Overridden from Handler
+
+ @Override
+ public void
+ handleMessage (Message msg) {
+ AsyncResult ar;
+ if (DBG) log("handleMessage what=" + msg.what);
+
+ switch (msg.what) {
+ case EVENT_HANGUP_PENDINGMO:
+ if (mPendingMO != null) {
+ mPendingMO.onDisconnect();
+ removeConnection(mPendingMO);
+ mPendingMO = null;
+ }
+
+ updatePhoneState();
+ mPhone.notifyPreciseCallStateChanged();
+ break;
+ case EVENT_RESUME_BACKGROUND:
+ try {
+ resumeWaitingOrHolding();
+ } catch (CallStateException e) {
+ if (Phone.DEBUG_PHONE) {
+ loge("handleMessage EVENT_RESUME_BACKGROUND exception=" + e);
+ }
+ }
+ break;
+ case EVENT_DIAL_PENDINGMO:
+ dialInternal(mPendingMO, mClirMode);
+ break;
+ }
+ }
+
+ @Override
+ protected void log(String msg) {
+ Rlog.d(LOG_TAG, "[ImsPhoneCallTracker] " + msg);
+ }
+
+ protected void loge(String msg) {
+ Rlog.e(LOG_TAG, "[ImsPhoneCallTracker] " + msg);
+ }
+
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("ImsPhoneCallTracker extends:");
+ super.dump(fd, pw, args);
+ pw.println(" mVoiceCallEndedRegistrants=" + mVoiceCallEndedRegistrants);
+ pw.println(" mVoiceCallStartedRegistrants=" + mVoiceCallStartedRegistrants);
+ pw.println(" mRingingCall=" + mRingingCall);
+ pw.println(" mForegroundCall=" + mForegroundCall);
+ pw.println(" mBackgroundCall=" + mBackgroundCall);
+ pw.println(" mHandoverCall=" + mHandoverCall);
+ pw.println(" mPendingMO=" + mPendingMO);
+ //pw.println(" mHangupPendingMO=" + mHangupPendingMO);
+ pw.println(" mPhone=" + mPhone);
+ pw.println(" mDesiredMute=" + mDesiredMute);
+ pw.println(" mState=" + mState);
+ }
+
+ @Override
+ protected void handlePollCalls(AsyncResult ar) {
+ }
+}
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCommandInterface.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCommandInterface.java
new file mode 100644
index 0000000..d59d4a1
--- /dev/null
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCommandInterface.java
@@ -0,0 +1,589 @@
+/*
+ * Copyright (C) 2013 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.imsphone;
+
+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.cdma.CdmaSmsBroadcastConfigInfo;
+import com.android.internal.telephony.gsm.SmsBroadcastConfigInfo;
+
+/**
+ * Volte doesn't need CommandsInterface. The class does nothing but made to work
+ * with PhoneBase's constructor.
+ */
+class ImsPhoneCommandInterface extends BaseCommands implements CommandsInterface {
+ ImsPhoneCommandInterface(Context context) {
+ super(context);
+ }
+
+ @Override public void setOnNITZTime(Handler h, int what, Object obj) {
+ }
+
+ @Override
+ public void getIccCardStatus(Message result) {
+ }
+
+ @Override
+ public void supplyIccPin(String pin, Message result) {
+ }
+
+ @Override
+ public void supplyIccPuk(String puk, String newPin, Message result) {
+ }
+
+ @Override
+ public void supplyIccPin2(String pin, Message result) {
+ }
+
+ @Override
+ public void supplyIccPuk2(String puk, String newPin2, Message result) {
+ }
+
+ @Override
+ public void changeIccPin(String oldPin, String newPin, Message result) {
+ }
+
+ @Override
+ public void changeIccPin2(String oldPin2, String newPin2, Message result) {
+ }
+
+ @Override
+ public void changeBarringPassword(String facility, String oldPwd,
+ String newPwd, Message result) {
+ }
+
+ @Override
+ public void supplyNetworkDepersonalization(String netpin, Message result) {
+ }
+
+ @Override
+ public void getCurrentCalls(Message result) {
+ }
+
+ @Override
+ @Deprecated public void getPDPContextList(Message result) {
+ }
+
+ @Override
+ public void getDataCallList(Message result) {
+ }
+
+ @Override
+ public void dial(String address, int clirMode, Message result) {
+ }
+
+ @Override
+ public void dial(String address, int clirMode, UUSInfo uusInfo,
+ Message result) {
+ }
+
+ @Override
+ public void getIMSI(Message result) {
+ }
+
+ @Override
+ public void getIMSIForApp(String aid, Message result) {
+ }
+
+ @Override
+ public void getIMEI(Message result) {
+ }
+
+ @Override
+ public void getIMEISV(Message result) {
+ }
+
+ @Override
+ public void hangupConnection (int gsmIndex, Message result) {
+ }
+
+ @Override
+ public void hangupWaitingOrBackground (Message result) {
+ }
+
+ @Override
+ public void hangupForegroundResumeBackground (Message result) {
+ }
+
+ @Override
+ public void switchWaitingOrHoldingAndActive (Message result) {
+ }
+
+ @Override
+ public void conference (Message result) {
+ }
+
+ @Override
+ public void setPreferredVoicePrivacy(boolean enable, Message result) {
+ }
+
+ @Override
+ public void getPreferredVoicePrivacy(Message result) {
+ }
+
+ @Override
+ public void separateConnection (int gsmIndex, Message result) {
+ }
+
+ @Override
+ public void acceptCall (Message result) {
+ }
+
+ @Override
+ public void rejectCall (Message result) {
+ }
+
+ @Override
+ public void explicitCallTransfer (Message result) {
+ }
+
+ @Override
+ public void getLastCallFailCause (Message result) {
+ }
+
+ @Deprecated
+ @Override
+ public void getLastPdpFailCause (Message result) {
+ }
+
+ @Override
+ public void getLastDataCallFailCause (Message result) {
+ }
+
+ @Override
+ public void setMute (boolean enableMute, Message response) {
+ }
+
+ @Override
+ public void getMute (Message response) {
+ }
+
+ @Override
+ public void getSignalStrength (Message result) {
+ }
+
+ @Override
+ public void getVoiceRegistrationState (Message result) {
+ }
+
+ @Override
+ public void getDataRegistrationState (Message result) {
+ }
+
+ @Override
+ public void getOperator(Message result) {
+ }
+
+ @Override
+ public void sendDtmf(char c, Message result) {
+ }
+
+ @Override
+ public void startDtmf(char c, Message result) {
+ }
+
+ @Override
+ public void stopDtmf(Message result) {
+ }
+
+ @Override
+ public void sendBurstDtmf(String dtmfString, int on, int off,
+ Message result) {
+ }
+
+ @Override
+ public void sendSMS (String smscPDU, String pdu, Message result) {
+ }
+
+ @Override
+ public void sendCdmaSms(byte[] pdu, Message result) {
+ }
+
+ @Override
+ public void sendImsGsmSms (String smscPDU, String pdu,
+ int retry, int messageRef, Message response) {
+ }
+
+ @Override
+ public void sendImsCdmaSms(byte[] pdu, int retry, int messageRef,
+ Message response) {
+ }
+
+ @Override
+ public void getImsRegistrationState (Message result) {
+ }
+
+ @Override
+ public void deleteSmsOnSim(int index, Message response) {
+ }
+
+ @Override
+ public void deleteSmsOnRuim(int index, Message response) {
+ }
+
+ @Override
+ public void writeSmsToSim(int status, String smsc, String pdu, Message response) {
+ }
+
+ @Override
+ public void writeSmsToRuim(int status, String pdu, Message response) {
+ }
+
+ @Override
+ public void setupDataCall(String radioTechnology, String profile,
+ String apn, String user, String password, String authType,
+ String protocol, Message result) {
+ }
+
+ @Override
+ public void deactivateDataCall(int cid, int reason, Message result) {
+ }
+
+ @Override
+ public void setRadioPower(boolean on, Message result) {
+ }
+
+ @Override
+ public void setSuppServiceNotifications(boolean enable, Message result) {
+ }
+
+ @Override
+ public void acknowledgeLastIncomingGsmSms(boolean success, int cause,
+ Message result) {
+ }
+
+ @Override
+ public void acknowledgeLastIncomingCdmaSms(boolean success, int cause,
+ Message result) {
+ }
+
+ @Override
+ public void acknowledgeIncomingGsmSmsWithPdu(boolean success, String ackPdu,
+ Message result) {
+ }
+
+ @Override
+ public void iccIO (int command, int fileid, String path, int p1, int p2,
+ int p3, String data, String pin2, Message result) {
+ }
+ @Override
+ public void iccIOForApp (int command, int fileid, String path, int p1, int p2,
+ int p3, String data, String pin2, String aid, Message result) {
+ }
+
+ @Override
+ public void getCLIR(Message result) {
+ }
+
+ @Override
+ public void setCLIR(int clirMode, Message result) {
+ }
+
+ @Override
+ public void queryCallWaiting(int serviceClass, Message response) {
+ }
+
+ @Override
+ public void setCallWaiting(boolean enable, int serviceClass,
+ Message response) {
+ }
+
+ @Override
+ public void setNetworkSelectionModeAutomatic(Message response) {
+ }
+
+ @Override
+ public void setNetworkSelectionModeManual(
+ String operatorNumeric, Message response) {
+ }
+
+ @Override
+ public void getNetworkSelectionMode(Message response) {
+ }
+
+ @Override
+ public void getAvailableNetworks(Message response) {
+ }
+
+ @Override
+ public void setCallForward(int action, int cfReason, int serviceClass,
+ String number, int timeSeconds, Message response) {
+ }
+
+ @Override
+ public void queryCallForwardStatus(int cfReason, int serviceClass,
+ String number, Message response) {
+ }
+
+ @Override
+ public void queryCLIP(Message response) {
+ }
+
+ @Override
+ public void getBasebandVersion (Message response) {
+ }
+
+ @Override
+ public void queryFacilityLock(String facility, String password,
+ int serviceClass, Message response) {
+ }
+
+ @Override
+ public void queryFacilityLockForApp(String facility, String password,
+ int serviceClass, String appId, Message response) {
+ }
+
+ @Override
+ public void setFacilityLock(String facility, boolean lockState,
+ String password, int serviceClass, Message response) {
+ }
+
+ @Override
+ public void setFacilityLockForApp(String facility, boolean lockState,
+ String password, int serviceClass, String appId, Message response) {
+ }
+
+ @Override
+ public void sendUSSD (String ussdString, Message response) {
+ }
+
+ @Override
+ public void cancelPendingUssd (Message response) {
+ }
+
+ @Override
+ public void resetRadio(Message result) {
+ }
+
+ @Override
+ public void invokeOemRilRequestRaw(byte[] data, Message response) {
+ }
+
+ @Override
+ public void invokeOemRilRequestStrings(String[] strings, Message response) {
+ }
+
+ @Override
+ public void setBandMode (int bandMode, Message response) {
+ }
+
+ @Override
+ public void queryAvailableBandMode (Message response) {
+ }
+
+ @Override
+ public void sendTerminalResponse(String contents, Message response) {
+ }
+
+ @Override
+ public void sendEnvelope(String contents, Message response) {
+ }
+
+ @Override
+ public void sendEnvelopeWithStatus(String contents, Message response) {
+ }
+
+ @Override
+ public void handleCallSetupRequestFromSim(
+ boolean accept, Message response) {
+ }
+
+ @Override
+ public void setPreferredNetworkType(int networkType , Message response) {
+ }
+
+ @Override
+ public void getPreferredNetworkType(Message response) {
+ }
+
+ @Override
+ public void getNeighboringCids(Message response) {
+ }
+
+ @Override
+ public void setLocationUpdates(boolean enable, Message response) {
+ }
+
+ @Override
+ public void getSmscAddress(Message result) {
+ }
+
+ @Override
+ public void setSmscAddress(String address, Message result) {
+ }
+
+ @Override
+ public void reportSmsMemoryStatus(boolean available, Message result) {
+ }
+
+ @Override
+ public void reportStkServiceIsRunning(Message result) {
+ }
+
+ @Override
+ public void getCdmaSubscriptionSource(Message response) {
+ }
+
+ @Override
+ public void getGsmBroadcastConfig(Message response) {
+ }
+
+ @Override
+ public void setGsmBroadcastConfig(SmsBroadcastConfigInfo[] config, Message response) {
+ }
+
+ @Override
+ public void setGsmBroadcastActivation(boolean activate, Message response) {
+ }
+
+ // ***** Methods for CDMA support
+ @Override
+ public void getDeviceIdentity(Message response) {
+ }
+
+ @Override
+ public void getCDMASubscription(Message response) {
+ }
+
+ @Override
+ public void setPhoneType(int phoneType) { //Set by CDMAPhone and GSMPhone constructor
+ }
+
+ @Override
+ public void queryCdmaRoamingPreference(Message response) {
+ }
+
+ @Override
+ public void setCdmaRoamingPreference(int cdmaRoamingType, Message response) {
+ }
+
+ @Override
+ public void setCdmaSubscriptionSource(int cdmaSubscription , Message response) {
+ }
+
+ @Override
+ public void queryTTYMode(Message response) {
+ }
+
+ @Override
+ public void setTTYMode(int ttyMode, Message response) {
+ }
+
+ @Override
+ public void sendCDMAFeatureCode(String FeatureCode, Message response) {
+ }
+
+ @Override
+ public void getCdmaBroadcastConfig(Message response) {
+ }
+
+ @Override
+ public void setCdmaBroadcastConfig(CdmaSmsBroadcastConfigInfo[] configs, Message response) {
+ }
+
+ @Override
+ public void setCdmaBroadcastActivation(boolean activate, Message response) {
+ }
+
+ @Override
+ public void exitEmergencyCallbackMode(Message response) {
+ }
+
+ @Override
+ public void supplyIccPinForApp(String pin, String aid, Message response) {
+ }
+
+ @Override
+ public void supplyIccPukForApp(String puk, String newPin, String aid, Message response) {
+ }
+
+ @Override
+ public void supplyIccPin2ForApp(String pin2, String aid, Message response) {
+ }
+
+ @Override
+ public void supplyIccPuk2ForApp(String puk2, String newPin2, String aid, Message response) {
+ }
+
+ @Override
+ public void changeIccPinForApp(String oldPin, String newPin, String aidPtr, Message response) {
+ }
+
+ @Override
+ public void changeIccPin2ForApp(String oldPin2, String newPin2, String aidPtr,
+ Message response) {
+ }
+
+ @Override
+ public void requestIsimAuthentication(String nonce, Message response) {
+ }
+
+ @Override
+ public void requestIccSimAuthentication(String data, Message response) {
+ }
+
+ @Override
+ public void getVoiceRadioTechnology(Message result) {
+ }
+
+ @Override
+ public void getCellInfoList(Message result) {
+ }
+
+ @Override
+ public void setCellInfoListRate(int rateInMillis, Message response) {
+ }
+
+ @Override
+ public void setInitialAttachApn(String apn, String protocol, int authType, String username,
+ String password, Message result) {
+ }
+
+ @Override
+ public void iccOpenLogicalChannel(String AID, Message response) {}
+
+ @Override
+ public void iccCloseLogicalChannel(int channel, Message response) {}
+
+ @Override
+ public void iccTransmitApduLogicalChannel(int channel, int cla, int instruction,
+ int p1, int p2, int p3, String data,
+ Message response) {}
+ @Override
+ public void iccTransmitApduBasicChannel(int cla, int instruction, int p1, int p2,
+ int p3, String data, Message response) {}
+
+ @Override
+ public void nvReadItem(int itemID, Message response) {}
+
+ @Override
+ public void nvWriteItem(int itemID, String itemValue, Message response) {}
+
+ @Override
+ public void nvWriteCdmaPrl(byte[] preferredRoamingList, Message response) {}
+
+ @Override
+ public void nvResetConfig(int resetType, Message response) {}
+
+ @Override
+ public void getHardwareConfig(Message result) {}
+}
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java
new file mode 100644
index 0000000..77ae57d
--- /dev/null
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java
@@ -0,0 +1,643 @@
+/*
+ * Copyright (C) 2013 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.imsphone;
+
+import android.content.Context;
+import android.os.AsyncResult;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.PowerManager;
+import android.os.Registrant;
+import android.os.SystemClock;
+import android.telephony.DisconnectCause;
+import android.telephony.PhoneNumberUtils;
+import android.telephony.Rlog;
+
+import com.android.internal.telephony.CallStateException;
+import com.android.internal.telephony.Connection;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.telephony.UUSInfo;
+
+import com.android.ims.ImsCall;
+import com.android.ims.ImsCallProfile;
+
+/**
+ * {@hide}
+ */
+public class ImsPhoneConnection extends Connection {
+ private static final String LOG_TAG = "ImsPhoneConnection";
+ private static final boolean DBG = true;
+
+ //***** Instance Variables
+
+ private ImsPhoneCallTracker mOwner;
+ private ImsPhoneCall mParent;
+ private ImsCall mImsCall;
+
+ private String mAddress; // MAY BE NULL!!!
+ private String mDialString; // outgoing calls only
+ private String mPostDialString; // outgoing calls only
+ private boolean mIsIncoming;
+ private boolean mDisconnected;
+
+ /*
+ int mIndex; // index in ImsPhoneCallTracker.connections[], -1 if unassigned
+ // The GSM index is 1 + this
+ */
+
+ /*
+ * These time/timespan values are based on System.currentTimeMillis(),
+ * i.e., "wall clock" time.
+ */
+ private long mCreateTime;
+ private long mConnectTime;
+ private long mDisconnectTime;
+
+ /*
+ * These time/timespan values are based on SystemClock.elapsedRealTime(),
+ * i.e., time since boot. They are appropriate for comparison and
+ * calculating deltas.
+ */
+ private long mConnectTimeReal;
+ private long mDuration;
+ private long mHoldingStartTime; // The time when the Connection last transitioned
+ // into HOLDING
+
+ private int mNextPostDialChar; // index into postDialString
+
+ private int mCause = DisconnectCause.NOT_DISCONNECTED;
+ private PostDialState mPostDialState = PostDialState.NOT_STARTED;
+ private int mNumberPresentation = PhoneConstants.PRESENTATION_ALLOWED;
+ private UUSInfo mUusInfo;
+
+ private boolean mIsMultiparty = false;
+
+ private Handler mHandler;
+
+ private PowerManager.WakeLock mPartialWakeLock;
+
+ //***** Event Constants
+ private static final int EVENT_DTMF_DONE = 1;
+ private static final int EVENT_PAUSE_DONE = 2;
+ private static final int EVENT_NEXT_POST_DIAL = 3;
+ private static final int EVENT_WAKE_LOCK_TIMEOUT = 4;
+
+ //***** Constants
+ private static final int PAUSE_DELAY_MILLIS = 3 * 1000;
+ private static final int WAKE_LOCK_TIMEOUT_MILLIS = 60*1000;
+
+ //***** Inner Classes
+
+ class MyHandler extends Handler {
+ MyHandler(Looper l) {super(l);}
+
+ @Override
+ public void
+ handleMessage(Message msg) {
+
+ switch (msg.what) {
+ case EVENT_NEXT_POST_DIAL:
+ case EVENT_DTMF_DONE:
+ case EVENT_PAUSE_DONE:
+ processNextPostDialChar();
+ break;
+ case EVENT_WAKE_LOCK_TIMEOUT:
+ releaseWakeLock();
+ break;
+ }
+ }
+ }
+
+ //***** Constructors
+
+ /** This is probably an MT call */
+ /*package*/
+ ImsPhoneConnection(Context context, ImsCall imsCall, ImsPhoneCallTracker ct, ImsPhoneCall parent) {
+ createWakeLock(context);
+ acquireWakeLock();
+
+ mOwner = ct;
+ mHandler = new MyHandler(mOwner.getLooper());
+ mImsCall = imsCall;
+
+ if ((imsCall != null) && (imsCall.getCallProfile() != null)) {
+ mAddress = imsCall.getCallProfile().getCallExtra(ImsCallProfile.EXTRA_OI);
+ mCnapName = imsCall.getCallProfile().getCallExtra(ImsCallProfile.EXTRA_CNA);
+ mNumberPresentation = presentationFromOir(
+ imsCall.getCallProfile().getCallExtraInt(ImsCallProfile.EXTRA_OIR));
+ mCnapNamePresentation = presentationFromOir(
+ imsCall.getCallProfile().getCallExtraInt(ImsCallProfile.EXTRA_CNAP));
+ } else {
+ mNumberPresentation = PhoneConstants.PRESENTATION_UNKNOWN;
+ mCnapNamePresentation = PhoneConstants.PRESENTATION_UNKNOWN;
+ }
+
+ mIsIncoming = true;
+ mCreateTime = System.currentTimeMillis();
+ mUusInfo = null;
+
+ //mIndex = index;
+
+ mParent = parent;
+ mParent.attach(this, ImsPhoneCall.State.INCOMING);
+ }
+
+ /** This is an MO call, created when dialing */
+ /*package*/
+ ImsPhoneConnection(Context context, String dialString, ImsPhoneCallTracker ct, ImsPhoneCall parent) {
+ createWakeLock(context);
+ acquireWakeLock();
+
+ mOwner = ct;
+ mHandler = new MyHandler(mOwner.getLooper());
+
+ mDialString = dialString;
+
+ mAddress = PhoneNumberUtils.extractNetworkPortionAlt(dialString);
+ mPostDialString = PhoneNumberUtils.extractPostDialPortion(dialString);
+
+ //mIndex = -1;
+
+ mIsIncoming = false;
+ mCnapName = null;
+ mCnapNamePresentation = PhoneConstants.PRESENTATION_ALLOWED;
+ mNumberPresentation = PhoneConstants.PRESENTATION_ALLOWED;
+ mCreateTime = System.currentTimeMillis();
+
+ mParent = parent;
+ parent.attachFake(this, ImsPhoneCall.State.DIALING);
+ }
+
+ public void dispose() {
+ }
+
+ static boolean
+ equalsHandlesNulls (Object a, Object b) {
+ return (a == null) ? (b == null) : a.equals (b);
+ }
+
+ @Override
+ public String getOrigDialString(){
+ return mDialString;
+ }
+
+ @Override
+ public String getAddress() {
+ return mAddress;
+ }
+
+ @Override
+ public ImsPhoneCall getCall() {
+ return mParent;
+ }
+
+ @Override
+ public long getCreateTime() {
+ return mCreateTime;
+ }
+
+ @Override
+ public long getConnectTime() {
+ return mConnectTime;
+ }
+
+ @Override
+ public long getConnectTimeReal() {
+ return mConnectTimeReal;
+ }
+
+ @Override
+ public long getDisconnectTime() {
+ return mDisconnectTime;
+ }
+
+ @Override
+ public long getDurationMillis() {
+ if (mConnectTimeReal == 0) {
+ return 0;
+ } else if (mDuration == 0) {
+ return SystemClock.elapsedRealtime() - mConnectTimeReal;
+ } else {
+ return mDuration;
+ }
+ }
+
+ @Override
+ public long getHoldingStartTime() {
+ return mHoldingStartTime;
+ }
+
+ @Override
+ public long getHoldDurationMillis() {
+ if (getState() != ImsPhoneCall.State.HOLDING) {
+ // If not holding, return 0
+ return 0;
+ } else {
+ return SystemClock.elapsedRealtime() - mHoldingStartTime;
+ }
+ }
+
+ @Override
+ public int getDisconnectCause() {
+ return mCause;
+ }
+
+ public void setDisconnectCause(int cause) {
+ mCause = cause;
+ }
+
+ public ImsPhoneCallTracker getOwner () {
+ return mOwner;
+ }
+
+ @Override
+ public boolean isIncoming() {
+ return mIsIncoming;
+ }
+
+ @Override
+ public ImsPhoneCall.State getState() {
+ if (mDisconnected) {
+ return ImsPhoneCall.State.DISCONNECTED;
+ } else {
+ return super.getState();
+ }
+ }
+
+ @Override
+ public void hangup() throws CallStateException {
+ if (!mDisconnected) {
+ mOwner.hangup(this);
+ } else {
+ throw new CallStateException ("disconnected");
+ }
+ }
+
+ @Override
+ public void separate() throws CallStateException {
+ throw new CallStateException ("not supported");
+ }
+
+ @Override
+ public PostDialState getPostDialState() {
+ return mPostDialState;
+ }
+
+ @Override
+ public void proceedAfterWaitChar() {
+ if (mPostDialState != PostDialState.WAIT) {
+ Rlog.w(LOG_TAG, "ImsPhoneConnection.proceedAfterWaitChar(): Expected "
+ + "getPostDialState() to be WAIT but was " + mPostDialState);
+ return;
+ }
+
+ setPostDialState(PostDialState.STARTED);
+
+ processNextPostDialChar();
+ }
+
+ @Override
+ public void proceedAfterWildChar(String str) {
+ if (mPostDialState != PostDialState.WILD) {
+ Rlog.w(LOG_TAG, "ImsPhoneConnection.proceedAfterWaitChar(): Expected "
+ + "getPostDialState() to be WILD but was " + mPostDialState);
+ return;
+ }
+
+ setPostDialState(PostDialState.STARTED);
+
+ // make a new postDialString, with the wild char replacement string
+ // at the beginning, followed by the remaining postDialString.
+
+ StringBuilder buf = new StringBuilder(str);
+ buf.append(mPostDialString.substring(mNextPostDialChar));
+ mPostDialString = buf.toString();
+ mNextPostDialChar = 0;
+ if (Phone.DEBUG_PHONE) {
+ Rlog.d(LOG_TAG, "proceedAfterWildChar: new postDialString is " +
+ mPostDialString);
+ }
+
+ processNextPostDialChar();
+ }
+
+ @Override
+ public void cancelPostDial() {
+ setPostDialState(PostDialState.CANCELLED);
+ }
+
+ /**
+ * Called when this Connection is being hung up locally (eg, user pressed "end")
+ */
+ void
+ onHangupLocal() {
+ mCause = DisconnectCause.LOCAL;
+ }
+
+ /** Called when the connection has been disconnected */
+ /*package*/ boolean
+ onDisconnect(int cause) {
+ Rlog.d(LOG_TAG, "onDisconnect: cause=" + cause);
+ if (mCause != DisconnectCause.LOCAL) mCause = cause;
+ return onDisconnect();
+ }
+
+ /*package*/ boolean
+ onDisconnect() {
+ boolean changed = false;
+
+ if (!mDisconnected) {
+ //mIndex = -1;
+
+ mDisconnectTime = System.currentTimeMillis();
+ mDuration = SystemClock.elapsedRealtime() - mConnectTimeReal;
+ mDisconnected = true;
+
+ mOwner.mPhone.notifyDisconnect(this);
+
+ if (mParent != null) {
+ changed = mParent.connectionDisconnected(this);
+ } else {
+ Rlog.d(LOG_TAG, "onDisconnect: no parent");
+ }
+ if (mImsCall != null) mImsCall.close();
+ mImsCall = null;
+ }
+ releaseWakeLock();
+ return changed;
+ }
+
+ /**
+ * An incoming or outgoing call has connected
+ */
+ void
+ onConnectedInOrOut() {
+ mConnectTime = System.currentTimeMillis();
+ mConnectTimeReal = SystemClock.elapsedRealtime();
+ mDuration = 0;
+
+ if (Phone.DEBUG_PHONE) {
+ Rlog.d(LOG_TAG, "onConnectedInOrOut: connectTime=" + mConnectTime);
+ }
+
+ if (!mIsIncoming) {
+ // outgoing calls only
+ processNextPostDialChar();
+ }
+ releaseWakeLock();
+ }
+
+ /*package*/ void
+ onStartedHolding() {
+ mHoldingStartTime = SystemClock.elapsedRealtime();
+ }
+ /**
+ * Performs the appropriate action for a post-dial char, but does not
+ * notify application. returns false if the character is invalid and
+ * should be ignored
+ */
+ private boolean
+ processPostDialChar(char c) {
+ if (PhoneNumberUtils.is12Key(c)) {
+ mOwner.mCi.sendDtmf(c, mHandler.obtainMessage(EVENT_DTMF_DONE));
+ } else if (c == PhoneNumberUtils.PAUSE) {
+ // From TS 22.101:
+ // It continues...
+ // Upon the called party answering the UE shall send the DTMF digits
+ // automatically to the network after a delay of 3 seconds( 20 ).
+ // The digits shall be sent according to the procedures and timing
+ // specified in 3GPP TS 24.008 [13]. The first occurrence of the
+ // "DTMF Control Digits Separator" shall be used by the ME to
+ // distinguish between the addressing digits (i.e. the phone number)
+ // and the DTMF digits. Upon subsequent occurrences of the
+ // separator,
+ // the UE shall pause again for 3 seconds ( 20 ) before sending
+ // any further DTMF digits.
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(EVENT_PAUSE_DONE),
+ PAUSE_DELAY_MILLIS);
+ } else if (c == PhoneNumberUtils.WAIT) {
+ setPostDialState(PostDialState.WAIT);
+ } else if (c == PhoneNumberUtils.WILD) {
+ setPostDialState(PostDialState.WILD);
+ } else {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public String
+ getRemainingPostDialString() {
+ if (mPostDialState == PostDialState.CANCELLED
+ || mPostDialState == PostDialState.COMPLETE
+ || mPostDialString == null
+ || mPostDialString.length() <= mNextPostDialChar
+ ) {
+ return "";
+ }
+
+ return mPostDialString.substring(mNextPostDialChar);
+ }
+
+ @Override
+ protected void finalize()
+ {
+ releaseWakeLock();
+ }
+
+ private void
+ processNextPostDialChar() {
+ char c = 0;
+ Registrant postDialHandler;
+
+ if (mPostDialState == PostDialState.CANCELLED) {
+ //Rlog.d(LOG_TAG, "##### processNextPostDialChar: postDialState == CANCELLED, bail");
+ return;
+ }
+
+ if (mPostDialString == null ||
+ mPostDialString.length() <= mNextPostDialChar) {
+ setPostDialState(PostDialState.COMPLETE);
+
+ // notifyMessage.arg1 is 0 on complete
+ c = 0;
+ } else {
+ boolean isValid;
+
+ setPostDialState(PostDialState.STARTED);
+
+ c = mPostDialString.charAt(mNextPostDialChar++);
+
+ isValid = processPostDialChar(c);
+
+ if (!isValid) {
+ // Will call processNextPostDialChar
+ mHandler.obtainMessage(EVENT_NEXT_POST_DIAL).sendToTarget();
+ // Don't notify application
+ Rlog.e(LOG_TAG, "processNextPostDialChar: c=" + c + " isn't valid!");
+ return;
+ }
+ }
+
+ postDialHandler = mOwner.mPhone.mPostDialHandler;
+
+ Message notifyMessage;
+
+ if (postDialHandler != null
+ && (notifyMessage = postDialHandler.messageForRegistrant()) != null) {
+ // The AsyncResult.result is the Connection object
+ PostDialState state = mPostDialState;
+ AsyncResult ar = AsyncResult.forMessage(notifyMessage);
+ ar.result = this;
+ ar.userObj = state;
+
+ // arg1 is the character that was/is being processed
+ notifyMessage.arg1 = c;
+
+ //Rlog.v(LOG_TAG, "##### processNextPostDialChar: send msg to postDialHandler, arg1=" + c);
+ notifyMessage.sendToTarget();
+ }
+ }
+
+ /**
+ * Set post dial state and acquire wake lock while switching to "started"
+ * state, the wake lock will be released if state switches out of "started"
+ * state or after WAKE_LOCK_TIMEOUT_MILLIS.
+ * @param s new PostDialState
+ */
+ private void setPostDialState(PostDialState s) {
+ if (mPostDialState != PostDialState.STARTED
+ && s == PostDialState.STARTED) {
+ acquireWakeLock();
+ Message msg = mHandler.obtainMessage(EVENT_WAKE_LOCK_TIMEOUT);
+ mHandler.sendMessageDelayed(msg, WAKE_LOCK_TIMEOUT_MILLIS);
+ } else if (mPostDialState == PostDialState.STARTED
+ && s != PostDialState.STARTED) {
+ mHandler.removeMessages(EVENT_WAKE_LOCK_TIMEOUT);
+ releaseWakeLock();
+ }
+ mPostDialState = s;
+ }
+
+ private void
+ createWakeLock(Context context) {
+ PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
+ mPartialWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, LOG_TAG);
+ }
+
+ private void
+ acquireWakeLock() {
+ Rlog.d(LOG_TAG, "acquireWakeLock");
+ mPartialWakeLock.acquire();
+ }
+
+ private void
+ releaseWakeLock() {
+ synchronized(mPartialWakeLock) {
+ if (mPartialWakeLock.isHeld()) {
+ Rlog.d(LOG_TAG, "releaseWakeLock");
+ mPartialWakeLock.release();
+ }
+ }
+ }
+
+ @Override
+ public int getNumberPresentation() {
+ return mNumberPresentation;
+ }
+
+ @Override
+ public UUSInfo getUUSInfo() {
+ return mUusInfo;
+ }
+
+ @Override
+ public Connection getOrigConnection() {
+ return null;
+ }
+
+ /* package */ void
+ setMultiparty(boolean isMultiparty) {
+ Rlog.d(LOG_TAG, "setMultiparty " + isMultiparty);
+ mIsMultiparty = isMultiparty;
+ }
+
+ @Override
+ public boolean isMultiparty() {
+ return mIsMultiparty;
+ }
+
+ /*package*/ ImsCall getImsCall() {
+ return mImsCall;
+ }
+
+ /*package*/ void setImsCall(ImsCall imsCall) {
+ mImsCall = imsCall;
+ }
+
+ /*package*/ void changeParent(ImsPhoneCall parent) {
+ mParent = parent;
+ }
+
+ /*package*/ boolean
+ update(ImsCall imsCall, ImsPhoneCall.State state) {
+ boolean changed = false;
+
+ if (state == ImsPhoneCall.State.ACTIVE) {
+ if (mParent.getState().isRinging()
+ || mParent.getState().isDialing()) {
+ onConnectedInOrOut();
+ }
+
+ if (mParent.getState().isRinging()
+ || mParent == mOwner.mBackgroundCall) {
+ //mForegroundCall should be IDLE
+ //when accepting WAITING call
+ //before accept WAITING call,
+ //the ACTIVE call should be held ahead
+ mParent.detach(this);
+ mParent = mOwner.mForegroundCall;
+ mParent.attach(this);
+ }
+ } else if (state == ImsPhoneCall.State.HOLDING) {
+ onStartedHolding();
+ }
+
+ changed = mParent.update(this, imsCall, state);
+
+ return changed;
+ }
+
+ @Override
+ public int getPreciseDisconnectCause() {
+ return 0;
+ }
+
+ private static int presentationFromOir(int oir) {
+ switch(oir) {
+ case ImsCallProfile.OIR_PRESENTATION_RESTRICTED:
+ return PhoneConstants.PRESENTATION_RESTRICTED;
+ case ImsCallProfile.OIR_PRESENTATION_NOT_RESTRICTED:
+ return PhoneConstants.PRESENTATION_ALLOWED;
+ default:
+ return PhoneConstants.PRESENTATION_UNKNOWN;
+ }
+ }
+}
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneFactory.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneFactory.java
new file mode 100644
index 0000000..6bb5ac9
--- /dev/null
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneFactory.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2013 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.imsphone;
+
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneNotifier;
+
+import android.content.Context;
+import android.telephony.Rlog;
+
+/**
+ * {@hide}
+ */
+public class ImsPhoneFactory {
+
+ /**
+ * Makes a {@link ImsPhone} object.
+ * @param context {@code Context} needed to create a Phone object
+ * @param phoneNotifier {@code PhoneNotifier} needed to create a Phone
+ * object
+ * @return the {@code ImsPhone} object
+ */
+ public static Phone makePhone(Context context,
+ PhoneNotifier phoneNotifier, Phone defaultPhone) {
+
+ try {
+ return new ImsPhone(context, phoneNotifier, defaultPhone);
+ } catch (Exception e) {
+ Rlog.e("VoltePhoneFactory", "makePhone", e);
+ return null;
+ }
+ }
+}
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneMmiCode.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneMmiCode.java
new file mode 100644
index 0000000..2f33ece
--- /dev/null
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneMmiCode.java
@@ -0,0 +1,1186 @@
+/*
+ * Copyright (C) 2013 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.imsphone;
+
+import android.content.Context;
+import android.content.res.Resources;
+
+import android.os.AsyncResult;
+import android.os.Handler;
+import android.os.Message;
+import android.telephony.PhoneNumberUtils;
+import android.text.SpannableStringBuilder;
+import android.text.TextUtils;
+import android.telephony.Rlog;
+
+import com.android.internal.telephony.CallForwardInfo;
+import com.android.internal.telephony.CommandException;
+import com.android.internal.telephony.CommandsInterface;
+import com.android.internal.telephony.uicc.IccRecords;
+
+import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_NONE;
+import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_VOICE;
+import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_DATA;
+import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_FAX;
+import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_SMS;
+import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_DATA_SYNC;
+import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_DATA_ASYNC;
+import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_PACKET;
+import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_PAD;
+import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_MAX;
+
+
+import com.android.internal.telephony.MmiCode;
+import com.android.internal.telephony.Phone;
+
+import java.util.regex.Pattern;
+import java.util.regex.Matcher;
+
+/**
+ * The motto for this file is:
+ *
+ * "NOTE: By using the # as a separator, most cases are expected to be unambiguous."
+ * -- TS 22.030 6.5.2
+ *
+ * {@hide}
+ *
+ */
+public final class ImsPhoneMmiCode extends Handler implements MmiCode {
+ static final String LOG_TAG = "ImsPhoneMmiCode";
+
+ //***** Constants
+
+ // Max Size of the Short Code (aka Short String from TS 22.030 6.5.2)
+ private static final int MAX_LENGTH_SHORT_CODE = 2;
+
+ // TS 22.030 6.5.2 Every Short String USSD command will end with #-key
+ // (known as #-String)
+ private static final char END_OF_USSD_COMMAND = '#';
+
+ // From TS 22.030 6.5.2
+ private static final String ACTION_ACTIVATE = "*";
+ private static final String ACTION_DEACTIVATE = "#";
+ private static final String ACTION_INTERROGATE = "*#";
+ private static final String ACTION_REGISTER = "**";
+ private static final String ACTION_ERASURE = "##";
+
+ // Supp Service codes from TS 22.030 Annex B
+
+ //Called line presentation
+ private static final String SC_CLIP = "30";
+ private static final String SC_CLIR = "31";
+
+ // Call Forwarding
+ private static final String SC_CFU = "21";
+ private static final String SC_CFB = "67";
+ private static final String SC_CFNRy = "61";
+ private static final String SC_CFNR = "62";
+
+ private static final String SC_CF_All = "002";
+ private static final String SC_CF_All_Conditional = "004";
+
+ // Call Waiting
+ private static final String SC_WAIT = "43";
+
+ // Call Barring
+ private static final String SC_BAOC = "33";
+ private static final String SC_BAOIC = "331";
+ private static final String SC_BAOICxH = "332";
+ private static final String SC_BAIC = "35";
+ private static final String SC_BAICr = "351";
+
+ private static final String SC_BA_ALL = "330";
+ private static final String SC_BA_MO = "333";
+ private static final String SC_BA_MT = "353";
+
+ // Supp Service Password registration
+ private static final String SC_PWD = "03";
+
+ // PIN/PIN2/PUK/PUK2
+ private static final String SC_PIN = "04";
+ private static final String SC_PIN2 = "042";
+ private static final String SC_PUK = "05";
+ private static final String SC_PUK2 = "052";
+
+ //***** Event Constants
+
+ private static final int EVENT_SET_COMPLETE = 0;
+ private static final int EVENT_QUERY_CF_COMPLETE = 1;
+ private static final int EVENT_USSD_COMPLETE = 2;
+ private static final int EVENT_QUERY_COMPLETE = 3;
+ private static final int EVENT_SET_CFF_COMPLETE = 4;
+ private static final int EVENT_USSD_CANCEL_COMPLETE = 5;
+
+ //***** Instance Variables
+
+ private ImsPhone mPhone;
+ private Context mContext;
+ private IccRecords mIccRecords;
+
+ private String mAction; // One of ACTION_*
+ private String mSc; // Service Code
+ private String mSia, mSib, mSic; // Service Info a,b,c
+ private String mPoundString; // Entire MMI string up to and including #
+ private String mDialingNumber;
+ private String mPwd; // For password registration
+
+ private boolean mIsPendingUSSD;
+
+ private boolean mIsUssdRequest;
+
+ private boolean mIsCallFwdReg;
+ private State mState = State.PENDING;
+ private CharSequence mMessage;
+
+ //***** Class Variables
+
+
+ // See TS 22.030 6.5.2 "Structure of the MMI"
+
+ private static Pattern sPatternSuppService = Pattern.compile(
+ "((\\*|#|\\*#|\\*\\*|##)(\\d{2,3})(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*))?)?)?)?#)(.*)");
+/* 1 2 3 4 5 6 7 8 9 10 11 12
+
+ 1 = Full string up to and including #
+ 2 = action (activation/interrogation/registration/erasure)
+ 3 = service code
+ 5 = SIA
+ 7 = SIB
+ 9 = SIC
+ 10 = dialing number
+*/
+
+ private static final int MATCH_GROUP_POUND_STRING = 1;
+
+ private static final int MATCH_GROUP_ACTION = 2;
+ //(activation/interrogation/registration/erasure)
+
+ private static final int MATCH_GROUP_SERVICE_CODE = 3;
+ private static final int MATCH_GROUP_SIA = 5;
+ private static final int MATCH_GROUP_SIB = 7;
+ private static final int MATCH_GROUP_SIC = 9;
+ private static final int MATCH_GROUP_PWD_CONFIRM = 11;
+ private static final int MATCH_GROUP_DIALING_NUMBER = 12;
+ static private String[] sTwoDigitNumberPattern;
+
+ //***** Public Class methods
+
+ /**
+ * Some dial strings in GSM are defined to do non-call setup
+ * things, such as modify or query supplementary service settings (eg, call
+ * forwarding). These are generally referred to as "MMI codes".
+ * We look to see if the dial string contains a valid MMI code (potentially
+ * with a dial string at the end as well) and return info here.
+ *
+ * If the dial string contains no MMI code, we return an instance with
+ * only "dialingNumber" set
+ *
+ * Please see flow chart in TS 22.030 6.5.3.2
+ */
+
+ static ImsPhoneMmiCode
+ newFromDialString(String dialString, ImsPhone phone) {
+ Matcher m;
+ ImsPhoneMmiCode ret = null;
+
+ m = sPatternSuppService.matcher(dialString);
+
+ // Is this formatted like a standard supplementary service code?
+ if (m.matches()) {
+ ret = new ImsPhoneMmiCode(phone);
+ ret.mPoundString = makeEmptyNull(m.group(MATCH_GROUP_POUND_STRING));
+ ret.mAction = makeEmptyNull(m.group(MATCH_GROUP_ACTION));
+ ret.mSc = makeEmptyNull(m.group(MATCH_GROUP_SERVICE_CODE));
+ ret.mSia = makeEmptyNull(m.group(MATCH_GROUP_SIA));
+ ret.mSib = makeEmptyNull(m.group(MATCH_GROUP_SIB));
+ ret.mSic = makeEmptyNull(m.group(MATCH_GROUP_SIC));
+ ret.mPwd = makeEmptyNull(m.group(MATCH_GROUP_PWD_CONFIRM));
+ ret.mDialingNumber = makeEmptyNull(m.group(MATCH_GROUP_DIALING_NUMBER));
+ // According to TS 22.030 6.5.2 "Structure of the MMI",
+ // the dialing number should not ending with #.
+ // The dialing number ending # is treated as unique USSD,
+ // eg, *400#16 digit number# to recharge the prepaid card
+ // in India operator(Mumbai MTNL)
+ if (ret.mDialingNumber != null &&
+ ret.mDialingNumber.endsWith("#") &&
+ dialString.endsWith("#")){
+ ret = new ImsPhoneMmiCode(phone);
+ ret.mPoundString = dialString;
+ }
+ } else if (dialString.endsWith("#")) {
+ // TS 22.030 sec 6.5.3.2
+ // "Entry of any characters defined in the 3GPP TS 23.038 [8] Default Alphabet
+ // (up to the maximum defined in 3GPP TS 24.080 [10]), followed by #SEND".
+
+ ret = new ImsPhoneMmiCode(phone);
+ ret.mPoundString = dialString;
+ } else if (isTwoDigitShortCode(phone.getContext(), dialString)) {
+ //Is a country-specific exception to short codes as defined in TS 22.030, 6.5.3.2
+ ret = null;
+ } else if (isShortCode(dialString, phone)) {
+ // this may be a short code, as defined in TS 22.030, 6.5.3.2
+ ret = new ImsPhoneMmiCode(phone);
+ ret.mDialingNumber = dialString;
+ }
+
+ return ret;
+ }
+
+ static ImsPhoneMmiCode
+ newNetworkInitiatedUssd (String ussdMessage,
+ boolean isUssdRequest, ImsPhone phone) {
+ ImsPhoneMmiCode ret;
+
+ ret = new ImsPhoneMmiCode(phone);
+
+ ret.mMessage = ussdMessage;
+ ret.mIsUssdRequest = isUssdRequest;
+
+ // If it's a request, set to PENDING so that it's cancelable.
+ if (isUssdRequest) {
+ ret.mIsPendingUSSD = true;
+ ret.mState = State.PENDING;
+ } else {
+ ret.mState = State.COMPLETE;
+ }
+
+ return ret;
+ }
+
+ static ImsPhoneMmiCode newFromUssdUserInput(String ussdMessge,
+ ImsPhone phone) {
+ ImsPhoneMmiCode ret = new ImsPhoneMmiCode(phone);
+
+ ret.mMessage = ussdMessge;
+ ret.mState = State.PENDING;
+ ret.mIsPendingUSSD = true;
+
+ return ret;
+ }
+
+ //***** Private Class methods
+
+ /** make empty strings be null.
+ * Regexp returns empty strings for empty groups
+ */
+ private static String
+ makeEmptyNull (String s) {
+ if (s != null && s.length() == 0) return null;
+
+ return s;
+ }
+
+ /** returns true of the string is empty or null */
+ private static boolean
+ isEmptyOrNull(CharSequence s) {
+ return s == null || (s.length() == 0);
+ }
+
+ private static int
+ scToCallForwardReason(String sc) {
+ if (sc == null) {
+ throw new RuntimeException ("invalid call forward sc");
+ }
+
+ if (sc.equals(SC_CF_All)) {
+ return CommandsInterface.CF_REASON_ALL;
+ } else if (sc.equals(SC_CFU)) {
+ return CommandsInterface.CF_REASON_UNCONDITIONAL;
+ } else if (sc.equals(SC_CFB)) {
+ return CommandsInterface.CF_REASON_BUSY;
+ } else if (sc.equals(SC_CFNR)) {
+ return CommandsInterface.CF_REASON_NOT_REACHABLE;
+ } else if (sc.equals(SC_CFNRy)) {
+ return CommandsInterface.CF_REASON_NO_REPLY;
+ } else if (sc.equals(SC_CF_All_Conditional)) {
+ return CommandsInterface.CF_REASON_ALL_CONDITIONAL;
+ } else {
+ throw new RuntimeException ("invalid call forward sc");
+ }
+ }
+
+ private static int
+ siToServiceClass(String si) {
+ if (si == null || si.length() == 0) {
+ return SERVICE_CLASS_NONE;
+ } else {
+ // NumberFormatException should cause MMI fail
+ int serviceCode = Integer.parseInt(si, 10);
+
+ switch (serviceCode) {
+ case 10: return SERVICE_CLASS_SMS + SERVICE_CLASS_FAX + SERVICE_CLASS_VOICE;
+ case 11: return SERVICE_CLASS_VOICE;
+ case 12: return SERVICE_CLASS_SMS + SERVICE_CLASS_FAX;
+ case 13: return SERVICE_CLASS_FAX;
+
+ case 16: return SERVICE_CLASS_SMS;
+
+ case 19: return SERVICE_CLASS_FAX + SERVICE_CLASS_VOICE;
+
+ case 20: return SERVICE_CLASS_DATA_ASYNC + SERVICE_CLASS_DATA_SYNC;
+
+ case 21: return SERVICE_CLASS_PAD + SERVICE_CLASS_DATA_ASYNC;
+ case 22: return SERVICE_CLASS_PACKET + SERVICE_CLASS_DATA_SYNC;
+ case 24: return SERVICE_CLASS_DATA_SYNC;
+ case 25: return SERVICE_CLASS_DATA_ASYNC;
+ case 26: return SERVICE_CLASS_DATA_SYNC + SERVICE_CLASS_VOICE;
+ case 99: return SERVICE_CLASS_PACKET;
+
+ default:
+ throw new RuntimeException("unsupported MMI service code " + si);
+ }
+ }
+ }
+
+ private static int
+ siToTime (String si) {
+ if (si == null || si.length() == 0) {
+ return 0;
+ } else {
+ // NumberFormatException should cause MMI fail
+ return Integer.parseInt(si, 10);
+ }
+ }
+
+ static boolean
+ isServiceCodeCallForwarding(String sc) {
+ return sc != null &&
+ (sc.equals(SC_CFU)
+ || sc.equals(SC_CFB) || sc.equals(SC_CFNRy)
+ || sc.equals(SC_CFNR) || sc.equals(SC_CF_All)
+ || sc.equals(SC_CF_All_Conditional));
+ }
+
+ static boolean
+ isServiceCodeCallBarring(String sc) {
+ Resources resource = Resources.getSystem();
+ if (sc != null) {
+ String[] barringMMI = resource.getStringArray(
+ com.android.internal.R.array.config_callBarringMMI);
+ if (barringMMI != null) {
+ for (String match : barringMMI) {
+ if (sc.equals(match)) return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ static String
+ scToBarringFacility(String sc) {
+ if (sc == null) {
+ throw new RuntimeException ("invalid call barring sc");
+ }
+
+ if (sc.equals(SC_BAOC)) {
+ return CommandsInterface.CB_FACILITY_BAOC;
+ } else if (sc.equals(SC_BAOIC)) {
+ return CommandsInterface.CB_FACILITY_BAOIC;
+ } else if (sc.equals(SC_BAOICxH)) {
+ return CommandsInterface.CB_FACILITY_BAOICxH;
+ } else if (sc.equals(SC_BAIC)) {
+ return CommandsInterface.CB_FACILITY_BAIC;
+ } else if (sc.equals(SC_BAICr)) {
+ return CommandsInterface.CB_FACILITY_BAICr;
+ } else if (sc.equals(SC_BA_ALL)) {
+ return CommandsInterface.CB_FACILITY_BA_ALL;
+ } else if (sc.equals(SC_BA_MO)) {
+ return CommandsInterface.CB_FACILITY_BA_MO;
+ } else if (sc.equals(SC_BA_MT)) {
+ return CommandsInterface.CB_FACILITY_BA_MT;
+ } else {
+ throw new RuntimeException ("invalid call barring sc");
+ }
+ }
+
+ //***** Constructor
+
+ ImsPhoneMmiCode(ImsPhone phone) {
+ // The telephony unit-test cases may create ImsPhoneMmiCode's
+ // in secondary threads
+ super(phone.getHandler().getLooper());
+ mPhone = phone;
+ mContext = phone.getContext();
+ mIccRecords = mPhone.mDefaultPhone.mIccRecords.get();
+ }
+
+ //***** MmiCode implementation
+
+ @Override
+ public State
+ getState() {
+ return mState;
+ }
+
+ @Override
+ public CharSequence
+ getMessage() {
+ return mMessage;
+ }
+
+ @Override
+ public Phone getPhone() { return mPhone; }
+
+ // inherited javadoc suffices
+ @Override
+ public void
+ cancel() {
+ // Complete or failed cannot be cancelled
+ if (mState == State.COMPLETE || mState == State.FAILED) {
+ return;
+ }
+
+ mState = State.CANCELLED;
+
+ if (mIsPendingUSSD) {
+ mPhone.cancelUSSD();
+ } else {
+ mPhone.onMMIDone (this);
+ }
+
+ }
+
+ @Override
+ public boolean isCancelable() {
+ /* Can only cancel pending USSD sessions. */
+ return mIsPendingUSSD;
+ }
+
+ //***** Instance Methods
+
+ String getDialingNumber() {
+ return mDialingNumber;
+ }
+
+ /** Does this dial string contain a structured or unstructured MMI code? */
+ boolean
+ isMMI() {
+ return mPoundString != null;
+ }
+
+ /* Is this a 1 or 2 digit "short code" as defined in TS 22.030 sec 6.5.3.2? */
+ boolean
+ isShortCode() {
+ return mPoundString == null
+ && mDialingNumber != null && mDialingNumber.length() <= 2;
+
+ }
+
+ static private boolean
+ isTwoDigitShortCode(Context context, String dialString) {
+ Rlog.d(LOG_TAG, "isTwoDigitShortCode");
+
+ if (dialString == null || dialString.length() != 2) return false;
+
+ if (sTwoDigitNumberPattern == null) {
+ sTwoDigitNumberPattern = context.getResources().getStringArray(
+ com.android.internal.R.array.config_twoDigitNumberPattern);
+ }
+
+ for (String dialnumber : sTwoDigitNumberPattern) {
+ Rlog.d(LOG_TAG, "Two Digit Number Pattern " + dialnumber);
+ if (dialString.equals(dialnumber)) {
+ Rlog.d(LOG_TAG, "Two Digit Number Pattern -true");
+ return true;
+ }
+ }
+ Rlog.d(LOG_TAG, "Two Digit Number Pattern -false");
+ return false;
+ }
+
+ /**
+ * Helper function for newFromDialString. Returns true if dialString appears
+ * to be a short code AND conditions are correct for it to be treated as
+ * such.
+ */
+ static private boolean isShortCode(String dialString, ImsPhone phone) {
+ // Refer to TS 22.030 Figure 3.5.3.2:
+ if (dialString == null) {
+ return false;
+ }
+
+ // Illegal dial string characters will give a ZERO length.
+ // At this point we do not want to crash as any application with
+ // call privileges may send a non dial string.
+ // It return false as when the dialString is equal to NULL.
+ if (dialString.length() == 0) {
+ return false;
+ }
+
+ if (PhoneNumberUtils.isLocalEmergencyNumber(phone.getContext(), dialString)) {
+ return false;
+ } else {
+ return isShortCodeUSSD(dialString, phone);
+ }
+ }
+
+ /**
+ * Helper function for isShortCode. Returns true if dialString appears to be
+ * a short code and it is a USSD structure
+ *
+ * According to the 3PGG TS 22.030 specification Figure 3.5.3.2: A 1 or 2
+ * digit "short code" is treated as USSD if it is entered while on a call or
+ * does not satisfy the condition (exactly 2 digits && starts with '1'), there
+ * are however exceptions to this rule (see below)
+ *
+ * Exception (1) to Call initiation is: If the user of the device is already in a call
+ * and enters a Short String without any #-key at the end and the length of the Short String is
+ * equal or less then the MAX_LENGTH_SHORT_CODE [constant that is equal to 2]
+ *
+ * The phone shall initiate a USSD/SS commands.
+ */
+ static private boolean isShortCodeUSSD(String dialString, ImsPhone phone) {
+ if (dialString != null && dialString.length() <= MAX_LENGTH_SHORT_CODE) {
+ if (phone.isInCall()) {
+ return true;
+ }
+
+ if (dialString.length() != MAX_LENGTH_SHORT_CODE ||
+ dialString.charAt(0) != '1') {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * @return true if the Service Code is PIN/PIN2/PUK/PUK2-related
+ */
+ boolean isPinPukCommand() {
+ return mSc != null && (mSc.equals(SC_PIN) || mSc.equals(SC_PIN2)
+ || mSc.equals(SC_PUK) || mSc.equals(SC_PUK2));
+ }
+
+ /**
+ * See TS 22.030 Annex B.
+ * In temporary mode, to suppress CLIR for a single call, enter:
+ * " * 31 # [called number] SEND "
+ * In temporary mode, to invoke CLIR for a single call enter:
+ * " # 31 # [called number] SEND "
+ */
+ boolean
+ isTemporaryModeCLIR() {
+ return mSc != null && mSc.equals(SC_CLIR) && mDialingNumber != null
+ && (isActivate() || isDeactivate());
+ }
+
+ /**
+ * returns CommandsInterface.CLIR_*
+ * See also isTemporaryModeCLIR()
+ */
+ int
+ getCLIRMode() {
+ if (mSc != null && mSc.equals(SC_CLIR)) {
+ if (isActivate()) {
+ return CommandsInterface.CLIR_SUPPRESSION;
+ } else if (isDeactivate()) {
+ return CommandsInterface.CLIR_INVOCATION;
+ }
+ }
+
+ return CommandsInterface.CLIR_DEFAULT;
+ }
+
+ boolean isActivate() {
+ return mAction != null && mAction.equals(ACTION_ACTIVATE);
+ }
+
+ boolean isDeactivate() {
+ return mAction != null && mAction.equals(ACTION_DEACTIVATE);
+ }
+
+ boolean isInterrogate() {
+ return mAction != null && mAction.equals(ACTION_INTERROGATE);
+ }
+
+ boolean isRegister() {
+ return mAction != null && mAction.equals(ACTION_REGISTER);
+ }
+
+ boolean isErasure() {
+ return mAction != null && mAction.equals(ACTION_ERASURE);
+ }
+
+ /**
+ * Returns true if this is a USSD code that's been submitted to the
+ * network...eg, after processCode() is called
+ */
+ public boolean isPendingUSSD() {
+ return mIsPendingUSSD;
+ }
+
+ @Override
+ public boolean isUssdRequest() {
+ return mIsUssdRequest;
+ }
+
+ boolean
+ isSupportedOverImsPhone() {
+ if (isShortCode()) return true;
+ else if (mDialingNumber != null) return false;
+ else if (isServiceCodeCallForwarding(mSc)
+ || isServiceCodeCallBarring(mSc)
+ || (mSc != null && mSc.equals(SC_WAIT))) {
+
+ int serviceClass = siToServiceClass(mSib);
+ if (serviceClass != SERVICE_CLASS_NONE
+ && serviceClass != SERVICE_CLASS_VOICE) {
+ return false;
+ }
+ return true;
+ } else if (isPinPukCommand()
+ || (mSc != null
+ && (mSc.equals(SC_PWD) || mSc.equals(SC_CLIP) || mSc.equals(SC_CLIR)))) {
+ return false;
+ } else if (mPoundString != null) return true;
+
+ return false;
+ }
+
+ /** Process a MMI code or short code...anything that isn't a dialing number */
+ void
+ processCode () {
+ try {
+ if (isShortCode()) {
+ Rlog.d(LOG_TAG, "isShortCode");
+
+ // These just get treated as USSD.
+ sendUssd(mDialingNumber);
+ } else if (isServiceCodeCallForwarding(mSc)) {
+ Rlog.d(LOG_TAG, "is CF");
+ // service group is not supported
+
+ String dialingNumber = mSia;
+ int reason = scToCallForwardReason(mSc);
+ int time = siToTime(mSic);
+
+ if (isInterrogate()) {
+ mPhone.getCallForwardingOption(reason,
+ obtainMessage(EVENT_QUERY_CF_COMPLETE, this));
+ } else {
+ int cfAction;
+
+ if (isActivate()) {
+ // 3GPP TS 22.030 6.5.2
+ // a call forwarding request with a single * would be
+ // interpreted as registration if containing a forwarded-to
+ // number, or an activation if not
+ if (isEmptyOrNull(dialingNumber)) {
+ cfAction = CommandsInterface.CF_ACTION_ENABLE;
+ mIsCallFwdReg = false;
+ } else {
+ cfAction = CommandsInterface.CF_ACTION_REGISTRATION;
+ mIsCallFwdReg = true;
+ }
+ } else if (isDeactivate()) {
+ cfAction = CommandsInterface.CF_ACTION_DISABLE;
+ } else if (isRegister()) {
+ cfAction = CommandsInterface.CF_ACTION_REGISTRATION;
+ } else if (isErasure()) {
+ cfAction = CommandsInterface.CF_ACTION_ERASURE;
+ } else {
+ throw new RuntimeException ("invalid action");
+ }
+
+ int isSettingUnconditional =
+ ((reason == CommandsInterface.CF_REASON_UNCONDITIONAL) ||
+ (reason == CommandsInterface.CF_REASON_ALL)) ? 1 : 0;
+
+ int isEnableDesired =
+ ((cfAction == CommandsInterface.CF_ACTION_ENABLE) ||
+ (cfAction == CommandsInterface.CF_ACTION_REGISTRATION)) ? 1 : 0;
+
+ Rlog.d(LOG_TAG, "is CF setCallForward");
+ mPhone.setCallForwardingOption(cfAction, reason,
+ dialingNumber, time, obtainMessage(
+ EVENT_SET_CFF_COMPLETE,
+ isSettingUnconditional,
+ isEnableDesired, this));
+ }
+ } else if (isServiceCodeCallBarring(mSc)) {
+ // sia = password
+ // sib = basic service group
+ // service group is not supported
+
+ String password = mSia;
+ String facility = scToBarringFacility(mSc);
+
+ if (isInterrogate()) {
+ mPhone.getCallBarring(facility,
+ obtainMessage(EVENT_QUERY_COMPLETE, this));
+ } else if (isActivate() || isDeactivate()) {
+ mPhone.setCallBarring(facility, isActivate(), password,
+ obtainMessage(EVENT_SET_COMPLETE, this));
+ } else {
+ throw new RuntimeException ("Invalid or Unsupported MMI Code");
+ }
+ } else if (mSc != null && mSc.equals(SC_WAIT)) {
+ // sia = basic service group
+ // service group is not supported
+ if (isActivate() || isDeactivate()) {
+ mPhone.setCallWaiting(isActivate(),
+ obtainMessage(EVENT_SET_COMPLETE, this));
+ } else if (isInterrogate()) {
+ mPhone.getCallWaiting(obtainMessage(EVENT_QUERY_COMPLETE, this));
+ } else {
+ throw new RuntimeException ("Invalid or Unsupported MMI Code");
+ }
+ } else if (mPoundString != null) {
+ sendUssd(mPoundString);
+ } else {
+ throw new RuntimeException ("Invalid or Unsupported MMI Code");
+ }
+ } catch (RuntimeException exc) {
+ mState = State.FAILED;
+ mMessage = mContext.getText(com.android.internal.R.string.mmiError);
+ mPhone.onMMIDone(this);
+ }
+ }
+
+ /**
+ * Called from ImsPhone
+ *
+ * An unsolicited USSD NOTIFY or REQUEST has come in matching
+ * up with this pending USSD request
+ *
+ * Note: If REQUEST, this exchange is complete, but the session remains
+ * active (ie, the network expects user input).
+ */
+ void
+ onUssdFinished(String ussdMessage, boolean isUssdRequest) {
+ if (mState == State.PENDING) {
+ if (ussdMessage == null) {
+ mMessage = mContext.getText(com.android.internal.R.string.mmiComplete);
+ } else {
+ mMessage = ussdMessage;
+ }
+ mIsUssdRequest = isUssdRequest;
+ // If it's a request, leave it PENDING so that it's cancelable.
+ if (!isUssdRequest) {
+ mState = State.COMPLETE;
+ }
+
+ mPhone.onMMIDone(this);
+ }
+ }
+
+ /**
+ * Called from ImsPhone
+ *
+ * The radio has reset, and this is still pending
+ */
+
+ void
+ onUssdFinishedError() {
+ if (mState == State.PENDING) {
+ mState = State.FAILED;
+ mMessage = mContext.getText(com.android.internal.R.string.mmiError);
+
+ mPhone.onMMIDone(this);
+ }
+ }
+
+ void sendUssd(String ussdMessage) {
+ // Treat this as a USSD string
+ mIsPendingUSSD = true;
+
+ // Note that unlike most everything else, the USSD complete
+ // response does not complete this MMI code...we wait for
+ // an unsolicited USSD "Notify" or "Request".
+ // The matching up of this is done in ImsPhone.
+
+ mPhone.sendUSSD(ussdMessage,
+ obtainMessage(EVENT_USSD_COMPLETE, this));
+ }
+
+ /** Called from ImsPhone.handleMessage; not a Handler subclass */
+ @Override
+ public void
+ handleMessage (Message msg) {
+ AsyncResult ar;
+
+ switch (msg.what) {
+ case EVENT_SET_COMPLETE:
+ ar = (AsyncResult) (msg.obj);
+
+ onSetComplete(msg, ar);
+ break;
+
+ case EVENT_SET_CFF_COMPLETE:
+ ar = (AsyncResult) (msg.obj);
+
+ /*
+ * msg.arg1 = 1 means to set unconditional voice call forwarding
+ * msg.arg2 = 1 means to enable voice call forwarding
+ */
+ if ((ar.exception == null) && (msg.arg1 == 1)) {
+ boolean cffEnabled = (msg.arg2 == 1);
+ if (mIccRecords != null) {
+ mIccRecords.setVoiceCallForwardingFlag(1, cffEnabled, mDialingNumber);
+ }
+ }
+
+ onSetComplete(msg, ar);
+ break;
+
+ case EVENT_QUERY_CF_COMPLETE:
+ ar = (AsyncResult) (msg.obj);
+ onQueryCfComplete(ar);
+ break;
+
+ case EVENT_QUERY_COMPLETE:
+ ar = (AsyncResult) (msg.obj);
+ onQueryComplete(ar);
+ break;
+
+ case EVENT_USSD_COMPLETE:
+ ar = (AsyncResult) (msg.obj);
+
+ if (ar.exception != null) {
+ mState = State.FAILED;
+ mMessage = getErrorMessage(ar);
+
+ mPhone.onMMIDone(this);
+ }
+
+ // Note that unlike most everything else, the USSD complete
+ // response does not complete this MMI code...we wait for
+ // an unsolicited USSD "Notify" or "Request".
+ // The matching up of this is done in ImsPhone.
+
+ break;
+
+ case EVENT_USSD_CANCEL_COMPLETE:
+ mPhone.onMMIDone(this);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ //***** Private instance methods
+
+ private CharSequence getErrorMessage(AsyncResult ar) {
+ return mContext.getText(com.android.internal.R.string.mmiError);
+ }
+
+ private CharSequence getScString() {
+ if (mSc != null) {
+ if (isServiceCodeCallBarring(mSc)) {
+ return mContext.getText(com.android.internal.R.string.BaMmi);
+ } else if (isServiceCodeCallForwarding(mSc)) {
+ return mContext.getText(com.android.internal.R.string.CfMmi);
+ } else if (mSc.equals(SC_PWD)) {
+ return mContext.getText(com.android.internal.R.string.PwdMmi);
+ } else if (mSc.equals(SC_WAIT)) {
+ return mContext.getText(com.android.internal.R.string.CwMmi);
+ }
+ }
+
+ return "";
+ }
+
+ private void
+ onSetComplete(Message msg, AsyncResult ar){
+ StringBuilder sb = new StringBuilder(getScString());
+ sb.append("\n");
+
+ if (ar.exception != null) {
+ mState = State.FAILED;
+ if (ar.exception instanceof CommandException) {
+ CommandException.Error err = ((CommandException)(ar.exception)).getCommandError();
+ if (err == CommandException.Error.PASSWORD_INCORRECT) {
+ sb.append(mContext.getText(
+ com.android.internal.R.string.passwordIncorrect));
+ } else {
+ sb.append(mContext.getText(
+ com.android.internal.R.string.mmiError));
+ }
+ } else {
+ sb.append(mContext.getText(
+ com.android.internal.R.string.mmiError));
+ }
+ } else if (isActivate()) {
+ mState = State.COMPLETE;
+ if (mIsCallFwdReg) {
+ sb.append(mContext.getText(
+ com.android.internal.R.string.serviceRegistered));
+ } else {
+ sb.append(mContext.getText(
+ com.android.internal.R.string.serviceEnabled));
+ }
+ } else if (isDeactivate()) {
+ mState = State.COMPLETE;
+ sb.append(mContext.getText(
+ com.android.internal.R.string.serviceDisabled));
+ } else if (isRegister()) {
+ mState = State.COMPLETE;
+ sb.append(mContext.getText(
+ com.android.internal.R.string.serviceRegistered));
+ } else if (isErasure()) {
+ mState = State.COMPLETE;
+ sb.append(mContext.getText(
+ com.android.internal.R.string.serviceErased));
+ } else {
+ mState = State.FAILED;
+ sb.append(mContext.getText(
+ com.android.internal.R.string.mmiError));
+ }
+
+ mMessage = sb;
+ mPhone.onMMIDone(this);
+ }
+
+ /**
+ * @param serviceClass 1 bit of the service class bit vectory
+ * @return String to be used for call forward query MMI response text.
+ * Returns null if unrecognized
+ */
+
+ private CharSequence
+ serviceClassToCFString (int serviceClass) {
+ switch (serviceClass) {
+ case SERVICE_CLASS_VOICE:
+ return mContext.getText(com.android.internal.R.string.serviceClassVoice);
+ case SERVICE_CLASS_DATA:
+ return mContext.getText(com.android.internal.R.string.serviceClassData);
+ case SERVICE_CLASS_FAX:
+ return mContext.getText(com.android.internal.R.string.serviceClassFAX);
+ case SERVICE_CLASS_SMS:
+ return mContext.getText(com.android.internal.R.string.serviceClassSMS);
+ case SERVICE_CLASS_DATA_SYNC:
+ return mContext.getText(com.android.internal.R.string.serviceClassDataSync);
+ case SERVICE_CLASS_DATA_ASYNC:
+ return mContext.getText(com.android.internal.R.string.serviceClassDataAsync);
+ case SERVICE_CLASS_PACKET:
+ return mContext.getText(com.android.internal.R.string.serviceClassPacket);
+ case SERVICE_CLASS_PAD:
+ return mContext.getText(com.android.internal.R.string.serviceClassPAD);
+ default:
+ return null;
+ }
+ }
+
+ /** one CallForwardInfo + serviceClassMask -> one line of text */
+ private CharSequence
+ makeCFQueryResultMessage(CallForwardInfo info, int serviceClassMask) {
+ CharSequence template;
+ String sources[] = {"{0}", "{1}", "{2}"};
+ CharSequence destinations[] = new CharSequence[3];
+ boolean needTimeTemplate;
+
+ // CF_REASON_NO_REPLY also has a time value associated with
+ // it. All others don't.
+
+ needTimeTemplate =
+ (info.reason == CommandsInterface.CF_REASON_NO_REPLY);
+
+ if (info.status == 1) {
+ if (needTimeTemplate) {
+ template = mContext.getText(
+ com.android.internal.R.string.cfTemplateForwardedTime);
+ } else {
+ template = mContext.getText(
+ com.android.internal.R.string.cfTemplateForwarded);
+ }
+ } else if (info.status == 0 && isEmptyOrNull(info.number)) {
+ template = mContext.getText(
+ com.android.internal.R.string.cfTemplateNotForwarded);
+ } else { /* (info.status == 0) && !isEmptyOrNull(info.number) */
+ // A call forward record that is not active but contains
+ // a phone number is considered "registered"
+
+ if (needTimeTemplate) {
+ template = mContext.getText(
+ com.android.internal.R.string.cfTemplateRegisteredTime);
+ } else {
+ template = mContext.getText(
+ com.android.internal.R.string.cfTemplateRegistered);
+ }
+ }
+
+ // In the template (from strings.xmls)
+ // {0} is one of "bearerServiceCode*"
+ // {1} is dialing number
+ // {2} is time in seconds
+
+ destinations[0] = serviceClassToCFString(info.serviceClass & serviceClassMask);
+ destinations[1] = PhoneNumberUtils.stringFromStringAndTOA(info.number, info.toa);
+ destinations[2] = Integer.toString(info.timeSeconds);
+
+ if (info.reason == CommandsInterface.CF_REASON_UNCONDITIONAL &&
+ (info.serviceClass & serviceClassMask)
+ == CommandsInterface.SERVICE_CLASS_VOICE) {
+ boolean cffEnabled = (info.status == 1);
+ if (mIccRecords != null) {
+ mIccRecords.setVoiceCallForwardingFlag(1, cffEnabled, info.number);
+ }
+ }
+
+ return TextUtils.replace(template, sources, destinations);
+ }
+
+
+ private void
+ onQueryCfComplete(AsyncResult ar) {
+ StringBuilder sb = new StringBuilder(getScString());
+ sb.append("\n");
+
+ if (ar.exception != null) {
+ mState = State.FAILED;
+ sb.append(getErrorMessage(ar));
+ } else {
+ CallForwardInfo infos[];
+
+ infos = (CallForwardInfo[]) ar.result;
+
+ if (infos.length == 0) {
+ // Assume the default is not active
+ sb.append(mContext.getText(com.android.internal.R.string.serviceDisabled));
+
+ // Set unconditional CFF in SIM to false
+ if (mIccRecords != null) {
+ mIccRecords.setVoiceCallForwardingFlag(1, false, null);
+ }
+ } else {
+
+ SpannableStringBuilder tb = new SpannableStringBuilder();
+
+ // Each bit in the service class gets its own result line
+ // The service classes may be split up over multiple
+ // CallForwardInfos. So, for each service class, find out
+ // which CallForwardInfo represents it and then build
+ // the response text based on that
+
+ for (int serviceClassMask = 1
+ ; serviceClassMask <= SERVICE_CLASS_MAX
+ ; serviceClassMask <<= 1
+ ) {
+ for (int i = 0, s = infos.length; i < s ; i++) {
+ if ((serviceClassMask & infos[i].serviceClass) != 0) {
+ tb.append(makeCFQueryResultMessage(infos[i],
+ serviceClassMask));
+ tb.append("\n");
+ }
+ }
+ }
+ sb.append(tb);
+ }
+
+ mState = State.COMPLETE;
+ }
+
+ mMessage = sb;
+ mPhone.onMMIDone(this);
+
+ }
+
+ private void
+ onQueryComplete(AsyncResult ar) {
+ StringBuilder sb = new StringBuilder(getScString());
+ sb.append("\n");
+
+ if (ar.exception != null) {
+ mState = State.FAILED;
+ sb.append(getErrorMessage(ar));
+ } else {
+ int[] ints = (int[])ar.result;
+
+ if (ints.length != 0) {
+ if (ints[0] == 0) {
+ sb.append(mContext.getText(com.android.internal.R.string.serviceDisabled));
+ } else if (mSc.equals(SC_WAIT)) {
+ // Call Waiting includes additional data in the response.
+ sb.append(createQueryCallWaitingResultMessage(ints[1]));
+ } else if (isServiceCodeCallBarring(mSc)) {
+ // ints[0] for Call Barring is a bit vector of services
+ sb.append(createQueryCallBarringResultMessage(ints[0]));
+ } else if (ints[0] == 1) {
+ // for all other services, treat it as a boolean
+ sb.append(mContext.getText(com.android.internal.R.string.serviceEnabled));
+ } else {
+ sb.append(mContext.getText(com.android.internal.R.string.mmiError));
+ }
+ } else {
+ sb.append(mContext.getText(com.android.internal.R.string.mmiError));
+ }
+ mState = State.COMPLETE;
+ }
+
+ mMessage = sb;
+ mPhone.onMMIDone(this);
+ }
+
+ private CharSequence
+ createQueryCallWaitingResultMessage(int serviceClass) {
+ StringBuilder sb = new StringBuilder(
+ mContext.getText(com.android.internal.R.string.serviceEnabledFor));
+
+ for (int classMask = 1
+ ; classMask <= SERVICE_CLASS_MAX
+ ; classMask <<= 1
+ ) {
+ if ((classMask & serviceClass) != 0) {
+ sb.append("\n");
+ sb.append(serviceClassToCFString(classMask & serviceClass));
+ }
+ }
+ return sb;
+ }
+ private CharSequence
+ createQueryCallBarringResultMessage(int serviceClass)
+ {
+ StringBuilder sb = new StringBuilder(
+ mContext.getText(com.android.internal.R.string.serviceEnabledFor));
+
+ for (int classMask = 1
+ ; classMask <= SERVICE_CLASS_MAX
+ ; classMask <<= 1
+ ) {
+ if ((classMask & serviceClass) != 0) {
+ sb.append("\n");
+ sb.append(serviceClassToCFString(classMask & serviceClass));
+ }
+ }
+ return sb;
+ }
+
+ /***
+ * TODO: It would be nice to have a method here that can take in a dialstring and
+ * figure out if there is an MMI code embedded within it. This code would replace
+ * some of the string parsing functionality in the Phone App's
+ * SpecialCharSequenceMgr class.
+ */
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder("ImsPhoneMmiCode {");
+
+ sb.append("State=" + getState());
+ if (mAction != null) sb.append(" action=" + mAction);
+ if (mSc != null) sb.append(" sc=" + mSc);
+ if (mSia != null) sb.append(" sia=" + mSia);
+ if (mSib != null) sb.append(" sib=" + mSib);
+ if (mSic != null) sb.append(" sic=" + mSic);
+ if (mPoundString != null) sb.append(" poundString=" + mPoundString);
+ if (mDialingNumber != null) sb.append(" dialingNumber=" + mDialingNumber);
+ if (mPwd != null) sb.append(" pwd=" + mPwd);
+ sb.append("}");
+ return sb.toString();
+ }
+}
diff --git a/src/java/com/android/internal/telephony/sip/SipCommandInterface.java b/src/java/com/android/internal/telephony/sip/SipCommandInterface.java
index 07945c8..6223e48 100644
--- a/src/java/com/android/internal/telephony/sip/SipCommandInterface.java
+++ b/src/java/com/android/internal/telephony/sip/SipCommandInterface.java
@@ -540,6 +540,10 @@
}
@Override
+ public void requestIccSimAuthentication(String data, Message response) {
+ }
+
+ @Override
public void getVoiceRadioTechnology(Message result) {
}
@@ -589,4 +593,8 @@
@Override
public void nvResetConfig(int resetType, Message response) {
}
+
+ @Override
+ public void getHardwareConfig(Message result) {
+ }
}
diff --git a/src/java/com/android/internal/telephony/sip/SipConnectionBase.java b/src/java/com/android/internal/telephony/sip/SipConnectionBase.java
index 42978c0..8d8a554 100644
--- a/src/java/com/android/internal/telephony/sip/SipConnectionBase.java
+++ b/src/java/com/android/internal/telephony/sip/SipConnectionBase.java
@@ -198,4 +198,24 @@
public int getPreciseDisconnectCause() {
return 0;
}
+
+ @Override
+ public long getHoldingStartTime() {
+ return mHoldingStartTime;
+ }
+
+ @Override
+ public long getConnectTimeReal() {
+ return mConnectTimeReal;
+ }
+
+ @Override
+ public Connection getOrigConnection() {
+ return null;
+ }
+
+ @Override
+ public boolean isMultiparty() {
+ return false;
+ }
}
diff --git a/src/java/com/android/internal/telephony/test/SimulatedCommands.java b/src/java/com/android/internal/telephony/test/SimulatedCommands.java
index 90914a0..250e507 100644
--- a/src/java/com/android/internal/telephony/test/SimulatedCommands.java
+++ b/src/java/com/android/internal/telephony/test/SimulatedCommands.java
@@ -1643,6 +1643,11 @@
}
@Override
+ public void requestIccSimAuthentication(String data, Message response) {
+ unimplemented(response);
+ }
+
+ @Override
public void getVoiceRadioTechnology(Message response) {
unimplemented(response);
}
@@ -1720,4 +1725,9 @@
public void nvResetConfig(int resetType, Message response) {
unimplemented(response);
}
+
+ @Override
+ public void getHardwareConfig(Message result) {
+ unimplemented(result);
+ }
}
diff --git a/src/java/com/android/internal/telephony/uicc/IccCardApplicationStatus.java b/src/java/com/android/internal/telephony/uicc/IccCardApplicationStatus.java
index 6dfb4be..f1b0e43 100644
--- a/src/java/com/android/internal/telephony/uicc/IccCardApplicationStatus.java
+++ b/src/java/com/android/internal/telephony/uicc/IccCardApplicationStatus.java
@@ -27,6 +27,7 @@
* {@hide}
*/
public class IccCardApplicationStatus {
+ // TODO: Replace with constants from PhoneConstants.APPTYPE_xxx
public enum AppType{
APPTYPE_UNKNOWN,
APPTYPE_SIM,
diff --git a/src/java/com/android/internal/telephony/uicc/IccCardProxy.java b/src/java/com/android/internal/telephony/uicc/IccCardProxy.java
index 06eda37..134b02f 100644
--- a/src/java/com/android/internal/telephony/uicc/IccCardProxy.java
+++ b/src/java/com/android/internal/telephony/uicc/IccCardProxy.java
@@ -29,17 +29,21 @@
import android.os.UserHandle;
import android.telephony.Rlog;
import android.telephony.ServiceState;
+import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import com.android.internal.telephony.CommandsInterface;
import com.android.internal.telephony.IccCard;
import com.android.internal.telephony.IccCardConstants;
import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.telephony.MccTable;
import com.android.internal.telephony.RILConstants;
import com.android.internal.telephony.TelephonyIntents;
import com.android.internal.telephony.IccCardConstants.State;
import com.android.internal.telephony.cdma.CdmaSubscriptionSourceManager;
import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.Subscription;
+import com.android.internal.telephony.SubscriptionController;
import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppState;
import com.android.internal.telephony.uicc.IccCardApplicationStatus.PersoSubState;
import com.android.internal.telephony.uicc.IccCardStatus.CardState;
@@ -49,8 +53,12 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_ALPHA;
+import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC;
+import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_ISO_COUNTRY;
import static com.android.internal.telephony.TelephonyProperties.PROPERTY_SIM_STATE;
+
/**
* @Deprecated use {@link UiccController}.getUiccCard instead.
*
@@ -84,6 +92,14 @@
private static final int EVENT_NETWORK_LOCKED = 9;
private static final int EVENT_CDMA_SUBSCRIPTION_SOURCE_CHANGED = 11;
+ private static final int EVENT_ICC_RECORD_EVENTS = 500;
+ private static final int EVENT_SUBSCRIPTION_ACTIVATED = 501;
+ private static final int EVENT_SUBSCRIPTION_DEACTIVATED = 502;
+
+ // FIXME Rename mCardIndex to mSlotId.
+ private Integer mCardIndex = null;
+ private Subscription mSubscriptionData = null;
+
private final Object mLock = new Object();
private Context mContext;
private CommandsInterface mCi;
@@ -117,6 +133,15 @@
setExternalState(State.NOT_READY);
}
+ public IccCardProxy(Context context, CommandsInterface ci, int cardIndex) {
+ this(context, ci);
+
+ mCardIndex = cardIndex;
+
+ resetProperties();
+ setExternalState(State.NOT_READY, false);
+ }
+
public void dispose() {
synchronized (mLock) {
log("Disposing");
@@ -226,6 +251,33 @@
setExternalState(State.READY);
break;
case EVENT_RECORDS_LOADED:
+ if (mIccRecords != null) {
+ String operator = mIccRecords.getOperatorNumeric();
+ int slotId = mCardIndex;
+
+ log("operator = " + operator + " slotId = " + slotId);
+
+ if (operator != null) {
+ log("update icc_operator_numeric=" + operator);
+ setSystemProperty(PROPERTY_ICC_OPERATOR_NUMERIC, slotId, operator);
+ String countryCode = operator.substring(0,3);
+ if (countryCode != null) {
+ setSystemProperty(PROPERTY_ICC_OPERATOR_ISO_COUNTRY, slotId,
+ MccTable.countryCodeForMcc(Integer.parseInt(countryCode)));
+ } else {
+ loge("EVENT_RECORDS_LOADED Country code is null");
+ }
+
+ long[] subId = SubscriptionController.getInstance().getSubId(slotId);
+ // Update MCC MNC device configuration information only for default sub.
+ if (subId[0] == SubscriptionController.getInstance().getDefaultSubId()) {
+ log("update mccmnc=" + operator + " config for default subscription.");
+ MccTable.updateMccMncConfiguration(mContext, operator, false);
+ }
+ } else {
+ loge("EVENT_RECORDS_LOADED Operator name is null");
+ }
+ }
broadcastIccStateChangedIntent(IccCardConstants.INTENT_VALUE_ICC_LOADED, null);
break;
case EVENT_IMSI_READY:
@@ -238,15 +290,52 @@
case EVENT_CDMA_SUBSCRIPTION_SOURCE_CHANGED:
updateQuietMode();
break;
+ case EVENT_SUBSCRIPTION_ACTIVATED:
+ log("EVENT_SUBSCRIPTION_ACTIVATED");
+ onSubscriptionActivated();
+ break;
+
+ case EVENT_SUBSCRIPTION_DEACTIVATED:
+ log("EVENT_SUBSCRIPTION_DEACTIVATED");
+ onSubscriptionDeactivated();
+ break;
+
+ case EVENT_ICC_RECORD_EVENTS:
+ if ((mCurrentAppType == UiccController.APP_FAM_3GPP) && (mIccRecords != null)) {
+ int slotId = mCardIndex;
+ AsyncResult ar = (AsyncResult)msg.obj;
+ int eventCode = (Integer) ar.result;
+ if (eventCode == SIMRecords.EVENT_SPN) {
+ setSystemProperty(PROPERTY_ICC_OPERATOR_ALPHA, slotId,
+ mIccRecords.getServiceProviderName());
+ }
+ }
+ break;
+
default:
loge("Unhandled message with number: " + msg.what);
break;
}
}
+ private void onSubscriptionActivated() {
+ //mSubscriptionData = SubscriptionManager.getCurrentSubscription(mCardIndex);
+
+ updateIccAvailability();
+ updateStateProperty();
+ }
+
+ private void onSubscriptionDeactivated() {
+ resetProperties();
+ mSubscriptionData = null;
+ updateIccAvailability();
+ updateStateProperty();
+ }
+
+
private void updateIccAvailability() {
synchronized (mLock) {
- UiccCard newCard = mUiccController.getUiccCard();
+ UiccCard newCard = mUiccController.getUiccCard(mCardIndex);
CardState state = CardState.CARDSTATE_ABSENT;
UiccCardApplication newApp = null;
IccRecords newRecords = null;
@@ -271,6 +360,20 @@
}
}
+ void resetProperties() {
+ if (mCurrentAppType == UiccController.APP_FAM_3GPP) {
+ log("update icc_operator_numeric=" + "");
+ setSystemProperty(PROPERTY_ICC_OPERATOR_NUMERIC, mCardIndex, "");
+ setSystemProperty(PROPERTY_ICC_OPERATOR_ISO_COUNTRY, mCardIndex, "");
+ setSystemProperty(PROPERTY_ICC_OPERATOR_ALPHA, mCardIndex, "");
+ }
+ }
+
+ private void HandleDetectedState() {
+ // CAF_MSIM SAND
+// setExternalState(State.DETECTED, false);
+ }
+
private void updateExternalState() {
if (mUiccCard == null || mUiccCard.getCardState() == CardState.CARDSTATE_ABSENT) {
if (mRadioOn) {
@@ -288,14 +391,15 @@
if (mUiccApplication == null) {
setExternalState(State.NOT_READY);
- return;
}
switch (mUiccApplication.getState()) {
case APPSTATE_UNKNOWN:
- case APPSTATE_DETECTED:
setExternalState(State.UNKNOWN);
break;
+ case APPSTATE_DETECTED:
+ HandleDetectedState();
+ break;
case APPSTATE_PIN:
setExternalState(State.PIN_REQUIRED);
break;
@@ -303,7 +407,8 @@
setExternalState(State.PUK_REQUIRED);
break;
case APPSTATE_SUBSCRIPTION_PERSO:
- if (mUiccApplication.getPersoSubState() == PersoSubState.PERSOSUBSTATE_SIM_NETWORK) {
+ if (mUiccApplication.getPersoSubState() ==
+ PersoSubState.PERSOSUBSTATE_SIM_NETWORK) {
setExternalState(State.NETWORK_LOCKED);
} else {
setExternalState(State.UNKNOWN);
@@ -326,6 +431,10 @@
mIccRecords.registerForImsiReady(this, EVENT_IMSI_READY, null);
mIccRecords.registerForRecordsLoaded(this, EVENT_RECORDS_LOADED, null);
}
+
+ if (mIccRecords != null) {
+ mIccRecords.registerForRecordsEvents(this, EVENT_ICC_RECORD_EVENTS, null);
+ }
}
private void unregisterUiccCardEvents() {
@@ -335,10 +444,20 @@
if (mUiccApplication != null) mUiccApplication.unregisterForNetworkLocked(this);
if (mIccRecords != null) mIccRecords.unregisterForImsiReady(this);
if (mIccRecords != null) mIccRecords.unregisterForRecordsLoaded(this);
+ if (mIccRecords != null) mIccRecords.unregisterForRecordsEvents(this);
+ }
+
+ private void updateStateProperty() {
+ setSystemProperty(PROPERTY_SIM_STATE, mCardIndex,getState().toString());
}
private void broadcastIccStateChangedIntent(String value, String reason) {
synchronized (mLock) {
+ if (mCardIndex == null) {
+ loge("broadcastIccStateChangedIntent: Card Index is not set; Return!!");
+ return;
+ }
+
if (mQuietMode) {
log("QuietMode: NOT Broadcasting intent ACTION_SIM_STATE_CHANGED " + value
+ " reason " + reason);
@@ -350,14 +469,35 @@
intent.putExtra(PhoneConstants.PHONE_NAME_KEY, "Phone");
intent.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE, value);
intent.putExtra(IccCardConstants.INTENT_KEY_LOCKED_REASON, reason);
-
- if (DBG) log("Broadcasting intent ACTION_SIM_STATE_CHANGED " + value
- + " reason " + reason);
+ SubscriptionManager.putPhoneIdAndSubIdExtra(intent, mCardIndex);
+ log("Broadcasting intent ACTION_SIM_STATE_CHANGED " + value
+ + " reason " + reason + " for mCardIndex : " + mCardIndex);
ActivityManagerNative.broadcastStickyIntent(intent, READ_PHONE_STATE,
UserHandle.USER_ALL);
}
}
+ private void setExternalState(State newState, boolean override) {
+ synchronized (mLock) {
+ if (mCardIndex == null) {
+ loge("setExternalState: Card Index is not set; Return!!");
+ return;
+ }
+
+ if (!override && newState == mExternalState) {
+ return;
+ }
+ mExternalState = newState;
+ setSystemProperty(PROPERTY_SIM_STATE, mCardIndex, getState().toString());
+ broadcastIccStateChangedIntent(getIccStateIntentString(mExternalState),
+ getIccStateReason(mExternalState));
+ // TODO: Need to notify registrants for other states as well.
+ if ( State.ABSENT == mExternalState) {
+ mAbsentRegistrants.notifyRegistrants();
+ }
+ }
+ }
+
private void processLockedState() {
synchronized (mLock) {
if (mUiccApplication == null) {
@@ -389,18 +529,6 @@
}
}
- private void setExternalState(State newState, boolean override) {
- synchronized (mLock) {
- if (!override && newState == mExternalState) {
- return;
- }
- mExternalState = newState;
- SystemProperties.set(PROPERTY_SIM_STATE, mExternalState.toString());
- broadcastIccStateChangedIntent(getIccStateIntentString(mExternalState),
- getIccStateReason(mExternalState));
- }
- }
-
private void setExternalState(State newState) {
setExternalState(newState, false);
}
@@ -423,7 +551,6 @@
case READY: return IccCardConstants.INTENT_VALUE_ICC_READY;
case NOT_READY: return IccCardConstants.INTENT_VALUE_ICC_NOT_READY;
case PERM_DISABLED: return IccCardConstants.INTENT_VALUE_ICC_LOCKED;
- case CARD_IO_ERROR: return IccCardConstants.INTENT_VALUE_ICC_CARD_IO_ERROR;
default: return IccCardConstants.INTENT_VALUE_ICC_UNKNOWN;
}
}
@@ -727,6 +854,11 @@
}
}
+ private void setSystemProperty(String property, int slotId, String value) {
+ long[] subId = SubscriptionController.getInstance().getSubId(slotId);
+ TelephonyManager.setTelephonyProperty(property, subId[0], value);
+ }
+
private void log(String s) {
Rlog.d(LOG_TAG, s);
}
diff --git a/src/java/com/android/internal/telephony/uicc/IccConstants.java b/src/java/com/android/internal/telephony/uicc/IccConstants.java
index ae1c442..f367f29 100644
--- a/src/java/com/android/internal/telephony/uicc/IccConstants.java
+++ b/src/java/com/android/internal/telephony/uicc/IccConstants.java
@@ -76,6 +76,7 @@
static final int EF_DOMAIN = 0x6f03;
static final int EF_IST = 0x6f07;
static final int EF_PCSCF = 0x6f09;
+ static final int EF_PSI = 0x6fe5;
// SMS record length from TS 51.011 10.5.3
static public final int SMS_RECORD_LENGTH = 176;
diff --git a/src/java/com/android/internal/telephony/uicc/IccFileHandler.java b/src/java/com/android/internal/telephony/uicc/IccFileHandler.java
index dc42f70..a460819 100644
--- a/src/java/com/android/internal/telephony/uicc/IccFileHandler.java
+++ b/src/java/com/android/internal/telephony/uicc/IccFileHandler.java
@@ -518,6 +518,7 @@
case EF_EXT1:
case EF_EXT2:
case EF_EXT3:
+ case EF_PSI:
return MF_SIM + DF_TELECOM;
case EF_ICCID:
diff --git a/src/java/com/android/internal/telephony/uicc/IccRecords.java b/src/java/com/android/internal/telephony/uicc/IccRecords.java
index ec920f9..82d8082 100644
--- a/src/java/com/android/internal/telephony/uicc/IccRecords.java
+++ b/src/java/com/android/internal/telephony/uicc/IccRecords.java
@@ -67,6 +67,7 @@
protected boolean mIsVoiceMailFixed = false;
protected int mCountVoiceMessages = 0;
protected String mImsi;
+ private String auth_rsp;
protected int mMncLength = UNINITIALIZED;
protected int mMailboxIndex = 0; // 0 is no mailbox dailing number associated
@@ -75,6 +76,8 @@
protected String mGid1;
+ private final Object mLock = new Object();
+
// ***** Constants
// Markers for mncLength
@@ -93,6 +96,7 @@
public static final int EVENT_GET_ICC_RECORD_DONE = 100;
protected static final int EVENT_APP_READY = 1;
+ private static final int EVENT_AKA_AUTHENTICATE_DONE = 90;
@Override
public String toString() {
@@ -391,10 +395,12 @@
//***** Overridden from Handler
@Override
public void handleMessage(Message msg) {
+ AsyncResult ar;
+
switch (msg.what) {
case EVENT_GET_ICC_RECORD_DONE:
try {
- AsyncResult ar = (AsyncResult) msg.obj;
+ ar = (AsyncResult) msg.obj;
IccRecordLoaded recordLoaded = (IccRecordLoaded) ar.userObj;
if (DBG) log(recordLoaded.getEfName() + " LOADED");
@@ -412,6 +418,27 @@
}
break;
+ case EVENT_AKA_AUTHENTICATE_DONE:
+ ar = (AsyncResult)msg.obj;
+ auth_rsp = null;
+ if (DBG) log("EVENT_AKA_AUTHENTICATE_DONE");
+ if (ar.exception != null) {
+ loge("Exception ICC SIM AKA: " + ar.exception);
+ break;
+ } else {
+ try {
+ auth_rsp = (String)ar.result;
+ if (DBG) log("ICC SIM AKA: auth_rsp = " + auth_rsp);
+ } catch (Exception e) {
+ loge("Failed to parse ICC SIM AKA contents: " + e);
+ }
+ }
+ synchronized (mLock) {
+ mLock.notifyAll();
+ }
+
+ break;
+
default:
super.handleMessage(msg);
}
@@ -505,6 +532,41 @@
return null;
}
+ /**
+ * Returns the response of the SIM application on the UICC to authentication
+ * challenge/response algorithm. The data string and challenge response are
+ * Base64 encoded Strings.
+ * Can support EAP-SIM, EAP-AKA with results encoded per 3GPP TS 31.102.
+ *
+ * @param data authentication challenge data
+ * @return challenge response
+ */
+ public String getIccSimChallengeResponse(String data) {
+ if (DBG) log("getIccSimChallengeResponse-data: (original) " + data);
+
+ data = data + mParentApp.getAid();
+
+ if (DBG) log("getIccSimChallengeResponse-data: (with AID) " + data);
+
+ try {
+ synchronized(mLock) {
+ mCi.requestIccSimAuthentication(data, obtainMessage(EVENT_AKA_AUTHENTICATE_DONE));
+ try {
+ mLock.wait();
+ } catch (InterruptedException e) {
+ loge("interrupted while trying to request Icc Sim Auth");
+ }
+ }
+ } catch(Exception e) {
+ loge( "Fail while trying to request Icc Sim Auth");
+ return null;
+ }
+
+ if (DBG) log("getIccSimChallengeResponse-auth_rsp" + auth_rsp);
+
+ return auth_rsp;
+ }
+
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("IccRecords: " + this);
pw.println(" mDestroyed=" + mDestroyed);
diff --git a/src/java/com/android/internal/telephony/uicc/IsimFileHandler.java b/src/java/com/android/internal/telephony/uicc/IsimFileHandler.java
index 07c5ec8..6fe16c9 100644
--- a/src/java/com/android/internal/telephony/uicc/IsimFileHandler.java
+++ b/src/java/com/android/internal/telephony/uicc/IsimFileHandler.java
@@ -37,6 +37,8 @@
case EF_IMPI:
case EF_IMPU:
case EF_DOMAIN:
+ case EF_IST:
+ case EF_PCSCF:
return MF_SIM + DF_ADF;
}
String path = getCommonIccEFPath(efid);
diff --git a/src/java/com/android/internal/telephony/uicc/IsimRecords.java b/src/java/com/android/internal/telephony/uicc/IsimRecords.java
index 25376c1..b176d4e 100644
--- a/src/java/com/android/internal/telephony/uicc/IsimRecords.java
+++ b/src/java/com/android/internal/telephony/uicc/IsimRecords.java
@@ -41,4 +41,24 @@
* @return an array of IMS public user identity strings, or null if not available
*/
String[] getIsimImpu();
+
+ /**
+ * Returns the IMS Service Table (IST) that was loaded from the ISIM.
+ * @return IMS Service Table or null if not present or not loaded
+ */
+ String getIsimIst();
+
+ /**
+ * Returns the IMS Proxy Call Session Control Function(PCSCF) that were loaded from the ISIM.
+ * @return an array of PCSCF strings with one PCSCF per string, or null if
+ * not present or not loaded
+ */
+ String[] getIsimPcscf();
+
+ /**
+ * Returns the response of ISIM Authetification through RIL.
+ * Returns null if the Authentification hasn't been successed or isn't present iphonesubinfo.
+ * @return the response of ISIM Authetification, or null if not available
+ */
+ String getIsimChallengeResponse(String nonce);
}
diff --git a/src/java/com/android/internal/telephony/uicc/IsimUiccRecords.java b/src/java/com/android/internal/telephony/uicc/IsimUiccRecords.java
index 8e25132..96d19bc 100644
--- a/src/java/com/android/internal/telephony/uicc/IsimUiccRecords.java
+++ b/src/java/com/android/internal/telephony/uicc/IsimUiccRecords.java
@@ -21,6 +21,8 @@
import android.os.Handler;
import android.os.Message;
import android.telephony.Rlog;
+import android.content.Intent;
+
import com.android.internal.telephony.CommandsInterface;
import com.android.internal.telephony.gsm.SimTlv;
@@ -35,6 +37,8 @@
import static com.android.internal.telephony.uicc.IccConstants.EF_DOMAIN;
import static com.android.internal.telephony.uicc.IccConstants.EF_IMPI;
import static com.android.internal.telephony.uicc.IccConstants.EF_IMPU;
+import static com.android.internal.telephony.uicc.IccConstants.EF_IST;
+import static com.android.internal.telephony.uicc.IccConstants.EF_PCSCF;
/**
* {@hide}
@@ -43,14 +47,22 @@
protected static final String LOG_TAG = "IsimUiccRecords";
private static final boolean DBG = true;
- private static final boolean DUMP_RECORDS = false; // Note: PII is logged when this is true
+ private static final boolean DUMP_RECORDS = true; // Note: PII is logged when this is true
+ public static final String INTENT_ISIM_REFRESH = "com.android.intent.isim_refresh";
private static final int EVENT_APP_READY = 1;
+ private static final int EVENT_ISIM_REFRESH = 31;
+ private static final int EVENT_AKA_AUTHENTICATE_DONE = 90;
// ISIM EF records (see 3GPP TS 31.103)
private String mIsimImpi; // IMS private user identity
private String mIsimDomain; // IMS home network domain name
private String[] mIsimImpu; // IMS public user identity(s)
+ private String mIsimIst; // IMS Service Table
+ private String[] mIsimPcscf; // IMS Proxy Call Session Control Function
+ private String auth_rsp;
+
+ private final Object mLock = new Object();
private static final int TAG_ISIM_VALUE = 0x80; // From 3GPP TS 31.103
@@ -59,7 +71,9 @@
return "IsimUiccRecords: " + super.toString()
+ " mIsimImpi=" + mIsimImpi
+ " mIsimDomain=" + mIsimDomain
- + " mIsimImpu=" + mIsimImpu;
+ + " mIsimImpu=" + mIsimImpu
+ + " mIsimIst=" + mIsimIst
+ + " mIsimPcscf=" + mIsimPcscf;
}
public IsimUiccRecords(UiccCardApplication app, Context c, CommandsInterface ci) {
@@ -69,6 +83,9 @@
// recordsToLoad is set to 0 because no requests are made yet
mRecordsToLoad = 0;
+ // Start off by setting empty state
+ resetRecords();
+ mCi.registerForIccRefresh(this, EVENT_ISIM_REFRESH, null);
mParentApp.registerForReady(this, EVENT_APP_READY, null);
if (DBG) log("IsimUiccRecords X ctor this=" + this);
@@ -78,6 +95,7 @@
public void dispose() {
log("Disposing " + this);
//Unregister for all events
+ mCi.unregisterForIccRefresh(this);
mParentApp.unregisterForReady(this);
resetRecords();
super.dispose();
@@ -85,11 +103,14 @@
// ***** Overridden from Handler
public void handleMessage(Message msg) {
+ AsyncResult ar;
+
if (mDestroyed.get()) {
Rlog.e(LOG_TAG, "Received message " + msg +
"[" + msg.what + "] while being destroyed. Ignoring.");
return;
}
+ loge("IsimUiccRecords: handleMessage " + msg + "[" + msg.what + "] ");
try {
switch (msg.what) {
@@ -97,6 +118,37 @@
onReady();
break;
+ case EVENT_ISIM_REFRESH:
+ ar = (AsyncResult)msg.obj;
+ loge("ISim REFRESH(EVENT_ISIM_REFRESH) with exception: " + ar.exception);
+ if (ar.exception == null) {
+ Intent intent = new Intent(INTENT_ISIM_REFRESH);
+ loge("send ISim REFRESH: " + INTENT_ISIM_REFRESH);
+ mContext.sendBroadcast(intent);
+ handleIsimRefresh((IccRefreshResponse)ar.result);
+ }
+ break;
+
+ case EVENT_AKA_AUTHENTICATE_DONE:
+ ar = (AsyncResult)msg.obj;
+ log("EVENT_AKA_AUTHENTICATE_DONE");
+ if (ar.exception != null) {
+ log("Exception ISIM AKA: " + ar.exception);
+ break;
+ } else {
+ try {
+ auth_rsp = (String)ar.result;
+ log("ISIM AKA: auth_rsp = " + auth_rsp);
+ } catch (Exception e) {
+ log("Failed to parse ISIM AKA contents: " + e);
+ }
+ }
+ synchronized (mLock) {
+ mLock.notifyAll();
+ }
+
+ break;
+
default:
super.handleMessage(msg); // IccRecords handles generic record load responses
@@ -121,14 +173,27 @@
mFh.loadEFTransparent(EF_DOMAIN, obtainMessage(
IccRecords.EVENT_GET_ICC_RECORD_DONE, new EfIsimDomainLoaded()));
mRecordsToLoad++;
+ mFh.loadEFTransparent(EF_IST, obtainMessage(
+ IccRecords.EVENT_GET_ICC_RECORD_DONE, new EfIsimIstLoaded()));
+ mRecordsToLoad++;
+ mFh.loadEFLinearFixedAll(EF_PCSCF, obtainMessage(
+ IccRecords.EVENT_GET_ICC_RECORD_DONE, new EfIsimPcscfLoaded()));
+ mRecordsToLoad++;
- log("fetchIsimRecords " + mRecordsToLoad);
+ if (DBG) log("fetchIsimRecords " + mRecordsToLoad + " requested: " + mRecordsRequested);
}
protected void resetRecords() {
// recordsRequested is set to false indicating that the SIM
// read requests made so far are not valid. This is set to
// true only when fresh set of read requests are made.
+ mIsimImpi = null;
+ mIsimDomain = null;
+ mIsimImpu = null;
+ mIsimIst = null;
+ mIsimPcscf = null;
+ auth_rsp = null;
+
mRecordsRequested = false;
}
@@ -171,6 +236,33 @@
}
}
+ private class EfIsimIstLoaded implements IccRecords.IccRecordLoaded {
+ public String getEfName() {
+ return "EF_ISIM_IST";
+ }
+ public void onRecordLoaded(AsyncResult ar) {
+ byte[] data = (byte[]) ar.result;
+ mIsimIst = IccUtils.bytesToHexString(data);
+ if (DUMP_RECORDS) log("EF_IST=" + mIsimIst);
+ }
+ }
+ private class EfIsimPcscfLoaded implements IccRecords.IccRecordLoaded {
+ public String getEfName() {
+ return "EF_ISIM_PCSCF";
+ }
+ public void onRecordLoaded(AsyncResult ar) {
+ ArrayList<byte[]> pcscflist = (ArrayList<byte[]>) ar.result;
+ if (DBG) log("EF_PCSCF record count: " + pcscflist.size());
+ mIsimPcscf = new String[pcscflist.size()];
+ int i = 0;
+ for (byte[] identity : pcscflist) {
+ String pcscf = isimTlvToString(identity);
+ if (DUMP_RECORDS) log("EF_PCSCF[" + i + "]=" + pcscf);
+ mIsimPcscf[i++] = pcscf;
+ }
+ }
+ }
+
/**
* ISIM records for IMS are stored inside a Tag-Length-Value record as a UTF-8 string
* with tag value 0x80.
@@ -194,6 +286,7 @@
// One record loaded successfully or failed, In either case
// we need to update the recordsToLoad count
mRecordsToLoad -= 1;
+ if (DBG) log("onRecordLoaded " + mRecordsToLoad + " requested: " + mRecordsRequested);
if (mRecordsToLoad == 0 && mRecordsRequested == true) {
onAllRecordsLoaded();
@@ -205,10 +298,94 @@
@Override
protected void onAllRecordsLoaded() {
+ if (DBG) log("record load complete");
mRecordsLoadedRegistrants.notifyRegistrants(
new AsyncResult(null, null, null));
}
+ private void handleFileUpdate(int efid) {
+ switch (efid) {
+ case EF_IMPI:
+ mFh.loadEFTransparent(EF_IMPI, obtainMessage(
+ IccRecords.EVENT_GET_ICC_RECORD_DONE, new EfIsimImpiLoaded()));
+ mRecordsToLoad++;
+ break;
+
+ case EF_IMPU:
+ mFh.loadEFLinearFixedAll(EF_IMPU, obtainMessage(
+ IccRecords.EVENT_GET_ICC_RECORD_DONE, new EfIsimImpuLoaded()));
+ mRecordsToLoad++;
+ break;
+
+ case EF_DOMAIN:
+ mFh.loadEFTransparent(EF_DOMAIN, obtainMessage(
+ IccRecords.EVENT_GET_ICC_RECORD_DONE, new EfIsimDomainLoaded()));
+ mRecordsToLoad++;
+ break;
+
+ case EF_IST:
+ mFh.loadEFTransparent(EF_IST, obtainMessage(
+ IccRecords.EVENT_GET_ICC_RECORD_DONE, new EfIsimIstLoaded()));
+ mRecordsToLoad++;
+ break;
+
+ case EF_PCSCF:
+ mFh.loadEFLinearFixedAll(EF_PCSCF, obtainMessage(
+ IccRecords.EVENT_GET_ICC_RECORD_DONE, new EfIsimPcscfLoaded()));
+ mRecordsToLoad++;
+
+ default:
+ fetchIsimRecords();
+ break;
+ }
+ }
+
+ private void handleIsimRefresh(IccRefreshResponse refreshResponse) {
+ if (refreshResponse == null) {
+ if (DBG) log("handleIsimRefresh received without input");
+ return;
+ }
+
+ if (refreshResponse.aid != null &&
+ !refreshResponse.aid.equals(mParentApp.getAid())) {
+ // This is for different app. Ignore.
+ if (DBG) log("handleIsimRefresh received different app");
+ return;
+ }
+
+ switch (refreshResponse.refreshResult) {
+ case IccRefreshResponse.REFRESH_RESULT_FILE_UPDATE:
+ if (DBG) log("handleIsimRefresh with REFRESH_RESULT_FILE_UPDATE");
+ handleFileUpdate(refreshResponse.efId);
+ break;
+
+ case IccRefreshResponse.REFRESH_RESULT_INIT:
+ if (DBG) log("handleIsimRefresh with REFRESH_RESULT_INIT");
+ // need to reload all files (that we care about)
+ // onIccRefreshInit();
+ fetchIsimRecords();
+ break;
+
+ case IccRefreshResponse.REFRESH_RESULT_RESET:
+ if (DBG) log("handleIsimRefresh with REFRESH_RESULT_RESET");
+ // need to reload all files (that we care about)
+ mCi.setRadioPower(false, null);
+ /* Note: no need to call setRadioPower(true). Assuming the desired
+ * radio power state is still ON (as tracked by ServiceStateTracker),
+ * ServiceStateTracker will call setRadioPower when it receives the
+ * RADIO_STATE_CHANGED notification for the power off. And if the
+ * desired power state has changed in the interim, we don't want to
+ * override it with an unconditional power on.
+ */
+ break;
+
+ default:
+ // unknown refresh operation
+ if (DBG) log("handleIsimRefresh with unknown operation");
+ break;
+ }
+ }
+
/**
* Return the IMS private user identity (IMPI).
* Returns null if the IMPI hasn't been loaded or isn't present on the ISIM.
@@ -239,6 +416,52 @@
return (mIsimImpu != null) ? mIsimImpu.clone() : null;
}
+ /**
+ * Returns the IMS Service Table (IST) that was loaded from the ISIM.
+ * @return IMS Service Table or null if not present or not loaded
+ */
+ @Override
+ public String getIsimIst() {
+ return mIsimIst;
+ }
+
+ /**
+ * Returns the IMS Proxy Call Session Control Function(PCSCF) that were loaded from the ISIM.
+ * @return an array of PCSCF strings with one PCSCF per string, or null if
+ * not present or not loaded
+ */
+ @Override
+ public String[] getIsimPcscf() {
+ return (mIsimPcscf != null) ? mIsimPcscf.clone() : null;
+ }
+
+ /**
+ * Returns the response of ISIM Authetification through RIL.
+ * Returns null if the Authentification hasn't been successed or isn't present iphonesubinfo.
+ * @return the response of ISIM Authetification, or null if not available
+ */
+ @Override
+ public String getIsimChallengeResponse(String nonce){
+ if (DBG) log("getIsimChallengeResponse-nonce:"+nonce);
+ try {
+ synchronized(mLock) {
+ mCi.requestIsimAuthentication(nonce,obtainMessage(EVENT_AKA_AUTHENTICATE_DONE));
+ try {
+ mLock.wait();
+ } catch (InterruptedException e) {
+ log("interrupted while trying to request Isim Auth");
+ }
+ }
+ } catch(Exception e) {
+ if (DBG) log( "Fail while trying to request Isim Auth");
+ return null;
+ }
+
+ if (DBG) log("getIsimChallengeResponse-auth_rsp"+auth_rsp);
+
+ return auth_rsp;
+ }
+
@Override
public int getDisplayRule(String plmn) {
// Not applicable to Isim
@@ -252,7 +475,12 @@
@Override
public void onRefresh(boolean fileChanged, int[] fileList) {
- // We do not handle it in Isim
+ if (fileChanged) {
+ // A future optimization would be to inspect fileList and
+ // only reload those files that we care about. For now,
+ // just re-fetch all SIM records that we cache.
+ fetchIsimRecords();
+ }
}
@Override
@@ -284,6 +512,8 @@
pw.println(" mIsimImpi=" + mIsimImpi);
pw.println(" mIsimDomain=" + mIsimDomain);
pw.println(" mIsimImpu[]=" + Arrays.toString(mIsimImpu));
+ pw.println(" mIsimIst" + mIsimIst);
+ pw.println(" mIsimPcscf"+mIsimPcscf);
pw.flush();
}
}
diff --git a/src/java/com/android/internal/telephony/uicc/RuimRecords.java b/src/java/com/android/internal/telephony/uicc/RuimRecords.java
index 49edced..ff2e651 100755
--- a/src/java/com/android/internal/telephony/uicc/RuimRecords.java
+++ b/src/java/com/android/internal/telephony/uicc/RuimRecords.java
@@ -32,6 +32,7 @@
import android.os.AsyncResult;
import android.os.Message;
import android.os.SystemProperties;
+import android.telephony.TelephonyManager;
import android.telephony.Rlog;
import android.text.TextUtils;
@@ -135,6 +136,7 @@
protected void resetRecords() {
mCountVoiceMessages = 0;
mMncLength = UNINITIALIZED;
+ log("setting0 mMncLength" + mMncLength);
mIccId = null;
mAdnCache.reset();
@@ -487,9 +489,13 @@
String operatorNumeric = getRUIMOperatorNumeric();
if (operatorNumeric != null) {
if (operatorNumeric.length() <= 6) {
+ log("update mccmnc=" + operatorNumeric);
MccTable.updateMccMncConfiguration(mContext, operatorNumeric, false);
}
}
+ } else {
+ String operatorNumeric = getRUIMOperatorNumeric();
+ log("NO update mccmnc=" + operatorNumeric);
}
break;
@@ -642,6 +648,7 @@
if (!TextUtils.isEmpty(operator)) {
log("onAllRecordsLoaded set 'gsm.sim.operator.numeric' to operator='" +
operator + "'");
+ log("update icc_operator_numeric=" + operator);
SystemProperties.set(PROPERTY_ICC_OPERATOR_NUMERIC, operator);
} else {
log("onAllRecordsLoaded empty 'gsm.sim.operator.numeric' skipping");
@@ -857,4 +864,12 @@
pw.println(" mHomeNetworkId=" + mHomeNetworkId);
pw.flush();
}
+
+ private void setSystemProperty(String key, String val) {
+ // Update the system properties only in case NON-DSDS.
+ // TODO: Shall have a better approach!
+ if (!TelephonyManager.getDefault().isMultiSimEnabled()) {
+ SystemProperties.set(key, val);
+ }
+ }
}
diff --git a/src/java/com/android/internal/telephony/uicc/SIMRecords.java b/src/java/com/android/internal/telephony/uicc/SIMRecords.java
index b1e744e..dbf573b 100644
--- a/src/java/com/android/internal/telephony/uicc/SIMRecords.java
+++ b/src/java/com/android/internal/telephony/uicc/SIMRecords.java
@@ -23,6 +23,7 @@
import android.os.AsyncResult;
import android.os.Message;
import android.os.SystemProperties;
+import android.telephony.TelephonyManager;
import android.telephony.PhoneNumberUtils;
import android.telephony.SmsMessage;
import android.text.TextUtils;
@@ -227,6 +228,7 @@
mVoiceMailNum = null;
mCountVoiceMessages = 0;
mMncLength = UNINITIALIZED;
+ log("setting0 mMncLength" + mMncLength);
mIccId = null;
// -1 means no EF_SPN found; treat accordingly.
mSpnDisplayCondition = -1;
@@ -239,9 +241,10 @@
mAdnCache.reset();
log("SIMRecords: onRadioOffOrNotAvailable set 'gsm.sim.operator.numeric' to operator=null");
- SystemProperties.set(PROPERTY_ICC_OPERATOR_NUMERIC, null);
- SystemProperties.set(PROPERTY_ICC_OPERATOR_ALPHA, null);
- SystemProperties.set(PROPERTY_ICC_OPERATOR_ISO_COUNTRY, null);
+ log("update icc_operator_numeric=" + null);
+ setSystemProperty(PROPERTY_ICC_OPERATOR_NUMERIC, null);
+ setSystemProperty(PROPERTY_ICC_OPERATOR_ALPHA, null);
+ setSystemProperty(PROPERTY_ICC_OPERATOR_ISO_COUNTRY, null);
// recordsRequested is set to false indicating that the SIM
// read requests made so far are not valid. This is set to
@@ -598,7 +601,8 @@
mImsi = null;
}
- log("IMSI: " + /* imsi.substring(0, 6) +*/ "xxxxxxx");
+ log("IMSI: mMncLength=" + mMncLength);
+ log("IMSI: " + mImsi.substring(0, 6) + "xxxxxxx");
if (((mMncLength == UNKNOWN) || (mMncLength == 2)) &&
((mImsi != null) && (mImsi.length() >= 6))) {
@@ -606,6 +610,7 @@
for (String mccmnc : MCCMNC_CODES_HAVING_3DIGITS_MNC) {
if (mccmnc.equals(mccmncCode)) {
mMncLength = 3;
+ log("IMSI: setting1 mMncLength=" + mMncLength);
break;
}
}
@@ -617,13 +622,15 @@
try {
int mcc = Integer.parseInt(mImsi.substring(0,3));
mMncLength = MccTable.smallestDigitsMccForMnc(mcc);
+ log("setting2 mMncLength=" + mMncLength);
} catch (NumberFormatException e) {
mMncLength = UNKNOWN;
- loge("Corrupt IMSI!");
+ loge("Corrupt IMSI! setting3 mMncLength=" + mMncLength);
}
}
if (mMncLength != UNKNOWN && mMncLength != UNINITIALIZED) {
+ log("update mccmnc=" + mImsi.substring(0, 3 + mMncLength));
// finally have both the imsi and the mncLength and can parse the imsi properly
MccTable.updateMccMncConfiguration(mContext,
mImsi.substring(0, 3 + mMncLength), false);
@@ -854,17 +861,21 @@
}
mMncLength = data[3] & 0xf;
+ log("setting4 mMncLength=" + mMncLength);
if (mMncLength == 0xf) {
mMncLength = UNKNOWN;
+ log("setting5 mMncLength=" + mMncLength);
}
} finally {
if (((mMncLength == UNINITIALIZED) || (mMncLength == UNKNOWN) ||
(mMncLength == 2)) && ((mImsi != null) && (mImsi.length() >= 6))) {
String mccmncCode = mImsi.substring(0, 6);
+ log("mccmncCode=" + mccmncCode);
for (String mccmnc : MCCMNC_CODES_HAVING_3DIGITS_MNC) {
if (mccmnc.equals(mccmncCode)) {
mMncLength = 3;
+ log("setting6 mMncLength=" + mMncLength);
break;
}
}
@@ -876,20 +887,21 @@
int mcc = Integer.parseInt(mImsi.substring(0,3));
mMncLength = MccTable.smallestDigitsMccForMnc(mcc);
+ log("setting7 mMncLength=" + mMncLength);
} catch (NumberFormatException e) {
mMncLength = UNKNOWN;
- loge("Corrupt IMSI!");
+ loge("Corrupt IMSI! setting8 mMncLength=" + mMncLength);
}
} else {
// Indicate we got this info, but it didn't contain the length.
mMncLength = UNKNOWN;
-
- log("MNC length not present in EF_AD");
+ log("MNC length not present in EF_AD setting9 mMncLength=" + mMncLength);
}
}
if (mImsi != null && mMncLength != UNKNOWN) {
// finally have both imsi and the length of the mnc and can parse
// the imsi properly
+ log("update mccmnc=" + mImsi.substring(0, 3 + mMncLength));
MccTable.updateMccMncConfiguration(mContext,
mImsi.substring(0, 3 + mMncLength), false);
}
@@ -1338,14 +1350,15 @@
if (!TextUtils.isEmpty(operator)) {
log("onAllRecordsLoaded set 'gsm.sim.operator.numeric' to operator='" +
operator + "'");
- SystemProperties.set(PROPERTY_ICC_OPERATOR_NUMERIC, operator);
+ log("update icc_operator_numeric=" + operator);
+ setSystemProperty(PROPERTY_ICC_OPERATOR_NUMERIC, operator);
} else {
log("onAllRecordsLoaded empty 'gsm.sim.operator.numeric' skipping");
}
if (!TextUtils.isEmpty(mImsi)) {
log("onAllRecordsLoaded set mcc imsi=" + mImsi);
- SystemProperties.set(PROPERTY_ICC_OPERATOR_ISO_COUNTRY,
+ setSystemProperty(PROPERTY_ICC_OPERATOR_ISO_COUNTRY,
MccTable.countryCodeForMcc(Integer.parseInt(mImsi.substring(0,3))));
} else {
log("onAllRecordsLoaded empty imsi skipping setting mcc");
@@ -1582,7 +1595,7 @@
if (DBG) log("Load EF_SPN: " + mSpn
+ " spnDisplayCondition: " + mSpnDisplayCondition);
- SystemProperties.set(PROPERTY_ICC_OPERATOR_ALPHA, mSpn);
+ setSystemProperty(PROPERTY_ICC_OPERATOR_ALPHA, mSpn);
mSpnState = GetSpnFsmState.IDLE;
} else {
@@ -1603,7 +1616,7 @@
mSpn = IccUtils.adnStringFieldToString(data, 0, data.length);
if (DBG) log("Load EF_SPN_CPHS: " + mSpn);
- SystemProperties.set(PROPERTY_ICC_OPERATOR_ALPHA, mSpn);
+ setSystemProperty(PROPERTY_ICC_OPERATOR_ALPHA, mSpn);
mSpnState = GetSpnFsmState.IDLE;
} else {
@@ -1620,7 +1633,7 @@
mSpn = IccUtils.adnStringFieldToString(data, 0, data.length);
if (DBG) log("Load EF_SPN_SHORT_CPHS: " + mSpn);
- SystemProperties.set(PROPERTY_ICC_OPERATOR_ALPHA, mSpn);
+ setSystemProperty(PROPERTY_ICC_OPERATOR_ALPHA, mSpn);
}else {
if (DBG) log("No SPN loaded in either CHPS or 3GPP");
}
@@ -1771,4 +1784,12 @@
pw.println(" mGid1=" + mGid1);
pw.flush();
}
+
+ private void setSystemProperty(String key, String val) {
+ // Update the system properties only in case NON-DSDS.
+ // TODO: Shall have a better approach!
+ if (!TelephonyManager.getDefault().isMultiSimEnabled()) {
+ SystemProperties.set(key, val);
+ }
+ }
}
diff --git a/src/java/com/android/internal/telephony/uicc/UiccCard.java b/src/java/com/android/internal/telephony/uicc/UiccCard.java
index 7a38421..3d8cb3c 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccCard.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccCard.java
@@ -81,12 +81,23 @@
private static final int EVENT_CLOSE_LOGICAL_CHANNEL_DONE = 16;
private static final int EVENT_TRANSMIT_APDU_DONE = 17;
+ int mSlotId;
+
public UiccCard(Context c, CommandsInterface ci, IccCardStatus ics) {
if (DBG) log("Creating");
mCardState = ics.mCardState;
update(c, ci, ics);
}
+ public UiccCard(Context c, CommandsInterface ci, IccCardStatus ics, int slotId) {
+ mCardState = ics.mCardState;
+ mSlotId = slotId;
+ update(c, ci, ics);
+ }
+
+ protected UiccCard() {
+ }
+
public void dispose() {
synchronized (mLock) {
if (DBG) log("Disposing card");
@@ -134,18 +145,7 @@
}
}
- if (mUiccApplications.length > 0 && mUiccApplications[0] != null) {
- // Initialize or Reinitialize CatService
- mCatService = CatService.getInstance(mCi,
- mContext,
- this);
- } else {
- if (mCatService != null) {
- mCatService.dispose();
- }
- mCatService = null;
- }
-
+ createAndUpdateCatService();
sanitizeApplicationIndexes();
RadioState radioState = mCi.getRadioState();
@@ -168,6 +168,26 @@
}
}
+ protected void createAndUpdateCatService() {
+ if (mUiccApplications.length > 0 && mUiccApplications[0] != null) {
+ // Initialize or Reinitialize CatService
+ if (mCatService == null) {
+ mCatService = CatService.getInstance(mCi, mContext, this, mSlotId);
+ } else {
+ ((CatService)mCatService).update(mCi, mContext, this);
+ }
+ } else {
+ if (mCatService != null) {
+ mCatService.dispose();
+ }
+ mCatService = null;
+ }
+ }
+
+ public CatService getCatService() {
+ return mCatService;
+ }
+
@Override
protected void finalize() {
if (DBG) log("UiccCard finalized");
@@ -362,6 +382,24 @@
}
/**
+ * Returns the SIM application of the specified type.
+ *
+ * @param type ICC application type (@see com.android.internal.telephony.PhoneConstants#APPTYPE_xxx)
+ * @return application corresponding to type or a null if no match found
+ */
+ public UiccCardApplication getApplicationByType(int type) {
+ synchronized (mLock) {
+ for (int i = 0 ; i < mUiccApplications.length; i++) {
+ if (mUiccApplications[i] != null &&
+ mUiccApplications[i].getType().ordinal() == type) {
+ return mUiccApplications[i];
+ }
+ }
+ return null;
+ }
+ }
+
+ /**
* Exposes {@link CommandsInterface.iccOpenLogicalChannel}
*/
public void iccOpenLogicalChannel(String AID, Message response) {
@@ -393,6 +431,17 @@
mCi.sendEnvelopeWithStatus(contents, response);
}
+ /* Returns number of applications on this card */
+ public int getNumApplications() {
+ int count = 0;
+ for (UiccCardApplication a : mUiccApplications) {
+ if (a != null) {
+ count++;
+ }
+ }
+ return count;
+ }
+
private void log(String msg) {
Rlog.d(LOG_TAG, msg);
}
diff --git a/src/java/com/android/internal/telephony/uicc/UiccCardApplication.java b/src/java/com/android/internal/telephony/uicc/UiccCardApplication.java
index 448568e..647ef09 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccCardApplication.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccCardApplication.java
@@ -548,6 +548,10 @@
}
}
+ public String getAppLabel() {
+ return mAppLabel;
+ }
+
public PinState getPin1State() {
synchronized (mLock) {
if (mPin1Replaced) {
diff --git a/src/java/com/android/internal/telephony/uicc/UiccController.java b/src/java/com/android/internal/telephony/uicc/UiccController.java
index 9485e78..073103d 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccController.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccController.java
@@ -22,9 +22,12 @@
import android.os.Message;
import android.os.Registrant;
import android.os.RegistrantList;
+import android.telephony.TelephonyManager;
import android.telephony.Rlog;
import com.android.internal.telephony.CommandsInterface;
+import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.telephony.SubscriptionController;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -80,15 +83,21 @@
private static final int EVENT_ICC_STATUS_CHANGED = 1;
private static final int EVENT_GET_ICC_STATUS_DONE = 2;
+ private CommandsInterface[] mCis;
+ private UiccCard[] mUiccCards = new UiccCard[TelephonyManager.getDefault().getPhoneCount()];
+
private static final Object mLock = new Object();
private static UiccController mInstance;
private Context mContext;
+/*
private CommandsInterface mCi;
private UiccCard mUiccCard;
+*/
- private RegistrantList mIccChangedRegistrants = new RegistrantList();
+ protected RegistrantList mIccChangedRegistrants = new RegistrantList();
+/*
public static UiccController make(Context c, CommandsInterface ci) {
synchronized (mLock) {
if (mInstance != null) {
@@ -98,6 +107,29 @@
return mInstance;
}
}
+*/
+
+ public static UiccController make(Context c, CommandsInterface[] ci) {
+ synchronized (mLock) {
+ if (mInstance != null) {
+ throw new RuntimeException("MSimUiccController.make() should only be called once");
+ }
+ mInstance = new UiccController(c, ci);
+ return (UiccController)mInstance;
+ }
+ }
+
+ private UiccController(Context c, CommandsInterface []ci) {
+ if (DBG) log("Creating UiccController");
+ mContext = c;
+ mCis = ci;
+ for (int i = 0; i < mCis.length; i++) {
+ Integer index = new Integer(i);
+ mCis[i].registerForIccStatusChanged(this, EVENT_ICC_STATUS_CHANGED, index);
+ // TODO remove this once modem correctly notifies the unsols
+ mCis[i].registerForAvailable(this, EVENT_ICC_STATUS_CHANGED, index);
+ }
+ }
public static UiccController getInstance() {
synchronized (mLock) {
@@ -110,21 +142,32 @@
}
public UiccCard getUiccCard() {
- synchronized (mLock) {
- return mUiccCard;
- }
+ return getUiccCard(SubscriptionController.getInstance().getPhoneId(SubscriptionController.getInstance().getDefaultSubId()));
}
- // Easy to use API
- public UiccCardApplication getUiccCardApplication(int family) {
+ public UiccCard getUiccCard(int slotId) {
synchronized (mLock) {
- if (mUiccCard != null) {
- return mUiccCard.getApplication(family);
+ if (isValidCardIndex(slotId)) {
+ return mUiccCards[slotId];
}
return null;
}
}
+ public UiccCard[] getUiccCards() {
+ // Return cloned array since we don't want to give out reference
+ // to internal data structure.
+ synchronized (mLock) {
+ return mUiccCards.clone();
+ }
+ }
+
+ // Easy to use API
+ public UiccCardApplication getUiccCardApplication(int family) {
+ return getUiccCardApplication(SubscriptionController.getInstance().getPhoneId(SubscriptionController.getInstance().getDefaultSubId()), family);
+ }
+
+/*
// Easy to use API
public IccRecords getIccRecords(int family) {
synchronized (mLock) {
@@ -137,8 +180,21 @@
return null;
}
}
+*/
// Easy to use API
+ public IccRecords getIccRecords(int slotId, int family) {
+ synchronized (mLock) {
+ UiccCardApplication app = getUiccCardApplication(slotId, family);
+ if (app != null) {
+ return app.getIccRecords();
+ }
+ return null;
+ }
+ }
+
+/*
+ // Easy to use API
public IccFileHandler getIccFileHandler(int family) {
synchronized (mLock) {
if (mUiccCard != null) {
@@ -150,6 +206,19 @@
return null;
}
}
+*/
+
+ // Easy to use API
+ public IccFileHandler getIccFileHandler(int slotId, int family) {
+ synchronized (mLock) {
+ UiccCardApplication app = getUiccCardApplication(slotId, family);
+ if (app != null) {
+ return app.getIccFileHandler();
+ }
+ return null;
+ }
+ }
+
//Notifies when card status changes
public void registerForIccChanged(Handler h, int what, Object obj) {
@@ -171,15 +240,22 @@
@Override
public void handleMessage (Message msg) {
synchronized (mLock) {
+ Integer index = getCiIndex(msg);
+
+ if (index < 0 || index >= mCis.length) {
+ Rlog.e(LOG_TAG, "Invalid index : " + index + " received with event " + msg.what);
+ return;
+ }
+
switch (msg.what) {
case EVENT_ICC_STATUS_CHANGED:
if (DBG) log("Received EVENT_ICC_STATUS_CHANGED, calling getIccCardStatus");
- mCi.getIccCardStatus(obtainMessage(EVENT_GET_ICC_STATUS_DONE));
+ mCis[index].getIccCardStatus(obtainMessage(EVENT_GET_ICC_STATUS_DONE, index));
break;
case EVENT_GET_ICC_STATUS_DONE:
if (DBG) log("Received EVENT_GET_ICC_STATUS_DONE");
AsyncResult ar = (AsyncResult)msg.obj;
- onGetIccCardStatusDone(ar);
+ onGetIccCardStatusDone(ar, index);
break;
default:
Rlog.e(LOG_TAG, " Unknown Event " + msg.what);
@@ -187,6 +263,29 @@
}
}
+ private Integer getCiIndex(Message msg) {
+ AsyncResult ar;
+ Integer index = new Integer(PhoneConstants.DEFAULT_CARD_INDEX);
+
+ /*
+ * The events can be come in two ways. By explicitly sending it using
+ * sendMessage, in this case the user object passed is msg.obj and from
+ * the CommandsInterface, in this case the user object is msg.obj.userObj
+ */
+ if (msg != null) {
+ if (msg.obj != null && msg.obj instanceof Integer) {
+ index = (Integer)msg.obj;
+ } else if(msg.obj != null && msg.obj instanceof AsyncResult) {
+ ar = (AsyncResult)msg.obj;
+ if (ar.userObj != null && ar.userObj instanceof Integer) {
+ index = (Integer)ar.userObj;
+ }
+ }
+ }
+ return index;
+ }
+
+/*
private UiccController(Context c, CommandsInterface ci) {
if (DBG) log("Creating UiccController");
mContext = c;
@@ -195,39 +294,71 @@
// This is needed so that we query for sim status in the case when we boot in APM
mCi.registerForAvailable(this, EVENT_ICC_STATUS_CHANGED, null);
}
+*/
- private synchronized void onGetIccCardStatusDone(AsyncResult ar) {
+ // Easy to use API
+ public UiccCardApplication getUiccCardApplication(int slotId, int family) {
+ synchronized (mLock) {
+ if (isValidCardIndex(slotId)) {
+ UiccCard c = mUiccCards[slotId];
+ if (c != null) {
+ return mUiccCards[slotId].getApplication(family);
+ }
+ }
+ return null;
+ }
+ }
+
+ private synchronized void onGetIccCardStatusDone(AsyncResult ar, Integer index) {
if (ar.exception != null) {
Rlog.e(LOG_TAG,"Error getting ICC status. "
+ "RIL_REQUEST_GET_ICC_STATUS should "
+ "never return an error", ar.exception);
return;
}
+ if (!isValidCardIndex(index)) {
+ Rlog.e(LOG_TAG,"onGetIccCardStatusDone: invalid index : " + index);
+ return;
+ }
IccCardStatus status = (IccCardStatus)ar.result;
- if (mUiccCard == null) {
+ if (mUiccCards[index] == null) {
//Create new card
- mUiccCard = new UiccCard(mContext, mCi, status);
+ mUiccCards[index] = new UiccCard(mContext, mCis[index], status, index);
+
+/*
+ // Update the UiccCard in base class, so that if someone calls
+ // UiccManager.getUiccCard(), it will return the default card.
+ if (index == PhoneConstants.DEFAULT_CARD_INDEX) {
+ mUiccCard = mUiccCards[index];
+ }
+*/
} else {
//Update already existing card
- mUiccCard.update(mContext, mCi , status);
+ mUiccCards[index].update(mContext, mCis[index] , status);
}
if (DBG) log("Notifying IccChangedRegistrants");
- mIccChangedRegistrants.notifyRegistrants();
+ mIccChangedRegistrants.notifyRegistrants(new AsyncResult(null, index, null));
+
+ }
+
+ private boolean isValidCardIndex(int index) {
+ return (index >= 0 && index < mUiccCards.length);
}
private void log(String string) {
Rlog.d(LOG_TAG, string);
}
+
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("UiccController: " + this);
pw.println(" mContext=" + mContext);
pw.println(" mInstance=" + mInstance);
- pw.println(" mCi=" + mCi);
- pw.println(" mUiccCard=" + mUiccCard);
+// pw.println(" mCi=" + mCi);
+// pw.println(" mUiccCard=" + mUiccCard);
pw.println(" mIccChangedRegistrants: size=" + mIccChangedRegistrants.size());
for (int i = 0; i < mIccChangedRegistrants.size(); i++) {
pw.println(" mIccChangedRegistrants[" + i + "]="
@@ -235,8 +366,8 @@
}
pw.println();
pw.flush();
- if (mUiccCard != null) {
- mUiccCard.dump(fd, pw, args);
- }
+// for (int i = 0; i < mUiccCards.length; i++) {
+// mUiccCards[i].dump(fd, pw, args);
+// }
}
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/gsm/GSMPhoneTest.java b/tests/telephonytests/src/com/android/internal/telephony/gsm/GSMPhoneTest.java
index 699f113..2e9b88c 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/gsm/GSMPhoneTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/gsm/GSMPhoneTest.java
@@ -23,6 +23,8 @@
import android.test.AndroidTestCase;
import android.test.PerformanceTestCase;
+import android.telephony.DisconnectCause;
+
import com.android.internal.telephony.Call;
import com.android.internal.telephony.CallStateException;
import com.android.internal.telephony.Connection;
@@ -187,7 +189,7 @@
assertTrue(!cn.isIncoming());
assertEquals(Connection.PostDialState.NOT_STARTED, cn.getPostDialState());
- assertEquals(Connection.DisconnectCause.NOT_DISCONNECTED, cn.getDisconnectCause());
+ assertEquals(DisconnectCause.NOT_DISCONNECTED, cn.getDisconnectCause());
assertFalse(mGSMPhone.canConference());
@@ -393,7 +395,7 @@
assertEquals(Call.State.DISCONNECTED, cn.getState());
- assertEquals(Connection.DisconnectCause.LOCAL, cn.getDisconnectCause());
+ assertEquals(DisconnectCause.LOCAL, cn.getDisconnectCause());
assertFalse(mGSMPhone.canConference());
@@ -404,7 +406,7 @@
assertFalse(mGSMPhone.getForegroundCall().isDialingOrAlerting());
assertFalse(mGSMPhone.getRingingCall().isRinging());
- assertEquals(Connection.DisconnectCause.LOCAL, cn.getDisconnectCause());
+ assertEquals(DisconnectCause.LOCAL, cn.getDisconnectCause());
assertEquals(0, mGSMPhone.getForegroundCall().getConnections().size());
assertEquals(PhoneConstants.State.IDLE, mGSMPhone.getState());
@@ -480,7 +482,7 @@
cn = mGSMPhone.getRingingCall().getEarliestConnection();
assertEquals(Call.State.DISCONNECTED, cn.getState());
- assertEquals(Connection.DisconnectCause.INCOMING_MISSED, cn.getDisconnectCause());
+ assertEquals(DisconnectCause.INCOMING_MISSED, cn.getDisconnectCause());
assertFalse(mGSMPhone.canConference());
@@ -488,7 +490,7 @@
mGSMPhone.clearDisconnected();
- assertEquals(Connection.DisconnectCause.INCOMING_MISSED, cn.getDisconnectCause());
+ assertEquals(DisconnectCause.INCOMING_MISSED, cn.getDisconnectCause());
assertEquals(0, mGSMPhone.getForegroundCall().getConnections().size());
assertEquals(PhoneConstants.State.IDLE, mGSMPhone.getState());
@@ -528,7 +530,7 @@
assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
} while (mGSMPhone.getState() != PhoneConstants.State.IDLE);
- assertEquals(Connection.DisconnectCause.INCOMING_MISSED, cn.getDisconnectCause());
+ assertEquals(DisconnectCause.INCOMING_MISSED, cn.getDisconnectCause());
// One Ringing Call
@@ -582,7 +584,7 @@
} while (mGSMPhone.getState() != PhoneConstants.State.IDLE);
assertEquals(Call.State.DISCONNECTED, mGSMPhone.getForegroundCall().getState());
- assertEquals(Connection.DisconnectCause.NORMAL, cn.getDisconnectCause());
+ assertEquals(DisconnectCause.NORMAL, cn.getDisconnectCause());
// Test missed calls
@@ -601,7 +603,7 @@
ar = (AsyncResult) msg.obj;
cn = (Connection) ar.result;
- assertEquals(Connection.DisconnectCause.INCOMING_MISSED, cn.getDisconnectCause());
+ assertEquals(DisconnectCause.INCOMING_MISSED, cn.getDisconnectCause());
assertEquals(PhoneConstants.State.IDLE, mGSMPhone.getState());
assertEquals(Call.State.DISCONNECTED, mGSMPhone.getRingingCall().getState());
@@ -621,7 +623,7 @@
assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
} while (mGSMPhone.getState() != PhoneConstants.State.OFFHOOK);
- assertEquals(Connection.DisconnectCause.NOT_DISCONNECTED, cn.getDisconnectCause());
+ assertEquals(DisconnectCause.NOT_DISCONNECTED, cn.getDisconnectCause());
assertEquals(Call.State.IDLE, mGSMPhone.getRingingCall().getState());
assertEquals(Call.State.ACTIVE, mGSMPhone.getForegroundCall().getState());
@@ -637,7 +639,7 @@
} while (mGSMPhone.getForegroundCall().getState()
!= Call.State.DISCONNECTED);
- assertEquals(Connection.DisconnectCause.LOCAL, cn.getDisconnectCause());
+ assertEquals(DisconnectCause.LOCAL, cn.getDisconnectCause());
//
// Test held and hangup held calls
@@ -772,7 +774,7 @@
assertNotNull("Message Time Out", msg);
assertEquals(PhoneConstants.State.IDLE, mGSMPhone.getState());
- assertEquals(Connection.DisconnectCause.NORMAL, cn.getDisconnectCause());
+ assertEquals(DisconnectCause.NORMAL, cn.getDisconnectCause());
assertEquals(0, mGSMPhone.getRingingCall().getConnections().size());
assertEquals(1, mGSMPhone.getForegroundCall().getConnections().size());
@@ -809,7 +811,7 @@
assertEquals(PhoneConstants.State.IDLE, mGSMPhone.getState());
assertEquals(Call.State.DISCONNECTED, mGSMPhone.getForegroundCall().getState());
- assertEquals(Connection.DisconnectCause.LOCAL, cn.getDisconnectCause());
+ assertEquals(DisconnectCause.LOCAL, cn.getDisconnectCause());
// Test 2: local hangup in "ALERTING" state
mGSMPhone.dial("+13125551212");
@@ -835,7 +837,7 @@
assertEquals(PhoneConstants.State.IDLE, mGSMPhone.getState());
assertEquals(Call.State.DISCONNECTED, mGSMPhone.getForegroundCall().getState());
- assertEquals(Connection.DisconnectCause.LOCAL, cn.getDisconnectCause());
+ assertEquals(DisconnectCause.LOCAL, cn.getDisconnectCause());
// Test 3: local immediate hangup before GSM index is
// assigned (CallTracker.hangupPendingMO case)
@@ -854,7 +856,7 @@
assertEquals(Call.State.DISCONNECTED, mGSMPhone.getForegroundCall().getState());
- assertEquals(Connection.DisconnectCause.LOCAL,
+ assertEquals(DisconnectCause.LOCAL,
mGSMPhone.getForegroundCall().getEarliestConnection().getDisconnectCause());
}
@@ -1521,7 +1523,7 @@
assertNotNull("Message Time Out", msg);
assertEquals(PhoneConstants.State.IDLE, mGSMPhone.getState());
- assertEquals(Connection.DisconnectCause.NORMAL, cn.getDisconnectCause());
+ assertEquals(DisconnectCause.NORMAL, cn.getDisconnectCause());
assertEquals(0, mGSMPhone.getRingingCall().getConnections().size());
assertEquals(1, mGSMPhone.getForegroundCall().getConnections().size());
@@ -1559,7 +1561,7 @@
assertNotNull("Message Time Out", msg);
assertEquals(PhoneConstants.State.IDLE, mGSMPhone.getState());
- assertEquals(Connection.DisconnectCause.BUSY, cn.getDisconnectCause());
+ assertEquals(DisconnectCause.BUSY, cn.getDisconnectCause());
assertEquals(0, mGSMPhone.getRingingCall().getConnections().size());
assertEquals(1, mGSMPhone.getForegroundCall().getConnections().size());
@@ -1606,7 +1608,7 @@
assertEquals(PhoneConstants.State.IDLE, mGSMPhone.getState());
- assertEquals(Connection.DisconnectCause.CONGESTION, cn.getDisconnectCause());
+ assertEquals(DisconnectCause.CONGESTION, cn.getDisconnectCause());
assertEquals(0, mGSMPhone.getRingingCall().getConnections().size());
assertEquals(1, mGSMPhone.getForegroundCall().getConnections().size());
diff --git a/tests/telephonytests/src/com/android/internal/telephony/gsm/UsimDataDownloadCommands.java b/tests/telephonytests/src/com/android/internal/telephony/gsm/UsimDataDownloadCommands.java
index f179468..e64b597 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/gsm/UsimDataDownloadCommands.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/gsm/UsimDataDownloadCommands.java
@@ -625,6 +625,10 @@
}
@Override
+ public void requestIccSimAuthentication(String data, Message response) {
+ }
+
+ @Override
public void getVoiceRadioTechnology(Message response) {
}