此处的代码为Android O的代码,本也有有一些patch的添加和修改,不过这些都不影响整个流程的理解。Handover相关的

大致可分为两种情况:直接读取带NFC的特殊设备如:带Nfc的蓝牙耳机、键盘、照相机等;还一种是传输文件的过程中由于文

件较大使用Handover也就是BT(目前仅支持BT)来传输此时也称之为Android Beam。

1、当检测到支持Nfc的蓝牙键盘、鼠标等的时候:(我们以链接蓝牙键盘为例子来分析).

从NfcDispatcher类中的dispatchTag()方法开始分析,至于如何到这一步的可以看别的文章。

  1. public int dispatchTag(Tag tag) {
  2. ......
  3. if (tryPeripheralHandover(message)) {
  4. if (DBG) Log.i(TAG, "matched BT HANDOVER");
  5. NfcIddEvent.NfcTag.dispatchedToPrioritizedMethod(tag,
  6. NfcIddEvent.NfcTag.VALUE_APP_HANDOVER);
  7. return screenUnlocked ? DISPATCH_UNLOCK : DISPATCH_SUCCESS;
  8. }
  9. ......
  10. }
  1. public boolean tryPeripheralHandover(NdefMessage m){
  2. //下面这个方法mHandoverDataParser.parseBluetooth(m)方法是通过检测到的Tag中携带的Ndef格式的信息,
  3. //信息包含了当前Nfc设备(蓝牙鼠标、键盘、耳机等)的各种设备信息,再往下的具体解析和协议息息相关,暂无深入分析。
  4. HandoverDataParser.BluetoothHandoverData handover = mHandoverDataParser.parseBluetooth(m);
  5. //如果解析出来的信息出问题,就需要去分析一下解析相关的协议了。
  6. ......
  7. //下面就是解析完Tag中的NDEF携带的信息后,放到响应的变量中以便后续使用。
  8. Intent intent = new Intent(mContext, PeripheralHandoverService.class);
  9. intent.putExtra(PeripheralHandoverService.EXTRA_PERIPHERAL_DEVICE, handover.device);
  10. intent.putExtra(PeripheralHandoverService.EXTRA_PERIPHERAL_NAME, handover.name);
  11. intent.putExtra(PeripheralHandoverService.EXTRA_PERIPHERAL_TRANSPORT, handover.transport);
  12. if (handover.oobData != null) {
  13. intent.putExtra(PeripheralHandoverService.EXTRA_PERIPHERAL_OOB_DATA, handover.oobData);
  14. }
  15. if (handover.uuids != null) {
  16. intent.putExtra(PeripheralHandoverService.EXTRA_PERIPHERAL_UUIDS, handover.uuids);
  17. }
  18. if (handover.btClass != null) {
  19. intent.putExtra(PeripheralHandoverService.EXTRA_PERIPHERAL_CLASS, handover.btClass);
  20. }
  21. intent.putExtra(PeripheralHandoverService.EXTRA_CLIENT, mMessenger);
  22. intent.putExtra(PeripheralHandoverService.EXTRA_BT_ENABLED, mBluetoothEnabledByNfc.get());
  23. ...
  24. //启动PeripheralHandoverService去进一步的处理。
  25. mContext.startServiceAsUser(intent, UserHandle.CURRENT);
  26. return true;
  27. }

接下来就是看PeripheralHandoverService中的构造和onCreate以及onStartCommand

  1. public class PeripheralHandoverService extends Service implements BluetoothPeripheralHandover.Callback {
  2. ...
  3. public PeripheralHandoverService() {
  4. mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
  5. mHandler = new MessageHandler();
  6. mMessenger = new Messenger(mHandler);
  7. mBluetoothHeadsetConnected = false;
  8. mBluetoothEnabledByNfc = false;
  9. mStartId = 0;
  10. }
  11. ...
  12. @Override
  13. public void onCreate() {
  14. super.onCreate();
  15. mNfcAdapter = NfcAdapter.getDefaultAdapter(getApplicationContext());
  16. IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
  17. registerReceiver(mBluetoothStatusReceiver, filter);
  18. }
  19. @Override
  20. public int onStartCommand(Intent intent, int flags, int startId) {
  21. ......
  22. if (doPeripheralHandover(intent.getExtras())) {
  23. return START_STICKY;
  24. } else {
  25. stopSelf(startId);
  26. return START_NOT_STICKY;
  27. }
  28. }
  29. }

主要的我们需要关注的就是:
                a、注册的BluetoothAdapter.ACTION_STATE_CHANGED广播。
                b、调用doPeripheralHandover去进一步的操作。
           mBluetoothStatusReceiver如下:这个reveiver的作用在蓝牙打开的时候并且是由Nfc打开的时候就进一步的调用handover

相关的流程进行处理。

  1. final BroadcastReceiver mBluetoothStatusReceiver = new BroadcastReceiver() {
  2. @Override
  3. public void onReceive(Context context, Intent intent) {
  4. String action = intent.getAction();
  5. if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
  6. handleBluetoothStateChanged(intent);
  7. }
  8. }
  9. };
  10. private void handleBluetoothStateChanged(Intent intent) {
  11. int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,
  12. BluetoothAdapter.ERROR);
  13. if (state == BluetoothAdapter.STATE_ON) {
  14. // If there is a pending device pairing, start it
  15. //下面最主要的其实就是调用mBluetoothPeripheralHandover.start()方法进行进一步的操作,
  16. //而此时一般蓝牙刚被Nfc设备打开,根据是否配对来提示用户进行配对或者直接连接。
  17. if (mBluetoothPeripheralHandover != null &&
  18. !mBluetoothPeripheralHandover.hasStarted()) {
  19. if (!mBluetoothPeripheralHandover.start()) {
  20. mNfcAdapter.resumePolling();
  21. }
  22. }
  23. } else if (state == BluetoothAdapter.STATE_OFF) {
  24. if (mBluetoothAdapter.getState() != BluetoothAdapter.STATE_TURNING_ON) {
  25. mBluetoothEnabledByNfc = false;
  26. }
  27. mBluetoothHeadsetConnected = false;
  28. }
  29. }
          doPeripheralHandover的详解
  1. boolean doPeripheralHandover(Bundle msgData) {
  2. //只能有一个BluetoothPeripheralHandover对象,处理这个的Nfc Tag信息读取,
  3. //此时想要再检测就得把Nfc设备的开关进行打开和关闭操作。
  4. if (mBluetoothPeripheralHandover != null) {
  5. Log.d(TAG, "Ignoring pairing request, existing handover in progress.");
  6. return true;
  7. }
  8. ......//这块就是把msgData中携带的数据取出来。(NDEF信息中的数据)
  9. mBluetoothEnabledByNfc = msgData.getBoolean(EXTRA_BT_ENABLED);
  10. //BluetoothPeripheralHandover是真正的处理的地方,此处进行唯一的实例化.
  11. //传入的参数大都是从NDEF信息中解析出来的.
  12. mBluetoothPeripheralHandover = new BluetoothPeripheralHandover(
  13. this, mDevice, name, transport, oobData, uuids, btClass, this);
  14. ......
  15. if (mBluetoothAdapter.isEnabled()) {
  16. //如果蓝牙已经打开,直接调用start()去进行配对等的处理。
  17. if (!mBluetoothPeripheralHandover.start()) {
  18. mHandler.removeMessages(MSG_PAUSE_POLLING);
  19. mNfcAdapter.resumePolling();
  20. }
  21. } else {
  22. //如果蓝牙没有打开那么就调用enableBluetooth去打开蓝牙设备.
  23. //此处你打开蓝牙成功后也会往外发送广播最终到前面的receiver中最后也是调用start进行了处理。
  24. if (!enableBluetooth()) {
  25. Log.e(TAG, "Error enabling Bluetooth.");
  26. mBluetoothPeripheralHandover = null;
  27. return false;
  28. }
  29. }
  30. return true;
  31. }
  32. //此处调用的是framework专门为system级别的app设定的方法enableNoAutoConnect,这个方法
  33. //打开蓝牙后不会记录此次操作。
  34. boolean enableBluetooth() {
  35. if (!mBluetoothAdapter.isEnabled()) {
  36. mBluetoothEnabledByNfc = true;
  37. return mBluetoothAdapter.enableNoAutoConnect();
  38. }
  39. return true;
  40. }

