GsmSMSDispatcher.java revision 1f952a178db86559ff4bab79c4a9b5fae18096bf
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.provider.Telephony.Sms.Intents; 26import android.telephony.ServiceState; 27import android.util.Config; 28import android.util.Log; 29 30import com.android.internal.telephony.IccUtils; 31import com.android.internal.telephony.gsm.SmsMessage; 32import com.android.internal.telephony.CommandsInterface; 33import com.android.internal.telephony.SMSDispatcher; 34import com.android.internal.telephony.SmsHeader; 35import com.android.internal.telephony.SmsMessageBase; 36 37import java.util.ArrayList; 38import java.util.HashMap; 39 40 41final class GsmSMSDispatcher extends SMSDispatcher { 42 private static final String TAG = "GSM"; 43 44 private GSMPhone mGsmPhone; 45 46 GsmSMSDispatcher(GSMPhone phone) { 47 super(phone); 48 mGsmPhone = phone; 49 } 50 51 /** 52 * Called when a status report is received. This should correspond to 53 * a previously successful SEND. 54 * 55 * @param ar AsyncResult passed into the message handler. ar.result should 56 * be a String representing the status report PDU, as ASCII hex. 57 */ 58 protected void handleStatusReport(AsyncResult ar) { 59 String pduString = (String) ar.result; 60 SmsMessage sms = SmsMessage.newFromCDS(pduString); 61 62 if (sms != null) { 63 int messageRef = sms.messageRef; 64 for (int i = 0, count = deliveryPendingList.size(); i < count; i++) { 65 SmsTracker tracker = deliveryPendingList.get(i); 66 if (tracker.mMessageRef == messageRef) { 67 // Found it. Remove from list and broadcast. 68 deliveryPendingList.remove(i); 69 PendingIntent intent = tracker.mDeliveryIntent; 70 Intent fillIn = new Intent(); 71 fillIn.putExtra("pdu", IccUtils.hexStringToBytes(pduString)); 72 try { 73 intent.send(mContext, Activity.RESULT_OK, fillIn); 74 } catch (CanceledException ex) {} 75 76 // Only expect to see one tracker matching this messageref 77 break; 78 } 79 } 80 } 81 acknowledgeLastIncomingSms(true, Intents.RESULT_SMS_HANDLED, null); 82 } 83 84 85 /** {@inheritDoc} */ 86 protected int dispatchMessage(SmsMessageBase smsb) { 87 88 // If sms is null, means there was a parsing error. 89 if (smsb == null) { 90 return Intents.RESULT_SMS_GENERIC_ERROR; 91 } 92 SmsMessage sms = (SmsMessage) smsb; 93 boolean handled = false; 94 95 // Special case the message waiting indicator messages 96 if (sms.isMWISetMessage()) { 97 mGsmPhone.updateMessageWaitingIndicator(true); 98 handled |= sms.isMwiDontStore(); 99 if (Config.LOGD) { 100 Log.d(TAG, "Received voice mail indicator set SMS shouldStore=" + !handled); 101 } 102 } else if (sms.isMWIClearMessage()) { 103 mGsmPhone.updateMessageWaitingIndicator(false); 104 handled |= sms.isMwiDontStore(); 105 if (Config.LOGD) { 106 Log.d(TAG, "Received voice mail indicator clear SMS shouldStore=" + !handled); 107 } 108 } 109 110 if (handled) { 111 return Intents.RESULT_SMS_HANDLED; 112 } 113 114 SmsHeader smsHeader = sms.getUserDataHeader(); 115 // See if message is partial or port addressed. 116 if ((smsHeader == null) || (smsHeader.concatRef == null)) { 117 // Message is not partial (not part of concatenated sequence). 118 byte[][] pdus = new byte[1][]; 119 pdus[0] = sms.getPdu(); 120 121 if (smsHeader != null && smsHeader.portAddrs != null) { 122 if (smsHeader.portAddrs.destPort == SmsHeader.PORT_WAP_PUSH) { 123 return mWapPush.dispatchWapPdu(sms.getUserData()); 124 } else { 125 // The message was sent to a port, so concoct a URI for it. 126 dispatchPortAddressedPdus(pdus, smsHeader.portAddrs.destPort); 127 } 128 } else { 129 // Normal short and non-port-addressed message, dispatch it. 130 dispatchPdus(pdus); 131 } 132 return Activity.RESULT_OK; 133 } else { 134 // Process the message part. 135 return processMessagePart(sms, smsHeader.concatRef, smsHeader.portAddrs); 136 } 137 } 138 139 /** {@inheritDoc} */ 140 protected void sendData(String destAddr, String scAddr, int destPort, 141 byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) { 142 SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu( 143 scAddr, destAddr, destPort, data, (deliveryIntent != null)); 144 sendRawPdu(pdu.encodedScAddress, pdu.encodedMessage, sentIntent, deliveryIntent); 145 } 146 147 /** {@inheritDoc} */ 148 protected void sendText(String destAddr, String scAddr, String text, 149 PendingIntent sentIntent, PendingIntent deliveryIntent) { 150 SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu( 151 scAddr, destAddr, text, (deliveryIntent != null)); 152 sendRawPdu(pdu.encodedScAddress, pdu.encodedMessage, sentIntent, deliveryIntent); 153 } 154 155 /** {@inheritDoc} */ 156 protected void sendMultipartText(String destinationAddress, String scAddress, 157 ArrayList<String> parts, ArrayList<PendingIntent> sentIntents, 158 ArrayList<PendingIntent> deliveryIntents) { 159 160 int refNumber = getNextConcatenatedRef() & 0x00FF; 161 162 for (int i = 0, msgCount = parts.size(); i < msgCount; i++) { 163 SmsHeader.ConcatRef concatRef = new SmsHeader.ConcatRef(); 164 concatRef.refNumber = refNumber; 165 concatRef.seqNumber = i + 1; // 1-based sequence 166 concatRef.msgCount = msgCount; 167 // TODO: We currently set this to true since our messaging app will never 168 // send more than 255 parts (it converts the message to MMS well before that). 169 // However, we should support 3rd party messaging apps that might need 16-bit 170 // references 171 // Note: It's not sufficient to just flip this bit to true; it will have 172 // ripple effects (several calculations assume 8-bit ref). 173 concatRef.isEightBits = true; 174 SmsHeader smsHeader = new SmsHeader(); 175 smsHeader.concatRef = concatRef; 176 177 PendingIntent sentIntent = null; 178 if (sentIntents != null && sentIntents.size() > i) { 179 sentIntent = sentIntents.get(i); 180 } 181 182 PendingIntent deliveryIntent = null; 183 if (deliveryIntents != null && deliveryIntents.size() > i) { 184 deliveryIntent = deliveryIntents.get(i); 185 } 186 187 SmsMessage.SubmitPdu pdus = SmsMessage.getSubmitPdu(scAddress, destinationAddress, 188 parts.get(i), deliveryIntent != null, SmsHeader.toByteArray(smsHeader)); 189 190 sendRawPdu(pdus.encodedScAddress, pdus.encodedMessage, sentIntent, deliveryIntent); 191 } 192 } 193 194 /** 195 * Send a multi-part text based SMS which already passed SMS control check. 196 * 197 * It is the working function for sendMultipartText(). 198 * 199 * @param destinationAddress the address to send the message to 200 * @param scAddress is the service center address or null to use 201 * the current default SMSC 202 * @param parts an <code>ArrayList</code> of strings that, in order, 203 * comprise the original message 204 * @param sentIntents if not null, an <code>ArrayList</code> of 205 * <code>PendingIntent</code>s (one for each message part) that is 206 * broadcast when the corresponding message part has been sent. 207 * The result code will be <code>Activity.RESULT_OK<code> for success, 208 * or one of these errors: 209 * <code>RESULT_ERROR_GENERIC_FAILURE</code> 210 * <code>RESULT_ERROR_RADIO_OFF</code> 211 * <code>RESULT_ERROR_NULL_PDU</code>. 212 * @param deliveryIntents if not null, an <code>ArrayList</code> of 213 * <code>PendingIntent</code>s (one for each message part) that is 214 * broadcast when the corresponding message part has been delivered 215 * to the recipient. The raw pdu of the status report is in the 216 * extended data ("pdu"). 217 */ 218 private void sendMultipartTextWithPermit(String destinationAddress, 219 String scAddress, ArrayList<String> parts, 220 ArrayList<PendingIntent> sentIntents, 221 ArrayList<PendingIntent> deliveryIntents) { 222 223 // check if in service 224 int ss = mPhone.getServiceState().getState(); 225 if (ss != ServiceState.STATE_IN_SERVICE) { 226 for (int i = 0, count = parts.size(); i < count; i++) { 227 PendingIntent sentIntent = null; 228 if (sentIntents != null && sentIntents.size() > i) { 229 sentIntent = sentIntents.get(i); 230 } 231 SmsTracker tracker = SmsTrackerFactory(null, sentIntent, null); 232 handleNotInService(ss, tracker); 233 } 234 return; 235 } 236 237 int refNumber = getNextConcatenatedRef() & 0x00FF; 238 239 for (int i = 0, msgCount = parts.size(); i < msgCount; i++) { 240 SmsHeader.ConcatRef concatRef = new SmsHeader.ConcatRef(); 241 concatRef.refNumber = refNumber; 242 concatRef.seqNumber = i + 1; // 1-based sequence 243 concatRef.msgCount = msgCount; 244 concatRef.isEightBits = false; 245 SmsHeader smsHeader = new SmsHeader(); 246 smsHeader.concatRef = concatRef; 247 248 PendingIntent sentIntent = null; 249 if (sentIntents != null && sentIntents.size() > i) { 250 sentIntent = sentIntents.get(i); 251 } 252 253 PendingIntent deliveryIntent = null; 254 if (deliveryIntents != null && deliveryIntents.size() > i) { 255 deliveryIntent = deliveryIntents.get(i); 256 } 257 258 SmsMessage.SubmitPdu pdus = SmsMessage.getSubmitPdu(scAddress, destinationAddress, 259 parts.get(i), deliveryIntent != null, SmsHeader.toByteArray(smsHeader)); 260 261 HashMap<String, Object> map = new HashMap<String, Object>(); 262 map.put("smsc", pdus.encodedScAddress); 263 map.put("pdu", pdus.encodedMessage); 264 265 SmsTracker tracker = SmsTrackerFactory(map, sentIntent, deliveryIntent); 266 sendSms(tracker); 267 } 268 } 269 270 /** {@inheritDoc} */ 271 protected void sendSms(SmsTracker tracker) { 272 HashMap map = tracker.mData; 273 274 byte smsc[] = (byte[]) map.get("smsc"); 275 byte pdu[] = (byte[]) map.get("pdu"); 276 277 Message reply = obtainMessage(EVENT_SEND_SMS_COMPLETE, tracker); 278 mCm.sendSMS(IccUtils.bytesToHexString(smsc), 279 IccUtils.bytesToHexString(pdu), reply); 280 } 281 282 /** 283 * Send the multi-part SMS based on multipart Sms tracker 284 * 285 * @param tracker holds the multipart Sms tracker ready to be sent 286 */ 287 protected void sendMultipartSms (SmsTracker tracker) { 288 ArrayList<String> parts; 289 ArrayList<PendingIntent> sentIntents; 290 ArrayList<PendingIntent> deliveryIntents; 291 292 HashMap map = tracker.mData; 293 294 String destinationAddress = (String) map.get("destination"); 295 String scAddress = (String) map.get("scaddress"); 296 297 parts = (ArrayList<String>) map.get("parts"); 298 sentIntents = (ArrayList<PendingIntent>) map.get("sentIntents"); 299 deliveryIntents = (ArrayList<PendingIntent>) map.get("deliveryIntents"); 300 301 sendMultipartTextWithPermit(destinationAddress, 302 scAddress, parts, sentIntents, deliveryIntents); 303 304 } 305 306 /** {@inheritDoc} */ 307 protected void acknowledgeLastIncomingSms(boolean success, int result, Message response){ 308 // FIXME unit test leaves cm == null. this should change 309 if (mCm != null) { 310 mCm.acknowledgeLastIncomingGsmSms(success, resultToCause(result), response); 311 } 312 } 313 314 /** {@inheritDoc} */ 315 protected void activateCellBroadcastSms(int activate, Message response) { 316 // Unless CBS is implemented for GSM, this point should be unreachable. 317 Log.e(TAG, "Error! The functionality cell broadcast sms is not implemented for GSM."); 318 response.recycle(); 319 } 320 321 /** {@inheritDoc} */ 322 protected void getCellBroadcastSmsConfig(Message response){ 323 // Unless CBS is implemented for GSM, this point should be unreachable. 324 Log.e(TAG, "Error! The functionality cell broadcast sms is not implemented for GSM."); 325 response.recycle(); 326 } 327 328 /** {@inheritDoc} */ 329 protected void setCellBroadcastConfig(int[] configValuesArray, Message response) { 330 // Unless CBS is implemented for GSM, this point should be unreachable. 331 Log.e(TAG, "Error! The functionality cell broadcast sms is not implemented for GSM."); 332 response.recycle(); 333 } 334 335 private int resultToCause(int rc) { 336 switch (rc) { 337 case Activity.RESULT_OK: 338 case Intents.RESULT_SMS_HANDLED: 339 // Cause code is ignored on success. 340 return 0; 341 case Intents.RESULT_SMS_OUT_OF_MEMORY: 342 return CommandsInterface.GSM_SMS_FAIL_CAUSE_MEMORY_CAPACITY_EXCEEDED; 343 case Intents.RESULT_SMS_GENERIC_ERROR: 344 default: 345 return CommandsInterface.GSM_SMS_FAIL_CAUSE_UNSPECIFIED_ERROR; 346 } 347 } 348} 349