现在的位置: 首页 > 综合 > 正文

Android NFC P2P学习2 – Service层

2018年04月16日 ⁄ 综合 ⁄ 共 8385字 ⁄ 字号 评论关闭

上次我们讲到了NfcActivityManager对象,其实还有重要的一点没有说,就是NfcActivityManager其实继承了INdefPushCallback.Stub,也就是说对于NfcService来说,它是一个Stub,它们之间可以通过进程间通信。这里有个细节,虽然说与NFC P2P推送相关的有两个回调接口:NfcAdapter.CreateNdefMessageCallback和NfcAdapter.OnNdefPushCompleteCallback,但是回顾上节,和NfcService通信时,并不是将这两个回调接口传递过去(它们也不是Stub),而是将NfcActivityManager自己传递给NfcService,见代码:

    /**
     * Register NfcActivityState with the NFC service.
     */
    synchronized void updateNfcService(NfcActivityState state) {
        boolean serviceCallbackNeeded = state.ndefMessageCallback != null ||
                state.onNdefPushCompleteCallback != null;

        try {
            NfcAdapter.sService.setForegroundNdefPush(state.resumed ? state.ndefMessage : null,
                    state.resumed && serviceCallbackNeeded ? this : null);
        } catch (RemoteException e) {
            mAdapter.attemptDeadServiceRecovery(e);
        }
    }

好了,进入Nfc服务层,我们来看一下P2pLinkManager这个类。

