第10章 车载电话与通讯:CarPhoneService、通话状态管理、联系人同步、短信与通知
车载电话模块,说实话,是汽车智能化里最容易被低估的一块。很多人觉得不就是个蓝牙电话嘛,但真正做过才知道,这里面的坑多到能让你怀疑人生。我当年接手第一个车载项目时,就天真地以为调通HFP(Hands-Free Profile)就完事了,结果被通话状态管理、联系人同步这些细节折磨得够呛。
今天我们就来聊聊Android Automotive里电话与通讯的那些事。我会把CarPhoneService、通话状态管理、联系人同步、短信与通知这几个核心点掰开揉碎了讲。
10.1 CarPhoneService:车载电话的“大脑”
CarPhoneService是Android Automotive中专门管理电话功能的服务。它不像手机上的TelephonyManager那样直接操作SIM卡,而是通过蓝牙HFP与手机交互。
说白了,车机本身不打电话,它只是手机的“遥控器”加“扬声器”。
核心职责:
- 管理蓝牙HFP连接
- 转发通话状态到UI层
- 处理联系人同步请求
- 管理通话记录
它的架构其实挺清晰的。我画了张图,你看一眼就明白了:
你看,CarPhoneService处在中间层,向上给UI提供接口,向下管理蓝牙协议。我习惯把它理解成一个“翻译官”——把手机那边的电话事件,翻译成车机能听懂的语言。
10.2 通话状态管理:别让用户懵圈
通话状态管理,说白了就是让车机知道“现在电话在干嘛”。是正在响铃?还是通话中?还是挂断了?
Android Automotive里定义了这么几种状态:
| 状态 | 含义 | 典型场景 |
|---|---|---|
| CALL_STATE_IDLE | 空闲 | 没有通话 |
| CALL_STATE_RINGING | 来电响铃 | 有来电,等待接听 |
| CALL_STATE_OFFHOOK | 摘机 | 通话中或正在拨号 |
| CALL_STATE_DIALING | 拨号中 | 正在呼出 |
| CALL_STATE_ALERTING | 对方响铃 | 拨出后等待对方接听 |
嗯,这里要注意。CALL_STATE_OFFHOOK和CALL_STATE_DIALING是有重叠的。我刚开始做的时候,就踩过这个坑——以为OFFHOOK就是通话中,结果发现拨号时也是OFFHOOK。后来我加了个子状态判断才搞定。
我的经验:建议在UI层不要直接依赖TelephonyManager的通话状态,而是通过CarPhoneService封装一层。这样即使底层协议变了,UI也不用改。
来看一段核心代码,这是监听通话状态的标准写法:
// 注册通话状态监听
TelephonyManager telephonyManager =
(TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
telephonyManager.listen(new PhoneStateListener() {
@Override
public void onCallStateChanged(int state, String phoneNumber) {
switch (state) {
case TelephonyManager.CALL_STATE_IDLE:
// 空闲状态,更新UI
updateCallUI(CallState.IDLE);
break;
case TelephonyManager.CALL_STATE_RINGING:
// 来电,显示来电界面
showIncomingCallUI(phoneNumber);
break;
case TelephonyManager.CALL_STATE_OFFHOOK:
// 通话中,显示通话界面
showActiveCallUI(phoneNumber);
break;
}
}
}, PhoneStateListener.LISTEN_CALL_STATE);
这段代码看着简单,但实际项目中你还要处理多路通话、通话保持、三方通话这些复杂场景。我曾经在一个项目中,用户反馈说“挂断电话后界面还显示通话中”,查了半天发现是状态回调延迟导致的。解决方案是加了个超时机制——如果IDLE状态超过500ms还没收到,就强制重置UI。
10.3 联系人同步:PBAP协议的那些事
联系人同步,用的是PBAP(Phone Book Access Profile)。车机通过蓝牙从手机拉取联系人数据。
这里有个关键点:PBAP有两种同步方式。
- 全量同步:每次连接时,把手机通讯录全部拉下来。简单粗暴,但耗时长。
- 增量同步:只同步变化的部分。效率高,但实现复杂。
我个人建议,第一次连接时做全量同步,之后用增量同步。但要注意,有些手机对PBAP的支持不太好,增量同步可能会漏掉联系人。我遇到过一台某品牌手机,每次增量同步都会漏掉姓“张”的联系人,最后只能改成每次全量同步。
避坑指南:我曾经因为联系人同步太慢被客户投诉。后来发现是数据库写入没做批量处理,一条一条insert当然慢。改成ContentProvider的bulkInsert后,速度提升了10倍。
联系人数据在车机端的存储,我建议用ContentProvider封装。这样其他应用(比如语音助手、导航)也能方便地读取联系人。
// 批量插入联系人
ContentValues[] valuesArray = new ContentValues[contactList.size()];
for (int i = 0; i < contactList.size(); i++) {
Contact contact = contactList.get(i);
ContentValues values = new ContentValues();
values.put(ContactsContract.Contacts.DISPLAY_NAME, contact.getName());
values.put(ContactsContract.Contacts.PHOTO_URI, contact.getPhotoUri());
// ... 其他字段
valuesArray[i] = values;
}
getContentResolver().bulkInsert(ContactsContract.Contacts.CONTENT_URI, valuesArray);
你想想看,如果不用bulkInsert,1000个联系人就要执行1000次数据库操作,那体验能好吗?
10.4 短信与通知:MAP协议的应用
短信和通知,用的是MAP(Message Access Profile)。车机通过这个协议读取手机上的短信和通知。
MAP协议分两种角色:
- MSE(Message Server Equipment):手机端,提供消息数据
- MCE(Message Client Equipment):车机端,消费消息数据
车机作为MCE,向手机MSE请求消息列表,然后解析显示。这里有个细节——MAP协议传输的是纯文本,所以表情符号、富文本这些需要车机自己处理。
核心流程:
- 车机通过蓝牙与手机建立MAP连接
- 车机发送GetMessageListing请求,获取消息列表
- 车机根据消息ID,发送GetMessage请求获取具体内容
- 车机解析消息内容,显示到UI上
- 收到新消息通知时,车机主动拉取新消息
说实话,短信这块现在用得越来越少了。大家更习惯用微信、WhatsApp这些。但车载系统还是要支持,毕竟有些场景下(比如开车时),语音播报短信比看手机安全得多。
通知这块,Android Automotive有自己的一套通知管理机制。车机上的通知和手机上的通知是独立的,但可以通过蓝牙同步。我建议把通知分成三类:
| 通知类型 | 处理方式 | 示例 |
|---|---|---|
| 紧急通知 | 立即播报+显示 | 安全警报、紧急联系人来电 |
| 普通通知 | 显示但不播报 | 短信、App推送 |
| 静默通知 | 仅记录,不打扰 | 系统更新、后台任务 |
嗯,这里要注意。开车时不要一股脑把所有通知都播报出来,那会分散驾驶员注意力。我见过一个项目,把微信群里每条消息都播报,结果驾驶员差点撞车。后来我们加了个“驾驶模式”开关,只有紧急通知才会播报。
我的习惯:在实现通知功能时,我会先定义一个通知优先级枚举。然后根据当前驾驶状态(行驶中/停车中)动态调整通知的展示方式。这样既安全又灵活。
最后说一句,车载电话与通讯这块,技术实现其实不难,难的是在各种手机、各种蓝牙芯片上都能稳定工作。我建议你在开发阶段就准备至少10台不同品牌的手机做兼容性测试。别问我怎么知道的——都是泪的教训。
公众号:蓝海资料掘金营,微信deep3321