【笔记】APN 配置参数 bitmask 数据转换(Android & KaiOS)

一、参数说明

(一)APN配置结构对比

| 平台 | Android | KaiOS |
| 文件类型 | xml | json |

结构 每个<apn>标签是一条APN,包含完成的信息 层级数组结构,使用JSON格式的数据。最外层是mcc,其次mnc,最后APN用数组形式配置(每个APN都是一个对象,不含mccmnc属性)。
Android: apns-conf.xml
XML 复制代码
<!-- Android: apns-conf.xml -->
<?xml version="1.0" encoding="utf-8"?>
<apns version="8">
  <apn carrier="T-Mobile US" mcc="001" mnc="01" apn="fast.t-mobile.com" user="" password="" server="" proxy="" port="" mmsc="http://mms.msg.eng.t-mobile.com/mms/wapenc" mmsproxy="" mmsport="" type="default,mms,supl,hipri,xcap,rcs" protocol="IPV6" roaming_protocol="IP" bearer_bitmask="" mvno_type="ecid" mvno_match_data="[4]4310260" class="" user_visible="true" user_editable="true" authtype="0"/>
</apns>
KaiOS: apns.json

特别需要注意格式(很容易出错),数组最后不用加逗号,注意大/中括号的首位一致性。

css 复制代码
{
"202": {
  "10": [
    {"carrier":"Wind Internet","apn":"gint.b-online.gr","type":["default","supl"]},
    {"voicemail":"122","type":["operatorvariant"]},
    {"carrier":"Wind MMS","apn":"mnet.b-online.gr","mmsc":"http://192.168.200.95/servlets/mms","mmsproxy":"192.168.200.11","mmsport":"9401","type":["mms"]}
  ],
  "01": [
    {"carrier":"Cosmote Wireless Internet","apn":"","type":["ia"]},
    {"voicemail":"123","type":["operatorvariant"]},
    {"carrier":"Cosmote Wireless Internet","apn":"internet","type":["default","supl"]},
    {"carrier":"Cosmote Mms","apn":"mms","mmsc":"http://mmsc.cosmote.gr:8002","mmsproxy":"10.10.10.20","mmsport":"8080","type":["mms"]}
  ],
  "09": [
    {"carrier":"Q Internet","apn":"myq","type":["default","supl"]},
    {"voicemail":"122","type":["operatorvariant"]},
    {"carrier":"Q-Telecom MMS GPRS","apn":"q-mms.myq.gr","mmsc":"http://mms.myq.gr","mmsproxy":"192.168.80.134","mmsport":"8080","type":["mms"]}
  ]
},
"001": {
  "01": [
    {"carrier":"Testing SIM default","apn":"test","type":["default"],"protocol":"IPV4V6","roaming_protocol":"IPV4V6","user_visible":"true"},
    {"carrier":"IMS","apn":"ims","type":["ims"],"protocol":"IPV4V6","roaming_protocol":"IPV4V6","user_visible":"true"},
    {"carrier":"XCAP","apn":"xcap","type":["xcap"],"protocol":"IPV4V6","roaming_protocol":"IPV4V6","user_visible":"true","authtype":"0"}
  ]
}
}

(二)bearer配置值对比

  • 1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20 (The original bearer value in Android,位运算)
  • 1048575(The original bearer value in KaiOS using decimalism)

二、代码解析

(一)Android

待完善

(二)KaiOS(DataCallManager.jsm)

1、bitmask的进制转换

可以参考PDN建立逻辑,gecko/dom/system/gonk/radio/DataCallManager.jsm

javascript 复制代码
//DataCallManager.jsm

//检查对应的rat是否包含在此APN bearer 配置中
// Check rat value include in the bit map or not.
function bitmaskHasTech(aBearerBitmask, aRadioTech) {
  if (aBearerBitmask == 0) {
    return true;
  } else if (aRadioTech > 0) {
    return (aBearerBitmask & (1 << (aRadioTech - 1))) != 0;
  }
  return false;
}

