blob: b18fa4a898b5009416d5d6da0cb6c23ba32de91c [file] [log] [blame]
/*
* Copyright (C) 2006 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.gsm;
import android.app.Activity;
import android.app.PendingIntent;
import android.app.PendingIntent.CanceledException;
import android.content.Intent;
import android.net.Uri;
import android.os.AsyncResult;
import android.os.Message;
import android.provider.Telephony.Sms;
import android.provider.Telephony.Sms.Intents;
import android.telephony.Rlog;
import android.telephony.ServiceState;
import com.android.internal.telephony.GsmAlphabet;
import com.android.internal.telephony.ImsSMSDispatcher;
import com.android.internal.telephony.InboundSmsHandler;
import com.android.internal.telephony.PhoneBase;
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.uicc.IccRecords;
import com.android.internal.telephony.uicc.IccUtils;
import com.android.internal.telephony.uicc.UiccCardApplication;
import com.android.internal.telephony.uicc.UiccController;
import java.util.HashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
public final class GsmSMSDispatcher extends SMSDispatcher {
private static final String TAG = "GsmSMSDispatcher";
private static final boolean VDBG = false;
protected UiccController mUiccController = null;
private AtomicReference<IccRecords> mIccRecords = new AtomicReference<IccRecords>();
private AtomicReference<UiccCardApplication> mUiccApplication =
new AtomicReference<UiccCardApplication>();
private GsmInboundSmsHandler mGsmInboundSmsHandler;
/** Status report received */
private static final int EVENT_NEW_SMS_STATUS_REPORT = 100;
public GsmSMSDispatcher(PhoneBase phone, SmsUsageMonitor usageMonitor,
ImsSMSDispatcher imsSMSDispatcher,
GsmInboundSmsHandler gsmInboundSmsHandler) {
super(phone, usageMonitor, imsSMSDispatcher);
mCi.setOnSmsStatus(this, EVENT_NEW_SMS_STATUS_REPORT, null);
mGsmInboundSmsHandler = gsmInboundSmsHandler;
mUiccController = UiccController.getInstance();
mUiccController.registerForIccChanged(this, EVENT_ICC_CHANGED, null);
Rlog.d(TAG, "GsmSMSDispatcher created");
}
@Override
public void dispose() {
super.dispose();
mCi.unSetOnSmsStatus(this);
mUiccController.unregisterForIccChanged(this);
}
@Override
protected String getFormat() {
return SmsConstants.FORMAT_3GPP;
}
/**
* Handles 3GPP format-specific events coming from the phone stack.
* Other events are handled by {@link SMSDispatcher#handleMessage}.
*
* @param msg the message to handle
*/
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case EVENT_NEW_SMS_STATUS_REPORT:
handleStatusReport((AsyncResult) msg.obj);
break;
case EVENT_NEW_ICC_SMS:
// pass to InboundSmsHandler to process
mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_NEW_SMS, msg.obj);
break;
case EVENT_ICC_CHANGED:
onUpdateIccAvailability();
break;
default:
super.handleMessage(msg);
}
}
/**
* Called when a status report is received. This should correspond to
* a previously successful SEND.
*
* @param ar AsyncResult passed into the message handler. ar.result should
* be a String representing the status report PDU, as ASCII hex.
*/
private void handleStatusReport(AsyncResult ar) {
String pduString = (String) ar.result;
SmsMessage sms = SmsMessage.newFromCDS(pduString);
if (sms != null) {
int tpStatus = sms.getStatus();
int messageRef = sms.mMessageRef;
for (int i = 0, count = deliveryPendingList.size(); i < count; i++) {
SmsTracker tracker = deliveryPendingList.get(i);
if (tracker.mMessageRef == messageRef) {
// Found it. Remove from list and broadcast.
if(tpStatus >= Sms.STATUS_FAILED || tpStatus < Sms.STATUS_PENDING ) {
deliveryPendingList.remove(i);
// Update the message status (COMPLETE or FAILED)
tracker.updateSentMessageStatus(mContext, tpStatus);
}
PendingIntent intent = tracker.mDeliveryIntent;
Intent fillIn = new Intent();
fillIn.putExtra("pdu", IccUtils.hexStringToBytes(pduString));
fillIn.putExtra("format", getFormat());
try {
intent.send(mContext, Activity.RESULT_OK, fillIn);
} catch (CanceledException ex) {}
// Only expect to see one tracker matching this messageref
break;
}
}
}
mCi.acknowledgeLastIncomingGsmSms(true, Intents.RESULT_SMS_HANDLED, null);
}
/** {@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));
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*/);
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.");
sendRawPdu(tracker);
}
} else {
Rlog.e(TAG, "GsmSMSDispatcher.sendData(): getSubmitPdu() returned null");
}
}
/** {@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));
if (pdu != null) {
HashMap map = getSmsTrackerMap(destAddr, scAddr, text, pdu);
SmsTracker tracker = getSmsTracker(map, sentIntent, deliveryIntent, getFormat(),
messageUri, false /*isExpectMore*/, text /*fullMessageText*/, 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.");
sendRawPdu(tracker);
}
} else {
Rlog.e(TAG, "GsmSMSDispatcher.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);
}
/** {@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) {
SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu(scAddress, destinationAddress,
message, deliveryIntent != null, SmsHeader.toByteArray(smsHeader),
encoding, smsHeader.languageTable, smsHeader.languageShiftTable);
if (pdu != null) {
HashMap map = getSmsTrackerMap(destinationAddress, scAddress,
message, pdu);
return getSmsTracker(map, sentIntent,
deliveryIntent, getFormat(), unsentPartCount, anyPartFailed, messageUri,
smsHeader, !lastPart, fullMessageText, true /*isText*/);
} else {
Rlog.e(TAG, "GsmSMSDispatcher.sendNewSubmitPdu(): getSubmitPdu() returned null");
return null;
}
}
@Override
protected void sendSubmitPdu(SmsTracker tracker) {
sendRawPdu(tracker);
}
/** {@inheritDoc} */
@Override
protected void sendSms(SmsTracker tracker) {
HashMap<String, Object> map = tracker.mData;
byte pdu[] = (byte[]) map.get("pdu");
if (tracker.mRetryCount > 0) {
Rlog.d(TAG, "sendSms: "
+ " mRetryCount=" + tracker.mRetryCount
+ " mMessageRef=" + tracker.mMessageRef
+ " SS=" + mPhone.getServiceState().getState());
// per TS 23.040 Section 9.2.3.6: If TP-MTI SMS-SUBMIT (0x01) type
// TP-RD (bit 2) is 1 for retry
// and TP-MR is set to previously failed sms TP-MR
if (((0x01 & pdu[0]) == 0x01)) {
pdu[0] |= 0x04; // TP-RD
pdu[1] = (byte) tracker.mMessageRef; // TP-MR
}
}
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;
}
HashMap<String, Object> map = tracker.mData;
byte smsc[] = (byte[]) map.get("smsc");
byte[] pdu = (byte[]) map.get("pdu");
Message reply = obtainMessage(EVENT_SEND_SMS_COMPLETE, tracker);
// sms over gsm 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()) {
if (tracker.mRetryCount > 0) {
// per TS 23.040 Section 9.2.3.6: If TP-MTI SMS-SUBMIT (0x01) type
// TP-RD (bit 2) is 1 for retry
// and TP-MR is set to previously failed sms TP-MR
if (((0x01 & pdu[0]) == 0x01)) {
pdu[0] |= 0x04; // TP-RD
pdu[1] = (byte) tracker.mMessageRef; // TP-MR
}
}
if (tracker.mRetryCount == 0 && tracker.mExpectMore) {
mCi.sendSMSExpectMore(IccUtils.bytesToHexString(smsc),
IccUtils.bytesToHexString(pdu), reply);
} else {
mCi.sendSMS(IccUtils.bytesToHexString(smsc),
IccUtils.bytesToHexString(pdu), reply);
}
} else {
mCi.sendImsGsmSms(IccUtils.bytesToHexString(smsc),
IccUtils.bytesToHexString(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++;
}
}
protected UiccCardApplication getUiccCardApplication() {
Rlog.d(TAG, "GsmSMSDispatcher: subId = " + mPhone.getSubId()
+ " slotId = " + mPhone.getPhoneId());
return mUiccController.getUiccCardApplication(mPhone.getPhoneId(),
UiccController.APP_FAM_3GPP);
}
private void onUpdateIccAvailability() {
if (mUiccController == null ) {
return;
}
UiccCardApplication newUiccApplication = getUiccCardApplication();
UiccCardApplication app = mUiccApplication.get();
if (app != newUiccApplication) {
if (app != null) {
Rlog.d(TAG, "Removing stale icc objects.");
if (mIccRecords.get() != null) {
mIccRecords.get().unregisterForNewSms(this);
}
mIccRecords.set(null);
mUiccApplication.set(null);
}
if (newUiccApplication != null) {
Rlog.d(TAG, "New Uicc application found");
mUiccApplication.set(newUiccApplication);
mIccRecords.set(newUiccApplication.getIccRecords());
if (mIccRecords.get() != null) {
mIccRecords.get().registerForNewSms(this, EVENT_NEW_ICC_SMS, null);
}
}
}
}
}