Android开发之Android5.1.1(CM12.1)源码中短信发送流程解析

首先我要从SmsManager开始一步步深入了解,相信大家在学习Android基础的时候接触过这个类。它在/frameworks/opt/telephony/src/java/android/telephony路径下,SmsManager:提供管理短信操作,如发送数据,文本和PDU短信。通过调用静态方法SmsManager.getDefault() 获取此对象。它里面提供了一系列发送短信的方法,我们就从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 = getISmsServiceOrThrow();
        iccISms.sendTextForSubscriber(getSubscriptionId(), ActivityThread.currentPackageName(),destinationAddress,scAddress, text, sentIntent, deliveryIntent);
    } catch (RemoteException ex)
 {
        // ignore it
    }
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

先判断地址和短信内容是否为空,并且抛出异常信息,然后通过ISms这样一个东西调用sendTextForSubscriber()方法将短信往下一个类进行传递。

getISmsServiceOrThrow():获取ISms服务。 
destinationAddress:收短信人的地址。 
scAddress:短信号码中心,如果传null则为默认短信号码中心。 
text:短信内容。 
sentIntent:短信发送成功或者失败的广播。 
deliveryIntent:对方收到短信时候的广播。 
getSubscriptionId():获取订阅id。 
ActivityThread.currentPackageName():当前的包名。

在上面接触到ISms这样一个东西,那么他是干嘛的呢?其实它一个接口,在frameworks/base/telephony/java/com/android/internal/telephony下面,我们会找到它,会发现它是是一个aidl文件,打开它是一个接口(interface),这是我们就要去找另外一个类了,在android5.1.1中有能力完成短信发送任务的系统服务它就是UiccSmsController.java。它在/frameworks/opt/telephony/src/java/com/android/internal/telephony路径下面,UiccSmsController:提供一个进程间通信访问ICC中的短信。打开它会发现这样的继承关系:

public class UiccSmsController extends ISms.Stub
 
 
  • 1
  • 1

里面有这样的一段代码:

public void sendText(String callingPackage, String destAddr, String scAddr,String text, PendingIntent sentIntent, PendingIntent deliveryIntent)
 {
  sendTextForSubscriber(getDefaultSmsSubId(), callingPackage, destAddr,scAddr, text, sentIntent, deliveryIntent);
 }

 public void sendTextForSubscriber(int subId, String callingPackage,String destAddr, String scAddr,
   String text,PendingIntent sentIntent, PendingIntent deliveryIntent)
 {
  sendTextWithOptionsUsingSubscriber(subId, callingPackage, destAddr,scAddr, text, sentIntent, deliveryIntent, -1, false, -1);
 }

 public void sendTextWithOptionsUsingSubscriber(int subId,String callingPackage, String destAddr,
   String scAddr, String text,PendingIntent sentIntent, PendingIntent deliveryIntent,int priority, boolean isExpectMore, int validityPeriod)
 {
  mContext.enforceCallingPermission(android.Manifest.permission.SEND_SMS,"Sending SMS message");
  IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
  if (iccSmsIntMgr.isShortSMSCode(destAddr))
  {
   iccSmsIntMgr.sendTextWithOptions(callingPackage, destAddr, scAddr,text, sentIntent, deliveryIntent, priority, isExpectMore,validityPeriod);
   return;
  }
  ArrayList<String> parts = new ArrayList<String>();
  parts.add(text);
  ArrayList<PendingIntent> sentIntents = new ArrayList<PendingIntent>();
  sentIntents.add(sentIntent);
  ArrayList<PendingIntent> deliveryIntents = new ArrayList<PendingIntent>();
  deliveryIntents.add(deliveryIntent);
  broadcastOutgoingSms(subId, callingPackage, destAddr, scAddr, false,parts, sentIntents, deliveryIntents, priority, isExpectMore,validityPeriod);
 }
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

可以看出现进行权限的操作,然后调用了IccSmsInterfaceManager 的sendTextWithOptions方法将短信进一步传递,后面进行广播的处理。通过getDefaultSmsSubId()获得了一个手机卡的默认SubId,在同级路径下找到IccSmsInterfaceManager 类打开之后,又会发现,它调用SMSDispatcher的sendText()方法将短信进一步传递。在同级路径下我们打开SMSDispatcher 会发现它是一个抽象类,并且继承了Handler。如下:

public abstract class SMSDispatcher extends Handler 
 
 
  • 1
  • 1

既然是抽象类,那肯定就有实现它的派生类,在Android5.1.1中我找到了三个派生类:CdmaSMSDispatcher、GsmSMSDispatcher、ImsSmsDispatcher。但是在IccSmsInterfaceManager 中只创建了ImsSmsDispatcher。

protected IccSmsInterfaceManager(PhoneBase phone)
 {
      mPhone = phone;
      mContext = phone.getContext();
      mAppOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
      mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
      mDispatcher = new ImsSMSDispatcher(phone, phone.mSmsStorageMonitor,phone.mSmsUsageMonitor);
 }
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