//bearer十进制转成二进制
// Show the detail rat type.
function bitmaskToString(aBearerBitmask) {
  if (aBearerBitmask == 0 || aBearerBitmask === undefined) {
    return 0;
  }

  let val = "";
  for (let i = 1; i < RIL.GECKO_RADIO_TECH.length; i++) {
    if ((aBearerBitmask & (1 << (i - 1))) != 0) {
      val = val.concat(i + "|");
    }
  }
  return val;
}

function bearerBitmapHasCdma(aBearerBitmask) {
  return (RIL_RADIO_CDMA_TECHNOLOGY_BITMASK & aBearerBitmask) != 0;
}

bitmaskToString接口中,将bitmask转化成String时,循环RIL.GECKO_RADIO_TECH的长度次,经过位运算转换成与Android原始配置的bearer_bitmask相同格式的bearer位符,用"|"间隔rat类型位。

是如何通过RIL调用ril_consts.js内的GECKO_RADIO_TECH?

DataCallManager.jsm 中定义RIL对象,并使用XPCOMUtils.defineLazyGetter()方法来实现懒加载,即在要使用时才加载和初始化对象(只有在第一次访问该对象时才会进行初始化和加载),避免不必要的性能开销和资源浪费。

RIL对象是通过ChromeUtils.import 方法从ril_consts.js文件中导入的,该对象由ril_consts.js文件中的代码创建和初始化的。

javascript 复制代码
//DataCallManaer.jsm

"use strict";

//XPCOM 是一个用于 实现跨语言组件的技术框架。
//导入XPCOMUtils对象(工具库),简化和封装XPCOM组件的开发和使用。
//使用常量const来定义 XPCOMUtils 对象,以确保在运行时不会发生对象被重新赋值的情况。
const { XPCOMUtils } = ChromeUtils.import(
  "resource://gre/modules/XPCOMUtils.jsm"
);

//定义RIL对象,后续调用 RIL.GECKO_RADIO_TECH
XPCOMUtils.defineLazyGetter(this, "RIL", function() {
  return ChromeUtils.import("resource://gre/modules/ril_consts.js");
});


//ref RIL.GECKO_RADIO_TECH
const TCP_BUFFER_SIZES = [
  null,
  "4092,8760,48000,4096,8760,48000", // gprs
  "4093,26280,70800,4096,16384,70800", // edge
  "58254,349525,1048576,58254,349525,1048576", // umts
  "16384,32768,131072,4096,16384,102400", // is95a = 1xrtt
  "16384,32768,131072,4096,16384,102400", // is95b = 1xrtt
  "16384,32768,131072,4096,16384,102400", // 1xrtt
  "4094,87380,262144,4096,16384,262144", // evdo0
  "4094,87380,262144,4096,16384,262144", // evdoa
  "61167,367002,1101005,8738,52429,262114", // hsdpa
  "40778,244668,734003,16777,100663,301990", // hsupa = hspa
  "40778,244668,734003,16777,100663,301990", // hspa
  "4094,87380,262144,4096,16384,262144", // evdob
  "131072,262144,1048576,4096,16384,524288", // ehrpd
  "524288,1048576,2097152,262144,524288,1048576", // lte
  "122334,734003,2202010,32040,192239,576717", // hspa+
  "4096,87380,110208,4096,16384,110208", // gsm (using default value)
  "4096,87380,110208,4096,16384,110208", // tdscdma (using default value)
  "122334,734003,2202010,32040,192239,576717", // iwlan
  "122334,734003,2202010,32040,192239,576717", // ca
];


//定义了一个常量RIL_RADIO_CDMA_TECHNOLOGY_BITMASK,用于表示CDMA射频技术类型的掩码值。
//RIL.GECKO_RADIO_TECH.indexOf 查找各个CDMA技术类型在GECKO_RADIO_TECH数组中的下标值,
//-1 的目的是得到对应的掩码位数,并将此转换成掩码值,
//使用按位左移(<<)运算符得到掩码值,
//各个掩码值按位或(|)操作得到最终的掩码值。
const RIL_RADIO_CDMA_TECHNOLOGY_BITMASK =
  (1 << (RIL.GECKO_RADIO_TECH.indexOf("is95a") - 1)) |
  (1 << (RIL.GECKO_RADIO_TECH.indexOf("is95b") - 1)) |
  (1 << (RIL.GECKO_RADIO_TECH.indexOf("1xrtt") - 1)) |
  (1 << (RIL.GECKO_RADIO_TECH.indexOf("evdo0") - 1)) |
  (1 << (RIL.GECKO_RADIO_TECH.indexOf("evdoa") - 1)) |
  (1 << (RIL.GECKO_RADIO_TECH.indexOf("evdob") - 1)) |
  (1 << (RIL.GECKO_RADIO_TECH.indexOf("ehrpd") - 1));


