美文网首页
WifiManager的连接和保存源码分析

WifiManager的连接和保存源码分析

作者: 小马要加油 | 来源:发表于2020-03-16 20:57 被阅读0次

    跟踪WifiManager.connect流程

    基于android 9.0 pk3.0

    连接wifi调用sdk接口

    wifiManager.connect

     /**
         * 连接一个network通过network
         *
         * 这个方法被用来代替 enableNetwork() and reconnect()
         *
         * @param networkId the ID of the network as returned by {@link #addNetwork} or {@link
         *        getConfiguredNetworks}.
         * @param 连接回调,可以是null
         * @throws IllegalStateException if the WifiManager instance needs to be
         * initialized again
         * @hide
         */
        public void connect(int networkId, ActionListener listener) {
            if (networkId < 0) throw new IllegalArgumentException("Network id cannot be negative");
            getChannel().sendMessage(CONNECT_NETWORK, networkId, putListener(listener));
        }
    
      /**
         * 连接网络通过configuration. 这个网络也会添加到前台用户配置的的网络列表当中
         *
         * 一个新的网络,这个方法用来代替addNetwork(), enableNetwork(), and reconnect()
         *
         * @param config the set of variables that describe the configuration,
         *            contained in a {@link WifiConfiguration} object.
         * @param listener for callbacks on success or failure. Can be null.
         * @throws IllegalStateException if the WifiManager instance needs to be
         * initialized again
         *
         * @hide
         */
        @SystemApi
        public void connect(WifiConfiguration config, ActionListener listener) {
            if (config == null) throw new IllegalArgumentException("config cannot be null");
            // Use INVALID_NETWORK_ID for arg1 when passing a config object
            // arg1 is used to pass network id when the network already exists
            getChannel().sendMessage(CONNECT_NETWORK, WifiConfiguration.INVALID_NETWORK_ID,
                    putListener(listener), config);
        }
    

    sdk提供的两个方法最后都是CONNECT_NETWORK发消息出去了。

    getChanel是AsyncChannel

    /**
     * Send a message to the destination handler
     *
     * @param what
     * @param arg1
     * @param arg2
     * @param obj
     */
    public void sendMessage(int what, int arg1, int arg2, Object obj) {
        Message msg = Message.obtain();
        msg.what = what;
        msg.arg1 = arg1;
        msg.arg2 = arg2;
        msg.obj = obj;
        sendMessage(msg);
    }
    

    看下sendMessage(msg)

    /**
     * Send a message to the destination handler.
     *
     * @param msg
     */
    public void sendMessage(Message msg) {
        msg.replyTo = mSrcMessenger;
        try {
            mDstMessenger.send(msg);
        } catch (RemoteException e) {
            replyDisconnected(STATUS_SEND_UNSUCCESSFUL);
        }
    }
    

    看下mDstMessenger是什么

    /**
     * Connect handler to messenger. This method is typically called
     * when a server receives a CMD_CHANNEL_FULL_CONNECTION request
     * and initializes the internal instance variables to allow communication
     * with the dstMessenger.
     *
     * @param srcContext
     * @param srcHandler
     * @param dstMessenger
     */
    public void connected(Context srcContext, Handler srcHandler, Messenger dstMessenger) {
        if (DBG) log("connected srcHandler to the dstMessenger  E");
    
        // Initialize source fields
        mSrcContext = srcContext;
        mSrcHandler = srcHandler;
        mSrcMessenger = new Messenger(mSrcHandler);
    
        // Initialize destination fields
        mDstMessenger = dstMessenger;
        if (DBG) log("connected srcHandler to the dstMessenger X");
    }
    

    看下WifiManager有没有调用传入dstMessenger

    private synchronized AsyncChannel getChannel() {
        if (mAsyncChannel == null) {
            Messenger messenger = getWifiServiceMessenger();
            if (messenger == null) {
                throw new IllegalStateException(
                        "getWifiServiceMessenger() returned null!  This is invalid.");
            }
    
            mAsyncChannel = new AsyncChannel();
            mConnected = new CountDownLatch(1);
    
            Handler handler = new ServiceHandler(mLooper);
            mAsyncChannel.connect(mContext, handler, messenger);
            try {
                mConnected.await();
            } catch (InterruptedException e) {
                Log.e(TAG, "interrupted wait at init");
            }
        }
        return mAsyncChannel;
    }
    

    getWifiServiceMessenger这个方法就是mstMessage

    /**
     * Get a reference to WifiService handler. This is used by a client to establish
     * an AsyncChannel communication with WifiService
     *
     * @return Messenger pointing to the WifiService handler
     * @hide
     */
    public Messenger getWifiServiceMessenger() {
        try {
            return mService.getWifiServiceMessenger(mContext.getOpPackageName());
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }
    

    然后我们转由去实现WifiServiceImpl.Java,为什么找到这个类,不知道有没有人有比较巧的方法去找aidl的实现,我是通过全局搜索继承了extends IWifiManager.Stub搜到的。感觉这个方法比较蠢,不知道有没有比较高效的方法。

    /**
     * Get a reference to handler. This is used by a client to establish
     * an AsyncChannel communication with WifiService
     */
    @Override
    public Messenger getWifiServiceMessenger(String packageName) throws RemoteException {
        enforceAccessPermission();
        if (enforceChangePermission(packageName) != MODE_ALLOWED) {
            // We don't have a good way of creating a fake Messenger, and returning null would
            // immediately break callers.
            throw new SecurityException("Could not create wifi service messenger");
        }
        mLog.info("getWifiServiceMessenger uid=%").c(Binder.getCallingUid()).flush();
        return new Messenger(mClientHandler);
    }
    

    看下mClientHandler是什么

    case WifiManager.CONNECT_NETWORK: {
        if (checkChangePermissionAndReplyIfNotAuthorized(
                msg, WifiManager.CONNECT_NETWORK_FAILED)) {
            WifiConfiguration config = (WifiConfiguration) msg.obj;
            int networkId = msg.arg1;
            Slog.d(TAG, "CONNECT "
                    + " nid=" + Integer.toString(networkId)
                    + " config=" + config
                    + " uid=" + msg.sendingUid
                    + " name="
                    + mContext.getPackageManager().getNameForUid(msg.sendingUid));
            if (config != null) {
                /* Command is forwarded to state machine */
                mWifiStateMachine.sendMessage(Message.obtain(msg));
            } else if (config == null
                    && networkId != WifiConfiguration.INVALID_NETWORK_ID) {
                mWifiStateMachine.sendMessage(Message.obtain(msg));
            } else {
                Slog.e(TAG, "ClientHandler.handleMessage ignoring invalid msg=" + msg);
                replyFailed(msg, WifiManager.CONNECT_NETWORK_FAILED,
                        WifiManager.INVALID_ARGS);
            }
        }
        break;
    }
    

    终于到这边了,监听到CONNECT_NETWORK请求,要去搞事情了。

    这里鉴权,看有没有权限开启wifi,然后打印一下log,就交由wifiStateMachine处理了。

    接下来看下状态机又要做什么

    wifiStateMachine没有sendMessage方法,去父类StateMachine看看

    /**
     * Enqueue a message to this state machine.
     *
     * Message is ignored if state machine has quit.
     */
    public void sendMessage(Message msg) {
        // mSmHandler can be null if the state machine has quit.
        SmHandler smh = mSmHandler;
        if (smh == null) return;
    
        smh.sendMessage(msg);
    }
    

    又来了,mSmHandler是什么,哪里传的

    /**
     * Initialize.
     *
     * @param looper for this state machine
     * @param name of the state machine
     */
    private void initStateMachine(String name, Looper looper) {
        mName = name;
        mSmHandler = new SmHandler(looper, this);
    }
    

    这个是私有方法,看下SmHandler

    private static class SmHandler extends Handler {}

    SmHandler就是个普通的handler,那前面去sendMessage,我们直接看handleMessage

    /**
     * 将消息发给状态机processMessage,
     * 同时也处理进入和退出切换状态机状态。
     */
    @Override
    public final void handleMessage(Message msg) {
        if (!mHasQuit) {
            if (mSm != null && msg.what != SM_INIT_CMD && msg.what != SM_QUIT_CMD) {
                mSm.onPreHandleMessage(msg);
            }
    
            if (mDbg) mSm.log("handleMessage: E msg.what=" + msg.what);
    
            /** Save the current message */
            mMsg = msg;
    
            /** State that processed the message */
            State msgProcessedState = null;
            if (mIsConstructionCompleted || (mMsg.what == SM_QUIT_CMD)) {
                /** Normal path */
                msgProcessedState = processMsg(msg);
            } else if (!mIsConstructionCompleted && (mMsg.what == SM_INIT_CMD)
                    && (mMsg.obj == mSmHandlerObj)) {
                /** Initial one time path. */
                mIsConstructionCompleted = true;
                invokeEnterMethods(0);
            } else {
                throw new RuntimeException("StateMachine.handleMessage: "
                        + "The start method not called, received msg: " + msg);
            }
            performTransitions(msgProcessedState, msg);
    
            // We need to check if mSm == null here as we could be quitting.
            if (mDbg && mSm != null) mSm.log("handleMessage: X");
    
            if (mSm != null && msg.what != SM_INIT_CMD && msg.what != SM_QUIT_CMD) {
                mSm.onPostHandleMessage(msg);
            }
        }
    }
    

    也就是说这个消息在执行前,现预执行onPreHandleMessage(msg),然后执行processMsg,最后执行mSm.onPostHandleMessage(msg),现看下processMsg做什么

    /**
     * 处理消息,如果当前状态机没有处理消息,通知父类去处理。如果还是没有处理就会调用unhandleMessage方法。
     */
    private final State processMsg(Message msg) {
        StateInfo curStateInfo = mStateStack[mStateStackTopIndex];
        if (mDbg) {
            mSm.log("processMsg: " + curStateInfo.state.getName());
        }
    
        if (isQuit(msg)) {
            transitionTo(mQuittingState);
        } else {
            while (!curStateInfo.state.processMessage(msg)) {
                /**
                 * Not processed
                 */
                curStateInfo = curStateInfo.parentStateInfo;
                if (curStateInfo == null) {
                    /**
                     * No parents left so it's not handled
                     */
                    mSm.unhandledMessage(msg);
                    break;
                }
                if (mDbg) {
                    mSm.log("processMsg: " + curStateInfo.state.getName());
                }
            }
        }
        return (curStateInfo != null) ? curStateInfo.state : null;
    }
    

    好了,这个类终于跟完了。现在回到状态机,是不是已经忘记要回到哪个类了呃~,那现在我们回到WifiMachineState去看onPreHandleMessage,processMessage,onPostHandleMessage。

    很好,没有重写这两个onPreHandleMessage,onPostHandleMessage方法,父类也是空的。

    那我们直接看processMessage,因为我们是连接,所以直接看ConnectModeState,就不跟这个状态机了,改天心情好再来一篇wifi状态机切换跟踪吧。

    /* Connecting to an access point */
    private State mConnectModeState = new ConnectModeState();
    

    直接到ConnectModeState.processMessage;

    [图片上传失败...(image-ac5695-1584363470016)]

    case WifiManager.CONNECT_NETWORK:
        /**
         * 连接信息包括arg1的networkId或者是obj的config
         * 一个新的network,要求config来连接
         * 一个已经保存的network,只需要network
         */
        netId = message.arg1;
        config = (WifiConfiguration) message.obj;
        boolean hasCredentialChanged = false;
        // New network addition.
        if (config != null) {
            result = mWifiConfigManager.addOrUpdateNetwork(config, message.sendingUid);
            if (!result.isSuccess()) {
                loge("CONNECT_NETWORK adding/updating config=" + config + " failed");
                messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
                replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
                        WifiManager.ERROR);
                break;
            }
            netId = result.getNetworkId();
            hasCredentialChanged = result.hasCredentialChanged();
        }
        if (!connectToUserSelectNetwork(
                netId, message.sendingUid, hasCredentialChanged)) {
            messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
            replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
                    WifiManager.NOT_AUTHORIZED);
            break;
        }
        mWifiMetrics.logStaEvent(StaEvent.TYPE_CONNECT_NETWORK, config);
        broadcastWifiCredentialChanged(WifiManager.WIFI_CREDENTIAL_SAVED, config);
        replyToMessage(message, WifiManager.CONNECT_NETWORK_SUCCEEDED);
        break;
    

    按照代码顺序,先看传入config的吧

    WifiConfigManager.addOrUpdateNetwork

    /**
     * 添加/更新一个network配置到数据库A
     * 如果networkId是-1,表示没有保存过,我们就创建一条新的configuration,
     * 不然就更新一个已存在configuration
     * @param config provided WifiConfiguration object.
     * @param uid    UID of the app requesting the network addition/modification.
     * @return NetworkUpdateResult object representing status of the update.
     */
    public NetworkUpdateResult addOrUpdateNetwork(WifiConfiguration config, int uid) {
        if (!doesUidBelongToCurrentUser(uid)) {
            Log.e(TAG, "UID " + uid + " not visible to the current user");
            return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID);
        }
        if (config == null) {
            Log.e(TAG, "Cannot add/update network with null config");
            return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID);
        }
        if (mPendingStoreRead) {
            Log.e(TAG, "Cannot add/update network before store is read!");
            return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID);
        }
        NetworkUpdateResult result = addOrUpdateNetworkInternal(config, uid);
        if (!result.isSuccess()) {
            Log.e(TAG, "Failed to add/update network " + config.getPrintableSsid());
            return result;
        }
        WifiConfiguration newConfig = getInternalConfiguredNetwork(result.getNetworkId());
        sendConfiguredNetworkChangedBroadcast(
                newConfig,
                result.isNewNetwork()
                        ? WifiManager.CHANGE_REASON_ADDED
                        : WifiManager.CHANGE_REASON_CONFIG_CHANGE);
        // Unless the added network is ephemeral or Passpoint, persist the network update/addition.
        if (!config.ephemeral && !config.isPasspoint()) {
            saveToStore(true);
            if (mListener != null) {
                if (result.isNewNetwork()) {
                    mListener.onSavedNetworkAdded(newConfig.networkId);
                } else {
                    mListener.onSavedNetworkUpdated(newConfig.networkId);
                }
            }
        }
        return result;
    }
    

    如果传参正确的话就会调用到 addOrUpdateNetworkInternal(config, uid);

    /**
     * 添加/更新一个network配置到数据库A
     * 如果networkId是-1,表示没有保存过,我们就创建一条新的configuration,
     * 不然就更新一个已存在configuration
     *
     * @param config provided WifiConfiguration object.
     * @param uid    UID of the app requesting the network addition/deletion.
     * @return NetworkUpdateResult object representing status of the update.
     */
    private NetworkUpdateResult addOrUpdateNetworkInternal(WifiConfiguration config, int uid) {
        if (mVerboseLoggingEnabled) {
            Log.v(TAG, "Adding/Updating network " + config.getPrintableSsid());
        }
        WifiConfiguration newInternalConfig = null;
    
        // First check if we already have a network with the provided network id or configKey.
        //首先先检查是否已经存在networkId了。
        WifiConfiguration existingInternalConfig = getInternalConfiguredNetwork(config);
        // 没有找到配置,说明要去添加一个网络
        if (existingInternalConfig == null) {
            if (!WifiConfigurationUtil.validate(config, WifiConfigurationUtil.VALIDATE_FOR_ADD)) {
                Log.e(TAG, "Cannot add network with invalid config");
                return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID);
            }
            newInternalConfig = createNewInternalWifiConfigurationFromExternal(config, uid);
            // 因为提供的初始配置可能为空
            // 再次检查是否已经存在同样的configkey
            existingInternalConfig = getInternalConfiguredNetwork(newInternalConfig.configKey());
        }
        // 已经存在configuration,去更新一个网络
        if (existingInternalConfig != null) {
            if (!WifiConfigurationUtil.validate(
                    config, WifiConfigurationUtil.VALIDATE_FOR_UPDATE)) {
                Log.e(TAG, "Cannot update network with invalid config");
                return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID);
            }
            // 鉴权
            if (!canModifyNetwork(existingInternalConfig, uid)) {
                Log.e(TAG, "UID " + uid + " does not have permission to update configuration "
                        + config.configKey());
                return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID);
            }
            newInternalConfig =
                    updateExistingInternalWifiConfigurationFromExternal(
                            existingInternalConfig, config, uid);
        }
    
        // 鉴权
        if (WifiConfigurationUtil.hasProxyChanged(existingInternalConfig, newInternalConfig)
                && !canModifyProxySettings(uid)) {
            Log.e(TAG, "UID " + uid + " does not have permission to modify proxy Settings "
                    + config.configKey() + ". Must have NETWORK_SETTINGS,"
                    + " or be device or profile owner.");
            return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID);
        }
    
        // Update the keys for non-Passpoint enterprise networks.  For Passpoint, the certificates
        // and keys are installed at the time the provider is installed.
        if (config.enterpriseConfig != null
                && config.enterpriseConfig.getEapMethod() != WifiEnterpriseConfig.Eap.NONE
                && !config.isPasspoint()) {
            if (!(mWifiKeyStore.updateNetworkKeys(newInternalConfig, existingInternalConfig))) {
                return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID);
            }
        }
    
        boolean newNetwork = (existingInternalConfig == null);
        // This is needed to inform IpClient about any IP configuration changes.
        boolean hasIpChanged =
                newNetwork || WifiConfigurationUtil.hasIpChanged(
                        existingInternalConfig, newInternalConfig);
        boolean hasProxyChanged =
                newNetwork || WifiConfigurationUtil.hasProxyChanged(
                        existingInternalConfig, newInternalConfig);
        // Reset the |hasEverConnected| flag if the credential parameters changed in this update.
        boolean hasCredentialChanged =
                newNetwork || WifiConfigurationUtil.hasCredentialChanged(
                        existingInternalConfig, newInternalConfig);
        if (hasCredentialChanged) {
            newInternalConfig.getNetworkSelectionStatus().setHasEverConnected(false);
        }
    
        // Add it to our internal map. This will replace any existing network configuration for
        // updates.
        try {
            mConfiguredNetworks.put(newInternalConfig);
        } catch (IllegalArgumentException e) {
            Log.e(TAG, "Failed to add network to config map", e);
            return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID);
        }
    
        if (mDeletedEphemeralSSIDs.remove(config.SSID)) {
            if (mVerboseLoggingEnabled) {
                Log.v(TAG, "Removed from ephemeral blacklist: " + config.SSID);
            }
        }
    
        // Stage the backup of the SettingsProvider package which backs this up.
        mBackupManagerProxy.notifyDataChanged();
    
        NetworkUpdateResult result =
                new NetworkUpdateResult(hasIpChanged, hasProxyChanged, hasCredentialChanged);
        result.setIsNewNetwork(newNetwork);
        result.setNetworkId(newInternalConfig.networkId);
    
        localLog("addOrUpdateNetworkInternal: added/updated config."
                + " netId=" + newInternalConfig.networkId
                + " configKey=" + newInternalConfig.configKey()
                + " uid=" + Integer.toString(newInternalConfig.creatorUid)
                + " name=" + newInternalConfig.creatorName);
        return result;
    }
    

    如果一切顺利的话最后会走到new NetworkUpdateResult(hasIpChanged, hasProxyChanged, hasCredentialChanged);返回NetworkUpdateResult回去

    回到状态机case WifiManager.CONNECT_NETWORK

    走到connectToUserSelectNetwork( netId, message.sendingUid, hasCredentialChanged)

    /**
     * 初始化网络连接,需要检查NETWORK_SETTINGS permission.
     */
    private boolean connectToUserSelectNetwork(int netId, int uid, boolean forceReconnect) {
        logd("connectToUserSelectNetwork netId " + netId + ", uid " + uid
                + ", forceReconnect = " + forceReconnect);
        if (mWifiConfigManager.getConfiguredNetwork(netId) == null) {
            loge("connectToUserSelectNetwork Invalid network Id=" + netId);
            return false;
        }
        if (!mWifiConfigManager.enableNetwork(netId, true, uid)
                || !mWifiConfigManager.updateLastConnectUid(netId, uid)) {
            logi("connectToUserSelectNetwork Allowing uid " + uid
                    + " with insufficient permissions to connect=" + netId);
        } else if (mWifiPermissionsUtil.checkNetworkSettingsPermission(uid)) {
            // Note user connect choice here, so that it will be considered in the next network
            // selection.
            mWifiConnectivityManager.setUserConnectChoice(netId);
        }
        if (!forceReconnect && mWifiInfo.getNetworkId() == netId) {
            // We're already connected to the user specified network, don't trigger a
            // reconnection unless it was forced.
            logi("connectToUserSelectNetwork already connecting/connected=" + netId);
        } else {
            mWifiConnectivityManager.prepareForForcedConnection(netId);
            startConnectToNetwork(netId, uid, SUPPLICANT_BSSID_ANY);
        }
        return true;
    }
    

    看下这个方法mWifiConnectivityManager.prepareForForcedConnection(netId);

    /**
     * Handler to prepare for connection to a user or app specified network
     */
    public void prepareForForcedConnection(int netId) {
        localLog("prepareForForcedConnection: netId=" + netId);
    
        clearConnectionAttemptTimeStamps();
        clearBssidBlacklist();
    }
    

    好像没什么关键的,下一个startConnectToNetwork(netId, uid, SUPPLICANT_BSSID_ANY);

    /**
     * 连接指定的wifi
     *
     * @param networkId ID of the network to connect to
     * @param uid UID of the app triggering the connection.
     * @param bssid BSSID of the network
     */
    public void startConnectToNetwork(int networkId, int uid, String bssid) {
        sendMessage(CMD_START_CONNECT, networkId, uid, bssid);
    }
    

    又来了,现在看下CMD_START_CONNECT

    case CMD_START_CONNECT:
        /* connect command coming from auto-join */
        netId = message.arg1;
        int uid = message.arg2;
        bssid = (String) message.obj;
        
        synchronized (mWifiReqCountLock) {
            if (!hasConnectionRequests()) {
                if (mNetworkAgent == null) {
                    loge("CMD_START_CONNECT but no requests and not connected,"
                            + " bailing");
                    break;
                } else if (!mWifiPermissionsUtil.checkNetworkSettingsPermission(uid)) {
                    loge("CMD_START_CONNECT but no requests and connected, but app "
                            + "does not have sufficient permissions, bailing");
                    break;
                }
            }
        }
    
        config = mWifiConfigManager.getConfiguredNetworkWithoutMasking(netId);
        logd("CMD_START_CONNECT sup state "
                + mSupplicantStateTracker.getSupplicantStateName()
                + " my state " + getCurrentState().getName()
                + " nid=" + Integer.toString(netId)
                + " roam=" + Boolean.toString(mIsAutoRoaming));
        if (config == null) {
            loge("CMD_START_CONNECT and no config, bail out...");
            break;
        }
        mTargetNetworkId = netId;
        setTargetBssid(config, bssid);
    
        if (mEnableConnectedMacRandomization.get()) {
            configureRandomizedMacAddress(config);
        }
    
        String currentMacAddress = mWifiNative.getMacAddress(mInterfaceName);
        mWifiInfo.setMacAddress(currentMacAddress);
        Log.i(TAG, "Connecting with " + currentMacAddress + " as the mac address");
    
        reportConnectionAttemptStart(config, mTargetRoamBSSID,
                WifiMetricsProto.ConnectionEvent.ROAM_UNRELATED);
        if (mWifiNative.connectToNetwork(mInterfaceName, config)) {
            mWifiMetrics.logStaEvent(StaEvent.TYPE_CMD_START_CONNECT, config);
            lastConnectAttemptTimestamp = mClock.getWallClockMillis();
            targetWificonfiguration = config;
            mIsAutoRoaming = false;
            if (getCurrentState() != mDisconnectedState) {
                transitionTo(mDisconnectingState);
            }
        } else {
            loge("CMD_START_CONNECT Failed to start connection to network " + config);
            reportConnectionAttemptEnd(
                    WifiMetrics.ConnectionEvent.FAILURE_CONNECT_NETWORK_FAILED,
                    WifiMetricsProto.ConnectionEvent.HLF_NONE);
            replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
                    WifiManager.ERROR);
            break;
        }
        break;
    

    这边可以看到mWifiNative.connectToNetwork(mInterfaceName, config)就去连接网络了。跟到neative就不跟了,反正我也看不懂╮(╯▽╰)╭.

    这样就完成connect config的流程。

    那如果是networkId!=-1的呢?回来CONNECT_NETWORK,其实刚刚已经分析完了,就是顺着走,在没有保存config的时候,保存一下config,这样获取networkId就不会是-1,然后走连接流程。

    image.png

    接下来分析wifiManager.save流程

    WifiManager.save

    /**
     * 保存config ,如果当前配置已经有了,就做更新,
     * 一个新的配置,会默认设置为enabled
     * For an existing network, it accomplishes the task of updateNetwork()
     * 这个api会造成重连如果当前的证书有change
     * @hide
     */
    public void save(WifiConfiguration config, ActionListener listener) {
        if (config == null) throw new IllegalArgumentException("config cannot be null");
        getChannel().sendMessage(SAVE_NETWORK, 0, putListener(listener), config);
    }
    

    因为这边已经分析过了,所以直接到WifiStateMachine吧

    case WifiManager.SAVE_NETWORK:
        result = saveNetworkConfigAndSendReply(message);
        netId = result.getNetworkId();
        if (result.isSuccess() && mWifiInfo.getNetworkId() == netId) {
            if (result.hasCredentialChanged()) {
                config = (WifiConfiguration) message.obj;
                // The network credentials changed and we're connected to this network,
                // start a new connection with the updated credentials.
                logi("SAVE_NETWORK credential changed for config=" + config.configKey()
                        + ", Reconnecting.");
                startConnectToNetwork(netId, message.sendingUid, SUPPLICANT_BSSID_ANY);
            } else {
                if (result.hasProxyChanged()) {
                    log("Reconfiguring proxy on connection");
                    mIpClient.setHttpProxy(
                            getCurrentWifiConfiguration().getHttpProxy());
                }
                if (result.hasIpChanged()) {
                    // The current connection configuration was changed
                    // We switched from DHCP to static or from static to DHCP, or the
                    // static IP address has changed.
                    log("Reconfiguring IP on connection");
                    // TODO(b/36576642): clear addresses and disable IPv6
                    // to simplify obtainingIpState.
                    transitionTo(mObtainingIpState);
                }
            }
        }
        break;
    

    看下这个方法saveNetworkConfigAndSendReply(message)

    /**
     * 私有的方法去调用add & enable network 
     *
     * @return NetworkUpdateResult with networkId of the added/updated configuration. Will return
     * {@link WifiConfiguration#INVALID_NETWORK_ID} in case of error.
     */
    private NetworkUpdateResult saveNetworkConfigAndSendReply(Message message) {
        WifiConfiguration config = (WifiConfiguration) message.obj;
        if (config == null) {
            loge("SAVE_NETWORK with null configuration "
                    + mSupplicantStateTracker.getSupplicantStateName()
                    + " my state " + getCurrentState().getName());
            messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
            replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED, WifiManager.ERROR);
            return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID);
        }
        NetworkUpdateResult result =
                mWifiConfigManager.addOrUpdateNetwork(config, message.sendingUid);
        if (!result.isSuccess()) {
            loge("SAVE_NETWORK adding/updating config=" + config + " failed");
            messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
            replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED, WifiManager.ERROR);
            return result;
        }
        if (!mWifiConfigManager.enableNetwork(
                result.getNetworkId(), false, message.sendingUid)) {
            loge("SAVE_NETWORK enabling config=" + config + " failed");
            messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
            replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED, WifiManager.ERROR);
            return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID);
        }
        broadcastWifiCredentialChanged(WifiManager.WIFI_CREDENTIAL_SAVED, config);
        replyToMessage(message, WifiManager.SAVE_NETWORK_SUCCEEDED);
        return result;
    }
    

    addOrUpdateNetwork这个方法很熟悉,前面已经分析过了,不在赘述

    这边save的逻辑还有一个,如果满足这个条件,就会在保存之后发起连接过程。

    if (result.isSuccess() && mWifiInfo.getNetworkId() == netId) {
        if (result.hasCredentialChanged()) {
            config = (WifiConfiguration) message.obj;
            // The network credentials changed and we're connected to this network,
            // start a new connection with the updated credentials.
            logi("SAVE_NETWORK credential changed for config=" + config.configKey()
                    + ", Reconnecting.");
            startConnectToNetwork(netId, message.sendingUid, SUPPLICANT_BSSID_ANY);
        }
    }
    

    先看下这个是什么:mWifiInfo.getNetworkId()

    去WifiInfo.java,

    /** @hide */
    public void setNetworkId(int id) {
        mNetworkId = id;
    }
    

    就这么个入口,还是回到刚刚的地方

    这边有5处调用setNetworkId的地方,第一二处

     if (SupplicantState.isConnecting(state)) {
                mWifiInfo.setNetworkId(stateChangeResult.networkId);
                mWifiInfo.setBSSID(stateChangeResult.BSSID);
                mWifiInfo.setSSID(stateChangeResult.wifiSsid);
            } else {
                // Reset parameters according to WifiInfo.reset()
                mWifiInfo.setNetworkId(WifiConfiguration.INVALID_NETWORK_ID);
                mWifiInfo.setBSSID(null);
                mWifiInfo.setSSID(null);
            }
    

    处于connecting的状态下,会把连接中的networkId设置进去,后面是设置成-1,与我们的要求不符合

    第三处:ConnectModeState.processMessage

    case WifiMonitor.NETWORK_CONNECTION_EVENT:
        if (mVerboseLoggingEnabled) log("Network connection established");
        mLastNetworkId = message.arg1;
        mWifiConfigManager.clearRecentFailureReason(mLastNetworkId);
        mLastBssid = (String) message.obj;
        reasonCode = message.arg2;
        // TODO: This check should not be needed after WifiStateMachinePrime r
        // Currently, the last connected network configuration is left in
        // wpa_supplicant, this may result in wpa_supplicant initiating connec
        // to it after a config store reload. Hence the old network Id lookups
        // work, so disconnect the network and let network selector reselect a
        // network.
        config = getCurrentWifiConfiguration();
        if (config != null) {
            mWifiInfo.setBSSID(mLastBssid);
            mWifiInfo.setNetworkId(mLastNetworkId);
            mWifiInfo.setMacAddress(mWifiNative.getMacAddress(mInterfaceName))
            ScanDetailCache scanDetailCache =
                    mWifiConfigManager.getScanDetailCacheForNetwork(config.net
            if (scanDetailCache != null && mLastBssid != null) {
                ScanResult scanResult = scanDetailCache.getScanResult(mLastBss
                if (scanResult != null) {
                    mWifiInfo.setFrequency(scanResult.frequency);
                }
            }
            mWifiConnectivityManager.trackBssid(mLastBssid, true, reasonCode);
            // We need to get the updated pseudonym from supplicant for EAP-SI
            if (config.enterpriseConfig != null
                    && TelephonyUtil.isSimEapMethod(
                            config.enterpriseConfig.getEapMethod())) {
                String anonymousIdentity =
                        mWifiNative.getEapAnonymousIdentity(mInterfaceName);
                if (anonymousIdentity != null) {
                    config.enterpriseConfig.setAnonymousIdentity(anonymousIden
                } else {
                    Log.d(TAG, "Failed to get updated anonymous identity"
                            + " from supplicant, reset it in WifiConfiguration
                    config.enterpriseConfig.setAnonymousIdentity(null);
                }
                mWifiConfigManager.addOrUpdateNetwork(config, Process.WIFI_UID
            }
            sendNetworkStateChangeBroadcast(mLastBssid);
            transitionTo(mObtainingIpState);
        } else {
            logw("Connected to unknown networkId " + mLastNetworkId
                    + ", disconnecting...");
            sendMessage(CMD_DISCONNECT);
        }
        break;
    

    看代码的意思是连上wifi了,下一步是获取wifi ip的样子。

    第四处L2ConnectedState.processMessage

    case WifiMonitor.NETWORK_CONNECTION_EVENT:
        mWifiInfo.setBSSID((String) message.obj);
        mLastNetworkId = message.arg1;
        mWifiInfo.setNetworkId(mLastNetworkId);
        mWifiInfo.setMacAddress(mWifiNative.getMacAddress(mInterfaceName));
        if(!mLastBssid.equals(message.obj)) {
            mLastBssid = (String) message.obj;
            sendNetworkStateChangeBroadcast(mLastBssid);
        }
        break;
    

    第五处:RoamingState.processMessage

    case WifiMonitor.NETWORK_CONNECTION_EVENT:
        if (mAssociated) {
            if (mVerboseLoggingEnabled) {
                log("roaming and Network connection established");
            }
            mLastNetworkId = message.arg1;
            mLastBssid = (String) message.obj;
            mWifiInfo.setBSSID(mLastBssid);
            mWifiInfo.setNetworkId(mLastNetworkId);
            int reasonCode = message.arg2;
            mWifiConnectivityManager.trackBssid(mLastBssid, true, reasonCode);
            sendNetworkStateChangeBroadcast(mLastBssid);
    
            // Successful framework roam! (probably)
            reportConnectionAttemptEnd(
                    WifiMetrics.ConnectionEvent.FAILURE_NONE,
                    WifiMetricsProto.ConnectionEvent.HLF_NONE);
    
            // We must clear the config BSSID, as the wifi chipset may decide to roam
            // from this point on and having the BSSID specified by QNS would cause
            // the roam to fail and the device to disconnect.
            // When transition from RoamingState to DisconnectingState or
            // DisconnectedState, the config BSSID is cleared by
            // handleNetworkDisconnect().
            clearTargetBssid("RoamingCompleted");
    
            // We used to transition to ObtainingIpState in an
            // attempt to do DHCPv4 RENEWs on framework roams.
            // DHCP can take too long to time out, and we now rely
            // upon IpClient's use of IpReachabilityMonitor to
            // confirm our current network configuration.
            //
            // mIpClient.confirmConfiguration() is called within
            // the handling of SupplicantState.COMPLETED.
            transitionTo(mConnectedState);
        } else {
            messageHandlingStatus = MESSAGE_HANDLING_STATUS_DISCARD;
        }
        break;
    

    总而言之,这个networkId都是在收到消息NETWORK_CONNECTION_EVENT才赋值,去WifiMonitor看下对消息值的定义

    /* Network connection completed */
     public static final int NETWORK_CONNECTION_EVENT             = BASE + 3;
    

    也就是说,本地wifi要和远程WiFi建立连接之后才能相等。也就是说connect和save都很多相同点

    connect=save+(热点之前已经建立连接的修改网络配置)

    image.png

    相关文章

      网友评论

          本文标题:WifiManager的连接和保存源码分析

          本文链接:https://www.haomeiwen.com/subject/sxwoehtx.html