On devices running Android 8.0 (API level 26) and higher, companion device
pairing performs a Bluetooth or Wi-Fi scan of nearby devices on behalf of your
app without requiring the
ACCESS_FINE_LOCATION
or
BLUETOOTH_ADMIN
permission. This helps minimize the number of permissions needed by your app
and is more privacy-friendly for users. After the device is paired, the device
can leverage the
REQUEST_COMPANION_RUN_IN_BACKGROUND
and
REQUEST_COMPANION_USE_DATA_IN_BACKGROUND
permissions to start the app from the background. You might use this method of
pairing devices for initial configuration of a BLE-capable smart watch, for
example.
On devices running Android 11 (API level 30) and higher, once a device is associated using companion device pairing, apps can scan for their associated Bluetooth devices without requiring a location permission.
Companion device pairing doesn't create the connection to chosen devices. You use Bluetooth or Wi-Fi connectivity APIs to establish connections. Companion device pairing also isn't intended for continuous scanning. It is a means for implementing custom protocols over Bluetooth, BLE, and Wi-Fi that are required for the companion device to function.
The requested permissions are granted when the user associates your app by
selecting a device from the list. These permissions are revoked only if you call
disassociate()
or if the app is uninstalled. An app is responsible for clearing its own
associations if the user no longer needs them, such as when they log out or
remove bonded devices.
Implement companion device pairing
You can customize the pairing request dialog when trying to pair with companion devices over Bluetooth, BLE, and Wi-Fi.
In your app, you can specify whether users see a list of possible companion devices or only one suggestion for a companion device. You can also filter the items that appear in the pairing request dialog, such as by type (Bluetooth, BLE, and Wi-Fi) or by device name.
In your implementation, you must do the following:
- Add the the following to the manifest:
<uses-feature android:name="android.software.companion_device_setup"/>
. - Check to make sure that Bluetooth or Wi-Fi is turned on.
Handle user-selected devices and any failures within
CompanionDeviceManager.Callback
. Upon successful pairing, this is typically where you could connect to the device via Bluetooth or Wi-Fi connectivity APIs. Retrieve the selected device object through the intent extras key,CompanionDeviceManager.EXTRA_DEVICE
.- For Bluetooth classic devices, expect a
BluetoothDevice
object. - For Bluetooth LE devices, expect an
android.bluetooth.le.ScanResult
object. - For Wi-Fi devices, expect a
android.net.wifi.ScanResult
object.
- For Bluetooth classic devices, expect a
The following code snippet demonstrates how to display a dialog asking whether the user wants to pair a handheld device with a single, Bluetooth-connected companion device named "My device."
Kotlin
class MyDeviceSelectionActivity : Activity() { private val deviceManager: CompanionDeviceManager by lazy(LazyThreadSafetyMode.NONE) { getSystemService(CompanionDeviceManager::class.java) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // ... // To skip filtering based on name and supported feature flags (UUIDs), // don't include calls to setNamePattern() and addServiceUuid(), // respectively. This example uses Bluetooth. val deviceFilter: BluetoothDeviceFilter = BluetoothDeviceFilter.Builder() .setNamePattern(Pattern.compile("My device")) .addServiceUuid(ParcelUuid(UUID(0x123abcL, -1L)), null) .build() // The argument provided in setSingleDevice() determines whether a single // device name or a list of device names is presented to the user as // pairing options. val pairingRequest: AssociationRequest = AssociationRequest.Builder() .addDeviceFilter(deviceFilter) .setSingleDevice(true) .build() // When the app tries to pair with the Bluetooth device, show the // appropriate pairing request dialog to the user. deviceManager.associate(pairingRequest, object : CompanionDeviceManager.Callback() { override fun onDeviceFound(chooserLauncher: IntentSender) { startIntentSenderForResult(chooserLauncher, SELECT_DEVICE_REQUEST_CODE, null, 0, 0, 0) } override fun onFailure(error: CharSequence?) { // Handle failure } }, null) } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent) { when (requestCode) { SELECT_DEVICE_REQUEST_CODE -> when(resultCode) { Activity.RESULT_OK -> { // User has chosen to pair with the Bluetooth device. val deviceToPair: BluetoothDevice = data.getParcelableExtra(CompanionDeviceManager.EXTRA_DEVICE) deviceToPair.createBond() // ... Continue interacting with the paired device. } } } } }
Java
public class MyDeviceSelectionActivity extends Activity { private CompanionDeviceManager deviceManager; private AssociationRequest pairingRequest; private BluetoothDeviceFilter deviceFilter; private static final int SELECT_DEVICE_REQUEST_CODE = 42; @override public void onCreate() { // ... deviceManager = getSystemService(CompanionDeviceManager.class); // To skip filtering based on name and supported feature flags (UUIDs), // don't include calls to setNamePattern() and addServiceUuid(), // respectively. This example uses Bluetooth. deviceFilter = new BluetoothDeviceFilter.Builder() .setNamePattern(Pattern.compile("My device")) .addServiceUuid(new ParcelUuid(new UUID(0x123abcL, -1L)), null) .build(); // The argument provided in setSingleDevice() determines whether a single // device name or a list of device names is presented to the user as // pairing options. pairingRequest = new AssociationRequest.Builder() .addDeviceFilter(deviceFilter) .setSingleDevice(true) .build(); // When the app tries to pair with the Bluetooth device, show the // appropriate pairing request dialog to the user. deviceManager.associate(pairingRequest, new CompanionDeviceManager.Callback() { @Override public void onDeviceFound(IntentSender chooserLauncher) { startIntentSenderForResult(chooserLauncher, SELECT_DEVICE_REQUEST_CODE, null, 0, 0, 0); } }, null); } @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == SELECT_DEVICE_REQUEST_CODE && resultCode == Activity.RESULT_OK) { // User has chosen to pair with the Bluetooth device. BluetoothDevice deviceToPair = data.getParcelableExtra(CompanionDeviceManager.EXTRA_DEVICE); deviceToPair.createBond(); // ... Continue interacting with the paired device. } } }
The following image shows the dialog box that appears.
Figure 1. The Companion Device Pairing screen