上面的两种情况最终都走到了BluetoothPeripheralHandover的start方法。

  1. public boolean start() {
  2. checkMainThread();
  3. ...
  4. IntentFilter filter = new IntentFilter();
  5. filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
  6. filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
  7. filter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
  8. filter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
  9. filter.addAction(BluetoothInputDevice.ACTION_CONNECTION_STATE_CHANGED);
  10. filter.addAction(ACTION_ALLOW_CONNECT);
  11. filter.addAction(ACTION_DENY_CONNECT);
  12. mContext.registerReceiver(mReceiver, filter);
  13. //很多地方都会发送这个timeout信息,因为无法确定蓝牙那块是走到了预期,或者出现什么别的情况。
  14. mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_TIMEOUT), TIMEOUT_MS);
  15. //现在的行为是初始化阶段ACTION_INIT
  16. mAction = ACTION_INIT;
  17. mRetryCount = 0;
  18. //调用nextStep去做进一步的处理
  19. nextStep();
  20. return true;
  21. }

这个nextStep很关进,它根据当前所处的不同的action来选择具体的我们下一步要执行的动作。

  1. void nextStep() {
  2. if (mAction == ACTION_INIT) {
  3. nextStepInit();
  4. } else if (mAction == ACTION_CONNECT) {
  5. nextStepConnect();
  6. } else {
  7. nextStepDisconnect();
  8. }
  9. }

此时我们处于ACTION_INIT的action所以走到nextStepInit();

  1. void nextStepInit() {
  2. switch (mState) {
  3. case STATE_INIT:
  4. //如下只要有一个为null,就去getProfileProxys且改变状态STATE_WAITING_FOR_PROXIES.
  5. if (mA2dp == null || mHeadset == null || mInput == null) {
  6. mState = STATE_WAITING_FOR_PROXIES;
  7. if (!getProfileProxys()) {
  8. complete(false);
  9. }
  10. break;
  11. }
  12. //走到此处的时候证明已经获得了需要连接的蓝牙的对应协议的profile对应的service的实现。
  13. case STATE_WAITING_FOR_PROXIES:
  14. //注意state的改变此时变成了STATE_INIT_COMPLETE
  15. mState = STATE_INIT_COMPLETE;
  16. // Check connected devices and see if we need to disconnect
  17. synchronized(mLock) {
  18. if (mTransport == BluetoothDevice.TRANSPORT_LE || mIsHidCapability) {
  19. //根据当前时候连接来决定下一个的状态.
  20. if (mInput.getConnectedDevices().contains(mDevice)) {
  21. Log.i(TAG, "ACTION_DISCONNECT addr=" + mDevice + " name=" + mName);
  22. mAction = ACTION_DISCONNECT;
  23. } else {
  24. Log.i(TAG, "ACTION_CONNECT addr=" + mDevice + " name=" + mName);
  25. mAction = ACTION_CONNECT;
  26. }
  27. } else {
  28. //处理A2DP和HeadSet.
  29. if (mA2dp.getConnectedDevices().contains(mDevice) ||
  30. mHeadset.getConnectedDevices().contains(mDevice)) {
  31. Log.i(TAG, "ACTION_DISCONNECT addr=" + mDevice + " name=" + mName);
  32. mAction = ACTION_DISCONNECT;
  33. } else {
  34. // Check if each profile of the device is disabled or not
  35. if (mHeadset.getPriority(mDevice) == BluetoothProfile.PRIORITY_OFF) {
  36. mIsHeadsetAvailable = false;
  37. }
  38. if (mA2dp.getPriority(mDevice) == BluetoothProfile.PRIORITY_OFF) {
  39. mIsA2dpAvailable = false;
  40. }
  41. if (!mIsHeadsetAvailable && !mIsA2dpAvailable) {
  42. Log.i(TAG, "Both Headset and A2DP profiles are unavailable");
  43. complete(false);
  44. break;
  45. }
  46. Log.i(TAG, "ACTION_CONNECT addr=" + mDevice + " name=" + mName);
  47. mAction = ACTION_CONNECT;
  48. mNeedStartTheMusic = mAudioManager.isBluetoothA2dpOn()
  49. & mAudioManager.isMusicActive();
  50. }
  51. }
  52. }
  53. //接着进行下一步。
  54. nextStep();
  55. }
  56. }

至此我们应该先分析一下getProfileProxys(),如下,很简单的实现,就是调用了蓝牙提供的接口去获得对应的proflie的但是并

又返回对象啊?注意BluetoothPeripheralHandover implements BluetoothProfile.ServiceListener.所以在你连接到对应的profile会回
调相关的接口onServiceConnected.

  1. boolean getProfileProxys() {
  2. if (mTransport == BluetoothDevice.TRANSPORT_LE || mIsHidCapability) {
  3. if (!mBluetoothAdapter.getProfileProxy(mContext, this, BluetoothProfile.INPUT_DEVICE))
  4. return false;
  5. } else {
  6. if(!mBluetoothAdapter.getProfileProxy(mContext, this, BluetoothProfile.HEADSET))
  7. return false;
  8. if(!mBluetoothAdapter.getProfileProxy(mContext, this, BluetoothProfile.A2DP))
  9. return false;
  10. }
  11. return true;
  12. }

当成功链接的时候,会走如下的回调,然后实现具体的profile对象的实例化.

  1. @Override
  2. public void onServiceConnected(int profile, BluetoothProfile proxy) {
  3. synchronized (mLock) {
  4. switch (profile) {
  5. case BluetoothProfile.HEADSET:
  6. mHeadset = (BluetoothHeadset) proxy;
  7. if (mA2dp != null) {
  8. mHandler.sendEmptyMessage(MSG_NEXT_STEP);
  9. }
  10. break;
  11. case BluetoothProfile.A2DP:
  12. mA2dp = (BluetoothA2dp) proxy;
  13. if (mHeadset != null) {
  14. mHandler.sendEmptyMessage(MSG_NEXT_STEP);
  15. }
  16. break;
  17. case BluetoothProfile.INPUT_DEVICE:
  18. mInput = (BluetoothInputDevice) proxy;
  19. if (mInput != null) {
  20. mHandler.sendEmptyMessage(MSG_NEXT_STEP);
  21. }
  22. break;
  23. }
  24. }
  25. }

并且在实例化完毕后会,往外发送MSG_NEXT_STEP信息。这个信息被如下Handler处理。

  1. final Handler mHandler = new Handler() {
  2. @Override
  3. public void handleMessage(Message msg) {
  4. switch (msg.what) {
  5. case MSG_TIMEOUT:
  6. ......
  7. break;
  8. case MSG_NEXT_STEP:
  9. //可以看到重新调用了nextStep.
  10. nextStep();
  11. break;
  12. case MSG_RETRY:
  13. ......
  14. break;
  15. }
  16. }
  17. };

但注意此时mAction == ACTION_INIT,但mState = STATE_WAITING_FOR_PROXIES.所以会调用到nextStepInit中的