// set to true in ril_consts.js to see debug messages
var DEBUG = RIL_DEBUG.DEBUG_RIL;   //调试用的log打印标识符

如上代码,CDMA技术类型对应的掩码值RIL_RADIO_CDMA_TECHNO如下:

is95a: 1 << (5-1) = 0x10

is95b: 1 << (6-1) = 0x20

1xrtt: 1 << (7-1) = 0x40

evdo0: 1 << (8-1) = 0x80

evdoa: 1 << (9-1) = 0x100

evdob: 1 << (14-1) = 0x2000

ehrpd: 1 << (15-1) = 0x4000

2、GECKO_RADIO_TECH 定义网络制式 (ril_consts.js)

在KaiOS中,RIL.GECKO_RADIO_TECH数组是在Gecko内核的代码中定义的,其实现位于Gecko代码库的"gecko/dom/system/gonk/radio/ril_consts.js"文件中。该文件定义了一系列RIL层的常量,包括射频技术类型、消息ID等。在该文件中,可以找到以下代码片段,其中定义了RIL.GECKO_RADIO_TECH数组的元素和顺序:

javascript 复制代码
//GECKO_RADIO_TECH 数组定义射频技术(网络连接)
this.GECKO_RADIO_TECH = [
  null,
  "gprs",     //1 GPRS
  "edge",     //2 EDGE
  "umts",     //3 UMTS
  "is95a",    //4 IS-95A
  "is95b",    //5 IS-95B
  "1xrtt",    //6 cdma1x?一种CDMA2000射频技术,是CDMAOne技术的升级版(1x Radio Transmission Technology)
  "evdo0",    //7 EVDO-0
  "evdoa",    //8 EVDO-A
  "hsdpa",    //9
  "hsupa",    //10
  "hspa",     //11
  "evdob",    //12 EVDO-B
  "ehrpd",    //13 EVDO-D
  "lte",      //14 LTE
  "hspa+",    //15 HSPA+
  "gsm",      //16 GSM
  "tdscdma",  //17 TD-SCDMA
  "iwlan",    //18 iWLAN(wifi)
  "lte_ca",   //19 LTE_CA
];

