iT邦幫忙

第 11 屆 iThome 鐵人賽

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

前情提要:

上一篇我們提到將藍芽裝置像是 Tag 一般感應後,
藍芽配對的流程解釋完畢,
這時候我們就可以來稍微看一下,
P2P 模式最終以藍芽傳輸的過程。

此時用戶已經點擊屏幕,開始回調 (Callback) 發送事件。
對應的 HandoverClientHandoverServer 已經啟動。
也就是從 P2pLinkManager 類的 SendTask 內部類開始分析:

packages/apps/Nfc/src/com/android/nfc/P2pLinkManager.java

 832     final class SendTask extends AsyncTask<Void, Void, Void> { 
 833         NdefPushClient nppClient;
 834         SnepClient snepClient;                       
 835         HandoverClient handoverClient;
 836  
**//需要通過BT傳輸的操作,等等又會再跳回來這裡**
 837         int **doHandover**(Uri[] uris, UserHandle userHandle) throws IOException {
 838             NdefMessage response = null;
**//先實例化BeamManager,用於管理從開始到結束的傳輸的動作**
 839             BeamManager beamManager = BeamManager.getInstance();       
 840             
... ...
**//如下是接收端根據協議把要請求的信息如藍牙地址等,封裝成NDEF格式的信息用於往外發送給
//另一個手機也就是發送端,然後發送端開啟自己的BT進行配對等等的操作,暫不分析此處細節**
 845             NdefMessage request = mHandoverDataParser.createHandoverRequestMessage();
 846             if (request != null) {
 847                 if (handoverClient != null) {
**//通過Client向接收端發送請求,內部就是往指定的服務器地址發送NDEF請求信息,請求用handover來傳輸
//此處的信息還是通過NFC來發送的**
 848                     response = handoverClient.sendHandoverRequest(request);    
 849                 }
**//如果遠端的手機或裝置不支持handover,那麼改用別的方式**
 850                 if (response == null && snepClient != null) {          
 851                     // Remote device may not support handover service, 
 852                     // try the (deprecated) SNEP GET implementation    
 853                     // for devices running Android 4.1
 854                     SnepMessage snepResponse = snepClient.get(request);
 855                     response = snepResponse.getNdefMessage();          
 856                 }
... ...

走到這裡證明遠端的 Handover 是成功的,
打算開始調用 BT (藍芽) 進行真正數據的傳輸。
這裡可以看到 mHandoverDataParser.getOutgoingHandoverData(response)
來把 HandoverServer 傳過來的 NDEF 數據解析,
並且賦值給我們需要的變量,比如把藍牙地址、名稱、數據配對信息等,
賦值給了 BeamManagerstartBeamSend 函式的參數。
這些數據內容多半是藍芽協議的部份,暫不分析:

packages/apps/Nfc/src/com/android/nfc/P2pLinkManager.java

 867             if (!beamManager.**startBeamSend**(mContext, 
 868                     **mHandoverDataParser.getOutgoingHandoverData(response)**, uris, userHandle)) {
 869                 return HANDOVER_BUSY;
 870             }
 871              
 872             return HANDOVER_SUCCESS;
 873         }    
... ...

因為一開始有實作 AsynkTask,所以會調用到 doInBackground
有興趣可以自行參考 AsynkTask 官網說明。
當解析傳過來的數據發現需要使用 Handover 的時候,
uris 有值時,此時進入到 doHandover

packages/apps/Nfc/src/com/android/nfc/P2pLinkManager.java

 884         @Override    
 885         public Void doInBackground(Void... args) {
... ...
 905             if (uris != null) {
 906                 if (DBG) Log.d(TAG, "Trying handover request");
 907                 try {
 908                     int handoverResult = **doHandover**(uris, userHandle);
 909                     switch (handoverResult) {
 910                         case HANDOVER_SUCCESS:
 911                             result = true;
 912                             break;
 913                         case HANDOVER_FAILURE:
 914                             result = false;
 915                             break;
 916                         case HANDOVER_UNSUPPORTED:
 917                             result = false;
 918                             onHandoverUnsupported();
 919                             break;
 920                         case HANDOVER_BUSY:
 921                             result = false;
 922                             onHandoverBusy();
 923                             break;
 924                     }
 925                 } catch (IOException e) {
 926                     result = false;
 927                 }    
 928             }

接下來我們就來看看 BeamManager 的簡單介紹:
這時候我們又回到剛剛 startBeamSend 函式執行階段,
已經收到回應開始嘗試使用 handover 模式,也稱為 Beam 模式。
注意第二個參數就是遠端的 Handoverserver 給出的回應,進一步封裝的數據。
packages/apps/Nfc/src/com/android/nfc/beam/BeamManager.java

 97     public boolean startBeamSend(Context context, 
 98                                **HandoverDataParser.BluetoothHandoverData outgoingHandoverData**,
 99                                Uri[] uris, UserHandle userHandle) { 
100         synchronized (mLock) {
101             if (mBeamInProgress) { 
102                 return false;
103             } else {
104                 mBeamInProgress = true;     
105             }
106         }    
107 
**//這個Record如其名字,就是記錄了remoteDevice(遠端的device)、uris(存放的要傳輸的數據的uri)等**
108         BeamTransferRecord transferRecord = BeamTransferRecord.forBluetoothDevice(
109                 **outgoingHandoverData.device**, outgoingHandoverData.carrierActivating,
110                 **uris**);
**//啟動BeamReceiveService來通過service進一步完成正真的傳輸**
111         Intent sendIntent = new Intent(context.getApplicationContext(),
112                 **BeamSendService.class**);     
113         sendIntent.putExtra(BeamSendService.EXTRA_BEAM_TRANSFER_RECORD, transferRecord);
114         sendIntent.putExtra(BeamSendService.EXTRA_BEAM_COMPLETE_CALLBACK,
115                 new Messenger(mCallback));  
116         context.startServiceAsUser(sendIntent, userHandle);
117         return true;      
118     }

再來就看看 intent 送過去之後做了什麼事吧:

packages/apps/Nfc/src/com/android/nfc/beam/BeamSendService.java

 32 public class BeamSendService extends Service implements BeamTransferManager.Callback {
... ...
 48     private final BroadcastReceiver mBluetoothStateReceiver = new BroadcastReceiver() {
 49         @Override            
 50         public void onReceive(Context context, Intent intent) {
 51             String action = intent.getAction(); 
 52             if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
 53                 **handleBluetoothStateChanged(intent)**;
 54             }                
 55         }                    
 56     };                       
 57  
**//先得到BluetoothAdapter後面作使用**
 58     public BeamSendService() {
 59         mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
 60     }

執行傳送的部份:

packages/apps/Nfc/src/com/android/nfc/beam/BeamSendService.java

 81     @Override     
 82     public int **onStartCommand**(Intent intent, int flags, int startId) {
... ...     
 95         if (**doTransfer(transferRecord)**) {
 96             if (DBG) Log.i(TAG, "Starting outgoing Beam transfer");
 97             return START_STICKY;
 98         } else {    
... ...
103     }

packages/apps/Nfc/src/com/android/nfc/beam/BeamSendService.java

105     boolean **doTransfer**(BeamTransferRecord transferRecord) {
106         if (**createBeamTransferManager**(transferRecord)) {
... ...
**//Ndef信息中攜帶的是藍牙的傳輸鏈接的時候才進行傳輸。**
112             if (transferRecord.dataLinkType == BeamTransferRecord.DATA_LINK_TYPE_BLUETOOTH) {
113                 if (mBluetoothAdapter.isEnabled()) {
114                     // Start the transfer
**//直接調用TransferManager的start開始傳送**
115                     **mTransferManager.start()**;
**//若藍芽沒打開,調用BT相關的接口去打開藍牙,打開成功後會發送藍牙狀態改變你的廣播**
116                 } else {
117                     if (!mBluetoothAdapter.enableNoAutoConnect()) {
118                         Log.e(TAG, "Error enabling Bluetooth.");
119                         mTransferManager = null;
120                         return false;
121                     }
122                     mBluetoothEnabledByNfc = true;
123                     if (DBG) Log.d(TAG, "Queueing out transfer "
124                             + Integer.toString(transferRecord.id));
... ...

這裡很重要,實例化 BeamTransferManager 來管理傳輸中的流程顯示等,
關於 BeamTransferManager 細部下一篇再來詳解:

packages/apps/Nfc/src/com/android/nfc/beam/BeamSendService.java

133     boolean createBeamTransferManager(BeamTransferRecord transferRecord) {
... ...       
142                        
143         mTransferManager = new **BeamTransferManager**(this, this, transferRecord, false);
144         mTransferManager.updateNotification();
145         return true;   
146     }

收到BT開啟的廣播以後開始準備傳送:

packages/apps/Nfc/src/com/android/nfc/beam/BeamSendService.java

148     private void **handleBluetoothStateChanged**(Intent intent) {
149         int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,
150                 BluetoothAdapter.ERROR); 
151         if (state == BluetoothAdapter.STATE_ON) {
152             if (mTransferManager != null &&
153                     mTransferManager.mDataLinkType == BeamTransferRecord.DATA_LINK_TYPE_BLUETOOTH) {
**//收到廣播以後同樣是調用TransferManager的start開始傳輸**
154                 **mTransferManager.start()**;
155             }        
156         } else if (state == BluetoothAdapter.STATE_OFF) {
157             mBluetoothEnabledByNfc = false;
158         }            
159     }

傳輸完成的回調 (Callback):

173     @Override
174     public void onTransferComplete(BeamTransferManager transfer, boolean success) {
175         // Play success sound
176         if (!success) {
177             if (DBG) Log.d(TAG, "Transfer failed, final state: " +
178                     Integer.toString(transfer.mState));
179         }
180  
181         if (mBluetoothEnabledByNfc) {  
182             mBluetoothEnabledByNfc = false;
183             mBluetoothAdapter.disable();   
184         }
185         
186         invokeCompleteCallback(success);
187         stopSelf(mStartId);
188     }

結語:

可以看到如上的很多的工作都是在 BeamTransferManager 中完成的!
BeamTransferManager 代碼較多此處就一點點分析先從實例化看起。
下一篇待續!


Reference :

https://blog.csdn.net/zy00000000001/article/details/73730458


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

尚未有邦友留言

立即登入留言