Merge kwd to master
Wink Saville [Thu, 12 Jun 2014 23:49:23 +0000 (16:49 -0700)]
Change-Id: Idb607c0aa32f80fe4fe1539aedea7a221e9e7f04

98 files changed:
Android.mk
CleanSpec.mk
src/java/android/provider/Telephony.java
src/java/android/telephony/CellBroadcastMessage.java
src/java/android/telephony/SmsManager.java
src/java/android/telephony/SmsMessage.java
src/java/com/android/internal/telephony/BaseCommands.java
src/java/com/android/internal/telephony/Call.java
src/java/com/android/internal/telephony/CallManager.java
src/java/com/android/internal/telephony/CellBroadcastHandler.java
src/java/com/android/internal/telephony/CommandException.java
src/java/com/android/internal/telephony/CommandsInterface.java
src/java/com/android/internal/telephony/Connection.java
src/java/com/android/internal/telephony/DefaultPhoneNotifier.java
src/java/com/android/internal/telephony/HardwareConfig.java [new file with mode: 0644]
src/java/com/android/internal/telephony/IIccPhoneBook.aidl
src/java/com/android/internal/telephony/IccPhoneBookInterfaceManager.java
src/java/com/android/internal/telephony/IccPhoneBookInterfaceManagerProxy.java
src/java/com/android/internal/telephony/IccProvider.java [changed mode: 0644->0755]
src/java/com/android/internal/telephony/IccSmsInterfaceManager.java
src/java/com/android/internal/telephony/InboundSmsHandler.java
src/java/com/android/internal/telephony/MccTable.java
src/java/com/android/internal/telephony/MmiCode.java
src/java/com/android/internal/telephony/Phone.java
src/java/com/android/internal/telephony/PhoneBase.java
src/java/com/android/internal/telephony/PhoneFactory.java
src/java/com/android/internal/telephony/PhoneNotifier.java
src/java/com/android/internal/telephony/PhoneProxy.java
src/java/com/android/internal/telephony/PhoneSubInfo.java
src/java/com/android/internal/telephony/PhoneSubInfoController.java [new file with mode: 0644]
src/java/com/android/internal/telephony/PhoneSubInfoProxy.java
src/java/com/android/internal/telephony/ProxyController.java [new file with mode: 0644]
src/java/com/android/internal/telephony/RIL.java
src/java/com/android/internal/telephony/ServiceStateTracker.java
src/java/com/android/internal/telephony/SmsStorageMonitor.java [changed mode: 0644->0755]
src/java/com/android/internal/telephony/SubInfoRecordUpdater.java [new file with mode: 0644]
src/java/com/android/internal/telephony/Subscription.java [new file with mode: 0644]
src/java/com/android/internal/telephony/SubscriptionController.java [new file with mode: 0644]
src/java/com/android/internal/telephony/SubscriptionData.java [new file with mode: 0644]
src/java/com/android/internal/telephony/TelephonyCapabilities.java
src/java/com/android/internal/telephony/TelephonyDevController.java [new file with mode: 0644]
src/java/com/android/internal/telephony/UiccPhoneBookController.java [new file with mode: 0644]
src/java/com/android/internal/telephony/UiccSmsController.java [new file with mode: 0755]
src/java/com/android/internal/telephony/VoicePhone.java [new file with mode: 0644]
src/java/com/android/internal/telephony/WapPushOverSms.java
src/java/com/android/internal/telephony/cat/CatService.java
src/java/com/android/internal/telephony/cat/RilMessageDecoder.java [changed mode: 0644->0755]
src/java/com/android/internal/telephony/cdma/CDMALTEPhone.java
src/java/com/android/internal/telephony/cdma/CDMAPhone.java
src/java/com/android/internal/telephony/cdma/CdmaConnection.java
src/java/com/android/internal/telephony/cdma/CdmaInboundSmsHandler.java
src/java/com/android/internal/telephony/cdma/CdmaLteServiceStateTracker.java
src/java/com/android/internal/telephony/cdma/CdmaMmiCode.java
src/java/com/android/internal/telephony/cdma/CdmaServiceCategoryProgramHandler.java
src/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
src/java/com/android/internal/telephony/cdma/CdmaSubscriptionSourceManager.java
src/java/com/android/internal/telephony/cdma/SmsMessage.java
src/java/com/android/internal/telephony/dataconnection/DataCallResponse.java
src/java/com/android/internal/telephony/dataconnection/DataConnection.java
src/java/com/android/internal/telephony/dataconnection/DcAsyncChannel.java
src/java/com/android/internal/telephony/dataconnection/DcController.java
src/java/com/android/internal/telephony/dataconnection/DcSwitchAsyncChannel.java [new file with mode: 0644]
src/java/com/android/internal/telephony/dataconnection/DcSwitchState.java [new file with mode: 0644]
src/java/com/android/internal/telephony/dataconnection/DcTracker.java
src/java/com/android/internal/telephony/dataconnection/DcTrackerBase.java
src/java/com/android/internal/telephony/dataconnection/DctController.java [new file with mode: 0644]
src/java/com/android/internal/telephony/gsm/GSMPhone.java
src/java/com/android/internal/telephony/gsm/GsmCallTracker.java
src/java/com/android/internal/telephony/gsm/GsmConnection.java
src/java/com/android/internal/telephony/gsm/GsmMmiCode.java
src/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
src/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
src/java/com/android/internal/telephony/imsphone/ImsPhone.java [new file with mode: 0644]
src/java/com/android/internal/telephony/imsphone/ImsPhoneBase.java [new file with mode: 0644]
src/java/com/android/internal/telephony/imsphone/ImsPhoneCall.java [new file with mode: 0644]
src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java [new file with mode: 0644]
src/java/com/android/internal/telephony/imsphone/ImsPhoneCommandInterface.java [new file with mode: 0644]
src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java [new file with mode: 0644]
src/java/com/android/internal/telephony/imsphone/ImsPhoneFactory.java [new file with mode: 0644]
src/java/com/android/internal/telephony/imsphone/ImsPhoneMmiCode.java [new file with mode: 0644]
src/java/com/android/internal/telephony/sip/SipCommandInterface.java
src/java/com/android/internal/telephony/sip/SipConnectionBase.java
src/java/com/android/internal/telephony/test/SimulatedCommands.java
src/java/com/android/internal/telephony/uicc/IccCardApplicationStatus.java
src/java/com/android/internal/telephony/uicc/IccCardProxy.java
src/java/com/android/internal/telephony/uicc/IccConstants.java
src/java/com/android/internal/telephony/uicc/IccFileHandler.java
src/java/com/android/internal/telephony/uicc/IccRecords.java
src/java/com/android/internal/telephony/uicc/IsimFileHandler.java
src/java/com/android/internal/telephony/uicc/IsimRecords.java
src/java/com/android/internal/telephony/uicc/IsimUiccRecords.java
src/java/com/android/internal/telephony/uicc/RuimRecords.java
src/java/com/android/internal/telephony/uicc/SIMRecords.java
src/java/com/android/internal/telephony/uicc/UiccCard.java
src/java/com/android/internal/telephony/uicc/UiccCardApplication.java
src/java/com/android/internal/telephony/uicc/UiccController.java
tests/telephonytests/src/com/android/internal/telephony/gsm/GSMPhoneTest.java
tests/telephonytests/src/com/android/internal/telephony/gsm/UsimDataDownloadCommands.java

