iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 19
0
  • Guide :
    • 介紹
    • Service
    • Adapter
    • Handover
    • Kernel

前情提要:

上一篇提到已經彈出 dialog 視窗詢問是否配對,
這裡會發現使用者在彈出視窗按下的事件中,
會發送一個帶有 BluetoothPeripheralHandover.ACTION_ALLOW_CONNECT 的 intent:

packages/apps/Nfc/src/com/android/nfc/handover/ConfirmConnectActivity.java

 32 public class ConfirmConnectActivity extends Activity {
 33     BluetoothDevice mDevice;
 34     AlertDialog mAlert = null;
 35     @Override           
 36     protected void onCreate(Bundle savedInstanceState) {
... ...
 46         builder.setMessage(confirmString)
 47                .setCancelable(false) 
**//使用者按下YES按鈕後處理**
 48                .setPositiveButton(res.getString(R.string.pair_yes),
 49                        new DialogInterface.OnClickListener() {
 50                    public void onClick(DialogInterface dialog, int id) {
 51                         Intent allowIntent = new Intent(**BluetoothPeripheralHandover.ACTION_ALLOW_CONNECT**);
 52                         allowIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice);
 53                         **sendBroadcast(allowIntent);** 
 54                         ConfirmConnectActivity.this.mAlert = null;
 55                         ConfirmConnectActivity.this.finish();
 56                    }    
 57                }) 
**//使用者按下NO按鈕後處理**
 58                .setNegativeButton(res.getString(R.string.pair_no),
 59                        new DialogInterface.OnClickListener() {
 60                    public void onClick(DialogInterface dialog, int id) {
 61                        Intent denyIntent = new Intent(BluetoothPeripheralHandover.ACTION_DENY_CONNECT);
 62                        denyIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice); 
 63                        sendBroadcast(denyIntent);   
 64                        ConfirmConnectActivity.this.mAlert = null;
 65                        ConfirmConnectActivity.this.finish();
 66                    }    
 67                });      
 6

然後再由先前的 handleIntent 接收處理:

packages/apps/Nfc/src/com/android/nfc/handover/BluetoothPeripheralHandover.java

455     void handleIntent(Intent intent) {   
456         String action = intent.getAction();  
457         // Everything requires the device to match...
458         BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
459         if (!mDevice.equals(device)) return; 
460              
461         if (**ACTION_ALLOW_CONNECT**.equals(action)) {
462             mHandler.removeMessages(MSG_TIMEOUT);                                                                                                                                    
463             mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_TIMEOUT), TIMEOUT_MS);
464             **nextStepConnect();**  
... ...

還記得嗎?剛剛 mState 切換成
STATE_WAITING_FOR_BOND_CONFIRMATION,呼叫 startBonding() 函式:

packages/apps/Nfc/src/com/android/nfc/handover/BluetoothPeripheralHandover.java

