/** {@inheritDoc} */ protected void sendMultipartText( String destinationAddress, String scAddress, ArrayList<String> parts, ArrayList<PendingIntent> sentIntents, ArrayList<PendingIntent> deliveryIntents) { int refNumber = getNextConcatenatedRef() & 0x00FF; int msgCount = parts.size(); int encoding = android.telephony.SmsMessage.ENCODING_UNKNOWN; mRemainingMessages = msgCount; TextEncodingDetails[] encodingForParts = new TextEncodingDetails[msgCount]; for (int i = 0; i < msgCount; i++) { TextEncodingDetails details = SmsMessage.calculateLength(parts.get(i), false); if (encoding != details.codeUnitSize && (encoding == android.telephony.SmsMessage.ENCODING_UNKNOWN || encoding == android.telephony.SmsMessage.ENCODING_7BIT)) { encoding = details.codeUnitSize; } encodingForParts[i] = details; } for (int i = 0; i < msgCount; i++) { SmsHeader.ConcatRef concatRef = new SmsHeader.ConcatRef(); concatRef.refNumber = refNumber; concatRef.seqNumber = i + 1; // 1-based sequence concatRef.msgCount = msgCount; // TODO: We currently set this to true since our messaging app will never // send more than 255 parts (it converts the message to MMS well before that). // However, we should support 3rd party messaging apps that might need 16-bit // references // Note: It's not sufficient to just flip this bit to true; it will have // ripple effects (several calculations assume 8-bit ref). concatRef.isEightBits = true; SmsHeader smsHeader = new SmsHeader(); smsHeader.concatRef = concatRef; if (encoding == android.telephony.SmsMessage.ENCODING_7BIT) { smsHeader.languageTable = encodingForParts[i].languageTable; smsHeader.languageShiftTable = encodingForParts[i].languageShiftTable; } PendingIntent sentIntent = null; if (sentIntents != null && sentIntents.size() > i) { sentIntent = sentIntents.get(i); } PendingIntent deliveryIntent = null; if (deliveryIntents != null && deliveryIntents.size() > i) { deliveryIntent = deliveryIntents.get(i); } SmsMessage.SubmitPdu pdus = SmsMessage.getSubmitPdu( scAddress, destinationAddress, parts.get(i), deliveryIntent != null, SmsHeader.toByteArray(smsHeader), encoding, smsHeader.languageTable, smsHeader.languageShiftTable); sendRawPdu(pdus.encodedScAddress, pdus.encodedMessage, sentIntent, deliveryIntent); } }
/** * Send a multi-part text based SMS which already passed SMS control check. * * It is the working function for sendMultipartText(). * * @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: * <code>RESULT_ERROR_GENERIC_FAILURE</code> * <code>RESULT_ERROR_RADIO_OFF</code> * <code>RESULT_ERROR_NULL_PDU</code>. * @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"). */ private void sendMultipartTextWithPermit( String destinationAddress, String scAddress, ArrayList<String> parts, ArrayList<PendingIntent> sentIntents, ArrayList<PendingIntent> deliveryIntents) { // check if in service int ss = mPhone.getServiceState().getState(); if (ss != ServiceState.STATE_IN_SERVICE) { for (int i = 0, count = parts.size(); i < count; i++) { PendingIntent sentIntent = null; if (sentIntents != null && sentIntents.size() > i) { sentIntent = sentIntents.get(i); } SmsTracker tracker = SmsTrackerFactory(null, sentIntent, null); handleNotInService(ss, tracker); } return; } int refNumber = getNextConcatenatedRef() & 0x00FF; int msgCount = parts.size(); int encoding = android.telephony.SmsMessage.ENCODING_UNKNOWN; mRemainingMessages = msgCount; TextEncodingDetails[] encodingForParts = new TextEncodingDetails[msgCount]; for (int i = 0; i < msgCount; i++) { TextEncodingDetails details = SmsMessage.calculateLength(parts.get(i), false); if (encoding != details.codeUnitSize && (encoding == android.telephony.SmsMessage.ENCODING_UNKNOWN || encoding == android.telephony.SmsMessage.ENCODING_7BIT)) { encoding = details.codeUnitSize; } encodingForParts[i] = details; } for (int i = 0; i < msgCount; i++) { SmsHeader.ConcatRef concatRef = new SmsHeader.ConcatRef(); concatRef.refNumber = refNumber; concatRef.seqNumber = i + 1; // 1-based sequence concatRef.msgCount = msgCount; concatRef.isEightBits = false; SmsHeader smsHeader = new SmsHeader(); smsHeader.concatRef = concatRef; if (encoding == android.telephony.SmsMessage.ENCODING_7BIT) { smsHeader.languageTable = encodingForParts[i].languageTable; smsHeader.languageShiftTable = encodingForParts[i].languageShiftTable; } PendingIntent sentIntent = null; if (sentIntents != null && sentIntents.size() > i) { sentIntent = sentIntents.get(i); } PendingIntent deliveryIntent = null; if (deliveryIntents != null && deliveryIntents.size() > i) { deliveryIntent = deliveryIntents.get(i); } SmsMessage.SubmitPdu pdus = SmsMessage.getSubmitPdu( scAddress, destinationAddress, parts.get(i), deliveryIntent != null, SmsHeader.toByteArray(smsHeader), encoding, smsHeader.languageTable, smsHeader.languageShiftTable); HashMap<String, Object> map = new HashMap<String, Object>(); map.put("smsc", pdus.encodedScAddress); map.put("pdu", pdus.encodedMessage); SmsTracker tracker = SmsTrackerFactory(map, sentIntent, deliveryIntent); sendSms(tracker); } }
/** * Get an SMS-SUBMIT PDU for a destination address and a message using the specified encoding. * * @param scAddress Service Centre address. Null means use default. * @param encoding Encoding defined by constants in * com.android.internal.telephony.SmsConstants.ENCODING_* * @param languageTable * @param languageShiftTable * @return a <code>SubmitPdu</code> containing the encoded SC address, if applicable, and the * encoded message. Returns null on encode error. * @hide */ public static SubmitPdu getSubmitPdu( String scAddress, String destinationAddress, String message, boolean statusReportRequested, byte[] header, int encoding, int languageTable, int languageShiftTable) { // Perform null parameter checks. if (message == null || destinationAddress == null) { return null; } if (encoding == ENCODING_UNKNOWN) { // Find the best encoding to use TextEncodingDetails ted = calculateLength(message, false); encoding = ted.codeUnitSize; languageTable = ted.languageTable; languageShiftTable = ted.languageShiftTable; if (encoding == ENCODING_7BIT && (languageTable != 0 || languageShiftTable != 0)) { if (header != null) { SmsHeader smsHeader = SmsHeader.fromByteArray(header); if (smsHeader.languageTable != languageTable || smsHeader.languageShiftTable != languageShiftTable) { Rlog.w( LOG_TAG, "Updating language table in SMS header: " + smsHeader.languageTable + " -> " + languageTable + ", " + smsHeader.languageShiftTable + " -> " + languageShiftTable); smsHeader.languageTable = languageTable; smsHeader.languageShiftTable = languageShiftTable; header = SmsHeader.toByteArray(smsHeader); } } else { SmsHeader smsHeader = new SmsHeader(); smsHeader.languageTable = languageTable; smsHeader.languageShiftTable = languageShiftTable; header = SmsHeader.toByteArray(smsHeader); } } } SubmitPdu ret = new SubmitPdu(); // MTI = SMS-SUBMIT, UDHI = header != null byte mtiByte = (byte) (0x01 | (header != null ? 0x40 : 0x00)); ByteArrayOutputStream bo = getSubmitPduHead(scAddress, destinationAddress, mtiByte, statusReportRequested, ret); // User Data (and length) byte[] userData; try { if (encoding == ENCODING_7BIT) { userData = GsmAlphabet.stringToGsm7BitPackedWithHeader( message, header, languageTable, languageShiftTable); } else { // assume UCS-2 try { userData = encodeUCS2(message, header); } catch (UnsupportedEncodingException uex) { Rlog.e(LOG_TAG, "Implausible UnsupportedEncodingException ", uex); return null; } } } catch (EncodeException ex) { // Encoding to the 7-bit alphabet failed. Let's see if we can // send it as a UCS-2 encoded message try { userData = encodeUCS2(message, header); encoding = ENCODING_16BIT; } catch (UnsupportedEncodingException uex) { Rlog.e(LOG_TAG, "Implausible UnsupportedEncodingException ", uex); return null; } } if (encoding == ENCODING_7BIT) { if ((0xff & userData[0]) > MAX_USER_DATA_SEPTETS) { // Message too long Rlog.e(LOG_TAG, "Message too long (" + (0xff & userData[0]) + " septets)"); return null; } // TP-Data-Coding-Scheme // Default encoding, uncompressed // To test writing messages to the SIM card, change this value 0x00 // to 0x12, which means "bits 1 and 0 contain message class, and the // class is 2". Note that this takes effect for the sender. In other // words, messages sent by the phone with this change will end up on // the receiver's SIM card. You can then send messages to yourself // (on a phone with this change) and they'll end up on the SIM card. bo.write(0x00); } else { // assume UCS-2 if ((0xff & userData[0]) > MAX_USER_DATA_BYTES) { // Message too long Rlog.e(LOG_TAG, "Message too long (" + (0xff & userData[0]) + " bytes)"); return null; } // TP-Data-Coding-Scheme // UCS-2 encoding, uncompressed bo.write(0x08); } // (no TP-Validity-Period) bo.write(userData, 0, userData.length); ret.encodedMessage = bo.toByteArray(); return ret; }