摘要

本文通过蜂窝数据上网的适配实践操作梳理了OpenHarmony 3.2Release 适配8541E芯片modem的方法。

本文描述的厂商库适配对接流程,不止适用于8541e,也适用于该芯片厂家的其他芯片,如7863、7885等,虽然部分具体的接口或底层驱动会有差异。

 

一、蜂窝数据上网可选方案分析

1、通过命令行脚本直接操作modem网卡方案:

底层modem适配好Linux modem网卡驱动后,在开机自启动shell脚本中,通过命令直接操作modem网卡,来开启拨号,获取或配置网络地址以及路由信息。该方案优点:适配工作量小,适合开机即启动数据上网的快速低成本物联网。缺点:没有图形化用户界面以及状态显示,无法提供给普通用户自行开关数据的功能,断网故障初步分析也需要专业人员进行处理。该方案不涉及OH框架以及应用,8541E modem这边通过以下命令来验证网卡驱动是否移植OK。其它芯片具体咨询对应的芯片厂商或模组厂商。

#8541E modem 举例电信卡,采用M-ETHER协议上网为例:
echo -e "AT+CGACT=0,1\\r" > /dev/stty_lte31
echo -e "AT+CFUN=1\\r" > /dev/stty_lte31
echo -e "AT+CGDCONT=1,\"IPV4V6\",\"ctnet\",\"\",0,0\\r" > /dev/stty_lte31                                          电信
            #echo -e "AT+CGDCONT=1,\"IPV4V6\",\"cmnet\",\"\",0,0\\r" > /dev/stty_lte31                             移动
            #echo -e "AT+CGDCONT=1,\"IPV4V6\",\"3gnet\",\"\",0,0\\r" > /dev/stty_lte31                             联通
echo -e "AT+CGDATA=\"M-ETHER\",1\\r" > /dev/stty_lte31
echo -e "AT+CGCONTRDP=1\\r" > /dev/stty_lte31
ifconfig seth_lte0 up
ifconfig seth_lte0 10.63.162.238
​
###---这里的10.63.162.238 ip地址来源于执行echo -e "AT+CGCONTRDP=1\\r" > /dev/stty_lte31后获取的ip信息,方法如下:---###
###新开一个hdc shell窗口,执行cat /dev/stty_lte31,当执行echo -e "AT+CGCONTRDP=1\\r" > /dev/stty_lte31后,新窗口打印带IP的信息,如下:---###
###---+CGCONTRDP: 1,5,CTNET.MNC011.MCC460.GPRS,10.63.162.238.255.0.0.0,,202.103.24.68,202.103.44.150,,,,,0---###
###---+CGCONTRDP: 1,5,CTNET.MNC011.MCC460.GPRS,0.0.0.0.0.0.0.0.23.116.14.60.44.32.224.197.0.0.0.0.0.0.0.0.0.0.0.0.0.0.---###
###---0.0,,36.14.0.13.16.0.1.0.0.0.0.0.0.0.0.6,36.14.0.13.0.0.1.0.0.0.0.0.0.0.0.6,,,,,0---###
###---OK---###
​
busybox ip route add 10.63.162.238 dev seth_lte0 proto static scope link
busybox ip route add default dev seth_lte0 scope link
busybox ip link set arp off dev seth_lte0
# ping www.baidu.com
Ping www.baidu.com (14.119.104.189): 56(84) bytes.
64 bytes from 14.119.104.189: icmp_seq=1 ttl=0 time=36 ms
64 bytes from 14.119.104.189: icmp_seq=2 ttl=0 time=39 ms
64 bytes from 14.119.104.189: icmp_seq=3 ttl=0 time=35 ms
​
--- 14.119.104.189 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss
round-trip min/avg/max = 0/0/36 ms

 

2、通过OpenHarmony telephony子系统操作蜂窝数据方案:

适配电话子系统RIL适配层,能提供友好通用交互界面,能通过OpenHarmony系统的通用设置界面的移动数据进行数据开关设置,能查看到当前运营商信息以及信号情况。也方便后期补充适配电话短信模块功能,从而提供更全面的整个电话子系统功能。

 

