/* * 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 com.android.internal.telephony.GsmAlphabet; import com.android.internal.telephony.GsmCdmaPhone; import com.android.internal.telephony.ImsSMSDispatcher; import com.android.internal.telephony.Phone; 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(Phone phone, SmsUsageMonitor usageMonitor, ImsSMSDispatcher imsSMSDispatcher) { super(phone, usageMonitor, imsSMSDispatcher); Rlog.d(TAG, "CdmaSMSDispatcher created"); } @Override public 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 */ public 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 */ private 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 public 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)); if (pdu != null) { HashMap map = getSmsTrackerMap(destAddr, scAddr, destPort, data, pdu); SmsTracker tracker = getSmsTracker(map, sentIntent, deliveryIntent, getFormat(), null /*messageUri*/, false /*isExpectMore*/, null /*fullMessageText*/, false /*isText*/, true /*persistMessage*/); 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); } } else { Rlog.e(TAG, "CdmaSMSDispatcher.sendData(): getSubmitPdu() returned null"); if (sentIntent != null) { try { sentIntent.send(SmsManager.RESULT_ERROR_GENERIC_FAILURE); } catch (CanceledException ex) { Rlog.e(TAG, "Intent has been canceled!"); } } } } /** {@inheritDoc} */ @Override public void sendText(String destAddr, String scAddr, String text, PendingIntent sentIntent, PendingIntent deliveryIntent, Uri messageUri, String callingPkg, boolean persistMessage, int priority, boolean isExpectMore, int validityPeriod) { SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu( scAddr, destAddr, text, (deliveryIntent != null), null, priority); if (pdu != null) { HashMap map = getSmsTrackerMap(destAddr, scAddr, text, pdu); SmsTracker tracker = getSmsTracker(map, sentIntent, deliveryIntent, getFormat(), messageUri, isExpectMore, text, true /*isText*/, validityPeriod, persistMessage); 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"); if (sentIntent != null) { try { sentIntent.send(SmsManager.RESULT_ERROR_GENERIC_FAILURE); } catch (CanceledException ex) { Rlog.e(TAG, "Intent has been canceled!"); } } } } /** {@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, int priority, boolean isExpectMore, int validityPeriod, 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 = SmsMessage.isAscii7bitSupportedForLongMessage() ? UserData.ENCODING_7BIT_ASCII : UserData.ENCODING_GSM_7BIT_ALPHABET; Rlog.d(TAG, "Message ecoding for proper 7 bit: " + uData.msgEncoding); } 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, priority); HashMap map = getSmsTrackerMap(destinationAddress, scAddress, message, submitPdu); return getSmsTracker(map, sentIntent, deliveryIntent, getFormat(), unsentPartCount, anyPartFailed, messageUri, smsHeader, (!lastPart || isExpectMore), fullMessageText, true /*isText*/, validityPeriod, true /*persistMessage*/); } @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 public void sendSms(SmsTracker tracker) { 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.getData().get("pdu"); // 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()) { mCi.sendCdmaSms(pdu, reply); } // If sending SMS over IMS is not enabled, send SMS over cdma. Simply // calling shouldSendSmsOverIms() to check for that here might yield a // different result if the conditions of UE being attached to eHRPD and // active 1x voice call have changed since we last called it in // ImsSMSDispatcher.isCdmaMo() else if (!mImsSMSDispatcher.isImsSmsEnabled()) { mCi.sendCdmaSms(pdu, reply); mImsSMSDispatcher.enableSendSmsOverIms(true); } 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++; } } }