GsmSMSDispatcher.java revision 64c499113a758cf80cddfd4d0183f944a1a6645a
1/* 2 * Copyright (C) 2006 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.internal.telephony.gsm; 18 19import android.app.Activity; 20import android.app.PendingIntent; 21import android.app.PendingIntent.CanceledException; 22import android.content.Intent; 23import android.os.AsyncResult; 24import android.os.Message; 25import android.telephony.ServiceState; 26import android.util.Config; 27import android.util.Log; 28 29import com.android.internal.telephony.IccUtils; 30import com.android.internal.telephony.gsm.SmsMessage; 31import com.android.internal.telephony.SMSDispatcher; 32import com.android.internal.telephony.SmsHeader; 33import com.android.internal.telephony.SmsMessageBase; 34 35import java.util.ArrayList; 36import java.util.HashMap; 37 38 39final class GsmSMSDispatcher extends SMSDispatcher { 40 private static final String TAG = "GSM"; 41 42 private GSMPhone mGsmPhone; 43 44 GsmSMSDispatcher(GSMPhone phone) { 45 super(phone); 46 mGsmPhone = phone; 47 } 48 49 /** 50 * Called when a status report is received. This should correspond to 51 * a previously successful SEND. 52 * 53 * @param ar AsyncResult passed into the message handler. ar.result should 54 * be a String representing the status report PDU, as ASCII hex. 55 */ 56 protected void handleStatusReport(AsyncResult ar) { 57 String pduString = (String) ar.result; 58 SmsMessage sms = SmsMessage.newFromCDS(pduString); 59 60 if (sms != null) { 61 int messageRef = sms.messageRef; 62 for (int i = 0, count = deliveryPendingList.size(); i < count; i++) { 63 SmsTracker tracker = deliveryPendingList.get(i); 64 if (tracker.mMessageRef == messageRef) { 65 // Found it. Remove from list and broadcast. 66 deliveryPendingList.remove(i); 67 PendingIntent intent = tracker.mDeliveryIntent; 68 Intent fillIn = new Intent(); 69 fillIn.putExtra("pdu", IccUtils.hexStringToBytes(pduString)); 70 try { 71 intent.send(mContext, Activity.RESULT_OK, fillIn); 72 } catch (CanceledException ex) {} 73 74 // Only expect to see one tracker matching this messageref 75 break; 76 } 77 } 78 } 79 80 if (mCm != null) { 81 mCm.acknowledgeLastIncomingSMS(true, null); 82 } 83 } 84 85 86 /** 87 * Dispatches an incoming SMS messages. 88 * 89 * @param sms the incoming message from the phone 90 */ 91 protected void dispatchMessage(SmsMessageBase smsb) { 92 93 // If sms is null, means there was a parsing error. 94 // TODO: Should NAK this. 95 if (smsb == null) { 96 return; 97 } 98 SmsMessage sms = (SmsMessage) smsb; 99 boolean handled = false; 100 101 // Special case the message waiting indicator messages 102 if (sms.isMWISetMessage()) { 103 mGsmPhone.updateMessageWaitingIndicator(true); 104 handled |= sms.isMwiDontStore(); 105 if (Config.LOGD) { 106 Log.d(TAG, "Received voice mail indicator set SMS shouldStore=" + !handled); 107 } 108 } else if (sms.isMWIClearMessage()) { 109 mGsmPhone.updateMessageWaitingIndicator(false); 110 handled |= sms.isMwiDontStore(); 111 if (Config.LOGD) { 112 Log.d(TAG, "Received voice mail indicator clear SMS shouldStore=" + !handled); 113 } 114 } 115 116 if (handled) return; 117 118 SmsHeader smsHeader = sms.getUserDataHeader(); 119 // See if message is partial or port addressed. 120 if ((smsHeader == null) || (smsHeader.concatRef == null)) { 121 // Message is not partial (not part of concatenated sequence). 122 byte[][] pdus = new byte[1][]; 123 pdus[0] = sms.getPdu(); 124 125 if (smsHeader.portAddrs != null) { 126 if (smsHeader.portAddrs.destPort == SmsHeader.PORT_WAP_PUSH) { 127 mWapPush.dispatchWapPdu(sms.getUserData()); 128 } 129 // The message was sent to a port, so concoct a URI for it. 130 dispatchPortAddressedPdus(pdus, smsHeader.portAddrs.destPort); 131 } else { 132 // Normal short and non-port-addressed message, dispatch it. 133 dispatchPdus(pdus); 134 } 135 } else { 136 // Process the message part. 137 processMessagePart(sms, smsHeader.concatRef, smsHeader.portAddrs); 138 } 139 } 140 141 /** {@inheritDoc} */ 142 protected void sendMultipartText(String destinationAddress, String scAddress, 143 ArrayList<String> parts, ArrayList<PendingIntent> sentIntents, 144 ArrayList<PendingIntent> deliveryIntents) { 145 146 int refNumber = getNextConcatenatedRef() & 0x00FF; 147 148 for (int i = 0, msgCount = parts.size(); i < msgCount; i++) { 149 SmsHeader.ConcatRef concatRef = new SmsHeader.ConcatRef(); 150 concatRef.refNumber = refNumber; 151 concatRef.seqNumber = i + 1; // 1-based sequence 152 concatRef.msgCount = msgCount; 153 concatRef.isEightBits = false; 154 SmsHeader smsHeader = new SmsHeader(); 155 smsHeader.concatRef = concatRef; 156 157 PendingIntent sentIntent = null; 158 if (sentIntents != null && sentIntents.size() > i) { 159 sentIntent = sentIntents.get(i); 160 } 161 162 PendingIntent deliveryIntent = null; 163 if (deliveryIntents != null && deliveryIntents.size() > i) { 164 deliveryIntent = deliveryIntents.get(i); 165 } 166 167 SmsMessage.SubmitPdu pdus = SmsMessage.getSubmitPdu(scAddress, destinationAddress, 168 parts.get(i), deliveryIntent != null, SmsHeader.toByteArray(smsHeader)); 169 170 sendRawPdu(pdus.encodedScAddress, pdus.encodedMessage, sentIntent, deliveryIntent); 171 } 172 } 173 174 /** 175 * Send a multi-part text based SMS which already passed SMS control check. 176 * 177 * It is the working function for sendMultipartText(). 178 * 179 * @param destinationAddress the address to send the message to 180 * @param scAddress is the service center address or null to use 181 * the current default SMSC 182 * @param parts an <code>ArrayList</code> of strings that, in order, 183 * comprise the original message 184 * @param sentIntents if not null, an <code>ArrayList</code> of 185 * <code>PendingIntent</code>s (one for each message part) that is 186 * broadcast when the corresponding message part has been sent. 187 * The result code will be <code>Activity.RESULT_OK<code> for success, 188 * or one of these errors: 189 * <code>RESULT_ERROR_GENERIC_FAILURE</code> 190 * <code>RESULT_ERROR_RADIO_OFF</code> 191 * <code>RESULT_ERROR_NULL_PDU</code>. 192 * @param deliveryIntents if not null, an <code>ArrayList</code> of 193 * <code>PendingIntent</code>s (one for each message part) that is 194 * broadcast when the corresponding message part has been delivered 195 * to the recipient. The raw pdu of the status report is in the 196 * extended data ("pdu"). 197 */ 198 private void sendMultipartTextWithPermit(String destinationAddress, 199 String scAddress, ArrayList<String> parts, 200 ArrayList<PendingIntent> sentIntents, 201 ArrayList<PendingIntent> deliveryIntents) { 202 203 // check if in service 204 int ss = mPhone.getServiceState().getState(); 205 if (ss != ServiceState.STATE_IN_SERVICE) { 206 for (int i = 0, count = parts.size(); i < count; i++) { 207 PendingIntent sentIntent = null; 208 if (sentIntents != null && sentIntents.size() > i) { 209 sentIntent = sentIntents.get(i); 210 } 211 SmsTracker tracker = SmsTrackerFactory(null, sentIntent, null); 212 handleNotInService(ss, tracker); 213 } 214 return; 215 } 216 217 int refNumber = getNextConcatenatedRef() & 0x00FF; 218 219 for (int i = 0, msgCount = parts.size(); i < msgCount; i++) { 220 SmsHeader.ConcatRef concatRef = new SmsHeader.ConcatRef(); 221 concatRef.refNumber = refNumber; 222 concatRef.seqNumber = i + 1; // 1-based sequence 223 concatRef.msgCount = msgCount; 224 concatRef.isEightBits = false; 225 SmsHeader smsHeader = new SmsHeader(); 226 smsHeader.concatRef = concatRef; 227 228 PendingIntent sentIntent = null; 229 if (sentIntents != null && sentIntents.size() > i) { 230 sentIntent = sentIntents.get(i); 231 } 232 233 PendingIntent deliveryIntent = null; 234 if (deliveryIntents != null && deliveryIntents.size() > i) { 235 deliveryIntent = deliveryIntents.get(i); 236 } 237 238 SmsMessage.SubmitPdu pdus = SmsMessage.getSubmitPdu(scAddress, destinationAddress, 239 parts.get(i), deliveryIntent != null, SmsHeader.toByteArray(smsHeader)); 240 241 HashMap<String, Object> map = new HashMap<String, Object>(); 242 map.put("smsc", pdus.encodedScAddress); 243 map.put("pdu", pdus.encodedMessage); 244 245 SmsTracker tracker = SmsTrackerFactory(map, sentIntent, deliveryIntent); 246 sendSms(tracker); 247 } 248 } 249 250 /** {@inheritDoc} */ 251 protected void sendSms(SmsTracker tracker) { 252 HashMap map = tracker.mData; 253 254 byte smsc[] = (byte[]) map.get("smsc"); 255 byte pdu[] = (byte[]) map.get("pdu"); 256 257 Message reply = obtainMessage(EVENT_SEND_SMS_COMPLETE, tracker); 258 mCm.sendSMS(IccUtils.bytesToHexString(smsc), 259 IccUtils.bytesToHexString(pdu), reply); 260 } 261 262 /** 263 * Send the multi-part SMS based on multipart Sms tracker 264 * 265 * @param tracker holds the multipart Sms tracker ready to be sent 266 */ 267 protected void sendMultipartSms (SmsTracker tracker) { 268 ArrayList<String> parts; 269 ArrayList<PendingIntent> sentIntents; 270 ArrayList<PendingIntent> deliveryIntents; 271 272 HashMap map = tracker.mData; 273 274 String destinationAddress = (String) map.get("destination"); 275 String scAddress = (String) map.get("scaddress"); 276 277 parts = (ArrayList<String>) map.get("parts"); 278 sentIntents = (ArrayList<PendingIntent>) map.get("sentIntents"); 279 deliveryIntents = (ArrayList<PendingIntent>) map.get("deliveryIntents"); 280 281 sendMultipartTextWithPermit(destinationAddress, 282 scAddress, parts, sentIntents, deliveryIntents); 283 284 } 285 286 /** {@inheritDoc} */ 287 protected void acknowledgeLastIncomingSms(boolean success, Message response){ 288 // FIXME unit test leaves cm == null. this should change 289 if (mCm != null) { 290 mCm.acknowledgeLastIncomingSMS(success, response); 291 } 292 } 293 294 /** {@inheritDoc} */ 295 protected void activateCellBroadcastSms(int activate, Message response) { 296 // Unless CBS is implemented for GSM, this point should be unreachable. 297 Log.e(TAG, "Error! The functionality cell broadcast sms is not implemented for GSM."); 298 response.recycle(); 299 } 300 301 /** {@inheritDoc} */ 302 protected void getCellBroadcastSmsConfig(Message response){ 303 // Unless CBS is implemented for GSM, this point should be unreachable. 304 Log.e(TAG, "Error! The functionality cell broadcast sms is not implemented for GSM."); 305 response.recycle(); 306 } 307 308 /** {@inheritDoc} */ 309 protected void setCellBroadcastConfig(int[] configValuesArray, Message response) { 310 // Unless CBS is implemented for GSM, this point should be unreachable. 311 Log.e(TAG, "Error! The functionality cell broadcast sms is not implemented for GSM."); 312 response.recycle(); 313 } 314 315} 316