<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                本節開始時介紹,Android平臺中,NFC系統模塊運行在com.android.nfc進程中,該進程對應的應用程序文件名為Nfc.apk。NFC系統模塊包含的組件非常多,所以通過以下幾條分析路線來介紹。 * NFC系統模塊的核心NfcService和一些重要成員的作用及之間的關系。 * R/W模式下NFC Tag的處理。 * Android Beam的實現。 * CE模式相關的處理。 **1、NfcService介紹** Nfc.apk源碼中包含一個NfcApplication類。當該應用啟動時,NfcApplication的onCreate函數將被調用。正是在這個onCreate函數中,NFC系統模塊的核心成員NfcService得以創建。我們直接來看NfcService的構造函數。 **NfcService.java::NfcService** ~~~ public NfcService(Application nfcApplication) { // NFC系統模塊重要成員 mNfcTagService = new TagService();// TagService用于和NFC Tag交互 // NfcAdapterService用于和Android系統中其他使用NfcService的客戶端交互 mNfcAdapter = new NfcAdapterService(); // NfcAdapterExtrasService用于和Android系統中使用Card Emulation模式的客戶端交互 mExtrasService = new NfcAdapterExtrasService(); sService = this; mContext = nfcApplication; // NativeNfcManager由dhimpl模塊實現,用于和具體芯片廠商提供的NFC模塊交互 mDeviceHost = new NativeNfcManager(mContext, this); // HandoverManager處理Connection Handover工作 HandoverManager handoverManager = new HandoverManager(mContext); // NfcDispatcher用于向客戶端派發NFC Tag相關的通知 mNfcDispatcher = new NfcDispatcher(mContext, handoverManager); // P2pLinkManager用于處理LLCP相關的工作 mP2pLinkManager = new P2pLinkManager(mContext, handoverManager, mDeviceHost.getDefaultLlcpMiu(), mDeviceHost.getDefaultLlcpRwSize()); // NativeNfcSecureElement用于和SE交互,它也由dhimpl模塊實現 mSecureElement = new NativeNfcSecureElement(mContext); mEeRoutingState = ROUTE_OFF; /* NfceeAccessControl用于判斷哪些應用程序有權限操作NFCEE。它將讀取/etc/nfcee_access.xml文件的 內容。nfcee_access.xml內容比較簡單,請參考Nfc目錄下的etc/sample_nfcee_access.xml來學習。 */ mNfceeAccessControl = new NfceeAccessControl(mContext); ...... // 向系統注冊一個“nfc”服務。注意,SERVICE_NAME的值為“nfc”。該服務對應的對象為mNfcAdapter ServiceManager.addService(SERVICE_NAME, mNfcAdapter); /* 注冊廣播事件監聽對象。NfcService對屏幕狀態、應用程序安裝和卸載等廣播事件感興趣。這部分內容請讀者 自行研究。 */ ...... // EnableDisableTask為一個AsyncTask,TASK_BOOT用于NfcService其他初始化工作 new EnableDisableTask().execute(TASK_BOOT); } ~~~ 由上述代碼可知,NfcService在其構造函數中,首先創建了NFC系統模塊的幾個核心成員。下文將詳細介紹它們的作用及之間的關系。NfcService向Binder系統添加了一個名為"nfc"的服務,該服務對應的Binder對象為mNfcAdapter,類型為NfcAdapter。通過一個AysncTask(代碼中的EnableDisableTask)完成NfcService其他初始化工作。 下面馬上來看NFC系統模塊核心成員。 **①、NfcService核心成員** 圖8-35所示為NfcAdapter、TagService等相關成員的類信息。 :-: ![](http://img.blog.csdn.net/20140322214731484?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvSW5ub3N0/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) 圖8-35 NfcAdapter、TagService及相關類成員結構圖 圖8-35中,NfcAdapter包含一個類型為INfcAdapter的sService成員變量,該變量通過Android Binder機制來和NfcService內部類NfcAdapter的實例(即上述代碼中的mAdapter)交互。NfcService內部的NfcAdapter對象即是注冊到Android系統服務中的那個名為"nfc"的Binder服務端對象。 NfcAdapter還包含一個類型為INfcTag的sTagService成員變量,該變量通過Android Binder機制來和NfcService內部類TagService的實例(即上述代碼中的mNfcTagService)交互。INfcTag接口封裝了對Tag操作相關的函數。注意,前面所示的Nfc客戶端示例中并沒有直接使用INfcTag接口的地方,但表8-12所列的各種Tech類內部需要通過ITag接口來操作NFC Tag。 NfcAdapterExtras包含一個類型為INfcAdapterExtras的sService成員變量,也通過Android Binder機制來和NfcService內部類NfcAdapterService的實例(即上述代碼中的mExtrasService)交互。 接著來看NfcService和NativeNfcManager,它們的類家族如圖8-36所示。 :-: ![](http://img.blog.csdn.net/20140322214753375?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvSW5ub3N0/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) 圖8-36 NativeNfcManager和NfcService類家族 Android NFC系統模塊通過接口類DeviceHost和其內部的接口類LlcpServerSocket、DeviceHostListener、LlcpSocket、LlcpConnectionlessSocket、NfcDepEndpoint、TagEndpoint將NFC系統模塊中和NFC芯片無關的處理邏輯,以及和芯片相關的處理邏輯進行了有效解耦。圖8-36中以"Native"開頭的類均定義在packages/app/Nfc/nxp目錄下,所以它們和NXP公司的NFC芯片相關。 DeviceHost接口中,DeviceHost類用于和底層NFC芯片交互,TagEndpoint用于和NFC Tag交互,NfcDepEndpoint用于和P2P對端設備交互,LlcpSocket和LlcpServerSocket分別用于LLCP中有鏈接數據傳輸服務的客戶端和服務器端,LlcpConnectionlessSocket用于LLCP中無連接數據傳輸服務。另外,DeviceHostListener也非常重要,它用于NativeNfcManager往NfcService傳遞NFC相關的通知事件。例如其中的onRemoteEndpointDiscovered代表搜索到一個NFC Tag、onLlcpActivited代表本機和對端NFC設備進入Link Activation(鏈路激活)階段。 NativeNfcManager實現了DeviceHost接口,以NXP公司的NativeNfcManager為例,它將通過libnfc_jni及libnfc和NXP公司的NFC芯片交互。 NativeNfcSecureElement用來和Secure Element交互。 接下來要出場的是HandoverManager以及P2pLinkManager,它們的家族關系如圖8-37所示。 :-: ![](http://img.blog.csdn.net/20140322214814406?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvSW5ub3N0/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) 圖8-37 P2pLinkManager家族關系圖 圖8-37所示的P2pLinkManager家族關系非常復雜,圖中的各成員說明如下。 * P2pLinkManager包含三個和傳輸相關的Server,分別是SnepServer、NdefPushServer以及HandoverServer。每一個Server都定義了相關的回調接口類(如SnepServer.callback),這些回調接口類的實現均由P2pLinkManager的內部類來實現。 * HandoverServer負責處理NFC Connection Handover協議,而具體的數據傳輸則由HandoverManager來實現。由圖中HandoverMangar的mBluetoothAdapter可知,Android默認使用藍牙來傳輸數據(提示:這部分內容請讀者學完本章后自行研究)。 * P2pEventManager用于處理NFC P2P中和用戶交互相關的邏輯。例如當搜索到一個P2P設備時,手機將會震動并發出提示音。 SendUi實現了類似圖8-29左圖的界面及用戶觸摸事件處理。在Android平臺中,當兩個設備LLCP鏈路被激活時,SendUi將顯示出來。其界面組成部分包括兩個部分,一個是SendUi界面顯示之前手機的截屏圖像,另外一個是“觸摸即可發送”的文本提示信息。當用戶觸摸SendUi界面時,數據將被發送出去。 **②、enableInternal函數** 介紹完NfcService的幾個核心成員后,馬上來看NfcService構造函數中最后創建的EnableDisableTask,由于設置了參數為TASK_BOOT,故最終被執行的函數為enableInternal。 **NfcService.java::EnableDisableTask:enableInternal** ~~~ boolean enableInternal() { ...... // 啟動一個WatchDog線程用來監視NFC底層操作是否超時 WatchDogThread watchDog = new WatchDogThread("enableInternal", INIT_WATCHDOG_MS); watchDog.start(); try { mRoutingWakeLock.acquire(); try { // 初始化NFC底層模塊,這部分內容請讀者自行閱讀 if (!mDeviceHost.initialize()) {......} } finally { mRoutingWakeLock.release(); } } finally { watchDog.cancel(); } synchronized(NfcService.this) { mObjectMap.clear(); // mIsNdefPushEnabled判斷是否啟用NPP協議,可在Settings中設置 mP2pLinkManager.enableDisable(mIsNdefPushEnabled, true); updateState(NfcAdapter.STATE_ON); } initSoundPool();// 創建SoundPool,用于播放NFC相關事件的通知音 applyRouting(true);// 啟動NFC Polling流程,一旦搜索到周圍的NFC設備,相關回調將被調用 return true; } ~~~ 我們重點關注上面代碼中和P2pLinkManager相關的enableDisable函數,其代碼如下所示。 **P2pLinkManager.java::enableDisable** ~~~ public void enableDisable(boolean sendEnable, boolean receiveEnable) { synchronized (this) {// 假設參數sendEnable和receiveEnable為true if (!mIsReceiveEnabled && receiveEnable) { /* 啟動SnepServer、NdefPushServer和HandoverServer。 下面這三個成員變量均在P2pLinkManager的構造函數中被創建,這部分內容請讀者自行閱讀 本章將只分析SnepServer。 */ mDefaultSnepServer.start(); mNdefPushServer.start(); mHandoverServer.start(); if (mEchoServer != null) // EchoServer用于測試,以后代碼分析將忽略它 mHandler.sendEmptyMessage(MSG_START_ECHOSERVER); }...... mIsSendEnabled = sendEnable; mIsReceiveEnabled = receiveEnable; } } ~~~ **③、SnepServer的start函數** SnepServer的start函數將創建一個ServerThread線程對象,其run函數代碼如下所示。 **SnepServer.java::ServerThread:run** ~~~ public void run() {// 注意:為了方便閱讀,此處代碼省略了synchronized和try/catch等一些代碼邏輯 boolean threadRunning; threadRunning = mThreadRunning; while (threadRunning) { // 創建一個LlcpServerSocket,其中mServiceSap值為0x04,mServiceName為“urn:nfc:sn:sne” // mMiu和mRwSize為本機NFC LLCP層的MIU和RW大小。1024為內部緩沖區大小,單位為字節 mServerSocket = NfcService.getInstance().createLlcpServerSocket(mServiceSap, mServiceName, mMiu, mRwSize, 1024); LlcpServerSocket serverSocket; serverSocket = mServerSocket; // 等待客戶端的鏈接 LlcpSocket communicationSocket = serverSocket.accept(); if (communicationSocket != null) { // 獲取客戶端設備的MIU int miu = communicationSocket.getRemoteMiu(); /* 判斷分片大小。mFragmentLength默認為-1。MIU非常重要。例如本機的MIU為1024,而 對端設備的MIU為512,那么本機在向對端發送數據時,每次發送的數據不能超過對端 MIU即512字節。 */ int fragmentLength = (mFragmentLength == -1) ?miu : Math.min(miu, mFragmentLength); // 每一個連接成功的客戶端對應一個ConnectionThread,其內容留待下文詳細分析 new ConnectionThread(communicationSocket, fragmentLength).start(); } } mServerSocket.close(); } ~~~ NfcService初始化完畢后,手機中的NFC模塊就進入工作狀態,一旦有Tag或其他設備進入其有效距離,NFC模塊即可開展相關工作。 下面先來分析NFC Tag的處理流程。 **2、NFC Tag處理流程分析** **①、notifyNdefMessageListeners流程** 當NFC設備檢測到一個NFC Tag時,NativeNfcManager的notifyNdefMessageListeners函數將被調用(由libnfc_jni在JNI層調用),其代碼如下所示。 **NativeNfcManager.java::notifyNdefMessageListeners** ~~~ private void notifyNdefMessageListeners(NativeNfcTag tag) { /* mListener指向NfcService,它實現了DeviceHostListener接口。 注意,notifyNdefMessageListeners的參數類型為NativeNfcTag,tag對象由jni層直接創建 并返回給Java層。 */ mListener.onRemoteEndpointDiscovered(tag); } ~~~ 上述代碼中,mListener指向NfcService,它的onRemoteEndPointDiscovered函數代碼如下所示。 **NfcService.java::onRemoteEndpointDiscovered** ~~~ public void onRemoteEndpointDiscovered(TagEndpoint tag) { // 注意,onRemoteEndpointDiscovered的參數類型是TagEndpoint // 由圖8-36可知,NativeNfcTag實現了該接口 sendMessage(NfcService.MSG_NDEF_TAG, tag);// 發送一個MSG_NDEF_TAG消息 } ~~~ NfcService的onRemoteEndpointDiscovered將給自己發送一個MSG_NDEF_TAG消息。 NfcService內部有一個NfcServiceHandler專門用來處理這些消息。其處理函數如下所示。 **NfcService.java::NfcServiceHandler:handleMessage** ~~~ final class NfcServiceHandler extends Handler { public void handleMessage(Message msg) { switch (msg.what) { ......// 其他消息處理 case MSG_NDEF_TAG: TagEndpoint tag = (TagEndpoint) msg.obj; playSound(SOUND_START);// 播放一個通知音 /* 從該NFC Tag中讀取NDEF消息,NativeNfcTag的findAndReadNdef比較復雜, 其大體工作流程是嘗試用該Tag支持的Technology來讀取其中的內容。 */ NdefMessage ndefMsg = tag.findAndReadNdef(); // 注意下面這段代碼,無論ndefMsg是否為空,dispatchTagEndpoint都會被調用 if (ndefMsg != null) { tag.startPresenceChecking();// 檢測目標Tag是否還在有效距離內 dispatchTagEndpoint(tag);// 重要函數,詳情見下節 } else { if (tag.reconnect()) {// 重新鏈接到此NFC Tag tag.startPresenceChecking(); dispatchTagEndpoint(tag); } ...... } break; ...... } } } ~~~ 由上述代碼可知,NfcService先調用TagEndpoint的findAndReadNdef函數來讀取Tag中的數據,然后NfcService將調用dispatchTagEndpoint做進一步處理。 >[info] 提示 findAndReadNdef的實現和具體的NFC芯片有關,而NXP公司的實現函數在NativeNfcTag類中,內容比較復雜,感興趣的讀者可以閱讀。 **②、dispatchTagEndpoint流程** 代碼如下。 **NfcService.java::NfcServiceHandler.dispatchTagEndpoint** ~~~ private void dispatchTagEndpoint(TagEndpoint tagEndpoint) { // 構造一個Tag對象。前面的示例中已經見過Tag。對客戶端來說,它代表目標NFC Tag Tag tag = new Tag(tagEndpoint.getUid(), tagEndpoint.getTechList(), tagEndpoint.getTechExtras(), tagEndpoint.getHandle(), mNfcTagService); registerTagObject(tagEndpoint);// 保存此tagEndpoint對象 // mNfcDispatcher的類型是NfcDispather,調用它的dispatchTag函數來分發Tag if (!mNfcDispatcher.dispatchTag(tag)) {......} } ~~~ **NfcDispatcher.java::dispatchTag** ~~~ public boolean dispatchTag(Tag tag) { NdefMessage message = null; Ndef ndef = Ndef.get(tag);// 構造一個Ndef對象,Ndef屬于Tag Technology的一種 /* 從Ndef獲取目標Tag中的NDEF消息。如果目標Tag中保存的是系統支持的NDEF消息,則message不為空。 特別注意:在前面代碼中見到的findAndReadNdef函數內部已經根據表8-11進行了相關處理。 */ if (ndef != null) message = ndef.getCachedNdefMessage(); PendingIntent overrideIntent; IntentFilter[] overrideFilters; String[][] overrideTechLists; // ①構造一個DispatchInfo對象,該對象內部有一個用來觸發Activity的Intent DispatchInfo dispatch = new DispatchInfo(mContext, tag, message); synchronized (this) { // 下面三個變量由前臺分發系統相關的NfcAdapter enableForegroundDispatch函數設置 overrideFilters = mOverrideFilters; overrideIntent = mOverrideIntent; overrideTechLists = mOverrideTechLists; } // 恢復App Switch,詳情可參考《深入理解Android:卷Ⅱ》6.3.3節關于resume/stopAppSwitches的介紹 resumeAppSwitches(); // 如果前臺Activity啟用了前臺分發功能,則只需要處理前臺分發相關工作即可 if (tryOverrides(dispatch, tag, message, overrideIntent, overrideFilters, overrideTechLists)) return true; // 處理Handover事件 if (mHandoverManager.tryHandover(message)) return true; // ②下面是Tag分發系統的處理,首先處理ACTION_NDEF_DISCOVERED if (tryNdef(dispatch, message)) return true; // 如果tryNdef處理失敗,則接著處理ACTION_TECH_DISCOVERED if (tryTech(dispatch, tag)) return true; // 如圖tryTech處理失敗,則處理ACTION_TAG_DISCOVERED // 設置DispatchInfo對象的內部Intent對應的ACTION為ACTION_TAG_DISCOVERED dispatch.setTagIntent(); // 首先從PackageManagerService查詢對ACTION_TAG_DICOVERED感興趣的Activity,如果有則啟動它 if (dispatch.tryStartActivity()) return true; return false; } ~~~ 上述代碼中有①②兩個重要函數,我們先來看第一個。 **NfcDispatcher.java::DispatchInfo構造函數** ~~~ public DispatchInfo(Context context, Tag tag, NdefMessage message) { // 這個Intent的內容將派發給NFC的客戶端 intent = new Intent(); /* 不論最終Intent的Action是什么,NFC系統模塊都會將tag對象和tag的ID包含在Intent中傳遞 給客戶端。 */ intent.putExtra(NfcAdapter.EXTRA_TAG, tag); intent.putExtra(NfcAdapter.EXTRA_ID, tag.getId()); // 如果NDEF消息不為空,則把它也保存在Intent中 if (message != null) { intent.putExtra(NfcAdapter.EXTRA_NDEF_MESSAGES, new NdefMessage[] {message}); ndefUri = message.getRecords()[0].toUri(); ndefMimeType = message.getRecords()[0].toMimeType(); } else { ndefUri = null; ndefMimeType = null; } /* rootIntent用來啟動目標Activity。NfcRootActivity是Nfc.apk中定義的一個Activity。 目標Activity啟動的過程如下。NFC系統模塊先啟動NfcRootActivity,然后再由NfcRootActivity 啟動目標Activity。由于NfcRootActivity設置了啟動標志(FLAG_ACTIVITY_NEW_TASK和 FLAG_ACTIVITY_CLEAR_TASK),所以目標Activity將單獨運行在一個Task中。關于ActivityManager 這部內容,感興趣的讀者可閱讀《深入理解Android:卷Ⅱ》第6章。 */ rootIntent = new Intent(context, NfcRootActivity.class); // 將Intent信息保存到rootIntent中。 rootIntent.putExtra(NfcRootActivity.EXTRA_LAUNCH_INTENT, intent); rootIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); this.context = context; packageManager = context.getPackageManager(); } ~~~ 接著來看第二個關鍵函數tryNdef,代碼如下所示。 **NfcDispatcher.java::tryNdef** ~~~ boolean tryNdef(DispatchInfo dispatch, NdefMessage message) { if (message == null) return false; /* setNdefIntent: 設置Dispatcher內部intent對象的Action為ACTION_NDEF_DISCOVERED。 如果Dispatch對象的ndefUri和ndefMimeType都為null,則函數返回null。 */ Intent intent = dispatch.setNdefIntent(); if (intent == null) return false; // 如果message中包含了AAR信息,則取出它們。AAR信息就是應用程序的包名 List<String> aarPackages = extractAarPackages(message); for (String pkg : aarPackages) { dispatch.intent.setPackage(pkg); /* tryStartActivity先檢查目標Activity是否存在以及目標Activity的IntenFiltert 是否匹配intent。注意,下面這個tryStartActivity沒有參數。 */ if (dispatch.tryStartActivity()) return true; } // 上面代碼對目標Activity進行了精確匹配,如果沒有找到,則嘗試啟動AAR指定的應用程序 if (aarPackages.size() > 0) { String firstPackage = aarPackages.get(0); PackageManager pm; /* 下面這段代碼用于啟動目標應用程序的某個Activity,由于AAR只是指定了應用程序的包名而沒有指定 Activity,所以getLaunchIntentForPackage將先檢查目標應用程序中是否有Activity的Category 為CATEGORY_INFO或CATEGORY_LAUNCHER,如果有則啟動它。 */ UserHandle currentUser = new UserHandle(ActivityManager.getCurrentUser()); pm = mContext.createPackageContextAsUser("android", 0,currentUser).getPackageManager(); Intent appLaunchIntent = pm.getLaunchIntentForPackage(firstPackage); if (appLaunchIntent != null && dispatch.tryStartActivity(appLaunchIntent)) return true; /* 如果上述處理失敗,則獲得能啟動應用市場去下載某個應用程序的的Intent,該Intent的 Data字段取值為“market:// details?id=應用程序包名”。 */ Intent marketIntent = getAppSearchIntent(firstPackage); if (marketIntent != null && dispatch.tryStartActivity(marketIntent)) return true; } // 處理沒有AAR的NDEF消息 dispatch.intent.setPackage(null); if (dispatch.tryStartActivity()) return true; return false; } ~~~ **③、NFC Tag處理流程總結** NFC Tag的處理流程還算簡單,下面總結其中涉及的重要函數調用,如圖8-38所示。 :-: ![](https://box.kancloud.cn/09824e1d0a1f071ab1e59f4c1bdbff8f_999x692.jpg) 圖8-38 NFC Tag處理流程 NFC Tag的處理流程比較簡單,其中有的代碼邏輯比較復雜,這部分內容主要集中在NativeNfcTag的findAndReadNdef函數中。它和具體NFC芯片廠商的實現有關,故把它留給感興趣的讀者自己來研究。 下面我們將研究Android Beam的工作流程。 **3、Android Beam工作流程分析** 當本機檢測到某個NFC設備進入有效距離并且能處理LLCP協議后,將通過notifyLlcpLinkActivation通知我們。本節就從這個函數開始分析。 **①、notifyLlcpLinkActivation流程** notifyLlcpLinkActivation代碼如下所示。 **NativeNfcManager.java::notifyLlcpLinkActivation** ~~~ private void notifyLlcpLinkActivation(NativeP2pDevice device) { // mListener指向NfcService // 它的onLlcpLinkActivated函數將發送一個MSG_LLCP_LINK_ACTIVATION消息 mListener.onLlcpLinkActivated(device); } ~~~ MSG_LLCP_LINK_ACTIVATION消息由NfcService的內部類NfcServiceHandler處理,它將調用llcpActivated函數,代碼如下所示。 **NfcService.java::NfcServiceHandler:llcpActivated** ~~~ private boolean llcpActivated(NfcDepEndpoint device) { /* NfcDepEndpoint代表對端設備,其真實類型是NativeP2pDevice。不論對端設備是Target還是 Initiator,P2pLinkManager的onLlcpActivated函數都將被調用。 */ if (device.getMode() == NfcDepEndpoint.MODE_P2P_TARGET) { if (device.connect()) {// 如果對端是Target,則需要連接上它 if (mDeviceHost.doCheckLlcp()) { if (mDeviceHost.doActivateLlcp()) { synchronized (NfcService.this) { mObjectMap.put(device.getHandle(), device); } mP2pLinkManager.onLlcpActivated(); return true; }...... } else if (device.getMode() == NfcDepEndpoint.MODE_P2P_INITIATOR) { if (mDeviceHost.doCheckLlcp()) { if (mDeviceHost.doActivateLlcp()) { synchronized (NfcService.this) { mObjectMap.put(device.getHandle(), device); } mP2pLinkManager.onLlcpActivated(); return true; }...... } return false; } } } } ~~~ P2pLinkManager的onLlcpActivated函數代碼如下所示。 **P2pLinkManager.java::onLlcpActivated** ~~~ public void onLlcpActivated() { synchronized (P2pLinkManager.this) { ..... switch (mLinkState) { case LINK_STATE_DOWN:// 如果之前沒有LLCP相關的活動,則mLinkState為LINK_STATE_DOWN mLinkState = LINK_STATE_UP; mSendState = SEND_STATE_NOTHING_TO_SEND; /* mEventListener指向P2pEventManager,它的onP2pInRange函數中將播放 通知音以提醒用戶,同時它還會通過SendUi截屏。請讀者自行閱讀該函數。 */ mEventListener.onP2pInRange(); prepareMessageToSend();// ①準備發送數據 if (mMessageToSend != null || (mUrisToSend != null && mHandoverManager.isHandoverSupported())) { mSendState = SEND_STATE_NEED_CONFIRMATION; // ②顯示提示界面,讀者可參考圖8-29的左圖 mEventListener.onP2pSendConfirmationRequested(); } break; ...... } } } ~~~ onLlcpActivated有兩個關鍵函數,我們先來看第一個函數prepareMessageToSend,其代碼如下所示。 **P2pLinkManager.java::prepareMessageToSend** ~~~ void prepareMessageToSend() { synchronized (P2pLinkManager.this) { ...... // 還記得NFC P2P模式示例程序嗎?可通過setNdefPushMessageCallback設置回調函數 if (mCallbackNdef != null) { try { mMessageToSend = mCallbackNdef.createMessage();// 從回調函數那獲取要發送的數據 /* getUris和NfcAdapter的setBeamPushUrisCallback函數有關,它用于發送file或 content類型的數據。由于這些數據需要借助Handover技術,請讀者自己來分析。 */ mUrisToSend = mCallbackNdef.getUris(); return; } ...... } // 如果沒有設置回調函數,則系統會嘗試獲取前臺應用進程的信息 List<RunningTaskInfo> tasks = mActivityManager.getRunningTasks(1); if (tasks.size() > 0) { // 獲取前臺應用進程的包名 String pkg = tasks.get(0).baseActivity.getPackageName(); /* 應用程序可以在其AndroidManifest中設置“android.nfc.disable_beam_default” 標簽為false,以阻止系統通過Android Beam來傳遞與該應用相關的信息。 */ if (beamDefaultDisabled(pkg)) mMessageToSend = null; else { /* createDefaultNdef將創建一個NDEF消息,該消息包含兩個NFC Record。 第一個NFC Record包含URI類型的數據,其內容為“http://play.google.com/store/apps /details?id=應用程序包名&feature=beam”。 第二個NFC Record為AAR類型,其內容為應用程序的包名。 */ mMessageToSend = createDefaultNdef(pkg);// 包名為pkg的應用程序 } }else mMessageToSend = null; } } ~~~ prepareMessageToSend很有意思,其主要工作如下。 * 如果設置回調對象,系統將從回調對象中獲取要發送的數據。 * 如果沒有回調對象,系統會獲取前臺應用程序的包名。如果前臺應用程序禁止通過 Android Beam分享信息,則prepareMessageToSend直接返回,否則它將創建一個包含了兩個NFC Record的NDEF消息。 接下來看第二個關鍵函數onP2pSendConfirmationRequested,其代碼如下所示。 **P2pEventManager.java::onP2pSendConfirmationRequested** ~~~ public void onP2pSendConfirmationRequested() { // 對于擁有顯示屏幕的Android設備來說,mSendUi不為空 if (mSendUi != null) mSendUi.showPreSend();// 顯示類似圖8-29左圖所示的界面以提醒用戶 else mCallback.onP2pSendConfirmed(); /* 對于那些沒有顯示屏幕的Android設備來說,直接調用onP2pSendConfirmed。mCallback指向 P2pLinkManager。待會將分析這個函數。 */ } ~~~ 在onP2pSendConfirmationRequested函數中,SendUi的showPreSend函數將繪制一個通知界面,如圖8-29所示。至此,notifyLlcpLinkActivation的工作完畢。在繼續分析Android Beam之前,先來總結notifyLlcpLinkActivation的工作流程,如圖8-39所示。 :-: ![](https://box.kancloud.cn/c820becf4a17a05a238748b30efb3c92_1287x483.jpg) 圖8-39 notifyLlcpLinkActivation流程 **②、Beam數據發送流程** SendUi界面將提醒用戶觸摸屏幕以發送數據,觸摸屏幕這一動作將導致SendUi的onTouch函數被調用,其代碼如下所示。 **SendUi.java::onTouch** ~~~ public boolean onTouch(View v, MotionEvent event) { ...... mCallback.onSendConfirmed();// mCallback指向P2pEventManager return true; } ~~~ **P2pEventManager.java::onSendConfirmed** ~~~ public void onSendConfirmed() { if (!mSending) { if (mSendUi != null) mSendUi.showStartSend();// 顯示數據發送動畫 mCallback.onP2pSendConfirmed();// mCallback指向P2pLinkManager } mSending = true; } ~~~ **P2pLinkManager.java::onP2pSendConfirmed** ~~~ public void onP2pSendConfirmed() { synchronized (this) { ...... mSendState = SEND_STATE_SENDING; if (mLinkState == LINK_STATE_UP) sendNdefMessage();// 關鍵函數 } } ~~~ sendNefMessage將創建一個類型為SendTask的AsyncTask實例以處理數據發送相關的工作,我們來看這個SendTask,相關代碼如下所示。 **P2pLinkManager.java::SendTask:doInBackground** ~~~ final class SendTask extends AsyncTask<Void, Void, Void> { public Void doInBackground(Void... args) { NdefMessage m; Uri[] uris; boolean result; m = mMessageToSend; uris = mUrisToSend; // 設置要發送的數據 long time = SystemClock.elapsedRealtime(); try { int snepResult = doSnepProtocol(mHandoverManager, m, uris, mDefaultMiu, mDefaultRwSize); ...... } catch (IOException e) { // 如果使用SNEP發送失敗,則將利用NPP協議再次嘗試發送 if (m != null) // 請讀者自行研究和NPP相關的代碼 result = new NdefPushClient().push(m); ...... } time = SystemClock.elapsedRealtime() - time; if (result) onSendComplete(m, time);// 發送完畢。請讀者自行閱讀該函數 return null; } } ~~~ 上述代碼中的doSnepProtocol函數內容如下。 **P2pLinkManager.java::SendTask:doSnepProtocol** ~~~ static int doSnepProtocol(HandoverManager handoverManager, NdefMessage msg, Uri[] uris, int miu, int rwSize) throws IOException { // 創建一個SnepClient客戶端 SnepClient snepClient = new SnepClient(miu, rwSize); try { snepClient.connect(); // ①連接遠端設備的SnepServer } ...... try { if (uris != null) {// 如果uris不為空,需要使用HandoverManager,這部分內容請讀者自行閱讀 ...... } else if (msg != null) { snepClient.put(msg);// ②利用SNEP的PUT命令發送數據 } return SNEP_SUCCESS; } catch ...... finally { snepClient.close(); } return SNEP_FAILURE; } ~~~ 重點介紹SnepClient的connect函數以及put函數。connect函數的代碼如下所示。 **SnepClient.java::connect** ~~~ public void connect() throws IOException { ...... LlcpSocket socket = null; SnepMessenger messenger; // SnepMessenger用于處理數據發送和接收 try { socket = NfcService.getInstance().createLlcpSocket(0, mMiu, mRwSize, 1024); if (mPort == -1) socket.connectToService(mServiceName);// 通過服務名來連接服務端 else socket.connectToSap(mPort); // 通過SAP連接服務端 // 獲取遠端設備的MIU int miu = socket.getRemoteMiu(); int fragmentLength = (mFragmentLength == -1) ? miu : Math.min(miu, mFragmentLength); messenger = new SnepMessenger(true, socket, fragmentLength); } ...... ...... } ~~~ put函數代碼如下所示。 **SnepClient.java::put** ~~~ public void put(NdefMessage msg) throws IOException { SnepMessenger messenger; messenger = mMessenger; synchronized (mTransmissionLock) { try { /* 獲取SNEP PUT命令對應的數據包,然后發送出去。SnepMessenger的sendMessage 函數將完成具體的發送工作。 */ messenger.sendMessage(SnepMessage.getPutRequest(msg)); messenger.getMessage(); }..... } } ~~~ 圖8-40總結了本節所述的Beam數據發送流程。 :-: ![](https://box.kancloud.cn/79044d4af1daa7f1c7f9720b4045d5aa_983x562.jpg) 圖8-40 Beam數據發送流程 接著來看Beam數據接收流程。 **③、Beam數據接收流程** 8.3.2節中曾介紹,SnepServer每接收一個客戶端的連接后均會創建一個ConnectionThread,它的代碼如下所示。 **SnepServer.java::ConnectionThread** ~~~ private class ConnectionThread extends Thread { private final LlcpSocket mSock; private final SnepMessenger mMessager; ConnectionThread(LlcpSocket socket, int fragmentLength) { super(TAG); mSock = socket; // 也創建一個SnepMessenger用來處理具體的數據接收 mMessager = new SnepMessenger(false, socket, fragmentLength); } public void run() { ......// 省略一些try/catch邏輯 while (running) { if (!handleRequest(mMessager, mCallback)) break; ...... } mSock.close(); } } ~~~ **SnepServer.java::handleRequest** ~~~ static boolean handleRequest(SnepMessenger messenger,          Callback callback) throws IOException { SnepMessage request; request = messenger.getMessage(); ...... if (((request.getVersion() & 0xF0) >> 4) != SnepMessage.VERSION_MAJOR) { messenger.sendMessage(SnepMessage.getMessage( SnepMessage.RESPONSE_UNSUPPORTED_VERSION)); } else if (request.getField() == SnepMessage.REQUEST_GET) { // 處理GET命令,callback類型為SnepServer的內部類Callback messenger.sendMessage(callback.doGet(request.getAcceptableLength(), request.getNdefMessage())); } else if (request.getField() == SnepMessage.REQUEST_PUT) { // 處理PUT命令 messenger.sendMessage(callback.doPut(request.getNdefMessage())); } ..... return true; } ~~~ 來看SnepServer.callback的doPut函數,它由P2pLinkManager的內部類實現,代碼如下所示。 **P2pLinkManager.java::SnepServer.Callback** ~~~ final SnepServer.Callback mDefaultSnepCallback = new SnepServer.Callback() { public SnepMessage doPut(NdefMessage msg) { onReceiveComplete(msg); return SnepMessage.getMessage(SnepMessage.RESPONSE_SUCCESS); } } ~~~ onReceiveComplete的代碼如下所示。 **P2pLinkManager.java::onReceiveComplete** ~~~ void onReceiveComplete(NdefMessage msg) { // 發送一個MSG_RECEIVE_COMPLETE消息 mHandler.obtainMessage(MSG_RECEIVE_COMPLETE, msg).sendToTarget(); } ~~~ MSG_RECEIVE_COMPLETE消息由P2pLinkManager的handleMessge處理,相關代碼邏輯如下所示。 **P2pLinkManager.java::** ~~~ public boolean handleMessage(Message msg) { switch (msg.what) { ...... case MSG_RECEIVE_COMPLETE: NdefMessage m = (NdefMessage) msg.obj; synchronized (this) { ...... mSendState = SEND_STATE_NOTHING_TO_SEND; mEventListener.onP2pReceiveComplete(true);// 取消本機顯示的SendUi界面 // sendMockNdefTag將發送一個MSG_MOCK_NDEF消息給NfcService的NfcServiceHandler NfcService.getInstance().sendMockNdefTag(m); } break; ..... } } ~~~ **NfcService.java::NfcServiceHandler:handleMessage** ~~~ final class NfcServiceHandler extends Handler { public void handleMessage(Message msg) { switch (msg.what) { case MSG_MOCK_NDEF: { NdefMessage ndefMsg = (NdefMessage) msg.obj; Bundle extras = new Bundle(); extras.putParcelable(Ndef.EXTRA_NDEF_MSG, ndefMsg); extras.putInt(Ndef.EXTRA_NDEF_MAXLENGTH, 0); extras.putInt(Ndef.EXTRA_NDEF_CARDSTATE, Ndef.NDEF_MODE_READ_ONLY); extras.putInt(Ndef.EXTRA_NDEF_TYPE, Ndef.TYPE_OTHER); // 創建一個模擬Tag對象 Tag tag = Tag.createMockTag(new byte[] { 0x00 }, new int[] { TagTechnology.NDEF },new Bundle[] { extras }); // 直接通過分發系統分發這個Tag boolean delivered = mNfcDispatcher.dispatchTag(tag); ...... break; } ...... } } } ~~~ Beam接收端的處理流程比較巧妙,系統將創建一個模擬的Tag對象,然后利用分發系統來處理它。圖8-41總結了Beam接收端的處理流程。 :-: ![](https://box.kancloud.cn/009b6118f851b65bedd3843b464e16ba_1008x590.jpg) 圖8-41 Beam數據接收流程 **4、CE模式** NFC系統模塊對CE模式的支持集中在以下兩點。 * 通過NfcAdapterExtras為CE模式的客戶端提供INfcAdapterExtras功能實現。這部分內容所涉及的調用流程比較簡單,請讀者自行研讀。當然,我們略過了和芯片相關的部分。 * 當NFC設備進入CE模式后,和它交互的另一端是諸如NFC Reader這樣的設備。此時對端發起一些操作,而NativeNfcManagement的一些回調函數將被觸發。下面代碼展示了和CE相關的這些回調函數。 **NativeNfcManager.java::CE相關回調函數** ~~~ // 根據NFCIP-1協議,該函數表示對端設備發送了DESELECT命令給我們。它屬于deactivation階段的命令 private void notifyTargetDeselected() {// mListern指向NfcService mListener.onCardEmulationDeselected(); } /* 用于通知SE上某個應用程序開始和對端設備進行交互。AID為Application ID的縮寫,它代表SE上 的某個應用程序(稱為Applet)。 */ private void notifyTransactionListeners(byte[] aid) { mListener.onCardEmulationAidSelected(aid); } // 用于通知SE模塊被激活 private void notifySeFieldActivated() { mListener.onRemoteFieldActivated(); } // 用于通知SE模塊被禁止 private void notifySeFieldDeactivated() { mListener.onRemoteFieldDeactivated(); } // 收到對端設備的APDU命令 private void notifySeApduReceived(byte[] apdu) { mListener.onSeApduReceived(apdu); } /* EMV是EMV標準是由國際三大銀行卡組織Europay(歐陸卡,已被萬事達收購)、MasterCard(萬事達卡)和 Visa(維薩)共同發起制定的銀行卡從磁條卡向智能IC卡轉移的技術標準,是基于IC卡的金融支付標準, 目前已成為公認的全球統一標準。下面這個函數用于通知EMV Card進入Removal階段。 該函數只適用于NXP公司的相關芯片。 */ private void notifySeEmvCardRemoval() { mListener.onSeEmvCardRemoval();// NfcService如何處理它呢 } // 用于通知MIFARE SMX Emulation被外部設備訪問。該函數只適用于NXP公司的相關芯片 private void notifySeMifareAccess(byte[] block) { mListener.onSeMifareAccess(block); } ~~~ NfcService是如何處理這些回調通知的呢?以notifySeEmvCardRemoval中的onSeEmvCardRemoval為例,NfcService將發送一個MSG_SE_EMV_CARD_REMOVAL消息,而這個消息的處理函數代碼如下所示。 **NfcService.java::NfcServiceHandler:handleMessage** ~~~ ...... case MSG_SE_EMV_CARD_REMOVAL:  // CE相關的消息全是類型的處理方式 Intent cardRemovalIntent = new Intent(); cardRemovalIntent.setAction(ACTION_EMV_CARD_REMOVAL); sendSeBroadcast(cardRemovalIntent); // 發送廣播 ...... ~~~ 由上述代碼的注釋可知,NfcService對CE相關的處理非常簡單,就是接收來自底層的通知事件,然后將其轉化為廣播事件發送給系統中感興趣的應用程序。 **5、Android中的NFC總結** 本節介紹了Android平臺中NFC系統模塊NfcService及其他重要組件。從整體上來說,NfcService以及與底層芯片無關的模塊難度不大,而與底層芯片相關的模塊則難度較大(位于com.android.nfc.dhimpl包中)。本章沒有討論dhimpl包的具體代碼,希望感興趣的讀者能結合芯片手冊自行研究它們。 另外,本節還對NFC R/W模式及P2P模塊的工作流程進行了相關介紹,這部分難度不大,相信讀者能輕松掌握。同時,作為課后作業,請讀者在本節基礎上自行學習Handover相關的處理流程。 最后,本節簡單介紹了NFC CE模式的處理流程,NfcService本身對CE相關的處理比較簡單,它僅根據CE相關的操作向系統發送不同的廣播,而這些廣播則會由感興趣的應用程序來處理,例如Google Wallet。 >[info] 提示 NFC CE模式其實內容相當復雜,涉及很多規范。筆者會在博客上繼續介紹NFC CE相關的知識,敬請讀者關注。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看