手机实时提取SIM卡打电话的信令声音-双卡手机来电如何获取哪一个卡的来电

手机实时提取SIM卡打电话的信令声音

--双卡手机来电如何获取哪一个卡的来电

  • 一、前言

前面的篇章《手机实时提取SIM卡打电话的信令声音-智能拨号器的双SIM卡切换方案》中,我们论述了局域网SIP坐席通过手机外呼出去时,手机中主副卡的呼叫调度策略。

但在实际的双SIM卡手机中,除了外呼策略之外,我们仍然需要考虑双卡来电时的应用场景。即不考虑同时来电的手机本身三方通话逻辑的情况下,针对时间上错开的SIM卡1和SIM卡2的来电,我们需要在来电的逻辑中实现如下两个部分的内容需求:

  1. 来电时,应该能识别出是哪一张手机卡的来电,以及对方号码是什么(即需要获知来电的主/被叫号码)。进而根据不同的SIM卡的号码,做不同的分发和处理策略。
  2. 不同手机卡来电时的声音,理应被接收到后分流到不同的局域网SIP坐席中。蓝牙电话app需要能够将这些实时的信令和声音,根据本地SIM1/SIM2号码的不同,而调度到不同的SIP坐席上。双SIM卡手机同时存在时,不同的线路的数据不应该混淆。

基于以上的前提和预设场景,本篇中针对双SIM卡手机的通话部分逻辑,补充了“非默认拨号卡”的常规使用方式的来电部分。真正实现了【双卡双待单通手机】把手机本身的双卡呼叫的能力,复制到了局域网SIP坐席的能力。

  • 二、Android获取来电事件和来电号码的方式

Android操作系统中,针对手机的来电事件通常采用下述方式获取:

  1. 具有android.permission.READ_PHONE_STATE权限;
  2. 接收android.intent.action.PHONE_STATE的广播事件来触发;
  3. 判断TelephonyManager的.getCallState()==TelephonyManager.CALL_STATE_RINGING;
  4. 获取intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER)的号码做为来电号码。

这样,在Android API Level>=29的安卓版本中(Android10及以上),手机app可以通过申请READ_PHONE_STATE权限后,通过上述第3步来获取来电事件,通过第4步来获取来电的对方电话号码。

具体代码的逻辑如下图所示:

  • 三、Android获取哪个SIM卡槽产生的来电

对于双卡双待手机来说,知道了上述的获取来电事件和号码,其实还是不够。因为来电跟外呼不同,没有办法通过设置的【默认呼叫卡】的方式固定来电时的本地接收号码(也没有这个必要)。

对于来电时的场景,正常情况下就需要对不同SIM卡的通话数据(语音和通话状态数据)进行分流,主动调度到不同的SIM卡对应的SIP坐席当中。在这个过程中,手机设备需要能够识别出来电的时候,到底是由哪一张SIM卡产生的来电,并能根据SIM手机卡对应的手机号来做调度和分流。

Android操作系统中,手机来电事件的卡槽号,通常采用下述方式获取:

  1. 具有android.permission.READ_PHONE_STATE权限;
  2. 接收android.intent.action.SUBSCRIPTION_PHONE_STATE的广播事件来触发;
  3. 判断intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER)号码不为空;
  4. 获取intent.getIntExtra("slot", -1)的数值来做为来电是对应SIM卡槽产生的。

通常,slot为0就是SIM卡1,slot为1就是SIM卡2的来电。这部分内容与获取本机SIM卡手机号时的slot-index值的索引保持一致。

我们在互联网上搜索这块内容时,网上对不同版本的Android出现不同的多SIM来电的号码获取方式,但实践表明,别的其它几种方式局限性太大,有的手机可以而有的型号的手机又不行。采用这个方式是普适性最广的一种方法。

具体代码的逻辑如下图所示。

  • ‌四、Android获取双SIM卡槽对应的手机号码的方式

这个需求也可以描述为:手机app获取插入的双SIM卡的手机号。(卡槽1的手机号和卡槽2的手机号)

这是一个非常复杂的任务,其难度不在获取方式,而在于不同Android版本和不同厂商型号的支持适配。