二、OpenHarmony telephony子系统操作蜂窝数据的适配修改:

 

1、适配框图介绍:

OH适配代码的开发集中在vendor adapter层,通过开发针对具体厂商库的OH适配代码,实现OH框架层与具体厂商驱动之间的对接。

image-20230720172156677

 

2、适配概要介绍:

2.1、原厂驱动适配:

原厂将对应的modem底层驱动服务进程以及库编译移植到OpenHarmony下。将编译好的驱动服务进程bin文件、so库文件、开机启动权限配置文件以及bin启动cfg脚本文件以及对应的配置文件,添加到vendor/厂商名/产品名/目录下。并添加进编译系统中。让编译系统将这些文件分别打包进镜像中。镜像烧写后,开机启动的权限配置以及bin自启动cfg脚本文件要安装到/system/etc/init/下。系统启动后根据原厂驱动的确认方法,确保底层modem驱动服务进程正确启动。

2.1、配置设备节点:

在“vendor/厂商名/产品名/hdf_config/uhdf/device_info.hcs”文件下配置riladapter_host设备节点。修改设备节点配置文件以后,需要删除源码工程中已经生成的hdf_default.hcb和hdf_devhost.cfg,然后重新编译。

        riladapter :: host {
            hostName = "riladapter_host";
            priority = 50;
            input_device :: device {
                device0 :: deviceNode {
                    policy = 2;
                    priority = 100;
                    moduleName = "libril_driver.z.so";
                    serviceName = "ril_service";
                }
            }
        }

 

2.2、配置厂商RIL适配库路径以及库名称:

在适配层添加modem.para以及对应的modem.para.dac,并添加编译系统,最后打包到/vendor/etc/param/下。让系统开机即设置modem厂商库的路径以及库名。

在modem.para指定厂商库全路径:

persist.sys.radio.vendorlib.path=/vendor/lib/lib****_ril_adapter*.z.so

在modem.para.dac指定系统参数persist.sys.radio.vendorlib.path的操作权限:

persist.sys.radio.vendorlib.path=riladapter_host:riladapter_host:0777

2.3、修改base/startup/init/services/etc/passwd中riladapter_host权限,使之有权限能够操作底层modem驱动设备节点

riladapter_host:x:0:3032:::/bin/false

2.4、开发编写OH适配层源码:

新增ril.h、at_support.h、at_support.c、vendor_adapter.c、vendor_report.h、vendor_report.c、vendor_util.c、at_network.h、at_network.c、at_data.h、at_data.c、at_modem.h、at_modem.c、at_call.h、at_call.c、at_sim.h、at_sim.c、at_sms.h、at_sms.c。其中ril.h来自于芯片原厂,提供了与modem驱动层交互的数据结构、数据定义以及函数定义等。根据OH hril层框架接口以及对应的厂商库驱动接口,适配开发上下层衔接函数。

适配开发与测试可以分级开发,首先完成at_support.c、vendor_adapter.c、vendor_report.c适配开发。然后再适配at_modem模块以及at_sim模块的sim卡状态函数接口,然后适配at_network模块。这样就先完成了具体业务适配前的设备状态->sim卡状态->基站驻网状态基础模块的适配。蜂窝数据上网业务只需要再适配at_data模块,裁减掉上层拨号以及联系人app即可。剩余的at_sim、at_call、at_sms业务可以根据需求逐步增补适配。

 

3、OH适配代码开发说明:

3.1、适配代码开发概要说明:

3.1.1、指定厂商驱动库加载路径,完成库加载以及初始化函数开发。并在库加载以及初始化过程中完成上下层互调的各自回调函数指针的交换。