STATE_WAITING_FOR_PROXIES这个选项当中。去看前面的对应的case中的代码。可以看到就是修改了mAction = XXX;然后
就是又去进行下一个操作了。
          此时又来到了nextStep(),但是mAction == ACTION_CONNECT或者mAction == ACTION_DISCONNECT我们先分析ACTI
ON_CONNECT对应的就是方法nextStepConnect。

  1. void nextStepConnect() {
  2. switch (mState) {
  3. //此时的state是STATE_INIT_COMPLETE走到如下
  4. case STATE_INIT_COMPLETE:
  5. //如果未配对就会去调用requestPairConfirmation.
  6. if (mDevice.getBondState() != BluetoothDevice.BOND_BONDED) {
  7. requestPairConfirmation();
  8. mState = STATE_WAITING_FOR_BOND_CONFIRMATION;
  9. break;
  10. }
  11. ......
  12. //到这一步的时候是用户根据弹框选择了确认配对
  13. case STATE_WAITING_FOR_BOND_CONFIRMATION:
  14. if (mDevice.getBondState() != BluetoothDevice.BOND_BONDED) {
  15. startBonding();
  16. break;
  17. }
  18. //到这一步的时候已经配对完毕了,要对不同的profile进行不同的connecte.
  19. //且会按照顺序通过判断依次进行链接.
  20. case STATE_BONDING:
  21. //状态改为正在连接.
  22. mState = STATE_CONNECTING;
  23. synchronized (mLock) {
  24. //如果是input设备如蓝牙键盘。
  25. if (mIsHidCapability) {
  26. if (mInput.getConnectionState(mDevice)
  27. != BluetoothProfile.STATE_CONNECTED) {
  28. mHidResult = RESULT_PENDING;
  29. //直接调用对应的proflie对象进行Connect
  30. mInput.connect(mDevice);
  31. toast(mContext.getString(R.string.nfc_strings_toast_connecting_txt),
  32. Toast.LENGTH_LONG);
  33. break;
  34. } else {
  35. mHidResult = RESULT_CONNECTED;
  36. }
  37. } else if (mTransport != BluetoothDevice.TRANSPORT_LE) {
  38. //只要不是input设备,就常尝试用mHeadset、mA2dp两个profile进行了链接
  39. //注意此处还会retry几次。
  40. if (mHeadset.getConnectionState(mDevice) !=
  41. BluetoothProfile.STATE_CONNECTED) {
  42. if (mIsHeadsetAvailable) {
  43. mHfpResult = RESULT_PENDING;
  44. mHeadset.connect(mDevice);
  45. } else {
  46. mHfpResult = RESULT_DISCONNECTED;
  47. }
  48. } else {
  49. mHfpResult = RESULT_CONNECTED;
  50. }
  51. if (mA2dp.getConnectionState(mDevice) != BluetoothProfile.STATE_CONNECTED) {
  52. if (mIsA2dpAvailable) {
  53. mA2dpResult = RESULT_PENDING;
  54. mA2dp.connect(mDevice);
  55. } else {
  56. mA2dpResult = RESULT_DISCONNECTED;
  57. }
  58. } else {
  59. mA2dpResult = RESULT_CONNECTED;
  60. }
  61. if (mA2dpResult == RESULT_PENDING || mHfpResult == RESULT_PENDING) {
  62. if (mRetryCount == 0) {
  63. toast(mContext.getString(R.string.nfc_strings_toast_connecting_txt),
  64. Toast.LENGTH_LONG);
  65. }
  66. if (mRetryCount < MAX_RETRY_COUNT) {
  67. sendRetryMessage(RETRY_CONNECT_WAIT_TIME_MS);
  68. break;
  69. }
  70. }
  71. }
  72. }
  73. //当连接完毕的时候,无论成功与否。
  74. case STATE_CONNECTING:
  75. if (mIsHidCapability) {
  76. //输入设备根据连接的结果来做相应的赋值和提示操作
  77. if (mHidResult == RESULT_PENDING) {
  78. break;
  79. } else if (mHidResult == RESULT_CONNECTED) {
  80. toast(mContext.getString(R.string.nfc_strings_toast_connected_txt, mName));
  81. mDevice.setAlias(mName);
  82. complete(true);
  83. } else {
  84. toast(mContext.getString(
  85. R.string.nfc_strings_toast_sht_connection_failed_txt));
  86. complete(false);
  87. }
  88. } else if (mTransport != BluetoothDevice.TRANSPORT_LE) {
  89. //处理A2DP和HeadSet的,基本同Hid
  90. ......
  91. if (mA2dpResult == RESULT_CONNECTED || mHfpResult == RESULT_CONNECTED) {
  92. ......
  93. } else {
  94. ......
  95. }
  96. }
  97. break;
  98. }
  99. }

接下来我们具体看看nextStepConnect这个方法内部的几个重要实现。
         首先是requestPairConfirmation,如下可以看到就是启动了一个Activity提示用户是否要进行配对,

  1. void requestPairConfirmation() {
  2. Intent dialogIntent = new Intent(mContext, ConfirmConnectActivity.class);
  3. dialogIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
  4. dialogIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice);
  5. dialogIntent.putExtra(BluetoothDevice.EXTRA_NAME, mName);
  6. mContext.startActivity(dialogIntent);
  7. }

我们进入到Activity中观察:

  1. public class ConfirmConnectActivity extends Activity {
  2. BluetoothDevice mDevice;
  3. AlertDialog mAlert = null;
  4. @Override
  5. protected void onCreate(Bundle savedInstanceState) {
  6. super.onCreate(savedInstanceState);
  7. AlertDialog.Builder builder = new AlertDialog.Builder(this,
  8. AlertDialog.THEME_DEVICE_DEFAULT_LIGHT);
  9. ...
  10. builder.setMessage(confirmString)
  11. .setCancelable(false)
  12. .setPositiveButton(res.getString(R.string.pair_yes),
  13. new DialogInterface.OnClickListener() {
  14. public void onClick(DialogInterface dialog, int id) {
  15. //当用户确认配对的时候往外发送BluetoothPeripheralHandover.ACTION_ALLOW_CONNECT的广播
  16. Intent allowIntent = new Intent(BluetoothPeripheralHandover.ACTION_ALLOW_CONNECT);
  17. allowIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice);
  18. sendBroadcast(allowIntent);
  19. ...
  20. }
  21. })
  22. .setNegativeButton(res.getString(R.string.pair_no),
  23. new DialogInterface.OnClickListener() {
  24. public void onClick(DialogInterface dialog, int id) {
  25. //当用户取消配对的时候往外发送BluetoothPeripheralHandover.ACTION_ALLOW_CONNECT的广播
  26. Intent denyIntent = new Intent(BluetoothPeripheralHandover.ACTION_DENY_CONNECT);
  27. denyIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice);
  28. sendBroadcast(denyIntent);
  29. ...
  30. }
  31. });
  32. mAlert = builder.create();
  33. mAlert.show();
  34. ...
  35. }
  36. }

