Bluetooth

Android provides a full Bluetooth implementation with support for many common in-car Bluetooth profiles. There are also many enhancements that improve the performance and experience with other devices and services.

Bluetooth connection management

Within Android, CarBluetoothService maintains the current user's Bluetooth devices and priority lists for each profile connection to the IVI. Devices are connected to profiles in a defined priority order. When to enable, disable, and connect devices to a profile is driven by a default connection policy that can be overridden with the use of a resource overlay, if desired.

Configure automotive connection management

Disable the default phone policy

The Android Bluetooth stack maintains a connection policy for phones that is enabled by default. This policy must be disabled on your device so that it doesn't conflict with the intended automotive policy in CarBluetoothService. While the Car product overlay should take care of this for you, you can disable the phone policy in a resource overlay by setting enable_phone_policy to false in MAXIMUM_CONNECTED_DEVICES in /packages/apps/Bluetooth/res/values/config.xml.

Use the default automotive policy

CarBluetoothService maintains the default profile permissions. The list of known devices and their profile re-connection priorities is in service/src/com/android/car/BluetoothProfileDeviceManager.java.

As well, the Bluetooth connection management policy can be found in service/src/com/android/car/BluetoothDeviceConnectionPolicy.java. By default, this policy defines instances when Bluetooth should connect to and disconnect from bonded devices. It also manages car-specific cases for when the adapter should be turned on and off.

Create your own custom automotive connection management policy

If the default automotive policy is not sufficient for your needs, it can also be disabled in favor of your own custom policy. Your custom policy, at a minimum, is responsible for determining when to enable and disable the Bluetooth adapter, as well as when to connect devices. It's possible to use a variety of events to enable/disable the Bluetooth adapter and to initiate device connections, including events due to changes in specific car properties.

Disable the default automotive policy

First, to use a custom policy, the default automotive policy must be disabled by setting useDefaultBluetoothConnectionPolicy to false in a resource overlay. This resource is originally defined as part of MAXIMUM_CONNECTED_DEVICES in packages/services/Car/service/res/values/config.xml.

Enable and disable Bluetooth adapter

One of the core functions of your policy is to turn the Bluetooth adapter on and off at the appropriate times. You can use the BluetoothAdapter.enable() and BluetoothAdapter.disable() framework APIs to enable and disable the adapter. These calls should respect the persisted state the user has selected through Settings or any other means. One way to do this is as follows:

/**
 * Turn on the Bluetooth adapter.
 */
private void enableBluetooth() {
    BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
    if (bluetoothAdapter == null) {
        return;
    }
    bluetoothAdapter.enable();
}

/**
 * Turn off the Bluetooth adapter.
 */
private void disableBluetooth() {
    BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
    if (bluetoothAdapter == null) {
        return;
    }
    // Will shut down _without_ persisting the off state as the desired state
    // of the Bluetooth adapter for next start up. This does nothing if the adapter
    // is already off, keeping the existing saved desired state for next reboot.
    bluetoothAdapter.disable(false);
}

Determine when to turn the Bluetooth adapter on and off

With your custom policy you are free to determine which events indicate the best times to enable and disable the adapter. One such way to do this is by using the power states MAXIMUM_CONNECTED_DEVICES in CarPowerManager:

private final CarPowerStateListenerWithCompletion mCarPowerStateListener =
        new CarPowerStateListenerWithCompletion() {
    @Override
    public void onStateChanged(int state, CompletableFuture<Void> future) {
        if (state == CarPowerManager.CarPowerStateListener.ON) {
            if (isBluetoothPersistedOn()) {
                enableBluetooth();
            }
            return;
        }

        // "Shutdown Prepare" is when the user perceives the car as off
        // This is a good time to turn off Bluetooth
        if (state == CarPowerManager.CarPowerStateListener.SHUTDOWN_PREPARE) {
            disableBluetooth();

            // Let CarPowerManagerService know we're ready to shut down
            if (future != null) {
                future.complete(null);
            }
            return;
        }
    }
};