/*------文件名-at_support.c------*/
static RIL_Respone g_rilRespone = {NULL, 0, false};
static const RIL_RadioFunctions *g_RadioFunctions = NULL;
static int token_value = 0;
static RIL_Token token_t0 = (void *)(&token_value);
void SetRadioFunctions(const RIL_RadioFunctions *pRadioFunctions)
{
    g_RadioFunctions = pRadioFunctions;
}
......
static void ReqGetSimStatusReponse(RIL_Token t, RIL_Errno e, void *response, size_t len)
{
    g_rilRespone.replied = true;
    struct ReportInfo reportInfo;
    HRilCardState cardState = {0};
    RIL_CardStatus_v6 *pRIL_CardStatus_v6 = NULL;
    reportInfo = CreateReportInfo(g_rilRespone.requestInfo, RilErrToHrilErr(e), HRIL_RESPONSE, 0);
    if (response != NULL) {
        pRIL_CardStatus_v6 = (RIL_CardStatus_v6*)(response);
        cardState.index = 0;
        cardState.simState = pRIL_CardStatus_v6->card_state;
        cardState.simType = 1;
        TELEPHONY_LOGI("ReqGetSimStatus:simState:%{public}d,len = %{public}zu", cardState.simState, len);
        OnSimReport(GetSlotId(g_rilRespone.requestInfo), reportInfo, (const uint8_t *)&cardState, sizeof(HRilCardState));
    } else {
        TELEPHONY_LOGI("ReqGetSimStatus:NULL");
        OnSimReport(GetSlotId(g_rilRespone.requestInfo), reportInfo, NULL, 0);    
    }
}
​
void OnRequestComplete(RIL_Token t, RIL_Errno e, void *response, size_t len)
{
    switch (g_rilRespone.rilRequestId) {
        case RIL_REQUEST_DEACTIVATE_DATA_CALL:
        case RIL_REQUEST_SET_INITIAL_ATTACH_APN:
        case RIL_REQUEST_RADIO_POWER:
            ReqNoDetailedDataRespone(t, e, response, len);
            break;
        case RIL_REQUEST_SETUP_DATA_CALL:
            ReqActivatePdpContextRespone(t, e, response, len);
            break;
        case RIL_REQUEST_GET_IMEI:
            ReqGetImeiRespone(t, e, response, len);
            break;
        ......
        default:
            break;
    }
}
​
void RequestTimedCallback(RIL_TimedCallBack callback, void *param, const struct timeval *relativeTime)
{
    TELEPHONY_LOGE("RequestTimedCallback 111 : %{public}p", callback);
    OnTimerCallback((HRilCallbackFun)callback, (uint8_t *)param, relativeTime);
    return;
}
​
void OnRequestAck(RIL_Token t)
{
    return;
}
......
​
​
/*------文件名-vendor_adapter.c------*/
#define RIL_LIB_PATH "/vendor/lib/lib****-ril.z.so"
typedef const RIL_RadioFunctions* (*InitFunc)(const struct RIL_Env* env);
......
static HRilOps g_hrilOps = {
    .callOps = &g_callReqOps,
    .simOps = &g_simReqOps,
    .smsOps = &g_smsReqOps,
    .networkOps = &g_networkReqOps,
    .dataOps = &g_dataReqOps,
    .modemOps = &g_modemReqOps,
};
​
static struct RIL_Env g_rilEnv = {
    .OnRequestComplete = OnRequestComplete,
    .OnUnsolicitedResponse = OnUnsolicitedResponse,
    .RequestTimedCallback = RequestTimedCallback,
    .OnRequestAck = OnRequestAck,
};
​
......
​
bool LoadRillib(void)
{
    TELEPHONY_LOGI("LoadRillib %{public}s", RIL_LIB_PATH);
    void *dlHandle = dlopen(RIL_LIB_PATH, RTLD_NOW);
    if (dlHandle == NULL) {
        TELEPHONY_LOGE("dlopen %{public}s is fail. %{public}s", RIL_LIB_PATH, dlerror());
        return false;
    }
​
    InitFunc rilInit = (InitFunc)dlsym(dlHandle, "RIL_Init");
    if (rilInit == NULL) {
        dlclose(dlHandle);
        TELEPHONY_LOGE("RIL_Init not defined or exported");
        return false;
    }
    const RIL_RadioFunctions* funcs = (rilInit(&g_rilEnv));
    if(funcs == NULL) {
        dlclose(dlHandle);
        TELEPHONY_LOGE("rilInit is null");
        return false;
    }
    SetRadioFunctions(funcs);
    TELEPHONY_LOGI("ril lib version:%{public}d", funcs->version);
    TELEPHONY_LOGI("ril lib onrequest addr:%{public}p", funcs->onRequest);
​
    return true;
}
​
const HRilOps *RilInitOps(const struct HRilReport *reportOps)
{
    TELEPHONY_LOGI("OH Enable Ril");
    if(!LoadRillib()) {
        TELEPHONY_LOGE("load sprd-ril lib failed !");
        return NULL;
    }
    SetReportOps(reportOps);
    // GetRadioState();
    return &g_hrilOps;
}