而如上的两个广播都是BluetoothPeripheralHandover在它的start()中进行注册的,最终由mReceiver处理

  1. final BroadcastReceiver mReceiver = new BroadcastReceiver() {
  2. @Override
  3. public void onReceive(Context context, Intent intent) {
  4. handleIntent(intent);
  5. }
  6. };
  7. void handleIntent(Intent intent) {
  8. String action = intent.getAction();
  9. ...
  10. //配对的Action
  11. if (ACTION_ALLOW_CONNECT.equals(action)) {
  12. //执行配对前再次发送延时信息,因为不确定配对多长时间完成,以及有没有成功
  13. mHandler.removeMessages(MSG_TIMEOUT);
  14. mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_TIMEOUT), TIMEOUT_MS);
  15. nextStepConnect();
  16. //下面是取消配对的action
  17. } else if (ACTION_DENY_CONNECT.equals(action)) {
  18. complete(false);
  19. //下面是配对状态变化的action
  20. } else if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action)
  21. && mState == STATE_BONDING) {
  22. int bond = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE,
  23. BluetoothAdapter.ERROR);
  24. if (bond == BluetoothDevice.BOND_BONDED) {
  25. mRetryCount = 0;
  26. nextStepConnect();
  27. } else if (bond == BluetoothDevice.BOND_NONE) {
  28. if (mRetryCount < MAX_RETRY_COUNT) {
  29. sendRetryMessage(RETRY_PAIRING_WAIT_TIME_MS);
  30. } else {
  31. toast(getToastString(R.string.pairing_peripheral_failed));
  32. complete(false);
  33. }
  34. }
  35. //下面是BluetoothHeadset profile链接状态变化的action
  36. } else if (BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED.equals(action) &&
  37. (mState == STATE_CONNECTING || mState == STATE_DISCONNECTING)) {
  38. ...
  39. //下面是BluetoothA2dp profile链接状态变化的action
  40. } else if (BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED.equals(action) &&
  41. (mState == STATE_CONNECTING || mState == STATE_DISCONNECTING)) {
  42. ...
  43. //下面是BluetoothInputDevice profile链接状态变化的action
  44. } else if (BluetoothInputDevice.ACTION_CONNECTION_STATE_CHANGED.equals(action) &&
  45. (mState == STATE_CONNECTING || mState == STATE_DISCONNECTING)) {
  46. int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, BluetoothAdapter.ERROR);
  47. if (state == BluetoothProfile.STATE_CONNECTED) {
  48. //链接成功的时候
  49. mHidResult = RESULT_CONNECTED;
  50. nextStep();
  51. } else if (state == BluetoothProfile.STATE_DISCONNECTED) {
  52. //连接失败的时候
  53. mHidResult = RESULT_DISCONNECTED;
  54. nextStep();
  55. }
  56. }
  57. }

此时假如我们选择的是和蓝牙键盘进行配对那么会走到nextStepConnect(),但此时的state是STATE_WAITING_FOR_BO

ND_CONFIRMATION
参照前面的代码,最终走到startBonding方法

  1. void startBonding() {
  2. //显示修改状态
  3. mState = STATE_BONDING;
  4. //直接调用BluetoothDevice的不同的Bond方法进行配对,暂不清楚这两个的具体区别???
  5. if (mOobData != null) {
  6. if (!mDevice.createBondOutOfBand(mTransport, mOobData)) {
  7. toast(mContext.getString(R.string.nfc_strings_toast_sht_paring_failed_txt));
  8. complete(false);
  9. }
  10. } else if (!mDevice.createBond(mTransport)) {
  11. toast(mContext.getString(R.string.nfc_strings_toast_sht_paring_failed_txt));
  12. complete(false);
  13. }
  14. }

无论我们配对成功与否,当配对状态发生改变的时候,就会走到前面说的handleIntent中对应的判断。ACTION_BOND_S

TATE_CHANGED,并且此时的state为STATE_BONDING.查看上面代码,在配对成功的时候调用nextStepConnect,失败的时
候根据判断重试或者直接做最后的赋值操作。
          在nextStepConnect()当中此时的state状态时STATE_BONDING,注意此时已经是配对成功了,但是state还没改变,处于
正在配对状态,那么我们进去case STATE_BONDING。然后先把状态改为:mState = STATE_CONNECTING;正在链接接着就
开始真正的connect。根据不同的profile调用不同的connect。在连接成功后BT那边会往外发送广播,会被handleIntent进行处理,
此处我们是以链接蓝牙键盘为例子,那么就会走到BluetoothInputDevice.ACTION_CONNECTION_STATE_CHANGED然后又去
调用nextStep(),又会走到nextStepConnect()。但此时的状态时是state为STATE_CONNECTING.进入到对应的判断中做最后的通
知处理即可,调用complete。
//成功的时候true,失败的时候false.

  1. void complete(boolean connected) {
  2. reportBluetoothPeripheralHandoverComplete(connected);
  3. mState = STATE_COMPLETE;
  4. mContext.unregisterReceiver(mReceiver);
  5. mHandler.removeMessages(MSG_TIMEOUT);
  6. mHandler.removeMessages(MSG_RETRY);
  7. synchronized (mLock) {
  8. if (mA2dp != null) {
  9. mBluetoothAdapter.closeProfileProxy(BluetoothProfile.A2DP, mA2dp);
  10. }
  11. if (mHeadset != null) {
  12. mBluetoothAdapter.closeProfileProxy(BluetoothProfile.HEADSET, mHeadset);
  13. }
  14. if (mInput != null) {
  15. mBluetoothAdapter.closeProfileProxy(BluetoothProfile.INPUT_DEVICE, mInput);
  16. }
  17. mA2dp = null;
  18. mHeadset = null;
  19. mInput = null;
  20. }
  21. mCallback.onBluetoothPeripheralHandoverComplete(connected);
  22. }

至此完成了类似于读取Tag的分发流程,把每个外设都看成是一个Nfc的Tag.

2、当是P2P模式最终以蓝牙传输的时候。

此处直接从P2P模式已经确认要发送开始,此时用户已经点击屏幕,开始回调发送事件。对应的HandoverClient和Handover
Server已经启动。(前面的文章有分析)就是P2pLinkManager类的SendTask内部类开始分析,且只分析handover的
  1. final class SendTask extends AsyncTask<Void, Void, Void> {
  2. ...
  3. HandoverClient handoverClient;
  4. //需要通过BT传输的操作
  5. int doHandover(Uri[] uris, UserHandle userHandle) throws IOException {
  6. NdefMessage response = null;
  7. //先实例化BeamManager,用于管理从开始到结束的传输的动作。
  8. BeamManager beamManager = BeamManager.getInstance();
  9. //判断是不是有一个传输正在进行。
  10. if (beamManager.isBeamInProgress()) {
  11. return HANDOVER_BUSY;
  12. }
  13. //如下是客户端根据协议把要请求的信息如蓝牙地址等,封装成NDEF格式的信息用于往外发送给
  14. //另一个手机也就是服务端,然后服务端开启自己的BT进行配对等等的操作,暂不分析此处的是现实细节
  15. NdefMessage request = mHandoverDataParser.createHandoverRequestMessage();
  16. //如果成功封装成Ndef信息
  17. if (request != null) {
  18. if (handoverClient != null) {
  19. //通过Client想客户端发送请求,内部就是往指定的服务器地址发送Ndef请求信息,请求试用handover来传输
  20. //此处的信息还是通过Nfc来发送的。
  21. response = handoverClient.sendHandoverRequest(request);
  22. }
  23. //如果远端的手机不支持handover,那么改用别的方式.
  24. if (response == null && snepClient != null) {
  25. SnepMessage snepResponse = snepClient.get(request);
  26. response = snepResponse.getNdefMessage();
  27. }
  28. if (response == null) {
  29. if (snepClient != null)
  30. return HANDOVER_UNSUPPORTED;
  31. else
  32. return HANDOVER_FAILURE;
  33. }
  34. } else {
  35. return HANDOVER_UNSUPPORTED;
  36. }
  37. //走到这里的时候证明远端的Handover是成功,打算开始调用BT进行真正的数据的传输。
  38. //mHandoverDataParser.getOutgoingHandoverData(response),来把HandoverServer传过来的
  39. //Ndef数据解析,并且赋值给我们需要的变量,就是比如把蓝牙地址、名称、数据配对信息等,赋值给了BeamManager
  40. //的一些变量,此处暂不深入分析,因为的对照协议解析,暂时不太清楚???
  41. if (!beamManager.startBeamSend(mContext,
  42. mHandoverDataParser.getOutgoingHandoverData(response), uris, userHandle)) {
  43. return HANDOVER_BUSY;
  44. }
  45. return HANDOVER_SUCCESS;
  46. }
  47. int doSnepProtocol(NdefMessage msg) throws IOException {
  48. ...
  49. }
  50. @Override
  51. public Void doInBackground(Void... args) {
  52. ......
  53. //当解析传过来的数据发现需要使用handover的时候,uris被设置值,此时进入到doHandover
  54. if (uris != null) {
  55. try {
  56. int handoverResult = doHandover(uris, userHandle);
  57. switch (handoverResult) {
  58. case HANDOVER_SUCCESS:
  59. result = true;
  60. break;
  61. case HANDOVER_FAILURE:
  62. result = false;
  63. break;
  64. case HANDOVER_UNSUPPORTED:
  65. result = false;
  66. onHandoverUnsupported();
  67. break;
  68. case HANDOVER_BUSY:
  69. result = false;
  70. onHandoverBusy();
  71. break;
  72. }
  73. } catch (IOException e) {
  74. result = false;
  75. }
  76. }
  77. ......
  78. if (result) {
  79. onSendComplete(m, time);
  80. }
  81. return null;
  82. }
  83. }
