Bluetooth 藍芽開啟流程
藍芽的開啟連線主要流程:
開啟
檢測
掃描
連線
一、開啟(開啟藍芽BT Turn on Turn off)
(藍芽的on/off由類BluetoothEnabler控制。)
開啟過程涉及到的類:
BluetoothEnabler(on/off的控制介面,介面的點選和狀態文字的顯示)
BluetoothService(最主要的類,開啟具體命令的執行緒進行enable,disable等操作)
LocalBluetoothManager(本機藍芽裝置管理,開啟關閉,搜尋等等,初始化BluetoothAdapter)
BluetoothAdapter(framework封裝的類,提供本地藍芽裝置的配置,包括開啟藍芽,搜尋周圍藍芽裝置,設定本地藍芽可見性;建立LocalBluetoothManager和BluetoothService的對映關係,主要是通過它呼叫BluetoothService的函式)
BluetoothEventRedirector(接收Bluetooth API 的廣播和回撥,並且將Settings中的UI執行緒上的事件分派到正確的類)
以下是結合google原生6.0程式碼作的簡要分析
- /packages/apps/Settings/src/com/android/settings/bluetooth/BluetoothEnabler.java
BluetoothEnabler.java裡面的函式onSwitchChanged(Switch switchView, boolean isChecked)監聽on/off的變化,通過mLocalAdapter.setBluetoothEnabled(isChecked);設定下去。
@Override
179 public void onSwitchChanged(Switch switchView, boolean isChecked) {
180 // Show toast message if Bluetooth is not allowed in airplane mode
181 if (isChecked &&
182 !WirelessUtils.isRadioAllowed(mContext, Settings.Global.RADIO_BLUETOOTH)) {
183 Toast.makeText(mContext, R.string.wifi_in_airplane_mode, Toast.LENGTH_SHORT).show ();
184 // Reset switch to off
185 switchView.setChecked(false);
186 }
187
188 MetricsLogger.action(mContext, MetricsLogger.ACTION_BLUETOOTH_TOGGLE, isChecked);
189
190 if (mLocalAdapter != null) {
191 mLocalAdapter.setBluetoothEnabled(isChecked);
192 }
193 mSwitch.setEnabled(false);
194 }
2.開啟(關閉)操作成功後會有一個非同步事件ACTION_STATE_CHANGED返回,非同步事件由類BluetoothEventRedirector控制(接收廣播,進行處理)。在收到ACTION_STATE_CHANGED非同步事件後,還需要做update本地裝置profile的事情,讀取上次關閉前搜尋到的藍芽裝置。
(1) ACTION_STATE_CHANGED: 定義在/frameworks/base/core/java/android/bluetooth/BluetoothAdapter.java中。
(2)通過LocalBluetoothManager.setBluetoothStateInt(int state)方法調到 CachedBluetoothDeviceManager.onBluetoothStateChanged方法來讀取上次關閉之前搜尋到device.
public synchronized void onBluetoothStateChanged(int bluetoothState) {
153 // When Bluetooth is turning off, we need to clear the non-bonded devices
154 // Otherwise, they end up showing up on the next BT enable
155 if (bluetoothState == BluetoothAdapter.STATE_TURNING_OFF) {
156 for (int i = mCachedDevices.size() - 1; i >= 0; i--) {
157 CachedBluetoothDevice cachedDevice = mCachedDevices.get(i);
158 if (cachedDevice.getBondState() != BluetoothDevice.BOND_BONDED) {
159 cachedDevice.setVisible(false);
160 mCachedDevices.remove(i);
161 } else {
162 // For bonded devices, we need to clear the connection status so that
163 // when BT is enabled next time, device connection status shall be retrieved
164 // by making a binder call.
165 cachedDevice.clearProfileConnectionState();
166 }
167 }
168 }
169 }
(3)來開啟EnableThread執行緒,進行開啟操作,藍芽的開啟關閉屬於非同步操作。
注意:
在啟動藍芽的時候,要注意的地方是不能正常啟動藍芽的情況,因為正常啟動的時候會返回BluetoothIntent.ENABLED_ACTION 這個Intent,當時當啟動出現異常的時候是沒有Intent返回的,android使用回撥函式來解決這個問題。
二、是否可檢測性(Discoverable)
藍芽模式有兩種模式:
SCAN_MODE_CONNECTABLE_DISCOVERABLE(可連線可發現)
SCAN_MODE_CONNECTABLE(可連線但不可發現)
藍芽的discoverable mode由/packages/apps/Settings/src/com/android/settings/bluetooth/BluetoothDiscoverableEnabler.java控制,
128 public boolean onPreferenceClick(Preference preference) {
129 // toggle discoverability
130 mDiscoverable = !mDiscoverable;
131 setEnabled(mDiscoverable);
132 return true;
133 }
通過onPreferenceClick()方法,呼叫setEnabled(mDiscoverable);,然後呼叫到BluetoothAdapter.java裡面的setScanMode () 方法
1133 /** @hide */
1134 public boolean setScanMode(int mode) {
1135 if (getState() != STATE_ON) return false;
1136 /* getDiscoverableTimeout() to use the latest from NV than use 0 */
1137 return setScanMode(mode, getDiscoverableTimeout());
1138 }
小結可檢測性這裡用到的類有:
BluetoothDiscoverableEnabler.java
BluetoothAdapter.java
LocalBluetoothManager
三、掃描藍芽
在/packages/apps/Settings/src/com/android/settings/bluetooth/BluetoothSettings.java裡面呼叫方法startScanning() ,然後到/frameworks/base/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java
裡面的函式
public void startScanning(boolean force) {
151 // Only start if we're not already scanning
152 if (!mAdapter.isDiscovering()) {
153 if (!force) {
154 // Don't scan more than frequently than SCAN_EXPIRATION_MS,
155 // unless forced
156 if (mLastScan + SCAN_EXPIRATION_MS > System.currentTimeMillis()) {
157 return;
158 }
159
160 // If we are playing music, don't scan unless forced.
161 A2dpProfile a2dp = mProfileManager.getA2dpProfile();
162 if (a2dp != null && a2dp.isA2dpPlaying()) {
163 return;
164 }
165 }
166
167 if (mAdapter.startDiscovery()) {//開始搜尋
168 mLastScan = System.currentTimeMillis();
169 }
170 }
171 }