//定义遵循的协议类型
this.GECKO_PROFILE_INFO_TYPE_COMMON = 0;
this.GECKO_PROFILE_INFO_TYPE_3GPP = 1;
this.GECKO_PROFILE_INFO_TYPE_3GPP2 = 2;
3、DataCall 对 rat 的使用案例
javascript 复制代码
  dataRegistrationChanged(aRadioTech) {
    let targetBearer;
    if (this.apnSetting.bearer === undefined) {
      targetBearer = 0;
    } else {
      targetBearer = this.apnSetting.bearer;
    }
    if (DEBUG) {
      this.debug(
        "dataRegistrationChanged: targetBearer: " +
          bitmaskToString(targetBearer)
      );
    }

    if (bitmaskHasTech(targetBearer, aRadioTech)) {
      // Ignore same rat type. Let handler process the retry.
    } else {
      if (DEBUG) {
        this.debug(
          "dataRegistrationChanged: current APN do not support this rat reset DC. APN:" +
            JSON.stringify(this.apnSetting)
        );
      // Clean the requestedNetworkInterfaces due to current DC can not support this rat under DC retrying state.
      // Let handler process the retry.
      let targetRequestedNetworkInterfaces = this.requestedNetworkInterfaces.slice();
      for (let networkInterface of targetRequestedNetworkInterfaces) {
        this.disconnect(networkInterface);
      }
    }
  },

gecko/koost/telephony/TelephonyBinderService.h

javascript 复制代码
// Cover the GECKO_RADIO_TECH to NETWORK_TYPE_*
int32_t convertRadioTech(const nsAString& rat);

三、日志分析

追溯在PDN建立过程中,读取apn配置的bearer参数到DataCall使用的radio类型的bearer值变化情况。

四、方案开发

相关介绍:KaiOS 新增APN信息字段的代码实现-CSDN博客

在 APN Editor中实现bearer显示

代码模块:gaia/apps/settings /js/panels/apn_editor/apn_editor.js

javascript 复制代码
/**
 * The apn editor module
 */
'use strict';
define(function(require) { //eslint-disable-line
  const ApnEditorConst = require('panels/apn_editor_tct/apn_editor_const');
  const ApnEditorSession = require('panels/apn_editor_tct/apn_editor_session');
  const ApnUtils = require('modules/apn_tct/apn_utils');

  //以下三个常量都是从ApnEditorConst模块中导入的
  const { APN_PROPERTIES } = ApnEditorConst;
  const { APN_PROPERTY_DEFAULTS } = ApnEditorConst;
  const { VALUE_CONVERTERS } = ApnEditorConst;


  return function apnEditor(rootElement) {
    return new ApnEditor(rootElement);
  };
});
将kaios中十进制的bearer转换同Android原始配置的bitmask

测试代码

javascript 复制代码
// 功能:将十进制bearer转换成1-20字符串(同Android)
function bitmaskToString(aBearerBitmask) {
  if (aBearerBitmask == 0 || aBearerBitmask === undefined) {
    return 0;
  }

  let val = "";
  for (let i = 1; i < 20; i++) {
    if ((aBearerBitmask & (1 << (i - 1))) != 0) {
      val = val.concat(i + "|");
    }
  }
  return val;
}

//常量数组,定义rat
const GECKO_RADIO_TECH = [
  null,
  "gprs",
  "edge",
  "umts",
  "is95a",
  "is95b",
  "1xrtt",
  "evdo0",
  "evdoa",
  "hsdpa",
  "hsupa",
  "hspa",
  "evdob",
  "ehrpd",
  "lte",
  "hspa+",
  "gsm",
  "tdscdma",
  "iwlan",
  "lte_ca",
];

//将1-20的bitmask转换成对应的网络制式
function bitmaskToRatString(aBitmask) {
   if (aBitmask == 0 || aBitmask === undefined) {
    return "unspecified";
  }
  let rat = "";
  let splitResult = aBitmask.split("|");
  console.log('splitResult = '+ splitResult);
  rat = splitResult.map(x => GECKO_RADIO_TECH[x]).join(",").slice(0,-1);
  //slice(startIndex,endIndex) 用于去掉最后一个逗号
  return rat;
}

// 测试代码
let apnBearer = '312312';  //apn.json原始配置值
let targetBearer;  //1-20转换目标值
let bearerString = '';

if (apnBearer === undefined) {
    targetBearer = 0;
} else {
    targetBearer = apnBearer;
}

bearerString = bitmaskToString(targetBearer);


// 输出结果
console.log('targetBearer = ' + targetBearer);
console.log('bearerString = '+ bearerString);
相关推荐
m0_7482359530 分钟前
CentOS 7使用RPM安装MySQL
android·mysql·centos
ac-er88884 小时前
Yii框架中的队列:如何实现异步操作
android·开发语言·php
流氓也是种气质 _Cookie6 小时前
uniapp 在线更新应用
android·uniapp
zhangphil8 小时前
Android ValueAnimator ImageView animate() rotation,Kotlin
android·kotlin
徊忆羽菲8 小时前
CentOS7使用源码安装PHP8教程整理
android
编程、小哥哥10 小时前
python操作mysql
android·python
Couvrir洪荒猛兽10 小时前
Android实训十 数据存储和访问
android
五味香12 小时前
Java学习,List 元素替换
android·java·开发语言·python·学习·golang·kotlin
十二测试录13 小时前
【自动化测试】—— Appium使用保姆教程
android·经验分享·测试工具·程序人生·adb·appium·自动化
Couvrir洪荒猛兽14 小时前
Android实训九 数据存储和访问
android