当前位置: 首页 > news >正文

手机实时提取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线路-双卡双待单通方案

相关文章:

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

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

共阳极LED的控制与短路问题解析

共阳极LED的控制与短路问题解析 在电子电路中,LED(发光二极管)是最常见的元件之一。LED的连接方式分为共阳极和共阴极,不同的连接方式决定了LED的控制逻辑。本文将重点讲解共阳极LED的工作原理,并解答“为什么给1不会…...

华为消费级QLC SSD来了

近日,有关消息显示,华为的消费级SSD产品线,eKitStor Xtreme 200E系列,在韩国一家在线零售商处首次公开销售,引起了业界的广泛关注。 尽管华为已经涉足服务器级别的SSD制造多年,但直到今年6月才正式推出面向…...

liunx下载gitlab

1.地址: https://mirrors.tuna.tsinghua.edu.cn/gitlab-ce/yum/el7/ 安装 postfix 并启动 yum install postfix systemctl start postfix systemctl enable postfix ssh服务启动 systemctl enable sshd systemctl start sshd开放 ssh 以及 http 服务&#xff0c…...

深度学习模型预测值集中在某一个值

深度学习模型,训练过程中,经常遇到预测的结果集中在某个值,而且在学习的过程中会变,样例如下。 主要有如下解决方案 1、更换relu ->tanh 或者其他激活函数 2、更改随机种子,估计是没有初始化好,或者调…...

Sqoop的使用

每个人的生活都是一个世界,即使最平凡的人也要为他那个世界的存在而战斗。 ——《平凡的世界》 目录 一、sqoop简介 1.1 导入流程 1.2 导出流程 二、使用sqoop 2.1 sqoop的常用参数 2.2 连接参数列表 2.3 操作hive表参数 2.4 其它参数 三、sqoop应用 - 导入…...

OpenGL ES 04 图片数据是怎么写入到对应纹理单元的

从指定路径加载图像并转换为 CGImage。获取图像的宽度和高度。创建一个 RGB 颜色空间。为图像数据分配内存。创建一个位图上下文并将图像绘制到上下文中。创建一个新的纹理对象并绑定到指定的纹理单元。指定二维纹理图像。释放分配的内存。设置纹理参数,包括放大和缩…...

C# 设计模式的六大原则(SOLID)

C# 设计模式的六大原则(SOLID) 引言 在面向对象编程中,设计模式提供了高效、可复用和可维护的代码结构。SOLID原则是软件设计中的一组重要原则,用于确保代码具有良好的可维护性、可扩展性和灵活性。SOLID是五个设计原则的首字母…...

数据库自增 id 过大导致前端时数据丢失

可以看到,前端响应参数是没有丢失精度的 但是在接受 axios 请求参数时出现了精度丢失 解决方案一:改变 axios 字符编码 axios.defaults.headers[Content-Type] application/json;charsetUTF-8; 未解决 解决方案二:手动使用 json.parse() …...

第二十六天 自然语言处理(NLP)词嵌入(Word2Vec、GloVe)

自然语言处理(NLP)中的词嵌入(Word2Vec、GloVe)技术,是NLP领域的重要组成部分,它们为词汇提供了高维空间到低维向量的映射,使得语义相似的词汇在向量空间中的距离更近。以下是对这些技术的详细解…...

MongoDB 固定集合

MongoDB 固定集合 MongoDB中的固定集合(Capped Collections)是一种具有固定大小的集合,当集合中的数据达到其最大大小时,它会自动覆盖最早的文档。这种类型的集合在MongoDB中用于实现高效的、固定大小的循环缓冲区。本文将详细介…...

数据结构9.3 - 文件基础(C++)

目录 1 打开文件字符读写关闭文件 上图源自&#xff1a;https://blog.csdn.net/LG1259156776/article/details/47035583 1 打开文件 法 1法 2ofstream file(path);ofstream file;file.open(path); #include<bits/stdc.h> using namespace std;int main() {char path[]…...

Leetcode 1254 Number of Closed Islands + Leetcode 1020 Number of Enclaves

Leetcode 1254 题意 给定一个m*n的矩阵含有0和1&#xff0c;1代表水&#xff0c;0代表陆地&#xff0c;岛屿是陆地的集合&#xff0c;如果一个岛屿和四个方向的边界相连&#xff0c;则不算封闭岛屿。求有多少个封闭的岛屿。 题目链接 https://leetcode.com/problems/number…...

Junit4单元测试快速上手

文章目录 POM依赖引入业务层测试代码Web层测试代码生成测试类文件 在工作中我用的最多的单元测试框架是Junit4。通常在写DAO、Service、Web层代码的时候都会进行单元测试&#xff0c;方便后续编码&#xff0c;前端甩锅。 POM依赖引入 <dependency><groupId>org.spr…...

U盘提示格式化?原因、恢复方案与预防措施全解析

一、U盘提示格式化现象概述 在日常使用U盘的过程中&#xff0c;我们有时会遇到一个令人头疼的问题——U盘插入电脑后&#xff0c;系统却弹出一个提示框&#xff0c;告知我们U盘需要格式化才能访问。这个提示往往伴随着数据的潜在丢失风险&#xff0c;让我们不禁为之心焦。U盘提…...

HTML——13.超链接

<!DOCTYPE html> <html><head><meta charset"UTF-8"><title>超链接</title></head><body><!--超链接:从一个网页链接到另一个网页--><!--语法&#xff1a;<a href"淘宝网链接的地址"> 淘宝…...

vue中的设计模式

