在上一篇http://blog.csdn.net/poison_h/article/details/50943909博客中,我们详细的阐述了,Android源代码下载已经编译的流程,如果还有不会的请看上一篇文章。由于这次是一个JG项目,属于JM项目,所以涉及到了对Android framework层的源代码修改,而里面有个功能就是要对Android系统中的短信系统进行修改。下面我将进行简单的分析下Android 5.1.1系统中短信发送的流程。希望能给正在这方面纠结的朋友给与一丁点方向参考,具体还是希望大家踊跃去阅读Android源代码。
首先我要从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
}
}
先判断地址和短信内容是否为空,并且抛出异常信息,然后通过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
里面有这样的一段代码:
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);
}
可以看出现进行权限的操作,然后调用了IccSmsInterfaceManager 的sendTextWithOptions方法将短信进一步传递,后面进行广播的处理。通过getDefaultSmsSubId()获得了一个手机卡的默认SubId,在同级路径下找到IccSmsInterfaceManager 类打开之后,又会发现,它调用SMSDispatcher的sendText()方法将短信进一步传递。在同级路径下我们打开SMSDispatcher 会发现它是一个抽象类,并且继承了Handler。如下:
public abstract class SMSDispatcher extends Handler
既然是抽象类,那肯定就有实现它的派生类,在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);
}
在同级路径下打开ImsSmsDispatcher.java我们会发现,ImsSmsDispatcher持有了CdmaSMSDispatcher、GsmSMSDispatcher这两个对象的实例:
private SMSDispatcher mCdmaDispatcher;
private SMSDispatcher mGsmDispatcher;
通过判断网络制式,分别调用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);
}
这儿我们以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");
}
}
我们会发现,首先将短信组装成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);
}
}
从上面可以得知,先从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);
}
可以看到上面需要对短信进行一个处理,判断重试次数,如果大于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++;
}
}
先是对手机状态进行判断,然后又将pdu取出来,然后弄了一个发送完成的消息。
isIms():判断IMS是否注册,和SMS是否支持。自此就开始调用mCi了,也就是CommandsInterface类了,这个类在/frameworks/opt/telephony/src/java/com/android/internal/telephony路径下,这个就进入额RIL层了,也就不是我研究的范围了。其实在我修改过中是没有修改到这一层,基本修改操作基本都是在调用sendRawPdu()方法之前完成。是不是已经脑壳都看糊了?好了,下面我们来看一张图,总结一下,短信发送的流程!
自此,希望大家能对短信的发送有个大体的认识,如果需要详细的了解短信发送的每个细节,可以下载源代码观看。结合源代码,观看本文效果更佳!