Android 如何獲取已連線的藍芽地址
專案中有一個需求,就是獲取已連線的藍芽地址
private void getConnectBt() { LogUtil.i("getConnectBt"); int a2dp = _bluetoothAdapter.getProfileConnectionState(BluetoothProfile.A2DP); int headset = _bluetoothAdapter.getProfileConnectionState(BluetoothProfile.HEADSET); int health = _bluetoothAdapter.getProfileConnectionState(BluetoothProfile.HEALTH); int flag = -1; if (a2dp == BluetoothProfile.STATE_CONNECTED) { flag = a2dp; } else if (headset == BluetoothProfile.STATE_CONNECTED) { flag = headset; } else if (health == BluetoothProfile.STATE_CONNECTED) { flag = health; } Log.d(TAG,"flag:"+flag); if (flag != -1) { _bluetoothAdapter.getProfileProxy(_context, new BluetoothProfile.ServiceListener() { @Override public void onServiceDisconnected(int profile) { } @Override public void onServiceConnected(int profile, BluetoothProfile proxy) { List<BluetoothDevice> mDevices = proxy.getConnectedDevices(); if (mDevices != null && mDevices.size() > 0) { for (BluetoothDevice device : mDevices) { Log.d(TAG,device.getName() + "," + device.getAddress()); } } else { } } }, flag); } }
從網上看到這段程式碼並沒有作用,由於flag一直等於-1,所以一直返回BluetoothProfile.STATE_DISCONNECTED。也就是說
這三個方法都是返回的BluetoothProfile.STATE_DISCONNECTEDint a2dp = _bluetoothAdapter.getProfileConnectionState(BluetoothProfile.A2DP); int headset = _bluetoothAdapter.getProfileConnectionState(BluetoothProfile.HEADSET); int health = _bluetoothAdapter.getProfileConnectionState(BluetoothProfile.HEALTH);
/** * Get the current connection state of a profile. * This function can be used to check whether the local Bluetooth adapter * is connected to any remote device for a specific profile. * Profile can be one of {@link BluetoothProfile#HEALTH}, {@link BluetoothProfile#HEADSET}, * {@link BluetoothProfile#A2DP}. * * <p>Requires {@link android.Manifest.permission#BLUETOOTH}. * * <p> Return value can be one of * {@link BluetoothProfile#STATE_DISCONNECTED}, * {@link BluetoothProfile#STATE_CONNECTING}, * {@link BluetoothProfile#STATE_CONNECTED}, * {@link BluetoothProfile#STATE_DISCONNECTING} */ public int getProfileConnectionState(int profile) { if (getState() != STATE_ON) return BluetoothProfile.STATE_DISCONNECTED; try { synchronized(mManagerCallback) { if (mService != null) return mService.getProfileConnectionState(profile); } } catch (RemoteException e) { Log.e(TAG, "getProfileConnectionState:", e); } return BluetoothProfile.STATE_DISCONNECTED; }
mService是IBluetooth介面物件
/**
* Use {@link #getDefaultAdapter} to get the BluetoothAdapter instance.
*/
BluetoothAdapter(IBluetoothManager managerService) {
if (managerService == null) {
throw new IllegalArgumentException("bluetooth manager service is null");
}
try {
mService = managerService.registerAdapter(mManagerCallback);
} catch (RemoteException e) {Log.e(TAG, "", e);}
mManagerService = managerService;
mLeScanClients = new HashMap<LeScanCallback, GattCallbackWrapper>();
mHandler = new Handler(Looper.getMainLooper());
}
public static synchronized BluetoothAdapter getDefaultAdapter() {
if (sAdapter == null) {
IBinder b = ServiceManager.getService(BLUETOOTH_MANAGER_SERVICE);
if (b != null) {
IBluetoothManager managerService = IBluetoothManager.Stub.asInterface(b);
sAdapter = new BluetoothAdapter(managerService);
} else {
Log.e(TAG, "Bluetooth binder is null");
}
}
return sAdapter;
}
我們看到managerService是IBluetoothManager的Proxy,我們來找找Stub端在哪。
private static class AdapterServiceBinder extends IBluetooth.Stub
class BluetoothManagerService extends IBluetoothManager.Stub
public IBluetooth registerAdapter(IBluetoothManagerCallback callback){
Message msg = mHandler.obtainMessage(MESSAGE_REGISTER_ADAPTER);
msg.obj = callback;
mHandler.sendMessage(msg);
synchronized(mConnection) {
return mBluetooth;
}
}
我們繼續看BluetoothAdapter的mService到底是什麼
mBluetooth = IBluetooth.Stub.asInterface(service);
private static class AdapterServiceBinder extends IBluetooth.Stub
原來BluetoothAdapter的mService = mBluetooth = IBluetooth.Stub.asInterface(service),BluetoothManagerService只是個門面而已,真正幹活的是IBluetooth.Stub也就是AdapterServiceBinder。我們回到最開始的問題,我們看AdapterService的getProfileConnectionState方法
int getProfileConnectionState(int profile) {
enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
return mAdapterProperties.getProfileConnectionState(profile);
}
我們繼續看AdapterProperties類
int getProfileConnectionState(int profile) {
synchronized (mObject) {
Pair<Integer, Integer> p = mProfileConnectionState.get(profile);
if (p != null) return p.first;
return BluetoothProfile.STATE_DISCONNECTED;
}
}
mProfileConnectionState是幹什麼用的,在此之前我們先看看藍芽的一些基本概念。
BluetoothProfile
描述Bluetooth Profile的介面。Bluetooth Profile是兩個裝置基於藍芽通訊的無線介面描述。
(對Bluetooth Profile的詳細解釋,來自百度:為了更容易的保持Bluetooth裝置之間的相容,Bluetooth規範中定義了 Profile。Profile定義了裝置如何實現一種連線或者應用,你可以把Profile理解為連線層或者應用層協議。 比如,如果一家公司希望它們的Bluetooth晶片支援所有的Bluetooth耳機,那麼它只要支援HeadSet Profile即可,而無須考慮該晶片與其它Bluetooth裝置的通訊與相容性問題。如果你想購買Bluetooth產品,你應該瞭解你的應用需要哪
些Profile來完成,並且確保你購買的Bluetooth產品支援這些Profile。)
BluetoothHeadset
提供行動電話的Bluetooth耳機支援。包括Bluetooth耳機和Hands-Free (v1.5) profiles。
BluetoothA2dp
定義兩個裝置間如何通過Bluetooth連線進行高質量的音訊傳輸。
A2DP(Advanced Audio Distribution Profile):高階音訊傳輸模式。
mProfileConnectionState也就是管理各種profile連線情況的一個集合
很顯然,我們最開始那段程式碼不起作用,是因為profile不對,我們來看profile都有哪些
/**
* Headset and Handsfree profile
*/
public static final int HEADSET = 1;
/**
* A2DP profile.
*/
public static final int A2DP = 2;
/**
* Health Profile
*/
public static final int HEALTH = 3;
/**
* Input Device Profile
* @hide
*/
public static final int INPUT_DEVICE = 4;
/**
* PAN Profile
* @hide
*/
public static final int PAN = 5;
/**
* PBAP
* @hide
*/
public static final int PBAP = 6;
/**
* GATT
*/
static public final int GATT = 7;
/**
* GATT_SERVER
*/
static public final int GATT_SERVER = 8;
我們列舉了主要的幾種,除了我們測試的HEADSET、A2DP、HEALTH外,還有GATT和GATT_SERVER,其實都是Hide。最開始的程式碼加上GATT和GATT_SERVER之後,依然沒有結果。
實在沒有辦法了,我看到了BluetoothAdapter有一個hide方法直接呼叫了AdapterProperties的getConnectionState方法。
/**
* @return the mConnectionState
*/
int getConnectionState() {
synchronized (mObject) {
return mConnectionState;
}
}
Class<BluetoothAdapter> bluetoothAdapterClass = BluetoothAdapter.class;//得到BluetoothAdapter的Class物件
try {//得到藍芽狀態的方法
Method method = bluetoothAdapterClass.getDeclaredMethod("getConnectionState", (Class[]) null);
//開啟許可權
method.setAccessible(true);
int state = (int) method.invoke(_bluetoothAdapter, (Object[]) null);
if(state == BluetoothAdapter.STATE_CONNECTED){
LogUtil.i("BluetoothAdapter.STATE_CONNECTED");
Set<BluetoothDevice> devices = _bluetoothAdapter.getBondedDevices();
LogUtil.i("devices:"+devices.size());
for(BluetoothDevice device : devices){
Method isConnectedMethod = BluetoothDevice.class.getDeclaredMethod("isConnected", (Class[]) null);
method.setAccessible(true);
boolean isConnected = (boolean) isConnectedMethod.invoke(device, (Object[]) null);
if(isConnected){
LogUtil.i("connected:"+device.getAddress());
return device.getAddress();
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
利用反射獲取了當前藍芽連線的狀態,根據getBondedDevices獲取已繫結的藍芽連線,然後遍歷BluetoothDevice,同樣利用反射的方法呼叫BluetoothDevice的isConnected方法。成功獲取。
參考文章: