转载请注明出处:http://blog.csdn.net/droyon/article/details/11699935

2,短彩信发送framework逻辑

相关文章:

-------------------------------------------------------------

1、短信发送上层逻辑

3、短信发送framework层逻辑

-------------------------------------------------------------

 

短信在SmsSingleRecipientSender.java中包装了SentIntents,以及DeliveryIntents,信息的内容在message中,信息的目的发送地址在mDest中,然后调用下面的代码进行信息的发送

smsManager.sendMultipartTextMessage(mDest, mServiceCenter, messages, sentIntents, deliveryIntents);
smsMessager对应的类为:SmsManager.java

2.1  进入SmsManager.java

public void sendMultipartTextMessage(            String destinationAddress, String scAddress, ArrayList parts,            ArrayList sentIntents, ArrayList deliveryIntents) {        if (TextUtils.isEmpty(destinationAddress)) {            throw new IllegalArgumentException("Invalid destinationAddress");        }        if (parts == null || parts.size() < 1) {            throw new IllegalArgumentException("Invalid message body");        }        if (parts.size() > 1) {            try {                ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));                if (iccISms != null) {                    iccISms.sendMultipartText(destinationAddress, scAddress, parts,                            sentIntents, deliveryIntents);                }            } catch (RemoteException ex) {                // ignore it            }        } else {            PendingIntent sentIntent = null;            PendingIntent deliveryIntent = null;            if (sentIntents != null && sentIntents.size() > 0) {                sentIntent = sentIntents.get(0);            }            if (deliveryIntents != null && deliveryIntents.size() > 0) {                deliveryIntent = deliveryIntents.get(0);            }            sendTextMessage(destinationAddress, scAddress, parts.get(0),                    sentIntent, deliveryIntent);        }    }

在这个类中,主要是根据parts的数量,进行跨进程调用ISms服务。如果是多条信息,执行:

iccISms.sendMultipartText(destinationAddress, scAddress, parts,                            sentIntents, deliveryIntents);

这里调用的ISms服务的sendMultipartText方法。

如果是单条信息,执行sendTextMessage

public void sendTextMessage(            String destinationAddress, String scAddress, String text,            PendingIntent sentIntent, PendingIntent deliveryIntent) {        if (TextUtils.isEmpty(destinationAddress)) {            throw new IllegalArgumentException("Invalid destinationAddress");        }        if (TextUtils.isEmpty(text)) {            throw new IllegalArgumentException("Invalid message body");        }        try {            ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));            if (iccISms != null) {                iccISms.sendText(destinationAddress, scAddress, text, sentIntent, deliveryIntent);            }        } catch (RemoteException ex) {            // ignore it        }    }
同样的,要调用ISms服务。只不过方法变成了Isms服务的sendText方法

关于iSms服务的注册,它是在初始化phone进程时注册的

public IccSmsInterfaceManagerProxy(IccSmsInterfaceManager            iccSmsInterfaceManager) {        this.mIccSmsInterfaceManager = iccSmsInterfaceManager;        if(ServiceManager.getService("isms") == null) {            ServiceManager.addService("isms", this);        }    }

然后我们进到IccSmsInterfaceManagerProxy.java中

2.2  IccSmsManagerProxy.java

    public void sendText(String destAddr, String scAddr,            String text, PendingIntent sentIntent, PendingIntent deliveryIntent) {        mIccSmsInterfaceManager.sendText(destAddr, scAddr, text, sentIntent, deliveryIntent);    }    public void sendMultipartText(String destAddr, String scAddr,            List parts, List sentIntents,            List deliveryIntents) throws android.os.RemoteException {        mIccSmsInterfaceManager.sendMultipartText(destAddr, scAddr,                parts, sentIntents, deliveryIntents);    }

代理类中进行实现

2.3 iccSmsManager.java

    public void sendMultipartText(String destAddr, String scAddr, List parts,            List sentIntents, List deliveryIntents) {        mPhone.getContext().enforceCallingPermission(                "android.permission.SEND_SMS",                "Sending SMS message");        if (Log.isLoggable("SMS", Log.VERBOSE)) {            int i = 0;            for (String part : parts) {                log("sendMultipartText: destAddr=" + destAddr + ", srAddr=" + scAddr +                        ", part[" + (i++) + "]=" + part);            }        }        mDispatcher.sendMultipartText(destAddr, scAddr, (ArrayList) parts,                (ArrayList) sentIntents, (ArrayList) deliveryIntents);    }

    public void sendText(String destAddr, String scAddr,            String text, PendingIntent sentIntent, PendingIntent deliveryIntent) {        mPhone.getContext().enforceCallingOrSelfPermission(                "android.permission.SEND_SMS",                "Sending SMS message");        if (Log.isLoggable("SMS", Log.VERBOSE)) {            log("sendText: destAddr=" + destAddr + " scAddr=" + scAddr +                " text='"+ text + "' sentIntent=" +                sentIntent + " deliveryIntent=" + deliveryIntent);        }        mDispatcher.sendText(destAddr, scAddr, text, sentIntent, deliveryIntent);    }

最终他们都要调到mDispatcher的sendText或者sendMultipartText方法,mDispatcher的原型类:SMSDispatcher,它有两个子类,GsmSmsDispatcher.java以及CdmaSmsDispatcher.java

2.4,GsmSmsDispatcher.java

protected void sendMultipartText(String destAddr, String scAddr,            ArrayList parts, ArrayList sentIntents,            ArrayList deliveryIntents) {        int refNumber = getNextConcatenatedRef() & 0x00FF;        int msgCount = parts.size();        int encoding = android.telephony.SmsMessage.ENCODING_UNKNOWN;        mRemainingMessages = msgCount;        TextEncodingDetails[] encodingForParts = new TextEncodingDetails[msgCount];        for (int i = 0; i < msgCount; i++) {            TextEncodingDetails details = calculateLength(parts.get(i), false);            if (encoding != details.codeUnitSize                    && (encoding == android.telephony.SmsMessage.ENCODING_UNKNOWN                            || encoding == android.telephony.SmsMessage.ENCODING_7BIT)) {                encoding = details.codeUnitSize;            }            encodingForParts[i] = details;        }        for (int i = 0; i < msgCount; i++) {            SmsHeader.ConcatRef concatRef = new SmsHeader.ConcatRef();            concatRef.refNumber = refNumber;            concatRef.seqNumber = i + 1;  // 1-based sequence            concatRef.msgCount = msgCount;            // TODO: We currently set this to true since our messaging app will never            // send more than 255 parts (it converts the message to MMS well before that).            // However, we should support 3rd party messaging apps that might need 16-bit            // references            // Note:  It's not sufficient to just flip this bit to true; it will have            // ripple effects (several calculations assume 8-bit ref).            concatRef.isEightBits = true;            SmsHeader smsHeader = new SmsHeader();            smsHeader.concatRef = concatRef;            // Set the national language tables for 3GPP 7-bit encoding, if enabled.            if (encoding == android.telephony.SmsMessage.ENCODING_7BIT) {                smsHeader.languageTable = encodingForParts[i].languageTable;                smsHeader.languageShiftTable = encodingForParts[i].languageShiftTable;            }            PendingIntent sentIntent = null;            if (sentIntents != null && sentIntents.size() > i) {                sentIntent = sentIntents.get(i);            }            PendingIntent deliveryIntent = null;            if (deliveryIntents != null && deliveryIntents.size() > i) {                deliveryIntent = deliveryIntents.get(i);            }            sendNewSubmitPdu(destAddr, scAddr, parts.get(i), smsHeader, encoding,                    sentIntent, deliveryIntent, (i == (msgCount - 1)));        }    }
遍历所有的短信part,然后执行sendNewSubmitPdu方法,这个方法在SmsDispatcher.java中是abstract的,真正的实现在其子类中:

protected void sendNewSubmitPdu(String destinationAddress, String scAddress,            String message, SmsHeader smsHeader, int encoding,            PendingIntent sentIntent, PendingIntent deliveryIntent, boolean lastPart) {        SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu(scAddress, destinationAddress,                message, deliveryIntent != null, SmsHeader.toByteArray(smsHeader),                encoding, smsHeader.languageTable, smsHeader.languageShiftTable);        if (pdu != null) {            sendRawPdu(pdu.encodedScAddress, pdu.encodedMessage, sentIntent, deliveryIntent);        } else {            Log.e(TAG, "GsmSMSDispatcher.sendNewSubmitPdu(): getSubmitPdu() returned null");        }    }

实现方式为执行sendRawPdu。

以上是sendMultipartsText,让我们看看sendText方法在SmsDispatcher.java中的实现:

protected abstract void sendText(String destAddr, String scAddr,            String text, PendingIntent sentIntent, PendingIntent deliveryIntent);

SmsDispatcher.java的设计方法为让其子类来决定sendText方法的实现方式,现贴出sendText的源码实现:

protected void sendText(String destAddr, String scAddr, String text,            PendingIntent sentIntent, PendingIntent deliveryIntent) {        SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu(                scAddr, destAddr, text, (deliveryIntent != null));        if (pdu != null) {            sendRawPdu(pdu.encodedScAddress, pdu.encodedMessage, sentIntent, deliveryIntent);        } else {            Log.e(TAG, "GsmSMSDispatcher.sendText(): getSubmitPdu() returned null");        }    }

我们看到其实现方式为执行sendRawPdu,到这里也就是说,无论是sendText还是sendMultipartsText方法,他们归根结底都是执行sendRawPdu方法,不同的是sendMultipartsText是遍历所有的短信parts,然后在调用sendRawPdu。

在这个方法的前一步会将信息的内容,要发送的目的地址,当前的系统时间等打包成SubmitPdu,关于短信pdu这部分,会稍后介绍,我们现在主要解析一下短信framework发送的主要逻辑。

下面我们看看sendRawPdu的方法实现:

protected void sendRawPdu(byte[] smsc, byte[] pdu, PendingIntent sentIntent,            PendingIntent deliveryIntent) {        if (mSmsSendDisabled) {            if (sentIntent != null) {                try {                    sentIntent.send(RESULT_ERROR_NO_SERVICE);                } catch (CanceledException ex) {}            }            Log.d(TAG, "Device does not support sending sms.");            return;        }        if (pdu == null) {            if (sentIntent != null) {                try {                    sentIntent.send(RESULT_ERROR_NULL_PDU);                } catch (CanceledException ex) {}            }            return;        }        HashMap map = new HashMap();        map.put("smsc", smsc);        map.put("pdu", pdu);        SmsTracker tracker = new SmsTracker(map, sentIntent,                deliveryIntent);        int ss = mPhone.getServiceState().getState();        if (ss != ServiceState.STATE_IN_SERVICE) {            handleNotInService(ss, tracker);        } else {            String appName = getAppNameByIntent(sentIntent);            if (mUsageMonitor.check(appName, SINGLE_PART_SMS)) {                sendSms(tracker);            } else {                sendMessage(obtainMessage(EVENT_POST_ALERT, tracker));            }        }    }
在这个方法里,首先会检查一些状态,例如pdu是否为null,smsSendDisabled等,然后包装一个SmsTracher对象,在发送前还要检查一下Phone进程的状态,是否处于“离线”状态,关于这个状态,moderm会根据当前所处的信号强度,做出改变。

如果当前处于服务中,那么就可以进行我们短信的发送了,发送调用sendMessage或者sendSms进行短信的发送,关于sendMessage这个方法,参数为Message,message生成代码:

public final Message obtainMessage(int what, Object obj)    {        return Message.obtain(this, what, obj);    }

也就是说这个message是和Handler相关的message。

PS:我不知道大家有没有注意,其实SmsDispatcher.java这个类就是个Handler。

在发送之前,会调用:

mUsageMonitor.check(appName, SINGLE_PART_SMS)
进行检查,所作的事情其实就是检查在一段时间内,等待发送的短信数目不超过MAX值,关于等待时间以及MAX数目:

SmsUsageMonitor.java

/** Default checking period for SMS sent without user permission. */    private static final int DEFAULT_SMS_CHECK_PERIOD = 3600000;    /** Default number of SMS sent in checking period without user permission. */    private static final int DEFAULT_SMS_MAX_COUNT = 100;

回到短信发送中,在此处,检查通过,故短信发送执行sendSms(tracker)

protected void sendSms(SmsTracker tracker) {        HashMap map = tracker.mData;        byte smsc[] = (byte[]) map.get("smsc");        byte pdu[] = (byte[]) map.get("pdu");        Message reply = obtainMessage(EVENT_SEND_SMS_COMPLETE, tracker);        mCm.sendSMS(IccUtils.bytesToHexString(smsc), IccUtils.bytesToHexString(pdu), reply);    }

其中mCm对象是CommandInterface的引用

protected final CommandsInterface mCm;

很多人对它不是很熟悉,但一定很熟悉它的其中一个子类,那就是RIL.java。

也就是说,短信发送流程,在此处会调用RIL.java,执行sendSMS方法,参数为smsc的pdu字符串,短信内容以及时间等的pdu,以及一个Message,WHAT值为EVENT_SEND_SMS_COMPLETE,tracher对象作为其Object。我们进入到RIL.java中

2.5,RIL.java

public void    sendSMS (String smscPDU, String pdu, Message result) {        RILRequest rr                = RILRequest.obtain(RIL_REQUEST_SEND_SMS, result);        rr.mp.writeInt(2);        rr.mp.writeString(smscPDU);        rr.mp.writeString(pdu);        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));        send(rr);    }

关于RIL.java,它会和reference_ril.c进行配合,进行at指令的发送以及接收反馈等信息。reference_ril.c文件会被编译到reference-ril.so中。

在此处,RIL.java会将相应的pdu等信息打包,然后通过socket发送到reference_ril.c中,相关处理如下:

2.6,reference_ril.c

static void requestSendSMS(void *data, size_t datalen, RIL_Token t){    int err;    const char *smsc;    const char *pdu;    int tpLayerLength;    char *cmd1, *cmd2;    RIL_SMS_Response response;    ATResponse *p_response = NULL;    smsc = ((const char **)data)[0];    pdu = ((const char **)data)[1];    tpLayerLength = strlen(pdu)/2;    // "NULL for default SMSC"    if (smsc == NULL) {        smsc= "00";    }    asprintf(&cmd1, "AT+CMGS=%d", tpLayerLength);    asprintf(&cmd2, "%s%s", smsc, pdu);    err = at_send_command_sms(cmd1, cmd2, "+CMGS:", &p_response);    if (err != 0 || p_response->success == 0) goto error;    memset(&response, 0, sizeof(response));    /* FIXME fill in messageRef and ackPDU */    RIL_onRequestComplete(t, RIL_E_SUCCESS, &response, sizeof(response));    at_response_free(p_response);    return;error:    RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);    at_response_free(p_response);}

调用AT命令,cmgs将信息发往moderm,进行相关流程处理。


最后,如果短信成功发送出去,还记得我们在发送时,从GsmSmsDispatcher.java传递到RIL.java中的Message吗,在发送之后,RIL.java会回调此Message,这个Message如下:

Message reply = obtainMessage(EVENT_SEND_SMS_COMPLETE, tracker);

前面说了,What = EVENT_SEND_SMS_COMPLETE,Object = tricker。我们之前也说了SmsDispacher.java以及其子类是Handler,那么这个Message在那里处理的那?答案是在SmsDispacher.java中,处理逻辑代码如下:

case EVENT_SEND_SMS_COMPLETE:            // An outbound SMS has been successfully transferred, or failed.            handleSendComplete((AsyncResult) msg.obj);            break;

protected void handleSendComplete(AsyncResult ar) {        SmsTracker tracker = (SmsTracker) ar.userObj;        PendingIntent sentIntent = tracker.mSentIntent;        if (ar.exception == null) {            if (false) {                Log.d(TAG, "SMS send complete. Broadcasting "                        + "intent: " + sentIntent);            }            if (tracker.mDeliveryIntent != null) {//wanghailu,hailushijie@163.com ,hlwang                // Expecting a status report.  Add it to the list.                int messageRef = ((SmsResponse)ar.result).messageRef;                tracker.mMessageRef = messageRef;                deliveryPendingList.add(tracker);            }            if (sentIntent != null) {                try {                    if (mRemainingMessages > -1) {                        mRemainingMessages--;                    }                    if (mRemainingMessages == 0) {                        Intent sendNext = new Intent();                        sendNext.putExtra(SEND_NEXT_MSG_EXTRA, true);                        sentIntent.send(mContext, Activity.RESULT_OK, sendNext);                    } else {                        sentIntent.send(Activity.RESULT_OK);                    }                } catch (CanceledException ex) {}            }        } else {            if (false) {                Log.d(TAG, "SMS send failed");            }            int ss = mPhone.getServiceState().getState();            if (ss != ServiceState.STATE_IN_SERVICE) {                handleNotInService(ss, tracker);            } else if ((((CommandException)(ar.exception)).getCommandError()                    == CommandException.Error.SMS_FAIL_RETRY) &&                   tracker.mRetryCount < MAX_SEND_RETRIES) {                // Retry after a delay if needed.                // TODO: According to TS 23.040, 9.2.3.6, we should resend                //       with the same TP-MR as the failed message, and                //       TP-RD set to 1.  However, we don't have a means of                //       knowing the MR for the failed message (EF_SMSstatus                //       may or may not have the MR corresponding to this                //       message, depending on the failure).  Also, in some                //       implementations this retry is handled by the baseband.                tracker.mRetryCount++;                Message retryMsg = obtainMessage(EVENT_SEND_RETRY, tracker);                sendMessageDelayed(retryMsg, SEND_RETRY_DELAY);            } else if (tracker.mSentIntent != null) {                int error = RESULT_ERROR_GENERIC_FAILURE;                if (((CommandException)(ar.exception)).getCommandError()                        == CommandException.Error.FDN_CHECK_FAILURE) {                    error = RESULT_ERROR_FDN_CHECK_FAILURE;                }                // Done retrying; return an error to the app.                try {                    Intent fillIn = new Intent();                    if (ar.result != null) {                        fillIn.putExtra("errorCode", ((SmsResponse)ar.result).errorCode);                    }                    if (mRemainingMessages > -1) {                        mRemainingMessages--;                    }                    if (mRemainingMessages == 0) {                        fillIn.putExtra(SEND_NEXT_MSG_EXTRA, true);                    }                    tracker.mSentIntent.send(mContext, error, fillIn);                } catch (CanceledException ex) {}            }        }    }



至此,短信发送流程大致如此,短信接收流程中framework部分的处理大致是发送流程的反方向。

首先检查AsyncResult对象中是否存在异常,如果成功发送的信息,那么不存在异常,如果发送失败,那么是存在Exception的,会进行异常的相应的逻辑处理,大体流程相似,故本处介绍无异常时的流程逻辑。

无论发送成功还是失败,大体都是执行tricker对象中的mSentIntents,这是一个PendingIntent,执行send会发送此广播,那么我们的上层应用Mms中的SmsReceiverService.java会收到这个广播,并进行相应的逻辑处理,逻辑代码大体如下:

private void handleSmsSent(Intent intent, int error) {        Uri uri = intent.getData();        mSending = false;        boolean sendNextMsg = intent.getBooleanExtra(EXTRA_MESSAGE_SENT_SEND_NEXT, false);        if (LogTag.DEBUG_SEND) {            Log.v(TAG, "handleSmsSent uri: " + uri + " sendNextMsg: " + sendNextMsg +                    " mResultCode: " + mResultCode +                    " = " + translateResultCode(mResultCode) + " error: " + error);        }        if (mResultCode == Activity.RESULT_OK) {            if (LogTag.DEBUG_SEND || Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)) {                Log.v(TAG, "handleSmsSent move message to sent folder uri: " + uri);            }            if (!Sms.moveMessageToFolder(this, uri, Sms.MESSAGE_TYPE_SENT, error)) {                Log.e(TAG, "handleSmsSent: failed to move message " + uri + " to sent folder");            }            if (sendNextMsg) {                sendFirstQueuedMessage();            }            // Update the notification for failed messages since they may be deleted.            MessagingNotification.updateSendFailedNotification(this);        } else if ((mResultCode == SmsManager.RESULT_ERROR_RADIO_OFF) ||                (mResultCode == SmsManager.RESULT_ERROR_NO_SERVICE)) {            if (Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)) {                Log.v(TAG, "handleSmsSent: no service, queuing message w/ uri: " + uri);            }            // We got an error with no service or no radio. Register for state changes so            // when the status of the connection/radio changes, we can try to send the            // queued up messages.            registerForServiceStateChanges();            // We couldn't send the message, put in the queue to retry later.            Sms.moveMessageToFolder(this, uri, Sms.MESSAGE_TYPE_QUEUED, error);            mToastHandler.post(new Runnable() {                public void run() {                    Toast.makeText(SmsReceiverService.this, getString(R.string.message_queued),                            Toast.LENGTH_SHORT).show();                }            });        } else if (mResultCode == SmsManager.RESULT_ERROR_FDN_CHECK_FAILURE) {            mToastHandler.post(new Runnable() {                public void run() {                    Toast.makeText(SmsReceiverService.this, getString(R.string.fdn_check_failure),                            Toast.LENGTH_SHORT).show();                }            });        } else {            messageFailedToSend(uri, error);            if (sendNextMsg) {                sendFirstQueuedMessage();            }        }    }

主要是根据ResultCode进行一些逻辑处理,比如如果发送成功,那么会首先更新短信由待发送变为已发送状态,并且更新Notification等。

如果需要发送报告,那么相关的逻辑大体相似,不再详细介绍。


Ps:发送成功,回调会从moderm那里带回来一个MessageRef,这是一个int值,很重要噢。


短信发送framework层逻辑大体介绍到这里,framework层的短彩信逻辑还有好多,比如接收信息的流程,或者接收长短信的流程等,不再一一介绍了。







更多相关文章

  1. 浅谈Java中Collections.sort对List排序的两种方法
  2. Python list sort方法的具体使用
  3. python list.sort()根据多个关键字排序的方法实现
  4. Android(安卓)获取屏幕高宽度,密度,通知栏高度,截图等常用方法
  5. onRetainNonConfigurationInstance和getLastNonConfigurationIns
  6. 键盘自动弹出解决
  7. Android获取本机Mac地址及IP地址方法
  8. 自定义自己的AlertDialog
  9. android appwidget service的初始化

随机推荐

  1. getter on xmlbeans生成的类返回null,它不
  2. 如何从webview获取javascript值到android
  3. 当只使用get()和set()方法时,用原始类型替换At
  4. 蓝桥杯 ALGO-53 算法训练 最小乘积(基本
  5. 关于LSA的相关学习---短文本聚类涉及到的
  6. 理顺 JavaScript (12) - 一个比较实用的
  7. 请问用Java如何逐行的读取一个文本文件呀
  8. web基础之自动处理表单填装javabean
  9. 给JavaScript24条最佳实践
  10. Java之美[从菜鸟到高手演变]之Java学习方