【笔记】【开发方案】APN 配置参数 bitmask 数据转换(Android KaiOS)

news2024/11/18 19:38:21

一、参数说明

(一)APN配置结构对比

平台AndroidKaiOS
文件类型xmljson
结构每个<apn>标签是一条APN,包含完成的信息层级数组结构,使用JSON格式的数据。最外层是mcc,其次mnc,最后APN用数组形式配置(每个APN都是一个对象,不含mccmnc属性)。

Android: apns-conf.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

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

{
"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
  • apn_editor_const.js

1、bitmask的进制转换

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

//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文件中的代码创建和初始化的。

//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数组的元素和顺序:

//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 的使用案例

  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

// Cover the GECKO_RADIO_TECH to NETWORK_TYPE_*
int32_t convertRadioTech(const nsAString& rat);

4、apn_editor_const.js 原始数据和 UI显示转换

KaiOS 基线提供了VALUE_CONVERTERS 常量对象,包含两个属性:TO_STRING和TO_DATA,每个属性包含了转换方法。

  • TO_STRING:将一个字符串数组转换成一个用逗号分隔的字符串
    • apn_editor.js 界面显示用
  • TO_DATA :将一个用逗号分隔的字符串转换为一个字符串数组
    • apn_editor_session.js 保存数据库用

源码如下,可参考APN Type显示的内容,对数组进行显示和配置数据存储。

/**
 * The apn editor const
 */

define([],() => {
  const APN_PROPERTY_DEFAULTS = {
    //省略大部分原生属性
    apn: '',
    types: ['default'],
    roaming_protocol: 'notDefined',
    //自定义
    mvno_type: 'notDefined',
    mvno_match_data: '',
    bearer:['unspecified']
  };

  const APN_PROPERTIES = Object.keys(APN_PROPERTY_DEFAULTS);

  //数据转换工具接口
  const VALUE_CONVERTERS = {
    TO_STRING: { //TO_STRING属性
      types(types) {
        //判断 types参数存在、是一个数组、且数组非空,就将字符串数组转换成用逗号分隔的字符串
        // types :参数是否存在
        // Array.isArray(types):是否为一个数组
        // types.length:数组是否非空
        if (types && Array.isArray(types) && types.length) {
          return types.join(', ');
        }
        return 'default'; //默认返回“default”
      }
    },
    TO_DATA: { //TO_DATA属性
      types(string) {
        //将用逗号分隔的字符串转换为字符串数组
        return string.split(',').map(str => str.trim());
      }
    }
  };

  //在apn_editor.js中定义了常量调用方法,APN_PROPERTIES、APN_PROPERTY_DEFAULTS、VALUE_CONVERTERS
  return { //一个对象,三个只读get属性?外部访问但不能修改他们的值?
    get APN_PROPERTIES() {
      return APN_PROPERTIES;
    },
    get APN_PROPERTY_DEFAULTS() {
      return APN_PROPERTY_DEFAULTS;
    },
    get VALUE_CONVERTERS() {
      return VALUE_CONVERTERS;
    }
  };
});
(1)测试代码

 对以上接口VALUE_CONVERTERS的测试代码和解析,输出案例 :

const types = ['gprs', 'edge', 'umts'];
const str = VALUE_CONVERTERS.TO_STRING.types(types);
console.log(str); // 输出 'gprs, edge, umts'

const str = 'gprs, edge, umts';
const types = VALUE_CONVERTERS.TO_DATA.types(str);
console.log(types); // 输出 ['gprs', 'edge', 'umts']
(2)apn_editor.js 调用TO_STRING 界面显示

外部模块调用源码实现

/**
 * The apn editor module
 */
'use strict';
define(function(require) { //eslint-disable-line
  const ApnEditorConst = require('panels/apn_editor/apn_editor_const');
  const ApnEditorSession = require('panels/apn_editor/apn_editor_session');

  //引用apn_editor_const.js 定义常量
  const { APN_PROPERTIES } = ApnEditorConst;
  const { APN_PROPERTY_DEFAULTS } = ApnEditorConst;
  const { VALUE_CONVERTERS } = ApnEditorConst;
  
  ApnEditor.prototype = {
    convertValue(value, converter) {
      if (converter) {
        return converter(value);
      }
      return value;
    },
  
    fillInputElements(inputElements, apn) {
      APN_PROPERTIES.forEach(function input(name) {
        const inputElement = inputElements[name];
        if (inputElement) {
          //数据处理,调用VALUE_CONVERTERS 转成字符串
          inputElement.value = this.convertValue(
            value,
            VALUE_CONVERTERS.TO_STRING[name]
          );

        } //if inputElement
      }, this);
    },


});
(3)apn_editor_session.js 保存信息到APN数据库

在commit 中保存APN到,通过apn manager执行addApn或 updateApn

/**
 * The apn editor session module
 */
define(['require','modules/apn/apn_settings_manager','panels/apn_editor/apn_editor_const','modules/apn/apn_utils'],function(require) { //eslint-disable-line

  const ApnSettingsManager = require('modules/apn/apn_settings_manager');
  const ApnEditorConst = require('panels/apn_editor/apn_editor_const');
  const ApnUtils = require('modules/apn/apn_utils');

  const { APN_PROPERTIES } = ApnEditorConst;
  const { VALUE_CONVERTERS } = ApnEditorConst;

  function ApnEditorSession(obj, inputElements, apnItem) {
    this.serviceId = obj.serviceId;
    this.apnMode = obj.mode;
    this.inputElements = inputElements;
    this.apnItem = apnItem;
  }


  ApnEditorSession.prototype = {
    convertValue(value, converter) {
      if (converter) {
        return converter(value);
      }
      return value;
    },
    exportApnSetting(inputElements) {
      const newApnSetting = {};
      APN_PROPERTIES.forEach(function input(name) {
        const inputElement = inputElements[name];
        if (inputElement && !inputElement.hidden) {
          newApnSetting[name.toLowerCase()] = this.convertValue(
            inputElement.value,
            VALUE_CONVERTERS.TO_DATA[name]
          );
        }
      }, this);
      return newApnSetting;
    },

    //保存新增APN数据,调用addApn
    commitNew() {
      const promises = [];
      const newApnSetting = this.exportApnSetting(this.inputElements);

      promises.push(ApnSettingsManager.addApn(this.serviceId, newApnSetting));

      return Promise.all(promises);
    },

    //保存编辑APN数据,调用updateApn
    commitEdit() {
      const promises = [];
      const newApnSetting = this.exportApnSetting(this.inputElements);

      promises.push(ApnSettingsManager.updateApn(this.serviceId,
          this.apnItem.id, newApnSetting));

      return Promise.all(promises);
    },



    //在apn_editor.js中传递  new/edit选择执行新增/编辑APN
    commit() {
      switch (this.apnMode) {
        case 'new':
          return this.commitNew();
        case 'edit':
          return this.commitEdit();
        default:
          console.error('invalid mode');
          return Promise.resolve();
      }
    },

    //取消,不保存任何数据
    cancel() {
      APN_PROPERTIES.forEach(function input(name) {
        this.inputElements[name].value = '';
      }, this);
      this.apnItem = null;
    },

    //还没找到mode的用法
    get mode() {
      return this.apnMode;
    }
  };

  return function apnEditorSession(obj, inputElements, apnItem) {
    return new ApnEditorSession(obj, inputElements, apnItem);
  };
});

三、日志分析

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

四、方案开发

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

在 APN Editor中实现bearer显示

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

/**
 * 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

1、十进制bearer -> bitmask位码 & networkType字符

将kaios中十进制的bearer转换同Android原始配置的bitmask及映射网络类型字符串

// 功能:将十进制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);
let ratString = bitmaskToRatString(bearerString);

// 输出结果
console.log('targetBearer = ' + targetBearer);
console.log('bearerString = '+ bearerString);
console.log('ratString = ' + ratString);

存在一个问题 ,在UI显示了rat的字符串,那么 commitEdit() 的时候会保存bearer为字符串,应该需要在apn_editor_session.js 中处理ApnSettings内容。

难点在于如何将gprs,edge,is95b,1xrtt 转换成bearer数值 ( bitmask=1|2|5|6 甚至是 十进制原文)

2、networktype字符 -> bitmask 位码

convertNetworkTypeToBitmask,将networktype字符串转换成bitmask(1|18|20格式)

//映射表,包含了不同的网络类型和对应的数字码。
const networkTypeMap = {
  "unspecified": "0",
  "gprs": "1",
  "edge": "2",
  "umts": "3",
  "is95a": "4",
  "is95b": "5",
  "1xrtt": "6",
  "evdo0": "7",
  "evdoa": "8",
  "hsdpa": "9",
  "hsupa": "10",
  "hspa": "11",
  "evdob": "12",
  "ehrpd": "13",
  "lte": "14",
  "hspa+": "15",
  "gsm": "16",
  "tdscdma": "17",
  "iwlan": "18",
  "lte_ca": "19"
};


//测试代码
//一、简单程序 
let bearerString = "gprs,edge,umts,hsdpa,hsupa,hspa,lte,hspa+"; //界面显示字符串包含多个网络类型,使用逗号分隔
let networkTypes  = bearerString.split(","); //string转换成字符串数组
let bitCode =  Array.from(networkTypes, bearerString => networkTypeMap[bearerString]); //将字符串数组转换成数字码数组
let bitmask = bitCode.join("|");

console.log("networkTypes = " + networkTypes);
console.log("bitCode = " + bitCode);


//二、改善,封装成接口调用
//参数类型:字符串 "gprs,edge,umts,hsdpa,hsupa,hspa,lte,hspa+"
//let bearerString = "gprs,edge,umts,hsdpa,hsupa,hspa,lte,hspa+"; 
function convertNetworkTypeToBitmask(aNetworkTypes) {
  let bitmask = '0'; //初始化为0和"0"的区别?
  if (aNetworkTypes == 0 || aNetworkTypes === undefined) {
    return 0;
  }

  //networkTypesArray是一个字符串数组,存储着多个网络类型的字符串,例如 ['gprs', 'edge']
  let networkTypesArray = aNetworkTypes.split(","); //字符串转成字符串数组
  
  //networkTypesBitCode是一个数字码数组,如[ '1', '2', '3' ]
  //映射函数对每个字符串进行转换,函数参数是每个字符串,它在映射表中对应的值就是所需bit位数字码
  //映射函数的代码为 aNetworkTypes => networkTypeMap[aNetworkTypes]
  let networkTypesBitCode = Array.from(networkTypesArray, aNetworkTypes => networkTypeMap[aNetworkTypes]); //字符串数组转换成功数字码 
  
  bitmask = networkTypesBitCode.join("|");
  return bitmask;
}

console.log("bitmask(api) = " + convertNetworkTypeToBitmask(bearerString));



//如果传参是数组,就更简单的封装接口了
//接受一个包含网络类型的数组作为参数,并将这个数组中的每种网络类型转换为对应的数字码,
//返回一个由数字码组成的字符串,使用"|"符号分隔,即bitmask

//通过map()方法遍历这个数组,将每种网络类型转换为对应的数字码,存在codes数组
//通过join()方法将codes数字码连接成字符串 ,用“|”符号分隔。


function convertNetworkTypes(networkTypes) {
  const codes = networkTypes.map(type => networkTypeMap[type]);
  return codes.join("|");
}

const types = ["gprs", "edge", "is95b", "1xrtt"];
const codeString = convertNetworkTypes(types);  // "1|2|5|6"

3、networktype字符 -> 十进制bearer

用如下bitcode数组能便于转换成 bitmask位码和十进制,不能直接通过bitmask转换成十进制,即bitmask 位码 -> 十进制bearer

 bitcode = 1,2,3,9,10,11,14,15

const NETWORK_TYPE_MAP = {
  "unspecified": "0",
  "gprs": "1",
  "edge": "2",
  "umts": "3",
  "is95a": "4",
  "is95b": "5",
  "1xrtt": "6",
  "evdo0": "7",
  "evdoa": "8",
  "hsdpa": "9",
  "hsupa": "10",
  "hspa": "11",
  "evdob": "12",
  "ehrpd": "13",
  "lte": "14",
  "hspa+": "15",
  "gsm": "16",
  "tdscdma": "17",
  "iwlan": "18",
  "lte_ca": "19"
};

//参数类型:字符串 "gprs,edge,umts,hsdpa,hsupa,hspa,lte,hspa+"
//let bearerString = "gprs,edge,umts,hsdpa,hsupa,hspa,lte,hspa+"; 
function convertNetworkTypeToBitCode(aNetworkTypes) {
  console.log("convertBitcodeToBearer: aNetworkTypes = " + aNetworkTypes);
  let bitcode = '0';
  if (aNetworkTypes == 0 || aNetworkTypes === undefined) {
    return 0;
  }
  let networkTypesArray = aNetworkTypes.split(","); //字符串转成字符串数组
  bitcode = Array.from(networkTypesArray, aNetworkTypes => NETWORK_TYPE_MAP[aNetworkTypes]); //  
  console.log("convertNetworkTypeToBitCode: bitcode = " + bitcode);
  return bitcode;
}

function convertBitCodeToBitmask(aBitCode) {
 let bitmask = aBitCode.join("|");
 console.log("convertBitCodeToBitmask: bitmask = " + bitmask);
 return bitmask;
}

function convertBitcodeToBearer(aBitCode) {
    let bearerDec = 0;
    let bearerBin =0;
    if (aBitCode == 0 || aBitCode === undefined) {
      return 0;
  	}
    
    //parseInt((code).toString(2)) 是将数字 code转成二进制字符串
    bearerBin = aBitCode.map(code => parseInt(code).toString(2)).join(""); //转成二进制
    console.log("convertBitcodeToBearer: bearerBin = " + bearerBin);
    bearerDec = parseInt(bearerBin,2);//将二进制转换成十进制 。
    console.log("convertBitcodeToBearer: bearerDec = " + bearerDec);
    return bearerDec;
}

let bearerString = "gprs,edge,umts,hsdpa,hsupa,hspa,lte,hspa+"; //界面显示字符串包含多个网络类型,使用逗号分隔

convertBitCodeToBitmask(convertNetworkTypeToBitCode(bearerString));
convertBitcodeToBearer(convertNetworkTypeToBitCode(bearerString));

bearerBin  存在问题,在bitcode 转成20位二进制数的时候,输出的bearerBin 不正确

优化以上代码:

  function convertNetworkTypeToBearer(aNetworkTypes) {
    dump('apn_editor_session: StringToBearer aNetworkTypes = ' + aNetworkTypes);
    if (aNetworkTypes == 0 || aNetworkTypes === undefined) {
      return 0;
    }
    let bitcode = '0';
    let bearerBin = "";
    let bearer = 0;
    let networkTypesArray = aNetworkTypes.split(",");
    bitcode = Array.from(networkTypesArray, aNetworkTypes => NETWORK_TYPE_MAP[aNetworkTypes]);  //bitcode array  like [18,14]
    bitcode =  bitcode.sort(function(a, b) {
      return a - b;
    });
    dump('apn_editor_session: StringToBearer bitcode = ' + bitcode);
    let bearerArray = new Array(20).fill(0);
    for (let i = 0; i < bitcode.length; i++) {
        bearerArray[bitcode[i]-1] = 1;
        dump('apn_editor: StringToBearer bitcode[i] = ' + bitcode[i]);
    }
    dump('apn_editor_session: StringToBearer bearerArray = ' + bearerArray);
    bearerBin = bearerArray.reverse().join("");
    bearer = parseInt(bearerBin,2);
    dump('apn_editor_session: StringToBearer bearer = ' + bearer);
    return bearer;
  }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1466852.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

VirtualBox+Vagrant快速搭建Centos7

目录 安装VirtualBox&#xff1a; 安装Vagrant&#xff1a; 创建Vagrant项目目录&#xff1a; 初始化Vagrant配置文件&#xff1a; 本地Vagrantfile中的镜像名称&#xff1a; 启动虚拟机&#xff1a; SSH登录虚拟机&#xff1a; 备注&#xff1a;安装镜像的另一种方式是…

【MATLAB】 EWT信号分解+FFT傅里叶频谱变换组合算法

有意向获取代码&#xff0c;请转文末观看代码获取方式~ 展示出图效果 1 EWT分解算法 EWT分解算法是一种基于小波变换的信号分解算法&#xff0c;它可以将信号分解为一系列具有不同频率特性的小波分量。该算法的基本思想是将信号分解为多个不同尺度的小波分量&#xff0c;并对…

展锐S8000安卓核心板参数_紫光展锐5G核心板模块定制方案

展锐S8000核心板模块是基于八核S8000平台开发设计的&#xff0c;采用了先进的6nm EUV制程技术。搭载了全新的智能Android 13操作系统&#xff0c;展现出超强的画面解析能力和高性能双通道MIPI&#xff0c;拥有120Hz高刷新率&#xff0c;独立NPU和3.2TOPS Al算力&#xff0c;同时…

2024年数学建模美赛详细总结以及经验分享

前言&#xff1a; 本文记录与二零二四年二月六日&#xff0c;正好今天是数学建模结束&#xff0c;打算写篇文章记录一下整个过程&#xff0c;以及一些感受、还有经验分享。记录这个过程的原因就是我在赛前&#xff0c;在博客上找了很久&#xff0c;也没有像我这么类似记…

2024,中国零售行业数字化走到哪了?

对于如今的中国零售业数字化而言&#xff0c;仍有许多亟待解决的问题&#xff0c;其像一根根“鱼刺”&#xff0c;卡在零售企业增长的“喉咙”中。 作者|斗斗 编辑|皮爷 出品|产业家 熙熙攘攘的人群&#xff0c;琳琅满目年货&#xff0c;一张张喜庆的春联、福字、窗花……

零基础备考PMP,需要多长时间?

PMP是一门专业性很强的项目管理知识&#xff0c;考试当然是有一定的难度&#xff0c;但是也没有难到让你怀疑人生的程度。 如果你在学习PMP之前&#xff0c;已经有一些经验&#xff0c;那么备考一个半月基本上是没多大问题的&#xff0c;如果你是零基础小白&#xff0c;那么备…

2024全国水科技大会暨流域水环境治理与水生态修复论坛(六)

论坛召集人 冯慧娟 中国环境科学研究院流域中心研究员 刘 春 河北科技大学环境与工程学院院长、教授 一、会议背景 为深入贯彻“山水林田湖是一个生命共同体”的重要指示精神&#xff0c;大力实施生态优先绿色发展战略&#xff0c;积极践行人、水、自然和谐共生理念&…

基于RK3399 Android11适配OV13850 MIPI摄像头

目录 1、原理图分析2、编写和配置设备树3、调试方法4、遇到的问题与解决5、补丁 1、原理图分析 从上图可看出&#xff0c;我们需要关心的&#xff0c;①MIPI数据和时钟接口使用的是MIPI_TX1/RX1 ②I2C使用的是I2C4总线 ③RST复位引脚使用的是GPIO2_D2 ④PWDN使用的是GPIO1_C7 ⑤…

洛谷 【算法1-6】二分查找与二分答案

【算法1-6】二分查找与二分答案 - 题单 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 鄙人不才&#xff0c;刷洛谷&#xff0c;迎蓝桥&#xff0c;【算法1-6】二分查找与二分答案 已刷&#xff0c;现将 AC 代码献上&#xff0c;望有助于各位 P2249 【深基13.例1】查找 - 洛谷…

MySQL数据库基础(十三):关系型数据库三范式介绍

文章目录 关系型数据库三范式介绍 一、什么是三范式 二、数据冗余 三、范式的划分 四、一范式 五、二范式 六、三范式 七、总结 关系型数据库三范式介绍 一、什么是三范式 设计关系数据库时&#xff0c;遵从不同的规范要求&#xff0c;设计出合理的关系型数据库&…

Clickhouse系列之连接工具连接、数据类型和数据库

基本操作 一、使用连接工具连接二、数据类型1、数字类型IntFloatDecimal 2、字符串类型StringFixedStringUUID 3、时间类型DateTimeDateTime64Date 4、复合类型ArrayEnum 5、特殊类型Nullable 三、数据库 一、使用连接工具连接 上一篇介绍了clickhouse的命令行登录&#xff0c…

数字滚动实现

介绍 vue-countup-v3 插件是一个基于 Vue3 的数字动画插件&#xff0c;用于在网站或应用程序中创建带有数字动画效果的计数器。通过该插件&#xff0c;我们可以轻松地实现数字的递增或递减动画&#xff0c;并自定义其样式和动画效果。该插件可以用于许多场景&#xff0c;例如展…

ELK介绍以及搭建

基础环境 hostnamectl set-hostname els01 hostnamectl set-hostname els02 hostnamectl set-hostname els03 hostnamectl set-hostname kbased -i s/SELINUXenforcing/SELINUXdisabled/ /etc/selinux/config systemctl stop firewalld & systemctl disable firewalld# 安…

pytest结合Allure生成测试报告

文章目录 1.Allure配置安装2.使用基本命令报告美化1.**前置条件**2.**用例步骤****3.标题和描述****4.用例优先级**3.进阶用法allure+parametrize参数化parametrize+idsparametrize+@allure.title()4.动态化参数5.环境信息**方式一****方式二**6.用例失败截图1.Allure配置安装 …

【深度学习】微调通义千问模型:LoRA 方法,微调Qwen1.8B教程,实践

官网资料: https://github.com/QwenLM/Qwen/blob/main/README_CN.md 文章目录 准备数据运行微调设置网络代理启动容器执行 LoRA 微调修改 finetune/finetune_lora_single_gpu.sh运行微调 执行推理 在本篇博客中&#xff0c;我们将介绍如何使用 LoRA 方法微调通义千问模型&#…

Qt_快速安装指南

下载Qt在线安装程序&#xff08;不仔细介绍&#xff09;注册Qt账号&#xff08;不仔细介绍&#xff09;使用快速运行的命令&#xff0c;按照指定的下载地址下载 在Qt指定目录打开cmd命令窗口.\eqt-unified-windows-x86-4.0.1-1-online. exe --mirror https://mirrors.ustc.edu.…

C++:派生类的生成过程(构造、析构)

目录 派生类的生成过程 派生类的构造函数与析构函数&#xff1a; 构造函数&#xff1a; 派生类组合类的构造和析构&#xff1a; 构造函数和析构函数调用顺序&#xff1a; 派生类的生成过程 三步骤&#xff1a; 吸收基类&#xff08;父类&#xff09;成员&#xff1a;实现代…

多输入时序预测|GWO-CNN-LSTM|灰狼算法优化的卷积-长短期神经网络时序预测(Matlab)

目录 一、程序及算法内容介绍&#xff1a; 基本内容&#xff1a; 亮点与优势&#xff1a; 二、实际运行效果&#xff1a; 三、算法介绍&#xff1a; 灰狼优化算法&#xff1a; 卷积神经网络-长短期记忆网络&#xff1a; 四、完整程序下载&#xff1a; 一、程序及算法内容…

CLion 2023:专注于C和C++编程的智能IDE mac/win版

JetBrains CLion 2023是一款专为C和C开发者设计的集成开发环境&#xff08;IDE&#xff09;&#xff0c;它集成了许多先进的功能&#xff0c;旨在提高开发效率和生产力。 CLion 2023软件获取 CLion 2023的智能代码编辑器提供了丰富的代码补全和提示功能&#xff0c;使您能够更…

snmp协议开通教程

目录 一、什么是snmp协议&#xff1f; 二、snmp协议可以用来干什么&#xff1f; 三、snmp协议的开通 1、snmpv2协议开通 2、snmpv3协议开通 一、什么是snmp协议&#xff1f; SNMP&#xff08;Simple Network Management Protocol&#xff09;是一种用于网络管理的标准协议&a…