3.1.2、封装通用指令发送函数;

/*------文件名-at_support.c------*/
static RIL_Respone g_rilRespone = {NULL, 0, false};
static const RIL_RadioFunctions *g_RadioFunctions = NULL;
static int token_value = 0;
static RIL_Token token_t0 = (void *)(&token_value);
void SetRadioFunctions(const RIL_RadioFunctions *pRadioFunctions)
{
    g_RadioFunctions = pRadioFunctions;
}
......
​
void SendRilCommand(ModuleType moduleType, const ReqDataInfo *requestInfo, int rilRequestId, void *data, size_t datalen)
{
    SetRilRespone(requestInfo, rilRequestId, false);
    if (g_RadioFunctions != NULL) {
        g_RadioFunctions->onRequest(rilRequestId, data, datalen, token_t0, RIL_SOCKET_1);
        if (!g_rilRespone.replied) {
            OnReportErrorByModuleType(moduleType, requestInfo, HRIL_ERR_INVALID_RESPONSE);
        }
    }
}
​

 

3.1.3、根据OH hril框架层请求接口函数,到ril.h中去找寻功能相同或相近的接口请求,封装成适配接口函数。接口函数将上层接口参数封装成modem驱动层对应的请求需要的参数形式,然后转发给modem驱动层。

/*------文件名-at_data.c------*/
......
void ReqSetInitApnInfo(const ReqDataInfo *requestInfo, const HRilDataInfo *data)
{
    TELEPHONY_LOGE("ReqSetInitApnInfo AT ....");
    struct ReportInfo reportInfo;
    if (data == NULL) {
        TELEPHONY_LOGE("data is null!!!");
        reportInfo = CreateReportInfo(requestInfo, HRIL_ERR_INVALID_PARAMETER, HRIL_RESPONSE, 0);
        OnDataReport(GetSlotId(requestInfo), reportInfo, NULL,0);
        return;
    }
    RIL_InitialAttachApn apnset = {0};
    apnset.apn =  data->apn;
    apnset.protocol = data->type;
    apnset.authtype = data->verType;
    apnset.username = data->userName;
    apnset.password = data->password;
    apnset.reqApn = "";
    apnset.reqProt = "";
    SendRilCommand(RIL_DATA_MODULE, requestInfo, RIL_REQUEST_SET_INITIAL_ATTACH_APN, &apnset, sizeof(RIL_InitialAttachApn));
    TELEPHONY_LOGE("ReqSetInitApnInfo  end....");
}
......

 

3.1.4、根据modem驱动层请求对应的响应数据以及响应说明,封装成请求的响应上报函数。请求说明,响应数据的数据结构以及说明一般定义在ril.h中。响应上报函数将下层请求的响应数据,封装成OH hril框架层对应的请求的响应函数所需的参数形式,转发给telephony上层。

