车载消息应用提供专为车载设备设计的消息功能。与其他车载应用一样,用户可以从启动器启动车载消息应用。
车载消息应用的新变化
借助新的车载消息应用,驾驶员可以:
- 获得专门的消息体验。
- 从启动器启动车载消息应用。
- 浏览驾驶之前和驾驶期间收到的消息。
- 收听和回复消息。
- 将消息通知设为静音。
- 发起新的对话。
术语表
本页使用了以下术语:
解绑应用的优势
解绑应用(例如车载消息应用)具有以下优势:
- 仅使用公共方法(不依赖于隐藏 API 的平台依赖项)
- 在 Android 平台之外开发应用
- 实现更频繁的版本发布(用于新功能和已修复的问题)
- 通过 Google Play 更新应用
详细了解解绑应用。
技术细节
本部分介绍了车载消息应用架构。如需了解详情,请参阅与 CarVoiceInteractionSession 集成。
基于电话功能的架构
通过蓝牙配对后,数据会从手机的电话功能数据库同步到汽车的电话功能数据库。断开蓝牙连接后,同步的数据会从汽车的电话功能数据库中删除。
此功能在 Android 12 中引入。主要优势包括:
- 可以从数据库中检索批量用户消息。
- 支持之前驾驶期间的消息。
- 对 Android 手机上的短信存储和检索使用类似的架构和 API。
- 完全从 Android 平台解绑。
以下是流程:
 图 1. 基于电话功能的数据流。
 图 1. 基于电话功能的数据流。
以文本格式说明的流程
 1. Phone connects to car.
    |
    --> 2. SMS data transferred from phone's database to car database.
          |
          --> 3. Car Messenger retrieves data from telephony database to display on UI.
                  |
                  --> 4. User interactions prompt the voice assistant.
                  |
          <-- 5. Car Messenger receives reply action from the voice assistant.
          |
    <-- 6. SMS is marked as read in car database.
    |
 7. Reply transmitted to recipients, phone database updated with reply and read status.
以下是我们如何处理数据:
 图 2. 车载消息应用数据处理。
 图 2. 车载消息应用数据处理。
以文本格式说明的流程
 1. Phone connects to car.
    |
    --> 2. SMS data transferred from phone's database to car database.
          |
          --> 3. Phone disconnects from car.
                  |
                  --> 4. SMS data deleted from car telephony database.
- 连接时,数据会使用蓝牙 MAP 从手机传输到汽车。
- 断开连接时,该手机的数据会从汽车的数据库中删除。
获取车载消息应用
从 Google Git 获取最新的 车载消息应用提交。
语音互动 API
车载消息应用使用 CarVoiceInteractionSession API 与助理集成。以下部分介绍了这些元素。
PendingIntent 模型
这些 API 使用 PendingIntent 将已解析的助理查询传递回车载消息应用。
以下是事件的顺序:
- 车载消息应用通过调用 - activity.showAssist(Bundle args)启动助理。args 包含 API 操作及其所需参数,如果需要,则包含 pending intent。
- 助理检索必要的用户输入,并将其与 pending intent 打包在一起。 
- 助理将 intent 传回车载消息应用。 
- 车载消息应用 解析 API 操作。 
标记为已读 API 操作
当助理正在朗读消息时,系统会向车载消息应用发送 PendingIntent,其中包含操作 VOICE_ACTION_READ_NOTIFICATION 或 VOICE_ACTION_READ_CONVERSATION,以将消息标记为已读。
直接回复 API 操作
当助理正在回复消息时,系统会向车载消息应用发送 PendingIntent,其中包含操作 VOICE_ACTION_REPLY_NOTIFICATION 和 VOICE_ACTION_REPLY_CONVERSATION,以回复对话。
直接发送短信 API 操作
系统会将包含 VOICE_ACTION_SEND_SMS 操作的软件包从车载消息应用发送到助理。
示例代码
/**
 *   KEY_PHONE_NUMBER - Recipient’s phone number. If this and the recipients name are not
 *   provided by the application, assistant must do contact disambiguation but is not required
 *   to add the name to the PendingIntent.
 *
 *   KEY_RECIPIENT_NAME - Recipient’s name. If this and the recipient phone number are not
 *   provided by the application, assistant must do contact disambiguation but is not required
 *   to add the name to the PendingIntent.
 *
 *   KEY_RECIPIENT_UID - Recipient’s UID in the ContactProvider database. Optionally provided
 *   by the application. Not required to be sent back by the assistant.
 *
 *   KEY_DEVICE_NAME - Friendly name of the device in which to send the message from. If not
 *   provided by the application, assistant must do device disambiguation but is not required
 *   to add it to PendingIntent. In V1 this is required to be sent by the application.
 *
 *   KEY_DEVICE_ADDRESS - Bluetooth device address of the device in which to send the message
 *   from. If not provided by the application, assistant must do device disambiguation and add
 *   this to the PendingIntent. In V1 this is required to be sent by the application.
 *
 *   KEY_SEND_PENDING_INTENT - @NotNull Will always be provided by the application. The
 *   application must preload the pending intent with any KEYs it provides the assistant that
 *   is also needed to send the message. (I.e if the application passes in the
 *   KEY_PHONE_NUMBER in the Bundle, the assistant can assume the application has already put
 *   this in the PendingIntent and may not re-add it to the PendingIntent).
 *
 */