vue中使用了哪些设计模式 1. 观察者模式&#xff08;Observer Pattern&#xff09; 应用场景&#xff1a;Vue 的响应式系统核心就是观察者模式。 实现方式&#xff1a;通过 Object.defineProperty 或 Proxy 监听数据变化&#xff0c;当数据发生变化时&#xff0c;通知依赖的视…...

利用python将图片转换为pdf格式的多种方法,实现批量转换,内置模板代码,全网最全,超详细!!!

文章目录 前言1、img2pdf库的使用1.1 安装img2pdf库1.2 案例演示&#xff08;模板代码&#xff09; 2、Pillow库的使用2.1 pillow库的安装2.2 案例演示&#xff08;模板代码&#xff09; 3、PyMuPDF库的使用3.1 安装pymupdf库3.2 案例演示&#xff08;模板代码&#xff09;3.3 …...

tcpdump的常见方法

详解tcpdump的使用方法&#xff1a;网络数据包捕获与分析 tcpdump是一个功能强大的命令行工具&#xff0c;用于捕获和分析通过网络接口传输的数据包。它广泛应用于网络故障诊断、网络安全监控和协议分析等领域。本文将详细介绍tcpdump的使用方法&#xff0c;包括安装、基本命令…...

工控主板ESM7000/6800E支持远程桌面控制

英创公司ESM7000 是面向工业领域的双核 Cortex-A7 高性能嵌入式主板&#xff0c;ESM6800E则为单核Cortex-A7 高性价比嵌入式主板&#xff0c;ESM7000、ESM6800E都是公司的成熟产品&#xff0c;已广泛应用于工业很多领域。ESM7000/6800E板卡中Linux系统配置为linux-4.9.11内核、…...

Qt/C++开发监控GB28181系统/取流协议/同时支持udp/tcp被动/tcp主动

一、前言说明 在2011版本的gb28181协议中&#xff0c;拉取视频流只要求udp方式&#xff0c;从2016开始要求新增支持tcp被动和tcp主动两种方式&#xff0c;udp理论上会丢包的&#xff0c;所以实际使用过程可能会出现画面花屏的情况&#xff0c;而tcp肯定不丢包&#xff0c;起码…...

DAY 47

三、通道注意力 3.1 通道注意力的定义 # 新增&#xff1a;通道注意力模块&#xff08;SE模块&#xff09; class ChannelAttention(nn.Module):"""通道注意力模块(Squeeze-and-Excitation)"""def __init__(self, in_channels, reduction_rat…...

基于Docker Compose部署Java微服务项目

一. 创建根项目 根项目&#xff08;父项目&#xff09;主要用于依赖管理 一些需要注意的点&#xff1a; 打包方式需要为 pom<modules>里需要注册子模块不要引入maven的打包插件&#xff0c;否则打包时会出问题 <?xml version"1.0" encoding"UTF-8…...

SpringCloudGateway 自定义局部过滤器

场景&#xff1a; 将所有请求转化为同一路径请求&#xff08;方便穿网配置&#xff09;在请求头内标识原来路径&#xff0c;然后在将请求分发给不同服务 AllToOneGatewayFilterFactory import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; impor…...

ABAP设计模式之---“简单设计原则(Simple Design)”

“Simple Design”&#xff08;简单设计&#xff09;是软件开发中的一个重要理念&#xff0c;倡导以最简单的方式实现软件功能&#xff0c;以确保代码清晰易懂、易维护&#xff0c;并在项目需求变化时能够快速适应。 其核心目标是避免复杂和过度设计&#xff0c;遵循“让事情保…...

CVE-2020-17519源码分析与漏洞复现(Flink 任意文件读取)

漏洞概览 漏洞名称&#xff1a;Apache Flink REST API 任意文件读取漏洞CVE编号&#xff1a;CVE-2020-17519CVSS评分&#xff1a;7.5影响版本&#xff1a;Apache Flink 1.11.0、1.11.1、1.11.2修复版本&#xff1a;≥ 1.11.3 或 ≥ 1.12.0漏洞类型&#xff1a;路径遍历&#x…...

【学习笔记】erase 删除顺序迭代器后迭代器失效的解决方案

目录 使用 erase 返回值继续迭代使用索引进行遍历 我们知道类似 vector 的顺序迭代器被删除后&#xff0c;迭代器会失效&#xff0c;因为顺序迭代器在内存中是连续存储的&#xff0c;元素删除后&#xff0c;后续元素会前移。 但一些场景中&#xff0c;我们又需要在执行删除操作…...

【Linux手册】探秘系统世界:从用户交互到硬件底层的全链路工作之旅

目录 前言 操作系统与驱动程序 是什么&#xff0c;为什么 怎么做 system call 用户操作接口 总结 前言 日常生活中&#xff0c;我们在使用电子设备时&#xff0c;我们所输入执行的每一条指令最终大多都会作用到硬件上&#xff0c;比如下载一款软件最终会下载到硬盘上&am…...

SpringAI实战:ChatModel智能对话全解

一、引言&#xff1a;Spring AI 与 Chat Model 的核心价值 &#x1f680; 在 Java 生态中集成大模型能力&#xff0c;Spring AI 提供了高效的解决方案 &#x1f916;。其中 Chat Model 作为核心交互组件&#xff0c;通过标准化接口简化了与大语言模型&#xff08;LLM&#xff0…...

echarts使用graphic强行给图增加一个边框(边框根据自己的图形大小设置)- 适用于无法使用dom的样式

pdf-lib https://blog.csdn.net/Shi_haoliu/article/details/148157624?spm1001.2014.3001.5501 为了完成在pdf中导出echarts图&#xff0c;如果边框加在dom上面&#xff0c;pdf-lib导出svg的时候并不会导出边框&#xff0c;所以只能在echarts图上面加边框 grid的边框是在图里…...