iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 17
1
Mobile Development

Android Pie 底層開發學習心得系列 第 17

[Day-17] Android Pie NFC (4) Handover (1)

  • Guide :
    • 介紹
    • Service
    • Adapter
    • Handover
    • Kernel

前言

這算是有點番外篇的感覺,剛好前文提到 Handover。
因為一般文章比較常看到 NFC 啟動或感應流程,
好像蠻少看到 P2P 這部份的,
我想應該是和現在很少人會去使用這部份有關吧,
畢竟目前手機很少人會放大檔案。
你可能會很困惑藍芽滑鼠就用藍芽就好了,
幹麻還需要用到 NFC 功能?
那假設你配對的過程,
只要像你過捷運那樣「逼!」一下就配對成功,
是不是快到要吃手手了呢。(笑)
Android Beam 的流程其實也是配對透過 NFC,
但是實際上傳輸是透過 dispatcherTag 函式給藍芽。

之前提到會使用 Handover 的主要有兩種情況:

  1. 負責一些帶有 NFC 功能的藍芽耳機、鍵盤、滑鼠這類外設 (peripheral)
  2. 容量較大的檔案透過藍芽的 Android Beam 傳輸

我們就以最簡單的帶有 NFC 功能的藍芽滑鼠作範例:
從 NFC 感應到這裡的流程請參考前文,
dispatchTag 是負責收到消息的當下作分發,
看要分給藍芽或 WiFi 去作下一步。

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

276     public int dispatchTag(Tag tag) {
... ...
336         if (tryPeripheralHandover(message)) {
337             if (DBG) Log.i(TAG, "matched BT HANDOVER");
338             return screenUnlocked ? DISPATCH_UNLOCK : DISPATCH_SUCCESS;
339         }
... ...

668     public boolean tryPeripheralHandover(NdefMessage m) {
669         if (m == null || !mDeviceSupportsBluetooth) return false;
670          
671         if (DBG) Log.d(TAG, "tryHandover(): " + m.toString());
672          
//**下面這個方法mHandoverDataParser.parseBluetooth(m)方法是通過檢測到的Tag中攜帶的NDEF式的信息,**
//**信息包含了當前NFC設備(藍牙鼠標、鍵盤、耳機等)的各種設備信息。**
673         HandoverDataParser.BluetoothHandoverData handover = mHandoverDataParser.parseBluetooth(m);
674         if (handover == null || !handover.valid) return false;
675         if (UserManager.get(mContext).hasUserRestriction(
676                 UserManager.DISALLOW_CONFIG_BLUETOOTH,  
677                 // hasUserRestriction does not support UserHandle.CURRENT
678                 UserHandle.of(ActivityManager.getCurrentUser()))) {
679             return false;
680         }
681          
//**下面就是解析完Tag中的NDEF攜帶的信息後,放到響應的變量中以便後續使用。**
682         Intent intent = new Intent(mContext, PeripheralHandoverService.class);
683         intent.putExtra(PeripheralHandoverService.EXTRA_PERIPHERAL_DEVICE, handover.device);
684         intent.putExtra(PeripheralHandoverService.EXTRA_PERIPHERAL_NAME, handover.name);
685         intent.putExtra(PeripheralHandoverService.EXTRA_PERIPHERAL_TRANSPORT, handover.transport);
686         if (handover.oobData != null) {
687             intent.putExtra(PeripheralHandoverService.EXTRA_PERIPHERAL_OOB_DATA, handover.oobData);
688         }
689         if (handover.uuids != null) {
690             intent.putExtra(PeripheralHandoverService.EXTRA_PERIPHERAL_UUIDS, handover.uuids);
691         }
692         if (handover.btClass != null) {
693             intent.putExtra(PeripheralHandoverService.EXTRA_PERIPHERAL_CLASS, handover.btClass);
694         }
695         intent.putExtra(PeripheralHandoverService.EXTRA_BT_ENABLED, mBluetoothEnabledByNfc.get());
696         intent.putExtra(PeripheralHandoverService.EXTRA_CLIENT, mMessenger);
//**啟動PeripheralHandoverService去進一步的處理。**
697         mContext.startServiceAsUser(intent, UserHandle.CURRENT);
698          
699         return true;               
700     }

接下來就是看 PeripheralHandoverService 中的 onCreate 以及 onStartCommand 構造:

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

41 public class PeripheralHandoverService extends Service implements BluetoothPeripheralHandover.Callback {
... ...
110     @Override
111     public int onStartCommand(Intent intent, int flags, int startId) {
112          
113         synchronized (sLock) {
114             if (mStartId != 0) {
115                 mStartId = startId;
116                 // already running
117                 return START_STICKY;           
118             }
119             mStartId = startId;
120         }
121          
122         if (intent == null) {
123             if (DBG) Log.e(TAG, "Intent is null, can't do peripheral handover.");
124             stopSelf(startId);
125             return START_NOT_STICKY;       
126         }
127          
128         if (**doPeripheralHandover**(intent.getExtras())) {
129             return START_STICKY;           
130         } else {
131             stopSelf(startId);
132             return START_NOT_STICKY;       
133         }
134     }    
135          
136     @Override
137     public void onCreate() {
138         super.onCreate();
139         mNfcAdapter = NfcAdapter.getDefaultAdapter(getApplicationContext());
140                        
141         IntentFilter filter = new IntentFilter(**BluetoothAdapter.ACTION_STATE_CHANGED**);
142         registerReceiver(mBluetoothStatusReceiver, filter);
143     }

上面我們需要關注的就是:

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

onCreate() 最後註冊的 mBluetoothStatusReceiver 如下:
packages/apps/Nfc//src/com/android/nfc/handover/PeripheralHandoverService.java

 91     final BroadcastReceiver mBluetoothStatusReceiver = new BroadcastReceiver() {
 92         @Override
 93         public void onReceive(Context context, Intent intent) {
 94             String action = intent.getAction();
 95             if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
 96                 handleBluetoothStateChanged(intent);
 97             }
 98         }
 99     };

這個 BroadcastReceiver 的作用在藍牙打開,並且是由 NFC 打開的時候,
就進一步的調用 mBluetoothPeripheralHandover 相關的流程進行處理:
packages/apps/Nfc//src/com/android/nfc/handover/PeripheralHandoverService.java

203     private void handleBluetoothStateChanged(Intent intent) {
204         int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,
205                 BluetoothAdapter.ERROR);
206         if (state == BluetoothAdapter.STATE_ON) {
207             // If there is a pending device pairing, start it
208             if (mBluetoothPeripheralHandover != null &&
209                     !mBluetoothPeripheralHandover.hasStarted()) {
210                 if (!**mBluetoothPeripheralHandover.start()**) {
211                     mNfcAdapter.resumePolling();
212                 } 
213             }     
214         } else if (state == BluetoothAdapter.STATE_OFF) {
215             mBluetoothEnabledByNfc = false;
216             mBluetoothHeadsetConnected = false;
217         }         
218     }

