Car Messenger

Car Messenger 可提供专为汽车设备设计的消息功能。与其他汽车应用一样,用户从启动器启动 Car Messenger。

Car Messenger 的新变化

借助全新的 Car Messenger 应用,驾驶员可以:

  • 获享专属消息功能。
  • 从启动器启动 Car Messenger。
  • 浏览在开车前和开车期间收到的消息。
  • 收听和回复消息。
  • 忽略消息通知。
  • 发起新对话。

术语

本页使用了以下术语:

点读 (TTR)
利用点读功能,语音助理能够在用户与消息通知互动时朗读短信,并代表用户进行回复。

直接回复
与点读功能类似,不同之处在于语音助理不会朗读消息并立即发出回复提示。

直接发送
与语音助理集成,以编写新的消息流,无论是否指定联系人。

未捆绑应用的优势

未捆绑应用(例如 Car Messenger)具有以下优势:

  • 仅使用公共方法(不对隐藏 API 具有平台依赖性)
  • 在 Android 平台之外开发应用
  • 发布版本更频繁(用于发布新功能和修复问题)
  • 通过 Google Play 更新应用

详细了解未捆绑应用

技术详情

本部分介绍了 Car Messenger 的架构。如需了解详情,请参阅与 CarVoiceInteractionSession 集成

基于电话的架构

通过蓝牙配对时,数据会从手机的电话数据库同步到汽车的电话数据库。 蓝牙断开连接后,系统会从汽车的电话数据库中删除已同步的数据。

Android 12 中引入了此功能。主要优势包括:

  • 可以从数据库中检索批量用户消息。
  • 支持之前驾车的消息。
  • 在 Android 手机上使用类似的架构和 API 来存储和检索短信。
  • 从 Android 平台完全解除捆绑。

具体流程如下:

基于电话的数据传输 图 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.

以下是我们对数据执行的操作:

Car Messenger 流量消耗 图 2. Car Messenger 数据处理。

以文字形式说明的传输过程:

 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 将数据从手机传输到汽车。
  • 断开连接后,系统会从汽车的数据库中删除该手机的数据。

下载 Car Messenger

从 Google Git 获取最新的 Car Messenger 提交内容。

语音交互 API

Car Messenger 使用 CarVoiceInteractionSession API 与助理集成。下文将对这些元素进行描述。

PendingIntent 模型

这些 API 使用 PendingIntent 将已解析的助理查询传回给 Car Messenger。

以下是事件的顺序:

  1. Car Messenger 通过调用 activity.showAssist(Bundle args) 启动助理。参数包含 API 操作及其必需参数,并根据需要包含一个待处理 intent。

  2. 助理会根据需要检索用户输入,并将其与待处理 intent 打包在一起。

  3. 助理会将 intent 发回 Car Messenger。

  4. Car Messenger 解析 API 操作。

标记为已读 API 操作

当助理朗读消息时,系统会将 PendingIntent 发送给 Car Messenger,并使用 VOICE_ACTION_READ_NOTIFICATIONVOICE_ACTION_READ_CONVERSATION 操作将消息标记为已读。

Direct Reply API 操作

当助理回复消息时,系统会将 PendingIntent 发送给 Car Messenger,并使用 VOICE_ACTION_REPLY_NOTIFICATIONVOICE_ACTION_REPLY_CONVERSATION 操作来回复对话。

Direct Send SMS API 操作

包含 VOICE_ACTION_SEND_SMS 操作的软件包会从 Car Messenger 发送到助理。

示例代码:

/**
 *   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. 拨号器应用中的“通讯录”页面。

下图展示了在未选择接收人时使用新消息按钮撰写消息的情况:

未选择任何接收人 图 4. Messenger 应用中的“新消息”按钮。

集成 Direct Send SMS 操作

以下是拨号器集成 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 类的要求。

调试 Car Messenger

请参阅以下部分,详细了解如何调试 Car Messenger。

调试蓝牙连接

  1. 运行命令 dumpsys

    adb shell dumpsys bluetooth_manager
    
    • 在 dumpsys 命令输出中搜索 MapClientService
     Profile: MapClientService
          mCurrentDevice: 99:99 (Pixel XL) name=Mce state=Connected
    
  2. 确认列出的设备正确无误。例如:

    设备列表 图 5. 设备列表。

  3. 如果未找到任何设备,请执行以下操作之一

    • 重新连接蓝牙。

    OR

    • 蓝牙设置中,确认短信已开启。

    OR

    • 在手机上,确认已授予消息访问权限

调试蓝牙数据库

Car Messenger 基于电话数据库构建而成。如需确定蓝牙是否正在填充该数据库,您可以使用表中的命令。

任务 命令
对话 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

调试 Car Messenger 和语音助理查询

如果构建映像为 enguserdebug,则默认输出日志。否则,要为 Car Messenger 启用日志记录,请按以下步骤操作:

  1. 针对相关标记 adb shell setprop log.tag.<TAG> DEBUG 运行。

  2. 预加载的助理启用日志记录功能。

  3. 对于可重现率极高的 bug,请考虑通过 Android Studio 使用断点。