Merge "Support insert messages into ICC EF_SMS"
diff --git a/src/com/android/providers/telephony/SmsProvider.java b/src/com/android/providers/telephony/SmsProvider.java
index 2ad3960..a5dfa79 100644
--- a/src/com/android/providers/telephony/SmsProvider.java
+++ b/src/com/android/providers/telephony/SmsProvider.java
@@ -502,11 +502,16 @@
         try {
             Uri insertUri = insertInner(url, initialValues, callerUid, callerPkg);
 
-            // The raw table is used by the telephony layer for storing an sms before
-            // sending out a notification that an sms has arrived. We don't want to notify
-            // the default sms app of changes to this table.
-            final boolean notifyIfNotDefault = sURLMatcher.match(url) != SMS_RAW_MESSAGE;
-            notifyChange(notifyIfNotDefault, insertUri, callerPkg);
+            int match = sURLMatcher.match(url);
+            // Skip notifyChange() if insertUri is null for SMS_ALL_ICC or SMS_ALL_ICC_SUBID caused
+            // by failure of insertMessageToIcc()(e.g. SIM full).
+            if (insertUri != null || (match != SMS_ALL_ICC && match != SMS_ALL_ICC_SUBID)) {
+                // The raw table is used by the telephony layer for storing an sms before sending
+                // out a notification that an sms has arrived. We don't want to notify the default
+                // sms app of changes to this table.
+                final boolean notifyIfNotDefault = match != SMS_RAW_MESSAGE;
+                notifyChange(notifyIfNotDefault, insertUri, callerPkg);
+            }
             return insertUri;
         } finally {
             Binder.restoreCallingIdentity(token);
@@ -572,6 +577,47 @@
                 table = "canonical_addresses";
                 break;
 
+            case SMS_ALL_ICC:
+            case SMS_ALL_ICC_SUBID:
+                int subId;
+                if (match == SMS_ALL_ICC) {
+                    subId = SmsManager.getDefaultSmsSubscriptionId();
+                } else {
+                    try {
+                        subId = Integer.parseInt(url.getPathSegments().get(1));
+                    } catch (NumberFormatException e) {
+                        throw new IllegalArgumentException(
+                                "Wrong path segements for SMS_ALL_ICC_SUBID, uri= " + url);
+                    }
+                }
+
+                if (initialValues == null) {
+                    throw new IllegalArgumentException("ContentValues is null");
+                }
+
+                String scAddress = initialValues.getAsString(Sms.SERVICE_CENTER);
+                String address = initialValues.getAsString(Sms.ADDRESS);
+                String message = initialValues.getAsString(Sms.BODY);
+                boolean isRead = true;
+                Integer obj = initialValues.getAsInteger(Sms.TYPE);
+
+                if (obj == null || address == null || message == null) {
+                    throw new IllegalArgumentException("Missing SMS data");
+                }
+
+                type = obj.intValue();
+                if (!isSupportedType(type)) {
+                    throw new IllegalArgumentException("Unsupported message type= " + type);
+                }
+                obj = initialValues.getAsInteger(Sms.READ); // 0: Unread, 1: Read
+                if (obj != null && obj.intValue() == 0) {
+                    isRead = false;
+                }
+
+                Long date = initialValues.getAsLong(Sms.DATE);
+                return insertMessageToIcc(subId, scAddress, address, message, type, isRead,
+                        date != null ? date : 0) ? url : null;
+
             default:
                 Log.e(TAG, "Invalid request: " + url);
                 return null;
@@ -705,6 +751,63 @@
         return null;
     }
 
+    private boolean isSupportedType(int messageType) {
+        return (messageType == Sms.MESSAGE_TYPE_INBOX)
+                || (messageType == Sms.MESSAGE_TYPE_OUTBOX)
+                || (messageType == Sms.MESSAGE_TYPE_SENT);
+    }
+
+    private int getMessageStatusForIcc(int messageType, boolean isRead) {
+        if (messageType == Sms.MESSAGE_TYPE_SENT) {
+            return SmsManager.STATUS_ON_ICC_SENT;
+        } else if (messageType == Sms.MESSAGE_TYPE_OUTBOX) {
+            return SmsManager.STATUS_ON_ICC_UNSENT;
+        } else { // Sms.MESSAGE_BOX_INBOX
+            if (isRead) {
+                return SmsManager.STATUS_ON_ICC_READ;
+            } else {
+                return SmsManager.STATUS_ON_ICC_UNREAD;
+            }
+        }
+    }
+
+    /**
+     * Inserts new message to the ICC for a subscription ID.
+     *
+     * @param subId the subscription ID.
+     * @param scAddress the SMSC for this message.
+     * @param address destination or originating address.
+     * @param message the message text.
+     * @param messageType type of the message.
+     * @param isRead ture if the message has been read. Otherwise false.
+     * @param date the date the message was received.
+     * @return true for succeess. Otherwise false.
+     */
+    private boolean insertMessageToIcc(int subId, String scAddress, String address, String message,
+            int messageType, boolean isRead, long date) {
+        if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+            throw new IllegalArgumentException("Invalid Subscription ID " + subId);
+        }
+        SmsManager smsManager = SmsManager.getSmsManagerForSubscriptionId(subId);
+
+        int status = getMessageStatusForIcc(messageType, isRead);
+        SmsMessage.SubmitPdu smsPdu =
+                SmsMessage.getSmsPdu(subId, status, scAddress, address, message, date);
+
+        if (smsPdu == null) {
+            throw new IllegalArgumentException("Failed to create SMS PDU");
+        }
+
+        // Use phone app permissions to avoid UID mismatch in AppOpsManager.noteOp() call.
+        long token = Binder.clearCallingIdentity();
+        try {
+            return smsManager.copyMessageToIcc(
+                    smsPdu.encodedScAddress, smsPdu.encodedMessage, status);
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
     @Override
     public int delete(Uri url, String where, String[] whereArgs) {
         int count;