查看原文
其他

Android 4.4 WiFi代理流程

卓桐 看雪学院 2021-03-07

本文为看雪论坛优秀文章

看雪论坛作者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

public WifiConfigController(
            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;
                } else if (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);
                }
            }
 
            ...


当设置代理的时候,显示给用户:

//展示代理信息
    private void showProxyFields() {
        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.getSelectedItemPosition() == 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);
        }
    }
     
     
     
    //保存代理设置
        private boolean ipAndProxyFieldsAreValid() {
        mLinkProperties.clear();
        mIpAssignment = (mIpSettingsSpinner != null &&
                mIpSettingsSpinner.getSelectedItemPosition() == STATIC_IP) ?
                IpAssignment.STATIC : IpAssignment.DHCP;
 
        if (mIpAssignment == IpAssignment.STATIC) {
            int result = validateIpConfigFields(mLinkProperties);
            if (result != 0) {
                return false;
            }
        }
 
        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();
            int port = 0;
            int result = 0;
            try {
                port = Integer.parseInt(portStr);
                //验证host和port是否正确
                result = ProxySelector.validate(host, portStr, exclusionList);
            } catch (NumberFormatException e) {
                result = R.string.proxy_error_invalid_port;
            }
            if (result == 0) {
                ProxyProperties proxyProperties= new ProxyProperties(host, port, exclusionList);
                mLinkProperties.setHttpProxy(proxyProperties);
            } else {
                return false;
            }
        }
        return true;
    }


保存和获取都是通过LinkProperties操作ProxyProperties,而ProxyProperties只是赋值和获取,没有其他操作。

public void setHttpProxy(ProxyProperties proxy) {
        mHttpProxy = proxy;
    }
    public ProxyProperties getHttpProxy() {
        return mHttpProxy;
    }


WifiConfigController属于WifiDialog,WifiDialog 是我们长按时弹出到设置IP、代理等的窗口。WifiDialog 属于packages/apps/Settings/src/com/android/settings/wifi/WifiSettings.java

//当添加新的网络或者编辑保存触发,远程调用mWifiManager.save
    /* package */ void submit(WifiConfigController configController) {
 
        final WifiConfiguration config = configController.getConfig();
 
        if (config == null) {
            if (mSelectedAccessPoint != null
                    && mSelectedAccessPoint.networkId != INVALID_NETWORK_ID) {
                mWifiManager.connect(mSelectedAccessPoint.networkId,
                        mConnectListener);
            }
        } else if (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,就不展开了。


public void save(WifiConfiguration config, ActionListener listener) {
        if (config == null) throw new IllegalArgumentException("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

case WifiManager.CONNECT_NETWORK:
                case WifiManager.SAVE_NETWORK: {
                    WifiConfiguration config = (WifiConfiguration) msg.obj;
                    int networkId = 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);
                            }
                        }
                    } else if (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

case WifiManager.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)) {
            return new NetworkUpdateResult(INVALID_NETWORK_ID);
        }
 
        boolean newNetwork = (config.networkId == INVALID_NETWORK_ID);
        //新增或者更新
        NetworkUpdateResult result = addOrUpdateNetworkNative(config);
        int netId = 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);
        return result;
    }
     
    //主要是保存ip、代理
    private NetworkUpdateResult addOrUpdateNetworkNative(WifiConfiguration config) {
        ...
        //获取当前连接
        WifiConfiguration currentConfig = mConfiguredNetworks.get(netId);
        if (currentConfig == null) {
            currentConfig = new WifiConfiguration();
            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);
        return result;
    }


WifiNative调用到wpa_ctrl,整个流程比较复杂。大概流程如下,把配置信息保存到wpa_supplicant,之后触发一些广播,主要的有:
WifiManager.LINK_CONFIGURATION_CHANGED_ACTION和WifiManager.NETWORK_STATE_CHANGED_ACTION
/frameworks/base/wifi/java/android/net/wifi/WifiStateTracker.java注册了广播接收者。


