NFC--P2P模式 java层源码码流程解析。

来源:互联网 发布:网站源码基地 编辑:程序博客网 时间:2024/06/05 15:37
           文章只对P2P模式的java层的代码进行了学习,且只在后续一步步深入NFC相关知识后会找时间更新,等学完NFC java层
架,熟悉C/C++以后,再分析一下native层的构架.
           直接从P2P模式的JNI层回调开始分析,(不清楚native层的逻辑,暂且记住回调接口)
           当有可被用作P2P模式的NFC设备被发现后,经过底层的一系列处理传输到JNI然后回调到:NativeNfcManager当中的
onLlcpLinkActivated().所有和native层交互的基本都在这个类中实现的。
  1. /**
  2. * Notifies P2P Device detected, to activate LLCP link
  3. */
  4. private void notifyLlcpLinkActivation(NativeP2pDevice device) {
  5. mListener.onLlcpLinkActivated(device);
  6. }
          mListener是DeviceHostListener由NfcService实现的,进入onLlcpLinkActivated()
开始java层的处理,native层感应到了P2P设备,java层需要解析一下,看看是什么格式,做什么操作等。
  1. /**
  2. * Notifies P2P Device detected, to activate LLCP link
  3. * NfcDepEndpoint:代表了一个远程的P2p设备,可以是target也可以是initiator.
  4. * 看native处理完返回来的是什么了。
  5. */
  6. @Override
  7. public void onLlcpLinkDeactivated(NfcDepEndpoint device) {
  8. sendMessage(NfcService.MSG_LLCP_LINK_DEACTIVATED, device);
  9. }
  10. void sendMessage(int what, Object obj) {
  11. Message msg = mHandler.obtainMessage();
  12. msg.what = what;
  13. msg.obj = obj;
  14. mHandler.sendMessage(msg);
  15. }
          对应到mHandler中的MSG_LLCP_LINK_DEACTIVATED.
  1. ......
  2. case MSG_LLCP_LINK_ACTIVATION:
  3. ......
  4. //主要就是回调下面,底层llcp链路建立完毕的回调。
  5. llcpActivated((NfcDepEndpoint) msg.obj);
  6. break;
  7. ......
         此处先说一下远程的P2P设备会有两种模式:
                 一种是Target:当前手机往外发送数据的时候,远程的设备就处于Target模式.
                 一种是Initiator:当前手机接受数据的时候,远程的设备就会处于Initiator模式.
         在上层就体现在NfcDepEndpoint.getMode()的不同,注意区分和传输数据是Socket的区分,这个下面再说.并且他们的基本