/*------文件名-at_support.c------*/
static void ReqGetImeiRespone(RIL_Token t, RIL_Errno e, void *response, size_t len)
{
    g_rilRespone.replied = true;
    ReportInfo reportInfo = CreateReportInfo(g_rilRespone.requestInfo, RilErrToHrilErr(e), HRIL_RESPONSE, 0);
    if (response == NULL) {
        if(len != 0) {
            reportInfo.error = HRIL_ERR_INVALID_RESPONSE;
            OnModemReport(GetSlotId(g_rilRespone.requestInfo), reportInfo, NULL, 0);
            TELEPHONY_LOGI("ReqGetImeiRespone ,response:%{public}p,len:%{public}zu, err:%{public}d", response, len, e);
        } else {
            char *imeiStr = " ";
            len = sizeof(" ");
            OnModemReport(GetSlotId(g_rilRespone.requestInfo), reportInfo, (const uint8_t *)imeiStr, len);
            TELEPHONY_LOGI("ReqGetImeiRespone ,imei:%{public}s, len:%{public}zu, err:%{public}d", (char *)imeiStr, len, e);
        }
    } else {
        OnModemReport(GetSlotId(g_rilRespone.requestInfo), reportInfo, (const uint8_t *)response, len);
        TELEPHONY_LOGI("ReqGetImeiRespone ,imei:%{public}s, len:%{public}zu, err:%{public}d,t:%{public}p", (char *)response, len, e, t);
    }
}
......
void OnRequestComplete(RIL_Token t, RIL_Errno e, void *response, size_t len)
{
    switch (g_rilRespone.rilRequestId) {
        case RIL_REQUEST_DEACTIVATE_DATA_CALL:
        case RIL_REQUEST_SET_INITIAL_ATTACH_APN:
        case RIL_REQUEST_RADIO_POWER:
            ReqNoDetailedDataRespone(t, e, response, len);
            break;
        ......
        case RIL_REQUEST_GET_IMEI:
            ReqGetImeiRespone(t, e, response, len);
            break;
        ......
        default:
            break;
    }
}
......

3.1.5、根据OH hril框架层所需的主动通知函数,到ril.h中去找寻功能相同或相近的主动通知请求,封装成主动通知函数。主动通知函数将下层主动通知的数据,封装成OH hril框架层对应的主动通知函数所需的参数形式,主动通知给telephony上层。

/*------文件名-at_report.c------*/
HRilRadioState GetRadioState()
{
    TELEPHONY_LOGI("GetRadioState enter");
    HRilRadioState hrilRadioState = RILRadioStateToHrilRadioState(GetRadioFunctions()->onStateRequest(RIL_SOCKET_1));
    ReportInfo reportInfo = {0};
    reportInfo.notifyId = HNOTI_MODEM_RADIO_STATE_UPDATED;
    reportInfo.type = HRIL_NOTIFICATION;
    reportInfo.error = HRIL_ERR_SUCCESS;
    OnModemReport(GetSlotId(NULL), reportInfo, (const uint8_t *)&hrilRadioState, sizeof(HRilRadioState));
    return hrilRadioState;
}
......
void OnUnsolicitedResponse(int unsolResponse, const void *data, size_t datalen, RIL_SOCKET_ID socket_id)
{
    if (socket_id != RIL_SOCKET_1) {
        TELEPHONY_LOGI("OnUnsolicitedResponse socket_id = %{public}d error.", socket_id);
        return;
    }
    switch (unsolResponse) {
        case RIL_UNSOL_DATA_CALL_LIST_CHANGED:
        {
            TELEPHONY_LOGI("case RIL_UNSOL_DATA_CALL_LIST_CHANGED");
            DealWithDataCallListChanged(data, datalen);
        }
        break;
        case RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED:
        {
            TELEPHONY_LOGI("case RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED");
            GetRadioState();
        }
        break;
        case RIL_UNSOL_VOICE_RADIO_TECH_CHANGED:
        {
            HRilVoiceRadioInfo hrilVoiceInfo;
            hrilVoiceInfo.actType = *(HRilRadioTech*)data;
            struct ReportInfo reportInfo = {0};
            reportInfo.notifyId = HNOTI_MODEM_VOICE_TECH_UPDATED;
            reportInfo.type = HRIL_NOTIFICATION;
            reportInfo.error = HRIL_ERR_SUCCESS;
            OnModemReport(GetSlotId(NULL), reportInfo, (const uint8_t*)&hrilVoiceInfo, sizeof(HRilVoiceRadioInfo));
        }
        break;
        ......
        default:
            TELEPHONY_LOGI("Notification id = %{public}d,not processed", unsolResponse);
        break;
    }
    return;
}
......

 

