| /* |
| * Copyright (C) 2006 The Android Open Source Project |
| * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. |
| * |
| * 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; |
| |
| import static android.telephony.SmsManager.RESULT_ERROR_GENERIC_FAILURE; |
| |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| |
| import android.app.PendingIntent; |
| import android.app.PendingIntent.CanceledException; |
| import android.os.AsyncResult; |
| import android.os.Message; |
| import android.provider.Telephony.Sms.Intents; |
| import android.telephony.Rlog; |
| |
| import com.android.internal.telephony.cdma.CdmaSMSDispatcher; |
| import com.android.internal.telephony.gsm.GsmSMSDispatcher; |
| import com.android.internal.telephony.InboundSmsHandler; |
| import com.android.internal.telephony.gsm.GsmInboundSmsHandler; |
| import com.android.internal.telephony.cdma.CdmaInboundSmsHandler; |
| import com.android.internal.telephony.SmsBroadcastUndelivered; |
| |
| public final class ImsSMSDispatcher extends SMSDispatcher { |
| private static final String TAG = "RIL_ImsSms"; |
| |
| private SMSDispatcher mCdmaDispatcher; |
| private SMSDispatcher mGsmDispatcher; |
| |
| GsmInboundSmsHandler mGsmInboundSmsHandler; |
| CdmaInboundSmsHandler mCdmaInboundSmsHandler; |
| |
| /** true if IMS is registered and sms is supported, false otherwise.*/ |
| private boolean mIms = false; |
| private String mImsSmsFormat = SmsConstants.FORMAT_UNKNOWN; |
| |
| public ImsSMSDispatcher(PhoneBase phone, SmsStorageMonitor storageMonitor, |
| SmsUsageMonitor usageMonitor) { |
| super(phone, usageMonitor); |
| Rlog.d(TAG, "ImsSMSDispatcher created"); |
| |
| // Create dispatchers, inbound SMS handlers and broadcast |
| // undelivered messages in raw table. |
| mCdmaDispatcher = new CdmaSMSDispatcher(phone, |
| storageMonitor, usageMonitor, this); |
| mGsmInboundSmsHandler = GsmInboundSmsHandler.makeInboundSmsHandler(phone.getContext(), |
| storageMonitor, phone); |
| mCdmaInboundSmsHandler = CdmaInboundSmsHandler.makeInboundSmsHandler(phone.getContext(), |
| storageMonitor, phone, (CdmaSMSDispatcher) mCdmaDispatcher); |
| mGsmDispatcher = new GsmSMSDispatcher(phone, |
| storageMonitor, usageMonitor, this, mGsmInboundSmsHandler); |
| Thread broadcastThread = new Thread(new SmsBroadcastUndelivered(phone.getContext(), |
| mGsmInboundSmsHandler, mCdmaInboundSmsHandler)); |
| broadcastThread.start(); |
| |
| mCi.registerForOn(this, EVENT_RADIO_ON, null); |
| mCi.registerForImsNetworkStateChanged(this, EVENT_IMS_STATE_CHANGED, null); |
| } |
| |
| /* Updates the phone object when there is a change */ |
| @Override |
| protected void updatePhoneObject(PhoneBase phone) { |
| Rlog.d(TAG, "In IMS updatePhoneObject "); |
| super.updatePhoneObject(phone); |
| mCdmaDispatcher.updatePhoneObject(phone); |
| mGsmDispatcher.updatePhoneObject(phone); |
| mGsmInboundSmsHandler.updatePhoneObject(phone); |
| mCdmaInboundSmsHandler.updatePhoneObject(phone); |
| } |
| |
| public void dispose() { |
| mCi.unregisterForOn(this); |
| mCi.unregisterForImsNetworkStateChanged(this); |
| mGsmDispatcher.dispose(); |
| mCdmaDispatcher.dispose(); |
| mGsmInboundSmsHandler.dispose(); |
| mCdmaInboundSmsHandler.dispose(); |
| } |
| |
| /** |
| * Handles events coming from the phone stack. Overridden from handler. |
| * |
| * @param msg the message to handle |
| */ |
| @Override |
| public void handleMessage(Message msg) { |
| AsyncResult ar; |
| |
| switch (msg.what) { |
| case EVENT_RADIO_ON: |
| case EVENT_IMS_STATE_CHANGED: // received unsol |
| mCi.getImsRegistrationState(this.obtainMessage(EVENT_IMS_STATE_DONE)); |
| break; |
| |
| case EVENT_IMS_STATE_DONE: |
| ar = (AsyncResult) msg.obj; |
| |
| if (ar.exception == null) { |
| updateImsInfo(ar); |
| } else { |
| Rlog.e(TAG, "IMS State query failed with exp " |
| + ar.exception); |
| } |
| break; |
| |
| default: |
| super.handleMessage(msg); |
| } |
| } |
| |
| private void setImsSmsFormat(int format) { |
| // valid format? |
| switch (format) { |
| case PhoneConstants.PHONE_TYPE_GSM: |
| mImsSmsFormat = "3gpp"; |
| break; |
| case PhoneConstants.PHONE_TYPE_CDMA: |
| mImsSmsFormat = "3gpp2"; |
| break; |
| default: |
| mImsSmsFormat = "unknown"; |
| break; |
| } |
| } |
| |
| private void updateImsInfo(AsyncResult ar) { |
| int[] responseArray = (int[])ar.result; |
| |
| mIms = false; |
| if (responseArray[0] == 1) { // IMS is registered |
| Rlog.d(TAG, "IMS is registered!"); |
| mIms = true; |
| } else { |
| Rlog.d(TAG, "IMS is NOT registered!"); |
| } |
| |
| setImsSmsFormat(responseArray[1]); |
| |
| if (("unknown".equals(mImsSmsFormat))) { |
| Rlog.e(TAG, "IMS format was unknown!"); |
| // failed to retrieve valid IMS SMS format info, set IMS to unregistered |
| mIms = false; |
| } |
| } |
| |
| @Override |
| protected void sendData(String destAddr, String scAddr, int destPort, |
| byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) { |
| if (isCdmaMo()) { |
| mCdmaDispatcher.sendData(destAddr, scAddr, destPort, |
| data, sentIntent, deliveryIntent); |
| } else { |
| mGsmDispatcher.sendData(destAddr, scAddr, destPort, |
| data, sentIntent, deliveryIntent); |
| } |
| } |
| |
| @Override |
| protected void sendMultipartText(String destAddr, String scAddr, |
| ArrayList<String> parts, ArrayList<PendingIntent> sentIntents, |
| ArrayList<PendingIntent> deliveryIntents) { |
| if (isCdmaMo()) { |
| mCdmaDispatcher.sendMultipartText(destAddr, scAddr, |
| parts, sentIntents, deliveryIntents); |
| } else { |
| mGsmDispatcher.sendMultipartText(destAddr, scAddr, |
| parts, sentIntents, deliveryIntents); |
| } |
| } |
| |
| @Override |
| protected void sendSms(SmsTracker tracker) { |
| // sendSms is a helper function to other send functions, sendText/Data... |
| // it is not part of ISms.stub |
| Rlog.e(TAG, "sendSms should never be called from here!"); |
| } |
| |
| @Override |
| protected void sendText(String destAddr, String scAddr, String text, |
| PendingIntent sentIntent, PendingIntent deliveryIntent) { |
| Rlog.d(TAG, "sendText"); |
| if (isCdmaMo()) { |
| mCdmaDispatcher.sendText(destAddr, scAddr, |
| text, sentIntent, deliveryIntent); |
| } else { |
| mGsmDispatcher.sendText(destAddr, scAddr, |
| text, sentIntent, deliveryIntent); |
| } |
| } |
| |
| @Override |
| public void sendRetrySms(SmsTracker tracker) { |
| String oldFormat = tracker.mFormat; |
| |
| // newFormat will be based on voice technology |
| String newFormat = |
| (PhoneConstants.PHONE_TYPE_CDMA == mPhone.getPhoneType()) ? |
| mCdmaDispatcher.getFormat() : |
| mGsmDispatcher.getFormat(); |
| |
| // was previously sent sms format match with voice tech? |
| if (oldFormat.equals(newFormat)) { |
| if (isCdmaFormat(newFormat)) { |
| Rlog.d(TAG, "old format matched new format (cdma)"); |
| mCdmaDispatcher.sendSms(tracker); |
| return; |
| } else { |
| Rlog.d(TAG, "old format matched new format (gsm)"); |
| mGsmDispatcher.sendSms(tracker); |
| return; |
| } |
| } |
| |
| // format didn't match, need to re-encode. |
| HashMap map = tracker.mData; |
| |
| // to re-encode, fields needed are: scAddr, destAddr, and |
| // text if originally sent as sendText or |
| // data and destPort if originally sent as sendData. |
| if (!( map.containsKey("scAddr") && map.containsKey("destAddr") && |
| ( map.containsKey("text") || |
| (map.containsKey("data") && map.containsKey("destPort"))))) { |
| // should never come here... |
| Rlog.e(TAG, "sendRetrySms failed to re-encode per missing fields!"); |
| if (tracker.mSentIntent != null) { |
| int error = RESULT_ERROR_GENERIC_FAILURE; |
| // Done retrying; return an error to the app. |
| try { |
| tracker.mSentIntent.send(mContext, error, null); |
| } catch (CanceledException ex) {} |
| } |
| return; |
| } |
| String scAddr = (String)map.get("scAddr"); |
| String destAddr = (String)map.get("destAddr"); |
| |
| SmsMessageBase.SubmitPduBase pdu = null; |
| // figure out from tracker if this was sendText/Data |
| if (map.containsKey("text")) { |
| Rlog.d(TAG, "sms failed was text"); |
| String text = (String)map.get("text"); |
| |
| if (isCdmaFormat(newFormat)) { |
| Rlog.d(TAG, "old format (gsm) ==> new format (cdma)"); |
| pdu = com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu( |
| scAddr, destAddr, text, (tracker.mDeliveryIntent != null), null); |
| } else { |
| Rlog.d(TAG, "old format (cdma) ==> new format (gsm)"); |
| pdu = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu( |
| scAddr, destAddr, text, (tracker.mDeliveryIntent != null), null); |
| } |
| } else if (map.containsKey("data")) { |
| Rlog.d(TAG, "sms failed was data"); |
| byte[] data = (byte[])map.get("data"); |
| Integer destPort = (Integer)map.get("destPort"); |
| |
| if (isCdmaFormat(newFormat)) { |
| Rlog.d(TAG, "old format (gsm) ==> new format (cdma)"); |
| pdu = com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu( |
| scAddr, destAddr, destPort.intValue(), data, |
| (tracker.mDeliveryIntent != null)); |
| } else { |
| Rlog.d(TAG, "old format (cdma) ==> new format (gsm)"); |
| pdu = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu( |
| scAddr, destAddr, destPort.intValue(), data, |
| (tracker.mDeliveryIntent != null)); |
| } |
| } |
| |
| // replace old smsc and pdu with newly encoded ones |
| map.put("smsc", pdu.encodedScAddress); |
| map.put("pdu", pdu.encodedMessage); |
| |
| SMSDispatcher dispatcher = (isCdmaFormat(newFormat)) ? |
| mCdmaDispatcher : mGsmDispatcher; |
| |
| tracker.mFormat = dispatcher.getFormat(); |
| dispatcher.sendSms(tracker); |
| } |
| |
| @Override |
| protected String getFormat() { |
| // this function should be defined in Gsm/CdmaDispatcher. |
| Rlog.e(TAG, "getFormat should never be called from here!"); |
| return "unknown"; |
| } |
| |
| @Override |
| protected GsmAlphabet.TextEncodingDetails calculateLength( |
| CharSequence messageBody, boolean use7bitOnly) { |
| Rlog.e(TAG, "Error! Not implemented for IMS."); |
| return null; |
| } |
| |
| @Override |
| protected void sendNewSubmitPdu(String destinationAddress, String scAddress, String message, |
| SmsHeader smsHeader, int format, PendingIntent sentIntent, |
| PendingIntent deliveryIntent, boolean lastPart) { |
| Rlog.e(TAG, "Error! Not implemented for IMS."); |
| } |
| |
| @Override |
| public boolean isIms() { |
| return mIms; |
| } |
| |
| @Override |
| public String getImsSmsFormat() { |
| return mImsSmsFormat; |
| } |
| |
| /** |
| * Determines whether or not to use CDMA format for MO SMS. |
| * If SMS over IMS is supported, then format is based on IMS SMS format, |
| * otherwise format is based on current phone type. |
| * |
| * @return true if Cdma format should be used for MO SMS, false otherwise. |
| */ |
| private boolean isCdmaMo() { |
| if (!isIms()) { |
| // IMS is not registered, use Voice technology to determine SMS format. |
| return (PhoneConstants.PHONE_TYPE_CDMA == mPhone.getPhoneType()); |
| } |
| // IMS is registered with SMS support |
| return isCdmaFormat(mImsSmsFormat); |
| } |
| |
| /** |
| * Determines whether or not format given is CDMA format. |
| * |
| * @param format |
| * @return true if format given is CDMA format, false otherwise. |
| */ |
| private boolean isCdmaFormat(String format) { |
| return (mCdmaDispatcher.getFormat().equals(format)); |
| } |
| } |