在同级路径下打开ImsSmsDispatcher.java我们会发现,ImsSmsDispatcher持有了CdmaSMSDispatcher、GsmSMSDispatcher这两个对象的实例:

private SMSDispatcher mCdmaDispatcher;
private SMSDispatcher mGsmDispatcher;
 
 
  • 1
  • 2
  • 1
  • 2

通过判断网络制式,分别调用mCdmaDispatcher或者mGsmDispatcher的sendText()方法。 
判断网络制式:

 /**
  * Determines whether or not to use CDMA format for MO SMS. If SMS over IMS
  * is supported, then format is based on IMS SMS format, otherwise format is
  * based on current phone type.
  *
  * @return true if Cdma format should be used for MO SMS, false otherwise.
  */
 private boolean isCdmaMo()
 {
      if (!isIms() || !shouldSendSmsOverIms())
  {
   // Either IMS is not registered or there is an active 1x voice call
   // while on eHRPD, use Voice technology to determine SMS format.
   return (PhoneConstants.PHONE_TYPE_CDMA == mPhone.getPhoneType());
  }
  // IMS is registered with SMS support
  return isCdmaFormat(mImsSmsFormat);
 }
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

这儿我们以mGsmDispatcher作为下一步讲解对象,继续研究短信的发送。在frameworks/opt/telephony/src/java/com/android/internal/telephony/gsm路径下打开GsmSMSDispatcher,找到sendText()方法,如下:

@Override
 protected void sendText(String destAddr, String scAddr, String text,PendingIntent sentIntent,
   PendingIntent deliveryIntent,Uri messageUri, String callingPkg, int priority,
   boolean isExpectMore, int validityPeriod)
 {
  SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu(scAddr, destAddr,text,
    (deliveryIntent != null), validityPeriod);
  if (pdu != null)
  {
   HashMap map = getSmsTrackerMap(destAddr, scAddr, text, pdu);
   SmsTracker tracker = getSmsTracker(map, sentIntent, deliveryIntent,getFormat(),
     messageUri, isExpectMore,text /* fullMessageText */, true /* isText */,validityPeriod);
   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");
  }
 }
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

我们会发现,首先将短信组装成pdu格式,然后进一步将pdu组装成为一个SmsTracker ,getCarrierAppPackageName():获取手机内置的载体app,一般手机厂商都不会内置这个,所以一般都会走sendRawPdu()这个方法。这个方法在父类SMSDispatcher 中,所以我们又得去父类看:

protected void sendRawPdu(SmsTracker tracker)
 {
  HashMap map = tracker.mData;
  byte pdu[] = (byte[]) map.get("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)
  {
   Rlog.e(TAG, "Empty PDU");
   tracker.onFailed(mContext, RESULT_ERROR_NULL_PDU, 0/* errorCode */);
   return;
  }
  PendingIntent sentIntent = tracker.mSentIntent;
  // Get calling app package name via UID from Binder call
  PackageManager pm = mContext.getPackageManager();
  int callingUid = Binder.getCallingUid();
  // Special case: We're being proxied by the telephony stack itself,
  // so use the intent generator's UID if one exists
  String[] packageNames;
  if (callingUid == android.os.Process.PHONE_UID && sentIntent != null&& sentIntent.getCreatorPackage() != null)
  {
   packageNames = new String[]
   { sentIntent.getCreatorPackage() };
  } else
  {
   packageNames = pm.getPackagesForUid(callingUid);
  }
  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);
  }
 }
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69

从上面可以得知,先从tracker中取出data,之后再取出pdu。 
判断短信发送是否可用 
pdu是否为空。 
checkDestination(tracker):判断内置的short code和发送的short code是否一致。 
然后调用GsmSMSDispatcher的sendSms()方法:

@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);
 }
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

可以看到上面需要对短信进行一个处理,判断重试次数,如果大于0就将第一个字节赋值为0x04 至于为什么是0x04我也不知道, 
TP-RD:是否拒绝相同重复消息。 
TP-MR:消息基准值。 
现在来看GsmSMSDispatcher的sendSmsByPstn()方法又干了些什么事情。

@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++;
  }
 }
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52

先是对手机状态进行判断,然后又将pdu取出来,然后弄了一个发送完成的消息。 
isIms():判断IMS是否注册,和SMS是否支持。自此就开始调用mCi了,也就是CommandsInterface类了,这个类在/frameworks/opt/telephony/src/java/com/android/internal/telephony路径下,这个就进入额RIL层了,也就不是我研究的范围了。其实在我修改过中是没有修改到这一层,基本修改操作基本都是在调用sendRawPdu()方法之前完成。是不是已经脑壳都看糊了?好了,下面我们来看一张图,总结一下,短信发送的流程! 
这里写图片描述

自此,希望大家能对短信的发送有个大体的认识,如果需要详细的了解短信发送的每个细节,可以下载源代码观看。结合源代码,观看本文效果更佳!



原文地址: http://blog.csdn.net/poison_h/article/details/50972149

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值