334     void nextStepConnect() {   
335         switch (mState) {      
... ...
353             case **STATE_WAITING_FOR_BOND_CONFIRMATION**:
354                 if (mDevice.getBondState() != BluetoothDevice.BOND_BONDED) {
355                     **startBonding**();
356                     break;     
357                 }

439     void startBonding() { 
**//狀態切換為STATE_BONDING**
440         **mState = STATE_BONDING;**
441         if (mRetryCount == 0) {
442             toast(getToastString(R.string.pairing_peripheral));
443         } 
444         if (mOobData != null) {
445             if (!mDevice.createBondOutOfBand(mTransport, mOobData)) {
446                 toast(getToastString(R.string.pairing_peripheral_failed));
447                 complete(false);
448             }
449         } else if (!mDevice.createBond(mTransport)) {
450                 toast(getToastString(R.string.pairing_peripheral_failed));
451                 complete(false);
452         } 
453     }

無論我們配對成功與否,當配對狀態發生改變的時候,
就會走到前面說的 handleIntent 中對應的判斷。
藍芽端的 BluetoothDevice 就會發出 intent 中帶有目前 action 為 ACTION_BOND_STATE_CHANGED
並且此時的 mState 為 STATE_BONDING

455     void handleIntent(Intent intent) { 
456         String action = intent.getAction();
... ...
467         } else if (BluetoothDevice.**ACTION_BOND_STATE_CHANGED**.equals(action) 
468                 && **mState == STATE_BONDING**) {   
469             int bond = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE,
470                     BluetoothAdapter.ERROR);    
471             if (bond == BluetoothDevice.BOND_BONDED) {
472                 mRetryCount = 0;
473                 **nextStepConnect**();

查看上面代碼,在配對成功的時候調用 nextStepConnect()
失敗的時候根據判斷重試,或者直接做最後的賦值操作。
nextStepConnect() 當中此時的狀態為 STATE_BONDING
注意此時已經是配對成功了,但是 mState 還沒改變:

packages/apps/Nfc/src/com/android/nfc/handover/BluetoothPeripheralHandover.java

334     void nextStepConnect() {
335         switch (mState) {      
... ...
**//到這一步的時候已經配對完畢了,要對不同的profile進行不同的connect
//且會按照順序通過判斷依次進行鏈接**
359             **case STATE_BONDING:**
360                 // Bluetooth Profile service will correctly serialize
361                 // HFP then A2DP connect
**//狀態切換為STATE_CONNECTING**
362                 **mState = STATE_CONNECTING;**
363                 synchronized (mLock) {
364                     if (mTransport == BluetoothDevice.TRANSPORT_LE) {
**//如果是HID設備(滑鼠)時**
365                         if (mInput.getConnectionState(mDevice)
366                                 != BluetoothProfile.STATE_CONNECTED) {
367                             mHidResult = RESULT_PENDING;
368                             toast(getToastString(R.string.connecting_peripheral));
369                             break;
370                         } else {
371                             **mHidResult = RESULT_CONNECTED**; 
372                         }
... ...
**//下面兩個Headset和A2DP的處理流程同上HID設備**
405                 }

藍芽端會根據不同的 profile 調用不同的 connect 方式。
在連接成功後藍芽那邊會往外發送廣播,會被 handleIntent 進行處理,
此處我們是以鏈接藍牙滑鼠為例子,
那麼就會走到BluetoothInputDevice.ACTION_CONNECTION_STATE_CHANGED
然後又去調用 nextStep() 又會走到 nextStepConnect()
但此時的狀態時為 STATE_CONNECTING

packages/apps/Nfc/src/com/android/nfc/handover/BluetoothPeripheralHandover.java

... ...
**//當完成連接後,無論成功與否都會跑如下過程**
407             **case STATE_CONNECTING:**
408                 if (mTransport == BluetoothDevice.TRANSPORT_LE) {
409                     if (mHidResult == RESULT_PENDING) {
410                         break;
411                     } else if (mHidResult == RESULT_CONNECTED) {
412                         toast(getToastString(R.string.connected_peripheral));
413                         mDevice.setAlias(mName);
414                         complete(true);
415                     } else {
416                         toast (getToastString(R.string.connect_peripheral_failed));
417                         complete(false);
418                     }  
... ...
**//下面Headset和A2DP的處理流程同上HID設備**
435                 break;

進入到對應的判斷中做最後的通知處理即可,調用complete。 

這裡就差不多完成了,轉交到 BluetoothAdapter 中把 profile 關閉,
再呼叫回調完成的函數:
packages/apps/Nfc/src/com/android/nfc/handover/BluetoothPeripheralHandover.java

523     void complete(boolean connected) {
524         if (DBG) Log.d(TAG, "complete()");
525         mState = STATE_COMPLETE;
526         mContext.unregisterReceiver(mReceiver); 
527         mHandler.removeMessages(MSG_TIMEOUT);
528         mHandler.removeMessages(MSG_RETRY);
529         synchronized (mLock) {
530             if (mA2dp != null) {
531                 mBluetoothAdapter.closeProfileProxy(BluetoothProfile.A2DP, mA2dp);
532             }
533             if (mHeadset != null) {
534                 mBluetoothAdapter.closeProfileProxy(BluetoothProfile.HEADSET, mHeadset);
535             }
536      
537             if (mInput != null) {
538                 mBluetoothAdapter.closeProfileProxy(BluetoothProfile.HID_HOST, mInput);
539             }
540      
541             mA2dp = null;    
542             mHeadset = null; 
543             mInput = null;   
544         }
545         mCallback.onBluetoothPeripheralHandoverComplete(connected);
546     }

結語:

至此完成了類似於讀取 Tag 的分發流程,
把每個外設 (Peripheral) 都看成是一個 NFC 的 Tag。
是不是很有趣啊!哈哈哈!
所以目前我們透過 NFC 分發流程已經完成了藍芽連接,
下一篇我們會介紹 P2P 模式最後要透過藍芽傳輸的過程。


Reference :


上一篇
[Day-18] Android Pie NFC (5) Handover (2)
下一篇
[Day-20] Android Pie NFC (7) Handover (4)
系列文
Android Pie 底層開發學習心得30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言