1. 程式人生 > >Wifi模組—原始碼分析Wifi啟動1(Android P)

Wifi模組—原始碼分析Wifi啟動1(Android P)

一 前言

      android框架層的函式呼叫是出了名的繞,開發者可能因為各種原因比如避免衝突、條件判斷、函式封裝等等各種各樣需要考慮的因素而使得框架層的方法呼叫顯得比較長,所以看原始碼的時候先看大體流程,有需要再深入某些重要的細節。不然一入原始碼深似海,會淹沒在茫茫原始碼中。繁雜的東西,自己只要不凌亂,找好線索,進行總結,便會簡單。

二 圖示呼叫流程

                                          

三 程式碼具體流程

1 應用層

1.1 packages/apps/settings/WifiSettings.java

 在onStart()建立一個WifiEnabler物件,實現wifi開關功能。

@Override
public void onStart() {
    super.onStart();

    // On/off switch is hidden for Setup Wizard (returns null)
    mWifiEnabler = createWifiEnabler();

    if (mIsRestricted) {
        restrictUi();
        return;
    }

    onWifiStateChanged(mWifiManager.getWifiState());
}
private WifiEnabler createWifiEnabler() {
    final SettingsActivity activity = (SettingsActivity) getActivity();
     return new WifiEnabler(activity, new SwitchBarController(activity.getSwitchBar()),
            mMetricsFeatureProvider);
}

1.2 packages/apps/settings/WifiEnabler.java

開啟Wifi開關變會有下面的操作。

@Override
    public boolean onSwitchToggled(boolean isChecked) {
    //Do nothing if called as a result of a state machine event
    if (mStateMachineEvent) {
        return true;
    }
    // Show toast message if Wi-Fi is not allowed in airplane mode
    if (isChecked && !WirelessUtils.isRadioAllowed(mContext, Settings.Global.RADIO_WIFI)) {
        Toast.makeText(mContext, R.string.wifi_in_airplane_mode, Toast.LENGTH_SHORT).show();
        // Reset switch to off. No infinite check/listener loop.
        mSwitchWidget.setChecked(false);
        return false;
    }

    if (isChecked) {
        mMetricsFeatureProvider.action(mContext, MetricsEvent.ACTION_WIFI_ON);
    } else {
        // Log if user was connected at the time of switching off.
        mMetricsFeatureProvider.action(mContext, MetricsEvent.ACTION_WIFI_OFF,
        mConnected.get());
    }
    if (!mWifiManager.setWifiEnabled(isChecked)) {
        // Error
        mSwitchWidget.setEnabled(true);
        Toast.makeText(mContext, R.string.wifi_error, Toast.LENGTH_SHORT).show();
    }
    return true;
}

 點選開關會呼叫mWifiManager.setWifiEnabled。

2 java 框架層

2.1 frameworks/base/wifi/java/android/net/wifi/WifiManager.java

public boolean setWifiEnabled(boolean enabled) {
    try {
        return mService.setWifiEnabled(mContext.getOpPackageName(), enabled);
    } catch (RemoteException e) {
        throw e.rethrowFromSystemServer();
    }
}

2.2 frameworks/base/wifi/java/android/net/wifi/IWifiManager.aidl

boolean setWifiEnabled(String packageName, boolean enable);

2.3 frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiSeviceImpl.java