接下来我们就来看看BeamManager的简单介绍

  1. public class BeamManager implements Handler.Callback {
  2. ...//省略成员变量显示
  3. //获取实例,单例模式
  4. private static final class Singleton {
  5. public static final BeamManager INSTANCE = new BeamManager();
  6. }
  7. private BeamManager() {
  8. mLock = new Object();
  9. mBeamInProgress = false;
  10. mCallback = new Handler(Looper.getMainLooper(), this);
  11. mNfcService = NfcService.getInstance();
  12. }
  13. public static BeamManager getInstance() {
  14. return Singleton.INSTANCE;
  15. }
  16. public boolean isBeamInProgress() {
  17. synchronized (mLock) {
  18. return mBeamInProgress;
  19. }
  20. }
  21. public boolean startBeamReceive(Context context,
  22. HandoverDataParser.BluetoothHandoverData handoverData) {
  23. synchronized (mLock) {
  24. if (mBeamInProgress) {
  25. return false;
  26. } else {
  27. mBeamInProgress = true;
  28. }
  29. }
  30. BeamTransferRecord transferRecord =
  31. BeamTransferRecord.forBluetoothDevice(
  32. handoverData.device, handoverData.carrierActivating, null);
  33. Intent receiveIntent = new Intent(context.getApplicationContext(),
  34. BeamReceiveService.class);
  35. receiveIntent.putExtra(BeamReceiveService.EXTRA_BEAM_TRANSFER_RECORD, transferRecord);
  36. receiveIntent.putExtra(BeamReceiveService.EXTRA_BEAM_COMPLETE_CALLBACK,
  37. new Messenger(mCallback));
  38. if (CtaUtils.showCtaBtDialogIfNeeded(context, mCallback,
  39. receiveIntent, handoverData.device)) {
  40. return true;
  41. }
  42. whitelistOppDevice(context, handoverData.device);
  43. context.startServiceAsUser(receiveIntent, UserHandle.CURRENT);
  44. return true;
  45. }
  46. //已经收到回应开始尝试使用handover模式也称之为Beam模式
  47. //注意第二个参数就是远端的Handoverserver给出的回应,进一步封装的数据
  48. public boolean startBeamSend(Context context,
  49. HandoverDataParser.BluetoothHandoverData outgoingHandoverData,
  50. Uri[] uris, UserHandle userHandle) {
  51. //只能执行一个传输.
  52. synchronized (mLock) {
  53. if (mBeamInProgress) {
  54. return false;
  55. } else {
  56. mBeamInProgress = true;
  57. }
  58. }
  59. //这个Record如其名字,就是记录了remoteDevice(远端的device)、uris(存放的要传输的数据的uri)等
  60. BeamTransferRecord transferRecord = BeamTransferRecord.forBluetoothDevice(
  61. outgoingHandoverData.device, outgoingHandoverData.carrierActivating,
  62. uris);
  63. //启动BeamReceiveService来通过service进一步完成正真的传输.
  64. Intent sendIntent = new Intent(context.getApplicationContext(),
  65. BeamSendService.class);
  66. sendIntent.putExtra(BeamSendService.EXTRA_BEAM_TRANSFER_RECORD, transferRecord);
  67. sendIntent.putExtra(BeamSendService.EXTRA_BEAM_COMPLETE_CALLBACK,
  68. new Messenger(mCallback));
  69. ......
  70. context.startServiceAsUser(sendIntent, userHandle);
  71. return true;
  72. }
  73. @Override
  74. public boolean handleMessage(Message msg) {
  75. if (msg.what == MSG_BEAM_COMPLETE) {
  76. synchronized (mLock) {
  77. mBeamInProgress = false;
  78. }
  79. boolean success = msg.arg1 == 1;
  80. if (success) {
  81. mNfcService.playSound(NfcService.SOUND_END);
  82. }
  83. return true;
  84. }
  85. return false;
  86. }
  87. void whitelistOppDevice(Context context, BluetoothDevice device) {
  88. if (DBG) Log.d(TAG, "Whitelisting " + device + " for BT OPP");
  89. Intent intent = new Intent(ACTION_WHITELIST_DEVICE);
  90. intent.setPackage(BLUETOOTH_PACKAGE);
  91. intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
  92. context.sendBroadcastAsUser(intent, UserHandle.CURRENT);
  93. }
  94. }

此时是调用到了startBeamSend进而启动BeamSendService来完成进一步的操作。

  1. public class BeamSendService extends Service implements BeamTransferManager.Callback{
  2. ......
  3. private final BroadcastReceiver mBluetoothStateReceiver = new BroadcastReceiver() {
  4. @Override
  5. public void onReceive(Context context, Intent intent) {
  6. String action = intent.getAction();
  7. if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
  8. handleBluetoothStateChanged(intent);
  9. }
  10. }
  11. };
  12. //构造很简单只是得到了BluetoothAdapter
  13. public BeamSendService() {
  14. mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
  15. }
  16. @Override
  17. public void onCreate() {
  18. super.onCreate();
  19. IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
  20. registerReceiver(mBluetoothStateReceiver, filter);
  21. }
  22. @Override
  23. public int onStartCommand(Intent intent, int flags, int startId) {
  24. ......
  25. //调用doTransfer开始往外发送.
  26. if (doTransfer(transferRecord)) {
  27. return START_STICKY;
  28. } else {
  29. //异常处理
  30. ......
  31. }
  32. }
  33. boolean doTransfer(BeamTransferRecord transferRecord) {
  34. if (createBeamTransferManager(transferRecord)) {
  35. //下面这个receiver我们后面在解释。
  36. mBeamStatusReceiver = new BeamStatusReceiver(this, mTransferManager);
  37. registerReceiver(mBeamStatusReceiver, mBeamStatusReceiver.getIntentFilter(),
  38. BeamStatusReceiver.BEAM_STATUS_PERMISSION, new Handler());
  39. //Ndef信息中携带的是蓝牙的传输链接的时候才进行传输。
  40. if (transferRecord.dataLinkType == BeamTransferRecord.DATA_LINK_TYPE_BLUETOOTH) {
  41. //如果已经打开
  42. if (mBluetoothAdapter.isEnabled()) {
  43. // 直接调用TransferManager的start开始传送,
  44. mTransferManager.start();
  45. } else {
  46. //调用BT相关的接口去打开蓝牙,打开成功后会发送蓝牙状态改变你的广播,
  47. if (!mBluetoothAdapter.enableNoAutoConnect()) {
  48. Log.e(TAG, "Error enabling Bluetooth.");
  49. mTransferManager = null;
  50. return false;
  51. }
  52. //用来标记是Nfc打开的BT
  53. mBluetoothEnabledByNfc = true;
  54. ...
  55. }
  56. }
  57. return true;
  58. }
  59. return false;
  60. }
  61. boolean createBeamTransferManager(BeamTransferRecord transferRecord) {
  62. ......
  63. //如下的实例化很关键
  64. //实例化BeamTransferManager来管理传输中的流程显示等,关于BeamTransferManager下面详解
  65. mTransferManager = new BeamTransferManager(this, this, transferRecord, false);
  66. //第一个notification!
  67. mTransferManager.updateNotification();
  68. return true;
  69. }
  70. //收到BT开启的广播以后开始准备传送.
  71. private void handleBluetoothStateChanged(Intent intent) {
  72. int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,
  73. BluetoothAdapter.ERROR);
  74. if (state == BluetoothAdapter.STATE_ON) {
  75. if (mTransferManager != null &&
  76. mTransferManager.mDataLinkType == BeamTransferRecord.DATA_LINK_TYPE_BLUETOOTH) {
  77. //收到广播以后同样是调用TransferManager的start开始传输.
  78. mTransferManager.start();
  79. }
  80. }
  81. }
  82. ......
  83. //传输完成的回调.
  84. @Override
  85. public void onTransferComplete(BeamTransferManager transfer, boolean success) {
  86. // Play success sound
  87. ......
  88. if (mBluetoothEnabledByNfc) {
  89. mBluetoothEnabledByNfc = false;
  90. mBluetoothAdapter.disable();
  91. }
  92. //
  93. invokeCompleteCallback(success);
  94. stopSelf(mStartId);
  95. }
  96. ......
  97. }