通常来说获取双卡甚至多卡的手机号码,一般需要经过下列3个步骤,不同品牌型号的手机会在不同的阶段获取得到手机号码,如下:

1)通过SubscriptionManager.getActiveSubscriptionInfoList()获取卡号列表。

  1. 具有android.permission.READ_PHONE_STATE权限;
  2. Android API Level>=23;
  3. 通过Context.getSystemService(TELEPHONY_SUBSCRIPTION_SERVICE)获取SubscriptionManager;
  4. 遍历SubscriptionManager.getActiveSubscriptionInfoList()读取卡槽号和手机号。

(注:标准方法,这个方法国内的手机,基本都不能够支持

2)通过content://telephony/siminfo 来读取手机的所有SIM卡号列表。

  1. 普通权限,最多授予READ_PHONE_STATE和通讯录权限即可;
  2. 有一些手机通常无法通过这个来获取号码,代码需要加try-catch;

(注:这个是手机中真实存放SIM卡的地方,拔插物理卡槽会直接变更这个表)

3)从通话历史呼叫中读取subscription_id和phone_account_address

  1. 具有android.permission.READ_CALL_LOG权限;
  2. Android API Level>=29;
  3. 使用Context.getContentResolver().query(CallLog.Calls.CONTENT_URI查询通话记录;
  4. cursor.getString(cursor.getColumnIndex("subscription_id"))读取卡槽号;
  5. cursor.getString(cursor.getColumnIndex("phone_account_address"))读取手机号;

(注:有些手机phone_account_address存有本机号码,有的型号只有subscription_id有值,比如荣耀X10手机)

经由以上三种方式的排列组合的方式,手机app总能够有一种办法可以直接检索出手机中SIM1/SIM2卡槽分别有没有插入手机卡、以及插入的卡的手机号是多少。我们在实际的app中也使用这个方法来适配和兼容多款不同手机厂商、不同型号的安卓手机,使手机插入多张SIM卡时,能将各手机卡的通话的操作、事件状态、以及语音数据,各自单独的分流到局域网内不同的SIP坐席中进行通话调度和操作。

  • 五、方案的局限性和风险点

上述这一堆的过程函数和权限,基本都要求Android API Level在29及以上,即大于等于Android10的版本中才会进入这些授权和权限读取。

但我们实践中发现,使用Android6、Android7以及Android10以下的其它版本时,其实根本不用授权,它自动就能使用上述段落的方法来直接获取到对应的事件和消息内容。Android10及以后,仅仅是把权限给规范化、需要专门授权和弹框授权而已。

在Android系统中,API Level大于等于23,即Android6.0版本的手机,在大陆乃至全球市场上占据几乎100%的市场份额。因此,使用此种方式来进行双SIM卡的外呼和来电转发,在操作系统层面上基本不存在问题。

但是,本篇文章中讲述的安卓手机的通话事件和语音部分,仅仅包括外呼的呼叫发起、新来电、以及呼叫挂断,并不包括呼叫的接通状态、以及来电时的接听/拒接操作。

我们在蓝牙电话方案中针对接通状态、来电接听/拒接操作,均通过蓝牙HFP协议的AT指令来完成。假设蓝牙模块在使用中因故障不能正常工作,或者后续规划的去掉外置蓝牙模块的场景下,手机的多SIM卡呼叫需要针对这块内容进行专项的分析和深入挖掘。

这块内容我们将在后续的篇章中,花一两个篇幅的内容来进行详细的分析和论述。

  • 六、总结

在手机双SIM卡甚至多SIM卡的呼叫业务中,不管是【双卡双待单通】,还是【双卡双待双通】,都会面临通话的发起方和呼叫目标的问题。由于手机硬件设备本身会把所有的语音数据都转接到手机麦克风/听筒/扬声器中进行使用,在外呼的时候可以设置【默认拨号卡】的方式进行呼叫。但在来电时手机设备应能明确区分并标识出是归属于SIM卡1还是SIM卡2产生的来电,并做好卡1/卡2同时来电时的【三方通话】的业务场景约束。

在本篇章中,我们针对Android系统本身的双SIM卡的来电能力进行了一定的探索,实现了在代码逻辑层面中识别“双卡手机来电如何获取哪一个卡的来电”的数据。并在后续的功能逻辑中,根据不同SIM卡手机号来实现通话数据和业务调度到不同的局域网SIP坐席的功能。

这样,对于手机app而言,就充分的利用上了手机本身自带的“双卡双待”的功能,降低了操作的复杂度,也减少了应用扩展的成本,为后续更加复杂的场景提供了理论和数据上的支撑。


上一篇:手机实时提取SIM卡打电话的信令声音-智能拨号器的双SIM卡切换方案

下一篇:手机实时提取SIM卡打电话的信令声音-智能拨号器的SIP线路-双卡双待单通方案

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

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

相关文章

离线语音识别+青云客语音机器人(幼儿园级别教程)

1、使用步骤 确保已安装以下库: pip install vosk sounddevice requests pyttsx3 2、下载 Vosk 模型: 下载适合的中文模型,如 vosk-model-small-cn-0.22。 下载地址: https://alphacephei.com/vosk/models 将模型解压后放置在…

Streaming Dense Video Captioning

原文出处 CVPR 原文链接 [2404.01297] Streaming Dense Video Captioninghttps://arxiv.org/abs/2404.01297 原文笔记 What 1、提出了一种基于聚类传入token的新记忆模块,该模块可以处理任意长的视频,并且可以在不访问视频所有帧的情况下处理视频(…

TCP 连接:三次握手与四次挥手

TCP 协议,全称为“传输控制协议”。 1. TCP 协议段格式 给出几个定义 : 16位源端口号 :用于标识发送端的应用程序。 16位目的端口号 :用于标识接收端的目标应用程序。 32位序号 :用于标识发送的每一个字节流中的第一…

IDEA+Docker一键部署项目SpringBoot项目

文章目录 1. 部署项目的传统方式2. 前置工作3. SSH配置4. 连接Docker守护进程5. 创建简单的SpringBoot应用程序6. 编写Dockerfile文件7. 配置远程部署 7.1 创建配置7.2 绑定端口7.3 添加执行前要运行的任务 8. 部署项目9. 开放防火墙的 11020 端口10. 访问项目11. 可能遇到的问…

redis开发与运维-redis0401-补充-redis流水线与Jedis执行流水线

文章目录 【README】【1】redis流水线Pipeline【1.1】redis流水线概念【1.2】redis流水线性能测试【1.2.1】使用流水线与未使用流水线的性能对比【1.2.2】使用流水线与redis原生批量命令的性能对比【1.2.3】流水线缺点 【1.3】Jedis客户端执行流水线【1.3.1】Jedis客户端执行流…

Uncaught ReferenceError: __VUE_HMR_RUNTIME__ is not defined

Syntax Error: Error: vitejs/plugin-vue requires vue (>3.2.13) or vue/compiler-sfc to be present in the dependency tree. 第一步 npm install vue/compiler-sfc npm run dev 运行成功,本地打开页面是空白,控制台报错 重新下载了vue-loa…

【微服务】【Sentinel】认识Sentinel

文章目录 1. 雪崩问题2. 解决方案3. 服务保护技术对比4. 安装 Sentinel4.1 启动控制台4.2 客户端接入控制台 参考资料: 1. 雪崩问题 微服务调用链路中的某个服务故障,引起整个链路中的所有微服务都不可用,这就是雪崩。动图演示: 在微服务系统…

STM32完全学习——使用定时器1精确延时

一、定时器的相关配置 首先一定要是递减定时器,递增的不太行,控制的不够准确,其次在大于10微秒的延时是非常准确的,小于的话,就没有那没准,但是凑合能用。误差都在一个微秒以内。使用高级定时器也就是时钟…

connect to host github.com port 22: Connection timed out 的解决方法

原因是 Github 被 GFW 屏蔽了。 Windows 系统,打开 C:\Windows\System32\drivers\etc,复制其中的 hosts 文件至桌面,用文本编辑器或者其他工具打开。 复制以下内容进去: 140.82.114.4 github.com 151.101.1.6 github.global.ss…

常见的排序算法过程和比较分析

比较分析 排序类别排序算法时间复杂度(最好)时间复杂度(最坏)时间复杂度(平均)辅助空间复杂度稳定性插入排序直接插入排序O(n)O(n)O(n)O(1)稳定插入排序折半插入排序O(n)O(n)O(n)O(1)稳定插入排序希尔排序…

【QGIS入门实战精品教程】7.3:QGIS制作千层饼(DEM+等高线+影像+TIN)

文章目录 一、效果展示二、数据准备三、制作过程1. 打开软件2. 添加图层3. 制作千层饼一、效果展示 二、数据准备 订阅专栏后,从专栏配套案例数据包中的7.3.rar中获取。 1. dem 2. 影像 3. 等高线 4. tin 三、制作过程 1. 打开软件 打开QGIS软件。 QGIS软件主界面。

如何将CSDN文章 导出为 PDF文件

一、首先,打开我们想要导出为 PDF格式的 CSDN文章,以下图为例。 二、按 F12 调出浏览器调式模式后,选择 控制台 三、在控制台处粘贴代码 代码: (function(){ use strict;var articleBox $("div.article_content"…

HTML——54. form元素属性

<!DOCTYPE html> <html><head><meta charset"UTF-8"><title>form元素属性</title></head><body><!--form标签用于创建一个表单&#xff0c;会将里面的内容一起发送服务器&#xff0c;结构类似于表格--><!-…

告别 $arr[0]: PHP 和 Laravel 中更优雅的数组处理方式

你是否曾经历过这样的惊魂时刻&#xff1a;线上代码突然崩溃&#xff0c;只因访问了一个不存在的数组元素&#xff1f;或者更糟的是&#xff0c;应用开始疯狂抛出错误&#xff0c;而你却毫无头绪&#xff1f;这一切的罪魁祸首可能就是看似人畜无害的硬编码数组索引&#xff0c;…

【Linux报告】实训六 重置超级用户密码

实训六 重置超级用户密码 2018编写-今日公布 【练习一】忘记root密码 步骤一&#xff1a;开启或重启系统&#xff0c;并且要在五秒之内按任何键&#xff1b; 步骤二&#xff1a;按任意键&#xff0c;停止进入系统&#xff0c;按【e】键&#xff0c;跳转新页面&#xff0c;再…

一种pod容器动态挂卷方案

一、背景 1.1 个人调试kvm 我们这边基于云平台的k8skubevirt&#xff0c;给安卓手机领域的开发工程师们提供了独占式虚拟机资源。这些资源主要用于工程师的个人级开发与调试&#xff0c;因此有如下特点&#xff1a; 使用时间与工作时间强相关&#xff0c;即工程师工作时间使…

Docker Compose 构建 EMQX 集群 实现mqqt 和websocket

EMQX 集群化管理mqqt真香 目录 #目录 /usr/emqx 容器构建 vim docker-compose.yml version: 3services:emqx1:image: emqx:5.8.3container_name: emqx1environment:- "EMQX_NODE_NAMEemqxnode1.emqx.io"- "EMQX_CLUSTER__DISCOVERY_STRATEGYstatic"- …

如何在没有 iCloud 的情况下将数据从 iPhone 传输到 iPhone

概括 您可能会遇到将数据从 iPhone 转移到 iPhone 的情况&#xff0c;尤其是当您获得新的 iPhone 15/14 时&#xff0c;您会很兴奋并希望将数据转移到它。 使用iCloud最终可以做到这一点&#xff0c;但它的缺点也不容忽视&#xff0c;阻碍了你选择它。例如&#xff0c;您需要…

CUDA与Microsoft Visual Studio不兼容问题

简介&#xff1a;在安装一些 python库时&#xff0c;涉及到第三方库&#xff08;特别是需要引用 C 代码&#xff09;时&#xff0c;通常的安装方式会涉及到编译过程&#xff0c;通常称为"源代码安装"&#xff08;source installation&#xff09;&#xff0c;或是 “…

Unity2D无限地图的实现(简单好抄)

说明&#xff1a;本教程实现的是在2D游戏中玩家在游戏中上下左右移动的时候自动进行地图拼接的功能&#xff0c;如果你只想实现左右移动的无限地图&#xff0c;那么这篇博客也能起到一定参考作用。 思路 第一步&#xff1a; 创建一个10*10的2D游戏对象当做地图 第二步创建一个…