Android4.4(MT8685)源碼藍牙解析--BLE搜索

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搜索分析完畢!



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章