iT邦幫忙

第 11 屆 iThome 鐵人賽

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

前情提要:
先前提到 PeripheralHandoverService 不管走兩條路的哪一條,

  1. onCreate() 註冊的 BluetoothAdapter.ACTION_STATE_CHANGED廣播。
  2. onStartCommand() 調用 doPeripheralHandover 去進一步的操作。 

最後都會通到 BluetoothPeripheralHandoverstart()
下面可以看到我們最後的 mAction 是 ACTION_INIT

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

153     /**                    
154      * Main entry point. This method is usually called after construction,
155      * to begin the BT sequence. Must be called on Main thread.
156      */                    
157     public boolean start() {
158         checkMainThread(); 
159         if (mState != STATE_INIT || mBluetoothAdapter == null
160                 || (mProvisioning && mTransport != BluetoothDevice.TRANSPORT_LE)) {
161             return false;  
162         }                  
163                            
164                            
165         IntentFilter filter = new IntentFilter();
166         filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
167         filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
168         filter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
169         filter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
170         filter.addAction(BluetoothHidHost.ACTION_CONNECTION_STATE_CHANGED);
171         filter.addAction(ACTION_ALLOW_CONNECT);
172         filter.addAction(ACTION_DENY_CONNECT);
173                            
174         mContext.registerReceiver(mReceiver, filter);
175                            
176         mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_TIMEOUT), TIMEOUT_MS);
177                            
178         **mAction = ACTION_INIT;**
179         mRetryCount = 0;   
180                            
181         **nextStep();**        
182                            
183         return true;       
184     }

這個 nextStep() 很關鍵,後面會很常用到。
它會根據當前所處不同的 action 來選擇我們下一步要執行的動作。
此時我們處於最一開始的 ACTION_INIT,所以走到 nextStepInit()
packages/apps/Nfc/src/com/android/nfc/handover/BluetoothPeripheralHandover.java

186     /**         
187      * Called to execute next step in state machine
188      */         
189     void nextStep() {
190         if (mAction == ACTION_INIT) {
191             **nextStepInit();**
192         } else if (mAction == ACTION_CONNECT) {
193             nextStepConnect();
194         } else {
195             nextStepDisconnect();          
196         }       
197     }

因為是第一次執行,所以 mState 就是 STATE_INIT

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

