| /* |
| * Copyright (C) 2008 The Android Open Source Project |
| * |
| * 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.cdma; |
| |
| import android.app.Activity; |
| import android.app.PendingIntent; |
| import android.app.PendingIntent.CanceledException; |
| import android.content.Intent; |
| import android.net.Uri; |
| import android.os.Message; |
| import android.os.SystemProperties; |
| import android.provider.Telephony.Sms; |
| import android.telephony.Rlog; |
| import android.telephony.ServiceState; |
| import android.telephony.SmsManager; |
| import android.telephony.TelephonyManager; |
| |
| import com.android.internal.telephony.GsmAlphabet; |
| import com.android.internal.telephony.ImsSMSDispatcher; |
| import com.android.internal.telephony.PhoneBase; |
| import com.android.internal.telephony.PhoneConstants; |
| import com.android.internal.telephony.SMSDispatcher; |
| import com.android.internal.telephony.SmsConstants; |
| import com.android.internal.telephony.SmsHeader; |
| import com.android.internal.telephony.SmsUsageMonitor; |
| import com.android.internal.telephony.TelephonyProperties; |
| import com.android.internal.telephony.cdma.sms.UserData; |
| |
| import java.util.HashMap; |
| import java.util.concurrent.atomic.AtomicBoolean; |
| import java.util.concurrent.atomic.AtomicInteger; |
| |
| public class CdmaSMSDispatcher extends SMSDispatcher { |
| private static final String TAG = "CdmaSMSDispatcher"; |
| private static final boolean VDBG = false; |
| |
| public CdmaSMSDispatcher(PhoneBase phone, SmsUsageMonitor usageMonitor, |
| ImsSMSDispatcher imsSMSDispatcher) { |
| super(phone, usageMonitor, imsSMSDispatcher); |
| Rlog.d(TAG, "CdmaSMSDispatcher created"); |
| } |
| |
| @Override |
| protected String getFormat() { |
| return SmsConstants.FORMAT_3GPP2; |
| } |
| |
| /** |
| * Send the SMS status report to the dispatcher thread to process. |
| * @param sms the CDMA SMS message containing the status report |
| */ |
| void sendStatusReportMessage(SmsMessage sms) { |
| if (VDBG) Rlog.d(TAG, "sending EVENT_HANDLE_STATUS_REPORT message"); |
| sendMessage(obtainMessage(EVENT_HANDLE_STATUS_REPORT, sms)); |
| } |
| |
| @Override |
| protected void handleStatusReport(Object o) { |
| if (o instanceof SmsMessage) { |
| if (VDBG) Rlog.d(TAG, "calling handleCdmaStatusReport()"); |
| handleCdmaStatusReport((SmsMessage) o); |
| } else { |
| Rlog.e(TAG, "handleStatusReport() called for object type " + o.getClass().getName()); |
| } |
| } |
| |
| /** |
| * Called from parent class to handle status report from {@code CdmaInboundSmsHandler}. |
| * @param sms the CDMA SMS message to process |
| */ |
| void handleCdmaStatusReport(SmsMessage sms) { |
| for (int i = 0, count = deliveryPendingList.size(); i < count; i++) { |
| SmsTracker tracker = deliveryPendingList.get(i); |
| if (tracker.mMessageRef == sms.mMessageRef) { |
| // Found it. Remove from list and broadcast. |
| deliveryPendingList.remove(i); |
| // Update the message status (COMPLETE) |
| tracker.updateSentMessageStatus(mContext, Sms.STATUS_COMPLETE); |
| |
| PendingIntent intent = tracker.mDeliveryIntent; |
| Intent fillIn = new Intent(); |
| fillIn.putExtra("pdu", sms.getPdu()); |
| fillIn.putExtra("format", getFormat()); |
| try { |
| intent.send(mContext, Activity.RESULT_OK, fillIn); |
| } catch (CanceledException ex) {} |
| break; // Only expect to see one tracker matching this message. |
| } |
| } |
| } |
| |
| /** {@inheritDoc} */ |
| @Override |
| protected void sendData(String destAddr, String scAddr, int destPort, |
| byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) { |
| SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu( |
| scAddr, destAddr, destPort, data, (deliveryIntent != null)); |
| HashMap map = getSmsTrackerMap(destAddr, scAddr, destPort, data, pdu); |
| SmsTracker tracker = getSmsTracker(map, sentIntent, deliveryIntent, getFormat(), |
| null /*messageUri*/, false /*isExpectMore*/, null /*fullMessageText*/, |
| false /*isText*/); |
| |
| String carrierPackage = getCarrierAppPackageName(); |
| if (carrierPackage != null) { |
| Rlog.d(TAG, "Found carrier package."); |
| DataSmsSender smsSender = new DataSmsSender(tracker); |
| smsSender.sendSmsByCarrierApp(carrierPackage, new SmsSenderCallback(smsSender)); |
| } else { |
| Rlog.v(TAG, "No carrier package."); |
| sendSubmitPdu(tracker); |
| } |
| } |
| |
| /** {@inheritDoc} */ |
| @Override |
| protected void sendText(String destAddr, String scAddr, String text, PendingIntent sentIntent, |
| PendingIntent deliveryIntent, Uri messageUri, String callingPkg) { |
| SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu( |
| scAddr, destAddr, text, (deliveryIntent != null), null); |
| if (pdu != null) { |
| HashMap map = getSmsTrackerMap(destAddr, scAddr, text, pdu); |
| SmsTracker tracker = getSmsTracker(map, sentIntent, deliveryIntent, getFormat(), |
| messageUri, false /*isExpectMore*/, text, true /*isText*/); |
| |
| String carrierPackage = getCarrierAppPackageName(); |
| if (carrierPackage != null) { |
| Rlog.d(TAG, "Found carrier package."); |
| TextSmsSender smsSender = new TextSmsSender(tracker); |
| smsSender.sendSmsByCarrierApp(carrierPackage, new SmsSenderCallback(smsSender)); |
| } else { |
| Rlog.v(TAG, "No carrier package."); |
| sendSubmitPdu(tracker); |
| } |
| } else { |
| Rlog.e(TAG, "CdmaSMSDispatcher.sendText(): getSubmitPdu() returned null"); |
| } |
| } |
| |
| /** {@inheritDoc} */ |
| @Override |
| protected void injectSmsPdu(byte[] pdu, String format, PendingIntent receivedIntent) { |
| throw new IllegalStateException("This method must be called only on ImsSMSDispatcher"); |
| } |
| |
| /** {@inheritDoc} */ |
| @Override |
| protected GsmAlphabet.TextEncodingDetails calculateLength(CharSequence messageBody, |
| boolean use7bitOnly) { |
| return SmsMessage.calculateLength(messageBody, use7bitOnly, false); |
| } |
| |
| /** {@inheritDoc} */ |
| @Override |
| protected SmsTracker getNewSubmitPduTracker(String destinationAddress, String scAddress, |
| String message, SmsHeader smsHeader, int encoding, |
| PendingIntent sentIntent, PendingIntent deliveryIntent, boolean lastPart, |
| AtomicInteger unsentPartCount, AtomicBoolean anyPartFailed, Uri messageUri, |
| String fullMessageText) { |
| UserData uData = new UserData(); |
| uData.payloadStr = message; |
| uData.userDataHeader = smsHeader; |
| if (encoding == SmsConstants.ENCODING_7BIT) { |
| uData.msgEncoding = UserData.ENCODING_GSM_7BIT_ALPHABET; |
| } else { // assume UTF-16 |
| uData.msgEncoding = UserData.ENCODING_UNICODE_16; |
| } |
| uData.msgEncodingSet = true; |
| |
| /* By setting the statusReportRequested bit only for the |
| * last message fragment, this will result in only one |
| * callback to the sender when that last fragment delivery |
| * has been acknowledged. */ |
| SmsMessage.SubmitPdu submitPdu = SmsMessage.getSubmitPdu(destinationAddress, |
| uData, (deliveryIntent != null) && lastPart); |
| |
| HashMap map = getSmsTrackerMap(destinationAddress, scAddress, |
| message, submitPdu); |
| return getSmsTracker(map, sentIntent, deliveryIntent, |
| getFormat(), unsentPartCount, anyPartFailed, messageUri, smsHeader, |
| false /*isExpextMore*/, fullMessageText, true /*isText*/); |
| } |
| |
| @Override |
| protected void sendSubmitPdu(SmsTracker tracker) { |
| if (SystemProperties.getBoolean(TelephonyProperties.PROPERTY_INECM_MODE, false)) { |
| if (VDBG) { |
| Rlog.d(TAG, "Block SMS in Emergency Callback mode"); |
| } |
| tracker.onFailed(mContext, SmsManager.RESULT_ERROR_NO_SERVICE, 0/*errorCode*/); |
| return; |
| } |
| sendRawPdu(tracker); |
| } |
| |
| /** {@inheritDoc} */ |
| @Override |
| protected void sendSms(SmsTracker tracker) { |
| HashMap<String, Object> map = tracker.mData; |
| |
| // byte[] smsc = (byte[]) map.get("smsc"); // unused for CDMA |
| byte[] pdu = (byte[]) map.get("pdu"); |
| |
| Rlog.d(TAG, "sendSms: " |
| + " isIms()=" + isIms() |
| + " mRetryCount=" + tracker.mRetryCount |
| + " mImsRetry=" + tracker.mImsRetry |
| + " mMessageRef=" + tracker.mMessageRef |
| + " SS=" + mPhone.getServiceState().getState()); |
| |
| sendSmsByPstn(tracker); |
| } |
| |
| /** {@inheritDoc} */ |
| @Override |
| protected void sendSmsByPstn(SmsTracker tracker) { |
| int ss = mPhone.getServiceState().getState(); |
| // if sms over IMS is not supported on data and voice is not available... |
| if (!isIms() && ss != ServiceState.STATE_IN_SERVICE) { |
| tracker.onFailed(mContext, getNotInServiceError(ss), 0/*errorCode*/); |
| return; |
| } |
| |
| Message reply = obtainMessage(EVENT_SEND_SMS_COMPLETE, tracker); |
| byte[] pdu = (byte[]) tracker.mData.get("pdu"); |
| |
| int currentDataNetwork = mPhone.getServiceState().getDataNetworkType(); |
| boolean imsSmsDisabled = (currentDataNetwork == TelephonyManager.NETWORK_TYPE_EHRPD |
| || (currentDataNetwork == TelephonyManager.NETWORK_TYPE_LTE |
| && !mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed())) |
| && mPhone.getServiceState().getVoiceNetworkType() |
| == TelephonyManager.NETWORK_TYPE_1xRTT |
| && ((CDMAPhone) mPhone).mCT.mState != PhoneConstants.State.IDLE; |
| |
| // sms over cdma is used: |
| // if sms over IMS is not supported AND |
| // this is not a retry case after sms over IMS failed |
| // indicated by mImsRetry > 0 |
| if (0 == tracker.mImsRetry && !isIms() || imsSmsDisabled) { |
| mCi.sendCdmaSms(pdu, reply); |
| } else { |
| mCi.sendImsCdmaSms(pdu, tracker.mImsRetry, tracker.mMessageRef, reply); |
| // increment it here, so in case of SMS_FAIL_RETRY over IMS |
| // next retry will be sent using IMS request again. |
| tracker.mImsRetry++; |
| } |
| } |
| } |