簡單概況啟動過程
1.
WifiP2pService --- > WifiP2pServiceImpl
2.WifiP2pServiceImp構(gòu)造方法
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();//狀態(tài)機
}
前言
7.1 源碼
要看懂源碼旨巷,就需要深入了解狀態(tài)機(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和藍(lán)牙代碼,必定涉及StateMachine懈万,同時也會涉及Handler跨進程通信(AsyncChannel)
2.c.mAsyncChannel
mAsyncChannel是Handler跨進程通信的關(guān)鍵類拴清。也就是說,三方應(yīng)用和WifiP2pService服務(wù)進行跨進程通信的實現(xiàn)方式就是采用Handler会通。了解了這些口予,則明白為什么要initialize,并且要傳入Looper對象
public Channel initialize(Context srcContext, Looper srcLooper, ChannelListener listener)
3.c.putListener(listener)
這種回調(diào)方法的處理方式涕侈,確實給人眼前一亮
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轉(zhuǎn)化成arg2發(fā)送出去
4.sticky broadcast的使用:
系統(tǒng)發(fā)送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之后,會立馬接收到的原因
系統(tǒng)服務(wù)
1.WifiP2pServiceImpl
先要回答一個問題:WifiP2pServiceImpl中的狀態(tài)機熬丧,在客戶端調(diào)用之前笋粟,已經(jīng)處于什么狀態(tài)?
如果wifi是打開的析蝴,則處于InactiveState矗钟。
具體原因:
先說思路
1)WifiP2pServiceImpl和WifiStateMachine是有聯(lián)動的,也是通過handler通信
a.WifiStateMachine中的mWifiP2pChannel為聯(lián)動的變量
b.WifiStateMachine中的mP2pSupported為關(guān)鍵變量
2)WifiP2pServiceImpl和WifiMonitor是有聯(lián)動的嫌变,在構(gòu)造方法中
WifiP2pServiceImpl.P2pStateMachine構(gòu)造方法中使用如下:
mWifiMonitor.registerHandler(interfaceName,
WifiMonitor.AP_STA_CONNECTED_EVENT, getHandler());
2.回到狀態(tài)機
InactiveState沒有處理消息DISCOVER_PEERS,傳給了父狀態(tài)P2pEnabledState躬它,關(guān)注核心點:
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)) {//調(diào)用jni腾啥。注意:通過WifiMonitor回復(fù)WifiMonitor.P2P_DEVICE_FOUND_EVENT
replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_SUCCEEDED);//客戶端調(diào)用的回調(diào)回復(fù)
sendP2pDiscoveryChangedBroadcast(true);//發(fā)送廣播,開始掃描發(fā)現(xiàn)對等設(shè)備
} else {
replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_FAILED,
WifiP2pManager.ERROR);
}
break;
case WifiMonitor.P2P_DEVICE_FOUND_EVENT://執(zhí)行jni層冯吓,底層給的回復(fù)
WifiP2pDevice device = (WifiP2pDevice) message.obj;
if (mThisDevice.deviceAddress.equals(device.deviceAddress)) break;
mPeers.updateSupplicantDetails(device);
sendPeersChangedBroadcast();//發(fā)送廣播倘待,帶有掃描到的設(shè)備
總結(jié)
1.discoverPeers(Channel c, ActionListener listener)的回調(diào)僅僅是判斷是否jni執(zhí)行成功
2.執(zhí)行成功,涉及需要進一步掃描設(shè)備组贺,所以通過廣播把數(shù)據(jù)報告出來凸舵,而不是通過回調(diào)
總之,回調(diào)的是執(zhí)行狀態(tài)失尖,結(jié)果通過廣播通知
案例
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
額外發(fā)現(xiàn)
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關(guān)聯(lián)啊奄?
兩者之間也通過雙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 是否可以默認(rèn)菇夸? -- 可以,更改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);//設(shè)置1
// DIRECT-XY-DEVICENAME (XY is randomly generated)
mWifiNative.setP2pSsidPostfix("-" + mThisDevice.deviceName);//設(shè)置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);//廣播更新狀態(tài)
if (DBG) logd("DeviceAddress: " + mThisDevice.deviceAddress);
mClientInfoList.clear();
mWifiNative.p2pFlush();
mWifiNative.p2pServiceFlush();
mServiceTransactionId = 0;
mServiceDiscReqId = null;
updatePersistentNetworks(RELOAD);
}