流程都一致,差异点是在Target端会先执行connect(),调用到NativeP2pDevice中的native方法doConnect()进行数据链路层的连接
保证底层已经是发现对方并且是可以连接的。
         再说两个native方法,位于NativeNfcManager当中,方便我们下面的分析.
  1. @Override
  2. public native boolean doCheckLlcp();检查llcp协议的合法性,如版本是否兼容。
  3. @Override
  4. public native boolean doActivateLlcp();执行链路,就是执行native层的llcp协议的连接
          接下来就是llcpActivated
  1. private boolean llcpActivated(NfcDepEndpoint device) {
  2. Log.d(TAG, "LLCP Activation message");
  3. //当远程p2p是Target端的时候,一定要注意,此时的模式指的是远端的用于和当前设备进行P2P操作的设备。
  4. if (device.getMode() == NfcDepEndpoint.MODE_P2P_TARGET) {
  5. if (device.connect()) {
  6. /* Check LLCP compliance */
  7. if (mDeviceHost.doCheckLlcp()) {
  8. /* Activate LLCP Link */
  9. if (mDeviceHost.doActivateLlcp()) {
  10. synchronized (NfcService.this) {
  11. //当放到集合当中
  12. mObjectMap.put(device.getHandle(), device);
  13. }
  14. //底层按照llcp协议建立完链路连接的时候调用如下方法。
  15. mP2pLinkManager.onLlcpActivated(device.getLlcpVersion());
  16. return true;
  17. } else {device.disconnect();//断开链接}
  18. } else {//断开连接打印相关log}
  19. } else {//不能连接远程的target,然后去执行发送数据.}
  20. //当是远程的P2P是发起者initiator的时候.
  21. } else if (device.getMode() == NfcDepEndpoint.MODE_P2P_INITIATOR) {
  22. //可以看到对比MODE_P2P_TARGET就是少了一个device.connect().
  23. /* Check LLCP compliancy */
  24. if (mDeviceHost.doCheckLlcp()) {
  25. /* Activate LLCP Link */
  26. if (mDeviceHost.doActivateLlcp()) {
  27. if (DBG) Log.d(TAG, "Target Activate LLCP OK");
  28. synchronized (NfcService.this) {
  29. // Register P2P device
  30. mObjectMap.put(device.getHandle(), device);
  31. }
  32. mP2pLinkManager.onLlcpActivated(device.getLlcpVersion());
  33. return true;
  34. }
  35. } else { Log.w(TAG, "checkLlcp failed");}
  36. }
  37. return false;
  38. }

  1. //这个集合中放置了所有的P2p链接的device
  2. final HashMap<Integer, Object> mObjectMap = new HashMap<Integer, Object>();
  3. key = device.getHandle() value = NfcDepEndpoint
         另外native层执行完链路连接以后会回调很多状态给上层,在P2pLinkManager类当中有如下三个回调:
  1. public void onLlcpFirstPacketReceived()
  2. public void onLlcpDeactivated()
  3. void onSendComplete(NdefMessage msg, long elapsedRealtime)
        无远端是Target还是initaitor,最后都调用了,P2pLinkManager中的onLlcpActivated。至此,native层的llcp链路建立成功并
且也通过了check,我们接下来只需要按照传输协议建立socket传递数据即可!
    进入到P2PLinkManager当中
  1. public void onLlcpActivated(byte peerLlcpVersion) {
  2. Log.i(TAG, "LLCP activated");
  3. synchronized (P2pLinkManager.this) {
  4. //一开始有一快是测试nfcpy用的服务器,不太清楚这个,先省略.
  5. ......
  6. mLastLlcpActivationTime = SystemClock.elapsedRealtime();
  7. mPeerLlcpVersion = peerLlcpVersion;
  8. //分别对不同的链接状态进行处理,它是在P2pLinkManager的构造中初始化为LINK_STATE_DOWN.
  9. switch (mLinkState) {
  10. //所以刚开始的时候是进入到这里。
  11. case LINK_STATE_DOWN:
  12. //加上了一些判断,保证现在是要进行发送数据的状态。
  13. //NFC的整个P2P的流程中会有很多的类似的状态判断,因为它的链接时刻都可能终端,毕竟要我们
  14. //拿着手机相互靠近,一不小心就离开了
  15. if (!mEventListener.isP2pIdle()
  16. && mSendState != SEND_STATE_PENDING) {
  17. break;
  18. }
  19. if (DBG) Log.d(TAG, "onP2pInRange()");
  20. //这个方法内就会得到一个当前屏幕截图的bitmap对象,但在这块还不会弹出提示的界面.
  21. mEventListener.onP2pInRange();
  22. //更改链接状态。
  23. mLinkState = LINK_STATE_UP;
  24. //mSendState状态在构造中初始化,为SEND_STATE_NOTHING_TO_SEND.所以走到else
  25. if (mSendState == SEND_STATE_PENDING) {
  26. ......
  27. } else {
  28. mSendState = SEND_STATE_NOTHING_TO_SEND;
  29. //按照app传入的各个要发送的数据进行
  30. prepareMessageToSend(true);
  31. //判断是否有数据要往外发,有数据往外发的时候才会进入。
  32. //当设备作为服务端Socket接收数据的时候,判断条件无法通过,接下来也不会进行什么操作,
  33. //而是会等待对应的SocketServer阻塞处的相应。
  34. if (mMessageToSend != null ||
  35. (mUrisToSend != null && mHandoverDataParser.isHandoverSupported())) {
  36. // We have data to send, connect LLCP services
  37. connectLlcpServices();
  38. //在上面connectLlcpServices还未异步执行完毕的时候,这块肯定就赋值了
  39. if ((mSendFlags & NfcAdapter.FLAG_NDEF_PUSH_NO_CONFIRM) != 0) {
  40. //标记不需要确认直接发送
  41. mSendState = SEND_STATE_SENDING;
  42. } else {
  43. //标记需要在UI上确认的才能P2p
  44. mSendState = SEND_STATE_NEED_CONFIRMATION;
  45. }
  46. }
  47. }
  48. break;
  49. ......
  50. }
  51. }
  52. }
          我们一步步看onLlcpActivated()中的重要的方法。
          mEventListener.onP2pInRange(); mEventListener是P2pEventListener.
  1. //进行P2P链接的时候回调事件用来更新UI界面.
  2. interface P2pEventListener {
  3. ......
  4. //Indicates a P2P device is in range.
  5. public void onP2pInRange();
  6. ......
  7. }
          而P2pEventManager实现了P2pEventListener这个接口,所以进入P2pEventManager中
  1. @Override
  2. public void onP2pInRange() {
  3. mNdefSent = false;
  4. mNdefReceived = false;
  5. mInDebounce = false;
  6. //未锁屏状态时,发送声音,震动.
  7. if (mNfcService.mScreenState == ScreenStateHelper.SCREEN_STATE_ON_UNLOCKED) {
  8. mNfcService.playSound(NfcService.SOUND_START);
  9. mVibrator.vibrate(VIBRATION_PATTERN, -1);
  10. }
  11. if (mSendUi != null) {
  12. //SendUi就是P2P过程中负责和见面交互的类。
  13. mSendUi.takeScreenshot();
  14. }
  15. }
         SendUI的实例化在NfcService的启动流程中,实例化P2pEventManager的时候,构造中会调用mSendUi = new SendUi(cont
ext,this),this的类型是SendUi.Callback也可以看到:P2pEventManager implements P2pEventListener, SendUi.Callback。
          此处有个重要的点,mCallback定义在SendUi当中。
    注意命名此处就是SendUi的完成发送和取消发送的回调
  1. public interface Callback {
  2. public void onSendConfirmed();//确认发送
  3. public void onCanceled();//取消发送
  4. }
  5. mCallback是在SendUi实例化的时候传入的,
  6. public SendUi(Context context, Callback callback) {
  7. mContext = context;
  8. mCallback = callback;
  9. ......
  10. }
          也就可知实现是在P2pEventManager当中,后面我们用到的时候直接去P2pEventManager中找实现.
         接着到SenUi中:
  1. public void takeScreenshot() {
  2. ...
  3. //更新当前状态
  4. mState = STATE_W4_SCREENSHOT;
  5. //去异步的执行画界面的操作.
  6. new ScreenshotTask().execute();
  7. }
          ScreenshotTask是SendUi的内部类Task当中:
  1. protected Bitmap doInBackground(Void... params) {
  2. return createScreenshot();
  3. }
  4. /**
  5. * 此处旨在理解流程,具体的画图算大小省略,这个方法会返回当前界面内容的一个bitmap对象.
  6. */
  7. Bitmap createScreenshot() {
  8. ......
  9. //根据不屏幕进行适配
  10. final int navBarHeight = ...;
  11. final int navBarHeightLandscape = ...;
  12. final int navBarWidth = ...;
  13. ......
  14. Bitmap bitmap = SurfaceControl.screenshot((int) dims[0], (int) dims[1]);
  15. ......
  16. int newLeft = ...;
  17. int newTop = ...;
  18. int newWidth = ...;
  19. int newHeight = ...;
  20. float smallestWidth = ...;
  21. float smallestWidthDp = ...;
  22. if (bitmap.getWidth() < bitmap.getHeight()) {
  23. newHeight = ...;
  24. } else {
  25. if (smallestWidthDp > 599) {
  26. newHeight = ...;
  27. } else {
  28. newHeight = ...;
  29. newWidth = ...;
  30. }
  31. }
  32. bitmap = Bitmap.createBitmap(bitmap, newLeft, newTop, newWidth, newHeight);
  33. return bitmap;
  34. }
  35. //执行完doInBackground以后到onPostExecute当中.
  36. @Override
  37. protected void onPostExecute(Bitmap result) {
  38. //mState在前面我们设置的是STATE_W4_SCREENSHOT,不同的状态做出不同的处理
  39. if (mState == STATE_W4_SCREENSHOT) {/
  40. //生成的截图设置到mScreenshotBitmap,这个在后面显示用户确认操作时候用.
  41. mScreenshotBitmap = result;
  42. //并且把mState变成将要发送的状态
  43. mState = STATE_W4_PRESEND;
  44. } else if (mState == STATE_W4_SCREENSHOT_THEN_STOP) {
  45. ......
  46. } else if (mState == STATE_W4_SCREENSHOT_PRESEND_REQUESTED ||
  47. mState == STATE_W4_SCREENSHOT_PRESEND_NFC_TAP_REQUESTED) {
  48. ......
  49. } else {
  50. Log.e(TAG, "Invalid state on screenshot completion: " + Integer.toString(mState));
  51. }
  52. }
         到此其实也只是获取到了一个用屏幕的截图绘制的bitmap,把他设置给了mScreenshotBitmap,并不会显示什么界面,然后
回到P2PLinkManager中的onLlcpActivated 接这前面的往下看先是:LinkState = LINK_STATE_UP;而对于mSendState的状态,在
构造中初始化的时候为SEND_STATE_NOTHING_TO_SEND,所以会走到else的分支。
  1. //依据APP设置的信息,准备发送的信息.
  2. //主要是把最终解析出来的数据放到mMessageToSend和mUrisToSend中
  3. //ndef的小数据一般用mMessageToSend存储,如:Contact、Email、等
  4. //picture、video等一般会放在mUrisToSend中
  5. void prepareMessageToSend(boolean generatePlayLink) {
  6. synchronized (P2pLinkManager.this) {
  7. //NdefMessage mMessageToSend;
  8. mMessageToSend = null;
  9. //Uri[] mUrisToSend
  10. mUrisToSend = null;
  11. //由在构造中的时候默认是false,但是在NfcService一系列附加的行为启动完毕后
  12. //调用到P2pLinkManager中enableDisable,会把它赋值为true.
  13. if (!mIsSendEnabled) {
  14. return;
  15. }
  16. //此处有一些判断,如是否检测到如前台程序Beam是否使能。
  17. //前台程序就是指,你有没有打开比如联系人、图库等注意是可分享的activity界面。
  18. //你打开后,选中某个图片,一轻触就开始走流程了,否则的话没啥反应.
  19. if (foregroundUids.isEmpty()) {
  20. Log.e(TAG, "Could not determine foreground UID.");
  21. return;
  22. }
  23. if (isBeamDisabled(foregroundUids.get(0))) {
  24. if (DBG) Log.d(TAG, "Beam is disabled by policy.");
  25. return;
  26. }
  27. ...
  28. //mCallbackNdef是IAppCallback接口的实现,通过调用接口setNdefCallback,进行设置的
  29. //一般无论是第三方app还是系统的联系人、图库等,都会设置这个回调
  30. if (mCallbackNdef != null) {
  31. //如果是自己添加的前台进程的话,走下面的流程
  32. if (foregroundUids.contains(mNdefCallbackUid)) {
  33. try {
  34. //createBeamShareData,在framework中有实现,在NfcActivityManager中实现
  35. //此处还需要深入的再看看怎么确定beam的数据的,(??????)
  36. //通过Log发现:
  37. // 图片资源的时候 mMessageToSend = null、
  38. // mUrisToSend = [Landroid.net.Uri;@473547e
  39. // mUserHandle = UserHandle{0}
  40. // mSendFlags = 0
  41. //*******************************
  42. // 联系人资源的时候 mMessageToSend = NdefMessage
  43. // [NdefRecord tnf=2 type=746578742F782D7663617264
  44. // payload=424547494E3A56434152
  45. // 440D0A56455253494F4E3A322E310D0A4E3A3B5A686
  46. // 16F7975616E3B3B3B0D0A464E3A5A68616F7975616E
  47. // 200D0A54454C3B43454C4C3A3133363938353233363
  48. // 9380D0A454E443A56434152440D0A]
  49. // mUrisToSend = null.
  50. // mUserHandle = UserHandle{0}
  51. // mSendFlags = 0
  52. //总的来说是由createBeamShareData内部实现的区分!
  53. BeamShareData shareData = mCallbackNdef.createBeamShareData(mPeerLlcpVersion);
  54. mMessageToSend = shareData.ndefMessage;
  55. mUrisToSend = shareData.uris;
  56. mUserHandle = shareData.userHandle;
  57. mSendFlags = shareData.flags;
  58. return;
  59. } catch (Exception e) {...}
  60. } else {
  61. //当如图库退出前台的时候,可能它的回调并没有清除。
  62. //比如你想作为Socket接收端,就会到当前判断不会去专门打开指定的应用界面去发送东西
  63. if (DBG) Log.d(TAG, "Last registered callback is not running in the foreground.");
  64. }
  65. }
  66. //当没有设置回调,就调用系统默认的,将当前Pkg的信息打包发送到上层。
  67. List<Integer> foregroundPids =
  68. mForegroundUtils.getForegroundPids(foregroundUids.get(0));
  69. if (foregroundPids.isEmpty()) {
  70. Log.e(TAG, "Could not determine foreground PID.");
  71. return;
  72. }
  73. //找到正在运行的pkg
  74. String pkgName = null;
  75. ActivityManager manager =
  76. (ActivityManager)mContext.getSystemService(Context.ACTIVITY_SERVICE);
  77. List<RunningAppProcessInfo> appList = manager.getRunningAppProcesses();
  78. for (RunningAppProcessInfo info : appList) {
  79. for (Integer pid : foregroundPids) {
  80. if (pid.intValue() == info.pid) {
  81. if (DBG) Log.d(TAG, "PID:" + info.pid + " Name:" + info.processName);
  82. pkgName = info.processName;
  83. break;
  84. }
  85. }
  86. ...
  87. }
  88. //
  89. if (pkgName != null) {
  90. if (!generatePlayLink || beamDefaultDisabled(pkgName)) {
  91. if (DBG) Log.d(TAG, "Disabling default Beam behavior");
  92. mMessageToSend = null;
  93. mUrisToSend = null;
  94. } else {
  95. mMessageToSend = createDefaultNdef(pkgName);
  96. mUrisToSend = null;
  97. mSendFlags = 0;
  98. }
  99. }
  100. //作为接收端的时候此处就会为null,然后返回不作处理
  101. if (DBG) Log.d(TAG, "mMessageToSend = " + mMessageToSend);
  102. if (DBG) Log.d(TAG, "mUrisToSend = " + mUrisToSend);
  103. }
  104. }
         再回到P2pLinkManager中的onLlcpActivated当中,继续往下看,接下来就是connectLlcpServices要连接llcp中相关service
了.此时就有个问题是什么时候启动的server那?
          是在NfcService启动的流程中,按照协议的规定对相应的Socket进行实例化,
  1. public P2pLinkManager(Context context, HandoverDataParser handoverDataParser, int defaultMiu,
  2. int defaultRwSize) {
  3. ......
  4. mNdefPushServer = new NdefPushServer(NDEFPUSH_SAP, mNppCallback);
  5. mDefaultSnepServer = new SnepServer(mDefaultSnepCallback, defaultMiu, defaultRwSize);
  6. mHandoverServer = new HandoverServer(context, HANDOVER_SAP, handoverDataParser, mHandoverCallback);
  7. ......
  8. }
         在每个ServerSocket当中实例化完毕后会监听Socket的链接,此处以SnepServer为例简单展示部分代码
          首先在P2pLinkManager中调用mDefaultSnepServer.start();
  1. public void start() {
  2. synchronized (SnepServer.this) {
  3. if (DBG) Log.d(TAG, "start, thread = " + mServerThread);
  4. if (mServerThread == null) {
  5. if (DBG) Log.d(TAG, "starting new server thread");
  6. mServerThread = new ServerThread();
  7. mServerThread.start();
  8. mServerRunning = true;
  9. }
  10. }
  11. }
          在ServerThread的run方法中
  1. @Override
  2. public void run() {
  3. ......
  4. LlcpServerSocket mServerSocket;
  5. while (threadRunning) {
  6. synchronized (SnepServer.this) {
  7. mServerSocket = NfcService.getInstance().createLlcpServerSocket(mServiceSap,
  8. mServiceName, mMiu, mRwSize, 1024);
  9. }
  10. ......
  11. //下面就会阻塞,等待Client端发送数据,此处一定注意和NFC的target/Initiator没啥关系
  12. //这块说的Client、Server是指建立的socket。当你想往外发数据是,会弹出确认提示,谁主动点击了
  13. //屏幕,执行了确定的动作,那么就是发送端,发送端默认是Client端,因为接收端默认已经启动了Server端,
  14. //等待对方发起连接.
  15. LlcpSocket communicationSocket = serverSocket.accept();
  16. }
  17. }
         其它的serverSocket启动模式类似。简单介绍完server端socket的启动,继续回去跟踪connectLlcpServices
  1. void connectLlcpServices() {
  2. synchronized (P2pLinkManager.this) {
  3. //调用ConnectTask异步的实现。
  4. mConnectTask = new ConnectTask();
  5. mConnectTask.execute();
  6. }
  7. }
          根据传输的格式来,来实例化对应的链接如:HandoverClient、SnepClient、NdefPushClient连接成功后返回true
  1. final class ConnectTask extends AsyncTask<Void, Void, Boolean> {
  2. @Override
  3. protected Boolean doInBackground(Void... params){
  4. //初始化一些参数,用来标记使用那种协议来解析数据.
  5. boolean needsHandover = false;
  6. boolean needsNdef = false;
  7. boolean success = false;
  8. //下面就是依次初始化这几个client,对应的server端就是我们前面说的
  9. //这些客户端的socket创建也是按照llcp协议的要求来设置一些属性如访问节点等.
  10. HandoverClient handoverClient = null;
  11. SnepClient snepClient = null;
  12. NdefPushClient nppClient = null;
  13. synchronized(P2pLinkManager.this) {
  14. //进入到此处的时候就会用bt进行传输
  15. if (mUrisToSend != null) {
  16. needsHandover = true;
  17. }
  18. //日历、联系人这一类的小数据
  19. if (mMessageToSend != null) {
  20. needsNdef = true;
  21. }
  22. }
  23. if (needsHandover) {
  24. handoverClient = new HandoverClient();
  25. try {
  26. //调用connect,最终会调用到:
  27. // LlcpSocket当中connectToService(HandoverServer.HANDOVER_SERVICE_NAME);
  28. //LlcpSocket是由NativeLlcpSocket实现,代表一个llcp的面向连接中的客户端。
  29. //去连接它对应的服务端的Socket,此处疑问(???)链接的时候服务端不会响应,等到往服务
  30. //端发数据就会响应了.
  31. //service.createLlcpSocket(0, MIU, 1, 1024);
  32. handoverClient.connect();
  33. success = true; // Regardless of NDEF result
  34. } catch (IOException e) {
  35. handoverClient = null;
  36. }
  37. }
  38. //当需要用ndef发送的时候
  39. if (needsNdef || (needsHandover && handoverClient == null))
  40. {
  41. //认证测试工具DTA,当处于DTA模式下的时候的处理
  42. if(NfcService.sIsDtaMode) {
  43. ......
  44. }else{
  45. //正常模式下,实例化SnepClient。
  46. snepClient = new SnepClient();
  47. }
  48. try
  49. {
  50. if(NfcService.sIsDtaMode) {
  51. ......
  52. }
  53. else
  54. {
  55. //也是调用连接,连接到对应的服务端主要分三步
  56. // NfcService.getInstance().createLlcpSocket(0, mMiu, mRwSize, 1024)
  57. // socket.connectToService(mServiceName)/socket.connectToSap(mPort);
  58. // 依据服务名或者Port连接
  59. // new SnepMessenger(),用于接收或发送SNEP消息
  60. // 主要函数:put()/get()
  61. snepClient.connect();
  62. }
  63. success = true;
  64. mDtaSnepClient = null;
  65. } catch (IOException e) {...}
  66. //可以看到当snepClient不能成功的时候才会用NdefPushClient npp.
  67. //这个是早期没引入P2P模式的时候的选择.
  68. if (!success) {
  69. nppClient = new NdefPushClient();
  70. try {
  71. // 进行连接操作,主要分两步
  72. // service.createLlcpSocket(0, MIU, 1, 1024);
  73. // sock.connectToService(NdefPushServer.SERVICE_NAME);
  74. // 主要函数:push()/close()
  75. nppClient.connect();
  76. success = true;
  77. } catch (IOException e) {
  78. nppClient = null;
  79. }
  80. }
  81. }
  82. synchronized (P2pLinkManager.this) {
  83. //如果操作的过程中取消了连接,则将前面的client端全部关闭
  84. if (isCancelled()) {
  85. // Cancelled by onLlcpDeactivated on UI thread
  86. if (handoverClient != null) {
  87. handoverClient.close();
  88. }
  89. if (snepClient != null) {
  90. snepClient.close();
  91. }
  92. if (nppClient != null) {
  93. nppClient.close();
  94. }
  95. if(mDtaSnepClient != null) {
  96. mDtaSnepClient.close();
  97. }
  98. return false;
  99. } else {
  100. ......
  101. mHandoverClient = handoverClient;
  102. mSnepClient = snepClient;
  103. mNdefPushClient = nppClient;
  104. return success;
  105. }
  106. }
  107. }
  108. //执行完doInBackground之后,走到这里.
  109. @Override
  110. protected void onPostExecute(Boolean result) {
  111. ...
  112. //如果成功连接到服务端的Socket.
  113. if (result) {
  114. onLlcpServicesConnected();
  115. } else {
  116. Log.e(TAG, "Could not connect required NFC transports");
  117. }
  118. }
  119. }
         至此socket的客户端启动完毕,并与server端建立起了链接,但是还未往外发送数据,根据mSendFlags设计的标记,来决
定是否需要用户确认,前面分析createBeamShareData的时候发现默认好像都是0.
  1. void onLlcpServicesConnected() {
  2. synchronized (P2pLinkManager.this) {
  3. //先检查连接状态
  4. if (mLinkState != LINK_STATE_UP) {
  5. return;
  6. }
  7. mLlcpServicesConnected = true;
  8. //mSendState在前面的onLlcpActivated,异步设置了
  9. if (mSendState == SEND_STATE_NEED_CONFIRMATION) {
  10. //需要用户手动确认的,
  11. if (DBG) Log.d(TAG, "onP2pSendConfirmationRequested()");
  12. mEventListener.onP2pSendConfirmationRequested();
  13. } else if (mSendState == SEND_STATE_SENDING) {
  14. //不需要确认直接发送
  15. mEventListener.onP2pResumeSend();
  16. sendNdefMessage();
  17. } else {
  18. // Either nothing to send or canceled/complete, ignore
  19. }
  20. }
  21. }
          注意此时还未显示出来用户提示的界面,(缩小屏幕,提示你轻触使用beam进行传送)。
         下面我们分析需要确认的情况,因为需要确认的情况包含不需要确认的情况,只不过多了个确认的步骤罢了。
         又到P2pEventManager中的onP2pSendConfirmationRequested中
  1. @Override
  2. public void onP2pSendConfirmationRequested() {
  3. mNfcService.playSound(NfcService.SOUND_START);
  4. mVibrator.vibrate(VIBRATION_PATTERN, -1);
  5. //SendUi我们前面已经说过.
  6. if (mSendUi != null) {
  7. //注意传入的参数是false
  8. mSendUi.showPreSend(false);
  9. } else {...}
  10. }
          展示准备send的画面
  1. public void showPreSend(boolean promptToNfcTap) {
  2. ......
  3. //mScreenshotLayout是个View对象用来承载预先显示的这个画面,里面会放入一系列的组件
  4. mScreenshotLayout.setOnTouchListener(this);
  5. //mScreenshotBitmap这就是前面在范围内的时候onP2pInRange截图生成的bitmap,
  6. mScreenshotView.setImageBitmap(mScreenshotBitmap);
  7. ......
  8. //由于promptToNfcTap是false,所以STATE_W4_TOUCH.
  9. //接下来你点击屏幕的时候(中间没有异常情况)
  10. mState = promptToNfcTap ? STATE_W4_NFC_TAP : STATE_W4_TOUCH;
  11. }
         到现在为止才会显示出来整体的用户提示界面如下图,
原图:打算往外发的

 检测到远程的P2P设备时候:

点击屏幕以后:

           之后按照提示,如果你此时两个P2P设备已经远离,再把设备靠近即可完成发送。如果不清楚这些流程,需要自己找手机试试。
           一旦用户点中间的界面,那么就会触发onTouch事件。
  1. @Override
  2. public boolean onTouch(View v, MotionEvent event) {
  3. //由于第一次到这执行完showPreSend后mState = STATE_W4_TOUCH。
  4. if (mState != STATE_W4_TOUCH) {
  5. return false;
  6. }
  7. ......
  8. //调用onSendConfirmed去弹出确认提示框
  9. mCallback.onSendConfirmed();
  10. return true;
  11. }
          此时是在SendUi当中,前面我们也说了它内部的callback的实现是在P2pEventManager如下:
  1. @Override
  2. public void onSendConfirmed() {
  3. if (!mSending) {
  4. if (mSendUi != null) {
  5. //调用下面这个界面变化成指定的传输提示动画。 图!
  6. mSendUi.showStartSend();
  7. }
  8. //此时的mCallback是在P2pEventListener声明,注意和SendUI中的区别.
  9. mCallback.onP2pSendConfirmed();
  10. }
  11. mSending = true;//标记处于发送当中
  12. }
  13. @Override
  14. public void onCanceled() {
  15. mSendUi.finish(SendUi.FINISH_SCALE_UP);
  16. mCallback.onP2pCanceled();
  17. }
         此处的mCallback定义在P2pEventListener
  1. public interface Callback {
  2. public void onP2pSendConfirmed();
  3. public void onP2pCanceled();
  4. }
  5. 实现在P2pLinkManager implements P2pEventListener.Callback
  6. @Override
  7. public void onP2pSendConfirmed() {
  8. onP2pSendConfirmed(true);
  9. }
  10. private void onP2pSendConfirmed(boolean requireConfirmation) {
  11. if (DBG) Log.d(TAG, "onP2pSendConfirmed()");
  12. synchronized (this) {
  13. //在一开始的onLlcpActivated当中被
  14. //mSendState = SEND_STATE_NEED_CONFIRMATION;
  15. //mSendState = SEND_STATE_SENDING; 这两种之一
  16. //mLinkState也在前面的onLlcpActivated当中被被设置为LINK_STATE_UP(中间没异常情况时,到这不会改变状态的),
  17. if (mLinkState == LINK_STATE_DOWN || (requireConfirmation
  18. && mSendState != SEND_STATE_NEED_CONFIRMATION)) {
  19. return;
  20. }
  21. //发送状态置为正在发送.
  22. mSendState = SEND_STATE_SENDING;
  23. //通过检查链接状态,如果llcp已经连接上了,则进行Ndef消息的发送
  24. if (mLinkState == LINK_STATE_UP) {
  25. if (mLlcpServicesConnected) {
  26. sendNdefMessage();
  27. } // else, will send messages when link comes up
  28. } else if (mLinkState == LINK_STATE_DEBOUNCE) {
  29. ......
  30. }
  31. }
  32. }
  33. void sendNdefMessage() {
  34. synchronized (this) {
  35. //下面是假如正在执行一个发送任务,把它取消掉.
  36. //两个handover,第二个到来的时候会提示正忙.
  37. //还有个问题,handover协议的时候一定要用bt吗(????)
  38. cancelSendNdefMessage();
  39. mSendTask = new SendTask();
  40. mSendTask.execute();
  41. }
  42. }
         最终是通过SendTask异步执行的,进入到SendTask类当中.
  1. @Override
  2. public Void doInBackground(Void... args){
  3. NdefMessage m;
  4. Uri[] uris;
  5. UserHandle userHandle;
  6. boolean result = false;
  7. synchronized (P2pLinkManager.this) {
  8. if (mLinkState != LINK_STATE_UP || mSendState != SEND_STATE_SENDING) {
  9. return null;
  10. }
  11. //依次取得P2pEventManager设置好的变量
  12. m = mMessageToSend;
  13. uris = mUrisToSend;
  14. userHandle = mUserHandle;
  15. snepClient = mSnepClient;
  16. handoverClient = mHandoverClient;
  17. nppClient = mNdefPushClient;
  18. }
  19. long time = SystemClock.elapsedRealtime();
  20. //uri有赋值的话,就直接进入Handover的处理,还是感觉uris就代表大数据的handover
  21. //需要通过Bt来进行传输的
  22. if (uris != null) {
  23. if (DBG) Log.d(TAG, "Trying handover request");
  24. try {
  25. int handoverResult = doHandover(uris, userHandle);
  26. switch (handoverResult) {
  27. case HANDOVER_SUCCESS:
  28. result = true;
  29. break;
  30. ...
  31. }
  32. } catch (IOException e) {
  33. result = false;
  34. }
  35. }
  36. //result为false且前面message有赋值的话,进入NDEF消息的处理
  37. if (!result && m != null && snepClient != null) {
  38. if (DBG) Log.d(TAG, "Sending ndef via SNEP");
  39. try {
  40. int snepResult = doSnepProtocol(m);
  41. switch (snepResult) {
  42. case SNEP_SUCCESS:
  43. result = true;
  44. break;
  45. ......
  46. }
  47. } catch (IOException e) {...}
  48. }
  49. //若不支持Snep格式,则用NPP方式推送
  50. if (!result && m != null && nppClient != null) {
  51. result = nppClient.push(m);
  52. }
  53. time = SystemClock.elapsedRealtime() - time;
  54. if (DBG) Log.d(TAG, "SendTask result=" + result + ", time ms=" + time);
  55. if (result) {
  56. //发送成功后的回调,最终发送MSG_SEND_COMPLETE消息.
  57. onSendComplete(m, time);
  58. }
  59. return null;
  60. }
        接下来依次介绍doHandover(uris)、doSnepProtocol(m)、nppClient.push(m)、onSendComplete(m, time)。
网上资料对doHandover的总结:
    doHandover():在AOSP中主要是生成BT 的NDEF Message,主要流程是先产生NDEF Record,然后将Record组成MSG,
后调用socket的send(),等send() 调用完成,在while循环中等待Handover的Response。如果对方不支持Handover时,就会尝试
调用SNEP的处理函数,处理完成之后,开始解析Response数据,然后将解析的数据存放在Intent中,然后发送广播消息,启动
对事件关心的BT activity。
         注意doHandover是位于SendTask类内的,详细的实现没有看(???)因为没看懂协议,这边肯定也是按照协议写的.
  1. int doHandover(Uri[] uris, UserHandle userHandle) throws IOException {
  2. NdefMessage response = null;
  3. //Handover就是beam的模式,
  4. BeamManager beamManager = BeamManager.getInstance();
  5. //如果有一个正在进行的handover,那么会提示正忙
  6. if (beamManager.isBeamInProgress()) {
  7. return HANDOVER_BUSY;
  8. }
  9. //创建handover支持的数据格式
  10. NdefMessage request = mHandoverDataParser.createHandoverRequestMessage();
  11. if (request != null) {
  12. if (handoverClient != null) {
  13. //下面内部会调用到LlcpSocket的sock.send(tmpBuffer).
  14. //它会作为socket的client端,就是客户端。(既然两个手机都开启了服务端,不会冲突到本地吗???)
  15. //最终会调用到native的对应的方法.
  16. response = handoverClient.sendHandoverRequest(request);
  17. }
  18. //当是null的时候远程不支持handover,使用snepClient进行发送
  19. //snepClient是android4.1以后加入的。会发送一个get请求???不是put
  20. if (response == null && snepClient != null) {
  21. // Remote device may not support handover service,
  22. SnepMessage snepResponse = snepClient.get(request);
  23. response = snepResponse.getNdefMessage();
  24. }
  25. if (response == null) {
  26. return HANDOVER_UNSUPPORTED;
  27. }
  28. } else {
  29. return HANDOVER_UNSUPPORTED;
  30. }
  31. //不要以为到这才开始,这只是判断,是否有个正在进行的,方法如下
  32. if (!beamManager.startBeamSend(mContext,
  33. mHandoverDataParser.getOutgoingHandoverData(response), uris, userHandle)) {
  34. return HANDOVER_BUSY;
  35. }
  36. return HANDOVER_SUCCESS;
  37. }
  38. public boolean startBeamSend(Context context,
  39. HandoverDataParser.BluetoothHandoverData outgoingHandoverData,
  40. Uri[] uris, UserHandle userHandle) {
  41. synchronized (mLock) {
  42. if (mBeamInProgress) {
  43. return false;
  44. } else {
  45. mBeamInProgress = true;
  46. }
  47. }
         接下来就是SNEP的分析了,也是按照SNEP协议来进行发送数据
  1. int doSnepProtocol(NdefMessage msg) throws IOException {
  2. if (msg != null) {
  3. snepClient.put(msg);
  4. return SNEP_SUCCESS;
  5. } else {
  6. return SNEP_FAILURE;
  7. }
  8. }
         SnepClient类中的put方法,
  1. public void put(NdefMessage msg) throws IOException {
  2. //一些个判断.
  3. SnepMessenger messenger;
  4. synchronized (this) {
  5. if (mState != CONNECTED) {
  6. throw new IOException("Socket not connected.");
  7. }
  8. messenger = mMessenger;
  9. }
  10. //重要的是SnepMessenger的sendMessage和getMessage
  11. synchronized (mTransmissionLock) {
  12. try {
  13. //下面两个方法就是Android按照SNEP协议进行编写执行的。
  14. //通过SnepMessenger进行发送,SnepMessenger实现了协议.
  15. messenger.sendMessage(SnepMessage.getPutRequest(msg));
  16. messenger.getMessage();
  17. } catch (SnepException e) {
  18. throw new IOException(e);
  19. }
  20. }
  21. }
          客户端生成一个put请求的SnepMessage信息。
  1. public static SnepMessage getPutRequest(NdefMessage ndef) {
  2. return new SnepMessage(VERSION, REQUEST_PUT, ndef.toByteArray().length, 0, ndef);
  3. }
          接下来就是真正的按照协议写的地方
  1. public void sendMessage(SnepMessage msg) throws IOException {
  2. byte[] buffer = msg.toByteArray();
  3. ...
  4. //取当前buffer的长度和定义的Fragment长度中较小值,看看是不是需要分段发送,先取出来较小的那个.
  5. int length = Math.min(buffer.length, mFragmentLength);
  6. byte[] tmpBuffer = Arrays.copyOfRange(buffer, 0, length);
  7. if (DBG) Log.d(TAG, "about to send a " + length + " byte fragment");
  8. //将消息透过socket发送出去
  9. //final LlcpSocket mSocket,实例化SnepMessage的时候传入的,最初是在SnepCLient实例化的时候
  10. //调用到connect的时候,调用如下产生
  11. //socket = NfcService.getInstance().createLlcpSocket(0, mMiu, mRwSize, 1024);
  12. mSocket.send(tmpBuffer);
  13. //若数据包不用切割(一个数据包能发完),则直接返回
  14. if (length == buffer.length) {
  15. return;
  16. }
  17. // Look for Continue or Reject from peer.
  18. //按照Snep协议,若切片后发送,则需要等待对方的回复后再决定下一步行动
  19. int offset = length;
  20. byte[] responseBytes = new byte[HEADER_LENGTH];
  21. //调用socket去接受回复。
  22. mSocket.receive(responseBytes);
  23. SnepMessage snepResponse;
  24. try {
  25. snepResponse = SnepMessage.fromByteArray(responseBytes);
  26. } catch (FormatException e) {
  27. throw new IOException("Invalid SNEP message", e);
  28. }
  29. if (DBG) Log.d(TAG, "Got response from first fragment: " + snepResponse.getField());
  30. //如果对方的回复不是continue,在返回错误
  31. if (snepResponse.getField() != remoteContinue) {
  32. throw new IOException("Invalid response from server (" +
  33. snepResponse.getField() + ")");
  34. }
  35. //dta模式
  36. if(NfcService.sIsDtaMode) {
  37. ......
  38. }
  39. // 符合要求后,将剩余的数据发送完成
  40. while (offset < buffer.length) {
  41. length = Math.min(buffer.length - offset, mFragmentLength);
  42. tmpBuffer = Arrays.copyOfRange(buffer, offset, offset + length);
  43. if (DBG) Log.d(TAG, "about to send a " + length + " byte fragment");
  44. //一直发送!!!!!!!感觉socket这可以好好看看啊
  45. mSocket.send(tmpBuffer);
  46. if(NfcService.sIsDtaMode) {
  47. ......
  48. }
  49. offset += length;
  50. }
  51. }
         根据协议,发送完毕后需要等待对方的response消息,就进入了getMessage()阶段,代码分析如下:
  1. public SnepMessage getMessage() throws IOException, SnepException {
  2. ......
  3. //等待对方的回复消息.
  4. size = mSocket.receive(partial);
  5. if (DBG) Log.d(TAG, "read " + size + " bytes");
  6. if (size < 0) {
  7. try {
  8. //接收的数据大小小于0,直接回复Reject
  9. mSocket.send(SnepMessage.getMessage(fieldReject).toByteArray());
  10. } catch (IOException e) {}
  11. throw new IOException("Error reading SNEP message.");
  12. } else if (size < HEADER_LENGTH) {
  13. //根据协议,接收的数据小于Header.size(因为Snep数据必须包含Header),直接回复Reject.
  14. try {
  15. if((NfcService.sIsDtaMode)&&(mIsClient)){
  16. if (DBG) Log.d(TAG, "Invalid header length");
  17. close();
  18. } else {
  19. mSocket.send(SnepMessage.getMessage(fieldReject).toByteArray());
  20. }
  21. mSocket.send(SnepMessage.getMessage(fieldReject).toByteArray());
  22. } catch (IOException e) {
  23. // Ignore
  24. }
  25. throw new IOException("Invalid fragment from sender.");
  26. } else {
  27. //更新buffer值
  28. readSize = size - HEADER_LENGTH;
  29. buffer.write(partial, 0, size);
  30. }
  31. DataInputStream dataIn = new DataInputStream(new ByteArrayInputStream(partial));
  32. requestVersion = dataIn.readByte();
  33. requestField = dataIn.readByte();
  34. requestLength = dataIn.readInt();
  35. if (DBG) Log.d(TAG, "read " + readSize + " of " + requestLength);
  36. //Header中携带的Version不匹配的时候,直接接收完成,退出。
  37. if (((requestVersion & 0xF0) >> 4) != SnepMessage.VERSION_MAJOR) {
  38. if(NfcService.sIsDtaMode) {
  39. sendMessage(SnepMessage.getMessage(SnepMessage.RESPONSE_UNSUPPORTED_VERSION));
  40. close();
  41. } else {
  42. ......
  43. }
  44. }
  45. if(NfcService.sIsDtaMode) {
  46. //dta模式的处理
  47. }
  48. //如果接收的数据小于Header中规定的数据,则请求对方继续发送,回复Continue,也是按照协议来的
  49. if (requestLength > readSize) {
  50. if (DBG) Log.d(TAG, "requesting continuation");
  51. mSocket.send(SnepMessage.getMessage(fieldContinue).toByteArray());
  52. } else {
  53. doneReading = true;
  54. }
  55. // Remaining fragments
  56. //让他continue以后,才能接受生剩余的
  57. while (!doneReading) {
  58. try {
  59. size = mSocket.receive(partial);
  60. if (DBG) Log.d(TAG, "read " + size + " bytes");
  61. if (size < 0) {
  62. try {
  63. mSocket.send(SnepMessage.getMessage(fieldReject).toByteArray());
  64. } catch (IOException e) {
  65. // Ignore
  66. }
  67. throw new IOException();
  68. } else {
  69. //正确接收数据,不断更新收到的数据
  70. readSize += size;
  71. buffer.write(partial, 0, size);
  72. if (readSize == requestLength) {
  73. doneReading = true;
  74. }
  75. }
  76. } catch (IOException e) {
  77. ......
  78. }
  79. }
  80. // Build NDEF message set from the stream
  81. try {
  82. //接收的肯定是二进制的数据流,将接收的数据转换成SNEP格式.
  83. return SnepMessage.fromByteArray(buffer.toByteArray());
  84. } catch (FormatException e) {
  85. Log.e(TAG, "Badly formatted NDEF message, ignoring", e);
  86. throw new SnepException(e);
  87. }
  88. }
          可能当前的P2P并不支持SNEP协议,那么就会尝试用NPP协议进行数据的解析,这样就到了NdefPushClient的push流
程,此部分的协议未看过.
  1. public boolean push(NdefMessage msg) {
  2. LlcpSocket sock = null;
  3. synchronized (mLock) {
  4. ......
  5. sock = mSocket;
  6. }
  7. //按照制定格式创建NdefPushProtoco,没看过这个协议.
  8. NdefPushProtocol proto = new NdefPushProtocol(msg, NdefPushProtocol.ACTION_IMMEDIATE);
  9. ......
  10. try {
  11. remoteMiu = sock.getRemoteMiu();
  12. if (DBG) Log.d(TAG, "about to send a " + buffer.length + " byte message");
  13. // 将当前信息完整的发送出去,一直到完成.
  14. while (offset < buffer.length) {
  15. int length = Math.min(buffer.length - offset, remoteMiu);
  16. byte[] tmpBuffer = Arrays.copyOfRange(buffer, offset, offset+length);
  17. if (DBG) Log.d(TAG, "about to send a " + length + " byte packet");
  18. sock.send(tmpBuffer);
  19. offset += length;
  20. }
  21. return true;
  22. } catch (IOException e) {
  23. ......
  24. } finally {
  25. ......
  26. }
  27. return false;
  28. }
         上述操作都完成后,表示send数据已经完成,需要通知一下用户界面,我们继续返回P2pLinkManager中的SendTask当
中,往下走是调用onSendComplete去进一步处理.
  1. void onSendComplete(NdefMessage msg, long elapsedRealtime) {
  2. // Make callbacks on UI thread
  3. mHandler.sendEmptyMessage(MSG_SEND_COMPLETE);
  4. }
         走到mHandler中的MSG_SEND_COMPLETE
  1. case MSG_SEND_COMPLETE:
  2. synchronized (P2pLinkManager.this) {
  3. ......
  4. mSendState = SEND_STATE_COMPLETE;
  5. mHandler.removeMessages(MSG_DEBOUNCE_TIMEOUT);
  6. if (DBG) Log.d(TAG, "onP2pSendComplete()");
  7. //回调发送完成的接口
  8. mEventListener.onP2pSendComplete();
  9. if (mCallbackNdef != null) {
  10. try {
  11. //发送完成通知用户界面.
  12. mCallbackNdef.onNdefPushComplete(mPeerLlcpVersion);
  13. } catch (Exception e) {
  14. Log.e(TAG, "Failed NDEF completed callback: " + e.getMessage());
  15. }
  16. }
  17. }
  18. break;
         由前面可知在mEventListener是现实P2pEventManager当中
  1. @Override
  2. public void onP2pSendComplete() {
  3. mNfcService.playSound(NfcService.SOUND_END);
  4. mVibrator.vibrate(VIBRATION_PATTERN, -1);
  5. if (mSendUi != null) {
  6. mSendUi.finish(SendUi.FINISH_SEND_SUCCESS);
  7. }
  8. mSending = false;
  9. mNdefSent = true;
  10. }
        接下来是mCallbackNdef前面也说了在framework中NfcActivityManager extends IAppCallback.Stub实现了这个callback
  1. @Override
  2. public void onNdefPushComplete(byte peerLlcpVersion) {
  3. NfcAdapter.OnNdefPushCompleteCallback callback;
  4. synchronized (NfcActivityManager.this) {
  5. NfcActivityState state = findResumedActivityState();
  6. if (state == null) return;
  7. callback = state.onNdefPushCompleteCallback;
  8. }
  9. NfcEvent event = new NfcEvent(mAdapter, peerLlcpVersion);
  10. // Make callback without lock
  11. if (callback != null) {
  12. //最终就调用到了这里(???)
  13. callback.onNdefPushComplete(event);
  14. }
  15. }
待整理问题:
    1)、文章中多的(???)的地方
    2)、需要配合llcp协议看看那几个socket的声明和实现。
后面对NFC相关知识越来越了解的话,会持续更新这个。

文章参考:
http://blog.csdn.net/xuwen0306/article/details/44724859


0 0