3.2、部分具体适配接口枚举:

3.2.1、梳理了部分OH请求接口函数以及对应的厂商ID:

接口类型 OH请求接口函数 厂商接口ID
modem SetRadioState RIL_REQUEST_RADIO_POWER
  GetRadioState currentState(RIL_SOCKET_ID socket_id)
  GetImei RIL_REQUEST_GET_IMEI RIL_REQUEST_GET_IMEISV
  GetMeid RIL_REQUEST_DEVICE_IDENTITY
  GetVoiceRadioTechnology RIL_REQUEST_VOICE_RADIO_TECH
sim GetSimStatus RIL_REQUEST_GET_SIM_STATUS
  GetSimIO RIL_REQUEST_SIM_IO
  GetImsi RIL_REQUEST_GET_IMSI
  GetSimLockStatus RIL_REQUEST_QUERY_FACILITY_LOCK
  SetSimLock RIL_REQUEST_SET_FACILITY_LOCK
  ChangeSimPassword RIL_REQUEST_CHANGE_BARRING_PASSWORD
  UnlockPin RIL_REQUEST_ENTER_SIM_PIN
  UnlockPuk RIL_REQUEST_ENTER_SIM_PUK
  GetSimPinInputTimes 暂时未找到ril层匹配的ID
  UnlockPin2 RIL_REQUEST_ENTER_SIM_PIN2
  UnlockPuk2 RIL_REQUEST_ENTER_SIM_PUK2
  GetSimPin2InputTimes 暂时未找到ril层匹配的ID
  SetActiveSim 暂时未找到ril层匹配的ID
  SimStkSendTerminalResponse RIL_REQUEST_STK_SEND_TERMINAL_RESPONSE
  SimStkSendEnvelope RIL_REQUEST_STK_SEND_ENVELOPE_COMMAND
  SimStkIsReady RIL_REQUEST_REPORT_STK_SERVICE_IS_RUNNING
  SetRadioProtocol 暂时未找到ril层匹配的ID
  SimOpenLogicalChannel RIL_REQUEST_SIM_OPEN_CHANNEL
  SimCloseLogicalChannel RIL_REQUEST_SIM_CLOSE_CHANNEL
  SimTransmitApduLogicalChannel RIL_REQUEST_SIM_TRANSMIT_APDU_CHANNEL
  UnlockSimLock 暂时未找到ril层匹配的ID
network GetSignalStrength RIL_REQUEST_SIGNAL_STRENGTH
  GetImsRegStatus RIL_REQUEST_IMS_REGISTRATION_STATE
  GetCsRegStatus RIL_REQUEST_VOICE_REGISTRATION_STATE
  GetPsRegStatus RIL_REQUEST_DATA_REGISTRATION_STATE
  GetOperatorInfo RIL_REQUEST_OPERATOR
  GetNetworkSearchInformation RIL_REQUEST_QUERY_AVAILABLE_NETWORKS
  GetNetworkSelectionMode RIL_REQUEST_QUERY_NETWORK_SELECTION_MODE
  SetNetworkSelectionMode RIL_REQUEST_SET_NETWORK_SELECTION_AUTOMATIC
  SetPreferredNetwork RIL_REQUEST_SET_PREFERRED_NETWORK_TYPE
  GetPreferredNetwork RIL_REQUEST_GET_PREFERRED_NETWORK_TYPE
  GetNeighboringCellInfoList RIL_REQUEST_GET_CELL_INFO_LIST
  GetCurrentCellInfo RIL_REQUEST_GET_NEIGHBORING_CELL_IDS
  GetRadioCapability RIL_REQUEST_GET_RADIO_CAPABILITY
  GetPhysicalChannelConfig 暂时未找到ril层匹配的ID
  SetLocateUpdates 暂时未找到ril层匹配的ID