Determine when to connect devices

Similarly, when you determine the events that should trigger device connections to begin, CarBluetoothManager provides the connectDevices() API call that proceeds to connect devices based on the priority lists defined for each Bluetooth profile.

One example of when you might want to do this is whenever the Bluetooth adapter turns on:

private class BluetoothBroadcastReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
        if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) {
            int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1);
            if (state == BluetoothAdapter.STATE_ON) {
                // mContext should be your app's context
                Car car = Car.createCar(mContext);
                CarBluetoothManager carBluetoothManager =
                        (CarBluetoothManager) car.getCarManager(Car.BLUETOOTH_SERVICE);
                carBluetoothManager.connectDevices();
            }
        }
    }
}

Verify automotive connection management

The easiest way to verify the behavior of your connection policy is to enable Bluetooth on your IVI and validate that it automatically connects to the correct devices in the appropriate order. You can toggle the Bluetooth adapter through the settings UI, or with the following adb commands:

adb shell su u$(adb shell am get-current-user)_system svc bluetooth disable
adb shell su u$(adb shell am get-current-user)_system svc bluetooth enable

In addition, the output of the following command can be used to see debug information related to Bluetooth connections:

adb shell dumpsys car_service

Finally, if you have built your own automotive policy, verifying any custom connection behavior requires controlling the events that you've chosen to trigger device connections.

Automotive Bluetooth profiles

In Android, the IVI can support multiple devices connected simultaneously over Bluetooth. Multi-device Bluetooth phone services let users connect separate devices concurrently, such as a personal phone and a work phone, and make hands-free calls from either device.

Connection limits are enforced by each individual Bluetooth profile, usually within the implementation of the profile service itself. By default, CarBluetoothService makes no further judgement on the maximum number of connected devices allowed.

Hands-Free Profile

The Bluetooth Hands-Free Profile (HFP) allows the vehicle to make and receive phone calls via a connected remote device. Each device connection registers a separate phone account with TelecomManager, which advertises any available phone accounts to the IVI apps.

The IVI can connect to multiple devices via HFP. MAX_STATE_MACHINES_POSSIBLE MAXIMUM_CONNECTED_DEVICES in HeadsetClientService defines the maximum number of simultaneous HFP connections.

When a user makes or receives a phone call from a device, the corresponding phone account creates an HfpClientConnection object. The Dialer app interacts with the HfpClientConnection object to manage call features, such as accepting a call or hanging up.

It should be noted that the default Dialer app does not support multiple simultaneously connected HFP devices. In order to implement multi-device HFP, customization is required to let users select which device account to use when making a call. The app then calls telecomManager.placeCall with the correct account. You need to verify that other multi-device functionality works as intended as well.

Verify multi-device HFP

To check that multi-device connectivity works properly over Bluetooth:

  1. Using Bluetooth, connect a device to the IVI and stream audio from the device.
  2. Connect two phones to the IVI over Bluetooth.
  3. Pick one phone. Place an outgoing call directly from the phone, and place an outgoing call using the IVI.
    1. Both times, verify the streamed audio pauses and the phone audio plays over the IVI connected speakers.
  4. Using the same phone, receive an incoming call directly on the phone, and receive an incoming call using the IVI.
    1. Both times, verify the streaming audio pauses and the phone audio plays over the IVI connected speakers.
  5. Repeat steps 3 and 4 with the other connected phone.

Emergency calling

The ability to make emergency calls is an important aspect of the telephony and Bluetooth functions in the car. There are a number of ways an emergency call can be initiated from the IVI, including:

  • Standalone eCall solution
  • eCall solution integrated into the IVI
  • Relying on a connected Bluetooth phone when no built-in system is available

Connect an emergency call

While eCall equipment is safety-critical, it's currently not integrated into Android. It's possible to use ConnectionService to expose emergency calling features through Android, which also has the benefit of introducing accessibility options for emergency calls. To learn more, see Building a calling app.

Here is an example of how to establish an emergency ConnectionService:

