summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoryanglv <yanglv@codeaurora.org>2015-01-26 16:29:24 +0800
committerGerrit - the friendly Code Review server <code-review@localhost>2015-01-27 19:32:35 -0800
commitaeead2151e43439842b0efd3765f6780b592a49d (patch)
treeb3d5b9cb64fa4a4b19c5426b346c623e965d455f
parenta6a83256a01faed7b936e89ab7b525073614af11 (diff)
downloadandroid_packages_providers_TelephonyProvider-aeead2151e43439842b0efd3765f6780b592a49d.tar.gz
android_packages_providers_TelephonyProvider-aeead2151e43439842b0efd3765f6780b592a49d.tar.bz2
android_packages_providers_TelephonyProvider-aeead2151e43439842b0efd3765f6780b592a49d.zip
TelephonyProvider: Implement inserting SMS into ICC card by provider
There will be an exception when inserting SMS into CDMA card due to new SELinux policy on L version, it can't set "radio" system property with a non-radio user ID. Move inserting SMS into ICC card implementation from Mms application to TelephonyProvider which is phone process. Change-Id: I187d2d10ff7fc73976ef699c5df0995ca29ec2bd CRs-Fixed: 775189
-rwxr-xr-x[-rw-r--r--]src/com/android/providers/telephony/SmsProvider.java365
1 files changed, 365 insertions, 0 deletions
diff --git a/src/com/android/providers/telephony/SmsProvider.java b/src/com/android/providers/telephony/SmsProvider.java
index 19a1eb7..736e86a 100644..100755
--- a/src/com/android/providers/telephony/SmsProvider.java
+++ b/src/com/android/providers/telephony/SmsProvider.java
@@ -16,6 +16,12 @@
package com.android.providers.telephony;
+import static android.telephony.SmsMessage.ENCODING_16BIT;
+import static android.telephony.SmsMessage.ENCODING_7BIT;
+import static android.telephony.SmsMessage.ENCODING_UNKNOWN;
+import static android.telephony.SmsMessage.MAX_USER_DATA_BYTES;
+import static android.telephony.SmsMessage.MAX_USER_DATA_SEPTETS;
+
import android.app.AppOpsManager;
import android.content.ContentProvider;
import android.content.ContentResolver;
@@ -38,14 +44,30 @@ import android.provider.Telephony.MmsSms;
import android.provider.Telephony.Sms;
import android.provider.Telephony.TextBasedSmsColumns;
import android.provider.Telephony.Threads;
+import android.telephony.PhoneNumberUtils;
import android.telephony.SmsManager;
import android.telephony.SmsMessage;
import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Log;
+import java.io.ByteArrayOutputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.Locale;
+
+import com.android.internal.telephony.EncodeException;
+import com.android.internal.telephony.GsmAlphabet;
+import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.telephony.SmsHeader;
+import com.android.internal.telephony.cdma.sms.BearerData;
+import com.android.internal.telephony.cdma.sms.CdmaSmsAddress;
+import com.android.internal.telephony.cdma.sms.UserData;
public class SmsProvider extends ContentProvider {
private static final Uri NOTIFICATION_URI = Uri.parse("content://sms");
@@ -61,11 +83,19 @@ public class SmsProvider extends ContentProvider {
private static final int SUB1 = 0;
private static final int SUB2 = 1;
private static final Integer ONE = Integer.valueOf(1);
+ private static final int OFFSET_ADDRESS_LENGTH = 0;
+ private static final int OFFSET_TOA = 1;
+ private static final int OFFSET_ADDRESS_VALUE = 2;
+ private static final int TIMESTAMP_LENGTH = 7; // See TS 23.040 9.2.3.11
private static final String[] CONTACT_QUERY_PROJECTION =
new String[] { Contacts.Phones.PERSON_ID };
private static final int PERSON_ID_COLUMN = 0;
+ private static final String SMS_BOX_ID = "box_id";
+ private static final Uri INSERT_SMS_INTO_ICC_SUCCESS = Uri.parse("content://iccsms/success");
+ private static final Uri INSERT_SMS_INTO_ICC_FAIL = Uri.parse("content://iccsms/fail");
+
/**
* These are the columns that are available when reading SMS
* messages from the ICC. Columns whose names begin with "is_"
@@ -474,6 +504,9 @@ public class SmsProvider extends ContentProvider {
table = "canonical_addresses";
break;
+ case SMS_ALL_ICC:
+ return insertMessageIntoIcc(initialValues);
+
default:
Log.e(TAG, "Invalid request: " + url);
return null;
@@ -599,6 +632,338 @@ public class SmsProvider extends ContentProvider {
return null;
}
+ private Uri insertMessageIntoIcc(ContentValues values) {
+ if (values == null) {
+ return INSERT_SMS_INTO_ICC_FAIL;
+ }
+ long subId = values.getAsLong(PhoneConstants.SUBSCRIPTION_KEY);
+ String address = values.getAsString(Sms.ADDRESS);
+ String message = values.getAsString(Sms.BODY);
+ int boxId = values.getAsInteger(SMS_BOX_ID);
+ long timestamp = values.getAsLong(Sms.DATE);
+ byte pdu[] = null;
+ int status;
+ if (Sms.isOutgoingFolder(boxId)) {
+ pdu = SmsMessage.getSubmitPdu(null, address, message, false, subId).encodedMessage;
+ status = SmsManager.STATUS_ON_ICC_SENT;
+ } else {
+ pdu = getDeliveryPdu(null, address, message, timestamp, subId);
+ status = SmsManager.STATUS_ON_ICC_READ;
+ }
+ boolean result = SmsManager.getSmsManagerForSubscriber(subId).copyMessageToIcc(null,
+ pdu, status);
+ return result ? INSERT_SMS_INTO_ICC_SUCCESS : INSERT_SMS_INTO_ICC_FAIL;
+ }
+
+ /**
+ * Generate a Delivery PDU byte array. see getSubmitPdu for reference.
+ */
+ public static byte[] getDeliveryPdu(String scAddress, String destinationAddress, String message,
+ long date, long subscription) {
+ if (isCdmaPhone(subscription)) {
+ return getCdmaDeliveryPdu(scAddress, destinationAddress, message, date);
+ } else {
+ return getGsmDeliveryPdu(scAddress, destinationAddress, message, date, null,
+ ENCODING_UNKNOWN);
+ }
+ }
+
+ private static boolean isCdmaPhone(long subscription) {
+ boolean isCdma = false;
+ int activePhone = TelephonyManager.getDefault().getCurrentPhoneType(subscription);
+ if (TelephonyManager.PHONE_TYPE_CDMA == activePhone) {
+ isCdma = true;
+ }
+ return isCdma;
+ }
+
+ public static byte[] getCdmaDeliveryPdu(String scAddress, String destinationAddress,
+ String message, long date) {
+ // Perform null parameter checks.
+ if (message == null || destinationAddress == null) {
+ Log.d(TAG, "getCDMADeliveryPdu,message =null");
+ return null;
+ }
+
+ // according to submit pdu encoding as written in privateGetSubmitPdu
+
+ // MTI = SMS-DELIVERY, UDHI = header != null
+ byte[] header = null;
+ byte mtiByte = (byte) (0x00 | (header != null ? 0x40 : 0x00));
+ ByteArrayOutputStream headerStream = getDeliveryPduHeader(destinationAddress, mtiByte);
+
+ ByteArrayOutputStream byteStream = new ByteArrayOutputStream(MAX_USER_DATA_BYTES + 40);
+
+ DataOutputStream dos = new DataOutputStream(byteStream);
+ // int status,Status of message. See TS 27.005 3.1, "<stat>"
+
+ /* 0 = "REC UNREAD" */
+ /* 1 = "REC READ" */
+ /* 2 = "STO UNSENT" */
+ /* 3 = "STO SENT" */
+
+ try {
+ // int uTeleserviceID;
+ int uTeleserviceID = 0; //.TELESERVICE_CT_WAP;// int
+ dos.writeInt(uTeleserviceID);
+
+ // unsigned char bIsServicePresent
+ byte bIsServicePresent = 0;// byte
+ dos.writeInt(bIsServicePresent);
+
+ // uServicecategory
+ int uServicecategory = 0;// int
+ dos.writeInt(uServicecategory);
+
+ // RIL_CDMA_SMS_Address
+ // digit_mode
+ // number_mode
+ // number_type
+ // number_plan
+ // number_of_digits
+ // digits[]
+ CdmaSmsAddress destAddr = CdmaSmsAddress.parse(PhoneNumberUtils
+ .cdmaCheckAndProcessPlusCode(destinationAddress));
+ if (destAddr == null)
+ return null;
+ dos.writeByte(destAddr.digitMode);// int
+ dos.writeByte(destAddr.numberMode);// int
+ dos.writeByte(destAddr.ton);// int
+ Log.d(TAG, "message type=" + destAddr.ton + "destination add=" + destinationAddress
+ + "message=" + message);
+ dos.writeByte(destAddr.numberPlan);// int
+ dos.writeByte(destAddr.numberOfDigits);// byte
+ dos.write(destAddr.origBytes, 0, destAddr.origBytes.length); // digits
+
+ // RIL_CDMA_SMS_Subaddress
+ // Subaddress is not supported.
+ dos.writeByte(0); // subaddressType int
+ dos.writeByte(0); // subaddr_odd byte
+ dos.writeByte(0); // subaddr_nbr_of_digits byte
+
+ SmsHeader smsHeader = new SmsHeader().fromByteArray(headerStream.toByteArray());
+ UserData uData = new UserData();
+ uData.payloadStr = message;
+ // uData.userDataHeader = smsHeader;
+ uData.msgEncodingSet = true;
+ uData.msgEncoding = UserData.ENCODING_UNICODE_16;
+
+ BearerData bearerData = new BearerData();
+ bearerData.messageType = BearerData.MESSAGE_TYPE_DELIVER;
+
+ bearerData.deliveryAckReq = false;
+ bearerData.userAckReq = false;
+ bearerData.readAckReq = false;
+ bearerData.reportReq = false;
+
+ bearerData.userData = uData;
+
+ byte[] encodedBearerData = BearerData.encode(bearerData);
+ if (null != encodedBearerData) {
+ // bearer data len
+ dos.writeByte(encodedBearerData.length);// int
+ Log.d(TAG, "encodedBearerData length=" + encodedBearerData.length);
+
+ // aBearerData
+ dos.write(encodedBearerData, 0, encodedBearerData.length);
+ } else {
+ dos.writeByte(0);
+ }
+
+ } catch (IOException e) {
+ Log.e(TAG, "Error writing dos", e);
+ } finally {
+ try {
+ if (null != byteStream) {
+ byteStream.close();
+ }
+
+ if (null != dos) {
+ dos.close();
+ }
+
+ if (null != headerStream) {
+ headerStream.close();
+ }
+ } catch (IOException e) {
+ Log.e(TAG, "Error close dos", e);
+ }
+ }
+
+ return byteStream.toByteArray();
+ }
+
+ /**
+ * Generate a Delivery PDU byte array. see getSubmitPdu for reference.
+ */
+ public static byte[] getGsmDeliveryPdu(String scAddress, String destinationAddress,
+ String message, long date, byte[] header, int encoding) {
+ // Perform null parameter checks.
+ if (message == null || destinationAddress == null) {
+ return null;
+ }
+
+ // MTI = SMS-DELIVERY, UDHI = header != null
+ byte mtiByte = (byte)(0x00 | (header != null ? 0x40 : 0x00));
+ ByteArrayOutputStream bo = getDeliveryPduHeader(destinationAddress, mtiByte);
+ // User Data (and length)
+ byte[] userData;
+ if (encoding == ENCODING_UNKNOWN) {
+ // First, try encoding it with the GSM alphabet
+ encoding = ENCODING_7BIT;
+ }
+ try {
+ if (encoding == ENCODING_7BIT) {
+ userData = GsmAlphabet.stringToGsm7BitPackedWithHeader(message, header, 0, 0);
+ } else { //assume UCS-2
+ try {
+ userData = encodeUCS2(message, header);
+ } catch (UnsupportedEncodingException uex) {
+ Log.e("GSM", "Implausible UnsupportedEncodingException ",
+ uex);
+ return null;
+ }
+ }
+ } catch (EncodeException ex) {
+ // Encoding to the 7-bit alphabet failed. Let's see if we can
+ // encode it as a UCS-2 encoded message
+ try {
+ userData = encodeUCS2(message, header);
+ encoding = ENCODING_16BIT;
+ } catch (UnsupportedEncodingException uex) {
+ Log.e("GSM", "Implausible UnsupportedEncodingException ",
+ uex);
+ return null;
+ }
+ }
+
+ if (encoding == ENCODING_7BIT) {
+ if ((0xff & userData[0]) > MAX_USER_DATA_SEPTETS) {
+ // Message too long
+ return null;
+ }
+ bo.write(0x00);
+ } else { //assume UCS-2
+ if ((0xff & userData[0]) > MAX_USER_DATA_BYTES) {
+ // Message too long
+ return null;
+ }
+ // TP-Data-Coding-Scheme
+ // Class 3, UCS-2 encoding, uncompressed
+ bo.write(0x0b);
+ }
+ byte[] timestamp = getTimestamp(date);
+ bo.write(timestamp, 0, timestamp.length);
+
+ bo.write(userData, 0, userData.length);
+ return bo.toByteArray();
+ }
+
+ private static ByteArrayOutputStream getDeliveryPduHeader(
+ String destinationAddress, byte mtiByte) {
+ ByteArrayOutputStream bo = new ByteArrayOutputStream(
+ MAX_USER_DATA_BYTES + 40);
+ bo.write(mtiByte);
+
+ byte[] daBytes;
+ daBytes = PhoneNumberUtils.networkPortionToCalledPartyBCD(destinationAddress);
+
+ if (daBytes == null) {
+ Log.d(TAG, "The number can not convert to BCD, it's an An alphanumeric address, " +
+ "destinationAddress = " + destinationAddress);
+ // Convert address to GSM 7 bit packed bytes.
+ try {
+ byte[] numberdata = GsmAlphabet
+ .stringToGsm7BitPacked(destinationAddress);
+ // Get the real address data
+ byte[] addressData = new byte[numberdata.length - 1];
+ System.arraycopy(numberdata, 1, addressData, 0, addressData.length);
+
+ daBytes = new byte[addressData.length + OFFSET_ADDRESS_VALUE];
+ // Get the address length
+ int addressLen = numberdata[0];
+ daBytes[OFFSET_ADDRESS_LENGTH] = (byte) ((addressLen * 7 % 4 != 0 ?
+ addressLen * 7 / 4 + 1 : addressLen * 7 / 4));
+ // Set address type to Alphanumeric according to 3GPP TS 23.040 [9.1.2.5]
+ daBytes[OFFSET_TOA] = (byte) 0xd0;
+ System.arraycopy(addressData, 0, daBytes, OFFSET_ADDRESS_VALUE, addressData.length);
+ } catch (Exception e) {
+ Log.e(TAG, "Exception when encoding to 7 bit data.");
+ }
+ } else {
+ // destination address length in BCD digits, ignoring TON byte and pad
+ // TODO Should be better.
+ bo.write((daBytes.length - 1) * 2
+ - ((daBytes[daBytes.length - 1] & 0xf0) == 0xf0 ? 1 : 0));
+ }
+
+ // destination address
+ bo.write(daBytes, 0, daBytes.length);
+
+ // TP-Protocol-Identifier
+ bo.write(0);
+ return bo;
+ }
+
+ private static byte[] encodeUCS2(String message, byte[] header)
+ throws UnsupportedEncodingException {
+ byte[] userData, textPart;
+ textPart = message.getBytes("utf-16be");
+
+ if (header != null) {
+ // Need 1 byte for UDHL
+ userData = new byte[header.length + textPart.length + 1];
+
+ userData[0] = (byte)header.length;
+ System.arraycopy(header, 0, userData, 1, header.length);
+ System.arraycopy(textPart, 0, userData, header.length + 1, textPart.length);
+ }
+ else {
+ userData = textPart;
+ }
+ byte[] ret = new byte[userData.length+1];
+ ret[0] = (byte) (userData.length & 0xff );
+ System.arraycopy(userData, 0, ret, 1, userData.length);
+ return ret;
+ }
+
+ private static byte[] getTimestamp(long time) {
+ // See TS 23.040 9.2.3.11
+ byte[] timestamp = new byte[TIMESTAMP_LENGTH];
+ SimpleDateFormat sdf = new SimpleDateFormat("yyMMddkkmmss:Z", Locale.US);
+ String[] date = sdf.format(time).split(":");
+ // generate timezone value
+ String timezone = date[date.length - 1];
+ String signMark = timezone.substring(0, 1);
+ int hour = Integer.parseInt(timezone.substring(1, 3));
+ int min = Integer.parseInt(timezone.substring(3));
+ int timezoneValue = hour * 4 + min / 15;
+ // append timezone value to date[0] (time string)
+ String timestampStr = date[0] + timezoneValue;
+
+ int digitCount = 0;
+ for (int i = 0; i < timestampStr.length(); i++) {
+ char c = timestampStr.charAt(i);
+ int shift = ((digitCount & 0x01) == 1) ? 4 : 0;
+ timestamp[(digitCount >> 1)] |= (byte)((charToBCD(c) & 0x0F) << shift);
+ digitCount++;
+ }
+
+ if (signMark.equals("-")) {
+ timestamp[timestamp.length - 1] = (byte) (timestamp[timestamp.length - 1] | 0x08);
+ }
+
+ return timestamp;
+ }
+
+ private static int charToBCD(char c) {
+ if (c >= '0' && c <= '9') {
+ return c - '0';
+ } else {
+ throw new RuntimeException ("invalid char for BCD " + c);
+ }
+ }
+
@Override
public int delete(Uri url, String where, String[] whereArgs) {
int count;