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:
- Using Bluetooth, connect a device to the IVI and stream audio from the device.
- Connect two phones to the IVI over Bluetooth.
- Pick one phone. Place an outgoing call directly from the phone,
and place an outgoing call using the IVI.
- Both times, verify the streamed audio pauses and the phone audio plays over the IVI connected speakers.
- Using the same phone, receive an incoming call directly on the phone, and
receive an incoming call using the IVI.
- Both times, verify the streaming audio pauses and the phone audio plays over the IVI connected speakers.
- 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.