0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

OpenHarmony电话子系统的其中的短彩信模块

OpenAtom OpenHarmony 来源:OpenAtom OpenHarmony 作者:OpenAtom OpenHarmony 2022-06-01 10:54 次阅读

一、简介

近年来,随着国内信息化市场的逐步成长,趋向成熟,以 OpenAtom OpenHarmony(简称“OpenHarmony”)为基座,以操作系统为代表的产业不断吸引了众多资源投入,汇聚了众多开发者的加入。深开鸿基于 OpenHarmony 做商业发行版,以 OpenHarmony 为技术底座,专注于智能物联网操作系统。立足金融、泛政府、能源、交通、工业和消费等领域,深开鸿专注于数字终端、数字生活和数字生产等智能物联网操作系统的深耕,配套软硬件多场景智能化服务,赋能千行百业。OpenHarmony 电话子系统为 OS 提供了基础的无线通信能力,本文将基于 OpenHarmony 电话子系统的其中的短彩信模块,详细分析短信彩信模块的 Framework 层代码框架、功能业务和代码关键流程,以便 OS 系统开发者、应用开发者能快速的入门 OpenHarmony 短彩信功能业务的开发和不同 modem 硬件厂商的适配工作。短彩信模块以系统常驻服务,为上层提供 JS API 接口,下层适配不同硬件厂商 Modem。提供短彩信系统框架功能业务,提供短信收发和彩信编解码基础能力;主要功能有 GSM/CDMA 短信收发、短信 PDU(Protocol data unit,协议数据单元)编解码、Wap Push 接收处理 、小区广播接收、彩信通知、 彩信编解码和 SIM 卡短信记录增删改查等。

二、基础知识

1. 短彩信框架图

e29a41b2-e155-11ec-ba43-dac502259ad0.png

2. 代码结构

Interfaces 对外提供暴露接口,包括 JS 接口定义,napi native C++ 的 JS 接口封装;frameworks 包括 Native 接口定义和彩信编解码对外暴露的工具;sa_profile 提供 SystemAbility 启动配置文件;sercives 包含了短信框架内部服务相关代码包括 Gsm/Cdma Pdu 编解码工具和接收处理业务逻辑代码。
/base/telephony/sms_mms├─ frameworks        # 短彩信内部框架接口层├─ interfaces        # 对外暴露的接口│ ├─ innerkits│ └─ kits├─ sa_profile        # 启动配置文件├─ services         # 服务内部代码│ ├─ include        # 头文件目录│ ├─ cdma         # CDMA制式源文件│ └─ gsm          # GSM制式源文件├─ test           # 单元测试目录└─utils#通用工具相关

3. 电话子系统(sms) 核心类

短彩信模块业务流程涉及到的比较重要的核心类成员;包括 sms_mms 服务,依赖的 core_service 和 ril_adapter 关键类。

e3110996-e155-11ec-ba43-dac502259ad0.png

4. 短信发送时序图

应用程序调用 API 发送短信,经过参数判断、鉴权、长短信拆分、PDU 编码;添加到发送队列,调用 RIL 发送短信接口发送短信到接收端。并将发送状态返回应用调用者,完成一次短信发送过程。

1.上层应用调用发送短信的 API JS 接口,调用 JS Napi 接口;

2.JS Napi 调用 Native 层 C++ SendMessage 跨 IPC 到 SmsInterfaceManager 对象;

3.SmsInterfaceManager 根据过滤策略来过滤掉短信和参数合法性检查;

4.SmsInterfaceManager 进行鉴权检查;

5.SmsInterfaceManager 调用 TextBasedSmsDelivery 或 TextBasedSmsDelivery 到 SmsSendManager;

6.SmsSendManager 调用搜网服务来获取 SIM 卡和网络状态;

7.GSM 网络将创建 GsmSmsSender,CDMA 网络将创建 CdmaSmsSender;

8.GsmSmsSender 或 CdmaSmsSender 将短信拆分并进行 PDU 编码然后放到队列中;

9.通过接口将短信发送到 RIL 层;

10.如果返回的状态失败将启动重发机制;

11.返回应用层发送的状态信息;

e344428e-e155-11ec-ba43-dac502259ad0.png

5. 短信接收时序图

1.SmsReceiveManager创建GsmSmsReceiveHandler和CdmaSmsReceiveHandler;2.GsmSmsReceiveHandler和CdmaSmsReceiveHandler分别注册事件到RIL层;3.RIL Adapter上报短信事件;4.GsmSmsReceiveHandler或CdmaSmsReceiveHandler调用SmsBaseMessage解析PDU数据;5.GsmSmsReceiveHandler和CdmaSmsReceiveHandler过滤无效短信;根据PDU头部信息将多段消息合并;6.拦截黑名单短信;7.将接收处理状态应答到RIL;8.广播发送接收处理的短信信息到广播接收者;

e3743930-e155-11ec-ba43-dac502259ad0.png

6. 相关仓

https://gitee.com/openharmony/telephony_sms_mms

https://gitee.com/openharmony/telephony_core_service

https://gitee.com/openharmony/telephony_ril_adapter

三、源码解析

1. SmsService 启动

服务入口类 SmsService.cpp 继承 safwk 组件的 SystemAblility 类;由 SA 系统服务管理框架(samgr)拉起服务。采用 telephony.cfg + profile.xml + libtel_sms_mms.z.so 的方式由 init 进程执行对应的 telephony.cfg 文件拉起 SmsService SystemAbility 对应的 telephony 进程,并执行 void SmsService::OnStart()。