data ReqSetInitApnInfo RIL_REQUEST_SET_INITIAL_ATTACH_APN
  ReqActivatePdpContext RIL_REQUEST_SETUP_DATA_CALL
  ReqDeactivatePdpContext RIL_REQUEST_DEACTIVATE_DATA_CALL
  ReqGetPdpContextList RIL_REQUEST_DATA_CALL_LIST
  ReqGetLinkBandwidthInfo 5G指令接口不支持,接口打桩
  ReqSetLinkBandwidthReportingRule OH打桩接口,未实现,接口打桩

3.2.2、梳理了部分OH主动通知函数以及对应的主动上报厂商ID:

接口类型 OH主动通知接口函数 厂商主动通知ID
  RadioStateUpdated RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED
  VoiceRadioTechUpdated RIL_UNSOL_VOICE_RADIO_TECH_CHANGED
sim SimStateUpdated RIL_UNSOL_RESPONSE_SIM_STATUS_CHANGED
  SimStkSessionEndNotify RIL_UNSOL_STK_SESSION_END
  SimStkProactiveNotify RIL_UNSOL_STK_PROACTIVE_COMMAND
  SimStkAlphaNotify RIL_UNSOL_STK_CC_ALPHA_NOTIFY
network NetworkCsRegStatusUpdated RIL_UNSOL_RESPONSE_VOICE_NETWORK_STATE_CHANGED
  SignalStrengthUpdated RIL_UNSOL_SIGNAL_STRENGTH
  NetworkTimeUpdated RIL_UNSOL_NITZ_TIME_RECEIVED(不完全匹配)
  NetworkCurrentCellUpdated 未找到RIL相应的上报接口
  NetworkPsRegStatusUpdated 未找到RIL相应的上报接口
  NetworkImsRegStatusUpdated RIL_UNSOL_RESPONSE_IMS_NETWORK_STATE_CHANGED
  NetworkPhyChnlCfgUpdated RIL_EXT_UNSOL_PHYSICAL_CHANNEL_CONFIG
  NetworkTimeZoneUpdated RIL_UNSOL_NITZ_TIME_RECEIVED (不完全匹配)
data PdpContextListUpdated RIL_UNSOL_DATA_CALL_LIST_CHANGED

3.2.3、接口适配注意事项:

1、请求接口函数找不到底层对应的请求id时,如果底层厂商库不能补齐对应的接口,可以尝试根据情况在请求函数中原地返回无效响应。这个凡是OH modem框架层工作时一定会执行的请求函数,即使找不到底层匹配的请求处理函数,也必须给与回应,否则会引发请求id一直被记录,无法释放,造成内存泄漏。无效响应举例:

void ReqSetNotificationFilter(const ReqDataInfo *requestInfo, const int32_t *newFilter)
{
    if (requestInfo == NULL) {
        TELEPHONY_LOGE("ReqSetNotificationFilter requestInfo is NULL");
        return;
    }
    if (newFilter == NULL) {
        struct ReportInfo reportInfo = CreateReportInfo(requestInfo, HRIL_ERR_INVALID_PARAMETER, HRIL_RESPONSE, 0);
        OnNetworkReport(requestInfo->slotId, reportInfo, NULL, 0);
        return;
    }
    TELEPHONY_LOGI("ReqSetNotificationFilter unsupported");
    struct ReportInfo reportInfo = CreateReportInfo(requestInfo, HRIL_ERR_INVALID_RESPONSE, HRIL_RESPONSE, 0);
    OnNetworkReport(requestInfo->slotId, reportInfo, NULL, 0);
}

2、请求接口函数对应的响应函数返回的数据不完全时,可以根据业务需求尝试只返回部分有效数据,其余部分填写默认值。因为有部分的响应数据可能只在特定业务的特定场景下才用到,实际该特定业务情况不会发生,所以最终可能不会影响到适配后的功能体验。

3、OH 上层不需要的主动通知消息,当modem底层有主动上报时,可以忽略,不做适配处理。

4、modem底层主动上报的通知数据不完全与OH hril层匹配,或数据缺失时,如果底层厂商库不能补齐对应的接口数据,可以根据业务需求尝试只返回部分有效数据,其余部分填写默认值。因为有部分的响应数据可能只在特定业务的特定场景下才用到,实际该特定业务情况不会发生,所以最终可能不会影响到适配后的功能体验。

