The company’s project requires the ability to connect a Bluetooth headset. Android doesn’t have an open port for pairing and connecting headphones, and most of the information on the web is about connecting to Bluetooth 4.0, with very few details about Bluetooth 2.0. It took me some time to look at the system’s Bluetooth Settings code, but it took me a while to figure out the solution. All the experience I explored and summarized by myself was combed and recorded for backup.
Scope of application of this article
The Android Bluetooth part is quite complex and involves a lot of terms and features. The pairing method introduced in this paper is suitable for common Bluetooth headsets and stereos, and is not connected to Bluetooth BLE or Socket communication by Bluetooth.
Let’s start with some names:
- Profile:
An important feature of Bluetooth is that all Bluetooth products do not need to implement the entire Bluetooth specification. To make it easier to maintain compatibility between Bluetooth devices, a Profile is defined in the Bluetooth specification. A Profile defines how a device implements a connection or application. You can think of a Profile as a connection layer or an application layer association. The connection we refer to in the title is to connect various profiles. The following profiles are all implemented by Android.
- A2dp:
Represents bluetooth stereo, and bluetooth headset music listening related to those, there is also an Avrcp audio/video remote control profile, is used for listening to pause, up and down song selection.
- Handset, Handfree:
Related to the phone, Bluetooth to answer and hang up the phone.
- Other:
Btservice is a directory for the basic operation of Bluetooth. The application of HDP Bluetooth in medical treatment; Hid: human computer interface, Bluetooth mouse keyboard and so on is this; Pbap: Phonebook Access Profile…
To prepare
- Add the required permissions in androidmanifest.xml
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
Copy the code
- Open the bluetooth
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if(! mBluetoothAdapter.isEnabled()) { Intent enableIntent =new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableIntent, REQUEST_ENABLE_BT);
}
Copy the code
- Registration of radio
Since bluetooth search, pairing, and connection status changes are broadcast by the system, you need to register these broadcasts to obtain status changes.
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(BluetoothDevice.ACTION_FOUND);
intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
intentFilter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
intentFilter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
intentFilter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
registerReceiver(mReceiver, intentFilter);
Copy the code
search
- Obtain paired devices. If a device has been paired successfully, the system stores its information locally. The system will not find the device again when you call the search.
Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices();
Copy the code
- Search equipment
mBluetoothAdapter.startDiscovery();
Copy the code
When the system finds a new Bluetooth device, it will broadcast the device’s information. So we need to get the device information by intercepting the Intent whose Action was bluetoothDevice.action_found.
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
Copy the code
pairing
Here comes the important point, a series of preparatory work, BluetoothDevice is now ready to start pairing. First, bluetooth devices must be paired successfully before connecting to different profiles. It is possible to connect to some models directly, but most of them do not respond. Then there is Android 4.4 API 19 and then open the pairing interface, for the previous system we can only get the interface through reflection.
- pairing
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
// The Bond interface is open only when the Android 4.4 API is 19 or higher
device.createBond();
} else {
//API 19 uses reflection to call the Bond interface
try {
device.getClass().getMethod("connect").invoke(device);
} catch(Exception e) { e.printStackTrace(); }}Copy the code
- A broadcast is sent if the match is successful
BluetoothDevice.ACTION_BOND_STATE_CHANGED
// Device binding status changed
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
int bondState = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.ERROR);
// Automatically connect after receiving a successful binding notification
if(item ! =null && bondState == BluetoothDevice.BOND_BONDED) {
connectDevice(item);
}
Copy the code
- Several states of pairing
public static final int BOND_NONE = 10;
public static final int BOND_BONDING = 11;
public static final int BOND_BONDED = 12;
Copy the code
The connection
Pairing (binding) and connection are two different processes. Pairing means that two devices discover the existence of each other, obtain the name and address of each other, and have the ability to establish a connection. A connection means that two devices share one RFCOMM channel and are capable of exchanging data. Only after you confirm that the bond is attached can you start the connection, which is to connect the Profile supported by the Bluetooth device.
You can observe the process of setting up bluetooth connection, which is to start pairing first, and then connect all supported profiles. This step is also a pit, there is no detailed explanation of this piece on the Internet. I also found the logic in the source code of the Setting for a long time. But the system application can directly call the method of connection, but is not open…
- The first thing we need to do is get a Profile in advance. Here we use A2dp as an example. The other principles are the same.
mBluetoothAdapter.getProfileProxy(this.new BluetoothProfile.ServiceListener() {
@Override
public void onServiceConnected(int profile, BluetoothProfile proxy) {
if (mA2dpService == null) { mA2dpService = (BluetoothA2dp) proxy; }}@Override
public void onServiceDisconnected(int profile) {
}
}, BluetoothProfile.A2DP);
Copy the code
- When we receive a match broadcast or confirm that the device has been matched, we call the Profile
connect
Method to connect. But this method was given by Google@hide
. Use reflection as above…
try {
mA2dpService.getClass().getMethod("connect", BluetoothDevice.class)
.invoke(mA2dpService, item.getDevice());
} catch (Exception e) {
e.printStackTrace();
}
Copy the code
- If the connection is successful, the system will send an announcement
BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
int profileState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, BluetoothProfile.STATE_DISCONNECTED);
Copy the code
- Several states of a connection
/** The profile is in disconnected state */
public static final int STATE_DISCONNECTED = 0;
/** The profile is in connecting state */
public static final int STATE_CONNECTING = 1;
/** The profile is in connected state */
public static final int STATE_CONNECTED = 2;
/** The profile is in disconnecting state */
public static final int STATE_DISCONNECTING = 3;
Copy the code
Pit craters
Ha-ha, do you think you’re done with it? ! Let me tell you about some of the other holes.
- Don’t forget to close profiles. We have obtained the Profile for connection, must close it after the connection is completed, if not closed, it will report an error.
mBluetoothAdapter.closeProfileProxy(BluetoothProfile.A2DP, mA2dpService);
mA2dpService = null;
Copy the code
- When the Bluetooth connection is successful, the system notifies the phone that the state has changed, just like switching the vertical screen, and needs to invoke the Activity’s life cycle again. I noticed that as soon as I connected, my interface would flash back to its original state. I wondered for a long time, then I kept thinking it was a reflection connection that caused the program to restart abnormally… After several gropes, I found that there is no setting
android:configChanges
Because of the.
android:configChanges="keyboard|keyboardHidden|navigation"
Copy the code
Actually, I have already thought of the first two attributes, but the last one, “This should never Normally Happen”, was written in the official document. I naively believed it and never tried it. Finally, I ran out of options and I wrote all the attributes, and then I subtracted them one by one, and I realized that I couldn’t do any less.
value | instructions |
---|---|
“keyboard” | The keyboard type changes – for example, the user inserts an external keyboard. |
“keyboardHidden” | Keyboard accessibility changes – for example, the user displays a hardware keyboard. |
“navigation” | The navigation type (trackball/arrow keys) has changed. (This usually never happens.) |