@Override
public synchronized boolean setWifiEnabled(String packageName, boolean enable)throws RemoteException {
    if (enforceChangePermission(packageName) != MODE_ALLOWED) {            return false;
    }

    Slog.d(TAG, "setWifiEnabled: " + enable + " pid=" + Binder.getCallingPid()
            + ", uid=" + Binder.getCallingUid() + ", package=" + packageName);
    mLog.info("setWifiEnabled package=% uid=% enable=%").c(packageName).c(Binder.getCallingUid()).c(enable).flush();

    boolean isFromSettings = checkNetworkSettingsPermission(
                Binder.getCallingPid(), Binder.getCallingUid());

    // If Airplane mode is enabled, only Settings is allowed to toggle Wifi
    if (mSettingsStore.isAirplaneModeOn() && !isFromSettings) {
            mLog.info("setWifiEnabled in Airplane mode: only Settings can enable wifi").flush();
            return false;
    }

    // If SoftAp is enabled, only Settings is allowed to toggle wifi
    boolean apEnabled = mWifiApState == WifiManager.WIFI_AP_STATE_ENABLED;

    if (apEnabled && !isFromSettings) {
        mLog.info("setWifiEnabled SoftAp not disabled: only Settings can enable wifi").flush();
        return false;
    }

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


    if (mPermissionReviewRequired) {
        final int wiFiEnabledState = getWifiEnabledState();
        if (enable) {
            if (wiFiEnabledState == WifiManager.WIFI_STATE_DISABLING
                    || wiFiEnabledState == WifiManager.WIFI_STATE_DISABL){
                if (startConsentUi(packageName, Binder.getCallingUid(),
                    WifiManager.ACTION_REQUEST_ENABLE)) {
                        return true;
                }
        }
        } else if (wiFiEnabledState == WifiManager.WIFI_STATE_ENABLING
            || wiFiEnabledState == WifiManager.WIFI_STATE_ENABLED) {
                if (startConsentUi(packageName, Binder.getCallingUid(),
                    WifiManager.ACTION_REQUEST_DISABLE)) {
                        return true;
                }
        }
    }

    mWifiController.sendMessage(CMD_WIFI_TOGGLED);
    return true;
}

       WifiManager.setWifiEnabled通過aidl跨程序呼叫到了WifiServiceImpl.setWifiEnabled,其中WifiServiceImpl是WifiService的實現類。在WifiServiceImpl的setWifiEnabled方法裡做的一些事情:

      enforceChangePermission 判斷呼叫的程序是否有許可權。想要開關wifi需要CHANGE_WIFI_STATE 許可權。

      isAirplaneModeOn 判斷飛航模式。

      handleWifiToggled 儲存wifi 操作的狀態。

      向WifiController傳送CMD_WIFI_TOGGLED訊息。

2.4 frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiController.java

        首先講一下WifiController,它是一個狀態機,可能Android每個版本的狀態機不完全一樣,比如Android P 和Android O比起來狀態機減少了一些分支。 WifiController在WIfiServiceImpl的建構函式中初始化、並開始執行。

         WifiController 和WifiStateMachine 不同,WifiStateMachine是一個複雜的狀態機,它維護了Wifi的啟動、掃描、連線、斷開等多個狀態。WifiController 是高級別的wifi狀態機,因為它管理的狀態是wifi開關,wifi熱點開關等狀態,只有在wifi開關等具體狀態下,判斷wifi處於啟動掃描附近熱點狀態等才是有意義的。

WifiController(Context context, WifiStateMachine wsm, Looper                        
        wifiStateMachineLooper, WifiSettingsStore wss, Looper                                   
        wifiServiceLooper, FrameworkFacade f,WifiStateMachinePrime wsmp) {
    super(TAG, wifiServiceLooper);
    ...

    // CHECKSTYLE:OFF IndentationCheck
    addState(mDefaultState);
        addState(mStaDisabledState, mDefaultState);
        addState(mStaEnabledState, mDefaultState);
            addState(mDeviceActiveState, mStaEnabledState);
        addState(mStaDisabledWithScanState, mDefaultState);
        addState(mEcmState, mDefaultState);
    // CHECKSTYLE:ON IndentationCheck
    ...

    if (checkScanOnlyModeAvailable()) {
        setInitialState(mStaDisabledWithScanState);
    }else {
        setInitialState(mStaDisabledState);
    }
    ...
    
}

 WifiController狀態機各狀態關係:

                                 

  狀態機初始狀態為StaDisabledState,在該狀態下對CMD_WIFI_TOGGLED訊息的處理:

class StaDisabledState extends State {
    private int mDeferredEnableSerialNumber = 0;
    private boolean mHaveDeferredEnable = false;
    private long mDisabledTimestamp;

    @Override
    public void enter() {
        mWifiStateMachinePrime.disableWifi();
        // Supplicant can't restart right away, so note the time we switched off
        mDisabledTimestamp = SystemClock.elapsedRealtime();
        mDeferredEnableSerialNumber++;
        mHaveDeferredEnable = false;
        mWifiStateMachine.clearANQPCache();
    }
    @Override
    public boolean processMessage(Message msg) {
        switch (msg.what) {
            case CMD_WIFI_TOGGLED:
                if (mSettingsStore.isWifiToggleEnabled()) {
                    if (doDeferEnable(msg)) {
                        if (mHaveDeferredEnable) {
                            //  have 2 toggles now, inc serial number and ignore both
                            mDeferredEnableSerialNumber++;
                        }
                        mHaveDeferredEnable = !mHaveDeferredEnable;
                        break;
                    }
                    transitionTo(mDeviceActiveState);
                } else if (checkScanOnlyModeAvailable()) {
                    // only go to scan mode if we aren't in airplane mode
                    if (mSettingsStore.isAirplaneModeOn()) {
                        transitionTo(mStaDisabledWithScanState);
                    }
                }
                break;
            ...
                
            default:
                return NOT_HANDLED;
        }
        return HANDLED;
    }
}

   在StaDisabledState狀態下沒做什麼處理,接著轉換到DeviceActiveState狀態,StaEnabledState是它的父狀態,由StateMachine的知識可知,轉換到該狀態時,會依次呼叫父、子狀態的enter()函式。先看DeviceActiveState的父狀態StaEnabledState:

class StaEnabledState extends State {
    @Override
        public void enter() {
            log("StaEnabledState.enter()");
        }