可以看到如上的很多的工作都是在BeamTransferManager中完成的!BeamTransferManager代码较多此处就一点点分析
先从实例化看起。

构造中实例化的变量很多,我们只看比较重要的。

  1. public BeamTransferManager(Context context, Callback callback,
  2. BeamTransferRecord pendingTransfer, boolean incoming) {
  3. ...
  4. mRemoteDevice = pendingTransfer.remoteDevice;
  5. //往外发送的时候是false.
  6. mIncoming = incoming;
  7. mTransferId = pendingTransfer.id;
  8. mDataLinkType = pendingTransfer.dataLinkType;
  9. mRemoteActivating = pendingTransfer.remoteActivating;
  10. mTotalCount = (pendingTransfer.uris != null) ? pendingTransfer.uris.length : 0;
  11. mProgress = 0.0f;
  12. mState = STATE_NEW;
  13. mUris = pendingTransfer.uris == null
  14. ? new ArrayList<Uri>()
  15. : new ArrayList<Uri>(Arrays.asList(pendingTransfer.uris));
  16. ......
  17. //将要往外发送的uris
  18. mOutgoingUris = pendingTransfer.uris;
  19. }

接下来就是第一个通知updateNotification,借此我们来分析一下此方法。

  1. void updateNotification() {
  2. Builder notBuilder = new Notification.Builder(mContext, BEAM_NOTIFICATION_CHANNEL);
  3. notBuilder.setXXX;
  4. ...
  5. String beamString;
  6. //下面判断是往外发送还是接受的,此处我们分析的是发送的。(接受的是不是从HandoverServer开始分析?)
  7. if (mIncoming) {
  8. beamString = mContext.getString(R.string.beam_progress);
  9. } else {
  10. beamString = mContext.getString(R.string.beam_outgoing);
  11. }
  12. //第一次的时候mState == STATE_NEW在构造中进行的实例化
  13. if (mState == STATE_NEW || mState == STATE_IN_PROGRESS ||
  14. mState == STATE_W4_NEXT_TRANSFER || mState == STATE_W4_MEDIA_SCANNER) {
  15. notBuilder.setXXX;
  16. ...
  17. //下面就是处理进度条显示的问题
  18. float progress = 0;
  19. if (mTotalCount > 0) {
  20. float progressUnit = 1.0f / mTotalCount;
  21. progress = (float) mCurrentCount * progressUnit + mProgress * progressUnit;
  22. }
  23. if (mTotalCount > 0 && progress > 0) {
  24. notBuilder.setProgress(100, (int) (100 * progress), false);
  25. } else {
  26. notBuilder.setProgress(100, 0, true);
  27. }
  28. //下面是当传输成功的时候
  29. } else if (mState == STATE_SUCCESS) {
  30. notBuilder.setAutoCancel(true);
  31. ...
  32. //下面是当传输失败的时候
  33. } else if (mState == STATE_FAILED) {
  34. notBuilder.setAutoCancel(false);
  35. ...
  36. //取消传输的时候
  37. } else if (mState == STATE_CANCELLED || mState == STATE_CANCELLING) {
  38. notBuilder.setAutoCancel(false);
  39. ...
  40. } else {
  41. return;
  42. }
  43. //把不同的显示内容notify出去即可。
  44. mNotificationManager.notify(null, mTransferId, notBuilder.build());
  45. }

再往下就是start()开始传输的函数了。

  1. public void start() {
  2. ......
  3. //当是往外传输的时候
  4. if (!mIncoming
  5. //当是通过蓝牙往外传输的时候
  6. if (mDataLinkType == BeamTransferRecord.DATA_LINK_TYPE_BLUETOOTH) {
  7. //传入Handoverserver返回回来的Ndef消息用于和本地蓝牙配对链接等。
  8. new BluetoothOppHandover(mContext, mRemoteDevice, mUris, mRemoteActivating).start();
  9. }
  10. }
  11. }

BluetoothOppHandover用于完成和本地蓝牙的交互操作,start如下:

  1. public void start() {
  2. if (mRemoteActivating) {
  3. Long timeElapsed = SystemClock.elapsedRealtime() - mCreateTime;
  4. if (timeElapsed < REMOTE_BT_ENABLE_DELAY_MS) {
  5. //此处左中也是调用sendIntent().
  6. mHandler.sendEmptyMessageDelayed(MSG_START_SEND,
  7. REMOTE_BT_ENABLE_DELAY_MS - timeElapsed);
  8. } else {
  9. sendIntent();
  10. }
  11. } else {
  12. // Remote BT enabled already, start send immediately
  13. sendIntent();
  14. }
  15. }

那么我们就看一下BluetoothOppHandover当中的sendIntent

  1. void sendIntent() {
  2. Intent intent = new Intent();
  3. intent.setPackage("com.android.bluetooth");
  4. String mimeType = MimeTypeUtil.getMimeTypeForUri(mContext, mUris.get(0));
  5. intent.setType(mimeType);
  6. intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice);
  7. ......
  8. //而如下两个广播那在Bluetooth.apk的应用中的BluetoothOppHandoverReceiver
  9. //进行了处理,最终由Bluetooth去完成发送文件的操作。
  10. if (mUris.size() == 1) {
  11. intent.setAction(ACTION_HANDOVER_SEND);
  12. intent.putExtra(Intent.EXTRA_STREAM, mUris.get(0));
  13. } else {
  14. intent.setAction(ACTION_HANDOVER_SEND_MULTIPLE);
  15. intent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, mUris);
  16. }
  17. if (DBG) Log.d(TAG, "Handing off outging transfer to BT");
  18. intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
  19. mContext.sendBroadcast(intent);
  20. complete();//状态置为complete
  21. }

到此我们就完成了Nfc端的发送广播和BT通信去完成最终的数据传输,一旦开始传输,蓝牙就会在每个传输的阶段发送广

