ImsSMSDispatcher.java revision 6f64a8956a101bd16c900017f7692f9284c6bdd6
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; 20import static android.telephony.SmsManager.RESULT_ERROR_NULL_PDU; 21 22import java.util.ArrayList; 23import java.util.HashMap; 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 sendText(String destAddr, String scAddr, String text, 194 PendingIntent sentIntent, PendingIntent deliveryIntent) { 195 Rlog.d(TAG, "sendText"); 196 if (isCdmaMo()) { 197 mCdmaDispatcher.sendText(destAddr, scAddr, 198 text, sentIntent, deliveryIntent); 199 } else { 200 mGsmDispatcher.sendText(destAddr, scAddr, 201 text, sentIntent, deliveryIntent); 202 } 203 } 204 205 @Override 206 protected void injectSmsPdu(byte[] pdu, String format, PendingIntent receivedIntent) { 207 Rlog.d(TAG, "ImsSMSDispatcher:injectSmsPdu"); 208 try { 209 // TODO We need to decide whether we should allow injecting GSM(3gpp) 210 // SMS pdus when the phone is camping on CDMA(3gpp2) network and vice versa. 211 android.telephony.SmsMessage msg = 212 android.telephony.SmsMessage.createFromPdu(pdu, format); 213 214 // Only class 1 SMS are allowed to be injected. 215 if (msg.getMessageClass() != android.telephony.SmsMessage.MessageClass.CLASS_1) { 216 if (receivedIntent != null) 217 receivedIntent.send(Intents.RESULT_SMS_GENERIC_ERROR); 218 return; 219 } 220 221 AsyncResult ar = new AsyncResult(receivedIntent, msg, null); 222 223 if (format.equals(SmsConstants.FORMAT_3GPP)) { 224 Rlog.i(TAG, "ImsSMSDispatcher:injectSmsText Sending msg=" + msg + 225 ", format=" + format + "to mGsmInboundSmsHandler"); 226 mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_INJECT_SMS, ar); 227 } else if (format.equals(SmsConstants.FORMAT_3GPP2)) { 228 Rlog.i(TAG, "ImsSMSDispatcher:injectSmsText Sending msg=" + msg + 229 ", format=" + format + "to mCdmaInboundSmsHandler"); 230 mCdmaInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_INJECT_SMS, ar); 231 } else { 232 // Invalid pdu format. 233 Rlog.e(TAG, "Invalid pdu format: " + format); 234 if (receivedIntent != null) 235 receivedIntent.send(Intents.RESULT_SMS_GENERIC_ERROR); 236 } 237 } catch (Exception e) { 238 Rlog.e(TAG, "injectSmsPdu failed: ", e); 239 try { 240 if (receivedIntent != null) 241 receivedIntent.send(Intents.RESULT_SMS_GENERIC_ERROR); 242 } catch (CanceledException ex) {} 243 } 244 } 245 246 @Override 247 public void sendRetrySms(SmsTracker tracker) { 248 String oldFormat = tracker.mFormat; 249 250 // newFormat will be based on voice technology 251 String newFormat = 252 (PhoneConstants.PHONE_TYPE_CDMA == mPhone.getPhoneType()) ? 253 mCdmaDispatcher.getFormat() : 254 mGsmDispatcher.getFormat(); 255 256 // was previously sent sms format match with voice tech? 257 if (oldFormat.equals(newFormat)) { 258 if (isCdmaFormat(newFormat)) { 259 Rlog.d(TAG, "old format matched new format (cdma)"); 260 mCdmaDispatcher.sendSms(tracker); 261 return; 262 } else { 263 Rlog.d(TAG, "old format matched new format (gsm)"); 264 mGsmDispatcher.sendSms(tracker); 265 return; 266 } 267 } 268 269 // format didn't match, need to re-encode. 270 HashMap map = tracker.mData; 271 272 // to re-encode, fields needed are: scAddr, destAddr, and 273 // text if originally sent as sendText or 274 // data and destPort if originally sent as sendData. 275 if (!( map.containsKey("scAddr") && map.containsKey("destAddr") && 276 ( map.containsKey("text") || 277 (map.containsKey("data") && map.containsKey("destPort"))))) { 278 // should never come here... 279 Rlog.e(TAG, "sendRetrySms failed to re-encode per missing fields!"); 280 if (tracker.mSentIntent != null) { 281 int error = RESULT_ERROR_GENERIC_FAILURE; 282 // Done retrying; return an error to the app. 283 try { 284 tracker.mSentIntent.send(mContext, error, null); 285 } catch (CanceledException ex) {} 286 } 287 return; 288 } 289 String scAddr = (String)map.get("scAddr"); 290 String destAddr = (String)map.get("destAddr"); 291 292 SmsMessageBase.SubmitPduBase pdu = null; 293 // figure out from tracker if this was sendText/Data 294 if (map.containsKey("text")) { 295 Rlog.d(TAG, "sms failed was text"); 296 String text = (String)map.get("text"); 297 298 if (isCdmaFormat(newFormat)) { 299 Rlog.d(TAG, "old format (gsm) ==> new format (cdma)"); 300 pdu = com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu( 301 scAddr, destAddr, text, (tracker.mDeliveryIntent != null), null); 302 } else { 303 Rlog.d(TAG, "old format (cdma) ==> new format (gsm)"); 304 pdu = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu( 305 scAddr, destAddr, text, (tracker.mDeliveryIntent != null), null); 306 } 307 } else if (map.containsKey("data")) { 308 Rlog.d(TAG, "sms failed was data"); 309 byte[] data = (byte[])map.get("data"); 310 Integer destPort = (Integer)map.get("destPort"); 311 312 if (isCdmaFormat(newFormat)) { 313 Rlog.d(TAG, "old format (gsm) ==> new format (cdma)"); 314 pdu = com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu( 315 scAddr, destAddr, destPort.intValue(), data, 316 (tracker.mDeliveryIntent != null)); 317 } else { 318 Rlog.d(TAG, "old format (cdma) ==> new format (gsm)"); 319 pdu = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu( 320 scAddr, destAddr, destPort.intValue(), data, 321 (tracker.mDeliveryIntent != null)); 322 } 323 } 324 325 // replace old smsc and pdu with newly encoded ones 326 map.put("smsc", pdu.encodedScAddress); 327 map.put("pdu", pdu.encodedMessage); 328 329 SMSDispatcher dispatcher = (isCdmaFormat(newFormat)) ? 330 mCdmaDispatcher : mGsmDispatcher; 331 332 tracker.mFormat = dispatcher.getFormat(); 333 dispatcher.sendSms(tracker); 334 } 335 336 @Override 337 protected String getFormat() { 338 // this function should be defined in Gsm/CdmaDispatcher. 339 Rlog.e(TAG, "getFormat should never be called from here!"); 340 return "unknown"; 341 } 342 343 @Override 344 protected GsmAlphabet.TextEncodingDetails calculateLength( 345 CharSequence messageBody, boolean use7bitOnly) { 346 Rlog.e(TAG, "Error! Not implemented for IMS."); 347 return null; 348 } 349 350 @Override 351 protected void sendNewSubmitPdu(String destinationAddress, String scAddress, String message, 352 SmsHeader smsHeader, int format, PendingIntent sentIntent, 353 PendingIntent deliveryIntent, boolean lastPart) { 354 Rlog.e(TAG, "Error! Not implemented for IMS."); 355 } 356 357 @Override 358 public boolean isIms() { 359 return mIms; 360 } 361 362 @Override 363 public String getImsSmsFormat() { 364 return mImsSmsFormat; 365 } 366 367 /** 368 * Determines whether or not to use CDMA format for MO SMS. 369 * If SMS over IMS is supported, then format is based on IMS SMS format, 370 * otherwise format is based on current phone type. 371 * 372 * @return true if Cdma format should be used for MO SMS, false otherwise. 373 */ 374 private boolean isCdmaMo() { 375 if (!isIms()) { 376 // IMS is not registered, use Voice technology to determine SMS format. 377 return (PhoneConstants.PHONE_TYPE_CDMA == mPhone.getPhoneType()); 378 } 379 // IMS is registered with SMS support 380 return isCdmaFormat(mImsSmsFormat); 381 } 382 383 /** 384 * Determines whether or not format given is CDMA format. 385 * 386 * @param format 387 * @return true if format given is CDMA format, false otherwise. 388 */ 389 private boolean isCdmaFormat(String format) { 390 return (mCdmaDispatcher.getFormat().equals(format)); 391 } 392} 393