5、由于OH hril框架层请求函数采用请求-响应在单一线程处理的同步处理机制,以及底层modem驱动层也是采用单一线程同步接收以及响应外部请求机制。在编写适配对接函数时,请遵循请求与响应的同步串行处理机制:同一时刻只有一个线程去发送请求到modem驱动层。发送完成阻塞在相同线程中等待底层modem响应(包括报错响应或超时响应)。

 

4、OH适配代码调试说明与举例:

4.1、适配接口函数开发阶段最佳调试工具:

通过在base/telephony/core_service/bundle.json中,添加base/telephony/core_service/services/tel_ril/test:tel_ril_test的编译,编译出core_service层函数接口测试工具软件。安装到system/bin/tel_ril_test用于调试。执行tel_ril_test,然后根据提示输入要调试的卡槽号,然后再选择函数对应的id号,即可调用指定的函数接口。根据日志打印,查看适配的接口数据以及信息。

4.2、适配调试问题举例:

4.2.1、路由问题处理介绍:

问题描述:

适配data模块的函数以后,不能上网,需要手动执行路由添加命令,才能上网。需要把手动执行的路由命令让电话子 系统cellularData模块自动添加到路由表。

问题截图:

image-20230720172156677

 

问题定位:

通过梳理数据开启流程日志,发现异常打印:Slot0: dataCall, capability:12, state:0, addr:10.100.43.123/8, dns: 211.137.58.20, gw: Verifying network Information(ipInfoArray or dnsInfoArray or routeInfoArray empty) 确认gw为空时,直接return,不会走到路由添加函数。

解决方案:

因厂商库采用点对点上网模式不存在gateway,故在getaway字段补充网关数据为0.0.0.0。经过测试确认添加网关数据为0.0.0.0以后,上层系统流程能将路由添加进系统,系统能够上网。

 

4.2.2、运营商信息不显示中文处理介绍:

问题描述:

锁屏界面和主界面,运营商信息栏显示CHINA MOBILE,不显示中文。

问题截图:

image-20230720172156677

 

问题定位:

对比3568日志发现: 3568:OperatorName::GetCustomName CMCC:中国移动 OperatorName::GetPlmn lac:0, numeric:46000, longNameRequired:1, plmn:中国移动 8541E:OperatorName::GetCustomName CMCC: OperatorName::GetPlmn lac:0, numeric:46000, longNameRequired:1, plmn:CHINA MOBILE 可知GetCustomName获取名称为空,分析名称数据来源:ResourceUtils::Get().GetValueByNamestd::string(ResourceUtils::CMCC, name);

确认未能从ResourceUtils中拿到中文数据。分析资源包:确认/system/app/ohos.telephony.resources指向的中文资源包base/telephony/core_service/telephonyres/main/resources/zh_CN/element/string.json在源码工程中有,但是预安装配置vendor/厂家名/产品名/preinstall-config/install_list.json中/system/app/ohos.telephony.resources被裁减。

问题解决:

vendor/厂家名/产品名/preinstall-config/install_list.json里 增补添加电话子系统资源包的安装后OK:

    {    
        "app_dir" : "/system/app/ohos.telephony.resources",
​
        "removable" : false
​
    },

4.2.3、RIL打开/dev/xxxx_lte0和/dev/xxxx_lte3设备文件失败:

问题描述:

系统启动过程后,riladapter_host进程启动失败。

问题定位截图:

image-20230720172156677

 

问题定位:

梳理RIL日志发现:RIL Opening AT interface /dev/xxx_lte3. retrying...。检查base/startup/init/services/etc/passwd中riladapter_host权限,发现权限依然为3032:3032,漏修改权限了:

riladapter_host:x:3032:3032:::/bin/false

 

问题解决:

修改base/startup/init/services/etc/passwd中riladapter_host权限,为root权限,问题解决,如下:

riladapter_host:x:0:3032:::/bin/false
Logo

社区规范:仅讨论OpenHarmony相关问题。

更多推荐