class SmsService : public SystemAbility, public SmsInterfaceStub {    DECLARE_DELAYED_SINGLETON(SmsService)    DECLARE_SYSTEM_ABILITY(SmsService) // necessarypublic:    void OnStart() override;    void OnStop() override;private:    constexpr static uint32_t CONNECT_SERVICE_WAIT_TIME = 2000; // ms    bool Init();    bool registerToService_ = false;    ServiceRunningState state_ = ServiceRunningState::STATE_NOT_START;};} // namespace Telephony}//namespaceOHOS

2. 模块初始化

SmsService 服务启动后,调用 OnStart() 函数;并创建服务内部类,初始化内部资源,包括 SmsInterfaceManager、SmsSendManager、SmsReceiveManager 对象创建和初始化。


void SmsService::OnStart() {    TELEPHONY_LOGI("SmsService::OnStart start service Enter.");    if (state_ == ServiceRunningState::STATE_RUNNING) {        TELEPHONY_LOGE("msService has already started.");        return;    }    if (!Init()) {        TELEPHONY_LOGE("failed to init SmsService");        return;    }    state_ = ServiceRunningState::STATE_RUNNING;    TELEPHONY_LOGI("SmsService::OnStart start service Exit.");}
bool SmsService::Init(){    if (!registerToService_) {        WaitCoreServiceToInit();    }    return true;}//等待核心服务完成初始化,并调用InitModule 初始化内部内部代码void SmsService::WaitCoreServiceToInit(){    std::thread connectTask([&]() {        while (true) {            TELEPHONY_LOGI("connect core service ...");            if (CoreManagerInner::GetInstance().IsInitFinished()) {                InitModule();                TELEPHONY_LOGI("SmsService Connection successful");                break;           }       std::sleep_for(milliseconds(CONNECT_SERVICE_WAIT_TIME));       }    });    connectTask.detach();}

3. Native IPC 接口实现

SmsService.cpp 继承 SmsInterfaceStub.cpp ;SmsInterfaceStub 是 native 层对外接口 IPC 服务端代码,继承了并实现了 IPC 对外接口 ISmsServiceInterface 用于跨进程对外提供 navtive C++ API 接口;OnRemoteRequest() 是服务端的请求入口,通过请求 Id 遍历 memberFuncMap_ 调用对应的实现方法。


int SmsInterfaceStub::OnRemoteRequest(uint32_t code, MessageParcel &data, MessageParcel &reply, MessageOption &option){    int32_t result = 0;    std::u16string myDescripter = SmsInterfaceStub::GetDescriptor();    std::u16string remoteDescripter = data.ReadInterfaceToken();    if (myDescripter == remoteDescripter) {        auto itFunc = memberFuncMap_.find(code);        if (itFunc != memberFuncMap_.end()) {            auto memberFunc = itFunc->second;            if (memberFunc != nullptr) {                (this->*memberFunc)(data, reply, option);            } else {                TELEPHONY_LOGE("memberFunc is nullptr");            }        } else {            TELEPHONY_LOGE("itFunc was not found");        }    } else {        TELEPHONY_LOGE("descriptor checked fail");        return result;}
// 添加memberFuncMap 请求处理函数SmsInterfaceStub::SmsInterfaceStub(){    memberFuncMap_[TEXT_BASED_SMS_DELIVERY] =&SmsInterfaceStub::OnSendSmsTextRequest;    memberFuncMap_[DATA_BASED_SMS_DELIVERY] = &SmsInterfaceStub::OnSendSmsDataRequest;    memberFuncMap_[SET_SMSC_ADDRESS] = &SmsInterfaceStub::OnSetSmscAddr;    memberFuncMap_[GET_SMSC_ADDRESS] = &SmsInterfaceStub::OnGetSmscAddr;    memberFuncMap_[ADD_SIM_MESSAGE] = &SmsInterfaceStub::OnAddSimMessage;    memberFuncMap_[DEL_SIM_MESSAGE] = &SmsInterfaceStub::OnDelSimMessage;    memberFuncMap_[UPDATE_SIM_MESSAGE] = &SmsInterfaceStub::OnUpdateSimMessage;    memberFuncMap_[GET_ALL_SIM_MESSAGE] = &SmsInterfaceStub::OnGetAllSimMessages;    memberFuncMap_[SET_CB_CONFIG] = &SmsInterfaceStub::OnSetCBConfig;    memberFuncMap_[SET_DEFAULT_SMS_SLOT_ID] = &SmsInterfaceStub::OnSetDefaultSmsSlotId;    memberFuncMap_[GET_DEFAULT_SMS_SLOT_ID] = &SmsInterfaceStub::OnGetDefaultSmsSlotId;    memberFuncMap_[SPLIT_MESSAGE] = &SmsInterfaceStub::OnSplitMessage;    memberFuncMap_[GET_SMS_SEGMENTS_INFO] = &SmsInterfaceStub::OnGetSmsSegmentsInfo;    memberFuncMap_[GET_IMS_SHORT_MESSAGE_FORMAT] = &SmsInterfaceStub::OnGetImsShortMessageFormat;    memberFuncMap_[IS_IMS_SMS_SUPPORTED] = &SmsInterfaceStub::OnIsImsSmsSupported;    memberFuncMap_[HAS_SMS_CAPABILITY] = &SmsInterfaceStub::OnHasSmsCapability;}

4. 多卡方案实现

根据卡槽数量创建对应的 SmsInterfaceManager 对象,并用 slotSmsInterfaceManagerMap_ 管理。

服务启动后会调用 InitModule() 方法并根据卡槽数量创建多个 SmsInterfaceManager 每个 SmsInterfaceManager 对象代码每一个卡槽。


void SmsInterfaceStub::InitModule() {    static bool bInitModule = false;    if (!bInitModule) {        bInitModule = true;        std::lock_guard lock(mutex_);        for (int32_t slotId = 0; slotId < SIM_SLOT_COUNT; ++slotId) {            slotSmsInterfaceManagerMap_[slotId] = std::make_shared(slotId);            if (slotSmsInterfaceManagerMap_[slotId] == nullptr) {                return;            }            slotSmsInterfaceManagerMap_[slotId]->InitInterfaceManager();            TELEPHONY_LOGI("SmsInterfaceStub InitModule slotId = %{public}d",slotId);        }    }}

5. 短信发送流程

JS 调用发送短信示例代码, 导入 SDK sms 包并构造 SendMessageOptions 对象参数。


import sms from "@ohos.telephony.sms"; //导入短彩信包let msg: SendMessageOptions = {  slotId: 0,  destinationHost: '123xxxxxxxx',  content: '这是一封短信',  sendCallback: (err, data) => {    if (err) {      // 接口调用失败,err非空      console.error(`failed to send message because ${err.message}`);      return;    }    // 接口调用成功,err为空    console.log(`success to send message: ${data.result}`);  }}// 调用接口sms.sendMessage(msg);

JS 发送短信接口 function sendMessage(options: SendMessageOptions): void 位于 sms_mms/interfaces/kits/js/@ohos.telephony.sms.d.ts。会调用位于 sms_mms/frameworks/js/napi/src/napi_sms.cpp Napi 封装的 SendMessage 发送短信接口。
static napi_value SendMessage(napi_env env, napi_callback_info info){    size_t parameterCount = 1;    napi_value parameters[1] = {0};    napi_value thisVar = nullptr;    void *data = nullptr;    napi_get_cb_info(env, info, ¶meterCount, parameters, &thisVar, &data);    int32_t messageMatchResult = MatchSendMessageParameters(env, parameters,parameterCount);    NAPI_ASSERT(env, messageMatchResult != MESSAGE_PARAMETER_NOT_MATCH, "type mismatch");    auto asyncContext = std::make_unique().release();    if (asyncContext == nullptr) {        std::string errorCode = std::to_string(napi_generic_failure);        std::string errorMessage = "error at SendMessageContext is nullptr";        NAPI_CALL(env, napi_throw_error(env, errorCode.c_str(), errorMessage.c_str()));        return nullptr;    }    ParseMessageParameter(messageMatchResult, env, parameters[0], *asyncContext);    napi_create_reference(env, thisVar, DEFAULT_REF_COUNT, &asyncContext->thisVarRef);    napi_value resourceName = nullptr;    napi_create_string_utf8(env, "SendMessage", NAPI_AUTO_LENGTH, &resourceName);    napi_create_async_work(env, nullptr, resourceName, NativeSendMessage, SendMessageCallback,        (void *)asyncContext, &(asyncContext->work));    napi_queue_async_work(env, asyncContext->work);    return NapiUtil::CreateUndefined(env);}

SendMessage 最终会调用 sms_mms/frameworks/js/napi/src/napi_sms.cpp ActuallySendMessage 函数;

ActuallySendMessage 调用 Native C++ 提供发送短信的单例类 SmsServiceManagerClient 这个类是现实了 OpenHarmony 系统 IPC 通讯框架的客户端;

用于 C/S 架构与 SmsService 服务通讯。SmsServiceManagerClient SendMessage() 接口需要两个回调对象分别是 SendCallback 和 DeliveryCallback 用于返回服务发送短信的状态结果回调。


static bool ActuallySendMessage(napi_env env, SendMessageContext ¶meter){    std::unique_ptr sendCallback =        std::make_unique(hasSendCallback, env, parameter.thisVarRef, parameter.sendCallbackRef);    std::unique_ptr deliveryCallback = std::make_unique(        hasDeliveryCallback, env, parameter.thisVarRef, parameter.deliveryCallbackRef);    //文本类型的短信    if (parameter.messageType == TEXT_MESSAGE_PARAMETER_MATCH) {        int32_t sendResult = DelayedSingleton::GetInstance()->SendMessage(            parameter.slotId, parameter.destinationHost, parameter.serviceCenter, parameter.textContent,            sendCallback.release(), deliveryCallback.release());        TELEPHONY_LOGI("NativeSendMessage SendTextMessage execResult = %{public}d", sendResult);    }     // 数据类型的短信    else if (parameter.messageType == RAW_DATA_MESSAGE_PARAMETER_MATCH) {        if (parameter.rawDataContent.size() > 0) {            uint16_t arrayLength = static_cast(parameter.rawDataContent.size());            int32_t sendResult = DelayedSingleton::GetInstance()->                SendMessage(parameter.slotId, parameter.destinationHost, parameter.serviceCenter,                parameter.destinationPort, ¶meter.rawDataContent[0],                arrayLength, sendCallback.release(), deliveryCallback.release());            TELEPHONY_LOGI("NativeSendMessage SendRawDataMessage execResult = %{public}d", sendResult);        }    }    return false;}
//Native C++ 发送短信接口函数int32_t SmsServiceManagerClient::SendMessage(int32_t slotId, const std::u16string desAddr,    const std::u16string scAddr, const std::u16string text, const sptr &callback,    const sptr &deliveryCallback){    if (InitSmsServiceProxy()) {        smsServiceInterface_->SendMessage(slotId, desAddr, scAddr, text, callback, deliveryCallback);        TELEPHONY_LOGI("execute SendMessage
");        return ERROR_NONE;    }    return ERROR_SERVICE_UNAVAILABLE;}
// InitSmsServiceProxy 用于初始化与SmsService IPC 通许连接工作bool SmsServiceManagerClient::InitSmsServiceProxy(){    if (smsServiceInterface_ == nullptr) {        std::lock_guard lock(mutex_);        sptr systemAbilityManager =            SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();        if (!systemAbilityManager) {            TELEPHONY_LOGE(" Get system ability mgr failed.");            return false;        }        sptr remoteObject = systemAbilityManager->GetSystemAbility(TELEPHONY_SMS_MMS_SYS_ABILITY_ID);        if (!remoteObject) {            TELEPHONY_LOGE("Get SMS Service Failed.");            return false;        }        smsServiceInterface_ = iface_cast(remoteObject);        if ((!smsServiceInterface_) || (!smsServiceInterface_->AsObject())) {            TELEPHONY_LOGE("Get SMS Service Proxy Failed.");            return false;        }        recipient_ = new SmsServiceInterfaceDeathRecipient();        if (!recipient_) {            TELEPHONY_LOGE("Failed to create death Recipient ptr SmsServiceInterfaceDeathRecipient!");            return false;        }        smsServiceInterface_->AsObject()->AddDeathRecipient(recipient_);    }    return true;}

通过 SmsServiceManagerClient 的 SendMessage 调用最终会通过 IPC 调用到 sms_mms/services/sms_interface_stub.cpp 的 OnRemoteRequest() 函数;

通过定义的 codeId 从 memberFuncMap_ 遍历出 OnSendSmsTextRequest() 方法并执行;

OnSendSmsTextRequest 主要是 IPC 数据的反序列号并调用 SmsService 实现的 SendMessage() 方法。


void SmsInterfaceStub::OnSendSmsTextRequest(MessageParcel &data, MessageParcel &reply, MessageOption &option){    int32_t result = 0;    sptr sendCallback = nullptr;    sptr deliveryCallback = nullptr;    int32_t slotId = data.ReadInt32();    u16string desAddr = data.ReadString16();    u16string scAddr = data.ReadString16();    u16string text = data.ReadString16();    sptr remoteSendCallback = data.ReadRemoteObject();    sptr remoteDeliveryCallback = data.ReadRemoteObject();    if (remoteSendCallback != nullptr) {        sendCallback = iface_cast(remoteSendCallback);    }    if (remoteDeliveryCallback != nullptr) {        deliveryCallback = iface_cast(remoteDeliveryCallback);    }    TELEPHONY_LOGI("MessageID::TEXT_BASED_SMS_DELIVERY %{public}d", slotId);    SendMessage(slotId, desAddr, scAddr, text, sendCallback, deliveryCallback);    reply.WriteInt32(result);}
void SmsService::SendMessage(int32_t slotId, const u16string desAddr, const u16string scAddr,    const u16string text, const sptr &sendCallback,    const sptr &deliveryCallback){  // 权限校验    if (!TelephonyPermission::SEND_MESSAGES)) {        SmsSender::SendResultCallBack(sendCallback, ISendShortMessageCallback::SEND_SMS_FAILURE_UNKNOWN);        TELEPHONY_LOGE("Check Permission Failed, No Has Telephony Send Messages Permisson.");        return;    }    // 获取对应slotId 对应的SmsInterfaceManager 对象    std::shared_ptr interfaceManager = GetSmsInterfaceManager(slotId);    ............    interfaceManager->TextBasedSmsDelivery(StringUtils::ToUtf8(desAddr), StringUtils::ToUtf8(scAddr),        StringUtils::ToUtf8(text), sendCallback, deliveryCallback);}

SmsInterfaceManager 对象 TextBasedSmsDelivery 用于发送短信接口调用 SmsSendManager 对象的 TextBasedSmsDelivery();根据当前 sloidId 卡的网络状态 调用 Gsm 或者 Cdma 来进一步发送短信处理。


void SmsSendManager::TextBasedSmsDelivery(const string &desAddr, const string &scAddr, const string &text,    const sptr &sendCallback,    const sptr &deliveryCallback){    ......此处省略.....    NetWorkType netWorkType = networkManager_->GetNetWorkType();    TELEPHONY_LOGI("netWorkType = %{public}d.", netWorkType);    if (netWorkType == NetWorkType::NET_TYPE_GSM) {        gsmSmsSender_->TextBasedSmsDelivery(desAddr, scAddr, text, sendCallback, deliveryCallback);    } else if (netWorkType == NetWorkType::NET_TYPE_CDMA) {        cdmaSmsSender_->TextBasedSmsDelivery(desAddr, scAddr, text, sendCallback, deliveryCallback);    } else {        SmsSender::SendResultCallBack(            sendCallback, ISendShortMessageCallback::SEND_SMS_FAILURE_SERVICE_UNAVAILABLE);        TELEPHONY_LOGI("network unknown send error.");    }}

Gsm 或者 Cdma 长短信分段和 PDU 的编码过程,入口函数 GsmSmsSender::TextBasedSmsDelivery()。

或者 CdmaSmsSender::TextBasedSmsDelivery() 并构造 SmsSendIndexer 对象添加到 Map 队列中,并调用 CoreService 提供的发送短信接口发送,等待发送结果;拿 Gsm 制式的来分析,代码如下:


void GsmSmsSender::TextBasedSmsDelivery(const string &desAddr, const string &scAddr, const string &text,    const sptr &sendCallback, const sptr &deliveryCallback){    bool isMore = false;    bool isStatusReport = false;    int ret = 0;    int headerCnt;    int cellsInfosSize;    unsigned char msgRef8bit;    SmsCodingScheme codingType;    GsmSmsMessage gsmSmsMessage;    std::vector cellsInfos;    // 长短信分段拆分    gsmSmsMessage.SplitMessage(cellsInfos, text, CheckForce7BitEncodeType(), codingType);    isStatusReport = (deliveryCallback == nullptr) ? false : true;    std::shared_ptr tpdu =        gsmSmsMessage.CreateDefaultSubmitSmsTpdu(desAddr, scAddr, text, isStatusReport, codingType);
  ....部分代码省略.........    std::unique_lock lock(mutex_);    for (int i = 0; i < cellsInfosSize; i++) {        std::shared_ptr indexer = nullptr;        std::string segmentText;        segmentText.append((char *)(cellsInfos[i].encodeData.data()),cellsInfos[i].encodeData.size());   ....部分代码省略.........   // 编码PDU        std::shared_ptr encodeInfo = gsmSmsMessage.GetSubmitEncodeInfo(scAddr, isMore);        if (encodeInfo == nullptr) {            SendResultCallBack(indexer, ISendShortMessageCallback::SEND_SMS_FAILURE_UNKNOWN);            TELEPHONY_LOGE("create encodeInfo encodeInfo nullptr error.");            continue;        }        // 构造填充SmsSendIndexer对象        SetSendIndexerInfo(indexer, encodeInfo, msgRef8bit);        indexer->SetUnSentCellCount(unSentCellCount);        indexer->SetHasCellFailed(hasCellFailed);        SendSmsToRil(indexer);    }}
//判断是否是IMS网络域,调用CoreService 不同的接口进行发送void GsmSmsSender::SendSmsToRil(const shared_ptr &smsIndexer){  ....部分代码省略.........    GsmSimMessageParam smsData;    smsData.refId = refId;    smsData.smscPdu = StringUtils::StringToHex(smsIndexer->GetEncodeSmca());    if (!isImsNetDomain_ && smsIndexer->GetPsResendCount() == 0) {        uint8_t tryCount = smsIndexer->GetCsResendCount();        if (tryCount > 0) {            smsIndexer->UpdatePduForResend();        }        smsData.pdu = StringUtils::StringToHex(smsIndexer->GetEncodePdu());        if (tryCount == 0 && smsIndexer->GetHasMore()) {            TELEPHONY_LOGI("SendSmsMoreMode pdu len = %{public}zu", smsIndexer->GetEncodePdu().size());            CoreManagerInner::GetInstance().SendSmsMoreMode(slotId_,                RadioEvent::RADIO_SEND_SMS_EXPECT_MORE, smsData, shared_from_this());        } else {            TELEPHONY_LOGI("SendSms pdu len = %{public}zu", smsIndexer->GetEncodePdu().size());            CoreManagerInner::GetInstance().SendGsmSms(slotId_,                RadioEvent::RADIO_SEND_SMS, smsData, shared_from_this());        }    } else {        TELEPHONY_LOGI("ims network domain send sms interface.!");        smsIndexer->SetPsResendCount(smsIndexer->GetPsResendCount() + 1);        smsData.pdu = StringUtils::StringToHex(smsIndexer->GetEncodePdu());        if (smsIndexer->GetHasMore()) {            CoreManagerInner::GetInstance().SendSmsMoreMode(slotId_,                RadioEvent::RADIO_SEND_SMS_EXPECT_MORE, smsData, shared_from_this());        } else {            CoreManagerInner::GetInstance().SendGsmSms(slotId_,                RadioEvent::RADIO_SEND_SMS, smsData, shared_from_this());        }    }}

6. 核心服务下发流程

由于 core_service 服务和短彩信服务在同一个进程中,所以我们通过 CoreManagerInner::GetInstance() 来获取核心服务的单例对象,调用 SendGsmSms 或 SendCdmaSms 函数发送短信。


int32_t CoreManagerInner::SendGsmSms(int32_t slotId, int32_t eventId, GsmSimMessageParam &gsmMessage,    const std::shared_ptr &handler){    if (telRilManager_ == nullptr) {        TELEPHONY_LOGE("telRilManager is null!");        return TELEPHONY_ERR_LOCAL_PTR_NULL;    }    AppExecFwk::Pointer response = AppExecFwk::Get(eventId, gsmMessage.refId);    response->SetOwner(handler);    return telRilManager_->SendGsmSms(slotId, gsmMessage.smscPdu, gsmMessage.pdu, response);}

SendGsmSms 函数将 eventId、refId 和 handler 对象封装到 response 对象中再转发到 telRilManager 的 SendGsmSms 函数。


/*********************** TelRilNetwork end ****************************//*********************** TelRilSms start ******************************/int32_t TelRilManager::SendGsmSms(    int32_t slotId, std::string smscPdu, std::string pdu, const AppExecFwk::Pointer &response){    return TaskSchedule(response, "TelRilSms", GetTelRilSms(slotId), &TelRilSms::SendGsmSms, smscPdu, pdu);}

TelRilManager::SendGsmSms 里面只有一个 TaskSchedule 函数调用,其实 TaskSchedule 是个模板函数,就是为了统一下所有任务调用。


template    inline int32_t TaskSchedule(ResponsePtr &_result, const std::string _module, ClassTypePtr &_obj,    FuncType &&_func, ParamTypes &&..._args) const{        if (_func != nullptr) {            // The reason for using native member function access here is to            //   remove std::unique_ptr to prevent copying.            // The reason for not directly using pointers to access member functions is:            //   _obj is a smart pointer, not a native pointer.            return (_obj.*(_func))(std::forward(_args)..., _result);        } else {            TELEPHONY_LOGE("%{public}s - this %{public}p: %{public}s", _module.c_str(), &_obj, "null pointer");            return HRIL_ERR_NULL_POINT;        }}

这个模板函数最终会调用 TelRilSms::SendGsmSms 并且将 response 插入到函数的最后一个参数中。


int32_t TelRilSms::string &smsPdu, std::string &pdu, const AppExecFwk::Pointer &response){    std::shared_ptr telRilRequest = CreateTelRilRequest(HREQ_SMS_SEND_GSM_SMS, response);    if (telRilRequest == nullptr) {        TELEPHONY_LOGE("telRilRequest is nullptr");        return TELEPHONY_ERR_LOCAL_PTR_NULL;    }    TELEPHONY_LOGI("telRilRequest->serialId_:%{public}d", telRilRequest->serialId_);    MessageParcel data;    data.WriteInt32(slotId_);    GsmSmsMessageInfo mGsmSmsMessageInfo = ConstructGsmSendSmsRequestLinkList(smsPdu, pdu);    mGsmSmsMessageInfo.serial = telRilRequest->serialId_;    mGsmSmsMessageInfo.Marshalling(data);    MessageParcel reply;    OHOS::MessageOption option = {OHOS::TF_ASYNC};    if (cellularRadio_->SendRequest(HREQ_SMS_SEND_GSM_SMS, data, reply, option)) {        TELEPHONY_LOGE("cellularRadio_->SendRequest fail");    }    return TELEPHONY_ERR_SUCCESS;}

在 TelRilSms::SendGsmSms 函数中我们调用 CreateTelRilRequest 来将 serialId、response 和 HREQ_SMS_SEND_GSM_SMS 等保存到一个 map 中方便我们后续将短信发送的状态上报到短彩信服务中。下面 MessageParcel data 将 pdu、smscpdu、serialId 序列化到 data 中,最后通过 cellularRadio_->SendRequest 通过 IPC 将数据发送到 ril_adapter 服务中。进入 Ril 层我们分析 serviceshril_hdfsrchril_hdf.c 这个文件,hril_hdf.c 是 ril_adapter 层加载 vendor 库、注册发送和响应函数、启动读写线程、事件调度的入口。


static int32_t RilAdapterDispatch(struct HdfDeviceIoClient *client, int32_t cmd, struct HdfSBuf *data, struct HdfSBuf *reply){    int32_t ret;    static pthread_mutex_t dispatchMutex = PTHREAD_MUTEX_INITIALIZER;    pthread_mutex_lock(&dispatchMutex);    TELEPHONY_LOGI("RilAdapterDispatch cmd:%{public}d", cmd);    ret = DispatchRequest(cmd, data);    pthread_mutex_unlock(&dispatchMutex);    return ret;}
static struct IDeviceIoService g_rilAdapterService = {    .Dispatch = RilAdapterDispatch,    .Open = NULL,    .Release = NULL,};

我们看到 .Dispatch = RilAdapterDispatch 说明事件调度入口就是进入 RilAdapterDispatch 函数。RilAdapterDispatch 函数里面主要加互斥锁调用 DispatchRequest(cmd, data) 这个事件分发最终调用到 hril_sms 文件的 HRilSms::SendGsmSms 函数。


int32_t HRilSms::SendGsmSms(struct HdfSBuf *data){    struct GsmSmsMessageInfo message;    MessageParcel *parcel = nullptr;    const int32_t COUNT_STRINGS_VALUE = 2;    if (SbufToParcel(data, &parcel)) {        TELEPHONY_LOGE("RilAdapter failed to do SbufToParcel");        return HRIL_ERR_INVALID_PARAMETER;    }    if (parcel == nullptr) {        TELEPHONY_LOGE("parcel int32_t SendGsmSms is nullptr!");        return HRIL_ERR_INVALID_PARAMETER;    }    if (!message.ReadFromParcel(*parcel)) {        TELEPHONY_LOGE("RilAdapter failed to do ReadFromParcel!");        return HRIL_ERR_INVALID_PARAMETER;    }    return RequestWithStrings(        message.serial, HREQ_SMS_SEND_GSM_SMS, COUNT_STRINGS_VALUE, message.smscPdu.c_str(), message.pdu.c_str());}

HRilSms::SendGsmSms 函数将 core_service 发送下来的数据反序列化到 struct GsmSmsMessageInfo message 中,最后调用 RequestWithStrings 函数。最后将数据转发到 vendor 库的 at_sms.c 文件的 ReqSendGsmSms 函数。


void ReqSendGsmSms(const ReqDataInfo *requestInfo, const char *const *data, size_t dataLen){//省略非核心代码//向modem发送AT +CMGS指令    err = SendCommandSmsLock(cmd, smsPdu, "+CMGS:", 0, &responseInfo);    if (err != 0 || (responseInfo != NULL && !responseInfo->success)) {        HandlerSmsResult(&response, &reportInfo, requestInfo, &err, responseInfo);        return;   }//获取发送后状态  HandleResult(&err, result, responseInfo, &response);   reportInfo = CreateReportInfo(requestInfo, err, HRIL_RESPONSE, 0);    //上报发送后的状态   OnSmsReport(GetSlotId(requestInfo), reportInfo, (const uint8_t *)&response, sizeof(HRilSmsResponse));    FreeResponseInfo(responseInfo);}

ReqSendGsmSms 函数将 AT 指令下发到 modem 并且将发送的状态上报。上报流程和下发流程类似不做具体分析。

7. 核心服务主动上报

首先打开 vendor_adapter.c 我们可以看到 EventListeners 函数。


static void EventListeners(void){    int32_t waitNextTryTime = SLEEP_TIME;    const char *devicePath = DEVICE_PATH;    char atTtyPath[PARAMETER_SIZE] = {0};
    usleep(DELAY_WAIT_MS); // Prevent slow loading of system properties.    if (GetParameter(AT_TTY_PATH, "", atTtyPath, PARAMETER_SIZE) > 0) {        devicePath = atTtyPath;    }
    TELEPHONY_LOGI("opening AT interface %{public}s", devicePath);    AtSetOnUnusual(AtOnUnusual);    for (;;) {        while (g_fd < 0) {            if (devicePath != NULL) {                g_fd = open(devicePath, O_RDWR);            }            if (g_fd >= 0 && !memcmp(devicePath, DEVICE_PATH_DEFAULT, sizeof(DEVICE_PATH_DEFAULT) - 1)) {                struct termios ios;                tcgetattr(g_fd, &ios);                ios.c_lflag = 0;                tcsetattr(g_fd, TCSANOW, &ios);            }            if (g_fd < 0) {                TELEPHONY_LOGE("ril vendorlib,opening AT interface. retrying...");                sleep(waitNextTryTime);            }        }        g_atStatus = 0;        int32_t ret = ATStartReadLoop(g_fd, OnNotifyOps);        if (ret < 0) {            TELEPHONY_LOGE("AtRead error %d
", ret);            return;        }        ModemInit();        sleep(1);        WaitAtClose();    }}

EventListeners 中主要是 ATStartReadLoop 函数将创建一个线程读取 modem 上报的内容,在 OnNotifyOps 中短信上报内容是 AT +CMT 指令。将调用到 OnSmsReport 函数。


void OnNotifyOps(const char *s, const char *smsPdu){    //省略非核心代码    if (IsCallNoticeCmd(s)) {        CallReportInfoProcess(s);    } else if (ReportStrWith(s, "+CMT:")) {        HRilSmsResponse smsResponse = {};        smsResponse.pdu = (char *)smsPdu;        reportInfo.notifyId = HNOTI_SMS_NEW_SMS;        OnSmsReport(GetSlotId(NULL), reportInfo, (const uint8_t *)&smsResponse, strlen(smsResponse.pdu));//省略非核心代码}

OnReport 最终会调到 hril_manager.c 中的 OnSmsReport 函数。


void HRilManager::OnSmsReport(    int32_t slotId, const ReportInfo *reportInfo, const uint8_t *response, size_t responseLen){    OnReport(hrilSms_, slotId, reportInfo, response, responseLen);}

在 OnReport 函数中查找 notiMemberFuncMap 的 HNOTI_SMS_NEW_SMS 对应的函数指针。


// Notification    notiMemberFuncMap_[HNOTI_SMS_NEW_SMS] = &HRilSms::NewSmsNotify;    notiMemberFuncMap_[HNOTI_SMS_NEW_CDMA_SMS] =&HRilSms::NewCdmaSmsNotify;    notiMemberFuncMap_[HNOTI_SMS_STATUS_REPORT] = &HRilSms::SmsStatusReportNotify;    notiMemberFuncMap_[HNOTI_SMS_NEW_SMS_STORED_ON_SIM] = &HRilSms::NewSmsStoredOnSimNotify;    notiMemberFuncMap_[HNOTI_CB_CONFIG_REPORT] = &HRilSms::CBConfigNotify;
int32_t HRilSms::NewSmsNotify(int32_t indType, const HRilErrNumber e, const void *response, size_t responseLen){    //省略部分代码    std::unique_ptr parcel = std::make_unique();    if (parcel == nullptr) {        TELEPHONY_LOGE("parcel in NewSmsNotify is nullptr!");        return HRIL_ERR_GENERIC_FAILURE;    }    if (!parcel->WriteInterfaceToken(HRIL_INTERFACE_TOKEN)) {        TELEPHONY_LOGE("write interface token failed.");        return HRIL_ERR_GENERIC_FAILURE;    }    struct HdfSBuf *dataSbuf = ParcelToSbuf(parcel.get());    HRilResponseHeadInfo headInfo = {0};    headInfo.slotId = GetSlotId();    headInfo.type = (HRilResponseTypes)indType;    if (!HdfSbufWriteUnpadBuffer(dataSbuf, (const uint8_t *)&headInfo, sizeof(HRilResponseHeadInfo))) {        HdfSbufRecycle(dataSbuf);        return HRIL_ERR_GENERIC_FAILURE;    }    smsMessageInfo.Marshalling(*parcel.get());    indType = static_cast(ConvertIntToRadioNoticeType(indType));   //通过IPC上报数据到core_service   if (DataSbuf(dataSbuf, indType) == HRIL_ERR_GENERIC_FAILURE) {        TELEPHONY_LOGE("DataSbuf in NewSmsNotify is failed!");        return HRIL_ERR_GENERIC_FAILURE;    }    return HRIL_ERR_SUCCESS;}

在 NewSmsNotify 函数我们主要将短信 PDU、ID、类型序列化通过 IPC 上传到 core_service。core_service 在收到上报数据也只是转发到短彩信服务,这部分内容自行分析即可。

四、总结

开源项目的输出就是为了给开发者提供更多的学习途径,文本详细介绍了 OpenHarmony 电话子系统短彩信模块,从 JS 到 Framework 短彩信服务,以及跟 CoreService(核心服务)、RilAdapter 交互流程分析;提取出比较重要的代码片段分析了短信发送和短信接收流程,最终完成了整个框架层的调用过程。希望通过对 OpenHarmony 短彩信 Framework 层代码的分析和解读,能帮助广大开发者更全面地认识 OpenHarmony 短彩信模块的功能业务流程,更容易地适配自家的 modem 硬件,以此达到快速开发和优化功能的目的。

作为 OpenHarmony 开源项目的共建单位之一,深开鸿有责任、有义务、更有能力持续完善 OpenHarmony 的技术能力、不断以创新技术加速开源生态建设的布局。希望有更多的开发者参与到 OpenHarmony 生态的共建,共同推动生态繁荣。

审核编辑 :李倩


声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 源码
    +关注

    关注

    8

    文章

    571

    浏览量

    28574
  • OpenHarmony
    +关注

    关注

    23

    文章

    3202

    浏览量

    15151

原文标题:OpenHarmony源码解析之电话子系统——短彩信

文章出处:【微信号:gh_e4f28cfa3159,微信公众号:OpenAtom OpenHarmony】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    鸿蒙OpenHarmony技术:【设备互信认证】

    OpenHarmony中,设备互信认证模块作为安全子系统的子模块,负责设备间可信关系的建立、维护、使用、撤销等全生命周期的管理,实现可信设备间的互信认证和安全会话密钥协商,是搭载
    的头像 发表于 03-25 17:04 191次阅读
    鸿蒙<b class='flag-5'>OpenHarmony</b>技术:【设备互信认证】

    鸿蒙开发图形图像:【图形子系统

    图形子系统主要包括UI组件、布局、动画、字体、输入事件、窗口管理、渲染绘制等模块,构建基于轻量OS应用框架满足硬件资源较小的物联网设备或者构建基于标准OS的应用框架满足富设备的OpenHarmony
    的头像 发表于 03-23 16:50 332次阅读
    鸿蒙开发图形图像:【图形<b class='flag-5'>子系统</b>】

    鸿蒙开发实战:【电话服务子系统

    电话服务子系统,提供了一系列的API用于获取无线蜂窝网络和SIM卡相关的一些信息。应用可以通过调用API来获取当前注册网络名称、网络服务状态、信号强度以及SIM卡的相关信息。
    的头像 发表于 03-14 21:49 85次阅读
    鸿蒙开发实战:【<b class='flag-5'>电话</b>服务<b class='flag-5'>子系统</b>】

    【工作准备】OpenHarmony鸿蒙操作系统开发——基础必备软件

    前言 在下根据多年 OpenHarmony 内核及多个子系统的开发经验,将用到的必备工具软件列出一张清单,供新同学参考。如何精进?请自行搜索专业手册进行学习,我是碰到什么问题、需要什么小功能然后
    的头像 发表于 02-23 15:51 465次阅读
    【工作准备】<b class='flag-5'>OpenHarmony</b>鸿蒙操作<b class='flag-5'>系统</b>开发——基础必备软件

    启动System Init进入OpenHarmony系统过程分析与适配

    和内核等配置信息,其中各组件及特性需要根据设备需求配置,新增的子系统模块组件可以配置到此文件中。如果配置到rich.json等将要继承的配置文件也可以起作用,但放到此文件/对应的配置文件更规范。如果
    发表于 01-26 10:04

    IP矿用电话模块SV-2800VP系列设计

    打造一套低延迟、高效率、高灵活和多扩展的IP矿用广播对讲系统,亦可对传统煤矿电话系统加装此模块,进行智能化数字化升级。 SV-2800VP系列模块
    发表于 10-24 15:17 0次下载

    新接触openharmony原子化模块

    看了很多文章,openharmony3.2支持arkts开发原子化服务。但好像视频没有多少是关于Openharmony原子化服务的,视频都是harmonyOS的,我想问问各位大佬们,我能看os的原子化服务视频,然后做出来,运行的时候改为
    发表于 10-20 11:29

    Linux内核之LED子系统(二)

    这里说一说LED子系统的一些核心源代码文件,是如何实现LED子系统
    发表于 10-02 16:55 271次阅读
    Linux内核之LED<b class='flag-5'>子系统</b>(二)

    Qt For OpenHarmony

    声明周期管理,Qt GUI等模块通过Qt已完成的平台抽象层相关类实现对系统底层的调用,其中比较重要的实现类包括:EventDispatcher(实现系统底层事件接入)、BackingS
    发表于 09-15 15:39

    【触觉智能 Purple Pi OH 开发板体验】修改OpenHarmony 设备厂家名称 、硬件版本号 等系统属性详细步骤

    本文将介绍如下内容: 1.如何修改OpenHarmony系统属性,例如 设备厂家名称 、设备品牌名称 等。 2.在应用中获取系统属性信息 1.OpenHarmony
    发表于 09-09 22:44

    Arm Corstone SSE-700子系统技术参考手册

    Arm Corstone SSE-700是一个灵活的子系统,旨在为基于Arm Cortex‑A32、Cortex‑M或外部系统中的其他主机的丰富物联网(IoT)应用程序提供安全解决方案
    发表于 08-02 07:46

    OpenHarmony轻量系统书籍推荐《OpenHarmony轻量设备开发理论与实战》

    最近大家问的智能家居套件方面有没有可以参考的资料,这里给大家统一回复一下 推荐大家可以看这本书 《OpenHarmony轻量设备开发理论与实战》 本书系统地讲授OpenHarmony 轻量系统
    的头像 发表于 07-20 12:43 657次阅读

    OpenHarmony嵌入式系统原理与应用——基于RK2206芯片(微课视频版)》学习记录1 环境配置与源码编译

    子系统或功能 / 模块OpenHarmony 在传统的单设备系统能力的基础上,创造性地提出了基于同一套系统能力、适配多种终端形态的理念,
    发表于 06-25 11:26

    OpenHarmony 4.0 Beta1发布,邀您体验

    版本如期而至。4.0 Beta1版本在3.2 Release版本基础上,继续提升标准系统的ArkUI、应用框架、图形媒体等子系统能力,并提供首批API Level 10接口。 作为
    的头像 发表于 06-08 18:30 393次阅读

    如何使用cpp编写用于小型系统的app

    Ability 子系统是管理 OpenHarmony 应用运行状态的开发框架;包管理子系统OpenHarmony 为开发者提供的安装包管理框架;图形 UI
    的头像 发表于 04-25 09:47 576次阅读
    如何使用cpp编写用于小型<b class='flag-5'>系统</b>的app