private class WifiStateReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
 
            if (intent.getAction().equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
                ...
                Message msg = mCsHandler.obtainMessage(EVENT_STATE_CHANGED,
                        new NetworkInfo(mNetworkInfo));
                msg.sendToTarget();
            } else if (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内

private static class DefaultNetworkFactory implements NetworkFactory {
        ...
        @Override
        public NetworkStateTracker createTracker(int targetNetworkType, NetworkConfig config) {
            switch (config.radio) {
                case TYPE_WIFI:
                    return new WifiStateTracker(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);


内部类:

private class NetworkStateTrackerHandler extends Handler {
        public NetworkStateTrackerHandler(Looper looper) {
            super(looper);
        }
 
        @Override
        public void handleMessage(Message msg) {
            NetworkInfo info;
            switch (msg.what) {
                case NetworkStateTracker.EVENT_STATE_CHANGED: {
                    info = (NetworkInfo) msg.obj;
                    NetworkInfo.State state = info.getState();
                    ...
                    } else if (state == NetworkInfo.State.CONNECTED) {
                        //调用handleConnectivityChange
                        handleConnect(info);
                    }
                    if (mLockdownTracker != null) {
                        mLockdownTracker.onNetworkInfoChanged(info);
                    }
                    break;
                }
                case NetworkStateTracker.EVENT_CONFIGURATION_CHANGED: {
                    info = (NetworkInfo) msg.obj;
                    handleConnectivityChange(info.getType(), false);
                    break;
                }
                ...
                }
            }
        }
    }
     
 
    //EVENT_CONFIGURATION_CHANGED和EVENT_STATE_CHANGED最后都会调用到
    private void handleConnectivityChange(int netType, boolean doReset) {
        ...
        if (mNetConfigs[netType].isDefault()) {
                handleApplyDefaultProxy(newLp.getHttpProxy());
            }
        ...
    }
     
     
    //所以应该全局代理的优先级比wifi代理高
    private void handleApplyDefaultProxy(ProxyProperties proxy) {
    ...
            if (mGlobalProxy != null) return;
            if (!mDefaultProxyDisabled) {
                sendProxyBroadcast(proxy);
            }
        }
    }
     
     
    //从这里和后面的全局代理有重叠,就不继续了
    private void sendProxyBroadcast(ProxyProperties proxy) {
        if (proxy == null) proxy = new ProxyProperties("", 0, "");
        if (mPacManager.setCurrentProxyScriptUrl(proxy)) return;
        if (DBG) log("sending Proxy Broadcast for " + proxy);
        Intent intent = new Intent(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);
        final long ident = 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 = new SettingsObserver(mHandler, EVENT_APPLY_GLOBAL_HTTP_PROXY);
        mSettingsObserver.observe(mContext);
 
    //实现
    private static class SettingsObserver extends ContentObserver {
        private int mWhat;
        private Handler mHandler;
        SettingsObserver(Handler handler, int what) {
            super(handler);
            mHandler = handler;
            mWhat = what;
        }
 
        void observe(Context context) {
            ContentResolver resolver = context.getContentResolver();
            resolver.registerContentObserver(Settings.Global.getUriFor(
                    Settings.Global.HTTP_PROXY), false, this);
        }
 
    //当修改Settings.Global.HTTP_PROXY时触发
        @Override
        public void onChange(boolean selfChange) {
            mHandler.obtainMessage(mWhat).sendToTarget();
        }
    }
     
    //内部handler的部分代码
    case EVENT_APPLY_GLOBAL_HTTP_PROXY: {
                    handleDeprecatedGlobalHttpProxy();
                    break;
                }
     
    //取出设置的http代理,封装
    private void handleDeprecatedGlobalHttpProxy() {
        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];
            int proxyPort = 8080;
            if (data.length > 1) {
                try {
                    proxyPort = Integer.parseInt(data[1]);
                } catch (NumberFormatException e) {
                    return;
                }
            }
            ProxyProperties p = new ProxyProperties(data[0], proxyPort, "");
            setGlobalProxy(p);
        }
    }
     
     
    //解析出host和port,用Settings.Global.GLOBAL_HTTP_PROXY_HOST存储
    public void setGlobalProxy(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 = "";
            int port = 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 = new ProxyProperties(proxyProperties);
                host = mGlobalProxy.getHost();
                port = mGlobalProxy.getPort();
                exclList = mGlobalProxy.getExclusionList();
                if (proxyProperties.getPacFileUrl() != null) {
                    pacFileUrl = proxyProperties.getPacFileUrl();
                }
            } else {
                mGlobalProxy = null;
            }
            ContentResolver res = mContext.getContentResolver();
            final long token = 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.restoreCallingIdentity(token);
            }
        }
 
        if (mGlobalProxy == null) {
            proxyProperties = mDefaultProxy;
        }
        //发送通知
        sendProxyBroadcast(proxyProperties);
    }
     
  
     //发送广播Proxy.PROXY_CHANGE_ACTION
    private void sendProxyBroadcast(ProxyProperties proxy) {
        if (proxy == null) proxy = new ProxyProperties("", 0, "");
        if (mPacManager.setCurrentProxyScriptUrl(proxy)) return;
        if (DBG) log("sending Proxy Broadcast for " + proxy);
        Intent intent = new Intent(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);
        final long ident = Binder.clearCallingIdentity();
        try {
            mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
    }


调用
frameworks/base/core/java/android/app/ContextImpl.java

@Override
    public void sendStickyBroadcastAsUser(Intent intent, UserHandle user) {
        String resolvedType = 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

public final int broadcastIntent(IApplicationThread caller,
            Intent intent, String resolvedType, IIntentReceiver resultTo,
            int resultCode, String resultData, Bundle map,
            String requiredPermission, int appOp, boolean serialized, boolean sticky, int userId) 
{
        enforceNotIsolatedCaller("broadcastIntent");
        synchronized(this) {
            intent = verifyBroadcastLocked(intent);
 
            final ProcessRecord callerApp = getRecordForAppLocked(caller);
            final int callingPid = Binder.getCallingPid();
            final int callingUid = Binder.getCallingUid();
            final long origId = Binder.clearCallingIdentity();
            int res = broadcastIntentLocked(callerApp,
                    callerApp != null ? callerApp.info.packageName : null,
                    intent, resolvedType, resultTo,
                    resultCode, resultData, map, requiredPermission, appOp, serialized, sticky,
                    callingPid, callingUid, userId);
            Binder.restoreCallingIdentity(origId);
            return res;
        }
    }
     
 
//接收处理Proxy.PROXY_CHANGE_ACTION
private final int broadcastIntentLocked(ProcessRecord callerApp,
            String callerPackage, Intent intent, String resolvedType,
            IIntentReceiver resultTo, int resultCode, String resultData,
            Bundle map, String requiredPermission, int appOp,
            boolean ordered, boolean sticky, int callingPid, int callingUid,
            int userId) 
{
            ...
        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
 
            case UPDATE_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 (int i = 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的内部类

public void setHttpProxy(String host, String port, String exclList, String pacFileUrl) {
            Proxy.setHttpProxySystemProperty(host, port, exclList, pacFileUrl);
        }


frameworks/base/core/java/android/net/Proxy.java,设置java环境变量,存储HTTP、HTTPS的代理host、port。

public static final void setHttpProxySystemProperty(String host, String port, String exclList,
            String pacFileUrl) {
        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(new PacProxySelector());
        } 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 system 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。

public RouteSelector(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 = new LinkedList<Route>();
     
    //读取是否设置全局代理
    resetNextProxy(uri, address.getProxy());
  }
   
  //proxy为null,使用全局或者wifi代理,非应用内代理
  /** Resets {@link #nextProxy} to the first option. */
  private void resetNextProxy(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 static ProxySelector defaultSelector = new ProxySelectorImpl();
 
 
    //入口
    @Override public List<Proxy> select(URI uri) {
        return Collections.singletonList(selectOneProxy(uri));
    }
 
    //解析url的请求类型,查找设置的java环境变量中的代理host和port
    private Proxy selectOneProxy(URI uri) {
        if (uri == null) {
            throw new IllegalArgumentException("uri == null");
        }
        String scheme = uri.getScheme();
        if (scheme == null) {
            throw new IllegalArgumentException("scheme == null");
        }
 
        int port = -1;
        Proxy proxy = null;
        String nonProxyHostsKey = null;
        boolean httpProxyOkay = true;
        if ("http".equalsIgnoreCase(scheme)) {
            port = 80;
            nonProxyHostsKey = "http.nonProxyHosts";
            proxy = lookupProxy("http.proxyHost", "http.proxyPort", Proxy.Type.HTTP, port);
        } else if ("https".equalsIgnoreCase(scheme)) {
            port = 443;
            nonProxyHostsKey = "https.nonProxyHosts"; // RI doesn't support this
            proxy = lookupProxy("https.proxyHost", "https.proxyPort", Proxy.Type.HTTP, port);
        } else if ("ftp".equalsIgnoreCase(scheme)) {
            port = 80; // not 21 as you might guess
            nonProxyHostsKey = "ftp.nonProxyHosts";
            proxy = lookupProxy("ftp.proxyHost", "ftp.proxyPort", Proxy.Type.HTTP, port);
        } else if ("socket".equalsIgnoreCase(scheme)) {
            httpProxyOkay = false;
        } else {
            return Proxy.NO_PROXY;
        }
 
        if (nonProxyHostsKey != null
                && isNonProxyHost(uri.getHost(), System.getProperty(nonProxyHostsKey))) {
            return Proxy.NO_PROXY;
        }
 
        if (proxy != null) {
            return proxy;
        }
 
        if (httpProxyOkay) {
            proxy = lookupProxy("proxyHost", "proxyPort", Proxy.Type.HTTP, port);
            if (proxy != null) {
                return proxy;
            }
        }
 
        proxy = lookupProxy("socksProxyHost", "socksProxyPort", Proxy.Type.SOCKS, 1080);
        if (proxy != null) {
            return proxy;
        }
 
        return Proxy.NO_PROXY;
    }
 
    private Proxy lookupProxy(String hostKey, String portKey, Proxy.Type type, int defaultPort) {
        String host = System.getProperty(hostKey);
        if (host == null || host.isEmpty()) {
            return null;
        }
 
        int port = getSystemPropertyInt(portKey, defaultPort);
        return new Proxy(type, InetSocketAddress.createUnresolved(host, port));
    }


如此我们设置的全局/WiFi代理被封装成Proxy,之后结合上一篇文章:

//proxySelectorProxies不为null了
  private Proxy nextProxy() {
    // If the user specifies a proxy, try that and only that.
    if (userSpecifiedProxy != null) {
      hasNextProxy = false;
      return userSpecifiedProxy;
    }
 
    // 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()) {
        Proxy candidate = proxySelectorProxies.next();
        if (candidate.type() != Proxy.Type.DIRECT) {
          return candidate;
        }
      }
    }
 
    // Finally try a direct connection.
    hasNextProxy = false;
    return Proxy.NO_PROXY;
  }
   
  //上篇文章dns入口,proxy不为空,类型为http
  private void resetNextInetSocketAddress(Proxy proxy) throws UnknownHostException {
    socketAddresses = null; // Clear the addresses. Necessary if getAllByName() below throws!
 
    String socketHost;
    if (proxy.type() == Proxy.Type.DIRECT) {
      socketHost = uri.getHost();
      socketPort = getEffectivePort(uri);
    } else {
      SocketAddress proxyAddress = proxy.address();
      if (!(proxyAddress instanceof InetSocketAddress)) {
        throw new IllegalArgumentException(
            "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)可以实现该功能,而且还可以少构造一个参数。更新之后,怎么使其生效呢?还记得两个广播吗?我们想办法触发其中一个广播即可。

所以先断开连接再连接,触发广播,使代理立即生效。

代码如下:

private WifiConfiguration getCurrentWifiConfiguration(WifiManager manager) {
        if (manager == null || !manager.isWifiEnabled())
            return null;
 
        List<WifiConfiguration> configurationList = 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;
        }
 
        return configuration;
    }
 
    //打印下设置过的代理
    private boolean getWiFiProxySettings() {
        // 获得 WifiConfiguration
        WifiManager manager = (WifiManager) this.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
        WifiConfiguration config = getCurrentWifiConfiguration(manager);
        if (null == config)
            return false;
 
        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
            Object linkProperties = getField(config, "linkProperties");
            if (null == linkProperties)
                return false;
 
            // 获得 getHttpProxy 方法
            Class<?> proxyPropertiesClass = Class.forName("android.net.ProxyProperties");
 
            Class<?> lpClass = Class.forName("android.net.LinkProperties");
            Method getHttpProxy = lpClass.getDeclaredMethod("getHttpProxy");
            getHttpProxy.setAccessible(true);
 
            Object proxyProperties = getHttpProxy.invoke(linkProperties);
 
            Log.e("zhuo", "proxyProperties="+proxyProperties);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
     
    //传入代理host和port
    private boolean setWiFiProxySettings(String ip, int port) {
        // 获得 WifiConfiguration
        WifiManager manager = (WifiManager) this.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
        WifiConfiguration config = getCurrentWifiConfiguration(manager);
        if (null == config)
            return false;
 
        try {
             
            // 从 WifiConfiguration 中获得 linkProperties
            Object linkProperties = getField(config, "linkProperties");
            if (null == linkProperties)
                return false;
 
            // 获得 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 的对象
            Object proxySettings = 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设置成功");
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
     
 
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />


添加权限。

通过以上代码可以设置WiFi代理,可以把其他应用的HTTP(S)代理到设置的代理服务器。

检测代理和绕过代理以及反检测和反绕过待续。

补充


当设置的代理无法连通时,会跳过代理。

HttpURLConnectionImpl.java:

private boolean execute(boolean readResponse) throws IOException {
    try {
      httpEngine.sendRequest();
      if (readResponse) {
        httpEngine.readResponse();
      }
 
      return true;
    } catch (IOException e) {
        //捕获异常
      if (handleFailure(e)) {
        return false;
      } else {
        throw e;
      }
    }
  }
   
  //routeSelector.connectFailed,把异常传递给RouteSelector
    private boolean handleFailure(IOException e) throws IOException {
    RouteSelector routeSelector = httpEngine.routeSelector;
    if (routeSelector != null && httpEngine.connection != null) {
      routeSelector.connectFailed(httpEngine.connection, e);
    }
 
    OutputStream requestBody = httpEngine.getRequestBody();
    boolean canRetryRequestBody = requestBody == null
        || requestBody instanceof RetryableOutputStream;
    if (routeSelector == null && httpEngine.connection == null // No connection.
        || routeSelector != null && !routeSelector.hasNext() // No more routes to attempt.
        || !isRecoverable(e)
        || !canRetryRequestBody) {
      httpEngineFailure = e;
      return false;
    }
 
    httpEngine.release(true);
    RetryableOutputStream retryableOutputStream = (RetryableOutputStream) requestBody;
    httpEngine = newHttpEngine(method, rawRequestHeaders, null, retryableOutputStream);
    httpEngine.routeSelector = routeSelector; // Keep the same routeSelector.
    return true;
  }


RouteSelector.java,把不能连通的IP加入路由表:

public void connectFailed(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,因为不能连通的代理已经在路由表(黑名单)中,所以继续调用自身,跳过了代理
    public Connection 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()) return pooled;
      pooled.close();
    }
 
    // Compute the next route to attempt.
    if (!hasNextTlsMode()) {
      if (!hasNextInetSocketAddress()) {
        if (!hasNextProxy()) {
          if (!hasNextPostponed()) {
            throw new NoSuchElementException();
          }
          return new Connection(nextPostponed());
        }
        lastProxy = nextProxy();
        resetNextInetSocketAddress(lastProxy);
      }
      lastInetSocketAddress = nextInetSocketAddress();
      resetNextTlsMode();
    }
 
    boolean modernTls = nextTlsMode() == TLS_MODE_MODERN;
    Route route = new Route(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.
      return next(method);
    }
 
    return new Connection(route);
  }







- End -






看雪ID:卓桐

https://bbs.pediy.com/user-670707.htm 


*本文由看雪论坛  卓桐  原创,转载请注明来自看雪社区




推荐文章++++

Windows内存堆内容整理总结

格式化字符串漏洞

安卓源码+内核修改编译(修改内核调试标志绕过反调试)

GlobeImposter3.0 勒索分析

恶意样本检测——Mathematics Malware Detected Tools





进阶安全圈,不得不读的一本书






公众号ID:ikanxue
官方微博:看雪安全
商务合作:wsc@kanxue.com




“阅读原文”一起来充电吧!

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存