public static final String KEY_PHONE_NUMBER = “KEY_PHONE_NUMBER”;
public static final String KEY_RECIPIENT_NAME = “KEY_RECIPIENT_NAME”;
public static final String KEY_RECIPIENT_UID = “KEY_RECIPIENT_UID”;
public static final String KEY_DEVICE_NAME = “KEY_DEVICE_NAME”;
public static final String KEY_DEVICE_ADDRESS = “KEY_DEVICE_NAME”;
public static final String KEY_SEND_PENDING_INTENT =”KEY_SEND_PENDING_INTENT”;
下图展示了选择收件人时撰写消息的情况:
 图 3. 拨号器应用中的“联系人”页面。
 图 3. 拨号器应用中的“联系人”页面。
下图展示了使用新消息时,在未选择收件人的情况下撰写消息的情况:
 图 4. 消息应用中的“新消息”按钮。
 图 4. 消息应用中的“新消息”按钮。
集成“直接发送短信”操作
以下是拨号器集成 VOICE_ACTION_SEND_SMS 并提供可选参数的示例:
    /**
     * Build the {@link Bundle} to pass to assistant to send a sms.
     */
    public Bundle buildDirectSendBundle(String number, String name, String uid,
                                        BluetoothDevice device) {
        Bundle bundle = new Bundle();
        bundle.putString(CarVoiceInteractionSession.KEY_ACTION, VOICE_ACTION_SEND_SMS);
        // start optional parameters
        bundle.putString(CarVoiceInteractionSession.KEY_PHONE_NUMBER, number);
        bundle.putString(CarVoiceInteractionSession.KEY_RECIPIENT_NAME, name);
        bundle.putString(CarVoiceInteractionSession.KEY_RECIPIENT_UID, uid);
        // end optional parameters
        bundle.putString(CarVoiceInteractionSession.KEY_DEVICE_ADDRESS, device.getAddress());
        bundle.putString(CarVoiceInteractionSession.KEY_DEVICE_NAME,
                DialerUtils.getDeviceName(mContext, device));
        Intent intent = new Intent(mContext, MessagingService.class)
                .setAction(ACTION_DIRECT_SEND)
                .setClass(mContext, MessagingService.class);
        int requestCode = ACTION_DIRECT_SEND.hashCode();
        PendingIntent pendingIntent = PendingIntent.getForegroundService(
                mContext, requestCode, intent,
                PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_IMMUTABLE);
        bundle.putParcelable(KEY_SEND_PENDING_INTENT, pendingIntent);
        return bundle;
    }
TTR 和“直接回复”的增强功能
更新后的 API 现在使用通用的 Conversation 类,允许超出通知范围的操作,并在应用的上下文中扩展功能。这取代了之前使用 StatusBarNotification 类的要求。
调试车载消息应用
请参阅以下部分,详细了解如何调试车载消息应用。
调试蓝牙连接
- 运行 - dumpsys命令- adb shell dumpsys bluetooth_manager- 在 dumpsys 命令输出中搜索 MapClientService。
 - Profile: MapClientService mCurrentDevice: 99:99 (Pixel XL) name=Mce state=Connected
- 在 dumpsys 命令输出中搜索 
- 确认列出的设备是否正确。例如:  图 5. 设备列表。 图 5. 设备列表。
- 如果未找到设备,请执行以下一项操作: - 重新连接到蓝牙。
 - 或 - 在蓝牙设置中,确认短信已开启。
 - 或 - 在手机上,确认已授予消息访问权限。
 
调试蓝牙数据库
车载消息应用基于电话功能数据库构建。要确定蓝牙是否正在填充该数据库,您可以使用下表中的命令。
| 任务 | 命令 | 
|---|---|
| 对话 | adb shell content query--uri content://mms-sms/conversations?simple=true | 
| 仅限短信 | adb shell content query--uri content://sms | 
| 彩信/短信 | adb shell content query--uri content://mms-sms/conversations | 
| 仅限彩信 | adb shell content query--uri content://mms | 
| 仅限彩信收件箱 | adb shell content query--uri content://mms/conversations/inbox | 
| 仅限已发送短信 | adb shell content query--uri content://sms/sent | 
| 仅限短信收件箱 | adb shell content query--uri content://sms/conversations/inbox | 
| 彩信消息部分 1 (将 1替换为彩信的 ID) | adb shell content query--uri content://mms/part/1 | 
调试车载消息应用和语音助手查询
如果构建映像是 eng 或 userdebug,则默认情况下会打印日志。否则,要为车载消息应用启用日志记录:
- 针对相关标记运行 - adb shell setprop log.tag.<TAG> DEBUG。
- 为预加载的助理启用日志记录。 
- 对于高度可重现的错误,请考虑将断点与 Android Studio 结合使用。