Android之WIFI通信-P2P的discoverPeers
2022-02-19 本文已影响0人
锄禾豆
简单概况启动过程
1.
WifiP2pService --- > WifiP2pServiceImpl
2.WifiP2pServiceImp构造方法
public WifiP2pServiceImpl(Context context) {
mContext = context;
mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI_P2P, 0, NETWORKTYPE, "");
mP2pSupported = mContext.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_WIFI_DIRECT);
mThisDevice.primaryDeviceType = mContext.getResources().getString(
com.android.internal.R.string.config_wifi_p2p_device_type);
HandlerThread wifiP2pThread = new HandlerThread("WifiP2pService");
wifiP2pThread.start();
mClientHandler = new ClientHandler(wifiP2pThread.getLooper());
mP2pStateMachine = new P2pStateMachine(TAG, wifiP2pThread.getLooper(), mP2pSupported);
mP2pStateMachine.start();//状态机
}
前言
7.1 源码
要看懂源码,就需要深入了解状态机(StateMachine)和双Handler通信(AsyncChannel)
案例
WifiP2pManager.discoverPeers
客户端:
1.看discoverPeers代码
public void discoverPeers(Channel c, ActionListener listener) {
checkChannel(c);//检测c是否存在,如果不存在,直接抛异常处理。这也说明了,channel首先被初始化的意义
c.mAsyncChannel.sendMessage(DISCOVER_PEERS, 0, c.putListener(listener));
}
private static void checkChannel(Channel c) {
if (c == null) throw new IllegalArgumentException("Channel needs to be initialized");
}
看wifi和蓝牙代码,必定涉及StateMachine,同时也会涉及Handler跨进程通信(AsyncChannel)
2.c.mAsyncChannel
mAsyncChannel是Handler跨进程通信的关键类。也就是说,三方应用和WifiP2pService服务进行跨进程通信的实现方式就是采用Handler。了解了这些,则明白为什么要initialize,并且要传入Looper对象
public Channel initialize(Context srcContext, Looper srcLooper, ChannelListener listener)
3.c.putListener(listener)
这种回调方法的处理方式,确实给人眼前一亮
private int putListener(Object listener) {//返回值为key
if (listener == null) return INVALID_LISTENER_KEY;
int key;
synchronized (mListenerMapLock) {
do {
key = mListenerKey++;
} while (key == INVALID_LISTENER_KEY);
mListenerMap.put(key, listener);//把listener放在map中。
}
return key;
}
//private HashMap<Integer, Object> mListenerMap = new HashMap<Integer, Object>();
也就是说,在Handler通信中,把listener转化成arg2发送出去
4.sticky broadcast的使用:
系统发送sticky 广播后,app才注册此广播,是否可以接收到广播?
答案:能。具体原理,我们这里不分析。
private void sendP2pStateChangedBroadcast(boolean enabled) {
logd(Debug.getCallers(5));
final Intent intent = new Intent(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
if (enabled) {
intent.putExtra(WifiP2pManager.EXTRA_WIFI_STATE,
WifiP2pManager.WIFI_P2P_STATE_ENABLED);
} else {
intent.putExtra(WifiP2pManager.EXTRA_WIFI_STATE,
WifiP2pManager.WIFI_P2P_STATE_DISABLED);
}
mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
}
这也就是为什么,我们注册:WIFI_P2P_THIS_DEVICE_CHANGED_ACTION、WIFI_P2P_DISCOVERY_CHANGED_ACTION、WIFI_P2P_STATE_CHANGED_ACTION、WIFI_P2P_CONNECTION_CHANGED_ACTION,启动app之后,会立马接收到的原因
系统服务
1.WifiP2pServiceImpl
先要回答一个问题:WifiP2pServiceImpl中的状态机,在客户端调用之前,已经处于什么状态?
如果wifi是打开的,则处于InactiveState。
具体原因:
先说思路
1)WifiP2pServiceImpl和WifiStateMachine是有联动的,也是通过handler通信
a.WifiStateMachine中的mWifiP2pChannel为联动的变量
b.WifiStateMachine中的mP2pSupported为关键变量
2)WifiP2pServiceImpl和WifiMonitor是有联动的,在构造方法中
WifiP2pServiceImpl.P2pStateMachine构造方法中使用如下:
mWifiMonitor.registerHandler(interfaceName,
WifiMonitor.AP_STA_CONNECTED_EVENT, getHandler());
2.回到状态机
InactiveState没有处理消息DISCOVER_PEERS,传给了父状态P2pEnabledState,关注核心点:
case WifiP2pManager.DISCOVER_PEERS:
if (mDiscoveryBlocked) {
replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_FAILED,
WifiP2pManager.BUSY);
break;
}
// do not send service discovery request while normal find operation.
clearSupplicantServiceRequest();
if (mWifiNative.p2pFind(DISCOVER_TIMEOUT_S)) {//调用jni。注意:通过WifiMonitor回复WifiMonitor.P2P_DEVICE_FOUND_EVENT
replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_SUCCEEDED);//客户端调用的回调回复
sendP2pDiscoveryChangedBroadcast(true);//发送广播,开始扫描发现对等设备
} else {
replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_FAILED,
WifiP2pManager.ERROR);
}
break;
case WifiMonitor.P2P_DEVICE_FOUND_EVENT://执行jni层,底层给的回复
WifiP2pDevice device = (WifiP2pDevice) message.obj;
if (mThisDevice.deviceAddress.equals(device.deviceAddress)) break;
mPeers.updateSupplicantDetails(device);
sendPeersChangedBroadcast();//发送广播,带有扫描到的设备
总结
1.discoverPeers(Channel c, ActionListener listener)的回调仅仅是判断是否jni执行成功
2.执行成功,涉及需要进一步扫描设备,所以通过广播把数据报告出来,而不是通过回调
总之,回调的是执行状态,结果通过广播通知
案例
connect
android.net.wifi.p2p.PEERS_CHANGED
android.net.wifi.p2p.DISCOVERY_STATE_CHANGE
android.net.wifi.p2p.PEERS_CHANGED
android.net.wifi.p2p.THIS_DEVICE_CHANGED
android.net.wifi.p2p.CONNECTION_STATE_CHANGE
android.net.wifi.p2p.PEERS_CHANGED
额外发现
1.日志格式
if (DBG) log("halfConnectSync srcHandler to the dstMessenger E");
// We are connected
connected(srcContext, srcHandler, dstMessenger);
if (DBG) log("halfConnectSync srcHandler to the dstMessenger X");
为什么用E X
想一下execute这个单词
2.WifiP2pServiceImp和WifiStateMachine关联?
两者之间也通过双handler通信
05-19 13:12:35.216 414 414 I WifiP2pService: Registering wifip2p
05-19 13:12:35.216 414 486 D WifiP2pService: P2pDisabledState
05-19 13:12:35.243 414 486 D WifiP2pService: P2pDisabledState what 69633
05-19 13:12:35.243 414 486 D WifiP2pService: DefaultState what 69633
05-19 13:12:35.243 414 486 D WifiP2pService: P2pDisabledState what 69632
05-19 13:12:35.243 414 486 D WifiP2pService: DefaultState what 69632
05-19 13:12:35.243 414 486 D WifiP2pService: Full connection with WifiStateMachine established
05-19 13:12:38.160 414 486 D WifiP2pService: P2pDisabledState what 131203 //CMD_ENABLE_P2P
05-19 13:12:38.177 414 486 E WifiP2pService: Unable to change interface settings: java.lang.IllegalStateException: command '25 interface setcfg p2p0 0.0.0.0 0 up' failed with '400 25 Failed to clear address (No such device)'
05-19 13:12:38.177 414 486 D WifiP2pService: P2pEnablingState
05-19 13:12:38.177 414 486 D WifiP2pService: P2pEnablingState what 147457 //SUP_CONNECTION_EVENT
05-19 13:12:38.177 414 486 D WifiP2pService: P2p socket connection successful
问题
1.mWifiP2pManager.listen用来做什么?
2.mWifiP2pManager.createGroup 怎么使用?
3.mWifiP2pManager.setDeviceName 是否可以默认? -- 可以,更改SettingsProvider即可
private String getPersistedDeviceName() {
String deviceName = Settings.Global.getString(mContext.getContentResolver(),
Settings.Global.WIFI_P2P_DEVICE_NAME);
if (deviceName == null) {
/* We use the 4 digits of the ANDROID_ID to have a friendly
* default that has low likelihood of collision with a peer */
String id = Settings.Secure.getString(mContext.getContentResolver(),
Settings.Secure.ANDROID_ID);
return "Android_" + id.substring(0,4);
}
return deviceName;
}
private boolean setAndPersistDeviceName(String devName) {
if (devName == null) return false;
if (!mWifiNative.setDeviceName(devName)) {
loge("Failed to set device name " + devName);
return false;
}
mThisDevice.deviceName = devName;
mWifiNative.setP2pSsidPostfix("-" + mThisDevice.deviceName);
Settings.Global.putString(mContext.getContentResolver(),
Settings.Global.WIFI_P2P_DEVICE_NAME, devName);
sendThisDeviceChangedBroadcast();
return true;
}
第一次初始化
private void initializeP2pSettings() {
mWifiNative.setPersistentReconnect(true);
mThisDevice.deviceName = getPersistedDeviceName();//初始化
mWifiNative.setDeviceName(mThisDevice.deviceName);//设置1
// DIRECT-XY-DEVICENAME (XY is randomly generated)
mWifiNative.setP2pSsidPostfix("-" + mThisDevice.deviceName);//设置2
mWifiNative.setDeviceType(mThisDevice.primaryDeviceType);
// Supplicant defaults to using virtual display with display
// which refers to a remote display. Use physical_display
mWifiNative.setConfigMethods("virtual_push_button physical_display keypad");
// STA has higher priority over P2P
mWifiNative.setConcurrencyPriority("sta");
mThisDevice.deviceAddress = mWifiNative.p2pGetDeviceAddress();
updateThisDevice(WifiP2pDevice.AVAILABLE);//广播更新状态
if (DBG) logd("DeviceAddress: " + mThisDevice.deviceAddress);
mClientInfoList.clear();
mWifiNative.p2pFlush();
mWifiNative.p2pServiceFlush();
mServiceTransactionId = 0;
mServiceDiscReqId = null;
updatePersistentNetworks(RELOAD);
}