Android开发中Wifi连接流程分析
当我们在android手机上通过settings连接一个ap时,间接调用wifimanager的connect()方法:
/**
*connecttoanetworkwiththegivenconfiguration.thenetworkalso
*getsaddedtothesupplicantconfiguration.
*
*foranewnetwork,thisfunctionisusedinsteadofa
*sequenceofaddnetwork(),enablenetwork(),saveconfiguration()and
*reconnect()
*
*@paramconfigthesetofvariablesthatdescribetheconfiguration,
*containedina{@linkwificonfiguration}object.
*@paramlistenerforcallbacksonsuccessorfailure.canbenull.
*@throwsillegalstateexceptionifthewifimanagerinstanceneedstobe
*initializedagain
*
*@hide
*/
publicvoidconnect(wificonfigurationconfig,actionlistenerlistener){
if(config==null)thrownewillegalargumentexception("configcannotbenull");
validatechannel();
//useinvalid_network_idforarg1whenpassingaconfigobject
//arg1isusedtopassnetworkidwhenthenetworkalreadyexists
sasyncchannel.sendmessage(connect_network,wificonfiguration.invalid_network_id,
putlistener(listener),config);
}
/**
*connecttoanetworkwiththegivennetworkid.
*
*thisfunctionisusedinsteadofaenablenetwork(),saveconfiguration()and
*reconnect()
*
*@paramnetworkidthenetworkididentifiyingthenetworkinthe
*supplicantconfigurationlist
*@paramlistenerforcallbacksonsuccessorfailure.canbenull.
*@throwsillegalstateexceptionifthewifimanagerinstanceneedstobe
*initializedagain
*@hide
*/
publicvoidconnect(intnetworkid,actionlistenerlistener){
if(networkid<0)thrownewillegalargumentexception("networkidcannotbenegative");
validatechannel();
sasyncchannel.sendmessage(connect_network,networkid,putlistener(listener));
} connect()方法有两种形式,一种接受wificonfiguration对象,一种接受某个ap的networkid。wificonfiguration描述了一个wifi连接的所有配置信息。
wifimanager的servicehandler和wifiservice的clienthandler通过异步通道进行通信。所以这里通过asyncchannel机制,向wifiserviceimpl发送connect_network消息,可知在wifiserviceimpl::clienthandler中被处理:
/*clientcommandsareforwardedtostatemachine*/
casewifimanager.connect_network:
casewifimanager.save_network:{
wificonfigurationconfig=(wificonfiguration)msg.obj;
intnetworkid=msg.arg1;
if(msg.what==wifimanager.save_network){
slog.e("wifiserviceimpl","save"
+"nid="+integer.tostring(networkid)
+"uid="+msg.sendinguid
+"name="
+mcontext.getpackagemanager().getnameforuid(msg.sendinguid));
}
if(msg.what==wifimanager.connect_network){
slog.e("wifiserviceimpl","connect"
+"nid="+integer.tostring(networkid)
+"uid="+msg.sendinguid
+"name="
+mcontext.getpackagemanager().getnameforuid(msg.sendinguid));
}
if(config!=null&&isvalid(config)){
if(dbg)slog.d(tag,"connectwithconfig"+config);
mwifistatemachine.sendmessage(message.obtain(msg));
}elseif(config==null
&&networkid!=wificonfiguration.invalid_network_id){
if(dbg)slog.d(tag,"connectwithnetworkid"+networkid);
mwifistatemachine.sendmessage(message.obtain(msg));
}else{
slog.e(tag,"clienthandler.handlemessageignoringinvalidmsg="+msg);
if(msg.what==wifimanager.connect_network){
replyfailed(msg,wifimanager.connect_network_failed,
wifimanager.invalid_args);
}else{
replyfailed(msg,wifimanager.save_network_failed,
wifimanager.invalid_args);
}
}
break;
} clienthandler中并不做具体的连接动作,主要将connect_network消息被转发到wifistatemachine中,通过wifi状态机来驱动连接和dhcp过程 。connectmodestate处理:
casewifimanager.connect_network:
/**
*theconnectmessagecancontainanetworkidpassedasarg1onmessageor
*oraconfigpassedasobjonmessage.
*foranewnetwork,aconfigispassedtocreateandconnect.
*foranexistingnetwork,anetworkidispassed
*/
netid=message.arg1;
config=(wificonfiguration)message.obj;
mwificonnectionstatistics.numwifimanagerjoinattempt++;
booleanupdatedexisting=false;
/*savethenetworkconfig*/
if(config!=null){
//whenconnectingtoanaccesspoint,wifistatemachinewantstoupdatethe
//relevantconfigwithadministrativedata.thisupdateshouldnotbe
//considereda'real'update,thereforelockdownbydeviceownermustbe
//disregarded.
if(!recorduidifauthorized(config,message.sendinguid,
/*onlyannotate*/true)){
logw("notauthorizedtoupdatenetwork"
+"config="+config.ssid
+"cnid="+config.networkid
+"uid="+message.sendinguid);
replytomessage(message,wifimanager.connect_network_failed,
wifimanager.not_authorized);
break;
}
stringconfigkey=config.configkey(true/*allowcached*/);
wificonfigurationsavedconfig=
mwificonfigstore.getwificonfiguration(configkey);
if(savedconfig!=null){
//thereisanexistingconfigwiththisnetid,butitwasn'texposed
//(eitherauto_join_deletedorephemeral;seewificonfigstore#
//getconfigurednetworks).removethosebitsandupdatetheconfig.
config=savedconfig;
logd("connect_networkupdatingexistingconfigwithid="+
config.networkid+"configkey="+configkey);
config.ephemeral=false;
config.autojoinstatus=wificonfiguration.auto_join_enabled;
updatedexisting=true;
}
result=mwificonfigstore.savenetwork(config,message.sendinguid);
netid=result.getnetworkid();
}
config=mwificonfigstore.getwificonfiguration(netid);
if(config==null){
logd("connect_networknoconfigforid="+integer.tostring(netid)+""
+msupplicantstatetracker.getsupplicantstatename()+"mystate"
+getcurrentstate().getname());
replytomessage(message,wifimanager.connect_network_failed,
wifimanager.error);
break;
}else{
stringwasskipped=config.autojoinbailedduetolowrssi?"skipped":"";
logd("connect_networkid="+integer.tostring(netid)
+"config="+config.ssid
+"cnid="+config.networkid
+"supstate="+msupplicantstatetracker.getsupplicantstatename()
+"mystate"+getcurrentstate().getname()
+"uid="+message.sendinguid
+wasskipped);
}
autoroamsetbssid(netid,"any");
if(message.sendinguid==process.wifi_uid
||message.sendinguid==process.system_uid){
//asasanitymeasure,clearthebssidinthesupplicantnetworkblock.
//ifsystemorwifisettingswanttoconnect,theywillnot
//specifythebssid.
//ifanapphoweverhadaddedabssidtothisconfiguration,andthebssid
//waswrong,thenwewouldforeverfailtoconnectuntilthatbssid
//iscleanedup.
clearconfigbssid(config,"connect_network");
}
if(deferforuserinput(message,netid,true)){
break;
}elseif(mwificonfigstore.getwificonfiguration(netid).userapproved==
wificonfiguration.user_banned){
replytomessage(message,wifimanager.connect_network_failed,
wifimanager.not_authorized);
break;
}
mautoroaming=wifiautojoincontroller.auto_join_idle;
/*tellautojointheuserdidtrytoconnecttothatnetworkiffromsettings*/
booleanpersist=
mwificonfigstore.checkconfigoverridepermission(message.sendinguid);
mwifiautojoincontroller.updateconfigurationhistory(netid,true,persist);
mwificonfigstore.setlastselectedconfiguration(netid);
diddisconnect=false;
if(mlastnetworkid!=wificonfiguration.invalid_network_id
&&mlastnetworkid!=netid){
/**supplicantwillignorethereconnectifwearecurrentlyassociated,
*hencetriggeradisconnect
*/
diddisconnect=true;
mwifinative.disconnect();
}
//makesurethenetworkisenabled,sincesupplicantwillnotreenableit
mwificonfigstore.enablenetworkwithoutbroadcast(netid,false);
if(mwificonfigstore.selectnetwork(config,/*updatepriorities=*/true,
message.sendinguid)&&mwifinative.reconnect()){
lastconnectattempttimestamp=system.currenttimemillis();
targetwificonfiguration=mwificonfigstore.getwificonfiguration(netid);
/*thestatetrackerhandlesenablingnetworksuponcompletion/failure*/
msupplicantstatetracker.sendmessage(wifimanager.connect_network);
replytomessage(message,wifimanager.connect_network_succeeded);
if(diddisconnect){
/*expectadisconnectionfromtheoldconnection*/
transitionto(mdisconnectingstate);
}elseif(updatedexisting&&getcurrentstate()==mconnectedstate&&
getcurrentwificonfiguration().networkid==netid){
//updatethecurrentsetofnetworkcapabilities,butstayinthe
//currentstate.
updatecapabilities(config);
}else{
/**
*directlygotodisconnectedstatewherewe
*processtheconnectioneventsfromsupplicant
**/
transitionto(mdisconnectedstate);
}
}else{
loge("failedtoconnectconfig:"+config+"netid:"+netid);
replytomessage(message,wifimanager.connect_network_failed,
wifimanager.error);
break;
}
break; 通过代码,可知主要的处理动作如下:
将connect()传过来的ap信息保存到wificonfigstore对象中
通过wificonfigstore::selectnetwork()函数更新wificonfigstore和config的priority优先级属性,最后更新到wpa_s配置文件中;enable当前要连接的ap,disable其他的ap
通过wifinative::reconnect()函数向wpa_s发送连接指令,连接选定的ap
连接选定的ap是通过调用wifinative方法向wpa_supplicant发送connect指令,wpa_s通知驱动进行连接;当底层无线连接成功后,framework就能通过wifimonitor接受到wpa_s上报的event消息:
/**
*handleallsupplicanteventsexceptstate-change
*@parameventtheeventtype
*@paramremaindertherestofthestringfollowingthe
*eventnameand"?—?"
*/
voidhandleevent(intevent,stringremainder){
if(dbg){
logdbg("handleevent"+integer.tostring(event)+""+remainder);
}
switch(event){
casedisconnected:
handlenetworkstatechange(networkinfo.detailedstate.disconnected,remainder);
break;
caseconnected:
handlenetworkstatechange(networkinfo.detailedstate.connected,remainder);
break;
casescan_results:
mstatemachine.sendmessage(scan_results_event);
break;
casescan_failed:
mstatemachine.sendmessage(scan_failed_event);
break;
caseunknown:
if(dbg){
logdbg("handleeventunknown:"+integer.tostring(event)+""+remainder);
}
break;
default:
break;
}
}
privatevoidhandlenetworkstatechange(networkinfo.detailedstatenewstate,stringdata){
stringbssid=null;
intnetworkid=-1;
intreason=0;
intind=-1;
intlocal=0;
matchermatch;
if(newstate==networkinfo.detailedstate.connected){
match=mconnectedeventpattern.matcher(data);
if(!match.find()){
if(dbg)log.d(tag,"handlenetworkstatechange:couldntfindbssidineventstring");
}else{
bssid=match.group(1);
try{
networkid=integer.parseint(match.group(2));
}catch(numberformatexceptione){
networkid=-1;
}
}
notifynetworkstatechange(newstate,bssid,networkid,reason);
}elseif(newstate==networkinfo.detailedstate.disconnected){
match=mdisconnectedeventpattern.matcher(data);
if(!match.find()){
if(dbg)log.d(tag,"handlenetworkstatechange:couldnotparsedisconnectstring");
}else{
bssid=match.group(1);
try{
reason=integer.parseint(match.group(2));
}catch(numberformatexceptione){
reason=-1;
}
try{
local=integer.parseint(match.group(3));
}catch(numberformatexceptione){
local=-1;
}
}
notifynetworkstatechange(newstate,bssid,local,reason);
}
}
/**
*sendthestatemachineanotificationthatthestateofwificonnectivity
*haschanged.
*@paramnewstatethenewnetworkstate
*@parambssidwhenthenewstateis{@linknetworkinfo.detailedstate#connected},
*thisisthemacaddressoftheaccesspoint.otherwise,it
*is{@codenull}.
*@paramnetidtheconfigurednetworkonwhichthestatechangeoccurred
*/
voidnotifynetworkstatechange(networkinfo.detailedstatenewstate,
stringbssid,intnetid,intreason){
if(newstate==networkinfo.detailedstate.connected){
messagem=mstatemachine.obtainmessage(network_connection_event,
netid,reason,bssid);
mstatemachine.sendmessage(m);
}else{
messagem=mstatemachine.obtainmessage(network_disconnection_event,
netid,reason,bssid);
if(dbg)logdbg("wifimonitornotifynetworkdisconnect:"
+bssid
+"reason="+integer.tostring(reason));
mstatemachine.sendmessage(m);
}
} wifimonitor与wpa_s之间的通信是通过socket建立的,如前几篇博客所介绍的那样:wifimonitor通过建立socket连接与wpa_s通信;每当wpa_s有事件要上报时,wifimonitor会解析该event,并转发到wifi状态机中。
wifimonitor接收到wpa_s的连接事件时,向wifistatemachine发送network_connection_event消息,通知状态机底层无线已经连接成功,下一步可以获取ip了。
转到wifistatemachine中,connectmodestate进行处理:
casewifimonitor.network_connection_event:
if(dbg)log("networkconnectionestablished");
mlastnetworkid=message.arg1;//成功加入到某无线网络中的ap的networkid
mlastbssid=(string)message.obj;
mwifiinfo.setbssid(mlastbssid);
mwifiinfo.setnetworkid(mlastnetworkid);
sendnetworkstatechangebroadcast(mlastbssid);
transitionto(mobtainingipstate);
break; 这里会将这次连接的ap的networkid保存下来,并进入到obtainingipstate状态去真正触发dhcp动作。l2connectedstate是obtainingipstate的父状态,看它的enter()函数:
publicvoidenter(){
mrssipolltoken++;
if(menablerssipolling){
sendmessage(cmd_rssi_poll,mrssipolltoken,0);
}
if(mnetworkagent!=null){
loge("havenetworkagentwhenenteringl2connected");
setnetworkdetailedstate(detailedstate.disconnected);
}
setnetworkdetailedstate(detailedstate.connecting);//更新当前的网络连接状态
if(!textutils.isempty(mtcpbuffersizes)){
mlinkproperties.settcpbuffersizes(mtcpbuffersizes);
}
mnetworkagent=newwifinetworkagent(gethandler().getlooper(),mcontext,
"wifinetworkagent",mnetworkinfo,mnetworkcapabilitiesfilter,
mlinkproperties,60);//此处创建一个networkagent对象用于向connectivityservice通知相应的网络配置更新操作
//wemustcleartheconfigbssid,asthewifichipsetmaydecidetoroam
//fromthispointonandhavingthebssidspecifiedinthenetworkblockwould
//causetheroamtofaileandthedevicetodisconnect
clearcurrentconfigbssid("l2connectedstate");
try{
mipreachabilitymonitor=newipreachabilitymonitor(
minterfacename,
newipreachabilitymonitor.callback(){
@override
publicvoidnotifylost(inetaddressip,stringlogmsg){
sendmessage(cmd_ip_reachability_lost,logmsg);
}
});
}catch(illegalargumentexceptione){
log.wtf("failedtocreateipreachabilitymonitor",e);
}
} 我们再看obtainingipstate::enter()方法看如何获取获取ip地址: [java]view plaincopy
print?
@override
publicvoidenter(){
if(dbg){
stringkey="";
if(getcurrentwificonfiguration()!=null){
key=getcurrentwificonfiguration().configkey();
}
log("enterobtainingipstatenetid="+integer.tostring(mlastnetworkid)
+""+key+""
+"roam="+mautoroaming
+"static="+mwificonfigstore.isusingstaticip(mlastnetworkid)
+"watchdog="+obtainingipwatchdogcount);
}
//resetlinkdebouncing,indicatingwehavesuccessfullyre-connectedtotheap
//wemightstillberoaming
linkdebouncing=false;
//sendeventtocm&networkchangebroadcast
setnetworkdetailedstate(detailedstate.obtaining_ipaddr);
//wemustcleartheconfigbssid,asthewifichipsetmaydecidetoroam
//fromthispointonandhavingthebssidspecifiedinthenetworkblockwould
//causetheroamtofaileandthedevicetodisconnect
clearcurrentconfigbssid("obtainingipaddress");
try{
mnwservice.enableipv6(minterfacename);
}catch(remoteexceptionre){
loge("failedtoenableipv6:"+re);
}catch(illegalstateexceptione){
loge("failedtoenableipv6:"+e);
}
if(!mwificonfigstore.isusingstaticip(mlastnetworkid)){
if(isroaming()){
renewdhcp();
}else{
//removeanyipaddressontheinterfaceincasewe'reswitchingfromstatic
//ipconfigurationtodhcp.thisissafebecauseifwegetherewhennot
//roaming,wedon'thaveausableaddress.
clearipv4address(minterfacename);
startdhcp();//dhcp过程启动
}
obtainingipwatchdogcount++;
logd("startdhcpwatchdog"+obtainingipwatchdogcount);
//getlinklayerstatssoaswegetfreshtxpacketcounters
getwifilinklayerstats(true);
sendmessagedelayed(obtainmessage(cmd_obtaining_ip_address_watchdog_timer,
obtainingipwatchdogcount,0),obtaining_ip_address_guard_timer_msec);
}else{
//stopanyrunningdhcpbeforeassigningstaticip
stopdhcp();
staticipconfigurationconfig=mwificonfigstore.getstaticipconfiguration(
mlastnetworkid);
if(config.ipaddress==null){
logd("staticiplacksaddress");
sendmessage(cmd_static_ip_failure);
}else{
interfaceconfigurationifcg=newinterfaceconfiguration();
ifcg.setlinkaddress(config.ipaddress);
ifcg.setinterfaceup();
try{
mnwservice.setinterfaceconfig(minterfacename,ifcg);
if(dbg)log("staticipconfigurationsucceeded");
dhcpresultsdhcpresults=newdhcpresults(config);
sendmessage(cmd_static_ip_success,dhcpresults);
}catch(remoteexceptionre){
loge("staticipconfigurationfailed:"+re);
sendmessage(cmd_static_ip_failure);
}catch(illegalstateexceptione){
loge("staticipconfigurationfailed:"+e);
sendmessage(cmd_static_ip_failure);
}
}
}
} 这里wifi分了两种连接方式,static ip和dhcp。这里区分静态和dhcp是通过wificonfiguration对象来处理的。wificonfiguration代表一个配置过的ap连接,主要包含ipassignment(标识上层的连接方式是静态还是dhcp)、ap的networkid、ap的名字等等。
我们主要看动态获取ip的过程。进入dhcp流程之前,会先清除当前的ip地址信息。着重看startdhcp()函数处理:
voidstartdhcp(){
maybeinitdhcpstatemachine();//确保初始化wifistatemachine持有的mdhcpstatemachine对象,会传入当前的wifistatemachine对象,用来回发消息
mdhcpstatemachine.registerforpredhcpnotification();//注册mregisteredforpredhcpnotification字段为true,表明我们在发送dhcp包之前需要做一些准备工作
mdhcpstatemachine.sendmessage(dhcpstatemachine.cmd_start_dhcp);//发送开始dhcp的消息
} dhcpstatemachine是一个小状态机,它主要处理dhcp下的ip地址获取过程,并将获取到的dhcp结果告知wifistatemachine。看dhcpstatemachine处理该消息的过程:
casecmd_start_dhcp:
if(mregisteredforpredhcpnotification){
/*notifycontrollerbeforestartingdhcp*/
mcontroller.sendmessage(cmd_pre_dhcp_action);
transitionto(mwaitbeforestartstate);
}else{
if(rundhcpstart()){
transitionto(mrunningstate);
}
}
break; 由于我们之前设置了mregisteredforpredhcpnotification为true,这里会向wifistatemachine发送cmd_pre_dhcp_action消息,告知wifi状态机做一些dhcp之前的预处理工作。l2connectedstate处理该消息:
casedhcpstatemachine.cmd_pre_dhcp_action:
handlepredhcpsetup();
break;
voidhandlepredhcpsetup(){
mdhcpactive=true;
if(!mbluetoothconnectionactive){
/*
*thereareproblemssettingthewi-fidriver'spower
*modetoactivewhenbluetoothcoexistencemodeis
*enabledorsense.
*
*wesetwi-fitoactivemodewhen
*obtaininganipaddressbecausewe'vefound
*compatibilityissueswithsomerouterswithlowpower
*mode.
*
*inorderforthisactivepowermodetoproperlybeset,
*wedisablecoexistencemodeuntilwe'redonewith
*obtaininganipaddress.oneexceptionisifwe
*arecurrentlyconnectedtoaheadset,sincedisabling
*coexistencewouldinterruptthatconnection.
*/
//disablethecoexistencemode
mwifinative.setbluetoothcoexistencemode(
mwifinative.bluetooth_coexistence_mode_disabled);
}
//disablepowersaveandsuspendoptimizationsduringdhcp
//note:theorderhereisimportantfornow.brcmdriverchanges
//powersettingswhenwecontrolsuspendmodeoptimizations.
//todo:removethiscommentwhenthedriverisfixed.
setsuspendoptimizationsnative(suspend_due_to_dhcp,false);
mwifinative.setpowersave(false);
//updatelinklayerstats
getwifilinklayerstats(false);
/*p2pdiscoverybreaksdhcp,shutitdowninordertogetthroughthis*/
messagemsg=newmessage();
msg.what=wifip2pserviceimpl.block_discovery;
msg.arg1=wifip2pserviceimpl.enabled;
msg.arg2=dhcpstatemachine.cmd_pre_dhcp_action_complete;
msg.obj=mdhcpstatemachine;
mwifip2pchannel.sendmessage(msg);
} 从注释可知,为了保证wifi dhcp过程的顺利进行,对bluetooth、power和p2p都做了处理工作,其中:蓝牙会disable the coexistence mode;停止p2p的discovery过程,防止影响dhcp流程。md_pre_dhcp_action_complete消息会在wifip2pserviceimpl设置完p2p部分后,被转发到dhcpstatemachine,告知预处理工作已经结束,可以进行dhcp了。看dhcpstatemachine中的消息处理:
casecmd_pre_dhcp_action_complete:
if(rundhcpstart()){
transitionto(mrunningstate);
}else{
transitionto(mpollingstate);
}
break;
privatebooleanrundhcpstart(){
/*stopanyexistingdhcpdaemonbeforestartingnew*/
networkutils.stopdhcp(minterfacename);//在启动新的dhcp流程之前,停止当前正在进行的dhcp过程
mdhcpresults=null;
if(dbg)log.d(tag,"dhcprequeston"+minterfacename);
if(!networkutils.startdhcp(minterfacename)||!dhcpsucceeded()){
log.e(tag,"dhcprequestfailedon"+minterfacename+":"+
networkutils.getdhcperror());
mcontroller.obtainmessage(cmd_post_dhcp_action,dhcp_failure,0)
.sendtotarget();
returnfalse;
}
returntrue;
} 调用networkutils.startdhcp()方法启动dhcp流程去获取ip地址,调用dhcpsucceeded()方法获取该次dhcp的dhcpresults对象,它包含了ip地址、网关、dns等等地址信息。
privatebooleandhcpsucceeded(){
dhcpresultsdhcpresults=newdhcpresults();
if(!networkutils.getdhcpresults(minterfacename,dhcpresults)){
returnfalse;
}
if(dbg)log.d(tag,"dhcpresultsfoundfor"+minterfacename);
longleaseduration=dhcpresults.leaseduration;//inttolongconversion
//sanitycheckforrenewal
if(leaseduration>=0){
//todo:wouldbegoodtonotifytheuserthathisnetworkconfigurationis
//badandthatthedevicecannotrenewbelowmin_renewal_time_secs
if(leaseduration leaseduration=min_renewal_time_secs;
}
//doitabitearlierthanhalftheleasedurationtime
//tobeatthenativedhcpclientandavoidextrapackets
//48%foronehourleasetime=29minutes
malarmmanager.setexact(alarmmanager.elapsed_realtime_wakeup,
systemclock.elapsedrealtime()+
leaseduration*480,//inmilliseconds
mdhcprenewalintent);
}else{
//infiniteleasetime,norenewalneeded
}
//fillinanymissingfieldsindhcpresultsfromthepreviousresults.
//ifmdhcpresultsisnull(i.e.thisisthefirstserverresponse),
//thisisanoop.
dhcpresults.updatefromdhcprequest(mdhcpresults);
mdhcpresults=dhcpresults;
mcontroller.obtainmessage(cmd_post_dhcp_action,dhcp_success,0,dhcpresults)
.sendtotarget();
returntrue;
} 如果getdhcpresults()函数执行成功,dhcpstatemachine就会发送cmd_post_dhcp_action消息给wifistatemachine,状态位是dhcp_success,并附加rundhcp()获取到的dhcpresult对象;失败时则附加dhcp_failure。l2connectedstate处理cmd_post_dhcp_action消息: [java]view plaincopy
print?
casedhcpstatemachine.cmd_post_dhcp_action:
handlepostdhcpsetup();
if(message.arg1==dhcpstatemachine.dhcp_success){
if(dbg)log("dhcpsuccessful");
handleipv4success((dhcpresults)message.obj,dhcpstatemachine.dhcp_success);
//weadvancetomconnectedstatebecausehandleipv4successwillcall
//updatelinkproperties,whichthensendscmd_ip_configuration_successful.
}elseif(message.arg1==dhcpstatemachine.dhcp_failure){
mwifilogger.capturebugreportdata(wifilogger.report_reason_dhcp_failure);
if(dbg){
intcount=-1;
wificonfigurationconfig=getcurrentwificonfiguration();
if(config!=null){
count=config.numconnectionfailures;
}
log("dhcpfailurecount="+count);
}
handleipv4failure(dhcpstatemachine.dhcp_failure);
//asabove,wetransitiontomdisconnectingstateviaupdatelinkproperties.
}
break; 首先调用handlepostdhcpsetup()重置之前所做的预处理动作;再处理附加的状态位,成功则调用handleipv4success()更新网络的配置信息:
privatevoidhandleipv4success(dhcpresultsdhcpresults,intreason){
if(pdbg){
logd("handleipv4success<"+dhcpresults.tostring()+">");
logd("linkaddress"+dhcpresults.ipaddress);
}
inet4addressaddr;
synchronized(mdhcpresultslock){
mdhcpresults=dhcpresults;//保存当前的dhcp结果
addr=(inet4address)dhcpresults.ipaddress.getaddress();
}
if(isroaming()){
intpreviousaddress=mwifiinfo.getipaddress();
intnewaddress=networkutils.inetaddresstoint(addr);
if(previousaddress!=newaddress){
logd("handleipv4success,roamingandaddresschanged"+
mwifiinfo+"got:"+addr);
}
}
mwifiinfo.setinetaddress(addr);
mwifiinfo.setmeteredhint(dhcpresults.hasmeteredhint());
updatelinkproperties(reason);//更新网络配置信息
} updatelinkproperties()更新当前的网络配置信息:
privatevoidupdatelinkproperties(intreason){
linkpropertiesnewlp=makelinkproperties();//根据dhcp的结果创建新的linkproperties对象,用于对比dhcp前后的网络配置是否已经改变
finalbooleanlinkchanged=!newlp.equals(mlinkproperties);
finalbooleanwasprovisioned=isprovisioned(mlinkproperties);
finalbooleanisprovisioned=isprovisioned(newlp);
//todo:teachlinkpropertieshowtounderstandstaticassignment
//andsimplifyallthisprovisioningchangedetectionlogicby
//unifyingitunderlinkproperties.compareprovisioning().
finalbooleanlostprovisioning=
(wasprovisioned&&!isprovisioned)||
(mlinkproperties.hasipv4address()&&!newlp.hasipv4address())||
(mlinkproperties.isipv6provisioned()&&!newlp.isipv6provisioned());
finaldetailedstatedetailedstate=getnetworkdetailedstate();
if(linkchanged){//网络配置信息改变
if(dbg){
log("linkconfigurationchangedfornetid:"+mlastnetworkid
+"old:"+mlinkproperties+"new:"+newlp);
}
mlinkproperties=newlp;//将新的配置信息保存到该字段中
if(mipreachabilitymonitor!=null){
mipreachabilitymonitor.updatelinkproperties(mlinkproperties);
}
if(mnetworkagent!=null)mnetworkagent.sendlinkproperties(mlinkproperties);//通过networkagent对象通知connectifyservice更新网络配置信息
}
if(lostprovisioning){
log("lostiplayerprovisioning!"+
"was:"+mlinkproperties+
"now:"+newlp);
}
//ifwejustconfiguredorlostipconfiguration,dotheneedful.
//wedon'tjustcallhandlesuccessfulipconfiguration()orhandleipconfigurationlost()
//herebecausethoseshouldonlybecalledifwe'reattemptingtoconnectoralready
//connected,whereasupdatelinkpropertiescanbecalledatanytime.
switch(reason){
casedhcpstatemachine.dhcp_success:
casecmd_static_ip_success:
//ipv4provisioningsucceded.advancetoconnectedstate.
sendmessage(cmd_ip_configuration_successful);
if(!isprovisioned){
//canneverhappenunlessdhcpreportssuccessbutisprovisionedthinksthe
//resultingconfigurationisinvalid(e.g.,noipv4address,orthestatein
//mlinkpropertiesisoutofsyncwithreality,orthere'sabuginthiscode).
//todo:disconnecthereinstead.ifourconfigurationisnotusable,there'sno
//pointinstayingconnected,andifmlinkpropertiesisoutofsyncwith
//reality,thatwillcauseproblemsinthefuture.
logd("ipv4configsucceeded,butnotprovisioned");
}
break;
casedhcpstatemachine.dhcp_failure:
//dhcpfailed.ifwe'renotalreadyprovisioned,orwehadipv4andnowlostit,
//giveupanddisconnect.
//ifwe'realreadyprovisioned(e.g.,ipv6-onlynetwork),stayconnected.
if(!isprovisioned||lostprovisioning){
sendmessage(cmd_ip_configuration_lost);
}else{
//dhcpfailed,butwe'reprovisioned(e.g.,ifwe'reonanipv6-onlynetwork).
sendmessage(cmd_ip_configuration_successful);
//tobesurewedon'tgetstuckwithanon-workingnetworkifallwehadis
//ipv4,removetheipv4addressfromtheinterface(sincewe'reusingdhcp,
//anddhcpfailed).ifwehadanipv4addressbefore,thedeletionofthe
//addresswillcauseacmd_update_linkproperties.iftheipv4addresswas
//necessaryforprovisioning,itsdeletionwillcauseustodisconnect.
//
//thisshouldn'tbeneeded,becaunanipv4-onlynetworkadhcpfailurewill
//haveemptydhcpresultsandthusemptylinkproperties,andisprovisionedwill
//notreturntrueifwe'reusingdhcpanddon'thaveanipv4defaultroute.so
//fornowit'sonlyhereforextraredundancy.however,itwillincrease
//robustnessifwemovetogettingipv4routesfromnetlinkaswell.
loge("dhcpfailure:provisioned,clearingipv4address.");
if(!clearipv4address(minterfacename)){
sendmessage(cmd_ip_configuration_lost);
}
}
break;
casecmd_static_ip_failure:
//staticconfigurationwasinvalid,oranerroroccurredinapplyingit.giveup.
sendmessage(cmd_ip_configuration_lost);
break;
casecmd_update_linkproperties:
//ipaddresses,dnsservers,etc.changed.actaccordingly.
if(lostprovisioning){
//wenolongerhaveausablenetworkconfiguration.disconnect.
sendmessage(cmd_ip_configuration_lost);
}elseif(!wasprovisioned&&isprovisioned){
//wehaveausableipv6-onlyconfig.advancetoconnectedstate.
sendmessage(cmd_ip_configuration_successful);
}
if(linkchanged&&getnetworkdetailedstate()==detailedstate.connected){
//ifanythinghaschangedandwe'realreadyconnected,sendoutanotification.
sendlinkconfigurationchangedbroadcast();
}
break;
}
} 最后会根据dhcp是否成功发送cmd_ip_configuration_successful消息表明ip的配置已经完成(因为我们已经告知了connectifyservice去更新网络配置,并附加了新的mlinkproperties对象),l2connectedstate处理cmd_ip_configuration_successful消息:
casecmd_ip_configuration_successful:
handlesuccessfulipconfiguration();
sendconnectedstate();
transitionto(mconnectedstate); handlesuccessfulipconfiguration()函数处理:
privatevoidhandlesuccessfulipconfiguration(){
mlastsignallevel=-1;//forceupdateofsignalstrength
wificonfigurationc=getcurrentwificonfiguration();//获取代表当前连接的网络的wificonfiguration对象
if(c!=null){
//resetipfailuretracking
c.numconnectionfailures=0;
//重置numconnectionfailures字段,因为在wificonfigstore中,会根据numconnectionfailures字段判断当前网络的连接失败次数,默认失败10次时,会终止重连操作
//telltheframeworkwhetherthenewlyconnectednetworkistrustedoruntrusted.
updatecapabilities(c);
}
if(c!=null){
scanresultresult=getcurrentscanresult();
if(result==null){
logd("wifistatemachine:handlesuccessfulipconfigurationandnoscanresults"+
c.configkey());
}else{
//cleartheperbssidfailurecount
result.numipconfigfailures=0;
//clearthewholebssidblacklist,whichmeanssupplicantisfreetoretry
//anybssid,eventhoughitmayalreadyhaveanonzeroipfailurecount,
//thiswilltypicallyhappeniftheuserwalksawayandcomebacktohisarrea
//todo:implementblacklistingbasedonatimer,i.e.keepbssidblacklisted
//insupplicantforacoupleofhoursoraday
mwificonfigstore.clearbssidblacklist();
}
}
} sendconnectedstate()主要做一些网络状态的更新操作,发送network_state_changed_action通知当前的网络状态已经变化。此广播会附加wifistatemachine中三个常用的字段值:
/**
*thelinkpropertiesofthewifiinterface.
*donotmodifythisdirectly;useupdatelinkpropertiesinstead.
*/
privatelinkpropertiesmlinkproperties;//保存当前网络连接的配置信息,包括ip地址集、dns地址集、路由地址集等。
//note:donotreturntoclients-use#getwifiinfoforuid(int)
privatewifiinfomwifiinfo;//描述了一个wifi连接的状态,通过该对象我们可以得知当前wifi连接的ssid、netid、ip地址、mac地址等信息
privatenetworkinfomnetworkinfo;//表示一个网络接口(wlan0/eth0)的连接状态,通过该对象可以得知当前wifi连接处于哪一步、是否连接等状态 最后切换到connectedstate,至此ap已经连接、ip也已获取到,并且网络状态也已更新。后续的过程貌似没什么大用了。一个wifi的连接流程到这里就分析完了。
ps:
4.4之后android加入了ap评分机制。这个机制根据某个ap的很多配置信息一通处理,最后得出一个score,来表明某个ap的可连接性。它评判的属性有点多,后面在研究,现在本渣还是没有搞明白,囧....
在framework里面,其实屏蔽了很多复杂的操作,比如wifinative里面的大多数方法都是要通过jni调用wifi.c,再调到wpa_s。这些中间过程其实比framework的处理要复杂的多,代码也更晦涩。例如rundhcp()动作,有个专门的dhcpcd守护进程进行处理;它的实现跟wpa_s一样,都是很复杂的。即使了解dhcp协议的工作流程,要看懂它的代码实现也很痛苦。android里还有的pppoe拨号方式,它的守护进程pppd的实现也很复杂......;要做这方面的定制,对于偏向framework的人来说,是比较痛苦的。
推荐阅读
-
Android中打电话的数据流程分析
-
Android编程开发中ListView的常见用法分析
-
Android6.0开发中屏幕旋转原理与流程分析
-
Android开发中Wifi连接流程分析
-
Android开发实现AlertDialog中View的控件设置监听功能分析
-
android开发中如何使用jdbc连接数据库
-
adb通过wifi连接android设备流程解析
-
Android开发:自动连接指定SSID的wifi热点(不加密/加密)
-
Android Studio3.0开发JNI流程------JNI开发遇到中stack corruption detected[Fatal signal 6 (SIGABRT)......debug]
-
Android Studio3.0开发JNI流程------在JNI中输出打印日志