手机实时提取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^。
无奈吧,人生有时就是这样无奈。