mBluetoothPeripheralHandover 的宣告都在 BluetoothPeripheralHandover,
不急,後面會在看到。先看一下第二部份的 doPeripheralHandover 如下:
packages/apps/Nfc//src/com/android/nfc/handover/PeripheralHandoverService.java

151     boolean doPeripheralHandover(Bundle msgData) {
**//只能有一個BluetoothPeripheralHandover對象,處理這個的Nfc Tag信息讀取,
//此時想要再檢測就得把NFC設備的開關進行打開和關閉操作。**
152         if (mBluetoothPeripheralHandover != null) {
153             Log.d(TAG, "Ignoring pairing request, existing handover in progress.");
154             return true;
155         }            
156                      
... ...
**//這塊就是把msgData中攜帶的數據取出來。(NDEF信息中的數據)**
177         mBluetoothEnabledByNfc = msgData.getBoolean(EXTRA_BT_ENABLED);
178                      
**//BluetoothPeripheralHandover是真正的處理的地方,此處進行唯一的實例化。
//傳入的參數大都是從NDEF信息中解析出來的。**
179         mBluetoothPeripheralHandover = new BluetoothPeripheralHandover(
180                 this, mDevice, name, transport, oobData, uuids, btClass, this);
181                      
... ...
**//如果藍牙已經打開,直接調用start()去進行配對等的處理。**
186         if (mBluetoothAdapter.isEnabled()) {
187             if (!**mBluetoothPeripheralHandover.start()**) {
188                 mHandler.removeMessages(MSG_PAUSE_POLLING);
189                 mNfcAdapter.resumePolling();
190             }
**//如果藍牙沒有打開那麼就調用enableBluetooth去打開藍牙設備。
//此處你打開藍牙成功後也會往外發送廣播最終到前面的receiver中最後也是調用start進行了處理。**
191         } else {
192             // Once BT is enabled, the headset pairing will be started
193             if (!enableBluetooth()) {
194                 Log.e(TAG, "Error enabling Bluetooth.");
195                 mBluetoothPeripheralHandover = null;
196                 return false;
197             }
198         }
199  
200         return true;
201     }

**//此處調用的是framework專門為system級別的app設定的方法enableNoAutoConnect,
//這個方法打開藍牙後不會記錄此次操作。**
251     boolean enableBluetooth() {
252         if (!mBluetoothAdapter.isEnabled()) {
253             mBluetoothEnabledByNfc = true;
254             return mBluetoothAdapter.enableNoAutoConnect();
255         }    
256         return true;
257     }

結語:

我們會發現,
上面的兩種情況不管哪一個,
最終都走到了 BluetoothPeripheralHandoverstart() 方法,
接下來我們就在下一篇詳細說明吧。


Reference :

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


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

尚未有邦友留言

立即登入留言