BLE:全稱爲Bluetooth Low Energy。藍牙規範4.0最重要的一個特性就是低功耗。BLE使得藍牙設備可通過一粒鈕釦電池供電以維持續工作數年之久。很明顯,BLE使得藍牙設備在鐘錶、遠程控制、醫療保健及運動感應器等市場具有極光明的應用場景。
Google從Android 4.3開始添加了對藍牙4.0的支持。本文一個demo爲入口分析 BLE 搜索的流程。
package com.dy.ble;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
public class MainActivity extends Activity {
private static final String TAG = "BLE";
private Button scanBtn;
private BluetoothAdapter bluetoothAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if(!bluetoothAdapter.isEnabled()){
bluetoothAdapter.enable();
}
scanBtn = (Button) this.findViewById(R.id.btn_scan);
scanBtn.setOnClickListener(new OnClickListener(){
@SuppressLint("NewApi")
@Override
public void onClick(View arg0) {
if(bluetoothAdapter.isEnabled()){
bluetoothAdapter.startLeScan(callback);
}
}
});
}
@SuppressLint("NewApi")
private BluetoothAdapter.LeScanCallback callback = new BluetoothAdapter.LeScanCallback(){
@Override
public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {
Log.d(TAG, "onLeScan device = " + device + ",rssi = " + rssi + "scanRecord = " + scanRecord);
}
};
}
點擊按鈕就會開始掃描,掃描到設備時,就會觸發onLeScan這個回調方法,並且可以從參數中獲取掃描到的藍牙設備信息。下面分析BluetoothAdapter中的startLeScan方法。
public boolean startLeScan(LeScanCallback callback) {
return startLeScan(null, callback);
}
這裏調用了一個同名的方法,
public boolean startLeScan(UUID[] serviceUuids, LeScanCallback callback) {
if (DBG) Log.d(TAG, "startLeScan(): " + serviceUuids);
if (callback == null) {
if (DBG) Log.e(TAG, "startLeScan: null callback");
return false;
}
synchronized(mLeScanClients) {
if (mLeScanClients.containsKey(callback)) {
if (DBG) Log.e(TAG, "LE Scan has already started");
return false;
}
try {
IBluetoothGatt iGatt = mManagerService.getBluetoothGatt();
if (iGatt == null) {
if (DBG) Log.e("BluetoothAdapterReceiver", "iGatt == null");
// BLE is not supported
return false;
}
UUID uuid = UUID.randomUUID();
GattCallbackWrapper wrapper = new GattCallbackWrapper(this, callback, serviceUuids);
iGatt.registerClient(new ParcelUuid(uuid), wrapper);
if (wrapper.scanStarted()) {
if (DBG) Log.e("BluetoothAdapterReceiver", "wrapper.scanStarted()==true");
mLeScanClients.put(callback, wrapper);
return true;
}
} catch (RemoteException e) {
Log.e(TAG,"",e);
}
}
return false;
}
這個方法需要BLUETOOTH_ADMIN權限,第一個參數是各種藍牙服務的UUID數組,UUID是“Universally Unique Identifier”的簡稱,通用唯一識別碼的意思。對於藍牙設備,每個服務都有通用、獨立、唯一的UUID與之對應。也就是說,在同一時間、同一地點,不可能有兩個相同的UUID標識的不同服務。第二個參數是前面傳進來的LeScanCallback對象。
接下來分析下mManagerService,它是一個IBluetoothManager對象,IBluetoothManager是一個AIDL,可以實現跨進程通信,其在源碼中的路徑爲:/alps/frameworks/base/core/java/android/bluetooth/IBluetoothManager.aidl。下面來看看mManagerService的實例化,
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>();
}
直接將BluetoothAdapter構造方法的參數傳給了它,來看看這個參數到底是什麼?
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;
}
首先通過Binder機制獲取了BLUETOOTH_MANAGER_SERVICE服務的IBinder對象,這個服務是在系統啓動的時候添加進去的,在SystemServer.java中
<pre name="code" class="java"> bluetooth = new BluetoothManagerService(context);
ServiceManager.addService(BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE, bluetooth);
這裏實際就是實例化了一個BluetoothManagerService對象,然後把這個對象通過Binder保存在BLUETOOTH_MANAGER_SERVICE服務中。最後把這個IBinder對象轉化爲IBluetoothManager對象。所以managerService實際就是一個BluetoothManagerService對象。
現在回到BluetoothAdapter的startLeScan方法中,
IBluetoothGatt iGatt = mManagerService.getBluetoothGatt();
這裏實際就是調用BluetoothManagerService中的getBluetoothGatt方法了,我們進去看看
public IBluetoothGatt getBluetoothGatt() {
// sync protection
return mBluetoothGatt;
}
這裏直接返回一個IBluetoothGatt對象,那我們就來看看這個對象時在哪裏得到的呢?其實通過對代碼的研究發現, 這個對象是在藍牙開啓的時候得到的!
public boolean enable() {
if ((Binder.getCallingUid() != Process.SYSTEM_UID) &&
(!checkIfCallerIsForegroundUser())) {
Log.w(TAG,"enable(): not allowed for non-active and non system user");
return false;
}
mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
"Need BLUETOOTH ADMIN permission");
if (DBG) {
Log.d(TAG,"enable(): mBluetooth =" + mBluetooth +
" mBinding = " + mBinding);
}
/// M: MoMS permission check @{
if(FeatureOption.MTK_MOBILE_MANAGEMENT) {
checkEnablePermission();
return true;
}
/// @}
synchronized(mReceiver) {
mQuietEnableExternal = false;
mEnableExternal = true;
// waive WRITE_SECURE_SETTINGS permission check
long callingIdentity = Binder.clearCallingIdentity();
persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH);
Binder.restoreCallingIdentity(callingIdentity);
sendEnableMsg(false);
}
return true;
}
這是開啓藍牙的代碼,sendEnableMsg(false);這裏看來要發送一個消息,
private void sendEnableMsg(boolean quietMode) {
mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_ENABLE,
quietMode ? 1 : 0, 0));
}
果然,看看在哪裏接收了
@Override
public void handleMessage(Message msg) {
if (DBG) Log.d (TAG, "Message: " + msg.what);
switch (msg.what) {
<span style="white-space:pre"> </span> case MESSAGE_ENABLE:
if (DBG) {
Log.d(TAG, "MESSAGE_ENABLE: mBluetooth = " + mBluetooth);
}
mHandler.removeMessages(MESSAGE_RESTART_BLUETOOTH_SERVICE);
mEnable = true;
handleEnable(msg.arg1 == 1);
break;
<span style="white-space:pre"> </span>}
}
進入handleEnable方法看看
private void handleEnable(boolean quietMode) {
mQuietEnable = quietMode;
synchronized(mConnection) {
if (DBG) Log.d(TAG, "handleEnable: mBluetooth = " + mBluetooth +
", mBinding = " + mBinding + "quietMode = " + quietMode);
if ((mBluetooth == null) && (!mBinding)) {
if (DBG) Log.d(TAG, "Bind AdapterService");
//Start bind timeout and bind
Message timeoutMsg=mHandler.obtainMessage(MESSAGE_TIMEOUT_BIND);
mHandler.sendMessageDelayed(timeoutMsg,TIMEOUT_BIND_MS);
mConnection.setGetNameAddressOnly(false);
Intent i = new Intent(IBluetooth.class.getName());
if (!doBind(i, mConnection,Context.BIND_AUTO_CREATE, UserHandle.CURRENT)) {
mHandler.removeMessages(MESSAGE_TIMEOUT_BIND);
Log.e(TAG, "Fail to bind to: " + IBluetooth.class.getName());
} else {
mBinding = true;
}
} else if (mBluetooth != null) {
if (mConnection.isGetNameAddressOnly()) {
// if GetNameAddressOnly is set, we can clear this flag,
// so the service won't be unbind
// after name and address are saved
mConnection.setGetNameAddressOnly(false);
//Register callback object
try {
mBluetooth.registerCallback(mBluetoothCallback);
} catch (RemoteException re) {
Log.e(TAG, "Unable to register BluetoothCallback",re);
}
//Inform BluetoothAdapter instances that service is up
sendBluetoothServiceUpCallback();
}
//Enable bluetooth
try {
if (!mQuietEnable) {
if(!mBluetooth.enable()) {
Log.e(TAG,"IBluetooth.enable() returned false");
}
}
else {
if(!mBluetooth.enableNoAutoConnect()) {
Log.e(TAG,"IBluetooth.enableNoAutoConnect() returned false");
}
}
} catch (RemoteException e) {
Log.e(TAG,"Unable to call enable()",e);
}
}
}
}
這裏會調用doBinder方法來綁定服務,
boolean doBind(Intent intent, ServiceConnection conn, int flags, UserHandle user) {
ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
intent.setComponent(comp);
if (comp == null || !mContext.bindServiceAsUser(intent, conn, flags, user)) {
Log.e(TAG, "Fail to bind to: " + intent);
return false;
}
return true;
}
這個conn就是mConnection,那麼mConnection是什麼呢?
private BluetoothServiceConnection mConnection = new BluetoothServiceConnection();
private class BluetoothServiceConnection implements ServiceConnection {
private boolean mGetNameAddressOnly;
public void setGetNameAddressOnly(boolean getOnly) {
mGetNameAddressOnly = getOnly;
}
public boolean isGetNameAddressOnly() {
return mGetNameAddressOnly;
}
public void onServiceConnected(ComponentName className, IBinder service) {
if (DBG) Log.d(TAG, "BluetoothServiceConnection: " + className.getClassName());
Message msg = mHandler.obtainMessage(MESSAGE_BLUETOOTH_SERVICE_CONNECTED);
// TBD if (className.getClassName().equals(IBluetooth.class.getName())) {
if (className.getClassName().equals("com.android.bluetooth.btservice.AdapterService")) {
msg.arg1 = SERVICE_IBLUETOOTH;
// } else if (className.getClassName().equals(IBluetoothGatt.class.getName())) {
} else if (className.getClassName().equals("com.android.bluetooth.gatt.GattService")) {
msg.arg1 = SERVICE_IBLUETOOTHGATT;
} else {
Log.e(TAG, "Unknown service connected: " + className.getClassName());
return;
}
msg.obj = service;
mHandler.sendMessage(msg);
}
public void onServiceDisconnected(ComponentName className) {
// Called if we unexpected disconnected.
if (DBG) Log.d(TAG, "BluetoothServiceConnection, disconnected: " +
className.getClassName());
Message msg = mHandler.obtainMessage(MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED);
if (className.getClassName().equals("com.android.bluetooth.btservice.AdapterService")) {
msg.arg1 = SERVICE_IBLUETOOTH;
} else if (className.getClassName().equals("com.android.bluetooth.gatt.GattService")) {
msg.arg1 = SERVICE_IBLUETOOTHGATT;
} else {
Log.e(TAG, "Unknown service disconnected: " + className.getClassName());
return;
}
mHandler.sendMessage(msg);
}
}
現在我們就知道原來這個mConnection是一個綁定服務的連接對象,所以現在BluetoothManagerService綁定了一個IBluetooth的AIDL服務,這時onServiceConnected方法會執行,並且會發送一個MESSAGE_BLUETOOTH_SERVICE_CONNECTED消息,來看接收消息的地方
case MESSAGE_BLUETOOTH_SERVICE_CONNECTED:
{
if (DBG) Log.d(TAG,"MESSAGE_BLUETOOTH_SERVICE_CONNECTED: " + msg.arg1);
IBinder service = (IBinder) msg.obj;
synchronized(mConnection) {
if (msg.arg1 == SERVICE_IBLUETOOTHGATT) {
mBluetoothGatt = IBluetoothGatt.Stub.asInterface(service);
break;
} // else must be SERVICE_IBLUETOOTH
//Remove timeout
mHandler.removeMessages(MESSAGE_TIMEOUT_BIND);
mBinding = false;
mBluetooth = IBluetooth.Stub.asInterface(service);
try {
boolean enableHciSnoopLog = (Settings.Secure.getInt(mContentResolver,
Settings.Secure.BLUETOOTH_HCI_LOG, 0) == 1);
if (!mBluetooth.configHciSnoopLog(enableHciSnoopLog)) {
Log.e(TAG,"IBluetooth.configHciSnoopLog return false");
}
} catch (RemoteException e) {
Log.e(TAG,"Unable to call configHciSnoopLog", e);
}
if (mConnection.isGetNameAddressOnly()) {
//Request GET NAME AND ADDRESS
Message getMsg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS);
mHandler.sendMessage(getMsg);
if (!mEnable) return;
}
mConnection.setGetNameAddressOnly(false);
//Register callback object
try {
mBluetooth.registerCallback(mBluetoothCallback);
} catch (RemoteException re) {
Log.e(TAG, "Unable to register BluetoothCallback",re);
}
//Inform BluetoothAdapter instances that service is up
sendBluetoothServiceUpCallback();
//Do enable request
try {
if (mQuietEnable == false) {
if(!mBluetooth.enable()) {
Log.e(TAG,"IBluetooth.enable() returned false");
}
}
else
{
if(!mBluetooth.enableNoAutoConnect()) {
Log.e(TAG,"IBluetooth.enableNoAutoConnect() returned false");
}
}
} catch (RemoteException e) {
Log.e(TAG,"Unable to call enable()",e);
}
}
if (!mEnable) {
waitForOnOff(true, false);
handleDisable();
waitForOnOff(false, false);
}
break;
}
當msg的參數1爲SERVICE_IBLUETOOTHGATT時,實例化mBluetoothGatt對象,至此我們就可以得到mBluetoothGatt。
再一次回到BluetoothAdapter的startLeScan方法中,
public boolean startLeScan(UUID[] serviceUuids, LeScanCallback callback) {
if (DBG) Log.d(TAG, "startLeScan(): " + serviceUuids);
if (callback == null) {
if (DBG) Log.e(TAG, "startLeScan: null callback");
return false;
}
synchronized(mLeScanClients) {
if (mLeScanClients.containsKey(callback)) {
if (DBG) Log.e(TAG, "LE Scan has already started");
return false;
}
try {
IBluetoothGatt iGatt = mManagerService.getBluetoothGatt();
if (iGatt == null) {
if (DBG) Log.e("BluetoothAdapterReceiver", "iGatt == null");
// BLE is not supported
return false;
}
UUID uuid = UUID.randomUUID();
GattCallbackWrapper wrapper = new GattCallbackWrapper(this, callback, serviceUuids);
iGatt.registerClient(new ParcelUuid(uuid), wrapper);
if (wrapper.scanStarted()) {
if (DBG) Log.e("BluetoothAdapterReceiver", "wrapper.scanStarted()==true");
mLeScanClients.put(callback, wrapper);
return true;
}
} catch (RemoteException e) {
Log.e(TAG,"",e);
}
}
return false;
}
接着創建了一個GattCallbackWrapper對象,這是個BluetoothAdapter的內部類,主要用於獲取回調信息,然後iGatt註冊一個client,由BluetoothManagerService中的分析可知,iGatt實際是一個GattService內部類BluetoothGattBinder的對象
public void registerClient(ParcelUuid uuid, IBluetoothGattCallback callback) {
GattService service = getService();
if (service == null) return;
service.registerClient(uuid.getUuid(), callback);
}
這裏還是調用GattService的registerClient方法
void registerClient(UUID uuid, IBluetoothGattCallback callback) {
enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
if (DBG) Log.d(TAG, "registerClient() - UUID=" + uuid);
mClientMap.add(uuid, callback);
gattClientRegisterAppNative(uuid.getLeastSignificantBits(),
uuid.getMostSignificantBits());
}
這裏面調用了本地方法,對應的JNI文件是Com_android_bluetooth_gatt.cpp,
static void gattClientRegisterAppNative(JNIEnv* env, jobject object,
jlong app_uuid_lsb, jlong app_uuid_msb )
{
bt_uuid_t uuid;
if (!sGattIf) return;
set_uuid(uuid.uu, app_uuid_msb, app_uuid_lsb);
sGattIf->client->register_client(&uuid);
}
分析到這裏其實差不多了,因爲這裏系統會調用MTK提供的藍牙庫來實現搜索,源碼我們無法看到。
至此,藍牙BLE搜索分析完畢!