手机信息的发送

信息的发送:

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;
            }
        }