199     /*    
200      * Enables bluetooth and gets the profile proxies
201      */   
202     void nextStepInit() {
203         switch (mState) {
**//只要其中一個不為null,就去getProfileProxys函式且狀態改為STATE_WAITING_FOR_PROXIES**
204             case STATE_INIT:
205                 if (mA2dp == null || mHeadset == null || mInput == null) {
206                     mState = STATE_WAITING_FOR_PROXIES;
207                     if (!getProfileProxys()) { 
208                         complete(false);
209                     }
210                     break;
211                 }
... ...

至此我們應該先分析一下 getProfileProxys() 如下,
很簡單的實現,就是調用了藍牙提供的接口去獲得對應的 profile:
packages/apps/Nfc/src/com/android/nfc/handover/BluetoothPeripheralHandover.java

318     boolean getProfileProxys() {
319              
320         if (mTransport == BluetoothDevice.TRANSPORT_LE) {
321             if (!mBluetoothAdapter.getProfileProxy(mContext, this, BluetoothProfile.HID_HOST))
322                 return false;
323         } else {
324             if(!mBluetoothAdapter.getProfileProxy(mContext, this, BluetoothProfile.HEADSET))
325                 return false;
326              
327             if(!mBluetoothAdapter.getProfileProxy(mContext, this, BluetoothProfile.A2DP))
328                 return false;
329         }    
330              
331         return true;
332     }

注意 BluetoothPeripheralHandover implements BluetoothProfile.ServiceListener
所以在你連接到對應的 profile 會回調相關的接口 onServiceConnected
當成功鏈接的時候,會走如下的回調,然後實現具體的 profile 對象的實例化:
packages/apps/Nfc/src/com/android/nfc/handover/BluetoothPeripheralHandover.java

658     @Override    
659     public void onServiceConnected(int profile, BluetoothProfile proxy) {
660         synchronized (mLock) { 
661             switch (profile) {
662                 case BluetoothProfile.HEADSET:
663                     mHeadset = (BluetoothHeadset) proxy;
664                     if (mA2dp != null) {
665                         mHandler.sendEmptyMessage(MSG_NEXT_STEP);
666                     }
667                     break;
668                 case BluetoothProfile.A2DP:
669                     mA2dp = (BluetoothA2dp) proxy;
670                     if (mHeadset != null) {
671                         mHandler.sendEmptyMessage(MSG_NEXT_STEP);
672                     }
673                     break;
674                 case BluetoothProfile.HID_HOST:
675                     mInput = (BluetoothHidHost) proxy;
676                     if (mInput != null) {
677                         mHandler.sendEmptyMessage(MSG_NEXT_STEP);
678                     }
679                     break;
680             }    
681         }        
682     }

並且在實例化完畢後會,往外發送 MSG_NEXT_STEP 信息。
這個信息被如下 Handler 處理。
packages/apps/Nfc/src/com/android/nfc/handover/BluetoothPeripheralHandover.java

601     final Handler mHandler = new Handler() {
602         @Override    
603         public void handleMessage(Message msg) {
604             switch (msg.what) {
605                 case MSG_TIMEOUT:
... ...
**//可以看到重新調用了nextStep**
628                 case MSG_NEXT_STEP:
629                     nextStep();
630                     break;
631                 case MSG_RETRY:
... ...
641             }        
642         }            
643     };

注意此時 mAction 還是 ACTION_INIT
mState 已經變成 STATE_WAITING_FOR_PROXIES
所以會調用到 nextStepInit 中的 STATE_WAITING_FOR_PROXIES 這個選項當中:

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

199     /*    
200      * Enables bluetooth and gets the profile proxies
201      */   
202     void nextStepInit() {
203         switch (mState) {
... ...
212                 // fall-through
**//走到此處的時候證明已經獲得了需要連接的藍牙的對應協議的profile對應的service的實現**
213             **case STATE_WAITING_FOR_PROXIES:**
**//注意state此時變成了STATE_INIT_COMPLETE**
214                 **mState = STATE_INIT_COMPLETE;**
215                 // Check connected devices and see if we need to disconnect
216                 synchronized(mLock) {
217                     if (mTransport == BluetoothDevice.TRANSPORT_LE) { 
**//處理HID裝置(滑鼠、鍵盤)的部份,根據目前連接與否來決定下一個的action
//這時候已連接裝置不包含目前這個,所以mAction切換成ACTION_CONNECT**
218                         if (mInput.getConnectedDevices().contains(mDevice)) {
219                             Log.i(TAG, "ACTION_DISCONNECT addr=" + mDevice + " name=" + mName);
220                             mAction = ACTION_DISCONNECT;
221                         } else {
222                             Log.i(TAG, "ACTION_CONNECT addr=" + mDevice + " name=" + mName);
223                             **mAction = ACTION_CONNECT;**
224                         }
225                     } else {
**//處理A2DP和HeadSet的部份,內容同上**
... ...
246                     }
247                 }
248                 **nextStep();**
249         }          
250                    
251     }

此時 mAction 切換為 ACTION_CONNECT
因此來分析上面提到很關鍵的 nextStep() 對應的方法 nextStepConnect()
packages/apps/Nfc/src/com/android/nfc/handover/BluetoothPeripheralHandover.java

334     void nextStepConnect() {
335         switch (mState) {
**//此時的state是STATE_INIT_COMPLETE走到如下:**
336             case STATE_INIT_COMPLETE:
337 
**//如果未配對就會去調用requestPairConfirmation
//狀態切換為STATE_WAITING_FOR_BOND_CONFIRMATION**
338                 if (mDevice.getBondState() != BluetoothDevice.BOND_BONDED) {
339                     **requestPairConfirmation();**
340                     **mState = STATE_WAITING_FOR_BOND_CONFIRMATION;**
341                     break;
342                 }
343           
... ...

這時候要求配對會跳出一個 dialog 視窗詢問使用者,
發一個 intent 將藍芽參數丟給 ConfirmConnectActivity 處理:

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

564     void requestPairConfirmation() {
565         Intent dialogIntent = new Intent(mContext, **ConfirmConnectActivity**.class);
566         dialogIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
567         dialogIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice); 
568         dialogIntent.putExtra(BluetoothDevice.EXTRA_NAME, mName);
569                            
570         mContext.startActivity(dialogIntent);
571     }

結語:

下一篇藍芽配對完後就完成所有的初始化流程了。


Reference :


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

尚未有邦友留言

立即登入留言