本文为看雪论坛优秀文章
看雪论坛作者ID:卓桐
首先简单说下HTTP代理的原理和实现
举例,比如手机访问www.baidu.com,手机的ip为192.168.2.2,代理服务器的ip为192.168.3.4,端口为8087。
代理服务器这边socket监听8087端口,手机和代理服务器建立socket连接,把http请求的内容通过socket发送到代理服务器,代理服务器解析出Host。
得到host=www.baidu.com和port=80,然后解析www.baidu.com对应的ip=220.181.38.148,建立socket连接,把http请求的内容通过socket发送到220.181.38.148。同理把返回的内容发送回手机。
目前发现有两种代理方式,1是WiFi代理,2是全局代理。
手机设置WiFi
本来打算写全部的流程,但是从设置到WiFi涉及到内容实在太多了,我追了两天,所以尽量列重点,忽略不重要到。
一般是设置中选择WiFi长按,填入代理服务器,端口等。定位到实现代码
packages/apps/Settings/src/com/android/settings/wifi/WifiConfigController.java
publicWifiConfigController(
WifiConfigUiBase parent, View view, AccessPoint accessPoint, boolean edit) {
...
WifiInfo info = mAccessPoint.getInfo;
if(info != null&& info.getLinkSpeed != -1) {
addRow(group, R.string.wifi_speed, info.getLinkSpeed + WifiInfo.LINK_SPEED_UNITS);
}
addRow(group, R.string.wifi_security, mAccessPoint.getSecurityString(false));
boolean showAdvancedFields = false;
if(mAccessPoint.networkId != INVALID_NETWORK_ID) {
WifiConfiguration config = mAccessPoint.getConfig;
if(config.ipAssignment == IpAssignment.STATIC) {
mIpSettingsSpinner.setSelection(STATIC_IP);
showAdvancedFields = true;
} else{
mIpSettingsSpinner.setSelection(DHCP);
}
//Display IP addresses
for(InetAddress a : config.linkProperties.getAddresses) {
addRow(group, R.string.wifi_ip_address, a.getHostAddress);
}
//构造函数,从设置中取出状态,是否设置代理
if(config.proxySettings == ProxySettings.STATIC) {
mProxySettingsSpinner.setSelection(PROXY_STATIC);
showAdvancedFields = true;
} elseif(config.proxySettings == ProxySettings.PAC) {
mProxySettingsSpinner.setVisibility(View.GONE);
TextView textView = (TextView)mView.findViewById(R.id.proxy_pac_info);
textView.setVisibility(View.VISIBLE);
textView.setText(context.getString(R.string.proxy_url) +
config.linkProperties.getHttpProxy.getPacFileUrl);
showAdvancedFields = true;
} else{
mProxySettingsSpinner.setSelection(PROXY_NONE);
}
}
...
当设置代理的时候,显示给用户:
//展示代理信息
privatevoidshowProxyFields() {
WifiConfiguration config = null;
mView.findViewById(R.id.proxy_settings_fields).setVisibility(View.VISIBLE);
if(mAccessPoint != null&& mAccessPoint.networkId != INVALID_NETWORK_ID) {
config = mAccessPoint.getConfig;
}
if(mProxySettingsSpinner.getSelectedItemwww.58yuanyou.comPosition == PROXY_STATIC) {
mView.findViewById(R.id.proxy_warning_limited_support).setVisibility(View.VISIBLE);
mView.findViewById(R.id.proxy_fields).setVisibility(View.VISIBLE);
if(mProxyHostView == null) {
mProxyHostView = (TextView) mView.findViewById(R.id.proxy_hostname);
mProxyHostView.addTextChangedListener(this);
mProxyPortView = (TextView) mView.findViewById(R.id.proxy_port);
mProxyPortView.addTextChangedListener(this);
mProxyExclusionListView = (TextView) mView.findViewById(R.id.proxy_exclusionlist);
mProxyExclusionListView.addTextChangedListener(this);
}
if(config != null) {
//获取设置过的代理信息
ProxyProperties proxyProperties = config.linkProperties.getHttpProxy;
if(proxyProperties != null) {
mProxyHostView.setText(proxyProperties.getHost);
mProxyPortView.setText(Integer.toString(proxyProperties.getPort));
mProxyExclusionListView.setText(proxyProperties.getExclusionList);
}
}
} else{
mView.findViewById(R.id.proxy_warning_limited_support).setVisibility(View.GONE);
mView.findViewById(R.id.proxy_fields).setVisibility(View.GONE);
}
}
//保存代理设置
privateboolean ipAndProxyFieldsAreValid() {
mLinkProperties.clear;
mIpAssignment = (mIpSettingsSpinner != null&&
mIpSettingsSpinner.getSelectedItemPosition == STATIC_IP) ?
IpAssignment.STATIC : IpAssignment.DHCP;
if(mIpAssignment == IpAssignment.STATIC) {
intresult = validateIpConfigFields(mLinkProperties);
if(result != 0) {
returnfalse;
}
}
mProxySettings = (mProxySettingsSpinner != null&&
mProxySettingsSpinner.getSelectedItemPosition == PROXY_STATIC) ?
ProxySettings.STATIC : ProxySettings.NONE;
if(mProxySettings == ProxySettings.STATIC && mProxyHostView != null) {
String host = mProxyHostView.getText.toString;
String portStr = mProxyPortView.getText.toString;
String exclusionList = mProxyExclusionListView.getText.toString;
intport = 0;
intresult = 0;
try{
port = Integer.parseInt(portStr);
//验证host和port是否正确
result = ProxySelector.validate(host, portSXALPcdtr, exclusionList);
} catch(NumberFormatException e) {
result = R.string.proxy_error_invalid_port;
}
if(result == 0) {
ProxyProperties proxyProperties= newProxyProperties(host, port, exclusionList);
mLinkProperties.setHttpProxy(proxyProperties);
} else{
returnfalse;
}
}
returntrue;
}
保存和获取都是通过LinkProperties操作ProxyProperties,而ProxyProperties只是赋值和获取,没有其他操作。
publicvoidsetHttpProxy(ProxyProperties proxy){
mHttpProxy = proxy;
}
publicProxyProperties getHttpProxy{
returnmHttpProxy;
}
WifiConfigController属于WifiDialog,WifiDialog 是我们长按时弹出到设置IP、代理等的窗口。WifiDialog 属于packages/apps/Settings/src/com/android/settings/wifi/WifiSettings.java
//当添加新的网络或者编辑保存触发,远程调用mWifiManager.save
/* package */voidsubmit(WifiConfigController configController){
finalWifiConfiguration config = configController.getConfig;
if(config == null) {
if(mSelectedAccessPoint != null
&& mSelectedAccessPoint.networkId != INVALID_NETWORK_ID) {
mWifiManager.connect(mSelectedAccessPoint.networkId,
mConnectListener);
}
} elseif(config.networkId != INVALID_NETWORK_ID) {
if(mSelectedAccessPoint != null) {
mWifiManager.save(config, mSaveListener);
}
} else{
if(configController.isEdit) {
mWifiManager.save(config, mSaveListener);
} else{
mWifiManager.connect(config, mConnectListener);
}
}
if(mWifiManager.isWifiEnabled) {
mScanner.resume;
}
updateAccessPoints;
}
关于远程调用,安卓系统很多都是基于binder,一般不直接调用binder,而是使用AIDL(接口定义语言)和IDE工具生成封装好的代码。这里出现了一个新的远程调用的方式:Messenger。可能很多人不了解,因为一般只有系统framework有使用且使用不算多。本质上来说其实现也是基于binder实现跨进程调用,和AIDL个人总结有如下不同:
1、不用定义aidl文件。
2、Messenger只提供了一个方法进行进程间通信,send(Message msg)方法,发送一个Message,没有返回值,要拿到返回值,需要把client的Messenger作为msg.replyTo参数传递过去,service端处理完之后,在调用客户端的Messenger的send(Message msg)方法把返回值传递回client,这个过程是异步的。而AIDL你可以自己指定方法,指定返回值,它获取返回值是同步的。而AIDL调用默认是同步的,当然其实也可以异步。以上是针对客户端来说。
3、使用AIDL的时候,service端每收到一个client端的请求时,就会启动一个线程(非主线程)去执行相应的操作。而Messenger,service端收到的请求是放在Handler的MessageQueue里面,Handler大家都用过,它需要绑定一个Thread,然后不断poll message执行相关操作,这个过程是同步执行的。
4、Messenger需要和Handler绑定使用。
这里简单说下,有兴趣的可以自己写写例子熟悉使用。
WifiManager.java,sAsyncChanne内部封装的就是Messenger,就不展开了。
publicvoidsave(WifiConfiguration config, ActionListener listener) {
if(config == null) thrownewIllegalArgumentException("config cannot be null");
validateChannel;
sAsyncChannel.sendMessage(SAVE_NETWORK, 0, putListener(listener), config);
}
远程调用frameworks/base/services/java/com/android/server/wifi/WifiService.java,调用mWifiStateMachine.sendMessage
caseWifiManager.CONNECT_NETWORK:
caseWifiManager.SAVE_NETWORK: {
WifiConfiguration config = (WifiConfiguration) msg.obj;
intnetworkId = msg.arg1;
if(config != null&& config.isValid) {
// This is restricted because there is no UI for the user to
// monitor/control PAC.
if(config.proxySettings != ProxySettings.PAC) {
if(DBG) Slog.d(TAG, "Connect with config"+ config);
//继续传递msg
mWifiStateMachine.sendMessage(Message.obtain(msg));
} else{
Slog.e(TAG, "ClientHandler.handleMessage cannot process msg with PAC");
if(msg.what == WifiManager.CONNECT_NETWORK) {
replyFailed(msg, WifiManager.CONNECT_NETWORK_FAILED);
} else{
replyFailed(msg, WifiManager.SAVE_NETWORK_FAILED);
}
}
} elseif(config == null
&& networkId != WifiConfiguration.INVALID_NETWORK_ID) {
if(DBG) Slog.d(TAG, "Connect with networkId"+ networkId);
mWifiStateMachine.sendMessage(Message.obtain(msg));
} else{
Slog.e(TAG, "ClientHandler.handleMessage ignoring invalid msg="+ msg);
if(msg.what == WifiManager.CONNECT_NETWORK) {
replyFailed(msg, WifiManager.CONNECT_NETWORK_FAILED);
} else{
replyFailed(msg, WifiManager.SAVE_NETWORK_FAILED);
}
}
break;
}
/frameworks/base/wifi/java/android/net/wifi/WifiStateMachine.java,获取WifiConfiguration,调用mWifiConfigStore.saveNetwork
caseWifiManager.SAVE_NETWORK:
config = (WifiConfiguration) message.obj;
NetworkUpdateResult result = mWifiConfigStore.saveNetwork(config);
if(result.getNetworkId != WifiConfiguration.INVALID_NETWORK_ID) {
replyToMessage(message, WifiManager.SAVE_NETWORK_SUCCEEDED);
} else{
loge("Failed to save network");
replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED,
WifiManager.ERROR);
}
break;
/frameworks/base/wifi/java/android/net/wifi/WifiConfigStore.java
NetworkUpdateResult saveNetwork(WifiConfiguration config) {
if(VDBG) localLog("saveNetwork", config.networkId);
// A new network cannot have null SSID
if(config == null|| (config.networkId == INVALID_NETWORK_ID &&
config.SSID == null)) {
returnnewNetworkUpdateResult(INVALID_NETWORK_ID);
}
boolean newNetwork = (config.networkId == INVALID_NETWORK_ID);
//新增或者更新
NetworkUpdateResult result = addOrUpdateNetworkNative(config);
intnetId = result.getNetworkId;
/* enable a new network */
if(newNetwork && netId != INVALID_NETWORK_ID) {
//新增wifi,断开的当前连接的wifi,再连接指定wifi
mWifiNative.enableNetwork(netId, false);
mConfiguredNetworks.get(netId).status = Status.ENABLED;
}
//mWifiNative的调用会到wpa_ctrl,很复杂,就不列出了
mWifiNative.saveConfig;
sendConfiguredNetworksChangedBroadcast(config, result.isNewNetwork ?
WifiManager.CHANGE_REASON_ADDED : WifiManager.CHANGE_REASON_CONFIG_CHANGE);
returnresult;
}
//主要是保存ip、代理
privateNetworkUpdateResult addOrUpdateNetworkNative(WifiConfiguration config) {
...
//获取当前连接
WifiConfiguration currentConfig = mConfiguredNetworks.get(netId);
if(currentConfig == null) {
currentConfig = newWifiConfiguration;
currentConfig.ipAssignment = IpAssignment.DHCP;
currentConfig.proxySettings = ProxySettings.NONE;
currentConfig.networkId = netId;
}
readNetworkVariables(currentConfig);
mConfiguredNetworks.put(netId, currentConfig);
mNetworkIds.put(configKey(currentConfig), netId);
//把远程传进来的ip、代理等写入当前进程的WifiConfiguration
NetworkUpdateResult result = writeIpAndProxyConfigurationsOnChange(currentConfig, config);
result.setIsNewNetwork(newNetwork);
result.setNetworkId(netId);
returnresult;
}
WifiNative调用到wpa_ctrl,整个流程比较复杂。大概流程如下,把配置信息保存到wpa_supplicant,之后触发一些广播,主要的有:
WifiManager.LINK_CONFIGURATION_CHANGED_ACTION和WifiManager.NETWORK_STATE_CHANGED_ACTION
/frameworks/base/wifi/java/android/net/wifi/WifiStateTracker.java注册了广播接收者。
privateclassWifiStateReceiverextendsBroadcastReceiver{
@Override
publicvoidonReceive(Context context, Intent intent){
if(intent.getAction.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
...
Message msg = mCsHandler.obtainMessage(EVENT_STATE_CHANGED,
newNetworkInfo(mNetworkInfo));
msg.sendToTarget;
} elseif(intent.getAction.equals(WifiManager.LINK_CONFIGURATION_CHANGED_ACTION)) {
mLinkProperties = intent.getParcelableExtra(WifiManager.EXTRA_LINK_PROPERTIES);
Message msg = mCsHandler.obtainMessage(EVENT_CONFIGURATION_CHANGED, mNetworkInfo);
msg.sendToTarget;
}
}
}
在
frameworks/base/services/java/com/android/server/ConnectivityService.java内
privatestaticclassDefaultNetworkFactoryimplementsNetworkFactory{
...
@Override
publicNetworkStateTracker createTracker(inttargetNetworkType, NetworkConfig config){
switch(config.radio) {
caseTYPE_WIFI:
returnnewWifiStateTracker(targetNetworkType, config.name);
...
}
}
}
//构造函数内
try{
tracker = netFactory.createTracker(targetNetworkType, config);
mNetTrackers[targetNetworkType] = tracker;
} catch(IllegalArgumentException e) {
Slog.e(TAG, "Problem creating "+ getNetworkTypeName(targetNetworkType)
+ " tracker: "+ e);
continue;
}
tracker.startMonitoring(context, mTrackerHandler);
内部类:
privateclassNetworkStateTrackerHandlerextendsHandler{
publicNetworkStateTrackerHandler(Looper looper){
super(looper);
}
@Override
publicvoidhandleMessage(Message msg){
NetworkInfo info;
switch(msg.what) {
caseNetworkStateTracker.EVENT_STATE_CHANGED: {
info = (NetworkInfo) msg.obj;
NetworkInfo.State state = info.getState;
...
} elseif(state == NetworkInfo.State.CONNECTED) {
//调用handleConnectivityChange
handleConnect(info);
}
if(mLockdownTracker != null) {
mLockdownTracker.onNetworkInfoChanged(info);
}
break;
}
caseNetworkStateTracker.EVENT_CONFIGURATION_CHANGED: {
info = (NetworkInfo) msg.obj;
handleConnectivityChange(info.getType, false);
break;
}
...
}
}
}
}
//EVENT_CONFIGURATION_CHANGED和EVENT_STATE_CHANGED最后都会调用到
privatevoidhandleConnectivityChange(intnetType, booleandoReset){
...
if(mNetConfigs[netType].isDefault) {
handleApplyDefaultProxy(newLp.getHttpProxy);
}
...
}
//所以应该全局代理的优先级比wifi代理高
privatevoidhandleApplyDefaultProxy(ProxyProperties proxy){
...
if(mGlobalProxy != null) return;
if(!mDefaultProxyDisabled) {
sendProxyBroadcast(proxy);
}
}
}
//从这里和后面的全局代理有重叠,就不继续了
privatevoidsendProxyBroadcast(ProxyProperties proxy){
if(proxy == null) proxy = newProxyProperties("", 0, "");
if(mPacManager.setCurrentProxyUrl(proxy)) return;
if(DBG) log("sending Proxy Broadcast for "+ proxy);
Intent intent = newIntent(Proxy.PROXY_CHANGE_ACTION);
intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING |
Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
intent.putExtra(Proxy.EXTRA_PROXY_INFO, proxy);
finallongident = Binder.clearCallingIdentity;
try{
mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
} finally{
Binder.restoreCallingIdentity(ident);
}
}
WiFi代理分析到sendProxyBroadcast,因为后面的流程和全局代理一致。
通过设置Settings.Global.GLOBAL_HTTP_PROXY_HOST/Settings.Global.HTTP_PROXY代理
在frameworks/base/services/java/com/android/server/ConnectivityService.java中注册了监听器。
//注册监听器
mSettingsObserver = newSettingsObserver(mHandler, EVENT_APPLY_GLOBAL_HTTP_PROXY);
mSettingsObserver.observe(mContext);
//实现
privatestaticclassSettingsObserverextendsContentObserver{
privateintmWhat;
privateHandler mHandler;
SettingsObserver(Handler handler, intwhat) {
super(handler);
mHandler = handler;
mWhat = what;
}
voidobserve(Context context){
ContentResolver resolver = context.getContentResolver;
resolver.registerContentObserver(Settings.Global.getUriFor(
Settings.Global.HTTP_PROXY), false, this);
}
//当修改Settings.Global.HTTP_PROXY时触发
@Override
publicvoidonChange(booleanselfChange){
mHandler.obtainMessage(mWhat).sendToTarget;
}
}
//内部handler的部分代码
caseEVENT_APPLY_GLOBAL_HTTP_PROXY: {
handleDeprecatedGlobalHttpProxy;
break;
}
//取出设置的http代理,封装
privatevoidhandleDeprecatedGlobalHttpProxy{
String proxy = Settings.Global.getString(mContext.getContentResolver,
Settings.Global.HTTP_PROXY);
if(!TextUtils.isEmpty(proxy)) {
String data[] = proxy.split(":");
if(data.length == 0) {
return;
}
String proxyHost = data[0];
intproxyPort = 8080;
if(data.length > 1) {
try{
proxyPort = Integer.parseInt(data[1]);
} catch(NumberFormatException e) {
return;
}
}
ProxyProperties p = newProxyProperties(data[0], proxyPort, "");
setGlobalProxy(p);
}
}
//解析出host和port,用Settings.Global.GLOBAL_HTTP_PROXY_HOST存储
publicvoidsetGlobalProxy(ProxyProperties proxyProperties){
enforceConnectivityInternalPermission;
synchronized(mProxyLock) {
if(proxyProperties == mGlobalProxy) return;
if(proxyProperties != null&& proxyProperties.equals(mGlobalProxy)) return;
if(mGlobalProxy != null&& mGlobalProxy.equals(proxyProperties)) return;
String host = "";
intport = 0;
String exclList = "";
String pacFileUrl = "";
if(proxyProperties != null&& (!TextUtils.isEmpty(proxyProperties.getHost) ||
!TextUtils.isEmpty(proxyProperties.getPacFileUrl))) {
if(!proxyProperties.isValid) {
if(DBG)
log("Invalid proxy properties, ignoring: "+ proxyProperties.toString);
return;
}
mGlobalProxy = newProxyProperties(proxyProperties);
host = mGlobalProxy.getHost;
port = mGlobalProxy.getPort;
exclList = mGlobalProxy.getExclusionList;
if(proxyProperties.getPacFileUrl != null) {
pacFileUrl = proxyProperties.getPacFileUrl;
}
} else{
mGlobalProxy = null;
}
ContentResolver res = mContext.getContentResolver;
finallongtoken = Binder.clearCallingIdentity;
try{
//保存host和port
Settings.Global.putString(res, Settings.Global.GLOBAL_HTTP_PROXY_HOST, host);
Settings.Global.putInt(res, Settings.Global.GLOBAL_HTTP_PROXY_PORT, port);
Settings.Global.putString(res, Settings.Global.GLOBAL_HTTP_PROXY_EXCLUSION_LIST,
exclList);
Settings.Global.putString(res, Settings.Global.GLOBAL_HTTP_PROXY_PAC, pacFileUrl);
} finally{
Binder.restore原由网CallingIdentity(token);
}
}
if(mGlobalProxy == null) {
proxyProperties = mDefaultProxy;
}
//发送通知
sendProxyBroadcast(proxyProperties);
}
//发送广播Proxy.PROXY_CHANGE_ACTION
privatevoidsendProxyBroadcast(ProxyProperties proxy){
if(proxy == null) proxy = newProxyProperties("", 0, "");
if(mPacManager.setCurrentProxyUrl(proxy)) return;
if(DBG) log("sending Proxy Broadcast for "+ proxy);
Intent intent = newIntent(Proxy.PROXY_CHANGE_ACTION);
intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING |
Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
intent.putExtra(Proxy.EXTRA_PROXY_INFO, proxy);
finallongident = Binder.clearCallingIdentity;
try{
mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
} finally{
Binder.restoreCallingIdentity(ident);
}
}
调用
frameworks/base/core/java/android/app/ContextImpl.java
@Override
publicvoidsendStickyBroadcastAsUser(Intent intent, UserHandle user) {
StringresolvedType = intent.resolveTypeIfNeeded(getContentResolver);
try{
intent.prepareToLeaveProcess;
ActivityManagerNative.getDefault.broadcastIntent(
mMainThread.getApplicationThread, intent, resolvedType, null,
Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, false, true, user.getIdentifier);
} catch(RemoteException e) {
}
}
最终调用到的是frameworks/base/services/java/com/android/server/am/ActivityManagerService.java
publicfinalintbroadcastIntent(IApplicationThread caller,
Intent intent, String resolvedType, IIntentReceiver resultTo,
intresultCode, String resultData, Bundle map,
String requiredPermission, intappOp, booleanserialized, booleansticky, intuserId){
enforceNotIsolatedCaller("broadcastIntent");
synchronized(this) {
intent = verifyBroadcastLocked(intent);
finalProcessRecord callerApp = getRecordForAppLocked(caller);
finalintcallingPid = Binder.getCallingPid;
finalintcallingUid = Binder.getCallingUid;
finallongorigId = Binder.clearCallingIdentity;
intres = broadcastIntentLocked(callerApp,
callerApp != null? callerApp.info.packageName : null,
intent, resolvedType, resultTo,
resultCode, resultData, map, requiredPermission, appOp, serialized, sticky,
callingPid, callingUid, userId);
Binder.restoreCallingIdentity(origId);
returnres;
}
}
//接收处理Proxy.PROXY_CHANGE_ACTION
privatefinalintbroadcastIntentLocked(ProcessRecord callerApp,
String callerPackage, Intent intent, String resolvedType,
IIntentReceiver resultTo, intresultCode, String resultData,
Bundle map, String requiredPermission, intappOp,
booleanordered, booleansticky, intcallingPid, intcallingUid,
intuserId){
...
if(Proxy.PROXY_CHANGE_ACTION.equals(intent.getAction)) {
ProxyProperties proxy = intent.getParcelableExtra("proxy");
mHandler.sendMessage(mHandler.obtainMessage(UPDATE_HTTP_PROXY_MSG, proxy));
}
...
}
//handler接收UPDATE_HTTP_PROXY_MSG,解析出host,port,调用ApplicationThread的setHttpProxy
caseUPDATE_HTTP_PROXY_MSG: {
ProxyProperties proxy = (ProxyProperties)msg.obj;
String host = "";
String port = "";
String exclList = "";
String pacFileUrl = null;
if(proxy != null) {
host = proxy.getHost;
port = Integer.toString(proxy.getPort);
exclList = proxy.getExclusionList;
pacFileUrl = proxy.getPacFileUrl;
}
synchronized(ActivityManagerService.this) {
for(inti = mLruProcesses.size - 1; i >= 0; i--) {
ProcessRecord r = mLruProcesses.get(i);
if(r.thread != null) {
try{
r.thread.setHttpProxy(host, port, exclList, pacFileUrl);
} catch(RemoteException ex) {
Slog.w(TAG, "Failed to update http proxy for: "+
r.info.processName);
}
}
}
}
} break;
注意这里是在system_server进程,调用ApplicationThread的setHttpProxy属于跨进程调用,通过binder机制,只后回到应用进程。(上面的代码是已启动的应用进程都调用一遍)
ApplicationThread属于frameworks/base/core/java/android/app/ActivityThread.java的内部类
publicvoidsetHttpProxy(Stringhost, Stringport, StringexclList, StringpacFileUrl) {
Proxy.setHttpProxySystemProperty(host, port, exclList, pacFileUrl);
}
frameworks/base/core/java/android/net/Proxy.java,设置java环境变量,存储HTTP、HTTPS的代理host、port。
publicstaticfinal voidsetHttpProxySystemProperty(Stringhost, Stringport, StringexclList,
StringpacFileUrl) {
if(exclList != null) exclList = exclList.replace(",", "|");
if(false) Log.d(TAG, "setHttpProxySystemProperty :"+host+":"+port+" - "+exclList);
if(host != null) {
System.setProperty("http.proxyHost", host);
System.setProperty("https.proxyHost", host);
} else{
System.clearProperty("http.proxyHost");
System.clearProperty("https.proxyHost");
}
if(port != null) {
System.setProperty("http.proxyPort", port);
System.setProperty("https.proxyPort", port);
} else{
System.clearProperty("http.proxyPort");
System.clearProperty("https.proxyPort");
}
if(exclList != null) {
System.setProperty("http.nonProxyHosts", exclList);
System.setProperty("https.nonProxyHosts", exclList);
} else{
System.clearProperty("http.nonProxyHosts");
System.clearProperty("https.nonProxyHosts");
}
if(!TextUtils.isEmpty(pacFileUrl)) {
ProxySelector.setDefault(newPacProxySelector);
} else{
ProxySelector.setDefault(sDefaultProxySelector);
}
}
以上就是设置全局代理的流程,但是如果应用进程没启动没怎么办,其实在应用进程创建执行application之前调用ConnectivityService.java的getProxy,Proxy.setHttpProxySystemProperty会调用上面的重载函数,达到一致的效果。
IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
if(b != null) {
// In pre-boot mode (doing initial launch to collect password), not
// all systeXALPcdm is up. This includes the connectivity service, so don't
// crash if we can't get it.
IConnectivityManager service = IConnectivityManager.Stub.asInterface(b);
try{
ProxyProperties proxyProperties = service.getProxy;
Proxy.setHttpProxySystemProperty(proxyProperties);
} catch(RemoteException e) {}
}
代理生效
结合我们之前的DNS流程一文,其中有个关键的类RouteSelector。
publicRouteSelector(Address address, URI uri, ProxySelector proxySelector, ConnectionPool pool,
Dns dns, RouteDatabase routeDatabase) {
this.address = address;
this.uri = uri;
this.proxySelector = proxySelector;
this.pool = pool;
this.dns = dns;
this.routeDatabase = routeDatabase;
this.postponedRoutes = newLinkedList<Route>;
//读取是否设置全局代理
resetNextProxy(uri, address.getProxy);
}
//proxy为null,使用全局或者wifi代理,非应用内代理
/** Resets {@link #nextProxy} to the first option. */
privatevoidresetNextProxy(URI uri, Proxy proxy) {
this.hasNextProxy = true; // This includes NO_PROXY!
if(proxy != null) {
this.userSpecifiedProxy = proxy;
} else{//根据uri获取代理
List<Proxy> proxyList = proxySelector.select(uri);
if(proxyList != null) {
this.proxySelectorProxies = proxyList.iterator;
}
}
}
ProxySelector为构造函数参入的参数,ProxySelector为抽象类,实现为ProxySelectorImpl。
private staticProxySelector defaultSelector = newProxySelectorImpl;
//入口
@Override public List<Proxy> select(URI uri) {
returnCollections.singletonList(selectOneProxy(uri));
}
//解析url的请求类型,查找设置的java环境变量中的代理host和port
private ProxyselectOneProxy(URI uri) {
if(uri == null) {
thrownewIllegalArgumentException("uri == null");
}
Stringscheme = uri.getScheme;
if(scheme == null) {
thrownewIllegalArgumentException("scheme == null");
}
int port = -1;
Proxyproxy = null;
StringnonProxyHostsKey = null;
boolean httpProxyOkay = true;
if("http".equalsIgnoreCase(scheme)) {
port = 80;
nonProxyHostsKey = "http.nonProxyHosts";
proxy = lookupProxy("http.proxyHost", "http.proxyPort", Proxy.Type.HTTP, port);
} elseif("https".equalsIgnoreCase(scheme)) {
port = 443;
nonProxyHostsKey = "https.nonProxyHosts"; // RI doesn't support this
proxy = lookupProxy("https.proxyHost", "https.proxyPort", Proxy.Type.HTTP, port);
} elseif("ftp".equalsIgnoreCase(scheme)) {
port = 80; // not 21 as you might guess
nonProxyHostsKey = "ftp.nonProxyHosts";
proxy = lookupProxy("ftp.proxyHost", "ftp.proxyPort", Proxy.Type.HTTP, port);
} elseif("socket".equalsIgnoreCase(scheme)) {
httpProxyOkay = false;
} else{
returnProxy.NO_PROXY;
}
if(nonProxyHostsKey != null
&& isNonProxyHost(uri.getHost, System.getProperty(nonProxyHostsKey))) {
returnProxy.NO_PROXY;
}
if(proxy != null) {
returnproxy;
}
if(httpProxyOkay) {
proxy = lookupProxy("proxyHost", "proxyPort", Proxy.Type.HTTP, port);
if(proxy != null) {
returnproxy;
}
}
proxy = lookupProxy("socksProxyHost", "socksProxyPort", Proxy.Type.SOCKS, 1080);
if(proxy != null) {
returnproxy;
}
returnProxy.NO_PROXY;
}
private ProxylookupProxy(StringhostKey, StringportKey, Proxy.Type type, int defaultPort) {
Stringhost = System.getProperty(hostKey);
if(host == null|| host.isEmpty) {
returnnull;
}
int port = getSystemPropertyInt(portKey, defaultPort);
returnnewProxy(type, InetSocketAddress.createUnresolved(host, port));
}
如此我们设置的全局/WiFi代理被封装成Proxy,之后结合上一篇文章:
//proxySelectorProxies不为null了
private ProxynextProxy {
// If the user specifies a proxy, try that and only that.
if(userSpecifiedProxy != null) {
hasNextProxy = false;
returnuserSpecifiedProxy;
}
// Try each of the ProxySelector choices until one connection succeeds. If none succeed
// then we'll try a direct connection below.
if(proxySelectorProxies != null) {
while(proxySelectorProxies.hasNext) {
Proxycandidate = proxySelectorProxies.next;
if(candidate.type != Proxy.Type.DIRECT) {
returncandidate;
}
}
}
// Finally try a direct connection.
hasNextProxy = false;
returnProxy.NO_PROXY;
}
//上篇文章dns入口,proxy不为空,类型为http
private voidresetNextInetSocketAddress(Proxyproxy) throws UnknownHostException {
socketAddresses = null; // Clear the addresses. Necessary if getAllByName below throws!
StringsocketHost;
if(proxy.type == Proxy.Type.DIRECT) {
socketHost = uri.getHost;
socketPort = getEffectivePort(uri);
} else{
SocketAddress proxyAddress = proxy.address;
if(!(proxyAddress instanceofInetSocketAddress)) {
thrownewIllegalArgumentException(
"Proxy.address is not an "+ "InetSocketAddress: "+ proxyAddress.getClass);
}
InetSocketAddress proxySocketAddress = (InetSocketAddress) proxyAddress;
//代理服务器的host
socketHost = proxySocketAddress.getHostName;
socketPort = proxySocketAddress.getPort;
}
// Try each address for best behavior in mixed IPv4/IPv6 environments.
socketAddresses = dns.getAllByName(socketHost);
nextSocketAddressIndex = 0;
}
至此两种设置代理的方式流程(其实3种,上篇文章应用进程内设置代理)以及代理如何生效分析完毕。
设置WiFi代理流程:
submit->//WifiSettings.java,新增wifi或者修改ip、代理等保存
== save->//WifiManager.java,内部封装Messenger远程调用
==== handleMessage->//WifiService.java,接收远程调用
====== sendMessage->//WifiStateMachine.java,获取WifiConfiguration
======== saveNetwork->//WifiConfigStore.java
========== addOrUpdateNetworkNative-> //新增或者更新wifi
============ writeIpAndProxyConfigurationsOnChange-> //把远程传进来的ip、代理等写入当前进程的WifiConfiguration
========== enableNetwork/saveConfig -> //把配置信息保存到wpa_supplicant,之后触发一些广播
…
handleConnectivityChange->接收处理LINK_CONFIGURATION_CHANGED_ACTION
handleConnect->接收处理NETWORK_STATE_CHANGED_ACTION
==== handleApplyDefaultProxy-> //全局代理的优先级应该比wifi代理高
====== sendProxyBroadcast-> //发送广播Proxy.PROXY_CHANGE_ACTION
======== broadcastIntentLocked->//接收处理Proxy.PROXY_CHANGE_ACTION,ActivityManagerService.java
========== setHttpProxy->//ApplicationThread的setHttpProxy,回到应用进程执行,ActivityThread.java
============ setHttpProxySystemProperty->//Proxy.java,设置java环境变量,存储http、https的代理host、port。
//应用进程启动情况下
handleBindApplication->//ActivityThread.java
== getProxy->//跨进程调用ConnectivityService.java
==== setHttpProxySystemProperty->//Proxy.java
====== setHttpProxySystemProperty->//Proxy.java
设置全局代理流程:Settings.Global.GLOBAL_HTTP_PROXY_HOST/Settings.Global.HTTP_PROXY
SettingsObserver->//ConnectivityService.java,监听Settings.Global.HTTP_PROXY
handleDeprecatedGlobalHttpProxy->//解析出host,port
==setGlobalProxy->//Settings.Global.GLOBAL_HTTP_PROXY_HOST存储host
====sendProxyBroadcast->//发送广播Proxy.PROXY_CHANGE_ACTION
======broadcastIntentLocked->//接收处理Proxy.PROXY_CHANGE_ACTION,ActivityManagerService.java
========setHttpProxy->//ApplicationThread的setHttpProxy,回到应用进程执行,ActivityThread.java
==========setHttpProxySystemProperty->//Proxy.java,设置java环境变量,存储http、https的代理host、port。
//应用进程启动情况下
handleBindApplication->//ActivityThread.java
==getProxy->//跨进程调用ConnectivityService.java
====setHttpProxySystemProperty->//Proxy.java
======setHttpProxySystemProperty->//Proxy.java
代理生效流程如下:
…略过
RouteSelector->//构造函数
==resetNextProxy->//根据uri获取代理
====select->//ProxySelectorImpl.java
======selectOneProxy->//解析url的请求类型,查找设置的java环境变量中的代理host和port
========lookupProxy->//返回proxy
==========nextProxy->//发起网络请求前先检查是否设置代理
============resetNextInetSocketAddress->//获取dns,如果有设置代理,替换host和port为代理服务器。
代码实现设置WiFi代理
结合分析知道系统调用的是WifiManager.save(WifiConfiguration config,ActionListener listener),这是个隐藏方法,在sdk中无法直接引用,但是可以反射或者直接binder调用远程进程。
经过分析流程,发现其实主要是更新设置的代理到远程服务,而WifiManager有一个函数updateNetwork(WifiConfiguration config)可以实现该功能,而且还可以少构造一个参数。更新之后,怎么使其生效呢?还记得两个广播吗?我们想办法触发其中一个广播即可。
所以先断开连接再连接,触发广播,使代理立即生效。
代码如下:
privateWifiConfiguration getCurrentWifiConfiguration(WifiManager manager) {
if(manager == null|| !manager.isWifiEnabled)
returnnull;
List<WifiConfiguration> c//www.58yuanyou.comonfigurationList = manager.getConfiguredNetworks;
WifiConfiguration configuration = null;
int networkId = manager.getConnectionInfo.getNetworkId;
for(int i = 0; i < configurationList.size; ++i) {
WifiConfiguration wifiConfiguration = configurationList.get(i);
if(wifiConfiguration.networkId == networkId)
configuration = wifiConfiguration;
}
returnconfiguration;
}
//打印下设置过的代理
privatebooleangetWiFiProxySettings {
// 获得 WifiConfiguration
WifiManager manager = (WifiManager) this.getApplicationContext.getSystemService(Context.WIFI_SERVICE);
WifiConfiguration config = getCurrentWifiConfiguration(manager);
if(null== config)
returnfalse;
try{
if(android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {//26
ProxyInfo httpProxy = config.getHttpProxy;
Log.e("zhuo", "httpProxy="+httpProxy);
}
// 从 WifiConfiguration 中获得 linkProperties
ObjectlinkProperties = getField(config, "linkProperties");
if(null== linkProperties)
returnfalse;
// 获得 getHttpProxy 方法
Class<?> proxyPropertiesClass = Class.forName("android.net.ProxyProperties");
Class<?> lpClass = Class.forName("android.net.LinkProperties");
Method getHttpProxy = lpClass.getDeclaredMethod("getHttpProxy");
getHttpProxy.setAccessible(true);
ObjectproxyProperties = getHttpProxy.invoke(linkProperties);
Log.e("zhuo", "proxyProperties="+proxyProperties);
returntrue;
} catch(Exception e) {
e.printStackTrace;
returnfalse;
}
}
//传入代理host和port
privatebooleansetWiFiProxySettings(Stringip, int port) {
// 获得 WifiConfiguration
WifiManager manager = (WifiManager) this.getApplicationContext.getSystemService(Context.WIFI_SERVICE);
WifiConfiguration config = getCurrentWifiConfiguration(manager);
if(null== config)
returnfalse;
try{
// 从 WifiConfiguration 中获得 linkProperties
ObjectlinkProperties = getField(config, "linkProperties");
if(null== linkProperties)
returnfalse;
// 获得 setHttpProxy 方法
Class<?> ProxyProperties = Class.forName("android.net.ProxyProperties");
Class<?> LinkProperties = Class.forName("android.net.LinkProperties");
Method setHttpProxy = LinkProperties.getDeclaredMethod("setHttpProxy", ProxyProperties);
setHttpProxy.setAccessible(true);
// 获得 ProxyProperties 构造方法
Constructor proxyPropertiesCtor = ProxyProperties.getConstructor(String.class, int.class, String.class);
// 创建 ProxyProperties 的对象
ObjectproxySettings = proxyPropertiesCtor.newInstance(ip, port, null);
// 反射调用 linkProperties 的 setHttpProxy 方法, 参数为 ProxyProperties
setHttpProxy.invoke(linkProperties, proxySettings);
setProxySettings("STATIC", config);
// 保存设置
manager.updateNetwork(config);
manager.disconnect;
manager.reconnect;//需先断开再连接
Log.e("zhuo", "保存Proxy设置成功");
returntrue;
} catch(Exception e) {
e.printStackTrace;
returnfalse;
}
}
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
添加权限。
通过以上代码可以设置WiFi代理,可以把其他应用的HTTP(S)代理到设置的代理服务器。
检测代理和绕过代理以及反检测和反绕过待续。
补充
当设置的代理无法连通时,会跳过代理。
HttpURLConnectionImpl.java:
privatebooleanexecute(booleanreadResponse)throwsIOException {
try{
httpEngine.sendRequest;
if(readResponse) {
httpEngine.readResponse;
}
returntrue;
} catch(IOException e) {
//捕获异常
if(handleFailure(e)) {
returnfalse;
} else{
throwe;
}
}
}
//routeSelector.connectFailed,把异常传递给RouteSelector
privatebooleanhandleFailure(IOException e)throwsIOException {
RouteSelector routeSelector = httpEngine.routeSelector;
if(routeSelector != null&& httpEngine.connection != null) {
routeSelector.connectFailed(httpEngine.connection, e);
}
OutputStream requestBody = httpEngine.getRequestBody;
booleancanRetryRequestBody = requestBody == null
|| requestBody instanceofRetryableOutputStream;
if(routeSelector == null&& httpEngine.connection == null// No connection.
|| routeSelector != null&& !routeSelector.hasNext // No more routes to attempt.
|| !isRecoverable(e)
|| !canRetryRequestBody) {
httpEngineFailure = e;
returnfalse;
}
httpEngine.release(true);
RetryableOutputStream retryableOutputStream = (RetryableOutputStream) requestBody;
httpEngine = newHttpEngine(method, rawRequestHeaders, null, retryableOutputStream);
httpEngine.routeSelector = routeSelector; // Keep the same routeSelector.
returntrue;
}
RouteSelector.java,把不能连通的IP加入路由表:
publicvoidconnectFailed(Connection connection, IOException failure) {
Route failedRoute = connection.getRoute;
if(failedRoute.getProxy.type != Proxy.Type.DIRECT && proxySelector != null) {
// Tell the proxy selector when we fail to connect on a fresh connection.
proxySelector.connectFailed(uri, failedRoute.getProxy.address, failure);
}
routeDatabase.failed(failedRoute, failure);
}
//再次进行网络请求,执行到routeDatabase.shouldPostpone,因为不能连通的代理已经在路由表(黑名单)中,所以继续调用自身,跳过了代理
publicConnection next(String method) throws IOException {
// Always prefer pooled connections over new connections.
for(Connection pooled; (pooled = pool.get(address)) != null; ) {
if(method.equals("GET") || pooled.isReadable) returnpooled;
pooled.close;
}
// Compute the next route to attempt.
if(!hasNextTlsMode) {
if(!hasNextInetSocketAddress) {
if(!hasNextProxy) {
if(!hasNextPostponed) {
thrownewNoSuchElementException;
}
returnnewConnection(nextPostponed);
}
lastProxy = nextProxy;
resetNextInetSocketAddress(lastProxy);
}
lastInetSocketAddress = nextInetSocketAddress;
resetNextTlsMode;
}
boolean modernTls = nextTlsMode == TLS_MODE_MODERN;
Route route = newRoute(address, lastProxy, lastInetSocketAddress, modernTls);
if(routeDatabase.shouldPostpone(route)) {
postponedRoutes.add(route);
// We will only recurse in order to skip previously failed routes. They will be
// tried last.
returnnext(method);
}
returnnewConnection(route);
}
看雪ID:卓桐
https://bbs.pediy.com/user-670707.htm
*本文由看雪论坛 卓桐 原创,转载请注明来自看雪社区
进阶安全圈,不得不读的一本书
﹀
﹀
﹀