Car Messenger

Car Messenger offers messaging capabilities designed for automotive devices. Like other car apps, users start Car Messenger from the launcher.

What's new in Car Messenger?

With the new Car Messenger app, drivers:

  • Get a dedicated messaging experience.
  • Launch Car Messenger from the launcher.
  • Browse messages received before and during a drive.
  • Listen to and reply messages.
  • Mute message notifications.
  • Start new conversations.

Terminology

These terms are used on this page:

Tap-to-Read (TTR)
Tap-to-Read enables voice assistants to read and reply to text messages on behalf of that user, when the user interacts with message notifications.

Direct Reply
Similar to Tap-to-Read, except the voice assistant doesn't read out the message and immediately issues a prompt for a reply.

Direct Send
Integrates with a voice assistant to compose a new message flow with or without a specified contact.

Benefits of unbundled apps

Unbundled apps, such as Car Messenger, provide these advantages:

  • Uses public methods only (no platform dependencies on hidden APIs)
  • Develop apps outside the Android platform
  • Enable more frequent releases (for new features and fixed issues)
  • Update apps through Google Play

Learn more about unbundled apps.

Technical details

This section describes Car Messenger architecture. To learn more, see Integrate with CarVoiceInteractionSession.

Telephony-based architecture

When paired over Bluetooth, data is synchronized from the phone's telephony database to the car's telephony database. Upon Bluetooth disconnection, the synchronized data is deleted from the car's telephony database.

This capability was introduced in Android 12. The primary benefits include:

  • Bulk user messages can be retrieved from a database.
  • Messages from previous drives are supported.
  • Uses a similar architecture and API for SMS storage and retrieval on Android phones.
  • Becomes fully unbundled from the Android platform.

Here's the flow:

Telephony-based data flow Figure 1. Telephony-based data flow.

Flow illustrated in text format:

 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.

Here's what we do with the data:

Car Messenger data usage Figure 2. Car Messenger data handling.

Flow illustrated in text format:

 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.
  • On connect, the data is transferred from phone to the car using Bluetooth MAP.
  • On disconnect, the data for that phone is deleted from the car's database.

Get Car Messenger

Get the latest Car Messenger commit from Google Git.

Voice interaction APIs

Car Messenger uses CarVoiceInteractionSession APIs to integrate with the assistant. These elements are described in the following sections.

PendingIntent model

These APIs use PendingIntent to pass resolved assistant queries back to Car Messenger.

This is the sequence of events:

  1. Car Messenger launches assistant by calling activity.showAssist(Bundle args). The args contain the API action and its required parameters, containing a pending intent if needed.

  2. Assistant retrieves user input if necessary, and packages that with the pending intent.

  3. Assistant sends the intent back to Car Messenger.

  4. Car Messenger resolves the API action.

Mark as read API action

When assistant is reading a message, PendingIntent is sent to Car Messenger with the action VOICE_ACTION_READ_NOTIFICATION or VOICE_ACTION_READ_CONVERSATION to mark the message as read.

Direct Reply API action

When assistant is replying to a message, PendingIntent is sent to Car Messenger with the action VOICE_ACTION_REPLY_NOTIFICATION and VOICE_ACTION_REPLY_CONVERSATION to reply to the conversation.

Direct Send SMS API action

A bundle with the VOICE_ACTION_SEND_SMS action is sent from Car Messenger to the assistant.

Sample code:

/**
 *   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”;

This image shows composing a message when a recipient is selected:

Contacts page of the Dialer app Figure 3. Contacts page in the Dialer app.

This image shows composing a message when no recipient is selected using New Message:

No selected recipient Figure 4. New Message button in Messenger app.

Integrate Direct Send SMS action

Here's an example of Dialer integrating VOICE_ACTION_SEND_SMS, providing optional parameters:

    /**
     * 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;
    }

Enhancements to TTR and Direct Reply

The updated API now uses a versatile Conversation class, allowing operations beyond the notification realm and extending capabilities within the app's context. This replaces the prior requirement of using the StatusBarNotification class.

Debug Car Messenger

See the following sections to learn more about debugging Car Messenger.

Debug Bluetooth connections

  1. Run the dumpsys command:

    adb shell dumpsys bluetooth_manager
    
    • Search for MapClientService in the dumpsys command output.
     Profile: MapClientService
          mCurrentDevice: 99:99 (Pixel XL) name=Mce state=Connected
    
  2. Confirm that the correct device is listed. For example:

    Device list Figure 5. Device list.

  3. If no device is found, do one the following:

    • Reconnect to Bluetooth.

    OR

    • In Bluetooth Settings, confirm that Text Message is on.

    OR

    • On the phone, confirm that Message Access is granted.

Debug Bluetooth databases

Car Messenger is built on the telephony database. To determine if Bluetooth is populating that database, you can use the commands in the table.

Task Command
Conversation adb shell content query--uri content://mms-sms/conversations?simple=true
SMS messages only adb shell content query--uri content://sms
MMS/SMS messages adb shell content query--uri content://mms-sms/conversations
MMS messages only adb shell content query--uri content://mms
MMS inbox only adb shell content query--uri content://mms/conversations/inbox
SMS sent messages only adb shell content query--uri content://sms/sent
SMS inbox only adb shell content query--uri content://sms/conversations/inbox
MMS message part 1
(replace 1 with the ID of the MMS)
adb shell content query--uri content://mms/part/1

Debug Car Messenger and voice assistant queries

Logs print by default if the build image is eng or userdebug. Otherwise, to enable logging for Car Messenger:

  1. Run for the relevant tags adb shell setprop log.tag.<TAG> DEBUG.

  2. Enable logging for Preloaded Assistant.

  3. For highly reproducible bugs, consider using breakpoints with Android Studio.