Android4.4 wifi代理流程

首先簡單說下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);

            // 給構造方法創建參數
            Object[] proxyPropertiesCtorParams = new Object[3];
            proxyPropertiesCtorParams[0] = ip;
            proxyPropertiesCtorParams[1] = port;
            proxyPropertiesCtorParams[2] = null;

            // 創建 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);
  }

 

 

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章