1、该类是NfcService类的成员变量,在NfcService.onCreate()时,被创建。
 

    @Override
    public void onCreate() {
        super.onCreate();

        mNfcTagService = new TagService();
        mNfcAdapter = new NfcAdapterService();
        mExtrasService = new NfcAdapterExtrasService();

        Log.i(TAG, "Starting NFC service");

        sService = this;

        mContext = this;
        mDeviceHost = new NativeNfcManager(this, this);

        mP2pLinkManager = new P2pLinkManager(mContext);

2、P2pLinkManager提供打开NFC P2P数据推送及接收的方法,第一个参数是是否数据推送,第二个参数是是否接受数据。

mP2pLinkManager.enableDisable(true, true);

3、P2pLinkManager提供了推送Ndef消息的接口,我们跟随上节的流程,从这个入口切入进行学习:

    /**
     * Set NDEF message or callback for sending.
     * May be called from any thread.
     * NDEF messages or callbacks may be set at any time (even if NFC is
     * currently off or P2P send is currently off). They will become
     * active as soon as P2P send is enabled.
     */
    public void setNdefToSend(NdefMessage staticNdef, INdefPushCallback callbackNdef) {
        synchronized (this) {
            mStaticNdef = staticNdef;
            mCallbackNdef = callbackNdef;
        }
    }

上述函数非常简单,将要推送的NdefMessage以及回调的接口保存到P2pLinkManager的成员变量中。

接下去的流程是这样的,

如果设置中打开了NFC P2P,那么底层PN544芯片将对P2P设备进场进行查询。当一个P2P设备发现另一个P2P设备进场(进入NFC感应区域中)时,由底层通知NativeNfcManager,“侦测到P2P设备”这个消息:

    /**
     * Notifies P2P Device detected, to activate LLCP link
     */
    private void notifyLlcpLinkActivation(NativeP2pDevice device) {
        mListener.onLlcpLinkActivated(device);
    }

NativeNfcManager将通知已注册进其内部成员变量的DeviceHostListener(设备监听器),这里DeviceHostListener由NfcService实现。

NfcService收到消息后如下处理:

    /**
     * Notifies P2P Device detected, to activate LLCP link
     */
    @Override
    public void onLlcpLinkActivated(NfcDepEndpoint device) {
        sendMessage(NfcService.MSG_LLCP_LINK_ACTIVATION, device);
    }

简单的一句话,那么我们再来看看sendMessage这个方法中究竟做了什么?

    void sendMessage(int what, Object obj) {
        Message msg = mHandler.obtainMessage();
        msg.what = what;
        msg.obj = obj;
        mHandler.sendMessage(msg);
    }

可以看到这个LLCP链路激活的消息被发送出去,这里Handler,Looper,MessageQueue细节不讲,可自行查GOOGLE。

NfcService有一个成员变量,是NfcServiceHandler对象,该对象将处理刚才看到的那个链路激活的消息(当然链路停活消息也是它处理的),代码如下:

                case MSG_LLCP_LINK_ACTIVATION:
                    llcpActivated((NfcDepEndpoint) msg.obj);
                    break;

                case MSG_LLCP_LINK_DEACTIVATED:
                    NfcDepEndpoint device = (NfcDepEndpoint) msg.obj;
                    boolean needsDisconnect = false;

                    Log.d(TAG, "LLCP Link Deactivated message. Restart polling loop.");
                    synchronized (NfcService.this) {
                        /* Check if the device has been already unregistered */
                        if (mObjectMap.remove(device.getHandle()) != null) {
                            /* Disconnect if we are initiator */
                            if (device.getMode() == NfcDepEndpoint.MODE_P2P_TARGET) {
                                if (DBG) Log.d(TAG, "disconnecting from target");
                                needsDisconnect = true;
                            } else {
                                if (DBG) Log.d(TAG, "not disconnecting from initiator");
                            }
                        }
                    }
                    if (needsDisconnect) {
                        device.disconnect();  // restarts polling loop
                    }

                    mP2pLinkManager.onLlcpDeactivated();
                    break;

NfcDepEndpoint代表着一个P2P设备对象。

查看NfcService的llcpActivated方法:

        private boolean llcpActivated(NfcDepEndpoint device) {
            Log.d(TAG, "LLCP Activation message");

            if (device.getMode() == NfcDepEndpoint.MODE_P2P_TARGET) {
                if (DBG) Log.d(TAG, "NativeP2pDevice.MODE_P2P_TARGET");
                if (device.connect()) {
                    /* Check LLCP compliancy */
                    if (mDeviceHost.doCheckLlcp()) {
                        /* Activate LLCP Link */
                        if (mDeviceHost.doActivateLlcp()) {
                            if (DBG) Log.d(TAG, "Initiator Activate LLCP OK");
                            synchronized (NfcService.this) {
                                // Register P2P device
                                mObjectMap.put(device.getHandle(), device);
                            }
                            mP2pLinkManager.onLlcpActivated();
                            return true;
                        } else {
                            /* should not happen */
                            Log.w(TAG, "Initiator LLCP activation failed. Disconnect.");
                            device.disconnect();
                        }
                    } else {
                        if (DBG) Log.d(TAG, "Remote Target does not support LLCP. Disconnect.");
                        device.disconnect();
                    }
                } else {
                    if (DBG) Log.d(TAG, "Cannot connect remote Target. Polling loop restarted.");
                    /*
                     * The polling loop should have been restarted in failing
                     * doConnect
                     */
                }
            } else if (device.getMode() == NfcDepEndpoint.MODE_P2P_INITIATOR) {
                if (DBG) Log.d(TAG, "NativeP2pDevice.MODE_P2P_INITIATOR");
                /* Check LLCP compliancy */
                if (mDeviceHost.doCheckLlcp()) {
                    /* Activate LLCP Link */
                    if (mDeviceHost.doActivateLlcp()) {
                        if (DBG) Log.d(TAG, "Target Activate LLCP OK");
                        synchronized (NfcService.this) {
                            // Register P2P device
                            mObjectMap.put(device.getHandle(), device);
                        }
                        mP2pLinkManager.onLlcpActivated();
                        return true;
                    }
                } else {
                    Log.w(TAG, "checkLlcp failed");
                }
            }

            return false;
        }

内部有P2P主动模式及被动模式的判断,我们先忽略这些繁琐的细节,直接查看mP2pLinkManager.onLlcpActivated();

    public void onLlcpActivated() {
        Log.i(TAG, "LLCP activated");

        synchronized (P2pLinkManager.this) {
            switch (mLinkState) {
                case LINK_STATE_DOWN:
                    mLinkState = LINK_STATE_UP;
                    mSendState = SEND_STATE_NOTHING_TO_SEND;
                    if (DBG) Log.d(TAG, "onP2pInRange()");
                    mEventListener.onP2pInRange();

                    prepareMessageToSend();
                    if (mMessageToSend != null) {
                        mSendState = SEND_STATE_NEED_CONFIRMATION;
                        if (DBG) Log.d(TAG, "onP2pSendConfirmationRequested()");
                        mEventListener.onP2pSendConfirmationRequested();
                    }
                    break;

我们看到,

1、会准备要发送的数据,prepareMessageToSend();

2、这里会要求用户在手机屏幕上按键以确认。

先来看下准备发送数据的内容:

    void prepareMessageToSend() {
        synchronized (P2pLinkManager.this) {
            if (!mIsSendEnabled) {
                mMessageToSend = null;
                return;
            }

            NdefMessage messageToSend = mStaticNdef;
            INdefPushCallback callback = mCallbackNdef;

            if (callback != null) {
                try {
                    messageToSend = callback.createMessage();
                } catch (RemoteException e) {
                    // Ignore
                }
            }

            if (messageToSend == null) {
                messageToSend = createDefaultNdef();
            }
            mMessageToSend = messageToSend;
        }
    }

若回调接口不为null,则利用回调接口动态创建发送的NdefMessage,这里会调用到API层的CreateNdefMessageCallback实现对象。若创建出的消息为null,则创建默认发送的Ndef消息,如此一来便准备完毕。

我们再来看第二个内容,P2pEventManager,它实现了P2pEventListener,onLlcpActivated方法中,调用了两个方法:

1、mEventListener.onP2pInRange();

2、mEventListener.onP2pSendConfirmationRequested();

先看onP2pInRange()。

    @Override
    public void onP2pInRange() {
        mNfcService.playSound(NfcService.SOUND_START);
        mNdefSent = false;
        mNdefReceived = false;

        mVibrator.vibrate(VIBRATION_PATTERN, -1);
        mSendUi.takeScreenshot();
    }

这里将发出推送的声音,设置状态位,发出震动,并创建截屏。

    public void takeScreenshot() {
        mScreenshotBitmap = createScreenshot();
    }

再看onP2pSendConfirmationRequested()。

    @Override
    public void onP2pSendConfirmationRequested() {
        mSendUi.showPreSend();
    }

这里将预发送动画显示给手机用户,其代码非P2P核心,故省略。

这个时候,手机需要用户按键确认,当用户按键确认后,系统回调P2pLinkManager的onP2pSendConfirmed()方法。

    @Override
    public void onP2pSendConfirmed() {
        if (DBG) Log.d(TAG, "onP2pSendConfirmed()");
        synchronized (this) {
            if (mLinkState == LINK_STATE_DOWN || mSendState != SEND_STATE_NEED_CONFIRMATION) {
                return;
            }
            mSendState = SEND_STATE_SENDING;
            if (mLinkState == LINK_STATE_UP) {
                sendNdefMessage();
            }
        }
    }

终于到了真正发送数据的流程了,我们来看一看sendNdefMessage()方法。

    void sendNdefMessage() {
        synchronized (this) {
            cancelSendNdefMessage();
            mSendTask = new SendTask();
            mSendTask.execute();
        }
    }

这里发现是调用一个AsyncTask异步任务来做数据发送的。由于这是一个P2pLinkManager的内部类,所以可以方便的得到外部类的成员变量,即待发送的数据:

    final class SendTask extends AsyncTask<Void, Void, Void> {
        @Override
        public Void doInBackground(Void... args) {
            NdefMessage m;
            boolean result;

            synchronized (P2pLinkManager.this) {
                if (mLinkState != LINK_STATE_UP || mSendState != SEND_STATE_SENDING) {
                    return null;
                }
                m = mMessageToSend;
            }

            long time = SystemClock.elapsedRealtime();
            try {
                if (DBG) Log.d(TAG, "Sending ndef via SNEP");
                result = doSnepProtocol(m);
            } catch (IOException e) {
                Log.i(TAG, "Failed to connect over SNEP, trying NPP");

                if (isCancelled()) {
                    return null;
                }

                result = new NdefPushClient().push(m);
            }
            time = SystemClock.elapsedRealtime() - time;

            if (DBG) Log.d(TAG, "SendTask result=" + result + ", time ms=" + time);

            if (result) {
                onSendComplete(m, time);
            }
            return null;
        }
    }

我们看见了这里的doSnepProtocol,SNEP,简单NDEF交换协议,这里我引用一段Android的描述:

For NDEF push to function properly the other NFC device must support either NFC Forum's SNEP (Simple Ndef Exchange Protocol), or Android's "com.android.npp" (Ndef Push Protocol). This was optional on Gingerbread level Android NFC devices, but SNEP is mandatory on Ice-Cream-Sandwich and beyond. 

可见在Ice-Cream-Sandwith后强制使用SNEP了。该协议在第三节中详细介绍:)

如果发生异常,再尝试使用NPP协议推送数据。

完成数据推送后发送“推送完毕”通知。

同样是P2pLinkManager实现Handler.Callback来处理该消息:

            case MSG_SEND_COMPLETE:
                synchronized (P2pLinkManager.this) {
                    mSendTask = null;

                    if (mLinkState == LINK_STATE_DOWN || mSendState != SEND_STATE_SENDING) {
                        break;
                    }
                    mSendState = SEND_STATE_NOTHING_TO_SEND;
                    if (DBG) Log.d(TAG, "onP2pSendComplete()");
                    mEventListener.onP2pSendComplete();
                    if (mCallbackNdef != null) {
                        try {
                            mCallbackNdef.onNdefPushComplete();
                        } catch (RemoteException e) { }
                    }
                    mSendTask = null;
                }
                break;

上述函数中做了:

1、向事件监听器发送该消息,mEventListener.onP2pSendComplete();

2、向API层发送该消息,mCallbackNdef.onNdefPushComplete();

至此,一个Activity发送P2P消息的流程完成了,下节我们再来说SNEP等底层细节。

 

 

 

 

 

抱歉!评论已关闭.