手机实时提取SIM卡打电话的信令声音-智能拨号器的双SIM卡切换方案
手机实时提取SIM卡打电话的信令声音
--智能拨号器app的双SIM卡切换方案
- 一、前言
在蓝牙电话的方案中,由于采用市场上的存量手机来做为通讯呼叫的载体,而现在市面上大部分的手机都是“双卡双待单通”手机,简称双卡双待手机。即在手机开机后的使用过程中,两张SIM手机卡都能同时连接到对应的运营商基站、收发运营商预设的指令进行状态查询、话费状态、来电振铃等消息,但某一个时刻仅能有一张SIM卡可以处于通话过程。
这样的话,双卡双待手机就一定存在一个三方通话的互斥问题,分别为:
- 外呼时到底是默认选择SIM卡1/SIM卡2进行外呼?还是不默认,等真正外呼时再由用户进行手动选择?
- 通话过程中如果另外一张SIM有新的来电,是直接拒接?直接接通并挂起/挂断原通话?还是弹框提示用户,由用户进行手动选择?
本篇章中讨论的“双SIM卡自动切换策略”主要针对的是上述第1个场景。第2种场景我们使用手机默认自带的三方通话的逻辑,即:通话中来电让它自动弹框提醒但蓝牙电话不做处理,等待来电自动超时拒接或呼叫取消。
- 二、为何有双SIM卡切换的需求
主要根源在于蓝牙电话方案采用蓝牙HFP协议来做为通讯的载体,而蓝牙HFP协议并没有对多SIM卡手机的主/副卡进行足够的支持。
手机app运行过程中,app通过usb蓝牙与手机蓝牙保持长时间的SLC连接,并通过蓝牙HFP协议来发送操作指令和接收返回的事件状态,以及接收蓝牙返回的SCO语音。
蓝牙HFP协议中,蓝牙双方建立SLC连接后,可以使用ATD10086;的AT指令,通过蓝牙让手机呼叫10086的目标号码。蓝牙电话方案中,由于兼顾了Android手机、iPhone手机、老人机、儿童手表等各种带蓝牙和SIM卡的标准设备,在外呼时默认采用蓝牙HFP协议的ATD指令进行外呼。
在这样的使用方式中,蓝牙电话app并不是唯一遇到此蛋疼问题的应用场景,在中国大陆,蓝牙耳机、汽车的车机与手机进行蓝牙连接来拨打电话等经典场景照样被这个问题所困扰。我们随便在百度等信息过滤入口检索一下,分分钟能搜到诸如下列内容的描述:
以及,某一些品牌的官网上对蓝牙电话的权威性描述和解决方法:
事实上,蓝牙电话方案对双SIM卡支持的疑问并不仅仅在于外呼的时候默认选用哪一张SIM卡进行外呼。我们对于双卡双待的SIM卡需求,通常包括下述三个场景:
- App运行的时候,应用应该能知道手机到底插入了几张SIM卡?每一张卡的手机号码分别是多少?
- 手机外呼的时候使用的是哪一张卡打出去的?能不能呼叫时手动指定或者代码中指定?如果不能,是否存在默认呼叫卡的方式来进行外呼操作?
- 如果外呼时需要指定默认呼叫卡的方式发起呼叫,假设当前默认SIM卡1,那我想用SIM卡2来呼叫时,如何去切换默认卡,能让电话能从SIM卡2中拨打出去?
看出来了吗?其实切换默认呼叫卡的方式,不是外呼的必要条件。按数学逻辑思维来讲,这个叫“充分但不必要条件”??^V^,脑袋中无用的知识又增加了一些。
但是为什么我们还要“默认卡默认卡”的长篇论述,还要搞一堆的双卡切换方式出来呢?都是被这蓝牙HFP协议给逼的,你要是还想用蓝牙通讯来发起呼叫,你就没得选。就这么简单。
- 三、预设的双SIM卡的切换方式
有个鬼的预设,能直接切换SIM卡的操作都得是Android系统级应用或者能够进行提权的超级用户组的应用。我们这种立志做普通权限的app,还想着任意一个品牌和型号的手机都能正常安装和使用的普通应用想都不用想它能够直接在代码中做切换。
我们能够做的,就是在应用中跳转到手机厂商预设的【切换SIM卡操作的界面】,然后让用户手动点击,或者在app中开启“无障碍”功能之后,代码模拟进行点击的办法,来点击手机厂商预设的界面中,默认拨号卡的SIM卡1和SIM卡2的位置,进行手动或自动的SIM卡切换。
感谢大自然的馈赠,感谢前辈先驱的开源奉献,^V^,我们在GitHub中一下就搜索到了一个经典的、使用无障碍功能进行自动点击的开源库Smart-AutoClicker。使用它,能快速的通过界面截图和坐标点击的方式,记录下预设的点击脚本,并在需要使用时触发自动点击的操作。多余的话我就不说了,直接上链接,感兴趣的读者可以自行下载分析和研究。
GitHub - Nain57/Smart-AutoClicker: An open-source auto clicker on images for Android
笔者个人的AndroidStudio版本的原因,最新的master代码下载下来后没法编译和构建,好坑啊,然后就尝试着回退版本,最后试验使用的是它的1.3.0版本,下载分支如下地址所示。
Release SmartAutoClicker-1.3.0 · Nain57/Smart-AutoClicker · GitHub
确实能正常编译、安装和运行,也达到了预设的记录脚本和重放执行来进行SIM卡1/卡2切换的效果。
- 四、预设的切换双SIM卡的界面规划
在预设的规划中,对双SIM卡自动切换的方式,我们打算以手机型号做为脚本记录的主键(因为某个型号的某个厂商的手机,屏幕分辨率一定是一样的,甚至手机配置都会完全相同),使用屏幕分辨率来做为屏幕点击的基准,通过预录脚本或用户手动录制脚本的方式,将脚本分发到所有安装了【智能拨号器app】的手机设备中,进行SIM卡的自动切换。
app的设置界面中,会分别列举有【切换SIM卡1】和【切换SIM卡2】的脚本,设置界面允许用户自己修改与重新录制脚本的内容。并且设置界面中,会配置两个SIM卡之间的切换策略的触发条件,如每张SIM卡外呼次数或接通次数超过某个值就进行SIM卡切换等策略。
通过上述的做法,在代码层面进行双SIM卡的自动切换。SIM卡的脚本列表界面大致可以如下图所示:
每个用户的每个手机设备,都可以对切换的脚本进行手动的修改。但由于我们采用手机型号来做为预设内置的脚本,因此,同一个型号的手机理论上只需要录制一次,就可以将脚本应用到所有同型号的手机中进行生效。
脚本的录制的内容,大致如下面几个图片所示:
录制完毕后的脚本,最终会保存到智能拨号器的后台服务器中。在使用和用户账号的登录时加载和拉取到本地手机中进行使用。在电话呼叫过程中,如果满足SIM卡的切换条件,则会自动触发脚本进行SIM卡内容的切换。
切换后的默认SIM卡的手机号,应当可以从蓝牙HFP协议的AT+CNUM或其它方式将它读取出来,并实时作用到SIP注册账号等领域,供后台坐席进行切换后电话的业务拨打。
- 五、新功能和脚本引入带来的风险
由前文可知,这种切换方式主要风险在于引入了两个弹框权限【悬浮窗-Overlay】和【无障碍-Accessibility Service】,以及不同手机品牌厂商对【切换SIM卡操作的界面】访问路径和方式差异的脚本适配。如下图弹框授权的界面所示:
这里还是要多说一句:无障碍这个功能,不需要点那些默认的开关项,只需要点开进入【已下载的应用】里面,找到自己的那个应用app(如智能拨号器app),点进去,把它的开关打开即可(连应用的“快捷方式”的开关项都不用打开),就算完成了无障碍功能的授权。
写到这里,我其实想说有一个好消息和一个坏消息,但是踏马的真没有好消息。
坑爹的是,有部分手机(应该是大部分手机)无障碍的功能权限,需要应用每次启动运行的时候,都要手动再次弹一遍框然后再授一次权(即无障碍权限是跟进程号绑定的,应用退出了授权就失效了)。
这踏马要怎么玩?谁能告诉我要怎么玩?是安排一个人天天没事干就盯着屏幕,看看应用运行起来后会不会弹框然后点允许?还是应用能确保一旦运行就永远都不会崩溃,运行到手机报废都不会退出??真是喜极而泣,泪流满面。
- 六、反思与总结
这样的话,就真的不得不逼着我们进行反思了。这个使用“无障碍”功能+脚本重放点击,来实现双SIM卡切换的方式,实实在在是不具备商用的实际操作性。
操作又复杂、步骤又繁琐,一顿操作后好不容易稳定能用了,结果,应用退出了一下,居然要重新进行弹框授权??真是比大爷还难伺候。(这里我再次点名批评某些手机厂商,我X,手机熄屏你凭啥强杀我运行在前台最顶层的app,凭啥?你说它是不是有问题?最后再次特意提醒【智能拨号器app】的用户,手机运行过程中不要手动按电源熄屏,智能拨号器运行过程中加了电源锁了的,是永远不会熄屏的,不要手动熄屏)
人,只有知道了更多的知识,才会发现更多的未知。
方案走到这一步,我们开始进行总结和反思,我们做的到底是什么?为什么会需要引入这些莫名其妙、乱七八糟的东西?我们需要对外提供什么样的功能和能力?进而思考,我们到底需要的是什么?
从全局视图来看,我们可以很清晰的知道,智能拨号器app,就是一个app,而且是一个Android的app。与什么蓝牙方案、usb蓝牙、蓝牙HFP协议这些鬼东西完全毫无关系。我们是因为我们想用它(想走捷径),所以才引入了它,才使用它来对外提供服务。
但是现在,引入它了之后呢?到处都是被逼和妥协,到处都是没有办法、没有其它方案可选。就比如本篇前面我们说的:切换后的默认SIM卡的手机号,应当可以从蓝牙HFP协议的AT+CNUM或其它方式将它读取出来??真的是这样?操蛋的是我们实践发现,有部分手机,时灵时不灵,有时候它AT+CNUM死活返回的都是SIM卡1的手机号。坑爹不坑爹?
我们回过头看本篇前面篇章的分析:【
我们对于双卡双待的SIM卡需求,通常包括下述三个场景:
1、App运行的时候,应用应该能知道手机到底插入了几张SIM卡?每一张卡的手机号码分别是多少?
2、手机外呼的时候使用的是哪一张卡打出去的?能不能呼叫时手动指定或者代码中指定?如果不能,是否存在默认呼叫卡的方式来进行外呼操作?
如果外呼时需要指定默认呼叫卡的方式发起呼叫,假设当前默认SIM卡1,那我想用SIM卡2来呼叫时,如何去切换默认卡,能让电话能从SIM卡2中拨打出去?
】
我们开始思考是否真的有必要设置默认呼叫卡?我直接呼叫拨打电话行不行?哪怕我不用蓝牙电话方案、不用HFP协议、不用ATD10086;这样的呼叫指令外呼,可不可以?
一顿操作,发现真的可以。^V^,哎呀我的妈,之前那么多天的预研算是白搞了,方向走错走进死胡同了。
可行的方案就是下面章节的【最终的双卡呼叫方案或双卡切换方案】。
- 七、最终采用的双SIM卡切换方案
Android手机中,本身就可以使用代码Intent.ACTION_CALL的方式,在传递呼叫参数时,指定外呼的时候是使用SIM卡1,或是SIM卡2来进行外呼。^V^。
我们查阅了Android相关文档,此功能在API Level23、即Android6及之后的版本中生效。
智能拨号器app中,我们使用usb蓝牙与手机建立SLC蓝牙连接后,针对多SIM卡的场景,不再使用ATD10086;的方式进行外呼,而是直接采用app授权呼叫权限后,调用下述代码并传递SIM卡的卡号的方式进行指定SIM卡外呼。
然后在呼叫过程中,使用建立好的蓝牙SLC连接,接收+CLCC等事件状态反馈,获知呼叫的目标号码、振铃状态、接通/挂断状态,并将其同步到局域网的SIP服务器中,转接到局域网的呼叫坐席上。
通过以上方式,即可正常的实现SIM卡1和SIM卡2的对外呼叫。根本不需要设置什么默认呼叫卡、不需要做什么默认卡的设置和切换。几下就搞定了,一天天的净踏马的听他扯犊子。^V^。
为什么可以这样搞呢?为什么之前的电脑蓝牙打电话、手机蓝牙电话方案搞不了这种大智若愚、大巧不工的方案呢?根本原因是我们本来就是Android的app,我们也不考虑什么多系统的兼容和适配,去掉了中间商,“价格”和操作更加透明。
代码如下图所示。
- 八、结语
我们足足花了好几个月,使用16页来论证这个方案,结果最终方案就是两三句话,就实现了多SIM卡呼叫方案的选型和实现。一顿操作,结果你懂的。^V^。
无奈吧,人生有时就是这样无奈。
上一篇:蓝牙电话-如何设置双SIM卡自动切换策略(设想)
下一篇:手机实时提取SIM卡打电话的信令声音-(插播一条广告)蓝牙电话的Android版本-即将输出sdk
相关文章:

