系列文章目录

【安卓Framework学习】Wifi框架学习之核心类
【安卓Framework学习】Wifi框架学习之wifi状态机
【安卓Framework学习】Wifi框架学习之连接与断开流程
【【安卓Framework学习】Wifi框架学习之扫描模式及扫描流程.
【安卓Framework学习】Wifi框架学习之热点评分机制.
【安卓Framework学习】安卓连接管理(ConnectivityService)之wifi连接及注册.



前言

上一篇记录了安卓wifi框架中的主要的核心类,后续学习内容主要从实际的操作动作或者业务流程出发,分析其在wifi框架中的主要流程。本次学习内容针对常见的开启和关闭wifi来梳理其中的流程。文中出现的代码大部分基于android11源码。


一、wifi开启流程

1、wifi开启源码流程分析

wifi的开启和关闭在应用层部分是一样的,都是通过拿到系统服务在应用层的管理类WifiManager对象,然后通过调用WifiManager中系统服务的Binder对象的相关方法实现功能。应用层较为简单,调用如下方法。

/**
     * Enable or disable Wi-Fi.
     * <p>
     * Applications must have the {@link android.Manifest.permission#CHANGE_WIFI_STATE}
     * permission to toggle wifi.
     *
     * @param enabled {@code true} to enable, {@code false} to disable.
     * @return {@code false} if the request cannot be satisfied; {@code true} indicates that wifi is
     *         either already in the requested state, or in progress toward the requested state.
     * @throws  {@link java.lang.SecurityException} if the caller is missing required permissions.
     *
     * @deprecated Starting with Build.VERSION_CODES#Q, applications are not allowed to
     * enable/disable Wi-Fi.
     * <b>Compatibility Note:</b> For applications targeting
     * {@link android.os.Build.VERSION_CODES#Q} or above, this API will always fail and return
     * {@code false}. If apps are targeting an older SDK ({@link android.os.Build.VERSION_CODES#P}
     * or below), they can continue to use this API.
     * <p>
     * Deprecation Exemptions:
     * <ul>
     * <li>Device Owner (DO), Profile Owner (PO) and system apps.
     * </ul>
     */
    @Deprecated
    public boolean setWifiEnabled(boolean enabled) {
        try {
            return mService.setWifiEnabled(mContext.getOpPackageName(), enabled);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

由注释可以看出,通过传入一个boolean型参数来设置wifi的开关。但是,注释中标明此方法在android11中已经被弃用,从安卓10开始,系统不支持应用开关wifi,安卓9及早一点的系统还能使用此api.

在方法中没有做别的,直接调用了Binder对象对应服务的同名方法。而对应的mService为系统中的服务类WifiServiceImpl继承于BaseWifiService,其实现了IWifiManager接口。再 来看WifiServiceImpl实现类中同名方法的具体实现。

	public synchronized boolean setWifiEnabled(String packageName, boolean enable) {
        /*
         *此处省略开启前的一些校验,具体都是什么校验目前还没完全研究清楚
         */
        try {
            if (!mSettingsStore.handleWifiToggled(enable)) {
                // Nothing to do if wifi cannot be toggled
                return true;
            }
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
        if (mWifiPermissionsUtil.checkNetworkSettingsPermission(Binder.getCallingUid())) {
            mWifiMetrics.logUserActionEvent(enable ? UserActionEvent.EVENT_TOGGLE_WIFI_ON
                    : UserActionEvent.EVENT_TOGGLE_WIFI_OFF);
        }
        mWifiMetrics.incrementNumWifiToggles(isPrivileged, enable);
        mActiveModeWarden.wifiToggled();
        return true;
    }

方法中会先在系统设置数据库中写入wifi需要处于开启还是关闭的状态值,如果写入失败,则直接返回true了。这里为了分析开启流程,姑且认为它能够正常写入数据库中,并且传入的enable形参为true表示开启。随后会调用到mActiveModeWarden.wifiToggled()中。mActiveModeWarden中有一个WifiController状态机,wifiToggled()方法如下,直接给状态机发送了一个CMD_WIFI_TOGGLED消息。

 	/** Wifi has been toggled. */
    public void wifiToggled() {
        mWifiController.sendMessage(WifiController.CMD_WIFI_TOGGLED);
    }

再看WifiController状态机,一开始初始化了三个状态,DefaultStateDisabledStateEnabledState,其中DefaultState为另外两个的父状态,这是在状态机构造函数中确定的。在初始化了状态机后会调用状态机的start()方法,这个方法初始化了状态机初始所处的状态,如下。

	public void start() {
	    /*
	     * 此处省略
		 */
	    if (shouldEnableSta()) {
	        startClientModeManager();
	        setInitialState(mEnabledState);
	    } else {
	        setInitialState(mDisabledState);
	    }
	     /*
	     * 此处省略
		 */
	    super.start();
	}

可见一开始wifi处于关闭状态,那时候还没有写Settings的数据库,shouldEnableSta()会返回false,状态机的初始状态会进入DisabledState,所以在开启wifi时,发送的CMD_WIFI_TOGGLED消息会被DisabledState所处理,再次转到DisabledState状态处理方法中。

@Override
	public boolean processMessageFiltered(Message msg) {
	    switch (msg.what) {
	        case CMD_WIFI_TOGGLED:
	        case CMD_SCAN_ALWAYS_MODE_CHANGED:
	            if (shouldEnableSta()) {
	                startClientModeManager();
	                transitionTo(mEnabledState);
	            }
	            break;
	        /*省略其他分支处理*/
	        default:
	            return NOT_HANDLED;
	    }
	    return HANDLED;
	}

这里再一次调用了shouldEnableSta(),由于刚刚状态机初始化是在应用开启wifi之前,所以这一次在调用WifiServiceImpl.setWifiEnabled()时写了数据库,所以shouldEnableSta()返回true。随后进入处理CMD_WIFI_TOGGLED消息的分支。分支中调用了两个方法transitionTo()会将状态机的状态切换到EnabledState,而startClientModeManager()如下分析。

	private boolean startClientModeManager() {
        Log.d(TAG, "Starting ClientModeManager");
        ClientListener listener = new ClientListener();
        ClientModeManager manager = mWifiInjector.makeClientModeManager(listener);
        listener.setActiveModeManager(manager);
        manager.start();
        if (!switchClientModeManagerRole(manager)) {
            return false;
        }
        mActiveModeManagers.add(manager);
        return true;
    }

方法中通过WifiInjector获得了一个ClientModeManager 对象,并且调用了其start()方法,switchClientModeManagerRole(manager)这个方法大家注意一下,跑完前面的start()会继续进入这个方法。转入到ClientModeManager 中。

    public void start() {
        mTargetRole = ROLE_CLIENT_SCAN_ONLY;
        mStateMachine.sendMessage(ClientModeStateMachine.CMD_START);
    }

可以看到,在ClientModeManager start()方法中,又给一个状态机发送了CMD_START消息,这个状态机是ClientModeManager 内部定义的一个ClientModeStateMachine状态机,其初始状态为IdleState,也可以看到只有IdleState状态对消息CMD_START做了实质性的处理。转入到IdleState状态的消息处理方法中。

@Override
public boolean processMessage(Message message) {
    switch (message.what) {
        case CMD_START:
            // Always start in scan mode first.
            mClientInterfaceName =
                    mWifiNative.setupInterfaceForClientInScanMode(
                    mWifiNativeInterfaceCallback);
            if (TextUtils.isEmpty(mClientInterfaceName)) {
                Log.e(TAG, "Failed to create ClientInterface. Sit in Idle");
                mModeListener.onStartFailure();
                break;
            }
            transitionTo(mScanOnlyModeState);
            break;
        default:
            Log.d(TAG, "received an invalid message: " + message);
            return NOT_HANDLED;
    }
    return HANDLED;
}

调用到了WifiNative中的setupInterfaceForClientInScanMode()方法开启wifi模块分配一个Iface,在setupInterfaceForClientInScanMode()方法中开启了wifi芯片驱动、获得芯片信息、注册回调并开始监听Iface消息等,方法较为复杂这里暂不展开描述。然后判断返回的Iface的名字是否为空,不为空说明开启成功,并且将状态切到ScanOnlyModeState。根据状态机的实现,在处理完当前消息后会去切换到目的状态,这里状态机的内容较多不多赘述。在切换到ScanOnlyModeState后,状态机会根据状态树向上索引,并执行每一个状态的enter()方法,首先进入ScanOnlyModeState.enter().

public void enter() {
    Log.d(TAG, "entering ScanOnlyModeState");
    mClientModeImpl.setOperationalMode(ClientModeImpl.SCAN_ONLY_MODE,
            mClientInterfaceName);
    mRole = ROLE_CLIENT_SCAN_ONLY;
    mModeListener.onStarted();
    // Inform sar manager that scan only is being enabled
    mSarManager.setScanOnlyWifiState(WifiManager.WIFI_STATE_ENABLED);
    mWakeupController.start();
}

上面的setOperationalMode()方法是通知wifi状态机切换状态的,在安卓11里是ClientModeImpl类,在安卓9中是WifiStateMachinemModeListener.onStarted()方法是在实例化ClientModeManager对象时传入进来的监听器的回调方法。在进入到StartedStateenter()方法中(因为StartedStateScanOnlyModeState的父状态)。

public void enter() {
    Log.d(TAG, "entering StartedState");
    mIfaceIsUp = false;
    onUpChanged(mWifiNative.isInterfaceUp(mClientInterfaceName));
}

这里调用了WifiNative.isInterfaceUp()方法,判断当前获得到的Iface接口是否已经起来,由于之前是正常开启,那这里Iface肯定是起来的,所以返回true,进入到onUpChanged()方法中。

private void onUpChanged(boolean isUp) {
    if (isUp == mIfaceIsUp) {
        return;  // no change
    }
    mIfaceIsUp = isUp;
    if (!isUp) {
        // if the interface goes down we should exit and go back to idle state.
        Log.d(TAG, "interface down!");
        mStateMachine.sendMessage(CMD_INTERFACE_DOWN);
    }
}

其实这个回调就是判断所需要开启的Iface接口是否起来,如果未起来就发送接口被关闭的消息然后做后续处理。其实到这里ClientModeManagerCMD_START消息的处理就告一段落了,我们再返回到ActiveModeWarden中去看前面遗留下来的switchClientModeManagerRole()方法。

/**
 * Method to switch a client mode manager mode of operation (from ScanOnly To Connect &
 * vice-versa) based on the toggle state.
 */
private boolean switchClientModeManagerRole(@NonNull ClientModeManager modeManager) {
    if (mSettingsStore.isWifiToggleEnabled()) {
        modeManager.setRole(ActiveModeManager.ROLE_CLIENT_PRIMARY);
    } else if (checkScanOnlyModeAvailable()) {
        modeManager.setRole(ActiveModeManager.ROLE_CLIENT_SCAN_ONLY);
    } else {
        Log.e(TAG, "Something is wrong, no client mode toggles enabled");
        return false;
    }
    return true;
}

看注释可以大致了解到,这个方法是帮助ClientModeManagerScanOnlyModeState状态切换到ConnectModeState状态中的。由于在一开始就写入数据库中开启wifi,所以mSettingsStore.isWifiToggleEnabled()会返回true,那么会继续调用ClientModeManagersetRole()方法,这里注意传入的参数是ActiveModeManager.ROLE_CLIENT_PRIMARY。进入到setRole()方法中。

public void setRole(@Role int role) {
    Preconditions.checkState(CLIENT_ROLES.contains(role));
    if (role == ROLE_CLIENT_SCAN_ONLY) {
        mTargetRole = role;
        // Switch client mode manager to scan only mode.
        mStateMachine.sendMessage(ClientModeStateMachine.CMD_SWITCH_TO_SCAN_ONLY_MODE);
    } else if (CLIENT_CONNECTIVITY_ROLES.contains(role)) {
        mTargetRole = role;
        // Switch client mode manager to connect mode.
        mStateMachine.sendMessage(ClientModeStateMachine.CMD_SWITCH_TO_CONNECT_MODE, role);
    }
}

第一句Preconditions.checkState(CLIENT_ROLES.contains(role))不用理会,就是检测CLIENT_ROLES是否包含此角色,不包含就抛异常。但是后面发现并没有ActiveModeManager.ROLE_CLIENT_PRIMARY条件出现,这里再次进入CLIENT_CONNECTIVITY_ROLES,看看这个List中有什么,其在ActiveModeManager这个接口中,如下。

/** List of Client roles that could initiate a wifi connection */
List<Integer> CLIENT_CONNECTIVITY_ROLES = Arrays.asList(
        ROLE_CLIENT_PRIMARY,
        ROLE_CLIENT_SECONDARY,
        ROLE_CLIENT_LOCAL_ONLY);

可以看到里面包含了ActiveModeManager.ROLE_CLIENT_PRIMARY,所以上面setRole()方法中走入第二个分支,给ClientModeStateMachine状态机发送了一个切换状态的消息ClientModeStateMachine.CMD_SWITCH_TO_CONNECT_MODE,由于之前在开启wifi的时候已经切换到了ScanOnlyModeState状态,所以再次进入到ScanOnlyModeState的处理方法中。

public boolean processMessage(Message message) {
    switch (message.what) {
        case CMD_SWITCH_TO_SCAN_ONLY_MODE:
            // Already in scan only mode, ignore this command.
            break;
        default:
            return NOT_HANDLED;
    }
    return HANDLED;
}

由于其并没有处理此消息,所以会交给父状态处理,进入StartedState的处理方法中。

public boolean processMessage(Message message) {
    switch(message.what) {
        case CMD_START:
            // Already started, ignore this command.
            break;
        case CMD_SWITCH_TO_CONNECT_MODE:
            mRole = message.arg1; // could be any one of possible conne
            updateConnectModeState(WifiManager.WIFI_STATE_ENABLING,
                    WifiManager.WIFI_STATE_DISABLED);
            if (!mWifiNative.switchClientInterfaceToConnectivityMode(
                    mClientInterfaceName)) {
                updateConnectModeState(WifiManager.WIFI_STATE_UNKNOWN,
                        WifiManager.WIFI_STATE_ENABLING);
                updateConnectModeState(WifiManager.WIFI_STATE_DISABLED,
                        WifiManager.WIFI_STATE_UNKNOWN);
                mModeListener.onStartFailure();
                break;
            }
            transitionTo(mConnectModeState);
            break;
        /*此处省略其他分支,详细的可以自行去看源码*/
        default:
            return NOT_HANDLED;
    }
    return HANDLED;
}

可以看到,调用了updateConnectModeState()方法,并且传入了两个wifi的开启状态,进入此方法中。

/**
 * Update Wifi state and send the broadcast.
 * @param newState new Wifi state
 * @param currentState current wifi state
 */
private void updateConnectModeState(int newState, int currentState) {
    if (newState == WifiManager.WIFI_STATE_UNKNOWN) {
        // do not need to broadcast failure to system
        return;
    }
    if (mRole != ROLE_CLIENT_PRIMARY) {
        // do not raise public broadcast unless this is the primary client mode manager
        return;
    }
    mClientModeImpl.setWifiStateForApiCalls(newState);
    final Intent intent = new Intent(WifiManager.WIFI_STATE_CHANGED_ACTION);
    intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
    intent.putExtra(WifiManager.EXTRA_WIFI_STATE, newState);
    intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_STATE, currentState);
    mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
}

这个方法就比较明确了,注释也说的非常清楚,更新wifi的状态,并且非对外发送广播,这不正是发送的wifi开启或关闭状态的广播么WifiManager.WIFI_STATE_CHANGED_ACTION。然后继续看消息处理方法,会调用WifiNative.switchClientInterfaceToConnectivityMode(),这里其实是让下面C层Framework和硬件切换到一个能够连接的状态中,此方法和前面的WifiNative方法一样较为繁琐,这里不赘述。随后将状态切换到ConnectModeState,根据状态机的特点,消息处理完后会进入到新的状态,并且调用enter()方法。进入到ConnectModeState.enter()中。

public void enter() {
    Log.d(TAG, "entering ConnectModeState");
    mClientModeImpl.setOperationalMode(ClientModeImpl.CONNECT_MODE,
            mClientInterfaceName);
    mModeListener.onStarted();
    updateConnectModeState(WifiManager.WIFI_STATE_ENABLED,
            WifiManager.WIFI_STATE_ENABLING);
    // Inform sar manager that wifi is Enabled
    mSarManager.setClientWifiState(WifiManager.WIFI_STATE_ENABLED);
}

这里可以看到,先将ClientModeImpl状态机切换了,然后继续调用了updateConnectModeState()对外发送了一个广播,表示当前的wifi状态处于WifiManager.WIFI_STATE_ENABLED状态,至此ClientModeManagerCMD_SWITCH_TO_CONNECT_MODE消息处理完成了,那么ActiveModeWarden.startClientModeManager()方法也执行完并返回true,再次回到ActiveModeWarden中状态机的DisabledState状态中的消息处理方法processMessageFiltered中(前面有,这里不放代码了),将状态机WifiController的状态切换到了EnabledState,而EnabledStateenter()方法没有再次对外发送消息或其他操作,所以WifiController状态机对消息CMD_WIFI_TOGGLED也处理完毕了。至此WifiServiceImpl.setWifiEnabled()方法执行完毕,wifi成功打开,并对外发送了两次开启状态的广播。

2、wifi开启流程图

在这里插入图片描述


二、wifi关闭流程

1、wifi关闭源码分析

由上一部分开启wifi流程可知,在完成开启wifi后,ActiveModeWarden中的状态机WifiController会切换到EnabledState,与开启wifi重复的部分省略,直接进入到EnabledState状态处理函数中,如下。

public boolean processMessageFiltered(Message msg) {
    switch (msg.what) {
        case CMD_WIFI_TOGGLED:
        case CMD_SCAN_ALWAYS_MODE_CHANGED:
            if (shouldEnableSta()) {
                if (hasAnyClientModeManager()) {
                    switchAllClientModeManagers();
                } else {
                    startClientModeManager();
                }
            } else {
                stopAllClientModeManagers();
            }
            break;
        /*省略其他消息处理分支*/
        default:
            return NOT_HANDLED;
    }
    return HANDLED;
}

这里需要分析一下shouldEnableSta()这个方法了。

private boolean shouldEnableSta() {
    return mSettingsStore.isWifiToggleEnabled() || checkScanOnlyModeAvailable();
}
...
private boolean checkScanOnlyModeAvailable() {
    return mWifiPermissionsUtil.isLocationModeEnabled()
            && mSettingsStore.isScanAlwaysAvailable();
}

首先会去判断数据库中是否写入开启wifi的值,这里我们是关闭,说明mSettingsStore.isWifiToggleEnabled()返回的false,那再来具体看checkScanOnlyModeAvailable()mSettingsStore.isScanAlwaysAvailable()返回的是是否需要一直能够扫描,这个值需要专门调用WifiManager.setScanAlwaysAvailable()将其设置为true,由于一开始一直没有调用设置,所以mSettingsStore.isScanAlwaysAvailable()返回false,因此shouldEnableSta()返回false,消息处理会走入到else中,进入到stopAllClientModeManagers()方法中。

/**
 * Method to stop all client mode mangers.
 */
private void stopAllClientModeManagers() {
    Log.d(TAG, "Shutting down all client mode managers");
    for (ActiveModeManager manager : mActiveModeManagers) {
        if (!(manager instanceof ClientModeManager)) continue;
        ClientModeManager clientModeManager = (ClientModeManager) manager;
        clientModeManager.stop();
    }
}

这里可以看到,关闭了所有之前开启的ClientModeManager,这里为什么会有多个,我猜测是一个应用会有一个ClientModeManager对象。进入到ClientModeManager.stop()中。

/**
 * Disconnect from any currently connected networks and stop client mode.
 */
@Override
public void stop() {
    Log.d(TAG, " currentstate: " + getCurrentStateName());
    mTargetRole = ROLE_UNSPECIFIED;
    if (mIfaceIsUp) {
        updateConnectModeState(WifiManager.WIFI_STATE_DISABLING,
                WifiManager.WIFI_STATE_ENABLED);
    } else {
        updateConnectModeState(WifiManager.WIFI_STATE_DISABLING,
                WifiManager.WIFI_STATE_ENABLING);
    }
    mDeferStopHandler.start(getWifiOffDeferringTimeMs());
}

mTargetRole = ROLE_UNSPECIFIED这一句注意现在记住,后面会用到。由于之前是正常打开没有问题,所以接口是起来的mIfaceIsUptrue,会对外发送一个wifi状态广播,由WIFI_STATE_ENABLED变换为WIFI_STATE_DISABLING。随后进入到DeferStopHandler.start()方法中。

public void start(int delayMs) {
    /*省略其他代码*/
    // Most cases don't need delay, check it first to avoid unnecessary work.
    if (delayMs == 0) {
        continueToStopWifi();
        return;
    }
   /*省略其他代码*/
}

这里传入的是一个延时,表示要延迟多久关闭wifi,这里我们姑且认为不延时delayMs==0,则会继续进入continueToStopWifi()

private void continueToStopWifi() {
    /*省略其他代码*/
    if (mTargetRole == ROLE_UNSPECIFIED) {
        Log.d(TAG, "Continue to stop wifi");
        mStateMachine.quitNow();
        mWifiMetrics.noteWifiOff(mIsDeferring, isTimedOut, deferringDurationMillis);
    } else if (mTargetRole == ROLE_CLIENT_SCAN_ONLY) {
        if (!mWifiNative.switchClientInterfaceToScanMode(mClientInterfaceName)) {
            mModeListener.onStartFailure();
        } else {
            mStateMachine.sendMessage(
                    ClientModeStateMachine.CMD_SWITCH_TO_SCAN_ONLY_MODE_CONTINUE);
            mWifiMetrics.noteWifiOff(mIsDeferring, isTimedOut, deferringDurationMillis);
        }
    } else {
        updateConnectModeState(WifiManager.WIFI_STATE_ENABLED,
                WifiManager.WIFI_STATE_DISABLING);
    }
    /*注销一些回调*/
}

由于前面在stop()方法中mTargetRole = ROLE_UNSPECIFIED,所以会进入第一个分支WifiMetrics这个类主要用于记录wifi框架中的一些性能指标和打印一些日志,所以没有很实际的流程在内,所以只需关注mStateMachine.quitNow(),这个状态机对象就是ClientModeStateMachine了,进入了退出的流程。进入状态机父类StateMachine.quitNow()中。

	/**
     * Quit the state machine immediately all currently queued messages will be discarded.
     */
    StateMachine.quitNow
    public final void quitNow() {
        // mSmHandler can be null if the state machine is already stopped.
        SmHandler smh = mSmHandler;
        if (smh == null) return;

        smh.quitNow();
    }

	 /** @see StateMachine#quitNow() */
	 SmHandler.quitNow
	 private final void quitNow() {
	     if (mDbg) mSm.log("quitNow:");
	     sendMessageAtFrontOfQueue(obtainMessage(SM_QUIT_CMD, mSmHandlerObj));
	 }

可以看到,实际是向状态机内部的SmHandler发送了一个SM_QUIT_CMD消息,转入到SmHandler消息处理函数中。

public final void handleMessage(Message msg) {
    if (!mHasQuit) {
        /** 省略一些不重要代码 */
        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);
        /** 省略一些不重要代码 */
    }
}

那么会继续走入第一个分支中,进入到processMsg().

/**
 * Process the message. If the current state doesn't handle
 * it, call the states parent and so on. If it is never handled then
 * call the state machines unhandledMessage method.
 * @return the state that processed the message
 */
private final State processMsg(Message msg) {
    StateInfo curStateInfo = mStateStack[mStateStackTopIndex];
    /*省略日志打印*/
    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;
}

由于是退出消息,所以isQuit(msg)直接返回的true,因此接下来直接切换到退出的状态了QuittingState,再看performTransitions()方法。

private void performTransitions(State msgProcessedState, Message msg) {
    /*省略一些代码*/
    State destState = mDestState;
	if (destState != null) {
	    /**
	     * Process the transitions including transitions in the enter/exit methods
	     */
	    while (true) {
	        if (mDbg) mSm.log("handleMessage: new destination call exit/enter");
	        /**
	         * Determine the states to exit and enter and return the
	         * common ancestor state of the enter/exit states. Then
	         * invoke the exit methods then the enter methods.
	         */
	        StateInfo commonStateInfo = setupTempStateStackWithStatesToEnter(destState);
	        // flag is cleared in invokeEnterMethods before entering the target state
	        mTransitionInProgress = true;
	        invokeExitMethods(commonStateInfo);
	        int stateStackEnteringIndex = moveTempStateStackToStateStack();
	        invokeEnterMethods(stateStackEnteringIndex);
	        /**
	         * Since we have transitioned to a new state we need to have
	         * any deferred messages moved to the front of the message queue
	         * so they will be processed before any other messages in the
	         * message queue.
	         */
	        moveDeferredMessageAtFrontOfQueue();
	        if (destState != mDestState) {
	            // A new mDestState so continue looping
	            destState = mDestState;
	        } else {
	            // No change in mDestState so we're done
	            break;
	        }
	    }
	    mDestState = null;
	}
    /**
     * After processing all transitions check and
     * see if the last transition was to quit or halt.
     */
    if (destState != null) {
        if (destState == mQuittingState) {
            /**
             * Call onQuitting to let subclasses cleanup.
             */
            mSm.onQuitting();
            cleanupAfterQuitting();
        } else if (destState == mHaltingState) {
            /**
             * Call onHalting() if we've transitioned to the halting
             * state. All subsequent messages will be processed in
             * in the halting state which invokes haltedProcessMessage(msg);
             */
            mSm.onHalting();
        }
    }
}

invokeExitMethods()方法会将切换状态前的所有状态都退出即都会执行exit()方法,之前是处于ConnectModeState状态,所以ConnectModeStateStartedStateexit()方法都会被执行。

ConnectModeState
public void exit() {
    updateConnectModeState(WifiManager.WIFI_STATE_DISABLED,
            WifiManager.WIFI_STATE_DISABLING);
    // Inform sar manager that wifi is being disabled
    mSarManager.setClientWifiState(WifiManager.WIFI_STATE_DISABLED);
}

StartedState
public void exit() {
    mClientModeImpl.setOperationalMode(ClientModeImpl.DISABLED_MODE, null);
    if (mClientInterfaceName != null) {
        mWifiNative.teardownInterface(mClientInterfaceName);
        mClientInterfaceName = null;
        mIfaceIsUp = false;
    }
    // once we leave started, nothing else to do...  stop the state machine
    mRole = ROLE_UNSPECIFIED;
    mStateMachine.quitNow();
    mModeListener.onStopped();
}

可以看出ConnectModeState的退出方法中对外发送了广播wifi状态由WIFI_STATE_DISABLING变化到WIFI_STATE_DISABLED,然后继续进入StartedState的退出方法,首先是将wifi状态机切到DISABLED_MODE,然后因为mClientInterfaceName为之前打开的wifi的Iface,所以暂时还不为空,进入到WifiNative.teardownInterface()中将开启的Iface关闭,此时wifi其实已经关闭了。后面虽然再一次调用了StateMachine.quitNow(),但其实是在状态机处理消息的队列中再加一个退出消息,所以在前面一次退出执行完之后,这次退出其实是直接就不会执行了,具体可以自行分析StateMachine的源码。执行完invokeExitMethods(),后面会继续进入到cleanupAfterQuitting()中。

private final void cleanupAfterQuitting() {
    if (mSm.mSmThread != null) {
        // If we made the thread then quit looper which stops the thread.
        getLooper().quit();
        mSm.mSmThread = null;
    }
    mSm.mSmHandler = null;
    mSm = null;
    mMsg = null;
    mLogRecords.cleanup();
    mStateStack = null;
    mTempStateStack = null;
    mStateInfo.clear();
    mInitialState = null;
    mDestState = null;
    mDeferredMessages.clear();
    mHasQuit = true;
}

此方法就是将状态机内部的所有关键变量都置为空或者清干净。并且处理消息的Looper也会一并别关闭,后续不会再有任何方法执行了。至此,关闭wifi的流程结束。


2、wifi关闭流程图

在这里插入图片描述


总结

以上对wifi开启和关闭的java层Framework的源码学习分析,中间对WifiNative以及后面的方法没有分析,我认为这部分是到了HAL层,所以待学习和理清楚里面的逻辑后需要单独拧出来介绍。个人认为在学习Framework时,还是需要带着一定实际使用场景来分析源码,不然进入到源码后会无头绪的发散,导致后面不知道自己到底要看哪部分代码,得不偿失。文中有表述或分析不对之处望大佬们及时批评。

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