搜索
 找回密码
 立即注册

简单一步 , 微信登陆

Android wifi探究二:java层的wifi框架

作者:liuwei | 时间:2016-10-13 12:03:03 | 阅读:5571| 只看该作者
一.WifiService的启动
WifiService的启动可用如下简单时序图表示:

启动过程的图示画的比较简单,下面就顺着这个思路理一下代码的实现。
在SystemServer.java的 startOtherServices() 方法中,启动了WifiService,代码如下:
                mSystemServiceManager.startService(WIFI_P2P_SERVICE_CLASS);                mSystemServiceManager.startService(WIFI_SERVICE_CLASS);                mSystemServiceManager.startService(                            "com.android.server.wifi.WifiScanningService");                mSystemServiceManager.startService("com.android.server.wifi.RttService");
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
其中用到的WIFI_P2P_SERVICE_CLASS,WIFI_SERVICE_CLASS两个变量的值如下:
    private static final String WIFI_SERVICE_CLASS =            "com.android.server.wifi.WifiService";    private static final String WIFI_P2P_SERVICE_CLASS =            "com.android.server.wifi.p2p.WifiP2pService";
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4
以上代码可以看到,SystemServier中启动的Wifi相关的服务有四个,从上往下依次是P2P wifi服务,普通wifi,wifi扫描附近热点的服务以及以太网服务。
  • p2p wifi服务主要为Wi-Fi Direct提供相应的服务,Wi-Fi Direct是一种全新的技术,即使在没有传统的Wi-Fi网络或Wi-Fi接入点的环境中,仍然能够在诸如智能手机和数码相机等设备间实现点对点Wi-Fi连接。
  • wifi 服务这是我们关注的重点。
    下面我们看下wifi服务的启动过程。
    @SuppressWarnings("unchecked")    public SystemService startService(String className) {        final Class<SystemService> serviceClass;        try {            serviceClass = (Class<SystemService>)Class.forName(className);        } catch (ClassNotFoundException ex) {            Slog.i(TAG, "Starting " + className);            throw new RuntimeException("Failed to create service " + className                    + ": service class not found, usually indicates that the caller should "                    + "have called PackageManager.hasSystemFeature() to check whether the "                    + "feature is available on this device before trying to start the "                    + "services that implement it", ex);        }        return startService(serviceClass);    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
startService方法中通过Class.forName获得一个Class实例,但是这还不是WifiService的实例,然后调用startService进一步处理。这两个startService方法是重载方法,他们的参数类型不同。
   public <T extends SystemService> T startService(Class<T> serviceClass) {        final String name = serviceClass.getName();        Slog.i(TAG, "Starting " + name);        // Create the service.        if (!SystemService.class.isAssignableFrom(serviceClass)) {            throw new RuntimeException("Failed to create " + name                    + ": service must extend " + SystemService.class.getName());        }        final T service;        try {            C**tructor<T> c**tructor = serviceClass.getC**tructor(Context.class);            service = c**tructor.newInstance(mContext);        } catch (InstantiationException ex) {            throw new RuntimeException("Failed to create service " + name                    + ": service could not be instantiated", ex);        } catch (IllegalAcces**ception ex) {            throw new RuntimeException("Failed to create service " + name                    + ": service must have a public c**tructor with a Context argument", ex);        } catch (NoSuchMethodException ex) {            throw new RuntimeException("Failed to create service " + name                    + ": service must have a public c**tructor with a Context argument", ex);        } catch (InvocationTargetException ex) {            throw new RuntimeException("Failed to create service " + name                    + ": service c**tructor threw an exception", ex);        }        // Register it.        mServices.add(service);        // Start it.        try {            service.**tart();        } catch (RuntimeException ex) {            throw new RuntimeException("Failed to start service " + name                    + ": **tart threw an exception", ex);        }        return service;    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
在这个startService方法中,使用 C**tructor c**tructor = serviceClass.getC**tructor(Context.class);
service = c**tructor.newInstance(mContext);构建了一个WifiService的实例,然后使用mServices.add(service);向系统注册WifiService,并调用WifiService的**tart方法。构造WifiService会调用WifiService的构造方法,它的构造方法如下:
    public WifiService(Context context) {        super(context);        mImpl = new WifiServiceImpl(context);    }
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4
wifiService构造函数有新建了一个WifiServiceImpl实例,它才是Wifi管理服务真正的实现者,构造函数调用后不是调用了WifiService的**tart方法吗?
    @Override    public void **tart() {        Log.i(TAG, "Registering " + Context.WIFI_SERVICE);        publishBinderService(Context.WIFI_SERVICE, mImpl);    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5
在**tart方法中发布了Wifi服务,发布的WifiServiceImpl的实例。发布的过程如下:
1.
    protected final void publishBinderService(String name, IBinder service) {        publishBinderService(name, service, false);    }
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3
2.
    protected final void publishBinderService(String name, IBinder service,            boolean allowIsolated) {        ServiceManager.addService(name, service, allowIsolated);    }
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4
也就是说还是调用了ServiceManager的addService方法,这里就不再深入了,再深入就有点跑偏了。
通过以上分析,我们知道了真的wifi服务是WifiServiceImpl,它的构造方法如下:
    public WifiServiceImpl(Context context) {        mContext = context;        mInterfaceName =  SystemProperties.get("wifi.interface", "wlan0");        mTrafficPoller = new WifiTrafficPoller(mContext, mInterfaceName);        mWifiStateMachine = new WifiStateMachine(mContext, mInterfaceName, mTrafficPoller);        mWifiStateMachine.enableRssiPolling(true);        mBatteryStats = BatteryStatsService.getService();        mPowerManager = context.getSystemService(PowerManager.class);        mAppOps = (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE);        mUserManager = UserManager.get(mContext);        mNotificationController = new WifiNotificationController(mContext, mWifiStateMachine);        mSettingsStore = new WifiSettingsStore(mContext);        HandlerThread wifiThread = new HandlerThread("WifiService");        wifiThread.start();        mClientHandler = new ClientHandler(wifiThread.getLooper());        mWifiStateMachineHandler = new WifiStateMachineHandler(wifiThread.getLooper());        mWifiController = new WifiController(mContext, this, wifiThread.getLooper());    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
这里面做的事情还是很多的,主要有以下几点:
1.mInterfaceName 是从系统属性中获取的,它的值一般就是wlan0;
2.mTrafficPoller 这个实例的作用从其类的简介(Polls for traffic stats and notifies the clients )上可以看出他是用来查询流量统计信息比通知给客户端的。
3.mWifiStateMachine 这个实例代表着一个Wifi状态机,它定义了wifi的很多状态,通过消息驱动状态的转变。
4.mBatteryStats ,mPowerManager 用于wifi的电源管理,
5.mNotificationController 处理打开“打开wifi并且可以使用“的通知。
6.wifiThread 它是一个HandlerThread 的实例,HandlerThread 是一个内部有Looper的线程,wifiThread会一直**消息,消息到来以后,通过mClientHandler 的handleMessage来处理消息。
7.WifiStateMachineHandler 用于发送和处理wifi状态机相关的消息。
8.mWifiController 是另一个状态机,它和mWifiStateMachine 不同,mWifiStateMachine 表述wifi具体的状态,比如supplicant启动/关闭状态,driver启动/关闭状态等,mWifiController 则更高一级的控制wifi设备的开关状态,wifi热点的开关状态等。
理解mWifiController 和mWifiStateMachine 对于理解Android wifi框架至关重要。所以接下来,我们就着重分析wifi状态机的工作原理。
二.wifi状态机工作原理
我们说mWifiController 是高级别的wifi状态机,因为它管理的状态是wifi开关,wifi热点开关等状态,只有在wifi开关等具体状态下,判断wifi处于启动扫描附近热点状态等才是有意义的。
状态机无非就是一个定义了很多状态的机器,它收到消息后,会根据消息来切换这个机器的状态。mWifiController 的状态构造在它的构造方法中:
        addState(mDefaultState);            addState(mApStaDisabledState, mDefaultState);            addState(mStaEnabledState, mDefaultState);                addState(mDeviceActiveState, mStaEnabledState);                addState(mDeviceInactiveState, mStaEnabledState);                    addState(mScanOnlyLockHeldState, mDeviceInactiveState);                    addState(mFullLockHeldState, mDeviceInactiveState);                    addState(mFullHighPerfLockHeldState, mDeviceInactiveState);                    addState(mNoLockHeldState, mDeviceInactiveState);            addState(mStaDisabledWithScanState, mDefaultState);            addState(mApEnabledState, mDefaultState);            addState(mEcmState, mDefaultState);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
结构图如下:

每一个状态机都有一个初始状态:
        if (isScanningAlwaysAvailable) {            setInitialState(mStaDisabledWithScanState);        } else {            setInitialState(mApStaDisabledState);        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5
mWifiStateMachine 则表述wifi更加细致的状态,它的状态构建也是在构造函数中:
        addState(mDefaultState);            addState(mInitialState, mDefaultState);            addState(mSupplicantStartingState, mDefaultState);            addState(mSupplicantStartedState, mDefaultState);                addState(mDriverStartingState, mSupplicantStartedState);                addState(mDriverStartedState, mSupplicantStartedState);                    addState(mScanModeState, mDriverStartedState);                    addState(mConnectModeState, mDriverStartedState);                        addState(mL2ConnectedState, mConnectModeState);                            addState(mObtainingIpState, mL2ConnectedState);                            addState(mVerifyingLinkState, mL2ConnectedState);                            addState(mConnectedState, mL2ConnectedState);                            addState(mRoamingState, mL2ConnectedState);                        addState(mDisconnectingState, mConnectModeState);                        addState(mDisconnectedState, mConnectModeState);                        addState(mWpsRunningState, mConnectModeState);                addState(mWaitForP2pDisableState, mSupplicantStartedState);                addState(mDriverStoppingState, mSupplicantStartedState);                addState(mDriverStoppedState, mSupplicantStartedState);            addState(mSupplicantStoppingState, mDefaultState);            addState(mSoftApStartingState, mDefaultState);            addState(mSoftApStartedState, mDefaultState);                addState(mTetheringState, mSoftApStartedState);                addState(mTetheredState, mSoftApStartedState);                addState(mUntetheringState, mSoftApStartedState);        setInitialState(mInitialState);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
结构图如下:

并且初始化状态为mInitialState。
以上Android wifi框架中两个重要的状态机,那么状态机的工作机制是怎么样的呢?
以下是状态机工作原理简介:
状态机中的一个状态由State类的实例表示,State实例必须实现processMessage方法用来处理消息。并且可选的实现enter/exit/getName三个方法,enter/exit 等价于类的构造方法和销毁方法,本别用于初始化和清理一个状态。getName方法返回State的名字,默认的实现是返回类名。
当我们创建一个状态机时,需要使用addState方法给状态机添加状态,正如前面所展示的那样。setInitialState用于初始化一个状态机的初始状态。构建好一个状态机以后,我们需要调用start方法启动这个状态机,它就像一个机器,造好以后,加油或者充电,然后发动它,它就进入工作状态了。这个过程会调用初始化状态的enter方法初始化初始状态,如果初始状态由父状态,就会递归调用父状态,知道所有父状态的enter方法被调用。这样才算是完全初始化好了一个状态机,start方法还会时状态机进入已经构造结束阶段,这个时候,当有消息到来时,状态机就可以处理消息了。
处理消息的过程和初始化类似。当消息到来以后,当前状态就会调用processMessage来处理消息,如果当前消息能够处理消息,那么消息处理过程就结束了,此时会根据具体情况选择切换或者不切换状态机的状态。如果当前State不能处理消息,那么就会递交父State的processMessage来处理,父状态如果还不能处理就继续往上递交。如果一个消息从未被处理,unhandledMessage方法会被调用,这是最后处理这个消息的机会了。
如果我们期望停止状态机,可以调用quitNow或者quit方法。
当我们切换状态时,旧State的exit方法会被调用而新State的enter方法会被调用,同时他们父State也会做相同的事情。但是如果两个状态由相同的父状态,那么这个时候他们父状态就没有必要做任何操作了,因为它的状态其实并没有变。
以上就是一个状态机的工作原理的简要概述。我们可以想象,当应用程序需要扫描附近的热点时,如果wifi状态机正处于开启状态,那么上层的操作会导致wifi状态机接收到一个消息,开启的状态对它处理后,发现需要把wifi状态机切换到scan状态,于是开启状态的exit方法被调用,scan状态的enter方法被调用。切换不应该只是wifi状态机状态的切换,这个过程应该会调用底层的代码真正的把wifi的状态切换到对应的状态。切换过去以后wpa_supplicant会返回响应的事件,这又会导致响应的消息被wifi状态机接受,从而又促使wifi状态机状态的切换。
三.wifi框架梳理
再分析wifi框架之前,我先把我理解的wifi框架以图的形式展示出来:

android6.0的wifi框架中有两个非常重要的状态机:WifiController和WifiStateMachine,它们一起管理着wifi的各个状态以及状态之间的切换。WifiMonitor负责从wpa_supplicant接收事件,并且和WifiStateMachine交互。它们最终都会调用wifiNative,最终和wpa_supplicant交互。
拍砖要趁早,如果理解的不对,希望指出来。
下面我将会从给wap_supplicant发送命令与接收wpa_supplicant发出来的事件两方面梳理wifi框架的具体实现。
向wpa_supplicant发送命令
想象一下在应用程序我们怎么连接wifi:
1打开和关闭wifi
WifiManager wifi = (WifiManager) getSystemService(Context.WIFI_SERVICE);
wifi.setWifiEnabled(true);
2扫描附近热点
startScan();之后,接受WifiManager.SCAN_RESULTS_AVAILABLE_ACTION的广播会触发,在这个广播中调用getScanResults()方法可以获得一个List,它里面的每一个条目就是一个可连接的热点。
wifi.startScan();
results = wifi.getScanResults();
一般我们可能都需要做这两步吧,那么这两步会导致wifi状态机做怎么样的改变呢?
首先,用一张图来概括这个过程,然后大家可以顺着这个图的思路分析代码:

代码的分析过程如下:
首先从setWifiEnabled开始,wifi是一个WifiServiceImpl的客户端,它会通过binder和WifiServiceImpl的实例交互,也就是我么在WifiService中通过addService方法向系统注册的mImpl对象,忘记的可以回头看看前面。
    public boolean setWifiEnabled(boolean enabled) {        try {            return mService.setWifiEnabled(enabled);        } catch (RemoteException e) {            return false;        }    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
最终调用的的是WifiServiceImpl中的setWifiEnabled方法:
    public synchronized boolean setWifiEnabled(boolean enable) {        enforceChangePermission();        Slog.d(TAG, "setWifiEnabled: " + enable + " pid=" + Binder.getCallingPid()                    + ", uid=" + Binder.getCallingUid());        if (DBG) {            Slog.e(TAG, "Invoking mWifiStateMachine.setWifiEnabled\n");        }        /*        * Caller might not have WRITE_SECURE_SETTINGS,        * only CHANGE_WIFI_STATE is enforced        */        long ident = Binder.clearCallingIdentity();        try {            if (! mSettingsStore.handleWifiToggled(enable)) {                // Nothing to do if wifi cannot be toggled                return true;            }        } finally {            Binder.restoreCallingIdentity(ident);        }        mWifiController.sendMessage(CMD_WIFI_TOGGLED);        return true;    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
这个方法首先要使用mSettingsStore.handleWifiToggled(enable)来判断wifi状态是否可以切换,不能切换就直接返回。如果wifi状态可以切换,那么接下来会使用mWifiController.sendMessage(CMD_WIFI_TOGGLED);来给mWifiController状态佳发送一条消息,我们说过mWifiController状态机控制wifi设备的开关灯状态,由此开来这句话是对的哈。WifiController的sendMessage方法定义如下:
    /**     * Enqueue a message to this state machine.     *     * Message is ignored if state machine has quit.     */    public final void sendMessage(int what) {        // mSmHandler can be null if the state machine has quit.        SmHandler smh = mSmHandler;        if (smh == null) return;        smh.sendMessage(obtainMessage(what));    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
这里有使用了smh.sendMessage真正的向WifiController状态机的消息队列中发送消息。这时候,因为是使用mSmHandler来发送的消息,所以mSmHandler的handlerMessage方法会被调用:
    @Override        public final void handleMessage(Message msg) {            if (!mHasQuit) {                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 (mIsC**tructionCompleted) {                    /** Normal path */                    msgProcessedState = processMsg(msg);                } else if (!mIsC**tructionCompleted && (mMsg.what == SM_INIT_CMD)                        && (mMsg.obj == mSmHandlerObj)) {                    /** Initial one time path. */                    mIsC**tructionCompleted = true;                    invokeEnterMethods(0);                } else {                    throw new RuntimeException("StateMachine.handleMessage: "                            + "The start method not called, received msg: " + msg);                }                performTransiti**(msgProcessedState, msg);                // We need to check if mSm == null here as we could be quitting.                if (mDbg && mSm != null) mSm.log("handleMessage: X");            }        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
这个方法首先判断状态机是否退出,如果没有退出,就进一步判断状态机是否构建完成,如果构建完成的话,就调用processMsg来处理消息。
下面看看处理消息的过程:
        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;        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
1.首先获得当前的状态。‘
/* Stack used to manage the current hierarchy of states /
private StateInfo mStateStack[];
就是说mStateStack就是保存着当前的状态链。mStateStackTopIndex指向的状当前状态链最顶层的状态,比如说有如下状态链:
m
/\
a b
/\
c d
加入当前状态链是mdb,mStateStackTopIndex指向的就是d状态了。
2.判断是不是退出消息,如果是,就把状态机的状态置为退出状态。
3.调用当前状态的processMessage方法处理消息,如果当前状态没有处理消息,就调用其父状态的processMessage处理消息,依次往上。如果所有状态都没有处理消息,最终unhandledMessage方法就会被调用,这正如我们之前在wifi状态机原理中所说的那样。
4.如果消息被处理了,就返回curStateInfo,否则返回null。
curStateInfo是StateInfo的实例,StateInfo封装了State的状态信息类,这个类用于维护状态机中状态的层次关系。
这个时候,假如我们的状态机的状态为StaEnabledState状态,那么它的processMessage方法就会被调用:
    public boolean processMessage(Message msg) {            switch (msg.what) {                case CMD_WIFI_TOGGLED:                    if (mWifiStateMachine.syncGetWifiState() == 1) {                        log("wifi start fail before, now start it again");                        mWifiStateMachine.setSupplicantRunning(true);                    }                    if (! mSettingsStore.isWifiToggleEnabled() || msg.arg1 == WifiManager.WIFI_STATE_DISABLED) {                        if (mSettingsStore.isScanAlwaysAvailable()) {                            transitionTo(mStaDisabledWithScanState);                        } else {                            transitionTo(mApStaDisabledState);                        }                    }                    break;                    ...
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
1.当消息为CMD_AIRPLANE_TOGGLED的时候,在这种状态下,会使用mWifiStateMachine.setSupplicantRunning(true);来启动wpa_supplicant服务,这就是为什么上一篇博客 《Android wifi框架分析一:初步认识wpa_supplicant与wifi框架梳理》我们说打开wifi后wpa_supplicant就会启动的原因。然后把状态机切换到响应的状态。
2.WifiStateMachine.setSupplicantRunning方法如下:
    public void setSupplicantRunning(boolean enable) {        if (enable) {            sendMessage(CMD_START_SUPPLICANT);        } else {            sendMessage(CMD_STOP_SUPPLICANT);        }    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
我们看到这个时候逻辑就转到WifiStateMachine中了,这是一个新的状态机,工作原理都是一样的,这里就是发送对应的消息,这会促使WifiStateMachine做相应的操作。
3.transitionTo只是把简单的给mDestState变量赋值:
        private final void transitionTo(IState destState) {            mDestState = (State) destState;            if (mDbg) mSm.log("transitionTo: destState=" + mDestState.getName());        }
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4
我们说过状态机的切换要调用旧状态的exit方法,新状态的enter方法,那么回到handleMessage方法,在processMsg方法调用结束后,performTransiti**方法就会被调用:
       private void performTransiti**(State msgProcessedState, Message msg) {            /**             * If transitionTo has been called, exit and then enter             * the appropriate states. We loop on this to allow             * enter and exit methods to use transitionTo.             */            State orgState = mStateStack[mStateStackTopIndex].state;            /**             * Record whether message needs to be logged before we transition and             * and we won't log special messages SM_INIT_CMD or SM_QUIT_CMD which             * always set msg.obj to the handler.             */            boolean recordLogMsg = mSm.recordLogRec(mMsg) && (msg.obj != mSmHandlerObj);            if (mLogRecords.logOnlyTransiti**()) {                /** Record only if there is a transition */                if (mDestState != null) {                    mLogRecords.add(mSm, mMsg, mSm.getLogRecString(mMsg), msgProcessedState,                            orgState, mDestState);                }            } else if (recordLogMsg) {                /** Record message */                mLogRecords.add(mSm, mMsg, mSm.getLogRecString(mMsg), msgProcessedState, orgState,                        mDestState);            }            State destState = mDestState;            if (destState != null) {                /**                 * Process the transiti** including transiti** 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 comm**tateInfo = setupTempStateStackWithStatesToEnter(destState);                    invokeExitMethods(comm**tateInfo);                    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;            }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
1.首先从mStateStack[mStateStackTopIndex].state获取当前的状态
2.State destState = mDestState;从mDestState中获取目标状态。mDestState不就是我们在transitionTo中设置的目标状态吗?这里就用到了。
3.这里面invokeExitMethods和invokeEnterMethods方法就是调用对应状态的额exit和enter方法的。
至此状态机的操作结束,setWifiEnabled造成的影响终于结束了。
wifi.startScan()
那么 wifi.startScan(); 又做了什么事情呢?
因为我们已经详细分析过setWifiEnabled,这里简单分析:
WifiServiceImpl中的startScan方法被调用:
  public void startScan(ScanSettings settings, WorkSource workSource) { ...        mWifiStateMachine.startScan(Binder.getCallingUid(), scanRequestCounter++,                settings, workSource);    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5
WifiStateMachine.startScan方法被调用:
    public void startScan(int callingUid, int scanCounter,                          ScanSettings settings, WorkSource workSource) {        Bundle bundle = new Bundle();        bundle.putParcelable(CUSTOMIZED_SCAN_SETTING, settings);        bundle.putParcelable(CUSTOMIZED_SCAN_WORKSOURCE, workSource);        bundle.putLong(SCAN_REQUEST_TIME, System.currentTimeMillis());        sendMessage(CMD_START_SCAN, callingUid, scanCounter, bundle);    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
都是一样的套路,给状态机发消息。
handleMessage方法被调用,和之前一模一样。
然后当前状态的processMessage被调用来处理消息,不能处理就调用其父状态的processMessage来处理,以此类推。
假设我们处于DriverStartedState状态,其processMessage方法如下:
    @Override        public boolean processMessage(Message message) {            logStateAndMessage(message, getClass().getSimpleName());            switch(message.what) {                case CMD_START_SCAN:                    handleScanRequest(WifiNative.SCAN_WITHOUT_CONNECTION_SETUP, message);                    break;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
调用handleScanRequest进一步处理:
    private void handleScanRequest(int type, Message message) {        ...        // call wifi native to start the scan        if (startScanNative(type, freqs)) {            // only count battery c**umption if scan request is accepted            noteScanStart(message.arg1, workSource);            // a full scan covers everything, clearing scan request buffer            if (freqs == null)                mBufferedScanMsg.clear();            messageHandlingStatus = MESSAGE_HANDLING_STATUS_OK;            if (workSource != null) {                // External worksource was passed along the scan request,                // hence always send a broadcast                mSendScanResultsBroadcast = true;            }            return;        }        ...    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
可以看到调用startScanNative进一步处理:
    private boolean startScanNative(int type, String freqs) {        if (mWifiNative.scan(type, freqs)) {            mIsScanOngoing = true;            mIsFullScanOngoing = (freqs == null);            lastScanFreqs = freqs;            return true;        }        return false;    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
调用到mWifiNative.scan,看过上一篇博客的就会知道WifiNative会直接调用jni方法,进一步调用wifi.c中的方法和wpa_supplicant交互。整个wifi的框架就走了一遍。
执行完以后wpa_supplicant还要有事件返回呀,这个事件谁来接收?


收藏
收藏0
分享
分享
点赞
点赞4
反对
反对0
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册
手机版