public class YourEmergencyConnectionService extends ConnectionService {

    @Override
    public Connection onCreateOutgoingConnection(
            PhoneAccountHandle connectionManagerAccount,
            ConnectionRequest request) {
        // Your equipment specific procedure to make ecall
        // ...
    }

    private void onYourEcallEquipmentReady() {

        PhoneAccountHandle handle =
            new PhoneAccountHandle(new ComponentName(context, YourEmergencyConnectionService),
                    YourEmergencyConnectionId);
        PhoneAccount account =
            new PhoneAccount.Builder(handle, eCallOnlyAccount)
            .setSupportedUriSchemes(Arrays.asList(PhoneAccount.SCHEME_TEL))
            .setCapabilities(PhoneAccount.CAPABILITY_PLACE_EMERGENCY_CALLS
                    | PhoneAccount.CAPABILITY_MULTI_USER)
            .build():
        mTelecomManager.registerPhoneAccount(account);
        mTelecomManager.enablePhoneAccount(account.getAccountHandle(), true);
    }
}

Enable Bluetooth for emergency calls

Calling emergency before Android 10 involved direct dialing from a phone and invoking special equipment if available (for example, auto-trigger upon detection of danger or a user action). In Android 10 and higher, the Dialer in the car can directly call an emergency number, provided this MAXIMUM_CONNECTED_DEVICES in apps/Bluetooth/res/values/config.xml:

<!-- For supporting emergency call through the hfp client connection service --> <bool name=”hfp_client_connection_service_support_emergency_call”>true</bool>

By implementing emergency calling in this way, other apps, such as voice recognition, can also call an emergency number.

Phone Book Access Profile

The Bluetooth Phone Book Access Profile (PBAP) downloads contacts and call histories from a connected remote device. PBAP maintains an aggregated, searchable list of contacts that is updated by the PBAP client state machine. Each connected device interacts with a separate PBAP client state machine, resulting in contacts being associated with the proper device when making a call.

PBAP is unidirectional and therefore requires the IVI to instantiate connections to any MAXIMUM_CONNECTED_DEVICES in PbapClientService defines the maximum number of simultaneous PBAP device connections allowed with the IVI. The PBAP client stores the contacts for each connected device in the Contacts Provider which can then be accessed by an app to derive the phone book for each device.

In addition, the profile connection must be authorized by both the IVI and the mobile device in order for a connection to be made. When a PBAP client disconnects, the internal database removes all contacts and the call history associated with the previously connected device.

Message Access Profile

The Bluetooth Message Access Profile (MAP) allows the vehicle to send and receive SMS messages via a connected remote device. Currently, messages are not stored locally on the IVI. Instead, whenever the connected remote device receives a message, the IVI receives and parses the message and broadcasts its contents in an Intent instance, which can then be received by an app.

In order to connect to a mobile device for the purpose of sending and receiving messages, the IVI must initiate the MAP connection. MAXIMUM_CONNECTED_DEVICES in MapClientService defines the maximum number of simultaneous MAP device connections allowed with the IVI. Each connection must be authorized by the IVI and the mobile device before messages can be transferred.

Advanced Audio Distribution Profile

The Bluetooth Advanced Audio Distribution Profile (A2DP) allows the vehicle to receive audio streams from a connected remote device.

Unlike other profiles, the maximum number of connected A2DP devices is enforced in the native stack and not in Java. The value is currently hardcoded to 1 using the kDefaultMaxConnectedAudioDevices variable in packages/modules/Bluetooth/system/btif/src/btif_av.cc.

Audio/Video Remote Control Profile

The Bluetooth Audio/Video Remote Control Profile (AVRCP) allows the vehicle to control and browse media players on a connected remote device. Since the IVI plays the role of an AVRCP controller, any triggered controls that affect audio playback rely on an A2DP connection to the target device.

For a specific media player on an Android phone to be browsable by the IVI via AVRCP, the media app on the phone must provide a MediaBrowserService and allow com.android.bluetooth access to that service. Building a media browser service explains how to do this in detail.