文章只对Nfc的P2P模式的java层的代码进行了分析,native层的代码还不熟悉.直接从P2P模式
的JNI层回调开始分析。
整体时序图:
server端时序图:
当有可被用作P2P模式的NFC设备被发现后,经过底层的一系列处理传输到JNI然后回调到:
NativeNfcManager.java当中的notifyLlcpLinkActivation()。
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处理完返回来的是什么了。注意和TagEndpoint的区别,
5
* TagEndpoint是代表远端的Tag.
6
*/
7
@Override
8
public void onLlcpLinkDeactivated(NfcDepEndpoint device) {
9
sendMessage(NfcService.MSG_LLCP_LINK_DEACTIVATED, device);
10
}
1
void sendMessage(int what, Object obj) {
2
Message msg = mHandler.obtainMessage();
3
msg.what = what;
4
msg.obj = obj;
5
mHandler.sendMessage(msg);
6
}
对应到mHandler中的
1
case MSG_LLCP_LINK_ACTIVATION:
2
......
3
llcpActivated((NfcDepEndpoint) msg.obj);
4
break;
先介绍一些基本的只知识
远程的P2P设备会有两种模式:
1)、Target:当前手机往外发送数据的时候,远程的设备就是Target,自己就是Initiator
2)、Initiator:当前手机接受数据的时候,远程的设备就是Initiator,自己就是Target.
在上层就体现在NfcDepEndpoint.getMode()的不同.并且他们的基本流程都一致,差异点是在
Target端会先执行connect(),调用到NativeP2pDevice中的native方法doConnect()进行数据链路层的连
接保证底层已经是发现对方并且是可以连接的。
再然后简单了解一下NativeP2pDevice.java类的实例化,和NativeNfcTag实例化类似,在
NativeNfcManager.cpp当中
1
const char* gNativeP2pDeviceClassName = "com/android/nfc/dhimpl/NativeP2pDevice";
2
static struct nfc_jni_native_data* nat = NULL;
3
static jboolean nfcManager_initNativeStruc (JNIEnv* e, jobject o)
4
{
5
......
6
if (nfc_jni_cache_object(e, gNativeP2pDeviceClassName, &(nat->cached_P2pDevice))
7
== -1)
8
{
9
ALOGE("%s: fail cache NativeP2pDevice", __func__);
10
return JNI_FALSE;
11
}
12
......
13
}
把NativeP2pDevice类对应到了nat->cached_P2pDevice这个变量,然后在PeerToPeer.cpp中如
下方法完成了NativeP2pDevice的实例化和变量的设置,而且每次检测到P2P设备都会进行一次实例
化,就算第一次检测到了,然后点击传输的时候,第二次也会再实例化一个.(感觉就是出现在范围内
就一次)
1
/*******************************************************************************
2
**
3
** Function: llcpActivatedHandler
4
**
5
** Description: Receive LLLCP-activated event from stack.
6
** nat: JVM-related data.
7
** activated: Event data.
8
**
9
** Returns: None
10
**
11
*******************************************************************************/
12
void PeerToPeer::llcpActivatedHandler (nfc_jni_native_data* nat, tNFA_LLCP_ACTIVATED&
13
activated)
14
{
15
......
16
ALOGV("%s: get object class", fn);
17
ScopedLocalRef<jclass> tag_cls(e, e->GetObjectClass(nat->cached_P2pDevice));
18
......
19
//初始化变量
20
ALOGV("%s: instantiate", fn);
21
/* New target instance */
22
jmethodID ctor = e->GetMethodID(tag_cls.get(), "<init>", "()V");
23
ScopedLocalRef<jobject> tag(e, e->NewObject(tag_cls.get(), ctor));
24
25
/* Set P2P Target mode */
26
jfieldID f = e->GetFieldID(tag_cls.get(), "mMode", "I");
27
28
//如下有个疑问,亲测当使用两个手机的时候,无论是在发送方还是接收方,Log打印都是initiator.
29
//无论是大数据传输图片还是传输小数据联系人等.????????这是为什么那?
30
//发现偶尔,第一次的时候接收方可能回事target,其它都是initiator
31
if (activated.is_initiator == true) {
32
ALOGV("%s: p2p initiator", fn);
33
e->SetIntField(tag.get(), f, (jint) MODE_P2P_INITIATOR);
34
} else {
35
ALOGV("%s: p2p target", fn);
36
e->SetIntField(tag.get(), f, (jint) MODE_P2P_TARGET);
37
}
38
/* Set LLCP version */
39
f = e->GetFieldID(tag_cls.get(), "mLlcpVersion", "B");
40
e->SetByteField(tag.get(), f, (jbyte) activated.remote_version);
41
42
/* Set tag handle */
43
//这个handle也是每次都是同一个值,无论是接受还是发送,无论是不是同一个资源。
44
//而且重启手机再打印Log还是一样,真是很神奇!
45
f = e->GetFieldID(tag_cls.get(), "mHandle", "I");
46
e->SetIntField(tag.get(), f, (jint) 0x1234); //
47
48
if (nat->tag != NULL) {
49
e->DeleteGlobalRef(nat->tag);
50
}
51
nat->tag = e->NewGlobalRef(tag.get());
52
53
ALOGV("%s: notify nfc service", fn);
54
55
//调用回调!
56
/* Notify manager that new a P2P device was found */
57
e->CallVoidMethod(nat->manager,
58
android::gCachedNfcManagerNotifyLlcpLinkActivation, tag.get());
59
......
60
61
ALOGV("%s: exit", fn);
62
}
接下来开始正式分析P2P的流程,从llcpActivated()开始:
1
private boolean llcpActivated(NfcDepEndpoint device) {
2
Log.d(TAG, "LLCP Activation message");
3
//当远程p2p是Target端的时候,一定要注意,此时的Mode指的是远端的用于和当前设备进行P2P操作
4
//的设备。
5
if (device.getMode() == NfcDepEndpoint.MODE_P2P_TARGET) {
6
if (device.connect()) { //最终是NfcDepEndpoint中的native方法doConnect();
7
/* Check LLCP compliance */
8
if (mDeviceHost.doCheckLlcp()) {//doCheckLlcp是NativeNfcManager中Native方法
9
/* Activate LLCP Link */
10
if (mDeviceHost.doActivateLlcp()) {//doActivateLlcp也是
11
//NativeNfcManager中的Native方法
12
synchronized (NfcService.this) {
13
//当放到集合当中,注意这个集合也存放了,NativeNfcTag对象,也就是读
14
//取Tag时候实例化的那个
15
//fields below are used in multiple threads and protected by
16
//synchronized(this)
17
//final HashMap<Integer, Object> mObjectMap = new
18
//HashMap<Integer, Object>();
19
//暂时测试同一个手机下如前面所说handle一直是一个值,这样的话也就是
20
//说,P2P的Device里面只会存一个
21
mObjectMap.put(device.getHandle(), device);
22
}
23
//底层按照llcp协议建立完链路连接的时候调用如下方法。
24
mP2pLinkManager.onLlcpActivated(device.getLlcpVersion());
25
return true;
26
} else {device.disconnect();//断开链接}
27
} else {
28
//断开连接
29
if (DBG) Log.d(TAG, "Remote Target does not support LLCP.
30
Disconnect.");
31
device.disconnect();//最终是NfcDepEndpoint中的native方法doDisconnect();
32
}
33
} else {//不能连接远程的target,然后去执行发送数据.}
34
//当是远程的P2P是发起者initiator的时候.
35
} else if (device.getMode() == NfcDepEndpoint.MODE_P2P_INITIATOR) {
36
//可以看到对比MODE_P2P_TARGET就是少了一个device.connect().
37
/* Check LLCP compliancy */
38
if (mDeviceHost.doCheckLlcp()) {
39
/* Activate LLCP Link */
40
if (mDeviceHost.doActivateLlcp()) {
41
if (DBG) Log.d(TAG, "Target Activate LLCP OK");
42
synchronized (NfcService.this) {
43
// Register P2P device
44
mObjectMap.put(device.getHandle(), device);
45
}
46
mP2pLinkManager.onLlcpActivated(device.getLlcpVersion());
47
return true;
48
}
49
} else { Log.w(TAG, "checkLlcp failed");}
50
}
51
return false;
52
}
如上无论远端是Target还是initaitor,最后都调用了,P2pLinkManager中的onLlcpActivated。
至此,native层的llcp链路建立成功并且也通过了check,我们接下来只需要按照传输协议建立socket
传递数据即可!
另外native层执行完链路连接以后会回调很多状态给上层,在P2pLinkManager类当中有如下三个
回调:
1
public void onLlcpFirstPacketReceived()
2
public void onLlcpDeactivated()
3
void onSendComplete(NdefMessage msg, long elapsedRealtime)
进入到P2PLinkManager当中
1
public void onLlcpActivated(byte peerLlcpVersion) {
2
Log.i(TAG, "LLCP activated");
3
synchronized (P2pLinkManager.this) {
4
......
5
mLastLlcpActivationTime = SystemClock.elapsedRealtime();
6
mPeerLlcpVersion = peerLlcpVersion;
7
//分别对不同的链接状态进行处理,此时对应的是在P2pLinkManager的构造中初始化为
8
//LINK_STATE_DOWN.
9
//注意第一次tap,然后两个手机超出范围(会进行Deactivated相关回调,后面说),再次tap的
10
//时候mLinkState会变成LINK_STATE_DEBOUNCE.(有前面Deactivated设置的)
11
switch (mLinkState) {
12
//所以刚开始的时候是进入到这里。
13
case LINK_STATE_DOWN:
14
//加上了一些判断,保证现在是要进行P2P的状态。
15
//NFC的整个P2P的流程中会有很多的类似的状态判断,因为P2P的链接随时可能中断,
16
//毕竟要我们拿着手机相互靠近,一不小心就离开了
17
if (!mEventListener.isP2pIdle()
18
&& mSendState != SEND_STATE_PENDING) {
19
break;
20
}
21
if (DBG) Log.d(TAG, "onP2pInRange()");
22
//这个方法内就会得到一个当前屏幕截图的bitmap对象,但在这还不会弹出提示的界面.
23
mEventListener.onP2pInRange();
24
//更改链接状态。
25
mLinkState = LINK_STATE_UP;
26
//mSendState状态在构造中初始化,为SEND_STATE_NOTHING_TO_SEND.所以走到else
27
//第一次tap获取完毕屏幕截图的时候,第二次tap时候会处于SEND_STATE_PENDING.
28
if (mSendState == SEND_STATE_PENDING) {
29
......
30
} else {
31
mSendState = SEND_STATE_NOTHING_TO_SEND;
32
//按照app传入的各个要发送的数据进行
33
prepareMessageToSend(true);
34
//判断是否有数据要往外发,有数据往外发的时候才会进入。
35
//此时当是接收方的时候这会儿就不会做任何处理,因为都是null。此时会作为
36
//server端,等待client的链接
37
//mMessageToSend != null,代表传输小数据,直接发送即可。
38
//mUrisToSend != null,代表要传输大数据,需要用到BT,进行Handover.
39
if (mMessageToSend != null ||
40
(mUrisToSend != null &&
41
mHandoverDataParser.isHandoverSupported())) {
42
//如下是一个异步的处理方式,因此会直接往往下走
43
connectLlcpServices();
44
//上面connectLlcpServices还未异步执行完毕的时候,这块肯定就赋值了
45
//而对于一般的测试,发现发送方的发送,走到这里的时候都会走到else,需
46
//要进行确认.此时mSendState就变成SEND_STATE_NEED_CONFIRMATION了
47
if ((mSendFlags & NfcAdapter.FLAG_NDEF_PUSH_NO_CONFIRM) != 0)
48
{
49
//标记不需要确认直接发送
50
mSendState = SEND_STATE_SENDING;
51
} else {
52
//标记需要在UI上确认的才能P2p
53
mSendState = SEND_STATE_NEED_CONFIRMATION;
54
}
55
}
56
}
57
break;
58
case LINK_STATE_UP:
59
if (DBG) Log.d(TAG, "zhaoyuan Duplicate onLlcpActivated()");
60
return;
61
//正常传输东西的时候的第二次Tap,会走到这里
62
case LINK_STATE_DEBOUNCE:
63
// Immediately connect and try to send again
64
Log.d(TAG, "zhaoyuan LINK_STATE_DEBOUNCE");
65
mLinkState = LINK_STATE_UP;
66
if (mSendState == SEND_STATE_SENDING ||
67
mSendState == SEND_STATE_NEED_CONFIRMATION) {
68
// If we have something to send still, connect LLCP
69
//此时需要传输东西走到这里.
70
connectLlcpServices();
71
}
72
mHandler.removeMessages(MSG_DEBOUNCE_TIMEOUT);
73
break;
74
}
75
}
76
}
我们一步步看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
public class P2pEventManager implements P2pEventListener, SendUi.Callback {
2
......
3
public P2pEventManager(Context context, P2pEventListener.Callback callback) {
4
mNfcService = NfcService.getInstance();
5
mContext = context;
6
//mCallback定义在P2pEventListener中,位于P2pLinkManager.java当中
7
mCallback = callback;
8
......
9
10
mSending = false;
11
final int uiModeType = mContext.getResources().getConfiguration().uiMode
12
& Configuration.UI_MODE_TYPE_MASK;
13
//不懂下面的这个是什么意思!,抓取Log发现正常初始化的时候为UI_MODE_TYPE_NORMAL.
14
if (uiModeType == Configuration.UI_MODE_TYPE_APPLIANCE) {
15
// "Appliances" don't intrinsically have a way of confirming this, so we
16
// don't use the UI and just autoconfirm where necessary.
17
// Don't instantiate SendUi or else we'll use memory and never reclaim it.
18
mSendUi = null;
19
} else {
20
//this的类型是SendUi.Callback也可以看到:P2pEventManager implements
21
//P2pEventListener, SendUi.Callback
22
mSendUi = new SendUi(context, this);
23
}
24
}
25
@Override
26
public void onP2pInRange() {
27
mNdefSent = false;
28
mNdefReceived = false;
29
mInDebounce = false;
30
//屏幕处于亮屏且解锁的状态的时候,发送声音,震动.
31
if (mNfcService.mScreenState == ScreenStateHelper.SCREEN_STATE_ON_UNLOCKED) {
32
mNfcService.playSound(NfcService.SOUND_START);
33
mVibrator.vibrate(VIBRATION_PATTERN, -1);
34
}
35
if (mSendUi != null) {
36
//SendUi就是P2P过程中负责和界面交互的类。
37
mSendUi.takeScreenshot();
38
}
39
}
40
}
对于SendUi实例化时候传入的this,构定义如下,注意命名此处就是SendUi的完成发送和取消发
送的回调,定于SendUi当中,而实现于P2pEventManager.
接着到SenUi中:
1
public class SendUi implements Animator.AnimatorListener, View.OnTouchListener,
2
TimeAnimator.TimeListener, TextureView.SurfaceTextureListener,
3
android.view.Window.Callback {
4
5
public interface Callback {
6
public void onSendConfirmed();//确认发送
7
public void onCanceled();//取消发送
8
}
9
......
10
public void takeScreenshot() {
11
...
12
//更新当前状态
13
mState = STATE_W4_SCREENSHOT;
14
//去异步的获取一个屏幕截图.
15
new ScreenshotTask().execute();
16
}
17
......
18
final class ScreenshotTask extends AsyncTask<Void, Void, Bitmap> {
19
@Override
20
protected Bitmap doInBackground(Void... params) {
21
return createScreenshot();
22
}
23
@Override
24
protected void onPostExecute(Bitmap result) {
25
//在构造中mState = STATE_IDLE;
26
//mState在前面takeScreenshot设置的是STATE_W4_SCREENSHOT
27
if (mState == STATE_W4_SCREENSHOT) {
28
//生成的截图设置到mScreenshotBitmap,这个在后面显示用户确认操作时候用.
29
// Screenshot done, wait for request to start preSend anim
30
mScreenshotBitmap = result;
31
//并且把mState变成将要发送的状态
32
mState = STATE_W4_PRESEND;
33
} else if (mState == STATE_W4_SCREENSHOT_THEN_STOP) {
34
// We were asked to finish, move to idle state and exit
35
mState = STATE_IDLE;
36
} else if (mState == STATE_W4_SCREENSHOT_PRESEND_REQUESTED ||
37
mState == STATE_W4_SCREENSHOT_PRESEND_NFC_TAP_REQUESTED) {
38
if (result != null) {
39
mScreenshotBitmap = result;
40
boolean requestTap =
41
(mState == STATE_W4_SCREENSHOT_PRESEND_NFC_TAP_REQUESTED);
42
mState = STATE_W4_PRESEND;
43
showPreSend(requestTap);
44
} else {
45
// Failed to take screenshot; reset state to idle
46
// and don't do anything
47
Log.e(TAG, "Failed to create screenshot");
48
mState = STATE_IDLE;
49
}
50
} else {
51
Log.e(TAG, "Invalid state on screenshot completion: " +
52
Integer.toString(mState));
53
}
54
}
55
};
56
57
/**
58
* 此处旨在理解流程,具体的画图算大小省略,这个方法会返回当前界面的一个截图.
59
*/
60
Bitmap createScreenshot() {
61
......
62
//根据不同屏幕进行适配
63
final int navBarHeight = ...;
64
final int navBarHeightLandscape = ...;
65
final int navBarWidth = ...;
66
......
67
Bitmap bitmap = SurfaceControl.screenshot((int) dims[0], (int) dims[1]);
68
......
69
int newLeft = ...;
70
int newTop = ...;
71
int newWidth = ...;
72
int newHeight = ...;
73
float smallestWidth = ...;
74
float smallestWidthDp = ...;
75
if (bitmap.getWidth() < bitmap.getHeight()) {
76
newHeight = ...;
77
} else {
78
if (smallestWidthDp > 599) {
79
newHeight = ...;
80
} else {
81
newHeight = ...;
82
newWidth = ...;
83
}
84
}
85
bitmap = Bitmap.createBitmap(bitmap, newLeft, newTop, newWidth, newHeight);
86
return bitmap;
87
}
88
}
到此其实也只是获取到了一个用屏幕的截图绘制的bitmap,把它设置给了mScreenshotBitmap,
并不会显示什么界面,然后回到P2PLinkManager中的onLlcpActivated 接这前面的往下看先是:
1
mLinkState = LINK_STATE_UP;而对于mSendState的状态,在构造中初始化的时候为
2
mSendState = SEND_STATE_NOTHING_TO_SEND;所以刚开始会走到else的分支。
//依据第三方要传输消息的APP设置的信息,准备发送的信息.
//主要是把最终解析出来的数据放到mMessageToSend和mUrisToSend中
//ndef的小数据一般用mMessageToSend存储,如:Contact、Email、等
//picture、video等一般会放在mUrisToSend中
1
void prepareMessageToSend(boolean generatePlayLink) {
2
synchronized (P2pLinkManager.this) {
3
mMessageToSend = null;
4
mUrisToSend = null;
5
//由在构造中的时候默认是false,但是在NfcService一系列附加的行为启动完毕后
6
//调用到P2pLinkManager中enableDisable,会把它赋值为true.
7
if (!mIsSendEnabled) {
8
return;
9
}
10
//检测前台应用是否有活动的activity,前台程序就是指,你打开比如联系人、图库等注意是
11
//可分享的activity界面你打开后,选中某个图片,一轻触就开始走流程了,否则的话没啥反应.
12
if (foregroundUids.isEmpty()) {
13
Log.e(TAG, "Could not determine foreground UID.");
14
return;
15
}
16
//检测到如前台程序是否可以使用Beam功能。
17
if (isBeamDisabled(foregroundUids.get(0))) {
18
if (DBG) Log.d(TAG, "Beam is disabled by policy.");
19
return;
20
}
21
//mCallbackNdef是IAppCallback接口的实现,通过调用NfcAdapterService的接口
22
//setAppCallback进行设置的,一般无论是第三方app还是系统的联系人、图库等发送数据
23
//的时候都会设置.但由于系统里面很多应用,可能别的应用已经注册,观察发现发送方和
24
//接收方此处都是mCallbackNdef != null,但是foregroundUids可能没有包含当前的uid
25
if (mCallbackNdef != null) {
26
//如果是自己添加的前台进程的话,走下面的流程,发送方和接收方次处就会不同.
27
if (foregroundUids.contains(mNdefCallbackUid)) {
28
try {
29
//createBeamShareData在framework中有实现,在NfcActivityManager中实现
30
//此处还需要深入的再看看怎么确定beam的数据的,(??????)
31
//通过Log发现:
32
//图片资源的时候 mMessageToSend = null
33
// mUrisToSend = [Landroid.net.Uri;@473547e
34
// mUserHandle = UserHandle{0}
35
// mSendFlags = 0
36
//*******************************
37
// 联系人资源的时候 mMessageToSend = NdefMessage
38
// [NdefRecord tnf=2 type=746578742F782D7663617264
39
// payload=424547494E3A56434152
40
// 440D0A56455253494F4E3A322E310D0A4E3A3B5A686
41
// 16F7975616E3B3B3B0D0A464E3A5A68616F7975616E
42
// 200D0A54454C3B43454C4C3A3133363938353233363
43
// 9380D0A454E443A56434152440D0A]
44
// mUrisToSend = null.
45
// mUserHandle = UserHandle{0}
46
// mSendFlags = 0
47
//总的来说是由createBeamShareData内部实现的区分!
48
BeamShareData shareData =
49
mCallbackNdef.createBeamShareData(mPeerLlcpVersion);
50
mMessageToSend = shareData.ndefMessage;
51
mUrisToSend = shareData.uris;
52
mUserHandle = shareData.userHandle;
53
mSendFlags = shareData.flags;
54
return;
55
} ...
56
} else {
57
//当如图库退出前台的时候,可能它的回调并没有清除。
58
//你想作为Socket接收端,就会到当前判断不会去专门打开指定的应用界面去发送东西
59
if (DBG) Log.d(TAG, "Last registered callback is not running in the
60
foreground.");
61
}
62
}
63
//当没有设置回调,就调用系统默认的,将当前Pkg的信息打包发送到上层。
64
List<Integer> foregroundPids =
65
mForegroundUtils.getForegroundPids(foregroundUids.get(0));
66
......
67
//找到正在运行的pkg
68
String pkgName = null;
69
ActivityManager manager =
70
(ActivityManager)mContext.getSystemService(Context.ACTIVITY_SERVICE);
71
List<RunningAppProcessInfo> appList = manager.getRunningAppProcesses();
72
for (RunningAppProcessInfo info : appList) {
73
for (Integer pid : foregroundPids) {
74
if (pid.intValue() == info.pid) {
75
if (DBG) Log.d(TAG, "PID:" + info.pid + " Name:" +
76
info.processName);
77
pkgName = info.processName;
78
break;
79
}
80
}
81
if (pkgName != null) {
82
break;
83
}
84
}
85
//正常情况下都会找到一个正在运行的前台应用,如你是在home界面的时候
86
//Log:com.sonymobile.home 会被发现.
87
if (pkgName != null) {
88
if (!generatePlayLink || beamDefaultDisabled(pkgName)) {
89
if (DBG) Log.d(TAG, "Disabling default Beam behavior");
90
mMessageToSend = null;
91
mUrisToSend = null;
92
} else {
93
mMessageToSend = createDefaultNdef(pkgName);
94
mUrisToSend = null;
95
mSendFlags = 0;
96
}
97
}
98
//作为接收端的时候此处就会为null,不会发送任何数据.
99
if (DBG) Log.d(TAG, "mMessageToSend = " + mMessageToSend);
100
if (DBG) Log.d(TAG, "mUrisToSend = " + mUrisToSend);
101
}
102
}
再回到前面P2pLinkManager中的onLlcpActivated当中,继续往下看,此时作为接收方没有数据
发送的时候流程就到这结束了,作为发送方那么必然有一个不为null,此时调connectLlcpServices();
同样目前还是位于P2PLinkManager中
1
void connectLlcpServices() {
2
synchronized (P2pLinkManager.this) {
3
......
4
mConnectTask = new ConnectTask();
5
mConnectTask.execute();
6
}
7
}
8
final class ConnectTask extends AsyncTask<Void, Void, Boolean> {
9
10
//执行完对应的client的链接以后,会走到这里.
11
@Override
12
protected void onPostExecute(Boolean result) {
13
......
14
//如果成功connect到指定的server socket.
15
if (result) {
16
onLlcpServicesConnected();
17
} else {
18
Log.e(TAG, "Could not connect required NFC transports");
19
}
20
}
21
22
@Override
23
protected Boolean doInBackground(Void... params) {
24
//初始化一些参数,用来标记使用那种协议来解析数据.
25
boolean needsHandover = false;
26
boolean needsNdef = false;
27
boolean success = false;
28
//如下三个分别去链接接收方的对应的server,至于相关的server的初始化,在别的博客有说明.
29
//这些客户端的socket创建也是按照llcp协议的要求来设置一些属性如访问节点等.
30
HandoverClient handoverClient = null;
31
SnepClient snepClient = null;
32
NdefPushClient nppClient = null;
33
34
synchronized(P2pLinkManager.this) {
35
if (mUrisToSend != null) {
36
//进入到此处的时候就会用bt进行传输
37
needsHandover = true;
38
}
39
if (mMessageToSend != null) {
40
//日历、联系人这一类的小数据
41
needsNdef = true;
42
}
43
}
44
// We know either is requested - otherwise this task
45
// wouldn't have been started.
46
if (needsHandover) {
47
//此处实例化客户端的HandoverClient,每一个tap都会进行
48
handoverClient = new HandoverClient();
49
try {
50
//此时就会去链接远端,接收方手机的HandoverServer.
51
//调用connect,最终会调用到:
52
//LlcpSocket当中connectToService(HandoverServer.
53
//HANDOVER_SERVICE_NAME);
54
//LlcpSocket是由NativeLlcpSocket实现,代表一个llcp的面向连接中的客户端。
55
//连接它对应的服务端的Socket.在别的博客有介绍.后面也会去介绍server端的处理
56
handoverClient.connect();
57
success = true; // Regardless of NDEF result
58
} ...
59
}
60
//当需要用ndef发送的时候
61
if (needsNdef || (needsHandover && handoverClient == null)){
62
if(NfcService.sIsDtaMode) {
63
//不懂sIsDtaMode模式代表什么,好像和运营商的测试有关?
64
......
65
}else{
66
//可以看到对于Ndef的数据,先使用SnepClient进行链接.
67
snepClient = new SnepClient();
68
}
69
try{
70
if(NfcService.sIsDtaMode) {
71
......
72
}else{
73
snepClient.connect();
74
}
75
success = true;
76
mDtaSnepClient = null;
77
}...
78
//当SnepClient链接不成功的时候,在使用NdefPushClient进行链接
79
//此时就是看对方的手机支持不支持SnepClient,Android版本过老的话,就可能不支持
80
//目前测得手机都是支持的.
81
if (!success) {
82
nppClient = new NdefPushClient();
83
try {
84
nppClient.connect();
85
success = true;
86
}...
87
}
88
}
89
synchronized (P2pLinkManager.this) {
90
//当ConnectTask被cancel的时候
91
if (isCancelled()) {
92
// Cancelled by onLlcpDeactivated on UI thread
93
if (handoverClient != null) {
94
handoverClient.close();
95
}
96
if (snepClient != null) {
97
snepClient.close();
98
}
99
if (nppClient != null) {
100
nppClient.close();
101
}
102
if(mDtaSnepClient != null) {
103
mDtaSnepClient.close();
104
}
105
return false;
106
//当ConnectTask没有cancle的时候
107
} else {
108
//......
109
mHandoverClient = handoverClient;
110
mSnepClient = snepClient;
111
mNdefPushClient = nppClient;
112
return success;
113
}
114
}
115
}
116
};
根据传输的格式来,来实例化对应的链接如:HandoverClient、SnepClient、NdefPushClient连
接成功后返回true至此socket的客户端启动完毕,并与server端建立起了链接,但是还未往外发送数
据,回到onLlcpActivated()当中,我们刚在else中执行了connectLlcpServices(),再往下的分析看前
面对应的地方,然后回到现在.链接成功后执行
1
void onLlcpServicesConnected() {
2
synchronized (P2pLinkManager.this) {
3
//先检查连接状态,只有处于LINK_STATE_UP才可以进行如下操作
4
if (mLinkState != LINK_STATE_UP) {
5
return;
6
}
7
mLlcpServicesConnected = true;
8
//此时有两种情况,进入到需要传输数据的页面,第一次tap的时候,就是我们前面分析的流程
9
//此时mSendState在前面的onLlcpActivated异步设置了是SEND_STATE_NEED_CONFIRMATION.
10
//第二种情况是在已经确认后走到这里时,此时的mSendState,通过一些列回调设置成了
11
//SEND_STATE_SENDING,后面也会有关于这个的分析.
12
if (mSendState == SEND_STATE_NEED_CONFIRMATION) {
13
//需要用户手动确认的,就是那个"Tap your screen to beam"的传输提示.
14
if (DBG) Log.d(TAG, "onP2pSendConfirmationRequested()");
15
mEventListener.onP2pSendConfirmationRequested();
16
} else if (mSendState == SEND_STATE_SENDING) {
17
//不需要确认直接发送
18
mEventListener.onP2pResumeSend();
19
sendNdefMessage();
20
} else {
21
// Either nothing to send or canceled/complete, ignore
22
}
23
}
24
}
注意此时还未显示出来用户提示的界面,(缩小屏幕,提示你轻触使用beam进行传送)。下面继
续我们的跟踪(此时是第一次tap哦),又到P2pEventManager中onP2pSendConfirmationRequested中
1
@Override
2
public void onP2pSendConfirmationRequested() {
3
//播放声音和震动.
4
mNfcService.playSound(NfcService.SOUND_START);
5
mVibrator.vibrate(VIBRATION_PATTERN, -1);
6
//SendUi我们前面已经说过.
7
if (mSendUi != null) {
8
//注意传入的参数是false.
9
mSendUi.showPreSend(false);
10
} else {
11
//直接不需要弹出确认页面,进行确认逻辑.
12
mCallback.onP2pSendConfirmed();
13
}
14
}
分析确认send的画面,也就是showPreSend(),这个最终也会调用到onP2pSendConfirmed().
1
public void showPreSend(boolean promptToNfcTap) {
2
......
3
//mScreenshotLayout是个View对象用来承载预先显示的这个画面,里面会放入一系列的组件
4
//此时为它设置ontouch
5
mScreenshotLayout.setOnTouchListener(this);
6
//mScreenshotBitmap这就是前面在范围内的时候onP2pInRange截图生成的bitmap,
7
mScreenshotView.setImageBitmap(mScreenshotBitmap);
8
......
9
//由于promptToNfcTap是false,所以STATE_W4_TOUCH.
10
//接下来你点击屏幕的时候(中间没有异常情况)
11
mState = promptToNfcTap ? STATE_W4_NFC_TAP : STATE_W4_TOUCH;
12
}
到现在为止才会显示出来整体的用户提示界面如下:
原图如下:打算往外发的数据
检测到远程的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去弹"Bring devices together again"
9
//此时是在SendUI,因此mCallback对应的是P2pEventManager.
10
mCallback.onSendConfirmed();
11
return true;
12
}
点击以后才会出现如下图:
此时是在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
//实现是在P2pLinkManager,(都快晕菜了-.-!)
10
mCallback.onP2pSendConfirmed();
11
}
12
mSending = true;//标记处于发送当中
13
}
我们去P2pLinkManager当中.
1
@Override
2
public void onP2pSendConfirmed() {
3
onP2pSendConfirmed(true);
4
}
5
private void onP2pSendConfirmed(boolean requireConfirmation) {
6
if (DBG) Log.d(TAG, "onP2pSendConfirmed()");
7
synchronized (this) {
8
//在一开始的onLlcpActivated当中设置如下
9
//mSendState = SEND_STATE_NEED_CONFIRMATION;
10
//mLinkState也在前面的onLlcpActivated当中被被设置为LINK_STATE_UP,
11
//但是当我们离开P2P的范围的时候,此时有个回调会被执行,然后:
12
//mLinkState会被设置为LINK_STATE_DEBOUNCE.
13
if (mLinkState == LINK_STATE_DOWN || (requireConfirmation
14
&& mSendState != SEND_STATE_NEED_CONFIRMATION)) {
15
return;
16
}
17
//这个很重要把发送状态置为正在发送.
18
mSendState = SEND_STATE_SENDING;
19
//当是第一次tap的时候,两个手机没有离开P2P距离,会走到这里最。
20
if (mLinkState == LINK_STATE_UP) {
21
if (mLlcpServicesConnected) {
22
sendNdefMessage();
23
} // else, will send messages when link comes up
24
//第一次tap的时候,中间两个手机有离开P2P范围那么会走到这里
25
} else if (mLinkState == LINK_STATE_DEBOUNCE) {
26
scheduleTimeoutLocked(MSG_DEBOUNCE_TIMEOUT,
27
LINK_SEND_CONFIRMED_DEBOUNCE_MS);
28
mEventListener.onP2pSendDebounce();
29
}
30
}
31
}
32
33
//onP2pSendDebounce源码,位于P2pEventManager当中
34
@Override
35
public void onP2pSendDebounce() {
36
mInDebounce = true;
37
mNfcService.playSound(NfcService.SOUND_ERROR);
38
if (mSendUi != null) {
39
mSendUi.showSendHint();//显示"Bring devices together again"
40
}
41
}
42
43
//接下来应该是分析,一个是断开连接的回调,一个是第二次的tap!!
44
45
下面我们先分析一下tap完毕,你离开P2P范围的时候的回调,直接从JNI的回调看起
46
//在NativeNfcManager中,mListener是DeviceHostListener,对应的就是NfcService.
47
/**
48
* Notifies P2P Device detected, to activate LLCP link
49
*/
50
private void notifyLlcpLinkDeactivated(NativeP2pDevice device) {
51
mListener.onLlcpLinkDeactivated(device);
52
}
53
//然后到NfcService中
54
/**
55
* Notifies P2P Device detected, to activate LLCP link
56
*/
57
@Override
58
public void onLlcpLinkDeactivated(NfcDepEndpoint device) {
59
sendMessage(NfcService.MSG_LLCP_LINK_DEACTIVATED, device);
60
}
61
case MSG_LLCP_LINK_DEACTIVATED:
62
//不晓得下面的mIsDebugBuild用意何在
63
if (mIsDebugBuild) {
64
Intent deactIntent = new Intent(ACTION_LLCP_DOWN);
65
mContext.sendBroadcast(deactIntent);
66
}
67
NfcDepEndpoint device = (NfcDepEndpoint) msg.obj;
68
boolean needsDisconnect = false;
69
Log.d(TAG, "LLCP Link Deactivated message. Restart polling loop.");
70
synchronized (NfcService.this) {
71
/* Check if the device has been already unregistered */
72
if (mObjectMap.remove(device.getHandle()) != null) {
73
/* Disconnect if we are initiator */
74
if (device.getMode() == NfcDepEndpoint.MODE_P2P_TARGET) {
75
if (DBG) Log.d(TAG, "disconnecting from target");
76
needsDisconnect = true;
77
} else {
78
if (DBG) Log.d(TAG, "not disconnecting from initiator");
79
}
80
}
81
}
82
if (needsDisconnect) {
83
device.disconnect(); // restarts polling loop
84
}
85
mP2pLinkManager.onLlcpDeactivated();
86
break;
//到P2pLinkManager当中
1
/**
2
* Must be called on UI Thread.
3
*/
4
public void onLlcpDeactivated() {
5
Log.i(TAG, "LLCP deactivated.zhaoyuan");
6
synchronized (this) {
7
......
8
//在前面tap到,建立起链接的时候都会设置为LINK_STATE_UP状态
9
switch (mLinkState) {
10
case LINK_STATE_DOWN:
11
case LINK_STATE_DEBOUNCE:
12
Log.i(TAG, "Duplicate onLlcpDectivated()");
13
break;
14
case LINK_STATE_UP:
15
//设置为LINK_STATE_DEBOUNCE状态。
16
//mSendState 在onP2pSendConfirmed的时候设置为SEND_STATE_SENDING.
17
mLinkState = LINK_STATE_DEBOUNCE;
18
int debounceTimeout = 0;
19
switch (mSendState) {
20
case SEND_STATE_NOTHING_TO_SEND:
21
debounceTimeout = 0;
22
break;
23
case SEND_STATE_NEED_CONFIRMATION:
24
debounceTimeout = LINK_SEND_PENDING_DEBOUNCE_MS;
25
break;
26
case SEND_STATE_SENDING:
27
//int LINK_SEND_CONFIRMED_DEBOUNCE_MS = 5000;这个是5s的延时.
28
debounceTimeout = LINK_SEND_CONFIRMED_DEBOUNCE_MS;
29
break;
30
case SEND_STATE_COMPLETE:
31
debounceTimeout = LINK_SEND_COMPLETE_DEBOUNCE_MS;
32
break;
33
case SEND_STATE_CANCELED:
34
debounceTimeout = LINK_SEND_CANCELED_DEBOUNCE_MS;
35
}
36
//发送what = MSG_DEBOUNCE_TIMEOUT,延时debounceTimeout时间的一个msg.
37
scheduleTimeoutLocked(MSG_DEBOUNCE_TIMEOUT, debounceTimeout);
38
//注意当你只是tap,然后离开范围的时候,此时的mSendState为
39
//SEND_STATE_NEED_CONFIRMATION
40
//这个是在前面onLlcpServicesConnected()中设置的.
41
//而在触发ontouch,确认传送的时候,只要你再次tap,正常情况下此处会被设置
42
//mSendState为SEND_STATE_COMPLETE.后面分析第二次tap的时候详解.
43
if (mSendState == SEND_STATE_SENDING) {
44
Log.e(TAG, "onP2pSendDebounce()");
45
mEventListener.onP2pSendDebounce();
46
}
47
cancelSendNdefMessage();//mSendTask.cancel(true);
48
disconnectLlcpServices();
49
break;
50
}
51
}
52
}
53
54
//disconnectLlcpServices的源码.
55
void disconnectLlcpServices() {
56
synchronized (this) {
57
if (mConnectTask != null) {
58
mConnectTask.cancel(true);
59
mConnectTask = null;
60
}
61
// Close any already connected LLCP clients
62
if (mNdefPushClient != null) {
63
mNdefPushClient.close();
64
mNdefPushClient = null;
65
}
66
if (mSnepClient != null) {
67
mSnepClient.close();
68
mSnepClient = null;
69
}
70
if (mHandoverClient != null) {
71
mHandoverClient.close();
72
mHandoverClient = null;
73
}
74
mLlcpServicesConnected = false;
75
}
76
}
77
78
//MSG_DEBOUNCE_TIMEOUT发生时候的源码
79
case MSG_DEBOUNCE_TIMEOUT:
80
synchronized (this) {
81
if (mLinkState != LINK_STATE_DEBOUNCE) {
82
break;
83
}
84
if (DBG) Log.d(TAG, "Debounce timeout");
85
//就是把这些变量置为初始化的时候的状态
86
//这个可以保证发生timeout以后,第二次tap的时候初始化状态.
87
mLinkState = LINK_STATE_DOWN;
88
mSendState = SEND_STATE_NOTHING_TO_SEND;
89
mMessageToSend = null;
90
mUrisToSend = null;
91
if (DBG) Log.d(TAG, "onP2pOutOfRange()");
92
//结束SendUI的界面.
93
mEventListener.onP2pOutOfRange();
94
}
95
break;
96
//onP2pOutOfRange的源码
97
@Override
98
public void onP2pOutOfRange() {
99
if (mSending) {
100
mNfcService.playSound(NfcService.SOUND_ERROR);
101
mSending = false;
102
}
103
if (!mNdefSent && !mNdefReceived && mSendUi != null) {
104
mSendUi.finish(SendUi.FINISH_SCALE_UP);
105
}
106
mInDebounce = false;
107
}
有了Deactivated的回调,我们再分析一下,点击确认以后的第二次tap,此时要进行真正的传输
了。检测到P2p设备以后前面的一些列操作基本相同,然后走到P2pLinkManager中的
onLlcpActivated的时候。由于前面有点远,下面直接再次贴出相关源码
1
public void onLlcpActivated(byte peerLlcpVersion) {
2
Log.i(TAG, "LLCP activated");
3
synchronized (P2pLinkManager.this) {
4
......
5
//注意此时:已点击确认,且未发生timeout 那么:
6
//mLinkState = LINK_STATE_DEBOUNCE;
7
//mSendState = SEND_STATE_SENDING;
8
switch (mLinkState) {
9
case LINK_STATE_DOWN:
10
......
11
break;
12
case LINK_STATE_UP:
13
if (DBG) Log.d(TAG, "zhaoyuan Duplicate onLlcpActivated()");
14
return;
15
case LINK_STATE_DEBOUNCE:
16
// Immediately connect and try to send again
17
//重新把mLinkState设置为LINK_STATE_UP以便后面可以建立连接.
18
mLinkState = LINK_STATE_UP;
19
if (mSendState == SEND_STATE_SENDING ||
20
mSendState == SEND_STATE_NEED_CONFIRMATION) {
21
// If we have something to send still, connect LLCP
22
connectLlcpServices();
23
}
24
//移除timeout
25
mHandler.removeMessages(MSG_DEBOUNCE_TIMEOUT);
26
break;
27
}
28
}
29
}
关于connectLlcpServices往后的代码,我们前面已经贴出,此处直接贴出不同的地方.
到onLlcpServicesConnected以后
1
void onLlcpServicesConnected() {
2
synchronized (P2pLinkManager.this) {
3
//就是因为这个判断,前面需要重新设置状态.
4
if (mLinkState != LINK_STATE_UP) {
5
return;
6
}
7
mLlcpServicesConnected = true;
8
//此时mSendState已经变成SEND_STATE_SENDING了,所以直接执行发送.
9
if (mSendState == SEND_STATE_NEED_CONFIRMATION) {
10
......
11
} else if (mSendState == SEND_STATE_SENDING) {
12
mEventListener.onP2pResumeSend();
13
sendNdefMessage();
14
} ...
15
}
16
}
17
//sendNdefMessage源码
18
void sendNdefMessage() {
19
synchronized (this) {
20
cancelSendNdefMessage();
21
mSendTask = new SendTask();
22
//去执行SendTask的doInBackground.
23
mSendTask.execute();
24
}
25
}
SendTask相关源码.
1
final class SendTask extends AsyncTask<Void, Void, Void> {
2
NdefPushClient nppClient;
3
SnepClient snepClient;
4
HandoverClient handoverClient;
5
6
int doHandover(Uri[] uris, UserHandle userHandle) throws IOException {
7
NdefMessage response = null;
8
//获取BeamManager的实例,这个是管理Beam transfers的,保证同一时间只有一个在进行.
9
BeamManager beamManager = BeamManager.getInstance();
10
//如果有一个正在进行的handover,那么会提示正忙
11
if (beamManager.isBeamInProgress()) {
12
return HANDOVER_BUSY;
13
}
14
//HandoverDataParser类管理NFC传输到其它传输技术之间的切换。
15
//发送方按照Nfc Form的对应协议,把标准的蓝牙请求消息封装成NdefMessage。
16
//接收方收到的消息带有这些请求信息的时候就会配对了,建立连接了等.
17
//关于Handover的详细信息有别的博客详细介绍.
18
NdefMessage request = mHandoverDataParser.createHandoverRequestMessage();
19
if (request != null) {
20
if (handoverClient != null) {
21
//下面内部会调用到LlcpSocket的sock.send(tmpBuffer).
22
//它会作为socket的client端,就是客户端。(既然两个手机都开启了服务端,不会冲
23
//突到本地吗???)
24
//最终会调用到native的对应的方法.
25
//通过handoverClient往外发送前面生成NdefMessage的请求,并且接受远端的回应.
26
response = handoverClient.sendHandoverRequest(request);
27
}
28
//当是null的时候远程不支持handover,使用snepClient进行发送
29
//在以前比较老的版本没有handoverClient这个client的,所以此处兼容snepClient
30
if (response == null && snepClient != null) {
31
// Remote device may not support handover service,
32
//下面就是通过SnepClient相关的协议去发送数据,并获取response了。
33
//这是以前没有handoverClient的时候的实现,现在基本都用不到这里.
34
SnepMessage snepResponse = snepClient.get(request);
35
response = snepResponse.getNdefMessage();
36
}
37
if (response == null) {
38
return HANDOVER_UNSUPPORTED;
39
}
40
} else {
41
return HANDOVER_UNSUPPORTED;
42
}
43
//走到这里response有回应,开始走BeamManager相关流程去发送数据,repose就包含了接收设
44
//备的蓝牙地址.
45
//一定注意此时的andoverDataParser解析的就是server返回回来的消息,然后经过处理.
46
if (!beamManager.startBeamSend(mContext,
47
mHandoverDataParser.getOutgoingHandoverData(response), uris,
48
userHandle)) {
49
return HANDOVER_BUSY;
50
}
51
return HANDOVER_SUCCESS;
52
}
53
54
int doSnepProtocol(NdefMessage msg) throws IOException {
55
if (msg != null) {
56
snepClient.put(msg);
57
return SNEP_SUCCESS;
58
} else {
59
return SNEP_FAILURE;
60
}
61
}
62
63
@Override
64
public Void doInBackground(Void... args) {
65
NdefMessage m;
66
Uri[] uris;
67
UserHandle userHandle;
68
boolean result = false;
69
70
synchronized (P2pLinkManager.this) {
71
if (mLinkState != LINK_STATE_UP || mSendState != SEND_STATE_SENDING) {
72
return null;
73
}
74
//取出要发送的对应的变量.
75
m = mMessageToSend;
76
uris = mUrisToSend;
77
userHandle = mUserHandle;
78
snepClient = mSnepClient;
79
handoverClient = mHandoverClient;
80
nppClient = mNdefPushClient;
81
}
82
//当uris!= null的时候就表明传入的是大数据,需要使用BT进行handover.
83
//此处的uris就是我们要往外发送的数据了.
84
if (uris != null) {
85
if (DBG) Log.d(TAG, "Trying handover request");
86
try {
87
int handoverResult = doHandover(uris, userHandle);
88
switch (handoverResult) {
89
case HANDOVER_SUCCESS:
90
result = true;
91
break;
92
case HANDOVER_FAILURE:
93
result = false;
94
break;
95
case HANDOVER_UNSUPPORTED:
96
result = false;
97
onHandoverUnsupported();
98
break;
99
case HANDOVER_BUSY:
100
result = false;
101
onHandoverBusy();
102
break;
103
}
104
} ...
105
}
106
//当不能使用handover(或者说不需要使用),此时一般就是小数据
107
if (!result && m != null && snepClient != null) {
108
if (DBG) Log.d(TAG, "Sending ndef via SNEP");
109
try {
110
//直接优先使用snepClient去进行传输.
111
int snepResult = doSnepProtocol(m);
112
switch (snepResult) {
113
case SNEP_SUCCESS:
114
result = true;
115
break;
116
case SNEP_FAILURE:
117
result = false;
118
break;
119
default:
120
result = false;
121
}
122
} ...
123
}
124
//当snepclient不能成功传输的时候,再使用NdefPushClient.
125
if (!result && m != null && nppClient != null) {
126
result = nppClient.push(m);
127
}
128
129
//成功传输的时候.
130
if (result) {
131
onSendComplete(m, time);
132
}
133
134
return null;
135
}
136
};
先看发送完成的关于onSendComplete.
1
//P2pLinkManager中的onSendComplete
2
void onSendComplete(NdefMessage msg, long elapsedRealtime) {
3
// Make callbacks on UI thread
4
mHandler.sendEmptyMessage(MSG_SEND_COMPLETE);
5
}
6
case MSG_SEND_COMPLETE:
7
synchronized (P2pLinkManager.this) {
8
//释放SendTask.
9
mSendTask = null;
10
......
11
//发送状态设置为SEND_STATE_COMPLETE
12
mSendState = SEND_STATE_COMPLETE;
13
//移除timeout.
14
mHandler.removeMessages(MSG_DEBOUNCE_TIMEOUT);
15
if (DBG) Log.d(TAG, "onP2pSendComplete()");
16
mEventListener.onP2pSendComplete();
17
//这个是app注册了mCallbackNdef以后的回调.
18
if (mCallbackNdef != null) {
19
try {
20
mCallbackNdef.onNdefPushComplete(mPeerLlcpVersion);
21
} catch (Exception e) {
22
Log.e(TAG, "Failed NDEF completed callback: " + e.getMessage());
23
}
24
}
25
}
26
break;
27
28
//P2pEventManager中的onP2pSendComplete源码.
29
@Override
30
public void onP2pSendComplete() {
31
mNfcService.playSound(NfcService.SOUND_END);
32
mVibrator.vibrate(VIBRATION_PATTERN, -1);
33
if (mSendUi != null) {
34
mSendUi.finish(SendUi.FINISH_SEND_SUCCESS);
35
}
36
mSending = false;
37
mNdefSent = true;
38
}
接下来依次介绍doHandover(uris)、doSnepProtocol(m)、nppClient.push(m)、onSendComplete(m,
time)。
doHandover相关:
注意此处的handover是P2P的时候的发送端,也就是Client端的流程.
主要的工作:
1)、调用HandoverClient把本地的NFC的请求,发到远端的server,通过:
NdefMessage request = mHandoverDataParser.createHandoverRequestMessage();
handoverClient.sendHandoverRequest(request);
request是一个NdefMessage消息,符合NFC Form的相应协议标准,封装了蓝牙的地址、名字、
秘钥等信息用于远端的配对和链接。此处通过响应的socket协议发送.
2)、调用BeamManager的startBeamSend开始传输工作,来配合远端的手机进行Beam传输.
HandoverClient中的sendHandoverRequest
1
public NdefMessage sendHandoverRequest(NdefMessage msg) throws IOException {
2
......;
3
//在初始化相关的介绍中我们提到过,LlcpSocket代表LLCP中的client端,
4
//由NativeLlcpSocket它来实现的.
5
LlcpSocket sock = null;
6
synchronized (mLock) {
7
......
8
sock = mSocket;
9
}
10
int offset = 0;
11
byte[] buffer = msg.toByteArray();
12
ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
13
try {
14
int remoteMiu = sock.getRemoteMiu();
15
if (DBG) Log.d(TAG, "about to send a " + buffer.length + " byte message");
16
while (offset < buffer.length) {
17
int length = Math.min(buffer.length - offset, remoteMiu);
18
byte[] tmpBuffer = Arrays.copyOfRange(buffer, offset, offset+length);
19
if (DBG) Log.d(TAG, "about to send a " + length + " byte packet");
20
//Handover Client端往外发送消息.
21
sock.send(tmpBuffer);
22
offset += length;
23
}
24
25
//读取通过handover来获得的response.
26
// Now, try to read back the handover response
27
byte[] partial = new byte[sock.getLocalMiu()];
28
NdefMessage handoverSelectMsg = null;
29
while (true) {
30
int size = sock.receive(partial);
31
Log.d(TAG, "about to receive size = " + size );
32
if (size < 0) {
33
break;
34
}
35
byteStream.write(partial, 0, size);
36
try {
37
//此处暂不清楚具体获取的是什么,应该也是BT相关的一些信息,用于配对和链接等。
38
//都应该是按照NFC handover相关的协议的,实例化成NdefMessage.
39
handoverSelectMsg = new NdefMessage(byteStream.toByteArray());
40
// If we get here, message is complete
41
break;
42
}...
43
}
44
return handoverSelectMsg;
45
} ...
46
return null;
47
}
//Beamanager
1
public boolean startBeamSend(Context context,
2
HandoverDataParser.BluetoothHandoverData
3
outgoingHandoverData,
4
Uri[] uris, UserHandle userHandle) {
5
......
6
BeamTransferRecord transferRecord = BeamTransferRecord.forBluetoothDevice(
7
outgoingHandoverData.device, outgoingHandoverData.carrierActivating,
8
uris);
9
Intent sendIntent = new Intent(context.getApplicationContext(),
10
BeamSendService.class);
11
sendIntent.putExtra(BeamSendService.EXTRA_BEAM_TRANSFER_RECORD, transferRecord);
12
sendIntent.putExtra(BeamSendService.EXTRA_BEAM_COMPLETE_CALLBACK,
13
new Messenger(mCallback));
14
//国内过CTA测试的时候的弹框,毕竟不能偷偷地打开呀!
15
if (CtaUtils.showCtaBtDialogIfNeeded(context, mCallback, sendIntent, null)) {
16
return true;
17
}
18
//启动BeamSendService这个服务,
19
context.startServiceAsUser(sendIntent, userHandle);
20
return true;
21
}
//BeamSendService
主要工作:
1
1)、new BeamTransferManager;
2
2)、new BeamStatusReceiver;
3
3)、mBluetoothAdapter.enableNoAutoConnect();(if not enable BT)
4
and when BT enable handleBluetoothStateChanged();
5
4)、mTransferManager.start();
相关源码
1
public class BeamSendService extends Service implements
2
BeamTransferManager.Callback {
3
......
4
private BeamTransferManager mTransferManager;
5
private BeamStatusReceiver mBeamStatusReceiver;
6
......
7
private final BluetoothAdapter mBluetoothAdapter;
8
private final BroadcastReceiver mBluetoothStateReceiver = new BroadcastReceiver()
9
{
10
@Override
11
public void onReceive(Context context, Intent intent) {
12
String action = intent.getAction();
13
if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
14
handleBluetoothStateChanged(intent);
15
}
16
}
17
};
18
19
private void handleBluetoothStateChanged(Intent intent) {
20
int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,
21
BluetoothAdapter.ERROR);
22
//当一开始蓝牙是关闭的,然后我们会调用enableNoAutoConnect()去无痕迹的打开BT.
23
if (state == BluetoothAdapter.STATE_ON) {
24
if (mTransferManager != null &&
25
mTransferManager.mDataLinkType ==
26
BeamTransferRecord.DATA_LINK_TYPE_BLUETOOTH) {
27
//调用start开始传输
28
mTransferManager.start();
29
}
30
}
31
}
32
......
33
@Override
34
public void onCreate() {
35
......
36
//注册BT状态改变的广播
37
IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
38
registerReceiver(mBluetoothStateReceiver, filter);
39
}
40
41
@Override
42
public int onStartCommand(Intent intent, int flags, int startId) {
43
......
44
BeamTransferRecord transferRecord;
45
......
46
mCompleteCallback = intent.getParcelableExtra(EXTRA_BEAM_COMPLETE_CALLBACK);
47
if (doTransfer(transferRecord)) {
48
if (DBG) Log.i(TAG, "Starting outgoing Beam transfer");
49
return START_STICKY;
50
} ...
51
}
52
boolean doTransfer(BeamTransferRecord transferRecord) {
53
if (createBeamTransferManager(transferRecord)) {
54
// register Beam status receiver
55
mBeamStatusReceiver = new BeamStatusReceiver(this, mTransferManager);
56
registerReceiver(mBeamStatusReceiver,
57
mBeamStatusReceiver.getIntentFilter(),
58
BeamStatusReceiver.BEAM_STATUS_PERMISSION, new Handler());
59
60
if (transferRecord.dataLinkType ==
61
BeamTransferRecord.DATA_LINK_TYPE_BLUETOOTH) {
62
//如果蓝牙已经打开的话,直接开始传输.
63
if (mBluetoothAdapter.isEnabled()) {
64
mTransferManager.start();
65
} else {
66
//系统内部app调用的接口,此时蓝牙打开不会被记录,也不会自动连接.
67
if (!mBluetoothAdapter.enableNoAutoConnect()) {
68
......
69
return false;
70
}
71
mBluetoothEnabledByNfc = true;
72
}
73
}
74
return true;
75
}
76
return false;
77
}
78
boolean createBeamTransferManager(BeamTransferRecord transferRecord) {
79
......
80
mTransferManager = new BeamTransferManager(this, this, transferRecord, false);
81
//跟新通知栏,准备开始传输数据.
82
mTransferManager.updateNotification();
83
return true;
84
}
85
}
86
87
//其中关于BeamTransferManager
88
public BeamTransferManager(Context context, Callback callback,
89
BeamTransferRecord pendingTransfer, boolean incoming) {
90
......
91
//代表远端BT的BluetoothDevice对象.
92
mRemoteDevice = pendingTransfer.remoteDevice;
93
//是往外发送还是接受
94
mIncoming = incoming;
95
......
96
//传输的文件的总数,
97
//是发送方的时候uris最开始传入的地方就是在prepareMessageToSend中的createBeamShareData
98
//是由第三方app设置要传输的内容的。
99
//当是接受放的时候刚到这里mTotalCount为0,在后续会被设置为正确的count.
100
mTotalCount = (pendingTransfer.uris != null) ? pendingTransfer.uris.length : 0;
101
//通知栏上当前的进度
102
mProgress = 0.0f;
103
//状态
104
mState = STATE_NEW;
105
//要传输的uri
106
mUris = pendingTransfer.uris == null
107
? new ArrayList<Uri>()
108
: new ArrayList<Uri>(Arrays.asList(pendingTransfer.uris));
109
......
110
//当前的count和总共的count
111
mCurrentCount = 0;
112
mSuccessCount = 0;
113
//往外传输的uris.
114
mOutgoingUris = pendingTransfer.uris;
115
//......
116
mHandler = new Handler(Looper.getMainLooper(), this);
117
//用来检测当前transfer时候处于活动状态.
118
mHandler.sendEmptyMessageDelayed(MSG_TRANSFER_TIMEOUT, ALIVE_CHECK_MS);
119
mNotificationManager = (NotificationManager) mContext.getSystemService(
120
Context.NOTIFICATION_SERVICE);
121
......
122
}
123
//start
124
public void start() {
125
......
126
//如果是发送方mIncoming就是false,当是接收方的时候就是true,就到此为止了.
127
if (!mIncoming) {
128
if (mDataLinkType == BeamTransferRecord.DATA_LINK_TYPE_BLUETOOTH) {
129
new BluetoothOppHandover(mContext, mRemoteDevice, mUris,
130
mRemoteActivating).start();
131
}
132
}
133
}
134
135
//BluetoothOppHandover的start()
136
/**
137
* Main entry point. This method is usually called after construction,
138
* to begin the BT sequence. Must be called on Main thread.
139
*/
140
public void start() {
141
//测试的时候抓取Log发现mRemoteActivating为true.
142
if (mRemoteActivating) {
143
Long timeElapsed = SystemClock.elapsedRealtime() - mCreateTime;
144
if (timeElapsed < REMOTE_BT_ENABLE_DELAY_MS) {
145
mHandler.sendEmptyMessageDelayed(MSG_START_SEND,
146
REMOTE_BT_ENABLE_DELAY_MS - timeElapsed);
147
} else {
148
// Already waited long enough for BT to come up
149
// - start send.
150
sendIntent();
151
}
152
} else {
153
// Remote BT enabled already, start send immediately
154
sendIntent();
155
}
156
}
157
void sendIntent() {
158
Intent intent = new Intent();
159
intent.setPackage("com.android.bluetooth");
160
String mimeType = MimeTypeUtil.getMimeTypeForUri(mContext, mUris.get(0));
161
intent.setType(mimeType);
162
intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice);
163
......
164
//一个的时候发送ACTION_HANDOVER_SEND,多个的时候发送ACTION_HANDOVER_SEND_MULTIPLE.
165
if (mUris.size() == 1) {
166
intent.setAction(ACTION_HANDOVER_SEND);
167
intent.putExtra(Intent.EXTRA_STREAM, mUris.get(0));
168
} else {
169
intent.setAction(ACTION_HANDOVER_SEND_MULTIPLE);
170
intent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, mUris);
171
}
172
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
173
mContext.sendBroadcast(intent);
174
//complete里面就是把mState = STATE_COMPLETE;
175
complete();
176
}
至此发送方Nfc端的流程就完成了,但是BT端也会有流程,此处做一个简单的分析。(有个疑问就
是不知道两边的BT什么时候配对链接的???可能启动了各个服务尝试去连接远程触发的代码,进
而建立连接)
在BT中的BluetoothOppHandoverReceiver :
接收到Intent以后调用BluetoothOppManager中的saveSendingFileInfo进行相应变量的保存
和startTransfer(device)开始Handover传输.
BluetoothOppManager:
1)、startTransfer开启一个线程把要传输的Info存入到database当中.
通过 inner Class InsertShareInfoThread.run().insertMultipleShare/insertSingleShare
2)、用insertMultipleShare举例,最终insert到数据库当中
注意后面的这些差写入操作是异步的在一个单独的线程中,所以Log看起来就比较随意。到此处位置
的流程,并不能看到和蓝牙的进一步交互了,接下来的一系列的动作可能就是和远端的BT相互传输
的时候触发的吧或者说本身蓝牙初始化,建立完链接以后的动作.(好像是有个循环一直在等,有数据就
会发送).
到此位置Handover中关于P2p的流程已经分析完毕,至于详细的Handover的一些信息解析此处没
有涉及到。
我们再回到SendTask中的doInBackground,跳过doHandover()以后执行的是doSnepProtocol();
此时就不需要和BT交互了,是要直接通过NFC传输完的,为方便观察再次贴出代码
1
int doSnepProtocol(NdefMessage msg) throws IOException {
2
if (msg != null) {
3
snepClient.put(msg);
4
return SNEP_SUCCESS;
5
} ...
6
}
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
}...
18
}
19
}
客户端生成一个put请求的SnepMessage信息。REQUEST_PUT
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去接受server端的回复。
22
mSocket.receive(responseBytes);
23
SnepMessage snepResponse;
24
try {
25
snepResponse = SnepMessage.fromByteArray(responseBytes);
26
} ...
27
if (DBG) Log.d(TAG, "Got response from first fragment: " +
28
snepResponse.getField());
29
...
30
// 符合要求后,将剩余的数据发送完成
31
while (offset < buffer.length) {
32
length = Math.min(buffer.length - offset, mFragmentLength);
33
tmpBuffer = Arrays.copyOfRange(buffer, offset, offset + length);
34
if (DBG) Log.d(TAG, "about to send a " + length + " byte fragment");
35
//一直发送!
36
mSocket.send(tmpBuffer);
37
if(NfcService.sIsDtaMode) {
38
......
39
}
40
offset += length;
41
}
42
}
根据协议,发送完毕后需要等待对方的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
} ...
11
} else if (size < HEADER_LENGTH) {
12
//根据协议,接收的数据小于Header.size(因为Snep数据必须包含Header),直接回复Reject.
13
try {
14
if((NfcService.sIsDtaMode)&&(mIsClient)){
15
if (DBG) Log.d(TAG, "Invalid header length");
16
close();
17
} else {
18
mSocket.send(SnepMessage.getMessage(fieldReject).toByteArray());
19
}
20
mSocket.send(SnepMessage.getMessage(fieldReject).toByteArray());
21
} ...
22
} else {
23
//更新buffer值
24
readSize = size - HEADER_LENGTH;
25
buffer.write(partial, 0, size);
26
}
27
DataInputStream dataIn = new DataInputStream(new ByteArrayInputStream(partial));
28
requestVersion = dataIn.readByte();
29
requestField = dataIn.readByte();
30
requestLength = dataIn.readInt();
31
if (DBG) Log.d(TAG, "read " + readSize + " of " + requestLength);
32
//Header中携带的Version不匹配的时候,直接接收完成,退出。
33
//如果接收数据小于Header中规定的数据,则请求对方继续发送,回复Continue,也是按照协议来的
34
if (requestLength > readSize) {
35
if (DBG) Log.d(TAG, "requesting continuation");
36
mSocket.send(SnepMessage.getMessage(fieldContinue).toByteArray());
37
} else {
38
doneReading = true;
39
}
40
// Remaining fragments
41
//让他continue以后,才能接受生剩余的
42
while (!doneReading) {
43
try {
44
size = mSocket.receive(partial);
45
if (DBG) Log.d(TAG, "read " + size + " bytes");
46
if (size < 0) {
47
try {
48
mSocket.send(SnepMessage.getMessage(fieldReject).toByteArray());
49
} ...
50
} else {
51
//正确接收数据,不断更新收到的数据
52
readSize += size;
53
buffer.write(partial, 0, size);
54
if (readSize == requestLength) {
55
doneReading = true;
56
}
57
}
58
}...
59
}
60
try {
61
//接收的肯定是二进制的数据流,将接收的数据转换成SNEP格式.
62
return SnepMessage.fromByteArray(buffer.toByteArray());
63
} catch (FormatException e) {
64
Log.e(TAG, "Badly formatted NDEF message, ignoring", e);
65
throw new SnepException(e);
66
}
67
}
可能当前的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,
9
NdefPushProtocol.ACTION_IMMEDIATE);
10
......
11
try {
12
remoteMiu = sock.getRemoteMiu();
13
if (DBG) Log.d(TAG, "about to send a " + buffer.length + " byte message");
14
// 将当前信息完整的发送出去,一直到完成.
15
while (offset < buffer.length) {
16
int length = Math.min(buffer.length - offset, remoteMiu);
17
byte[] tmpBuffer = Arrays.copyOfRange(buffer, offset, offset+length);
18
if (DBG) Log.d(TAG, "about to send a " + length + " byte packet");
19
sock.send(tmpBuffer);
20
offset += length;
21
}
22
return true;
23
} catch (IOException e) {
24
......
25
} finally {
26
......
27
}
28
return false;
29
}
上述操作都完成后,表示send数据已经完成,需要通知一下用户界面,我们继续返回
P2pLinkManager中的SendTask当中,往下走是调用onSendComplete去进一步处理.
至此,完成了基本的发送方的流程,而对于进行P2P的时候的接收方。在onLlcpActivated()的时候
1
//此处值跟踪接受方相关的
2
public void onLlcpActivated(byte peerLlcpVersion) {
3
synchronized (P2pLinkManager.this) {
4
...
5
switch (mLinkState) {
6
case LINK_STATE_DOWN:
7
...
8
//下面的有屏幕截图,但是并不会显示
9
mEventListener.onP2pInRange();
10
mLinkState = LINK_STATE_UP;
11
//同样会走到else.
12
if (mSendState == SEND_STATE_PENDING) {
13
......
14
} else {
15
mSendState = SEND_STATE_NOTHING_TO_SEND;
16
prepareMessageToSend(true);
17
//执行完prepareMessageToSend后mMessageToSend和mUrisToSend都为null.
18
if (mMessageToSend != null ||
19
(mUrisToSend != null &&
20
mHandoverDataParser.isHandoverSupported())) {
21
......
22
}
23
Log.d(TAG, "zhaoyuan connectLlcpServices over mSendState =
24
"+mSendState);
25
}
26
break;
27
...
28
}
29
}
30
}
接收方此处相关的代码就执行到这里就结束了,不过那在你使用不同的协议进行传输数据的时
候,接收方的对应的server会做出相应.
其中有:HandoverServer、SnepServer、NdefPushServer等.由于对SnepServer和
NdefPushServer是在一点也看不懂,
此处分析HandoverServer,其它两个也是类似的.(这几个server的初始化也在别的博客又提到)
1
private class ServerThread extends Thread {
2
private boolean mThreadRunning = true;
3
LlcpServerSocket mServerSocket;
4
@Override
5
public void run() {
6
boolean threadRunning;
7
...
8
while (threadRunning) {
9
try {
10
synchronized (HandoverServer.this) {
11
mServerSocket =
12
NfcService.getInstance().createLlcpServerSocket(mSap,
13
HANDOVER_SERVICE_NAME, MIU, 1, 1024);
14
}
15
...
16
while (threadRunning) {
17
LlcpServerSocket serverSocket;
18
synchronized (HandoverServer.this) {
19
serverSocket = mServerSocket;
20
}
21
......
22
//server段初始化完成以后,会block在此处
23
LlcpSocket communicationSocket = serverSocket.accept();
24
if (communicationSocket != null) {
25
//一旦有Client成功连接
26
new ConnectionThread(communicationSocket).start();
27
}
28
......
29
}
30
} ...
31
}
32
}
33
......
34
}
35
//关于ConnectionThread的run方法,我们只关注和P2P的流程相关的至于Handover协议相关的
36
//详细解析,暂布料机
37
public void run() {
38
ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
39
try {
40
...
41
byte[] partial = new byte[mSock.getLocalMiu()];
42
NdefMessage handoverRequestMsg = null;
43
while (running) {
44
int size = mSock.receive(partial);
45
...
46
byteStream.write(partial, 0, size);
47
try {
48
handoverRequestMsg = new NdefMessage(byteStream.toByteArray());
49
} ...
50
if (handoverRequestMsg != null) {
51
......
52
mCallback.onHandoverRequestReceived();
53
//这是接收方(server)的BeamTransfer的入口,开始弹出通知更新流程等.
54
if (!beamManager.startBeamReceive(mContext,
55
handoverData.handoverData)) {
56
mCallback.onHandoverBusy();
57
break;
58
}
59
// We can process another handover transfer
60
byteStream = new ByteArrayOutputStream();
61
}
62
......
63
}
64
} ...
65
}
然后重新进入到BeamManager中的startBeamReceive里面,基本的流程和Send差不多的.
1
public boolean startBeamReceive(Context context,
2
HandoverDataParser.BluetoothHandoverData handoverData) {
3
......
4
BeamTransferRecord transferRecord =
5
BeamTransferRecord.forBluetoothDevice(
6
handoverData.device, handoverData.carrierActivating, null);
7
8
Intent receiveIntent = new Intent(context.getApplicationContext(),
9
BeamReceiveService.class);
10
receiveIntent.putExtra(BeamReceiveService.EXTRA_BEAM_TRANSFER_RECORD,
11
transferRecord);
12
receiveIntent.putExtra(BeamReceiveService.EXTRA_BEAM_COMPLETE_CALLBACK,
13
new Messenger(mCallback));
14
//国内CTA相关的
15
if (CtaUtils.showCtaBtDialogIfNeeded(context, mCallback,
16
receiveIntent, handoverData.device)) {
17
return true;
18
}
19
//添加到whitelistOppDevice就不会额外的弹出配对框
20
whitelistOppDevice(context, handoverData.device);
21
//启动BeamReceiveService.
22
context.startServiceAsUser(receiveIntent, UserHandle.CURRENT);
23
return true;
24
}
25
//BeamReceiveService相关的源码
26
public class BeamReceiveService extends Service implements
27
BeamTransferManager.Callback {
28
@Override
29
public int onStartCommand(Intent intent, int flags, int startId) {
30
......
31
//可以看到基本和send的设计理念差不多
32
if (prepareToReceive(transferRecord)) {
33
if (DBG) Log.i(TAG, "Ready for incoming Beam transfer");
34
return START_STICKY;
35
}...
36
}
37
boolean prepareToReceive(BeamTransferRecord transferRecord) {
38
......
39
40
if (!mBluetoothAdapter.isEnabled()) {
41
//没有打开BT的时候自动打开
42
if (!mBluetoothAdapter.enableNoAutoConnect()) {
43
Log.e(TAG, "Error enabling Bluetooth.");
44
return false;
45
}
46
mBluetoothEnabledByNfc = true;
47
}
48
//和Send同样实例化BeamTransferManager,只不过参数的实际值可能有点区别
49
mTransferManager = new BeamTransferManager(this, this, transferRecord, true);
50
51
//BeamStatusReceiver注册很多需要从BT接受的广播,都是和Transfer相关的
52
mBeamStatusReceiver = new BeamStatusReceiver(this, mTransferManager);
53
registerReceiver(mBeamStatusReceiver, mBeamStatusReceiver.getIntentFilter(),
54
BeamStatusReceiver.BEAM_STATUS_PERMISSION, new Handler());
55
//调用start开始,其实在发送方没做什么操作,可以看前面贴出的源码
56
mTransferManager.start();
57
//同样更新通知界面.
58
mTransferManager.updateNotification();
59
return true;
60
}
61
}
62
//接下来就需要关于BeamStatusReceiver的分析了
63
public class BeamStatusReceiver extends BroadcastReceiver {
64
......
65
@Override
66
public void onReceive(Context context, Intent intent) {
67
String action = intent.getAction();
68
int dataLinkType = intent.getIntExtra(EXTRA_HANDOVER_DATA_LINK_TYPE,
69
BeamTransferManager.DATA_LINK_TYPE_BLUETOOTH);
70
71
if (ACTION_CANCEL_HANDOVER_TRANSFER.equals(action)) {
72
if (mTransferManager != null) {
73
mTransferManager.cancel();
74
}
75
} else if (ACTION_TRANSFER_PROGRESS.equals(action) ||
76
ACTION_TRANSFER_DONE.equals(action) ||
77
ACTION_HANDOVER_STARTED.equals(action)) {
78
handleTransferEvent(intent, dataLinkType);
79
}
80
}
81
public IntentFilter getIntentFilter() {
82
IntentFilter filter = new IntentFilter(ACTION_TRANSFER_DONE);
83
filter.addAction(ACTION_TRANSFER_PROGRESS);
84
filter.addAction(ACTION_CANCEL_HANDOVER_TRANSFER);
85
filter.addAction(ACTION_HANDOVER_STARTED);
86
return filter;
87
}
88
private void handleTransferEvent(Intent intent, int deviceType) {
89
String action = intent.getAction();
90
String sourceAddress = intent.getStringExtra(EXTRA_ADDRESS);
91
......
92
//ACTION_TRANSFER_DONE是当接受完毕后跟新通知界面的
93
if (action.equals(ACTION_TRANSFER_DONE)) {
94
int handoverStatus = intent.getIntExtra(EXTRA_TRANSFER_STATUS,
95
HANDOVER_TRANSFER_STATUS_FAILURE);
96
if (handoverStatus == HANDOVER_TRANSFER_STATUS_SUCCESS) {
97
String uriString = intent.getStringExtra(EXTRA_TRANSFER_URI);
98
String mimeType = intent.getStringExtra(EXTRA_TRANSFER_MIMETYPE);
99
Uri uri = Uri.parse(uriString);
100
if (uri != null && uri.getScheme() == null) {
101
uri = Uri.fromFile(new File(uri.getPath()));
102
}
103
mTransferManager.finishTransfer(true, uri, mimeType);
104
} else {
105
mTransferManager.finishTransfer(false, null, null);
106
}
107
} else if (action.equals(ACTION_TRANSFER_PROGRESS)) {
108
//传输的途中,更新通知栏传输的进程.
109
float progress = intent.getFloatExtra(EXTRA_TRANSFER_PROGRESS, 0.0f);
110
mTransferManager.updateFileProgress(progress);
111
} else if (action.equals(ACTION_HANDOVER_STARTED)) {
112
//进入到此处的时候证明接收方和远端的BT已经建立好链接,并且配对了.
113
//下面就开始准备接受了,而EXTRA_OBJECT_COUNT,就是携带的总共需要接受的数量.
114
int count = intent.getIntExtra(EXTRA_OBJECT_COUNT, 0);
115
if (count > 0) {
116
mTransferManager.setObjectCount(count);
117
}
118
}
119
}
120
}
至于传输的通知栏的UI显示逻辑此处暂不深入了。
这样就完成了全部的P2P的java层的流程.
文章参考:
http://blog.youkuaiyun.com/xuwen0306/article/details/44724859