summaryrefslogtreecommitdiff
path: root/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
diff options
context:
space:
mode:
Diffstat (limited to 'telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java')
-rw-r--r--telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java375
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);
+ }
+
+}