播给Nfc的应用以便Nfc应用可以更新通知的显示。这些操作也是在BeamTransferManager当中实现的。我们也应该意识到有个
receiver去接受来自BT的广播,进而完成更新操作。这就是前面在SendService中的doTransfer()进行实例化的BeamStatusRece
iver,它用于我们上面所说的操作,我们去看一下。

  1. public class BeamStatusReceiver extends BroadcastReceiver {
  2. ...//各种成员变量的广播我们就省略了,代码中直接看即可
  3. //构造
  4. BeamStatusReceiver(Context context, BeamTransferManager transferManager) {
  5. mContext = context;
  6. mTransferManager = transferManager;
  7. }
  8. @Override
  9. public void onReceive(Context context, Intent intent) {
  10. String action = intent.getAction();
  11. int dataLinkType = intent.getIntExtra(EXTRA_HANDOVER_DATA_LINK_TYPE,
  12. BeamTransferManager.DATA_LINK_TYPE_BLUETOOTH);
  13. if (ACTION_CANCEL_HANDOVER_TRANSFER.equals(action)) {
  14. if (mTransferManager != null) {
  15. mTransferManager.cancel();
  16. }
  17. } else if (ACTION_TRANSFER_PROGRESS.equals(action) ||
  18. ACTION_TRANSFER_DONE.equals(action) ||
  19. ACTION_HANDOVER_STARTED.equals(action)) {
  20. handleTransferEvent(intent, dataLinkType);
  21. }
  22. }
  23. //感兴趣的广播
  24. public IntentFilter getIntentFilter() {
  25. IntentFilter filter = new IntentFilter(ACTION_TRANSFER_DONE);
  26. filter.addAction(ACTION_TRANSFER_PROGRESS);
  27. filter.addAction(ACTION_CANCEL_HANDOVER_TRANSFER);
  28. filter.addAction(ACTION_HANDOVER_STARTED);
  29. return filter;
  30. }
  31. private void handleTransferEvent(Intent intent, int deviceType) {
  32. String action = intent.getAction();
  33. int id = intent.getIntExtra(EXTRA_TRANSFER_ID, -1);
  34. String sourceAddress = intent.getStringExtra(EXTRA_ADDRESS);
  35. ......
  36. mTransferManager.setBluetoothTransferId(id);
  37. //接收到BT传输完成,发过来的对应广播以后更新,最终还是更新notification.
  38. //这个广播在传输完成后20s还不发,就会出现发送失败的显示.
  39. if (action.equals(ACTION_TRANSFER_DONE)) {
  40. int handoverStatus = intent.getIntExtra(EXTRA_TRANSFER_STATUS,
  41. HANDOVER_TRANSFER_STATUS_FAILURE);
  42. if (handoverStatus == HANDOVER_TRANSFER_STATUS_SUCCESS) {
  43. ......
  44. mTransferManager.finishTransfer(true, uri, mimeType);
  45. } else {
  46. mTransferManager.finishTransfer(false, null, null);
  47. }
  48. //传输的过程中BT发过来的广播,这个的实现就是在BluetoothOppNotification类中
  49. //嵌入到了蓝牙的通知中,就好比改更新蓝牙传输的进度的时候,隐藏了不更新,转而发送
  50. //这个notification然后Nfc来更新进度!
  51. } else if (action.equals(ACTION_TRANSFER_PROGRESS)) {
  52. float progress = intent.getFloatExtra(EXTRA_TRANSFER_PROGRESS, 0.0f);
  53. mTransferManager.updateFileProgress(progress);
  54. //用于开始Handover的处理
  55. } else if (action.equals(ACTION_HANDOVER_STARTED)) {
  56. int count = intent.getIntExtra(EXTRA_OBJECT_COUNT, 0);
  57. if (count > 0) {
  58. mTransferManager.setObjectCount(count);
  59. }
  60. }
  61. }
  62. }

通过上面可以大概的总结一下,在传输的过程中的时候是一直在调用mTransferManager.updateFileProgress(progress)而在

传输完毕后调用mTransferManager.finishTransfer(true, uri, mimeType);看起来并没有更新Notification啊别急,继续分析这两个的
实现。
     先看updateFileProgress
  1. public void updateFileProgress(float progress) {
  2. ......
  3. this.mProgress = progress;
  4. //用于接受的时候
  5. if (mIncoming && mRemoteDevice != null) whitelistOppDevice(mRemoteDevice);
  6. updateStateAndNotification(STATE_IN_PROGRESS);
  7. }
  8. //调用到了updateStateAndNotification,并且传入state = STATE_IN_PROGRESS
  9. void updateStateAndNotification(int newState) {
  10. this.mState = newState;
  11. ......
  12. //终于看到这个了,去更新Notification,如我们前面所分析。
  13. updateNotification();
  14. ......
  15. }
再看一下finishTransfer

  1. public void finishTransfer(boolean success, Uri uri, String mimeType) {
  2. ......
  3. if (success && uri != null) {
  4. ...
  5. //重新初始化进度
  6. mProgress = 0.0f;
  7. if (mimeType == null) {
  8. mimeType = MimeTypeUtil.getMimeTypeForUri(mContext, uri);
  9. }
  10. if (mimeType != null) {
  11. mUris.add(uri);
  12. mTransferMimeTypes.add(mimeType);
  13. }
  14. ...
  15. } else {
  16. Log.e(TAG, "Handover transfer failed");
  17. }
  18. mHandler.removeMessages(MSG_NEXT_TRANSFER_TIMER);
  19. if (mCurrentCount == mTotalCount) {
  20. if (mIncoming) {
  21. //当是接受的时候还回去做进一步处理,如把这个收到的数据放到那里等
  22. processFiles();
  23. } else {
  24. ......
  25. //发送的时候,根据发送成功的文件数量来调用updateStateAndNotification
  26. updateStateAndNotification(mSuccessCount > 0 ? STATE_SUCCESS : STATE_FAILED);
  27. }
  28. } else {
  29. //不止一个图的时候
  30. mHandler.sendEmptyMessageDelayed(MSG_NEXT_TRANSFER_TIMER, WAIT_FOR_NEXT_TRANSFER_MS);
  31. updateStateAndNotification(STATE_W4_NEXT_TRANSFER);
  32. }
  33. }
          对于发送而言也是调用到updateStateAndNotification,进而进入到updateNotification更新通知栏。至此我们分析完毕往外
