ImsSMSDispatcher.java revision 8d8e8f45200d17b78d196d8cba389fd9c547ac11
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; 18 19import static android.telephony.SmsManager.RESULT_ERROR_GENERIC_FAILURE; 20 21import java.util.ArrayList; 22import java.util.HashMap; 23import java.util.List; 24 25import android.app.PendingIntent; 26import android.app.PendingIntent.CanceledException; 27import android.os.AsyncResult; 28import android.os.Message; 29import android.provider.Telephony.Sms.Intents; 30import android.telephony.Rlog; 31 32import com.android.internal.telephony.cdma.CdmaSMSDispatcher; 33import com.android.internal.telephony.gsm.GsmSMSDispatcher; 34import com.android.internal.telephony.InboundSmsHandler; 35import com.android.internal.telephony.gsm.GsmInboundSmsHandler; 36import com.android.internal.telephony.cdma.CdmaInboundSmsHandler; 37import com.android.internal.telephony.SmsBroadcastUndelivered; 38 39public final class ImsSMSDispatcher extends SMSDispatcher { 40 private static final String TAG = "RIL_ImsSms"; 41 42 private SMSDispatcher mCdmaDispatcher; 43 private SMSDispatcher mGsmDispatcher; 44 45 private GsmInboundSmsHandler mGsmInboundSmsHandler; 46 private CdmaInboundSmsHandler mCdmaInboundSmsHandler; 47 48 49 /** true if IMS is registered and sms is supported, false otherwise.*/ 50 private boolean mIms = false; 51 private String mImsSmsFormat = SmsConstants.FORMAT_UNKNOWN; 52 53 public ImsSMSDispatcher(PhoneBase phone, SmsStorageMonitor storageMonitor, 54 SmsUsageMonitor usageMonitor) { 55 super(phone, usageMonitor, null); 56 Rlog.d(TAG, "ImsSMSDispatcher created"); 57 58 // Create dispatchers, inbound SMS handlers and 59 // broadcast undelivered messages in raw table. 60 mCdmaDispatcher = new CdmaSMSDispatcher(phone, usageMonitor, this); 61 mGsmInboundSmsHandler = GsmInboundSmsHandler.makeInboundSmsHandler(phone.getContext(), 62 storageMonitor, phone); 63 mCdmaInboundSmsHandler = CdmaInboundSmsHandler.makeInboundSmsHandler(phone.getContext(), 64 storageMonitor, phone, (CdmaSMSDispatcher) mCdmaDispatcher); 65 mGsmDispatcher = new GsmSMSDispatcher(phone, usageMonitor, this, mGsmInboundSmsHandler); 66 Thread broadcastThread = new Thread(new SmsBroadcastUndelivered(phone.getContext(), 67 mGsmInboundSmsHandler, mCdmaInboundSmsHandler)); 68 broadcastThread.start(); 69 70 mCi.registerForOn(this, EVENT_RADIO_ON, null); 71 mCi.registerForImsNetworkStateChanged(this, EVENT_IMS_STATE_CHANGED, null); 72 } 73 74 /* Updates the phone object when there is a change */ 75 @Override 76 protected void updatePhoneObject(PhoneBase phone) { 77 Rlog.d(TAG, "In IMS updatePhoneObject "); 78 super.updatePhoneObject(phone); 79 mCdmaDispatcher.updatePhoneObject(phone); 80 mGsmDispatcher.updatePhoneObject(phone); 81 mGsmInboundSmsHandler.updatePhoneObject(phone); 82 mCdmaInboundSmsHandler.updatePhoneObject(phone); 83 } 84 85 public void dispose() { 86 mCi.unregisterForOn(this); 87 mCi.unregisterForImsNetworkStateChanged(this); 88 mGsmDispatcher.dispose(); 89 mCdmaDispatcher.dispose(); 90 mGsmInboundSmsHandler.dispose(); 91 mCdmaInboundSmsHandler.dispose(); 92 } 93 94 /** 95 * Handles events coming from the phone stack. Overridden from handler. 96 * 97 * @param msg the message to handle 98 */ 99 @Override 100 public void handleMessage(Message msg) { 101 AsyncResult ar; 102 103 switch (msg.what) { 104 case EVENT_RADIO_ON: 105 case EVENT_IMS_STATE_CHANGED: // received unsol 106 mCi.getImsRegistrationState(this.obtainMessage(EVENT_IMS_STATE_DONE)); 107 break; 108 109 case EVENT_IMS_STATE_DONE: 110 ar = (AsyncResult) msg.obj; 111 112 if (ar.exception == null) { 113 updateImsInfo(ar); 114 } else { 115 Rlog.e(TAG, "IMS State query failed with exp " 116 + ar.exception); 117 } 118 break; 119 120 default: 121 super.handleMessage(msg); 122 } 123 } 124 125 private void setImsSmsFormat(int format) { 126 // valid format? 127 switch (format) { 128 case PhoneConstants.PHONE_TYPE_GSM: 129 mImsSmsFormat = "3gpp"; 130 break; 131 case PhoneConstants.PHONE_TYPE_CDMA: 132 mImsSmsFormat = "3gpp2"; 133 break; 134 default: 135 mImsSmsFormat = "unknown"; 136 break; 137 } 138 } 139 140 private void updateImsInfo(AsyncResult ar) { 141 int[] responseArray = (int[])ar.result; 142 143 mIms = false; 144 if (responseArray[0] == 1) { // IMS is registered 145 Rlog.d(TAG, "IMS is registered!"); 146 mIms = true; 147 } else { 148 Rlog.d(TAG, "IMS is NOT registered!"); 149 } 150 151 setImsSmsFormat(responseArray[1]); 152 153 if (("unknown".equals(mImsSmsFormat))) { 154 Rlog.e(TAG, "IMS format was unknown!"); 155 // failed to retrieve valid IMS SMS format info, set IMS to unregistered 156 mIms = false; 157 } 158 } 159 160 @Override 161 protected void sendData(String destAddr, String scAddr, int destPort, 162 byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) { 163 if (isCdmaMo()) { 164 mCdmaDispatcher.sendData(destAddr, scAddr, destPort, 165 data, sentIntent, deliveryIntent); 166 } else { 167 mGsmDispatcher.sendData(destAddr, scAddr, destPort, 168 data, sentIntent, deliveryIntent); 169 } 170 } 171 172 @Override 173 protected void sendMultipartText(String destAddr, String scAddr, 174 ArrayList<String> parts, ArrayList<PendingIntent> sentIntents, 175 ArrayList<PendingIntent> deliveryIntents) { 176 if (isCdmaMo()) { 177 mCdmaDispatcher.sendMultipartText(destAddr, scAddr, 178 parts, sentIntents, deliveryIntents); 179 } else { 180 mGsmDispatcher.sendMultipartText(destAddr, scAddr, 181 parts, sentIntents, deliveryIntents); 182 } 183 } 184 185 @Override 186 protected void sendSms(SmsTracker tracker) { 187 // sendSms is a helper function to other send functions, sendText/Data... 188 // it is not part of ISms.stub 189 Rlog.e(TAG, "sendSms should never be called from here!"); 190 } 191 192 @Override 193 protected void sendSmsByPstn(SmsTracker tracker) { 194 // This function should be defined in Gsm/CdmaDispatcher. 195 Rlog.e(TAG, "sendSmsByPstn should never be called from here!"); 196 } 197 198 @Override 199 protected void updateSmsSendStatus(int messageRef, boolean success) { 200 if (isCdmaMo()) { 201 updateSmsSendStatusHelper(messageRef, mCdmaDispatcher.sendPendingList, 202 mCdmaDispatcher, success); 203 updateSmsSendStatusHelper(messageRef, mGsmDispatcher.sendPendingList, 204 null, success); 205 } else { 206 updateSmsSendStatusHelper(messageRef, mGsmDispatcher.sendPendingList, 207 mGsmDispatcher, success); 208 updateSmsSendStatusHelper(messageRef, mCdmaDispatcher.sendPendingList, 209 null, success); 210 } 211 } 212 213 /** 214 * Find a tracker in a list to update its status. If the status is successful, 215 * send an EVENT_SEND_SMS_COMPLETE message. Otherwise, resend the message by PSTN if 216 * feasible. 217 * 218 * @param messageRef the reference number of the tracker. 219 * @param sendPendingList the list of trackers to look into. 220 * @param smsDispatcher the dispatcher for resending the message by PSTN. 221 * @param success true iff the message was sent successfully. 222 */ 223 private void updateSmsSendStatusHelper(int messageRef, 224 List<SmsTracker> sendPendingList, 225 SMSDispatcher smsDispatcher, 226 boolean success) { 227 synchronized (sendPendingList) { 228 for (int i = 0, count = sendPendingList.size(); i < count; i++) { 229 SmsTracker tracker = sendPendingList.get(i); 230 if (tracker.mMessageRef == messageRef) { 231 // Found it. Remove from list and broadcast. 232 sendPendingList.remove(i); 233 if (success) { 234 Rlog.d(TAG, "Sending SMS by IP succeeded."); 235 sendMessage(obtainMessage(EVENT_SEND_SMS_COMPLETE, 236 new AsyncResult(tracker, null, null))); 237 } else { 238 Rlog.d(TAG, "Sending SMS by IP failed."); 239 if (smsDispatcher != null) { 240 smsDispatcher.sendSmsByPstn(tracker); 241 } else { 242 Rlog.e(TAG, "No feasible way to send this SMS."); 243 } 244 } 245 // Only expect to see one tracker matching this messageref. 246 break; 247 } 248 } 249 } 250 } 251 252 @Override 253 protected void sendText(String destAddr, String scAddr, String text, 254 PendingIntent sentIntent, PendingIntent deliveryIntent) { 255 Rlog.d(TAG, "sendText"); 256 if (isCdmaMo()) { 257 mCdmaDispatcher.sendText(destAddr, scAddr, 258 text, sentIntent, deliveryIntent); 259 } else { 260 mGsmDispatcher.sendText(destAddr, scAddr, 261 text, sentIntent, deliveryIntent); 262 } 263 } 264 265 @Override 266 public void sendRetrySms(SmsTracker tracker) { 267 String oldFormat = tracker.mFormat; 268 269 // newFormat will be based on voice technology 270 String newFormat = 271 (PhoneConstants.PHONE_TYPE_CDMA == mPhone.getPhoneType()) ? 272 mCdmaDispatcher.getFormat() : 273 mGsmDispatcher.getFormat(); 274 275 // was previously sent sms format match with voice tech? 276 if (oldFormat.equals(newFormat)) { 277 if (isCdmaFormat(newFormat)) { 278 Rlog.d(TAG, "old format matched new format (cdma)"); 279 mCdmaDispatcher.sendSms(tracker); 280 return; 281 } else { 282 Rlog.d(TAG, "old format matched new format (gsm)"); 283 mGsmDispatcher.sendSms(tracker); 284 return; 285 } 286 } 287 288 // format didn't match, need to re-encode. 289 HashMap map = tracker.mData; 290 291 // to re-encode, fields needed are: scAddr, destAddr, and 292 // text if originally sent as sendText or 293 // data and destPort if originally sent as sendData. 294 if (!( map.containsKey("scAddr") && map.containsKey("destAddr") && 295 ( map.containsKey("text") || 296 (map.containsKey("data") && map.containsKey("destPort"))))) { 297 // should never come here... 298 Rlog.e(TAG, "sendRetrySms failed to re-encode per missing fields!"); 299 if (tracker.mSentIntent != null) { 300 int error = RESULT_ERROR_GENERIC_FAILURE; 301 // Done retrying; return an error to the app. 302 try { 303 tracker.mSentIntent.send(mContext, error, null); 304 } catch (CanceledException ex) {} 305 } 306 return; 307 } 308 String scAddr = (String)map.get("scAddr"); 309 String destAddr = (String)map.get("destAddr"); 310 311 SmsMessageBase.SubmitPduBase pdu = null; 312 // figure out from tracker if this was sendText/Data 313 if (map.containsKey("text")) { 314 Rlog.d(TAG, "sms failed was text"); 315 String text = (String)map.get("text"); 316 317 if (isCdmaFormat(newFormat)) { 318 Rlog.d(TAG, "old format (gsm) ==> new format (cdma)"); 319 pdu = com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu( 320 scAddr, destAddr, text, (tracker.mDeliveryIntent != null), null); 321 } else { 322 Rlog.d(TAG, "old format (cdma) ==> new format (gsm)"); 323 pdu = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu( 324 scAddr, destAddr, text, (tracker.mDeliveryIntent != null), null); 325 } 326 } else if (map.containsKey("data")) { 327 Rlog.d(TAG, "sms failed was data"); 328 byte[] data = (byte[])map.get("data"); 329 Integer destPort = (Integer)map.get("destPort"); 330 331 if (isCdmaFormat(newFormat)) { 332 Rlog.d(TAG, "old format (gsm) ==> new format (cdma)"); 333 pdu = com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu( 334 scAddr, destAddr, destPort.intValue(), data, 335 (tracker.mDeliveryIntent != null)); 336 } else { 337 Rlog.d(TAG, "old format (cdma) ==> new format (gsm)"); 338 pdu = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu( 339 scAddr, destAddr, destPort.intValue(), data, 340 (tracker.mDeliveryIntent != null)); 341 } 342 } 343 344 // replace old smsc and pdu with newly encoded ones 345 map.put("smsc", pdu.encodedScAddress); 346 map.put("pdu", pdu.encodedMessage); 347 348 SMSDispatcher dispatcher = (isCdmaFormat(newFormat)) ? 349 mCdmaDispatcher : mGsmDispatcher; 350 351 tracker.mFormat = dispatcher.getFormat(); 352 dispatcher.sendSms(tracker); 353 } 354 355 @Override 356 protected String getFormat() { 357 // this function should be defined in Gsm/CdmaDispatcher. 358 Rlog.e(TAG, "getFormat should never be called from here!"); 359 return "unknown"; 360 } 361 362 @Override 363 protected GsmAlphabet.TextEncodingDetails calculateLength( 364 CharSequence messageBody, boolean use7bitOnly) { 365 Rlog.e(TAG, "Error! Not implemented for IMS."); 366 return null; 367 } 368 369 @Override 370 protected void sendNewSubmitPdu(String destinationAddress, String scAddress, String message, 371 SmsHeader smsHeader, int format, PendingIntent sentIntent, 372 PendingIntent deliveryIntent, boolean lastPart) { 373 Rlog.e(TAG, "Error! Not implemented for IMS."); 374 } 375 376 @Override 377 public boolean isIms() { 378 return mIms; 379 } 380 381 @Override 382 public String getImsSmsFormat() { 383 return mImsSmsFormat; 384 } 385 386 /** 387 * Determines whether or not to use CDMA format for MO SMS. 388 * If SMS over IMS is supported, then format is based on IMS SMS format, 389 * otherwise format is based on current phone type. 390 * 391 * @return true if Cdma format should be used for MO SMS, false otherwise. 392 */ 393 private boolean isCdmaMo() { 394 if (!isIms()) { 395 // IMS is not registered, use Voice technology to determine SMS format. 396 return (PhoneConstants.PHONE_TYPE_CDMA == mPhone.getPhoneType()); 397 } 398 // IMS is registered with SMS support 399 return isCdmaFormat(mImsSmsFormat); 400 } 401 402 /** 403 * Determines whether or not format given is CDMA format. 404 * 405 * @param format 406 * @return true if format given is CDMA format, false otherwise. 407 */ 408 private boolean isCdmaFormat(String format) { 409 return (mCdmaDispatcher.getFormat().equals(format)); 410 } 411} 412