diff options
Diffstat (limited to 'telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java')
-rw-r--r-- | telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java | 375 |
1 files changed, 375 insertions, 0 deletions
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java b/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java new file mode 100644 index 000000000000..b96366b6f976 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java @@ -0,0 +1,375 @@ +/* + * Copyright (C) 2008 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.cdma; + + +import android.app.PendingIntent; +import android.content.ContentValues; +import android.database.Cursor; +import android.database.SQLException; +import android.os.AsyncResult; +import android.os.Message; +import android.util.Config; +import android.util.Log; + +import com.android.internal.telephony.SmsHeader; +import com.android.internal.telephony.SmsMessageBase; +import com.android.internal.telephony.SMSDispatcher; +import com.android.internal.telephony.cdma.SmsMessage; +import com.android.internal.telephony.cdma.sms.SmsEnvelope; +import com.android.internal.util.HexDump; + +import java.io.ByteArrayOutputStream; +import java.util.ArrayList; +import java.util.HashMap; + + +final class CdmaSMSDispatcher extends SMSDispatcher { + private static final String TAG = "CDMA"; + + CdmaSMSDispatcher(CDMAPhone phone) { + super(phone); + } + + /** + * Called when a status report is received. This should correspond to + * a previously successful SEND. + * Is a special GSM function, should never be called in CDMA!! + * + * @param ar AsyncResult passed into the message handler. ar.result should + * be a String representing the status report PDU, as ASCII hex. + */ + protected void handleStatusReport(AsyncResult ar) { + Log.d(TAG, "handleStatusReport is a special GSM function, should never be called in CDMA!"); + } + + /** + * Dispatches an incoming SMS messages. + * + * @param smsb the incoming message from the phone + */ + protected void dispatchMessage(SmsMessageBase smsb) { + SmsMessage sms = (SmsMessage) smsb; + int teleService; + boolean handled = false; + + // Decode BD stream and set sms variables. + sms.parseSms(); + teleService = sms.getTeleService(); + + // Teleservices W(E)MT and VMN are handled together: + if ((SmsEnvelope.TELESERVICE_WMT == teleService) + ||(SmsEnvelope.TELESERVICE_WEMT == teleService) + ||(SmsEnvelope.TELESERVICE_VMN == teleService)){ + // From here on we need decoded BD. + // Special case the message waiting indicator messages + if (sms.isMWISetMessage()) { + ((CDMAPhone) mPhone).updateMessageWaitingIndicator(true); + + if (sms.isMwiDontStore()) { + handled = true; + } + + if (Config.LOGD) { + Log.d(TAG, + "Received voice mail indicator set SMS shouldStore=" + !handled); + } + } else if (sms.isMWIClearMessage()) { + ((CDMAPhone) mPhone).updateMessageWaitingIndicator(false); + + if (sms.isMwiDontStore()) { + handled = true; + } + + if (Config.LOGD) { + Log.d(TAG, + "Received voice mail indicator clear SMS shouldStore=" + !handled); + } + } + } + + if (null == sms.getUserData()){ + handled = true; + if (Config.LOGD) { + Log.d(TAG, "Received SMS without user data"); + } + } + + if (handled) return; + + if (SmsEnvelope.TELESERVICE_WAP == teleService){ + processCdmaWapPdu(sms.getUserData(), sms.messageRef, sms.getOriginatingAddress()); + return; + } + + // Parse the headers to see if this is partial, or port addressed + int referenceNumber = -1; + int count = 0; + int sequence = 0; + int destPort = -1; + // From here on we need BD distributed to SMS member variables. + + SmsHeader header = sms.getUserDataHeader(); + if (header != null) { + for (SmsHeader.Element element : header.getElements()) { + switch (element.getID()) { + case SmsHeader.CONCATENATED_8_BIT_REFERENCE: { + byte[] data = element.getData(); + + referenceNumber = data[0] & 0xff; + count = data[1] & 0xff; + sequence = data[2] & 0xff; + + break; + } + + case SmsHeader.CONCATENATED_16_BIT_REFERENCE: { + byte[] data = element.getData(); + + referenceNumber = (data[0] & 0xff) * 256 + (data[1] & 0xff); + count = data[2] & 0xff; + sequence = data[3] & 0xff; + + break; + } + + case SmsHeader.APPLICATION_PORT_ADDRESSING_8_BIT: { + byte[] data = element.getData(); + + destPort = data[0] & 0xff; + + break; + } + + case SmsHeader.APPLICATION_PORT_ADDRESSING_16_BIT: { + byte[] data = element.getData(); + + destPort = (data[0] & 0xff) << 8; + destPort |= (data[1] & 0xff); + + break; + } + } + } + } + + if (referenceNumber == -1) { + // notify everyone of the message if it isn't partial + byte[][] pdus = new byte[1][]; + pdus[0] = sms.getPdu(); + + if (destPort != -1) {// GSM-style WAP indication + if (destPort == SmsHeader.PORT_WAP_PUSH) { + dispatchWapPdu(sms.getUserData()); + } + // The message was sent to a port, so concoct a URI for it + dispatchPortAddressedPdus(pdus, destPort); + } else { + // It's a normal message, dispatch it + dispatchPdus(pdus); + } + } else { + // Process the message part + processMessagePart(sms, referenceNumber, sequence, count, destPort); + } + } + + /** + * Processes inbound messages that are in the WAP-WDP PDU format. See + * wap-259-wdp-20010614-a section 6.5 for details on the WAP-WDP PDU format. + * WDP segments are gathered until a datagram completes and gets dispatched. + * + * @param pdu The WAP-WDP PDU segment + */ + protected void processCdmaWapPdu(byte[] pdu, int referenceNumber, String address){ + int segment; + int totalSegments; + int index = 0; + int msgType; + + int sourcePort; + int destinationPort; + + msgType = pdu[index++]; + if (msgType != 0){ + Log.w(TAG, "Received a WAP SMS which is not WDP. Discard."); + return; + } + totalSegments = pdu[index++]; // >=1 + segment = pdu[index++]; // >=0 + + //process WDP segment + sourcePort = (0xFF & pdu[index++]) << 8; + sourcePort |= 0xFF & pdu[index++]; + destinationPort = (0xFF & pdu[index++]) << 8; + destinationPort |= 0xFF & pdu[index++]; + + // Lookup all other related parts + StringBuilder where = new StringBuilder("reference_number ="); + where.append(referenceNumber); + where.append(" AND address = ?"); + String[] whereArgs = new String[] {address}; + + Log.i(TAG, "Received WAP PDU. Type = " + msgType + ", originator = " + address + + ", src-port = " + sourcePort + ", dst-port = " + destinationPort + + ", ID = " + referenceNumber + ", segment# = " + segment + "/" + totalSegments); + + byte[][] pdus = null; + Cursor cursor = null; + try { + cursor = mResolver.query(mRawUri, RAW_PROJECTION, where.toString(), whereArgs, null); + int cursorCount = cursor.getCount(); + if (cursorCount != totalSegments - 1) { + // We don't have all the parts yet, store this one away + ContentValues values = new ContentValues(); + values.put("date", new Long(0)); + values.put("pdu", HexDump.toHexString(pdu, index, pdu.length - index)); + values.put("address", address); + values.put("reference_number", referenceNumber); + values.put("count", totalSegments); + values.put("sequence", segment); + values.put("destination_port", destinationPort); + + mResolver.insert(mRawUri, values); + + return; + } + + // All the parts are in place, deal with them + int pduColumn = cursor.getColumnIndex("pdu"); + int sequenceColumn = cursor.getColumnIndex("sequence"); + + pdus = new byte[totalSegments][]; + for (int i = 0; i < cursorCount; i++) { + cursor.moveToNext(); + int cursorSequence = (int)cursor.getLong(sequenceColumn); + pdus[cursorSequence - 1] = HexDump.hexStringToByteArray( + cursor.getString(pduColumn)); + } + // The last part will be added later + + // Remove the parts from the database + mResolver.delete(mRawUri, where.toString(), whereArgs); + } catch (SQLException e) { + Log.e(TAG, "Can't access multipart SMS database", e); + return; // TODO: NACK the message or something, don't just discard. + } finally { + if (cursor != null) cursor.close(); + } + + // Build up the data stream + ByteArrayOutputStream output = new ByteArrayOutputStream(); + for (int i = 0; i < totalSegments-1; i++) { + // reassemble the (WSP-)pdu + output.write(pdus[i], 0, pdus[i].length); + } + + // This one isn't in the DB, so add it + output.write(pdu, index, pdu.length - index); + + byte[] datagram = output.toByteArray(); + // Dispatch the PDU to applications + switch (destinationPort) { + case SmsHeader.PORT_WAP_PUSH: + // Handle the PUSH + dispatchWapPdu(datagram); + break; + + default:{ + pdus = new byte[1][]; + pdus[0] = datagram; + // The messages were sent to any other WAP port + dispatchPortAddressedPdus(pdus, destinationPort); + break; + } + } + } + + /** {@inheritDoc} */ + protected void sendMultipartText(String destinationAddress, String scAddress, + ArrayList<String> parts, ArrayList<PendingIntent> sentIntents, + ArrayList<PendingIntent> deliveryIntents) { + + int ref = ++sConcatenatedRef & 0xff; + + for (int i = 0, count = parts.size(); i < count; i++) { + // build SmsHeader data + byte[] data = new byte[5]; + data[0] = (byte) SmsHeader.CONCATENATED_8_BIT_REFERENCE; + data[1] = (byte) 3; // 3 bytes follow + data[2] = (byte) ref; // reference #, unique per message + data[3] = (byte) count; // total part count + data[4] = (byte) (i + 1); // 1-based sequence + + PendingIntent sentIntent = null; + PendingIntent deliveryIntent = null; + + if (sentIntents != null && sentIntents.size() > i) { + sentIntent = sentIntents.get(i); + } + if (deliveryIntents != null && deliveryIntents.size() > i) { + deliveryIntent = deliveryIntents.get(i); + } + + SmsMessage.SubmitPdu pdus = SmsMessage.getSubmitPdu(scAddress, destinationAddress, + parts.get(i), deliveryIntent != null, data); + + sendRawPdu(pdus.encodedScAddress, pdus.encodedMessage, sentIntent, deliveryIntent); + } + } + + protected void sendRawPdu(byte[] smsc, byte[] pdu, PendingIntent sentIntent, + PendingIntent deliveryIntent) { + super.sendRawPdu(smsc, pdu, sentIntent, deliveryIntent); + } + + /** {@inheritDoc} */ + protected void sendSms(SmsTracker tracker) { + HashMap map = tracker.mData; + + byte smsc[] = (byte[]) map.get("smsc"); + byte pdu[] = (byte[]) map.get("pdu"); + + Message reply = obtainMessage(EVENT_SEND_SMS_COMPLETE, tracker); + + mCm.sendCdmaSms(pdu, reply); + } + + /** {@inheritDoc} */ + protected void acknowledgeLastIncomingSms(boolean success, Message response){ + // FIXME unit test leaves cm == null. this should change + if (mCm != null) { + mCm.acknowledgeLastIncomingCdmaSms(success, response); + } + } + + /** {@inheritDoc} */ + protected void activateCellBroadcastSms(int activate, Message response) { + mCm.activateCdmaBroadcastSms(activate, response); + } + + /** {@inheritDoc} */ + protected void getCellBroadcastSmsConfig(Message response) { + mCm.getCdmaBroadcastConfig(response); + } + + /** {@inheritDoc} */ + protected void setCellBroadcastConfig(int[] configValuesArray, Message response) { + mCm.setCdmaBroadcastConfig(configValuesArray, response); + } + +} |