手机实时提取SIM卡打电话的信令声音-智能拨号器的双SIM卡切换方案
手机实时提取SIM卡打电话的信令声音 --智能拨号器app的双SIM卡切换方案 一、前言 在蓝牙电话的方案中,由于采用市场上的存量手机来做为通讯呼叫的载体,而现在市面上大部分的手机都是“双卡双待单通”手机,简称双卡双待手机。即在手机开机后…...

探索Python WebSocket新境界:picows库揭秘
文章目录 探索Python WebSocket新境界:picows库揭秘第一部分:背景介绍第二部分:picows库概述第三部分:安装picows库第四部分:简单库函数使用方法第五部分:场景应用第六部分:常见Bug及解决方案第…...

2024年11月24日Github流行趋势
项目名称:FreeCAD 项目维护者:wwmayer, yorikvanhavre, berndhahnebach, chennes, WandererFan等项目介绍:FreeCAD是一个免费且开源的多平台3D参数化建模工具。项目star数:20,875项目fork数:4,117 项目名称࿱…...

NewStar CTF week5 Crypto wp
easy_ecc ecc的模板题,稍加推理就会发现c1mc2*k因此做一个减法就行,需要注意的点是c1,c2必须放到ecc里面过一道才能出正确结果 k 86388708736702446338970388622357740462258632504448854088010402300997950626097 p 644088904089909773124499208053…...
vue3+antd注册全局v-loading指令
文章目录 1. 创建指令文件2. 全局注册3. 使用 1. 创建指令文件 src/directives 在directives中创建如下文件 src│─directives│ index.ts└─loadingindex.tsindex.vuedirectives/ index.ts export * from ./loadingdirectives/loading/index.ts import { createApp } f…...

