Android6.0的SMS(短信)源码分析--短信发送
2016-03-23 16:10
681 查看
1 SMS发送流程
1.1 SmsManager
Android发送短信的接口可以认为是SmsManager,当然并不是所有的App都可以发送短信的,必须配置相关的权限。App中可以通过SmsManager.getDefault()得到SmsManager的单例。首先来SmsManager主要提供的接口有哪些。public static SmsManager | getDefault()获取 SmsManager 的默认实例 |
public void | sendTextMessage( String destinationAddress, String scAddress, String text, PendingIntent sentIntent, PendingIntent deliveryIntent) { sendTextMessageInternal(destinationAddress, scAddress, text, sentIntent, deliveryIntent, true /* persistMessageForCarrierApp*/); }发送一个基于 SMS 的文本 |
public void | sendDataMessage( String destinationAddress, String scAddress, short destinationPort, byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) 发送一个基于 SMS 的数据到指定的应用程序端口 |
public ArrayList<String> | divideMessage(String text) 当短信超过 SMS 消息的最大长度时,将短信分割为几块。 |
public void | sendMultipartTextMessage( String destinationAddress, String scAddress, ArrayList<String> parts, ArrayList<PendingIntent> sentIntents, ArrayList<PendingIntent> deliveryIntents) 发送一个基于SMS的多部分文本,调用者应用已经通过调用divideMessage (String text)将消息分割成正确的大小 |
普通短信的发送接口为SmsManager. sendTextMessage()。其函数原型和参数解释如下:
public void sendTextMessage(String destinationAddress, String scAddress, String text,PendingIntent sentIntent, PendingIntent deliveryIntent) destinationAddress: 收件人地址 scAddress: 短信中心号码,null为默认中心号码 sentIntent: 当消息发出时,成功或者失败的信息报告通过PendingIntent来广播。如果该参数为空,则发信程序会被所有位置程序检查一遍,这样会导致发送时间延长。 deliveryIntent: 当消息发送到收件人时,该PendingIntent会被广播。pdu数据在状态报告的extended data ("pdu")中。 |
public void sendTextMessage( String destinationAddress, String scAddress, String text, PendingIntent sentIntent, PendingIntent deliveryIntent) { sendTextMessageInternal(destinationAddress, scAddress, text, sentIntent, deliveryIntent, true /* persistMessageForCarrierApp*/); } |
private void sendTextMessageInternal(String destinationAddress, String scAddress, String text, PendingIntent sentIntent, PendingIntent deliveryIntent, boolean persistMessageForCarrierApp) { if (TextUtils.isEmpty(destinationAddress)) {//对目的地址进行非空判断 throw new IllegalArgumentException("Invalid destinationAddress"); } if (TextUtils.isEmpty(text)) {//对发送的内容进行非空判断 throw new IllegalArgumentException("Invalid message body"); } try { ISms iccISms = getISmsServiceOrThrow();//获取isim服务 iccISms.sendTextForSubscriber(getSubscriptionId(), ActivityThread.currentPackageName(), destinationAddress, scAddress, text, sentIntent, deliveryIntent, persistMessageForCarrierApp); } catch (RemoteException ex) { // ignore it } } |
public void sendTextForSubscriber(int subId, String callingPackage, String destAddr, String scAddr, String text, PendingIntent sentIntent, PendingIntent deliveryIntent, boolean persistMessageForNonDefaultSmsApp) { IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId); if (iccSmsIntMgr != null) { iccSmsIntMgr.sendText(callingPackage, destAddr, scAddr, text, sentIntent, deliveryIntent, persistMessageForNonDefaultSmsApp); } else { Rlog.e(LOG_TAG,"sendTextForSubscriber iccSmsIntMgr is null for" + " Subscription: " + subId); sendErrorInPendingIntent(sentIntent, SmsManager.RESULT_ERROR_GENERIC_FAILURE); } } |
public void sendText(String callingPackage, String destAddr, String scAddr, String text, PendingIntent sentIntent, PendingIntent deliveryIntent, boolean persistMessageForNonDefaultSmsApp) { //检查是否声明了发短信的权限 mPhone.getContext().enforceCallingPermission( Manifest.permission.SEND_SMS, "Sending SMS message"); sendTextInternal(callingPackage, destAddr, scAddr, text, sentIntent, deliveryIntent, persistMessageForNonDefaultSmsApp);//转调了内部方法 } |
private void sendTextInternal(String callingPackage, String destAddr, String scAddr, String text, PendingIntent sentIntent, PendingIntent deliveryIntent, boolean persistMessageForNonDefaultSmsApp) { if (Rlog.isLoggable("SMS", Log.VERBOSE)) { log("sendText: destAddr=" + destAddr + " scAddr=" + scAddr + " text='"+ text + "' sentIntent=" + sentIntent + " deliveryIntent=" + deliveryIntent); } //检查该操作是否被用户允许 if (mAppOps.noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(), callingPackage) != AppOpsManager.MODE_ALLOWED) { return; } if (!persistMessageForNonDefaultSmsApp) { // Only allow carrier app to skip auto message persistence. enforceCarrierPrivilege(); } destAddr = filterDestAddress(destAddr);//对目的地址进行检测 mDispatcher.sendText(destAddr, scAddr, text, sentIntent, deliveryIntent, null/*messageUri*/, callingPackage, persistMessageForNonDefaultSmsApp); } |
1.2 SmsDispatcher
SmsDispatcher总共派生出三个子类:CdmaSMSDispatcher、GsmSMSDispatcher、ImsSmsDispatcher,在IccSmsInterfaceManager创建时只创建ImsSMSDispatcher,而在ImsSmsDispatcher创建过程中会对创建其他两种制式的SmsDispatcher,IccSmsInterfaceManager把请求发送给ImsSMSDispatcher后,由ImsSMSDispatcher根据当前网络状态选择使用CdmaSmsDispatcher还是GsmSmsDispatcher。这里主要以Cdma为例。因此调用的是CdmaSmsDispathcer.sendText()。 protected void sendText(String destAddr, String scAddr, String text, PendingIntent sentIntent, PendingIntent deliveryIntent, Uri messageUri, String callingPkg, boolean persistMessage) { //将短信内容包装成pdu SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu( scAddr, destAddr, text, (deliveryIntent != null), null); if (pdu != null) { //接着将短信包装成tracker HashMap map = getSmsTrackerMap(destAddr, scAddr, text, pdu); SmsTracker tracker = getSmsTracker(map, sentIntent, deliveryIntent, getFormat(),messageUri, false /*isExpectMore*/, text, true /*isText*/, persistMessage); //carrier是运营商的意思,因此这里和运营商有关 String carrierPackage = getCarrierAppPackageName(); if (carrierPackage != null) {//通过运行商的app发送短信 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!"); } } } } |
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);//这里 } |
protected void sendRawPdu(SmsTracker tracker) { HashMap map = tracker.mData; //从tracker中解析出map byte pdu[] = (byte[]) map.get("pdu");//从map中解析出pdu if (mSmsSendDisabled) {//短信发送被禁止了 Rlog.e(TAG, "Device does not support sending sms."); tracker.onFailed(mContext, RESULT_ERROR_NO_SERVICE, 0/*errorCode*/); return; } if (pdu == null) {//pdu空 Rlog.e(TAG, "Empty PDU"); tracker.onFailed(mContext, RESULT_ERROR_NULL_PDU, 0/*errorCode*/); return; } // Get calling app package name via UID from Binder call PackageManager pm = mContext.getPackageManager(); String[] packageNames = pm.getPackagesForUid(Binder.getCallingUid()); if (packageNames == null || packageNames.length == 0) { // Refuse to send SMS if we can't get the calling package name. Rlog.e(TAG, "Can't get calling app package name: refusing to send SMS"); tracker.onFailed(mContext, RESULT_ERROR_GENERIC_FAILURE, 0/*errorCode*/); return; } // Get package info via packagemanager PackageInfo appInfo; try { // XXX this is lossy- apps can share a UID appInfo = pm.getPackageInfo(packageNames[0], PackageManager.GET_SIGNATURES); } catch (PackageManager.NameNotFoundException e) { Rlog.e(TAG, "Can't get calling app package info: refusing to send SMS"); tracker.onFailed(mContext, RESULT_ERROR_GENERIC_FAILURE, 0/*errorCode*/); return; } // checkDestination() returns true if the destination is not a premium short code or the // sending app is approved to send to short codes. Otherwise, a message is sent to our // handler with the SmsTracker to request user confirmation before sending. if (checkDestination(tracker)) { // check for excessive(过多的) outgoing SMS usage by this app if (!mUsageMonitor.check(appInfo.packageName, SINGLE_PART_SMS)) { sendMessage(obtainMessage(EVENT_SEND_LIMIT_REACHED_CONFIRMATION, tracker)); return; } sendSms(tracker);//这里 } } |
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");//再次从tracker中解出pdu Rlog.d(TAG, "sendSms: " + " isIms()=" + isIms() + " mRetryCount=" + tracker.mRetryCount + " mImsRetry=" + tracker.mImsRetry + " mMessageRef=" + tracker.mMessageRef + " SS=" + mPhone.getServiceState().getState()); sendSmsByPstn(tracker);//这里 } |
protected void sendSmsByPstn(SmsTracker tracker) { int ss = mPhone.getServiceState().getState();//获取phone状态 // 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,一遍发送完成后回到HandleMessage Message reply = obtainMessage(EVENT_SEND_SMS_COMPLETE, tracker); byte[] pdu = (byte[]) tracker.mData.get("pdu");//又解出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 //注意携带了参数reply是一个EVENT_SEND_SMS_COMPLETE, tracker的消息 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++;//如果是重试,重试次数加1 } } |
当RILJ发送完毕,reply消息被发送,因此sms发送成功的消息被smsDispather接收并HandleMessage。在handleMessage()中直接调用了handleSendComplete()方法。
protected void handleSendComplete(AsyncResult ar) { SmsTracker tracker = (SmsTracker) ar.userObj;//从ar中解出tracker PendingIntent sentIntent = tracker.mSentIntent;//从tracker中解出sendIntent if (ar.result != null) {//解出回应消息 tracker.mMessageRef = ((SmsResponse)ar.result).mMessageRef; } else { Rlog.d(TAG, "SmsResponse was null"); } if (ar.exception == null) {//如果没有异常,表示发送成功 if (DBG) Rlog.d(TAG, "SMS send complete. Broadcasting intent: " + sentIntent); //如果需要等待对方接受的结果状态,将tracker添加到pendinglist以等待结果 //需要注意的是这里的mSendItent和mDeliveryIntent都是pendingIntent,就是留待以后触发的意思,需要触发是调用PendingIntent.send()-—网络总结 if (tracker.mDeliveryIntent != null) { // Expecting a status report. Add it to the list. deliveryPendingList.add(tracker);//留待以后触发 } tracker.onSent(mContext);//发送消息广播,内部调用了PendingIntent.send() } else {//如果有异常,表示发送短信失败 if (DBG) Rlog.d(TAG, "SMS send failed"); //首先获取短信的状态 int ss = mPhone.getServiceState().getState(); //短信发送失败,可以重试,但服务不再Service状态,直接将重试次数设到超过最大 if ( tracker.mImsRetry > 0 && ss != ServiceState.STATE_IN_SERVICE) { // This is retry after failure over IMS but voice is not available. // Set retry to max allowed, so no retry is sent and // cause RESULT_ERROR_GENERIC_FAILURE to be returned to app. tracker.mRetryCount = MAX_SEND_RETRIES;//设置最大重试次数,即不重试 Rlog.d(TAG, "handleSendComplete: Skipping retry: " +" isIms()="+isIms() +" mRetryCount="+tracker.mRetryCount +" mImsRetry="+tracker.mImsRetry +" mMessageRef="+tracker.mMessageRef +" 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*/); } else if ((((CommandException)(ar.exception)).getCommandError() == CommandException.Error.SMS_FAIL_RETRY) && tracker.mRetryCount < MAX_SEND_RETRIES) { //发送失败,重试,从这里看出重试次数有次数限制 tracker.mRetryCount++; Message retryMsg = obtainMessage(EVENT_SEND_RETRY, tracker); sendMessageDelayed(retryMsg, SEND_RETRY_DELAY); } else { int errorCode = 0;//默认没有errorcode if (ar.result != null) {//根据ar设置errorcode errorCode = ((SmsResponse)ar.result).mErrorCode; } int error = RESULT_ERROR_GENERIC_FAILURE;//默认错误为这个 if (((CommandException)(ar.exception)).getCommandError() == CommandException.Error.FDN_CHECK_FAILURE) { //如果底层特殊上报了error,则根据底层设置error error = RESULT_ERROR_FDN_CHECK_FAILURE; } //将错误及错误code发送到上层,内部同样调用了PendingIntent.send()触发执行Intent tracker.onFailed(mContext, error, errorCode); } } } |
1.3 SmsTracker与pendingItent
这里主要涉及到了PendingIntent,因此还有待研究!1.4 小结
下图是普通短信的处理流程。可以看到的是,在上层,短信是通过源目地址以及String等体现出来的,接着往底层走是tracker,再接着到RILJ演变成了pdu数据,再到RILRequest下发到RILD。在图中也画出了长短信的处理流程,可以看到长短信的处理与普通短信的处理基本类似,仅仅是多了分段处理(在App中)。
相关文章推荐
- Android图片切片控制与显示案例实战
- Android6.0的SMS(短信)源码分析--短信接收
- android颜色透明度
- Android studio如何导入library及jar包和so包
- android的ListView实现
- android基础---->传感器的使用
- Canvas绘制以及使用第三方字体(多媒体学习基础)
- Android图像处理之Canvas
- 【技术分享】Android应用安全开发之浅谈加密算法的坑
- 打开android studio 时遇到卡顿,任务管理器里有N多个aapt.exe进程情况的解决
- Android_Module用法_和_如何制作自己的Library开源库
- Android应用中使用ViewPager和ViewPager指示器来制作Tab标签
- 解决Android调用系统相机拍照后相片无法在相册中显示问题
- Android的Looper类使用详解
- android 文件上传、下载
- android demo自动开启WiFi
- 如何在mac下安装android sdk
- Android 自定义属性,系统控件扩展
- Android ListView常用小技巧汇总
- Android_Handler详解(一)