index b0ab1d0..449faa0 100644 (file)
@@ -24,7 +24,7 @@ LOCAL_SRC_FILES := $(call all-java-files-under, src/java) \
        $(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
 
index c5b95a1..4f5f260 100644 (file)
@@ -44,6 +44,7 @@ $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/teleph
 $(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
index 82ad215..ae15b3a 100644 (file)
@@ -27,10 +27,12 @@ import android.database.Cursor;
 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 final class Telephony {
         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 final class Telephony {
         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 final class Telephony {
         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 final class Telephony {
             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 final class Telephony {
              */
             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 final class Telephony {
              */
             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 final class Telephony {
             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 final class Telephony {
             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 @@ public final class Telephony {
                 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 @@ public final class Telephony {
          * <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 @@ public final class Telephony {
              * <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 @@ public final class Telephony {
          * <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";
+
     }
 
     /**
index af11bc4..1d30608 100644 (file)
@@ -49,6 +49,31 @@ public class CellBroadcastMessage implements Parcelable {
     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();
index 3af756e..8fd48f4 100644 (file)
@@ -47,6 +47,7 @@ import java.util.List;
 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 final class SmsManager {
     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 final class SmsManager {
     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 final class SmsManager {
     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 @@ public final class SmsManager {
      * @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 final class SmsManager {
      */
     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 @@ public final class SmsManager {
      * {@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 @@ public final class SmsManager {
      * {@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 @@ public final class SmsManager {
      * {@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 @@ public final class SmsManager {
      * {@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 @@ public final class SmsManager {
      * {@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 @@ public final class SmsManager {
      * {@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 @@ public final class SmsManager {
      * @hide
      */
     boolean isImsSmsSupported() {
+        return isImsSmsSupported(getPreferredSmsSubscription());
+    }
+
+    /** @hide */
+    boolean isImsSmsSupported(long subId) {
         boolean boSupported = false;
         try {
             ISms iccISms = getISmsService();
@@ -589,6 +872,11 @@ public final class SmsManager {
      * @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 @@ public final class SmsManager {
         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). */
index 12d6949..64368aa 100644 (file)
@@ -96,6 +96,28 @@ public class SmsMessage {
      */
     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.
index 19ff4ed..47dcc99 100644 (file)
@@ -18,6 +18,7 @@ package com.android.internal.telephony;
 
 
 import android.content.Context;
+import android.os.Message;
 import android.os.RegistrantList;
 import android.os.Registrant;
 import android.os.Handler;
@@ -64,6 +65,9 @@ public abstract class BaseCommands implements CommandsInterface {
     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 @@ public abstract class BaseCommands implements CommandsInterface {
         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 @@ public abstract class BaseCommands implements CommandsInterface {
         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 @@ public abstract class BaseCommands implements CommandsInterface {
     }
 
     @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) {
+    }
 }
index b83d33e..46df399 100644 (file)
@@ -45,6 +45,9 @@ public abstract class Call {
         }
     }
 
+    public enum SrvccState {
+        NONE, STARTED, COMPLETED, FAILED, CANCELED;
+    }
 
     /* Instance Variables */
 
index aa4cca8..637b6eb 100644 (file)
@@ -26,6 +26,8 @@ import android.os.Message;
 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 @@ public final class CallManager {
     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 @@ public final class CallManager {
     protected final RegistrantList mRingbackToneRegistrants
     = new RegistrantList();
 
+    protected final RegistrantList mOnHoldToneRegistrants
+    = new RegistrantList();
+
     protected final RegistrantList mInCallVoicePrivacyOnRegistrants
     = new RegistrantList();
 
@@ -232,6 +238,21 @@ public final class CallManager {
     }
 
     /**
+     * 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 @@ public final class CallManager {
     }
 
     /**
+     * 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 @@ public final class CallManager {
     }
 
     /**
+     * @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 @@ public final class CallManager {
                         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 @@ public final class CallManager {
     }
 
     /**
+     * @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 @@ public final class CallManager {
     }
 
     /**
+     * @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 @@ public final class CallManager {
 
                 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 @@ public final class CallManager {
                 mSpeedUpAudioForMtCall = false;
                 break;
         }
+        Rlog.d(LOG_TAG, "setAudioMode state = " + getState());
     }
 
     private Context getContext() {
@@ -454,9 +589,10 @@ public final class CallManager {
         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 @@ public final class CallManager {
             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 @@ public final class CallManager {
         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 @@ public final class CallManager {
             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 @@ public final class CallManager {
             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 @@ public final class CallManager {
     }
 
     /**
+     * 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 @@ public final class CallManager {
      * 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 final class CallManager {
      */
     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 @@ public final class CallManager {
             }
         }
 
-        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 @@ public final class CallManager {
     }
 
     /**
+     * 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 @@ public final class CallManager {
      */
     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 @@ public final class CallManager {
     }
 
     /**
+     * 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 @@ public final class CallManager {
     }
 
     /**
+     * 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 @@ public final class CallManager {
     }
 
     /**
-     * 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 @@ public final class CallManager {
     }
 
     /**
+     * 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 @@ public final class CallManager {
     }
 
     /**
+     * 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 @@ public final class CallManager {
     }
 
     /**
+     * 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 @@ public final class CallManager {
         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 @@ public final class CallManager {
         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 @@ public final class CallManager {
     }
 
     /**
+     * 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 @@ public final class CallManager {
         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 @@ public final class CallManager {
         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 @@ public final class CallManager {
     }
 
     /**
+     * @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 @@ public final class CallManager {
     }
 
     /**
+     * @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 @@ public final class CallManager {
     }
 
     /**
+     * @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 @@ public final class CallManager {
     }
 
     /**
+     * @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 @@ public final class CallManager {
     }
 
     /**
+     * @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 @@ public final class CallManager {
     }
 
     /**
+     * @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 @@ public final class CallManager {
         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 @@ public final class CallManager {
         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 @@ public final class CallManager {
                     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 @@ public final class CallManager {
                         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 final class CallManager {
     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) {
index f7e0e3a..36f9521 100644 (file)
@@ -23,6 +23,7 @@ import android.content.Context;
 import android.content.Intent;
 import android.os.Message;
 import android.provider.Telephony;
+import android.telephony.SubscriptionManager;
 import android.telephony.SmsCbMessage;
 
 /**
@@ -88,6 +89,7 @@ public class CellBroadcastHandler extends WakeLockStateMachine {
             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);
     }
index 80338ae..bf24689 100644 (file)
@@ -44,6 +44,7 @@ public class CommandException extends RuntimeException {
         ILLEGAL_SIM_OR_ME,
         MISSING_RESOURCE,
         NO_SUCH_ELEMENT,
+        SUBSCRIPTION_NOT_SUPPORTED,
     }
 
     public CommandException(Error e) {
@@ -89,6 +90,8 @@ public class CommandException extends RuntimeException {
                 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);
index d292753..e75de0b 100644 (file)
@@ -191,6 +191,26 @@ public interface CommandsInterface {
     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 @@ public interface CommandsInterface {
      *
      * @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 @@ public interface CommandsInterface {
      * 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 @@ public interface CommandsInterface {
     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);
 }
index 59a5586..e1ef562 100644 (file)
@@ -99,6 +99,14 @@ public abstract class Connection {
     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 class Connection {
     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 class Connection {
     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.
index c001b79..d1cc618 100644 (file)
@@ -23,12 +23,19 @@ import android.os.RemoteException;
 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 @@ import java.util.List;
  * 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 @@ public class DefaultPhoneNotifier implements PhoneNotifier {
     @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 @@ public class DefaultPhoneNotifier implements PhoneNotifier {
     @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 @@ public class DefaultPhoneNotifier implements PhoneNotifier {
 
     @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 @@ public class DefaultPhoneNotifier implements PhoneNotifier {
 
     @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 @@ public class DefaultPhoneNotifier implements PhoneNotifier {
 
     @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 @@ public class DefaultPhoneNotifier implements PhoneNotifier {
 
     @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 @@ public class DefaultPhoneNotifier implements PhoneNotifier {
 
     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 @@ public class DefaultPhoneNotifier implements PhoneNotifier {
 
         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 @@ public class DefaultPhoneNotifier implements PhoneNotifier {
 
     @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 @@ public class DefaultPhoneNotifier implements PhoneNotifier {
 
     @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 @@ public class DefaultPhoneNotifier implements PhoneNotifier {
 
     @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 @@ public class DefaultPhoneNotifier implements PhoneNotifier {
 
     @Override
     public void notifyOtaspChanged(Phone sender, int otaspMode) {
+        // FIXME: subId?
         try {
             if (mRegistry != null) {
                 mRegistry.notifyOtaspChanged(otaspMode);
@@ -219,6 +290,7 @@ public class DefaultPhoneNotifier implements PhoneNotifier {
     }
 
     public void notifyPreciseCallState(Phone sender) {
+        // FIXME: subId?
         Call ringingCall = sender.getRingingCall();
         Call foregroundCall = sender.getForegroundCall();
         Call backgroundCall = sender.getBackgroundCall();
@@ -235,6 +307,7 @@ public class DefaultPhoneNotifier implements PhoneNotifier {
     }
 
     public void notifyDisconnectCause(int cause, int preciseCause) {
+        // FIXME: subId?
         try {
             mRegistry.notifyDisconnectCause(cause, preciseCause);
         } catch (RemoteException ex) {
@@ -244,6 +317,7 @@ public class DefaultPhoneNotifier implements PhoneNotifier {
 
     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 @@ public class DefaultPhoneNotifier implements PhoneNotifier {
         }
     }
 
+    @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 @@ public class DefaultPhoneNotifier implements PhoneNotifier {
                 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 (file)
index 0000000..0f855de
--- /dev/null
@@ -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));
+    }
+}
index 95b3efb..0f802cc 100644 (file)
@@ -46,6 +46,16 @@ interface IIccPhoneBook {
     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 @@ interface IIccPhoneBook {
             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 @@ interface IIccPhoneBook {
             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 @@ interface IIccPhoneBook {
      */
     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);
+
 }
index d4ae7fb..8699148 100644 (file)
@@ -28,6 +28,8 @@ import com.android.internal.telephony.uicc.AdnRecordCache;
 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 @@ import java.util.concurrent.atomic.AtomicBoolean;
  * 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 @@ public abstract class IccPhoneBookInterfaceManager extends IIccPhoneBook.Stub {
         }
     }
 
-    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 @@ public abstract class IccPhoneBookInterfaceManager extends IIccPhoneBook.Stub {
      * @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 @@ public abstract class IccPhoneBookInterfaceManager extends IIccPhoneBook.Stub {
      * @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 @@ public abstract class IccPhoneBookInterfaceManager extends IIccPhoneBook.Stub {
      *            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 @@ public abstract class IccPhoneBookInterfaceManager extends IIccPhoneBook.Stub {
      * @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(
index 3a2fbc5..b2ccbfe 100644 (file)
@@ -27,15 +27,12 @@ import java.util.List;
  * 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 @@ public class IccPhoneBookInterfaceManagerProxy extends IIccPhoneBook.Stub {
         mIccPhoneBookInterfaceManager = iccPhoneBookInterfaceManager;
     }
 
-    @Override
     public boolean
     updateAdnRecordsInEfBySearch (int efid,
             String oldTag, String oldPhoneNumber,
@@ -53,7 +49,6 @@ public class IccPhoneBookInterfaceManagerProxy extends IIccPhoneBook.Stub {
                 efid, oldTag, oldPhoneNumber, newTag, newPhoneNumber, pin2);
     }
 
-    @Override
     public boolean
     updateAdnRecordsInEfByIndex(int efid, String newTag,
             String newPhoneNumber, int index, String pin2) {
@@ -61,12 +56,10 @@ public class IccPhoneBookInterfaceManagerProxy extends IIccPhoneBook.Stub {
                 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);
     }
old mode 100644 (file)
new mode 100755 (executable)
index 6f2c4ed..79d2eba
@@ -20,10 +20,14 @@ import android.content.ContentProvider;
 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 @@ import com.android.internal.telephony.uicc.IccConstants;
  */
 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 @@ public class IccProvider extends ContentProvider {
         "_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 @@ public class IccProvider extends ContentProvider {
     @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 @@ public class IccProvider extends ContentProvider {
         Uri resultUri;
         int efType;
         String pin2 = null;
+        long subId;
 
         if (DBG) log("insert");
 
@@ -116,10 +166,23 @@ public class IccProvider extends ContentProvider {
         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 @@ public class IccProvider extends ContentProvider {
         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 @@ public class IccProvider extends ContentProvider {
                 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 @@ public class IccProvider extends ContentProvider {
 
         resultUri = Uri.parse(buf.toString());
 
+        getContext().getContentResolver().notifyChange(url, null);
         /*
         // notify interested parties that an insertion happened
         getContext().getContentResolver().notifyInsert(
@@ -164,6 +236,11 @@ public class IccProvider extends ContentProvider {
 
     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 @@ public class IccProvider extends ContentProvider {
     @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 @@ public class IccProvider extends ContentProvider {
                         "Cannot insert into URL: " + url);
         }
 
+        if (DBG) log("delete");
+
         // parse where clause
         String tag = null;
         String number = null;
@@ -207,8 +297,12 @@ public class IccProvider extends ContentProvider {
             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 @@ public class IccProvider extends ContentProvider {
             }
         }
 
-        if (TextUtils.isEmpty(number)) {
-            return 0;
-        }
-
-        if (efType == IccConstants.EF_FDN && TextUtils.isEmpty(pin2)) {
+        if (efType == 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 @@ public class IccProvider extends ContentProvider {
         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 @@ public class IccProvider extends ContentProvider {
         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 @@ public class IccProvider extends ContentProvider {
     }
 
     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 @@ public class IccProvider extends ContentProvider {
             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 @@ public class IccProvider extends ContentProvider {
 
     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 @@ public class IccProvider extends ContentProvider {
 
 
     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 @@ public class IccProvider extends ContentProvider {
             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 @@ public class IccProvider extends ContentProvider {
         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);
+        }
+    }
 }
index 938fd38..acef542 100644 (file)
@@ -47,7 +47,7 @@ import static android.telephony.SmsManager.STATUS_ON_ICC_UNREAD;
  * 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 @@ public class IccSmsInterfaceManager extends ISms.Stub {
         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 @@ public class IccSmsInterfaceManager extends ISms.Stub {
      * @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 @@ public class IccSmsInterfaceManager extends ISms.Stub {
      * @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 @@ public class IccSmsInterfaceManager extends ISms.Stub {
      *
      * @return list of SmsRawData of all sms on Icc
      */
-    @Override
+
     public List<SmsRawData> getAllMessagesFromIccEf(String callingPackage) {
         if (DBG) log("getAllMessagesFromEF");
 
@@ -333,7 +329,7 @@ public class IccSmsInterfaceManager extends ISms.Stub {
      *  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 @@ public class IccSmsInterfaceManager extends ISms.Stub {
      *  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 @@ public class IccSmsInterfaceManager extends ISms.Stub {
      *   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 @@ public class IccSmsInterfaceManager extends ISms.Stub {
                 (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);
     }
index 95a650f..709bfc2 100644 (file)
@@ -37,8 +37,10 @@ import android.provider.Telephony;
 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 @@ public abstract class InboundSmsHandler extends StateMachine {
         }
     }
 
+    // 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 @@ public abstract class InboundSmsHandler extends StateMachine {
      * @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);
     }
index a0c8349..c303219 100644 (file)
@@ -24,6 +24,7 @@ import android.net.wifi.WifiManager;
 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 final class MccTable
      */
     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 @@ public final class MccTable
      * @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");
 
index c71ff77..feab370 100644 (file)
@@ -45,6 +45,11 @@ public interface MmiCode
     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
      */
index 5b7b005..706332c 100644 (file)
@@ -29,6 +29,7 @@ import android.telephony.SignalStrength;
 
 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 @@ public interface Phone {
     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 @@ public interface Phone {
     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 interface Phone {
     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 @@ public interface Phone {
     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 @@ public interface Phone {
     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 @@ public interface Phone {
      * @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();
 }
index 2955880..a46934f 100644 (file)
 
 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.CellIdentityCdma;
 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.IccCardApplicationStatus.AppType;
 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.ArrayList;
 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 @@ import java.util.concurrent.atomic.AtomicReference;
 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 @@ public abstract class PhoneBase extends Handler implements Phone {
     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 @@ public abstract class PhoneBase extends Handler implements Phone {
     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 @@ public abstract class PhoneBase extends Handler implements Phone {
     /**
      * 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 @@ public abstract class PhoneBase extends Handler implements Phone {
     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 @@ public abstract class PhoneBase extends Handler implements Phone {
      */
     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 @@ public abstract class PhoneBase extends Handler implements Phone {
             mTelephonyTester = new TelephonyTester(this);
         }
 
-        setPropertiesByCarrier();
-
         setUnitTestMode(unitTestMode);
 
         SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
@@ -309,16 +359,27 @@ public abstract class PhoneBase extends Handler implements Phone {
                 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 @@ public abstract class PhoneBase extends Handler implements Phone {
             if (mTelephonyTester != null) {
                 mTelephonyTester.dispose();
             }
+
+            if (mVoicePhone != null) {
+                ((VoicePhone)mVoicePhone).unregisterForSilentRedial(this);
+                mVoicePhone.dispose();
+            }
         }
     }
 
@@ -342,6 +408,11 @@ public abstract class PhoneBase extends Handler implements Phone {
         mUiccApplication.set(null);
         mDcTracker = null;
         mUiccController = null;
+
+        if (mVoicePhone != null) {
+            mVoicePhone.removeReferences();
+            mVoicePhone = null;
+        }
     }
 
     /**
@@ -394,6 +465,20 @@ public abstract class PhoneBase extends Handler implements Phone {
                 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 @@ public abstract class PhoneBase extends Handler implements Phone {
         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 @@ public abstract class PhoneBase extends Handler implements Phone {
 
     // 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 @@ public abstract class PhoneBase extends Handler implements Phone {
         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 @@ public abstract class PhoneBase extends Handler implements Phone {
     }
 
     @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 @@ public abstract class PhoneBase extends Handler implements Phone {
         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 @@ public abstract class PhoneBase extends Handler implements Phone {
         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();
+    }
 }
index 33b21d4..5d20052 100644 (file)
@@ -18,10 +18,15 @@ package com.android.internal.telephony;
 
 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.gsm.GSMPhone;
 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 @@ public class PhoneFactory {
 
     //***** 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 @@ public class PhoneFactory {
                         "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 @@ public class PhoneFactory {
 
                 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);
-
-                // Instantiate UiccController so that all other classes can just call getInstance()
-                UiccController.make(context, sCommandsInterface);
-
-                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;
+                /* 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];
+
+                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));
+
+                        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 @@ public class PhoneFactory {
                 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 @@ public class PhoneFactory {
        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 class PhoneFactory {
     }
 
     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 @@ public class PhoneFactory {
         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 @@ public class PhoneFactory {
                 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);
+    }
 }
index aebf694..da3e305 100644 (file)
@@ -18,6 +18,7 @@ package com.android.internal.telephony;
 
 import android.telephony.CellInfo;
 import android.telephony.DataConnectionRealTimeInfo;
+import android.telephony.VoLteServiceState;
 
 import java.util.List;
 
@@ -58,4 +59,6 @@ public interface PhoneNotifier {
             String apn, String failCause);
 
     public void notifyDataConnectionRealTimeInfo(Phone sender, DataConnectionRealTimeInfo dcRtInfo);
+
+    public void notifyVoLteServiceStateChanged(Phone sender, VoLteServiceState lteState);
 }
index 7c232a1..3b5a562 100644 (file)
@@ -32,11 +32,19 @@ import android.telephony.CellInfo;
 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 @@ public class PhoneProxy extends Handler implements Phone {
     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 @@ public class PhoneProxy extends Handler implements Phone {
         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 @@ public class PhoneProxy extends Handler implements Phone {
         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 @@ public class PhoneProxy extends Handler implements Phone {
         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 @@ public class PhoneProxy extends Handler implements Phone {
         // 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 @@ public class PhoneProxy extends Handler implements Phone {
         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 @@ public class PhoneProxy extends Handler implements Phone {
     }
 
     @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 @@ public class PhoneProxy extends Handler implements Phone {
     }
 
     @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 @@ public class PhoneProxy extends Handler implements Phone {
         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 @@ public class PhoneProxy extends Handler implements Phone {
     }
 
     @Override
+    public UiccCard getUiccCard() {
+        return mActivePhone.getUiccCard();
+    }
+
+    @Override
     public void nvReadItem(int itemID, Message response) {
         mActivePhone.nvReadItem(itemID, response);
     }
@@ -1214,4 +1265,111 @@ public class PhoneProxy extends Handler implements Phone {
         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();
+    }
 }
index 185814d..4da8e9e 100755 (executable)
@@ -21,12 +21,15 @@ import java.io.PrintWriter;
 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 @@ public class PhoneSubInfo extends IPhoneSubInfo.Stub {
     /**
      * 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 @@ public class PhoneSubInfo extends IPhoneSubInfo.Stub {
      * 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 @@ public class PhoneSubInfo extends IPhoneSubInfo.Stub {
     /**
      * 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 @@ public class PhoneSubInfo extends IPhoneSubInfo.Stub {
     /**
      * 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 @@ public class PhoneSubInfo extends IPhoneSubInfo.Stub {
     /**
      * 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 @@ public class PhoneSubInfo extends IPhoneSubInfo.Stub {
     /**
      * 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 @@ public class PhoneSubInfo extends IPhoneSubInfo.Stub {
     /**
      * Retrieves the MSISDN string.
      */
-    @Override
     public String getMsisdn() {
         mContext.enforceCallingOrSelfPermission(READ_PHONE_STATE, "Requires READ_PHONE_STATE");
         return mPhone.getMsisdn();
@@ -134,7 +130,6 @@ public class PhoneSubInfo extends IPhoneSubInfo.Stub {
     /**
      * 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 @@ public class PhoneSubInfo extends IPhoneSubInfo.Stub {
      *
      * @hide
      */
-    @Override
     public String getCompleteVoiceMailNumber() {
         mContext.enforceCallingOrSelfPermission(CALL_PRIVILEGED,
                 "Requires CALL_PRIVILEGED");
@@ -159,7 +153,6 @@ public class PhoneSubInfo extends IPhoneSubInfo.Stub {
     /**
      * 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 @@ public class PhoneSubInfo extends IPhoneSubInfo.Stub {
      * 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 @@ public class PhoneSubInfo extends IPhoneSubInfo.Stub {
      * 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 @@ public class PhoneSubInfo extends IPhoneSubInfo.Stub {
      * @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 @@ public class PhoneSubInfo extends IPhoneSubInfo.Stub {
         }
     }
 
+    /**
+     * 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 @@ public class PhoneSubInfo extends IPhoneSubInfo.Stub {
         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 (file)
index 0000000..5e5606b
--- /dev/null
@@ -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;
+         }
+     }
+}
index 1974ff8..4a8e7d6 100755 (executable)
@@ -19,6 +19,7 @@ package com.android.internal.telephony;
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 
+import android.os.RemoteException;
 import android.os.ServiceManager;
 
 
@@ -146,6 +147,110 @@ public class PhoneSubInfoProxy extends IPhoneSubInfo.Stub {
     }
 
     @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 (file)
index 0000000..80612b7
--- /dev/null
@@ -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);
+    }
+}
index cc71b86..be9b1c9 100644 (file)
@@ -41,6 +41,7 @@ import android.os.Parcel;
 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.CdmaInformationRecords;
 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 @@ class RILRequest {
     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 @@ public final class RIL extends BaseCommands implements CommandsInterface {
     // 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 @@ public final class RIL extends BaseCommands implements CommandsInterface {
     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 final class RIL extends BaseCommands implements CommandsInterface {
         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 @@ public final class RIL extends BaseCommands implements CommandsInterface {
 
                     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 @@ public final class RIL extends BaseCommands implements CommandsInterface {
                 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 @@ public final class RIL extends BaseCommands implements CommandsInterface {
                         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 @@ public final class RIL extends BaseCommands implements CommandsInterface {
     //***** 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 @@ public final class RIL extends BaseCommands implements CommandsInterface {
             filter.addAction(Intent.ACTION_SCREEN_OFF);
             context.registerReceiver(mIntentReceiver, filter);
         }
+
+        TelephonyDevController tdc = TelephonyDevController.getInstance();
+        tdc.registerRIL(this);
     }
 
     //***** CommandsInterface implementation
@@ -657,6 +680,33 @@ public final class RIL extends BaseCommands implements CommandsInterface {
         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 @@ public final class RIL extends BaseCommands implements CommandsInterface {
 
     @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 @@ public final class RIL extends BaseCommands implements CommandsInterface {
             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 @@ public final class RIL extends BaseCommands implements CommandsInterface {
                 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 @@ public final class RIL extends BaseCommands implements CommandsInterface {
             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 @@ public final class RIL extends BaseCommands implements CommandsInterface {
                 }
                 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 @@ public final class RIL extends BaseCommands implements CommandsInterface {
             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 @@ public final class RIL extends BaseCommands implements CommandsInterface {
                     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 @@ public final class RIL extends BaseCommands implements CommandsInterface {
         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 @@ public final class RIL extends BaseCommands implements CommandsInterface {
             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 @@ public final class RIL extends BaseCommands implements CommandsInterface {
             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 @@ public final class RIL extends BaseCommands implements CommandsInterface {
         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}
      */
index f737ab8..8462427 100644 (file)
@@ -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.os.SystemClock;
 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 @@ public abstract class ServiceStateTracker extends Handler {
     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 @@ public abstract class ServiceStateTracker extends Handler {
     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 @@ public abstract class ServiceStateTracker extends Handler {
     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 class ServiceStateTracker extends Handler {
     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 @@ public abstract class ServiceStateTracker extends Handler {
         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 @@ public abstract class ServiceStateTracker extends Handler {
         // 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);
         }
     }
old mode 100644 (file)
new mode 100755 (executable)
index 820c408..ca5d015
@@ -26,6 +26,7 @@ import android.os.Message;
 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 @@ public final class SmsStorageMonitor extends Handler {
 
     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 @@ public final class SmsStorageMonitor extends Handler {
      * @param phone the Phone to use
      */
     public SmsStorageMonitor(PhoneBase phone) {
+        mPhone = phone;
         mContext = phone.getContext();
         mCi = phone.mCi;
 
@@ -139,6 +144,7 @@ public final class SmsStorageMonitor extends Handler {
         // 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 (file)
index 0000000..9e3d417
--- /dev/null
@@ -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 (file)
index 0000000..86bc201
--- /dev/null
@@ -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&nbs