初试无监督学习 - K均值聚类算法
文章目录 1. K均值聚类算法概述2. k均值聚类算法演示2.1 准备工作2.2 生成聚类用的样本数据集2.3 初始化KMeans模型对象,并指定类别数量2.4 用样本数据训练模型2.5 用训练好的模型生成预测结果2.6 输出预测结果2.7 可视化预测结果 3. 实战小结 1. K均值聚类算法概述…...

捉虫笔记(七)-再探谁把系统卡住了
捉虫笔记(七)-再探谁把系统卡住 1、内核调试 在实体物理机上,内核调试的第一个门槛就是如何建立调试链接。 这里我选择的建立网络连接进行内核调试。 至于如何建立网络连接后续文章再和大家分享。 2、如何分析 在上一篇文章中,我们…...

【Linux课程学习】:《简易版shell实现和原理》 《哪些命令可以让子进程执行,哪些命令让shell执行(内键命令)?为什么?》
🎁个人主页:我们的五年 🔍系列专栏:Linux课程学习 🌷追光的人,终会万丈光芒 🎉欢迎大家点赞👍评论📝收藏⭐文章 目录 打印命令行提示符(PrintCommandLin…...

2024年11月27日Github流行趋势
项目名称:screenshot-to-code 项目维护者:abi clean99 sweep-ai kachbit vagusX项目介绍:通过上传截图将其转换为整洁的代码(支持HTML/Tailwind/React/Vue)。项目star数:62,429项目fork数:7,614…...

Java中的线程池使用详解
文章目录 Java中的线程池使用详解一、引言二、线程池的创建与使用1、线程池的创建1.1、FixedThreadPool(固定大小线程池)1.2、CachedThreadPool(可缓存线程池)1.3、SingleThreadExecutor(单线程化线程池)1.…...

Redis(概念、IO模型、多路选择算法、安装和启停)
一、概念 关系型数据库是典型的行存储数据库,存在的问题是,按行存储的数据在物理层面占用的是连续存储空间,不适合海量数据存储。 Redis在生产中使用的最多的是用作数据缓存。 服务器先在缓存中查询数据,查到则返回,…...

计算机网络 第4章 网络层
计算机网络 (第八版)谢希仁 第 4 章 网络层4.2.2 IP地址**无分类编址CIDR**IP地址的特点 4.2.3 IP地址与MAC地址4.2.4 ARP 地址解析协议4.2.5 IP数据报的格式题目2:IP数据报分片与重组题目:计算IP数据报的首部校验和(不正确未改) …...

Java学习笔记--继承方法的重写介绍,重写方法的注意事项,方法重写的使用场景,super和this
目录 一,方法的重写 二,重写方法的注意事项 三,方法重写的使用场景 四,super和this 1.继承中构造方法的特点 2.super和this的具体使用 super的具体使用 this的具体使用 一,方法的重写 1.概述:子类中有一个和父类…...
高级java每日一道面试题-2024年11月27日-JVM篇-JVM的永久代中会发生垃圾回收么?
如果有遗漏,评论区告诉我进行补充 面试官: JVM的永久代中会发生垃圾回收么? 我回答: 在Java虚拟机(JVM)的历史版本中,确实存在一个称为“永久代”(Permanent Generation, 或者简称PermGen)的内存区域。永久代主要用…...

Spring Boot教程之十: 使用 Spring Boot 实现从数据库动态下拉列表
使用 Spring Boot 实现从数据库动态下拉列表 动态下拉列表(或依赖下拉列表)的概念令人兴奋,但编写起来却颇具挑战性。动态下拉列表意味着一个下拉列表中的值依赖于前一个下拉列表中选择的值。一个简单的例子是三个下拉框,分别显示…...

基于混合ABC和A*算法复现
基于混合ABC和A*算法复现 一、背景介绍二、算法原理(一)A*算法原理(二)人工蜂群算法原理(三)混合ABC和A*算法策略 三、代码实现(一)数据准备(二)关键函数实现…...

狂野飙车8+(Asphalt 8+) for Mac 赛车竞速游戏 安装教程
Mac分享吧 文章目录 狂野飙车8(Asphalt 8) for Mac 赛车竞速游戏软件 效果图展示一、狂野飙车8(Asphalt 8) 赛车竞速游戏 Mac电脑版——v2.1.11️⃣:下载软件2️⃣:安装软件2.1 左侧安装包拖入右侧文件夹中,等待安装完成,运行软件…...
网络技术-VRRP(虚拟路由冗余协议)部署介绍
一、VRRP的含义 VRRP(Virtual Router Redundancy Protocol,虚拟路由冗余协议)是一种高度可靠的路由器备用协议,用于在局域网内部提供路由器冗余。 其部署方式主要是通过多个路由器组成一个虚拟路由器组,通过协议选…...
C语言解决空瓶换水问题:高效算法与实现
标题:C语言解决空瓶换水问题:高效算法与实现 一、问题描述 在一个饮料促销活动中,你可以通过空瓶换水的方式免费获得更多的水:3个空瓶可以换1瓶水。喝完这瓶水后,空瓶会再次变为空瓶。假设你最初拥有一定数量的空瓶&a…...

day2全局注册
全局注册代码: //文件核心作用:导入App.vue,基于App.vue创建结构渲染index.htmlimport Vue from vue import App from ./App.vue //编写导入的代码,往代码的顶部编写(规范) import HmButton from ./components/Hm-But…...
变量 varablie 声明- Rust 变量 let mut 声明与 C/C++ 变量声明对比分析
一、变量声明设计:let 与 mut 的哲学解析 Rust 采用 let 声明变量并通过 mut 显式标记可变性,这种设计体现了语言的核心哲学。以下是深度解析: 1.1 设计理念剖析 安全优先原则:默认不可变强制开发者明确声明意图 let x 5; …...

docker详细操作--未完待续
docker介绍 docker官网: Docker:加速容器应用程序开发 harbor官网:Harbor - Harbor 中文 使用docker加速器: Docker镜像极速下载服务 - 毫秒镜像 是什么 Docker 是一种开源的容器化平台,用于将应用程序及其依赖项(如库、运行时环…...
Leetcode 3577. Count the Number of Computer Unlocking Permutations
Leetcode 3577. Count the Number of Computer Unlocking Permutations 1. 解题思路2. 代码实现 题目链接:3577. Count the Number of Computer Unlocking Permutations 1. 解题思路 这一题其实就是一个脑筋急转弯,要想要能够将所有的电脑解锁&#x…...

智能在线客服平台:数字化时代企业连接用户的 AI 中枢
随着互联网技术的飞速发展,消费者期望能够随时随地与企业进行交流。在线客服平台作为连接企业与客户的重要桥梁,不仅优化了客户体验,还提升了企业的服务效率和市场竞争力。本文将探讨在线客服平台的重要性、技术进展、实际应用,并…...

HBuilderX安装(uni-app和小程序开发)
下载HBuilderX 访问官方网站:https://www.dcloud.io/hbuilderx.html 根据您的操作系统选择合适版本: Windows版(推荐下载标准版) Windows系统安装步骤 运行安装程序: 双击下载的.exe安装文件 如果出现安全提示&…...

c#开发AI模型对话
AI模型 前面已经介绍了一般AI模型本地部署,直接调用现成的模型数据。这里主要讲述讲接口集成到我们自己的程序中使用方式。 微软提供了ML.NET来开发和使用AI模型,但是目前国内可能使用不多,至少实践例子很少看见。开发训练模型就不介绍了&am…...
Hive 存储格式深度解析:从 TextFile 到 ORC,如何选对数据存储方案?
在大数据处理领域,Hive 作为 Hadoop 生态中重要的数据仓库工具,其存储格式的选择直接影响数据存储成本、查询效率和计算资源消耗。面对 TextFile、SequenceFile、Parquet、RCFile、ORC 等多种存储格式,很多开发者常常陷入选择困境。本文将从底…...

视频行为标注工具BehaviLabel(源码+使用介绍+Windows.Exe版本)
前言: 最近在做行为检测相关的模型,用的是时空图卷积网络(STGCN),但原有kinetic-400数据集数据质量较低,需要进行细粒度的标注,同时粗略搜了下已有开源工具基本都集中于图像分割这块,…...

深入浅出深度学习基础:从感知机到全连接神经网络的核心原理与应用
文章目录 前言一、感知机 (Perceptron)1.1 基础介绍1.1.1 感知机是什么?1.1.2 感知机的工作原理 1.2 感知机的简单应用:基本逻辑门1.2.1 逻辑与 (Logic AND)1.2.2 逻辑或 (Logic OR)1.2.3 逻辑与非 (Logic NAND) 1.3 感知机的实现1.3.1 简单实现 (基于阈…...
Java求职者面试指南:计算机基础与源码原理深度解析
Java求职者面试指南:计算机基础与源码原理深度解析 第一轮提问:基础概念问题 1. 请解释什么是进程和线程的区别? 面试官:进程是程序的一次执行过程,是系统进行资源分配和调度的基本单位;而线程是进程中的…...