        @Override
        public boolean processMessage(Message msg) {
            switch (msg.what) {
                case CMD_WIFI_TOGGLED:
                    if (! mSettingsStore.isWifiToggleEnabled()) {
                        if (checkScanOnlyModeAvailable()) {
                            transitionTo(mStaDisabledWithScanState);
                        } else {
                            transitionTo(mStaDisabledState);
                        }
                    }
                    break;
                ...
                
}

StaEnabledState狀態下也沒做什麼處理,再接著看DeviceActiveState狀態:

/**
* Parent: StaEnabledState
*
* TODO (b/79209870): merge DeviceActiveState and StaEnabledState into a single state
*/
class DeviceActiveState extends State {
    @Override
    public void enter() {
        mWifiStateMachinePrime.enterClientMode();
        mWifiStateMachine.setHighPerfModeEnabled(false);
    }
    ...
}
            

可以看到在DeviceActiveState狀態下主要做了兩個操作mWifiStateMachinePrime.enterClientMode()和
        mWifiStateMachine.setHighPerfModeEnabled(false),主要看mWifiStateMachinePrime.enterClientMode()。

2.5 frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiStateMachinePrime.java

/**
* Method to switch wifi into client mode where connections to configured networks will be
* attempted.
*/
public void enterClientMode() {
    changeMode(ModeStateMachine.CMD_START_CLIENT_MODE);
}
private void changeMode(int newMode) {
    mModeStateMachine.sendMessage(newMode);
}

ModeStateMachine又是一個狀態機,不過這個狀態機比較簡單隻有三個狀態,初始狀態為WifiDisabledState。

private class ModeStateMachine extends StateMachine {
    // Commands for the state machine  - these will be removed,
    // along with the StateMachine itself
    public static final int CMD_START_CLIENT_MODE    = 0;
    public static final int CMD_START_SCAN_ONLY_MODE = 1;
    public static final int CMD_DISABLE_WIFI         = 3;

    private final State mWifiDisabledState = new WifiDisabledState();
    private final State mClientModeActiveState = new ClientModeActiveState();
    private final State mScanOnlyModeActiveState = new ScanOnlyModeActiveState();

    ModeStateMachine() {
        super(TAG, mLooper);

        addState(mClientModeActiveState);
        addState(mScanOnlyModeActiveState);
        addState(mWifiDisabledState);

        Log.d(TAG, "Starting Wifi in WifiDisabledState");
        setInitialState(mWifiDisabledState);
        start();
        ...
    }
 }

那就看看ModeStateMachine的相關處理,先是在初始狀態WifiDisabledState中。

class WifiDisabledState extends ModeActiveState {
    @Override
    public void enter() {
        Log.d(TAG, "Entering WifiDisabledState");
        mDefaultModeManager.sendScanAvailableBroadcast(mContext, false);
        mScanRequestProxy.enableScanningForHiddenNetworks(false);
        mScanRequestProxy.clearScanResults();
    }

    @Override
    public boolean processMessage(Message message) {
    Log.d(TAG, "received a message in WifiDisabledState: " + message);
        if (checkForAndHandleModeChange(message)) {
            return HANDLED;
        }
    return NOT_HANDLED;
    }

    @Override
    public void exit() {
        // do not have an active mode manager...  nothing to clean up
    }
}
private boolean checkForAndHandleModeChange(Message message) {
    switch(message.what) {
        case ModeStateMachine.CMD_START_CLIENT_MODE:
            Log.d(TAG, "Switching from " + getCurrentMode() + " to ClientMode");
            mModeStateMachine.transitionTo(mClientModeActiveState);
            break;
        case ModeStateMachine.CMD_START_SCAN_ONLY_MODE:
            Log.d(TAG, "Switching from " + getCurrentMode() + " to ScanOnlyMode");
            mModeStateMachine.transitionTo(mScanOnlyModeActiveState);
            break;
        case ModeStateMachine.CMD_DISABLE_WIFI:
            Log.d(TAG, "Switching from " + getCurrentMode() + " to WifiDisabled");
            mModeStateMachine.transitionTo(mWifiDisabledState);
            break;
        default:
            return NOT_HANDLED;
    }
    return HANDLED;
}

狀態機從WifiDisabledState狀態轉向ClientModeActiveState狀態,所以再繼續看ClientModeActiveState。

class ClientModeActiveState extends ModeActiveState {
    ClientListener mListener;
    private class ClientListener implements ClientModeManager.Listener {
        @Override
        public void onStateChanged(int state) {
            // make sure this listener is still active
            if (this != mListener) {
                Log.d(TAG, "Client mode state change from previous manager");
                return;
            }

            Log.d(TAG, "State changed from client mode. state = " + state);

            if (state == WifiManager.WIFI_STATE_UNKNOWN) {
                // error while setting up client mode or an unexpected failure.
                mModeStateMachine.sendMessage(CMD_CLIENT_MODE_FAILED, this);
            } else if (state == WifiManager.WIFI_STATE_DISABLED) {
                // client mode stopped
                mModeStateMachine.sendMessage(CMD_CLIENT_MODE_STOPPED, this);
            } else if (state == WifiManager.WIFI_STATE_ENABLED) {
                // client mode is ready to go
                Log.d(TAG, "client mode active");
            } else {
                // only care if client mode stopped or started, dropping
            }
        }
    }

    @Override
    public void enter() {
        Log.d(TAG, "Entering ClientModeActiveState");

        mListener = new ClientListener();
        mManager = mWifiInjector.makeClientModeManager(mListener);
        mManager.start();
        mActiveModeManagers.add(mManager);

        updateBatteryStatsWifiState(true);
    }
    ...
}

這裡的mManager是ActiveModeManager,是個介面,這裡的ClientModeManager實現了這個介面。我們繼續走下去,去看ClientModeManager,主要看mManager.start()這個呼叫。

2.6 frameworks/opt/net/wifi/service/java/com/android/server/wifi/ClientModeManager.java

/**
* Start client mode.
*/
public void start() {
    mStateMachine.sendMessage(ClientModeStateMachine.CMD_START);
}

ClientModeStateMachine也是個狀態機,該狀態機只有兩個狀態,初始狀態為IdleState。

private class ClientModeStateMachine extends StateMachine {
    // Commands for the state machine.
    public static final int CMD_START = 0;
    public static final int CMD_INTERFACE_STATUS_CHANGED = 3;
    public static final int CMD_INTERFACE_DESTROYED = 4;
    public static final int CMD_INTERFACE_DOWN = 5;
    private final State mIdleState = new IdleState();
    private final State mStartedState = new StartedState();
    ...

    ClientModeStateMachine(Looper looper) {
        super(TAG, looper);

        addState(mIdleState);
        addState(mStartedState);

        setInitialState(mIdleState);
        start();
    }
    ...
}

看一下初始狀態Idlesate狀態的處理

private class IdleState extends State {

    @Override
    public void enter() {
        Log.d(TAG, "entering IdleState");
        mClientInterfaceName = null;
        mIfaceIsUp = false;
    }

    @Override
    public boolean processMessage(Message message) {
        switch (message.what) {
            case CMD_START:
                updateWifiState(WifiManager.WIFI_STATE_ENABLING,
                        WifiManager.WIFI_STATE_DISABLED);

                mClientInterfaceName = mWifiNative.setupInterfaceForClientMode(
                        false /* not low priority */, mWifiNativeInterfaceCallback);
                if (TextUtils.isEmpty(mClientInterfaceName)) {
                    Log.e(TAG, "Failed to create ClientInterface. Sit in Idle");
                    updateWifiState(WifiManager.WIFI_STATE_UNKNOWN,
                            WifiManager.WIFI_STATE_ENABLING);
                    updateWifiState(WifiManager.WIFI_STATE_DISABLED,
                            WifiManager.WIFI_STATE_UNKNOWN);
                    break;
                }
                sendScanAvailableBroadcast(false);
                mScanRequestProxy.enableScanningForHiddenNetworks(false);
                mScanRequestProxy.clearScanResults();
                transitionTo(mStartedState);
                break;
            default:
                Log.d(TAG, "received an invalid message: " + message);
                return NOT_HANDLED;
        }
        return HANDLED;
    }
}

主要看 mWifiNative.setupInterfaceForClientMode的操作。

2.7 frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiNative.java

/**
* Setup an interface for Client mode operations.
*
* * This method configures an interface in STA mode in all the native daemons
* (wificond, wpa_supplicant & vendor HAL).
*
* @param lowPrioritySta The requested STA has a low request priority (lower probability of
*                       getting created, higher probability of getting destroyed).
* @param interfaceCallback Associated callback for notifying status changes for the iface.
* @return Returns the name of the allocated interface, will be null on failure.
*/
public String setupInterfaceForClientMode(boolean lowPrioritySta,
@NonNull InterfaceCallback interfaceCallback) {
    synchronized (mLock) {
        if (!startHal()) {
            Log.e(TAG, "Failed to start Hal");
            mWifiMetrics.incrementNumSetupClientInterfaceFailureDueToHal();
            return null;
        }
        if (!startSupplicant()) {
            Log.e(TAG, "Failed to start supplicant");
            mWifiMetrics.incrementNumSetupClientInterfaceFailureDueToSupplicant();
            return null;
        }
        Iface iface = mIfaceMgr.allocateIface(Iface.IFACE_TYPE_STA);
        if (iface == null) {
            Log.e(TAG, "Failed to allocate new STA iface");
            return null;
        }
        iface.externalListener = interfaceCallback;
        iface.name = createStaIface(iface, lowPrioritySta);
        if (TextUtils.isEmpty(iface.name)) {
            Log.e(TAG, "Failed to create STA iface in vendor HAL");
            mIfaceMgr.removeIface(iface.id);
            mWifiMetrics.incrementNumSetupClientInterfaceFailureDueToHal();
            return null;
        }
        if (mWificondControl.setupInterfaceForClientMode(iface.name) == null) {
            Log.e(TAG, "Failed to setup iface in wificond on " + iface);
            teardownInterface(iface.name);
            mWifiMetrics.incrementNumSetupClientInterfaceFailureDueToWificond();
            return null;
        }
        if (!mSupplicantStaIfaceHal.setupIface(iface.name)) {
            Log.e(TAG, "Failed to setup iface in supplicant on " + iface);
            teardownInterface(iface.name);
            mWifiMetrics.incrementNumSetupClientInterfaceFailureDueToSupplicant();
            return null;
        }
        iface.networkObserver = new NetworkObserverInternal(iface.id);
        if (!registerNetworkObserver(iface.networkObserver)) {
            Log.e(TAG, "Failed to register network observer on " + iface);
            teardownInterface(iface.name);
            return null;
        }
        mWifiMonitor.startMonitoring(iface.name);
        // Just to avoid any race conditions with interface state change callbacks,
        // update the interface state before we exit.
        onInterfaceStateChanged(iface, isInterfaceUp(iface.name));
        initializeNwParamsForClientInterface(iface.name);
        Log.i(TAG, "Successfully setup " + iface);
        return iface.name;
    }
}

到這裡就可以看到一些關鍵性的操作:

                         啟動Hal:startHal()

                         啟動supplicant:startSupplicant()

                         載入驅動(loadDriver):setupInterfaceForClientMode()

                         啟動WifiMonitor:WifiMonitor.startMonitoring()

       WifiMonitor.startMonitoring():這一步主要是在WifiMonitor中建立與wpa_supplicant通訊的socket通道、建立一個執行緒接收底層事件並分發處理。這裡會建立兩個socket通道與wpa_s通訊,一個用於下發指令,另一個用於接收事件。成功後WifiMonitor會向WifiStateMachine傳送一個代表socket通訊建立成功的訊息:SUP_CONNECTION_EVENT;收到這個訊息就表示Wifi已經啟動成功了。

     這次的Wifi啟動原始碼就講到Android的java框架層,下次接著繼續往下走流程:    

                HIDL—WPA適配層—wpa_supplicant—linux kernel 

    下面幾層的簡單說明:

3 HIDL

     Wifi到AndoidO之後不再使用jni,所以AndroidP也一樣不再使用jni來實現Java程式碼與本地的C/C++程式碼互動,而是使用HIDL,Hardware Interface Define Language。

4 WPA適配層

      wpa_supplicant適配層是通用的wpa_supplicant的封裝,在Android中作為WiFi部分的硬體抽象層來使用。wpa_supplicant適配層主要用於與wpa_supplicant守護程序的通訊,以提供給Android框架使用,它實現了載入、控制和訊息監控等功能。

5 wpa_supplicant

      wpa_supplicant是一個開源專案,已經被移植到Linux,Windows以及很多嵌入式系統上。它是WPA的應用層認證客戶端,負責完成認證相關的登入、加密等工作。              

    wpa_supplicant是一個獨立執行的守護程序,其核心是一個訊息迴圈,在訊息迴圈中處理WPA狀態機、控制命令、驅動事件、配置資訊等。wpa_supplicant有很多控制介面,也提供命令列和通行介面的控制模式:而Android與wpa_supplicant的通訊通過Socket完成。

         這個工程中的內容編譯後主要結果是生成動態庫libwpa_client.so和可執行程式wpa_supplicant。

6 Linux Kernel

Wifi的核心驅動程式。

四 總結

         Android P和Android O相比還是有不少變化的,主要是關鍵性的操作不再走WifiStateMachine這個線了,而是通過另外兩個比較簡單的狀態機ModeStateMachine和ClientModeStateMachine。