发送数据的时候的流程,接下来简单看一下接受数据的时候。理所应当的就想到了接受的地方肯定跑不了HandoverServer当中
啊,然后是在内部类ServerThread中接受然请求的handover信息,
         发送完respond以后调用到ConnectionThread中的run的如下代码:
  1. if (!beamManager.startBeamReceive(mContext, handoverData.handoverData)) {
  2. mCallback.onHandoverBusy();
  3. break;
  4. }
        调用到BeamTransferManager中的startBeamReceive,前面那我们已经列过其代码,此处再单独列出来分析一下。
  1. public boolean startBeamReceive(Context context,
  2. HandoverDataParser.BluetoothHandoverData handoverData) {
  3. ...
  4. //同样也是实例化一个transferRecord
  5. BeamTransferRecord transferRecord =
  6. BeamTransferRecord.forBluetoothDevice(
  7. handoverData.device, handoverData.carrierActivating, null);
  8. //期初的service就不同了,这块要启动的是BeamReceiveService.
  9. Intent receiveIntent = new Intent(context.getApplicationContext(),
  10. BeamReceiveService.class);
  11. receiveIntent.putExtra(BeamReceiveService.EXTRA_BEAM_TRANSFER_RECORD, transferRecord);
  12. receiveIntent.putExtra(BeamReceiveService.EXTRA_BEAM_COMPLETE_CALLBACK,
  13. new Messenger(mCallback));
  14. //接收数据的device的白名单的
  15. whitelistOppDevice(context, handoverData.device);
  16. context.startServiceAsUser(receiveIntent, UserHandle.CURRENT);
  17. return true;
  18. }
         下面列出BeamReceiveService的简单介绍。
  1. public class BeamReceiveService extends Service implements BeamTransferManager.Callback{
  2. ......
  3. private final BroadcastReceiver mBluetoothStateReceiver = new BroadcastReceiver() {
  4. @Override
  5. public void onReceive(Context context, Intent intent) {
  6. String action = intent.getAction();
  7. if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) {
  8. int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,
  9. BluetoothAdapter.ERROR);
  10. if (state == BluetoothAdapter.STATE_OFF) {
  11. //源码就什么也没写,看来是在关闭的时候不作操作。
  12. }
  13. }
  14. }
  15. };
  16. //构造
  17. public BeamReceiveService() {
  18. mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
  19. }
  20. @Override
  21. public int onStartCommand(Intent intent, int flags, int startId) {
  22. ......
  23. //同理发送的时候的设计形式,只不过此处调用的是prepareToReceive.
  24. if (prepareToReceive(transferRecord)) {
  25. if (DBG) Log.i(TAG, "Ready for incoming Beam transfer");
  26. return START_STICKY;
  27. } else {
  28. ......//异常处理
  29. }
  30. }
  31. // TODO: figure out a way to not duplicate this code
  32. @Override
  33. public void onCreate() {
  34. ......
  35. IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
  36. registerReceiver(mBluetoothStateReceiver, filter);
  37. }
  38. ......
  39. boolean prepareToReceive(BeamTransferRecord transferRecord) {
  40. ......
  41. //没有开启的时候
  42. if (!mBluetoothAdapter.isEnabled()) {
  43. //开启蓝牙下面的标志已经见过好多次了。
  44. if (!mBluetoothAdapter.enableNoAutoConnect()) {
  45. Log.e(TAG, "Error enabling Bluetooth.");
  46. return false;
  47. }
  48. mBluetoothEnabledByNfc = true;
  49. }
  50. //同样接受的时候在这里实例化BeamTransferManager.
  51. mTransferManager = new BeamTransferManager(this, this, transferRecord, true);
  52. //也需要更新进度条,同样实例化BeamStatusReceiver
  53. mBeamStatusReceiver = new BeamStatusReceiver(this, mTransferManager);
  54. registerReceiver(mBeamStatusReceiver, mBeamStatusReceiver.getIntentFilter(),
  55. BeamStatusReceiver.BEAM_STATUS_PERMISSION, new Handler());
  56. mTransferManager.start();
  57. mTransferManager.updateNotification();
  58. return true;
  59. }
  60. ......
  61. @Override
  62. public void onTransferComplete(BeamTransferManager transfer, boolean success) {
  63. ......
  64. }
  65. ......
  66. }
  67. //可以看到start对接受没什么实质性的操作
  68. public void start() {
  69. ......
  70. if (!mIncoming) {
  71. if (mDataLinkType == BeamTransferRecord.DATA_LINK_TYPE_BLUETOOTH) {
  72. new BluetoothOppHandover(mContext, mRemoteDevice, mUris, mRemoteActivating).start();
  73. }
  74. }
  75. }

接下来就是根据BT放接受的成都以及传过来的广播来更新Notification了,注意一下mIncoming的值就行。
        至此简单的分析了一下NFC和Handover(也成Beam)协同工作的流程,还有一些断开连接等流程未继续分析,不过代码都是

在这一块了。

NFC源码分析之Handover相关推荐

  1. NFC源码分析之R/W工作模式

    文章整理总结java层,NFC读取和写入Tag的流程. 整体的时序图: 1.读取Tag的流程 NfcService启动完成后,会通过NfcService中的applyRouting方法设置对应的Dis ...

  2. [转载]NFC源码分析之R/W工作模式

    https://blog.csdn.net/zy00000000001/article/details/71183262 文章整理总结java层,NFC读取和写入Tag的流程. 整体的时序图: 1.读 ...

  3. NFC源码分析-ZGS

    Nfc的app代码位于: android/package/apps/Nfc/... 编译生成Nfc的apk和libnfc_nci_jni.so Nfc的协议栈和Hal相关的代码位于: system/n ...

  4. NFC源码分析之初始化流程

    Nfc的app代码位于: android/package/apps/Nfc/... 编译生成Nfc的apk和libnfc_nci_jni.so Nfc的协议栈和Hal相关的代码位于: system/n ...

  5. android源码分析

    01_Android系统概述 02_Android系统的开发综述 03_Android的Linux内核与驱动程序 04_Android的底层库和程序 05_Android的JAVA虚拟机和JAVA环境 ...

  6. wpa_supplicant 源码分析 --conf 配置文件

    原文:wpa_supplicant源码分析--conf配置文件 | Winddoing's Notes 解析 wpa_supplicant 的配置文件,一般叫做 wpa_supplicant.conf ...

  7. 【Golang源码分析】Go Web常用程序包gorilla/mux的使用与源码简析

    目录[阅读时间:约10分钟] 一.概述 二.对比: gorilla/mux与net/http DefaultServeMux 三.简单使用 四.源码简析 1.NewRouter函数 2.HandleF ...

  8. SpringBoot-web开发(四): SpringMVC的拓展、接管(源码分析)

    [SpringBoot-web系列]前文: SpringBoot-web开发(一): 静态资源的导入(源码分析) SpringBoot-web开发(二): 页面和图标定制(源码分析) SpringBo ...

  9. SpringBoot-web开发(二): 页面和图标定制(源码分析)

    [SpringBoot-web系列]前文: SpringBoot-web开发(一): 静态资源的导入(源码分析) 目录 一.首页 1. 源码分析 2. 访问首页测试 二.动态页面 1. 动态资源目录t ...

最新文章

  1. cvBlob 作为静态库链接问题
  2. struts2拦截器遇到的问题
  3. error: Your local changes to the following files would be overwritten by merge
  4. Tool之Bazel:Bazel的简介、安装、使用方法之详细攻略
  5. 使用PIL库将一张小图贴到大图的指定位置
  6. Android广播的学习和使用
  7. shell 脚本,将/etc/目录下所有的软链接文件输出
  8. 【智能车Code review】——坡道图像与控制处理
  9. 使用Python+Tensorflow的CNN技术快速识别验证码
  10. STM32F103_SPI读写外部FLASH
  11. Win_Server_2003-2016_加密勒索事件必打补丁合集
  12. AR.js摄像头前置的问题(已解决)(H5调用摄像头)
  13. python小学口算题库生成器_PrimarySchoolMathematics
  14. Landsat数据下载+USGS
  15. win10在电脑中添加新用户
  16. BISTML-CRF项目源码
  17. 生活中人们通过计算机网络进行信息交流,浅谈网络计算机应用
  18. 计算机创建只读用户,如何把电脑的文件夹在局域网共享成只读,别人不能删除和修改?...
  19. R语言学习之科学计算——求导与积分
  20. html5单片机,3.4 单片机中三极管的应用

热门文章

  1. LCD1602与51单片机
  2. 光纤光信号闪红灯_光纤猫光信号亮红灯,怎么办?
  3. 数字集成电路基础入门及应用
  4. c语言mfc写贪吃蛇,手把手教你用MFC编写贪吃蛇.doc
  5. [斯特林数][自然数幂和]JZOJ 4220 WYF的盒子
  6. 声音领域的算法库一般有librosa、essentia、torchaudio、深度学习等
  7. 新东方手机摄影大赛自动投票脚本
  8. 573_玩客云安装qbittorrent
  9. 单独编译某个驱动模块
  10. Spring工具包下载