手机信息的发送
信息的发送:
private void sendSMS(String number, String text) {
SmsManager sms= SmsManager.getDefault();
//Log.d("@ying","number="+number);
//Log.d("@ying","text="+text);
Intent intent = new Intent("SENT_SMS_ACTION");
PendingIntent pi = PendingIntent.getBroadcast(getApplicationContext(), 0,intent, 0);
sms.sendTextMessage(number,null, text,pi,null);
}
这是一个最基本的短信发送的 demo.
短信的发送, 需要使用到 SmsManager.java 这个类. 现在就开始跟一跟, 了解下流程.
# SmsManager
//这个是公共接口. 调用了内部方法.
public void sendTextMessage(
String destinationAddress, String scAddress, String text,
PendingIntent sentIntent, PendingIntent deliveryIntent) {
sendTextMessageInternal(destinationAddress, scAddress, text,
sentIntent, deliveryIntent, true /* persistMessageForCarrierApp*/);
}
//真正的实现方法.
//判断,调用 ISms 的方法
private void sendTextMessageInternal(String destinationAddress, String scAddress,
String text, PendingIntent sentIntent, PendingIntent deliveryIntent,
boolean persistMessageForCarrierApp) {
if (TextUtils.isEmpty(destinationAddress)) {
throw new IllegalArgumentException("Invalid destinationAddress");
}
try {
ISms iccISms = getISmsServiceOrThrow();
iccISms.sendTextForSubscriber(getSubscriptionId(),
ActivityThread.currentPackageName(),
destinationAddress,
scAddress,
text,
sentIntent,
deliveryIntent,
persistMessageForCarrierApp /*默认为true*/);
} catch (RemoteException ex) {
// ignore it
}
}
在调用 ISms.sendTextForSubscriber 时, 会获取对应的 subId 来处理问题. 这就涉及到 Sim 的选择了. 先处理发送问题, 然后专门讲 SubId 的处理.
# ISmS.aidl
void sendTextForSubscriber(in int subId, String callingPkg, in String destAddr,
in String scAddr, in String text, in PendingIntent sentIntent,
in PendingIntent deliveryIntent, in boolean persistMessageForNonDefaultSmsApp/*true*/);
使用了 aidl 接口, 说明进行了跨进程的通信. 通过查找, 实现类在:
opt/telephony/src/java/com/android/internal/telephony 包下
# public class UiccSmsController extends ISms.Stub
public void sendTextForSubscriber(int subId, String callingPackage, String destAddr,
String scAddr, String text, PendingIntent sentIntent, PendingIntent deliveryIntent,
boolean persistMessageForNonDefaultSmsApp/*true*/) {
//通过 subId 获取对应卡槽的 SIM 卡.
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);
}
}
IccSmsInterfaceManager 官方表述: IccSmsInterfaceManager to provide an inter-process communication to access Sms in Icc.
# IccSmsInterfaceManager
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/*true*/);
}
//调用实现
private void sendTextInternal(String callingPackage, String destAddr, String scAddr,
String text, PendingIntent sentIntent, PendingIntent deliveryIntent,
boolean persistMessageForNonDefaultSmsApp/*true*/) {
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/*true*/);
}
对目标地址进行分析过滤后, 直接调用 SMSDispatcher 来处理. SMSDispatcher 是一个继承了 Handler 的抽象类. Handler 一般在子线程中使用. 实现类: ImsSMSDispatcher.java
# ImsSMSDispatcher.java
public void sendText(String destAddr, String scAddr, String text, PendingIntent sentIntent,
PendingIntent deliveryIntent, Uri messageUri, String callingPkg,
boolean persistMessage) {
Rlog.d(TAG, "sendText");
//判断 CDMA 还是 GSM 制式, 两个处理方式应该雷同.
if (isCdmaMo()) {
mCdmaDispatcher.sendText(destAddr, scAddr,
text, sentIntent, deliveryIntent, messageUri, callingPkg, persistMessage);
} else {
mGsmDispatcher.sendText(destAddr, scAddr,
text, sentIntent, deliveryIntent, messageUri, callingPkg, persistMessage);//从这里跳转
}
}
在 ImsSMSDispatcher 中仅仅判断了 SIM 的只是就直接分派给 SMSDispatcher 的子类进行处理
# GSMSMSDispatcher
public void sendText(String destAddr, String scAddr, String text, PendingIntent sentIntent,
PendingIntent deliveryIntent, Uri messageUri/*null*/, String callingPkg/*包名*/,
boolean persistMessage/*true*/) {
//pdu保存了 content 和 源地址 的二进制文件.
//有些时候 源地址 的内容为空, 但短信 content 绝不可能为空.
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*/,
persistMessage);
String carrierPackage = getCarrierAppPackageName();
//这个与 carrier 有关
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");
}
}
<!----------------------------------------------------------------
//看看的实现, 按一定的标准,处理成二进制文件.
SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu(
scAddr, destAddr, text, (deliveryIntent != null));
--------------------------------------------------------------------------
//
public static SubmitPdu getSubmitPdu(String scAddress,
String destinationAddress, String message, boolean statusReportRequested) {
SubmitPduBase spb;
if (useCdmaFormatForMoSms()) {
spb = com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu(scAddress,
destinationAddress, message, statusReportRequested, null);
} else {
spb = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(scAddress,
destinationAddress, message, statusReportRequested);
}
return new SubmitPdu(spb);
}
------------------------------------------------------------------------>
因为 GsmSMSDispatcher 和 ImsSMSDispatcher 都是继承至 SMSDispatcher. 现在绕转可能有点令人迷糊了
# SMSDispatcher
protected void sendRawPdu(SmsTracker tracker) {
HashMap map = tracker.getData();
//因为数据都是存在 tracker 中, 可以解包, 获得 Map 对象
// 再从 Map 中获取 pdu 内容. 这个是 content.
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;
}
// 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);// 走这里
}
if (PhoneNumberUtils.isLocalEmergencyNumber(mContext, tracker.mDestAddress)) {
new AsyncEmergencyContactNotifier(mContext).execute();
}
}
这里又调用 SendSms().
# GsmSMSDispatcher
protected void sendSms(SmsTracker tracker) {
HashMap<String, Object> map = tracker.getData();
byte pdu[] = (byte[]) map.get("pdu"); //再次解析
if (tracker.mRetryCount > 0) {
if (((0x01 & pdu[0]) == 0x01)) {
pdu[0] |= 0x04; // TP-RD
pdu[1] = (byte) tracker.mMessageRef; // TP-MR
}
}
sendSmsByPstn(tracker);
}
protected void sendSmsByPstn(SmsTracker tracker) {
int ss = mPhone.getServiceState().getState();
// if sms over IMS is not supported on data and voice is not available...
// MTK-START
// For the Wifi calling, We need to support sending SMS when radio is power off
// and wifi calling is enabled. So we need to pass the SMS sending request to the
// modem when radio is OFF.
if (!isIms() && ss != ServiceState.STATE_IN_SERVICE
&& !mTelephonyManager.isWifiCallingAvailable()) {
// MTK-END
tracker.onFailed(mContext, getNotInServiceError(ss), 0/*errorCode*/);
// MTK-START
Message delay = obtainMessage(EVENT_DELAY_SEND_MESSAGE_QUEUE, tracker);
// Delay 10 milliseconds to avoid ANR in lots of wait to send messages inside
sendMessageDelayed(delay, 10);
// MTK-END
return;
}
HashMap<String, Object> map = tracker.getData();
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()) {//false
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++;
}
}
现在就需要好好看看
mCi.sendImsGsmSms(IccUtils.bytesToHexString(smsc),
IccUtils.bytesToHexString(pdu), tracker.mImsRetry,
tracker.mMessageRef, reply);
mCi 的类为 CommandsInterface. 这是接口, 其实现类为 RIL.java
[]()
这里已经进入了 RIL 层.
public final class RIL extends BaseCommands implements CommandsInterface
public void
sendImsGsmSms (String smscPDU, String pdu, int retry, int messageRef,
Message result) {
RILRequest rr = RILRequest.obtain(RIL_REQUEST_IMS_SEND_SMS, result);
rr.mParcel.writeInt(RILConstants.GSM_PHONE);
rr.mParcel.writeByte((byte)retry);
rr.mParcel.writeInt(messageRef);
constructGsmSendSmsRilRequest(rr, smscPDU, pdu);
if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
mEventLog.writeRilSendSms(rr.mSerial, rr.mRequest);
send(rr);
}
在看这段代码时, 需了解下 RILRequest . 这是一个封装类, 用来封装 pdus, 将其封装成 Parcel.
# RIL
private void
constructGsmSendSmsRilRequest (RILRequest rr, String smscPDU, String pdu) {
rr.mParcel.writeInt(2);
rr.mParcel.writeString(smscPDU);
rr.mParcel.writeString(pdu);
}
这一步主要就是对数据进行封装, 然后进行发送处理.
# RIL
private void
send(RILRequest rr) {
Message msg;
if (mSocket == null) {
rr.onError(RADIO_NOT_AVAILABLE, null);
rr.release();
return;
}
msg = mSender.obtainMessage(EVENT_SEND, rr); //利用handler进行数据的实际处理. 可能短信发送是一个耗时操作.
acquireWakeLock(rr, FOR_WAKELOCK);
msg.sendToTarget();
}
//实际的操作
//***** Handler implementation
@Override public void
handleMessage(Message msg) {
RILRequest rr = (RILRequest)(msg.obj);
RILRequest req = null;
switch (msg.what) {
case EVENT_SEND:
case EVENT_SEND_ACK://发送消息,并返回数据,比如发送提醒.
try {
LocalSocket s;
s = mSocket;
if (s == null) {
rr.onError(RADIO_NOT_AVAILABLE, null);
decrementWakeLock(rr);
rr.release();
return;
}
byte[] data;
data = rr.mParcel.marshall();
// Acks should not be stored in list before sending
if (msg.what != EVENT_SEND_ACK) {
synchronized (mRequestList) {
mRequestList.append(rr.mSerial, rr);
rr.mParcel.recycle(); //释放数据.
rr.mParcel = null;
}
} else {
// for (msg.what == EVENT_SEND_ACK)
rr.mParcel.recycle();
rr.mParcel = null;
}
if (data.length > RIL_MAX_COMMAND_BYTES) {
throw new RuntimeException(
"Parcel larger than max bytes allowed! "
+ data.length);
}
// parcel length in big endian
dataLength[0] = dataLength[1] = 0;
dataLength[2] = (byte)((data.length >> 8) & 0xff);
dataLength[3] = (byte)((data.length) & 0xff);
//Rlog.v(RILJ_LOG_TAG, "writing packet: " + data.length + " bytes");
s.getOutputStream().write(dataLength);
s.getOutputStream().write(data);
if (msg.what == EVENT_SEND_ACK) {
rr.release();
return;
}
} catch (IOException ex) {
...
}
decrementWakeLock(rr);
rr.release();
break;
}
}