Android7 WIFI系统 PNO机制流程详解和隐藏BUG修改

WIFI启动过程,WifiStateMachine加载驱动固件,连接上wpa_s的socket并检查好配置文件后,进入到DisconnectedState状态。在DisconnectedState状态下启动了mWifiConnectivityManager.handleConnectionStateChanged(WifiConnectivityManager.WIFI_STATE_DISCONNECTED)。这是启动扫描的第一步。

handleConnectionStateChanged(WifiConnectivityManager.WIFI_STATE_DISCONNECTED)
  startConnectivityScan(SCAN_ON_SCHEDULE)  // SCAN_ON_SCHEDULE == fause

在startConnectivityScan(SCAN_ON_SCHEDULE)中看到:

if (mScreenOn) {
    startPeriodicScan(scanImmediately); // 亮屏下周期扫描
} else { // screenOff
    if (mWifiState == WIFI_STATE_CONNECTED) {
        startConnectedPnoScan();
    } else {
        startDisconnectedPnoScan(); // 息屏下扫描wifi
    }
}

以上代码的扫描过程根据屏幕是否点亮,分成两种情况。一种亮屏下,开始周期扫描。另一种是息屏状态,使用pno扫描。pno扫描又根据当前WifiState是连接状态还是断开状态分为两种情况。

一、startPeriodicScan方法

这个方法用来在亮屏下进行周期扫描:

if (!ENABLE_BACKGROUND_SCAN) {
    if (scanImmediately) {
        resetLastPeriodicSingleScanTimeStamp();
    }
    Log.e(TAG, "PeriodicSingleScan by zhangdalei");
    mPeriodicSingleScanInterval = PERIODIC_SCAN_INTERVAL_MS;
    startPeriodicSingleScan();
} 

二、startConnectedPnoScan方法

因为ENABLE_CONNECTED_PNO_SCAN变量设置为false,因此这个方法将不被执行。

三、startDisconnectedPnoScan方法

这是我们现在需要关注的PNO扫描过程:

    private void startDisconnectedPnoScan() {
        // Initialize PNO settings
        PnoSettings pnoSettings = new PnoSettings();
        ArrayList.PnoNetwork> pnoNetworkList =
                mConfigManager.retrieveDisconnectedPnoNetworkList();
        int listSize = pnoNetworkList.size();

        if (listSize == 0) {
            // No saved network
            localLog("No saved network for starting disconnected PNO.");
            return;
        }
        // 设置 pnoSetting 对象参数
        pnoSettings.networkList = new PnoSettings.PnoNetwork[listSize];
        pnoSettings.networkList = pnoNetworkList.toArray(pnoSettings.networkList);
        pnoSettings.min5GHzRssi = mMin5GHzRssi;
        pnoSettings.min24GHzRssi = mMin24GHzRssi;
        pnoSettings.initialScoreMax = mInitialScoreMax;
        pnoSettings.currentConnectionBonus = mCurrentConnectionBonus;
        pnoSettings.sameNetworkBonus = mSameNetworkBonus;
        pnoSettings.secureBonus = mSecureBonus;
        pnoSettings.band5GHzBonus = mBand5GHzBonus;

        // Initialize scan settings
        ScanSettings scanSettings = new ScanSettings();
        scanSettings.band = getScanBand();
        scanSettings.reportEvents = WifiScanner.REPORT_EVENT_NO_BATCH;
        scanSettings.numBssidsPerScan = 0;
        scanSettings.periodInMs = DISCONNECTED_PNO_SCAN_INTERVAL_MS;    // 20s
        // TODO: enable exponential back off scan later to further save energy
        // scanSettings.maxPeriodInMs = 8 * scanSettings.periodInMs;

        mPnoScanListener.clearScanDetails();
        // 调用Scanner的函数,启动断开wifi下的pno扫描
        mScanner.startDisconnectedPnoScan(scanSettings, pnoSettings, mPnoScanListener);
        mPnoScanStarted = true;
    }

在startDisconnectedPnoScan方法中,将传入的mPnoScanListener放到一个map中,得到一个key。然后调用

startPnoScan(scanSettings, pnoSettings, key);

继续看:

@Scanner.java
private void startPnoScan(ScanSettings scanSettings, PnoSettings pnoSettings, int key) {
    // Bundle up both the settings and send it across.
    Bundle pnoParams = new Bundle();
    // Set the PNO scan flag.
    scanSettings.isPnoScan = true;
    pnoParams.putParcelable(PNO_PARAMS_SCAN_SETTINGS_KEY, scanSettings);    // 将setting保存到budle,用来发送
    pnoParams.putParcelable(PNO_PARAMS_PNO_SETTINGS_KEY, pnoSettings);
    // 调用mAsyncChannel发送消息
    mAsyncChannel.sendMessage(CMD_START_PNO_SCAN, 0, key, pnoParams);
}

在WifiStateMachine::DriverStartedState中定义了mWifiScanner。初始化时,出入其中变量mService为WifiScanningServiceImpl。现在看看WifiScanningServiceImpl的初始化过程:

messager为WifiScanningServiceImpl对象,WifiScanner是一个client,WifiScanningServiceImpl是服务端,WifiScanner通过AsyncChannel向WifiScanningServiceImpl发送扫描的命令,在Scanner中完成扫描动作。

在WifiScanningServiceImpl::startService中创建了几个状态机对象并开启了状态机。现在关注mPnoScanStateMachine。

WifiPnoScanStateMachine(Looper looper) {
    super("WifiPnoScanStateMachine", looper);

    setLogRecSize(512);
    setLogOnlyTransitions(false);

    // CHECKSTYLE:OFF IndentationCheck
    addState(mDefaultState);
        addState(mStartedState, mDefaultState);
            addState(mHwPnoScanState, mStartedState);
                addState(mSingleScanState, mHwPnoScanState);
            addState(mSwPnoScanState, mStartedState);
    // CHECKSTYLE:ON IndentationCheck  初始化为mDefaultState并
    setInitialState(mDefaultState);

CMD_START_PNO_SCAN将在SwPnoScanState这个状态中处理。

@WifiScanningServiceImpl.java::SwPnoScanState

 public boolean processMessage(Message msg) {
    ClientInfo ci = mClients.get(msg.replyTo);
    switch (msg.what) {
        case WifiScanner.CMD_START_PNO_SCAN:
            Bundle pnoParams = (Bundle) msg.obj;
            if (pnoParams == null) {
                replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, "params null");
                return HANDLED;
            }
            pnoParams.setDefusable(true);
            PnoSettings pnoSettings =pnoParams.getParcelable(WifiScanner.PNO_PARAMS_PNO_SETTINGS_KEY);
            ScanSettings scanSettings =pnoParams.getParcelable(WifiScanner.PNO_PARAMS_SCAN_SETTIGS_KEY);
            if (addSwPnoScanRequest(ci, msg.arg2, scanSettings,pnoSettings)) {
                replySucceeded(msg);
            } else {
                replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, "bad request");
                transitionTo(mStartedState);
            }
            break;

在处理上次发送的CMD_START_PNO_SCAN消息时,在此处将调用StartedState()做进一步处理。现在看起实现:

@WifiScanningServiceImpl.java::StartedState
public boolean processMessage(Message msg) {
    ClientInfo ci = mClients.get(msg.replyTo);
    switch (msg.what) {
        case WifiScanner.CMD_START_PNO_SCAN:
            Bundle pnoParams = (Bundle) msg.obj;
            if (pnoParams == null) {
                replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, "params null");
                return HANDLED;
            }
            pnoParams.setDefusable(true);
            PnoSettings pnoSettings =
             pnoParams.getParcelable(WifiScanner.PNO_PARAMS_PNO_SETTINGS_KEY);
            // This message is handled after the transition to SwPnoScan/HwPnoScan state
            deferMessage(msg);
            if (mScannerImpl.isHwPnoSupported(pnoSettings.isConnected)) {
                transitionTo(mHwPnoScanState);
            } else {
                transitionTo(mSwPnoScanState);
            }
            break;

我们这里支持HardwarePno,将计入mHwPnoScanState状态,进行处理CMD_START_PNO_SCAN消息:

@HwPnoScanState
public boolean processMessage(Message msg) {
    ClientInfo ci = mClients.get(msg.replyTo);
    switch (msg.what) {
        case WifiScanner.CMD_START_PNO_SCAN:
            Bundle pnoParams = (Bundle) msg.obj;
            if (pnoParams == null) {
                replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, "params null");
                return HANDLED;
            }
            pnoParams.setDefusable(true);
            PnoSettings pnoSettings = pnoParams.getParcelable(WifiScanner.PNO_PARAMS_PNO_SETTINGS_KEY);
            ScanSettings scanSettings =
                    pnoParams.getParcelable(WifiScanner.PNO_PARAMS_SCAN_SETTINGS_KEY);
            if (addHwPnoScanRequest(ci, msg.arg2, scanSettings, pnoSettings)) {
                replySucceeded(msg);
            } else {
                replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, "bad request");
                transitionTo(mStartedState);
            }
            break;

上面代码进一步调用addHwPnoScanRequest()函数处理PNO扫描请求。

private boolean addHwPnoScanRequest(ClientInfo ci, int handler, ScanSettings scanSettings, PnoSettings pnoSettings) {
    WifiNative.PnoSettings nativePnoSettings = convertPnoSettingsToNative(pnoSettings);
    if (!mScannerImpl.setHwPnoList(nativePnoSettings,mPnoScanStateMachine)) {
        return false;
    }
}

这里的mScannerImpl实际是SupplicantWifiScannerImpl类。这里将调用到SupplicantWifiScannerImpl::setHwPnoList();

public boolean setHwPnoList(WifiNative.PnoSettings settings,
        WifiNative.PnoEventHandler eventHandler) {
    synchronized (mSettingsLock) {
        if (mPnoSettings != null) {
            Log.w(TAG, "Already running a PNO scan");
            return false;
        }
        mPnoEventHandler = eventHandler;
        mPnoSettings = settings;
        if (!setNetworkPriorities(settings.networkList)) return false;
        // For supplicant based PNO, we start the scan immediately when we set pno list.
        processPendingScans();
        return true;
    }
}

调用processPendingScans()进一步处理扫描请求。processPendingScans()函数用来处理各种扫描请求,包括正常的扫描和PNO扫描。现在我们只看PNO扫描:

private void processPendingScans() {
    else if (isHwPnoScanRequired()) {
        newScanSettings.setHwPnoScan(mPnoSettings.networkList, mPnoEventHandler);
        boolean status;
        // If the PNO network list has changed from the previous request, ensure that
        // we bypass the debounce logic and restart PNO scan.
        if (isDifferentPnoScanSettings(newScanSettings)) {
            // 启动PNO扫描
            status = restartHwPnoScan();
        } else {
            // 启动PNO扫描
            status = startHwPnoScan();
        }
        if (status) {
            mLastScanSettings = newScanSettings;
        } else {
            Log.e(TAG, "Failed to start PNO scan");
            // indicate scan failure async
            mEventHandler.post(new Runnable() {
                public void run() {
                    if (mPnoEventHandler != null) {
                        mPnoEventHandler.onPnoScanFailed();
                    }
                    // Clean up PNO state, we don't want to continue PNO scanning.
                    mPnoSettings = null;
                    mPnoEventHandler = null;
                }
            });
        }
}

我们看restartHwPnoScan()的执行流程。不过可想到,接下来将要向wpa_supplicant发送执行了。

private boolean restartHwPnoScan() {
    mHwPnoDebouncer.forceStopPnoScan();
    return mHwPnoDebouncer.startPnoScan(mHwPnoDebouncerListener);
}

public boolean startPnoScan(Listener listener) {
    if (DBG) Log.d(TAG, "Starting PNO scan");
    mListener = listener;
    if (!setPnoState(true)) {
        mListener = null;
        return false;
    }
    return true;
}

setPnoState()将调用updatePnoState()函数:

private boolean updatePnoState(boolean enable) {
    if (mCurrentPnoState == enable) {
        if (DBG) Log.d(TAG, "PNO state is already " + enable);
        return true;
    }
    mLastPnoChangeTimeStamp = mClock.elapsedRealtime();
    // 调用Native函数向wpa_supplicant发消息了
    if (mWifiNative.setPnoScan(enable)) {
        Log.d(TAG, "Changed PNO state from " + mCurrentPnoState + " to " + enable);
        mCurrentPnoState = enable;
        return true;
    } else {
        Log.e(TAG, "PNO state change to " + enable + " failed");
        mCurrentPnoState = false;
        return false;
    }
}
public boolean setPnoScan(boolean enable) {
    String cmd = enable ? "SET pno 1" : "SET pno 0";
    return doBooleanCommand(cmd);
}

大体的扫描流程就是这样了。

问题

Pno存在这样一个问题:

设置一个wifi密码为空,让设备连接。连接上后,设置wifi有密码。这个时候PNO能够匹配SSID找到wifi,但是加密方式改变了。这是PNO就会持续不断的扫描,导致功耗高。需要解决这个问题。

在SupplicantWifiScannerImpl.java中注册了接收WifiMonitor扫描结果的消息的handle。当扫描错误或是有扫描结果时将调用handle处理。

@SupplicantWifiScannerImpl.java
WifiMonitor.getInstance().registerHandler(mWifiNative.getInterfaceName(), WifiMonitor.SCAN_FAILED_EVENT, mEventHandler);
WifiMonitor.getInstance().registerHandler(mWifiNative.getInterfaceName(), WifiMonitor.SCAN_RESULTS_EVENT, mEventHandler);

@Override
public boolean handleMessage(Message msg) {
    switch(msg.what) {
        case WifiMonitor.SCAN_FAILED_EVENT:
            Log.w(TAG, "Scan failed");
            mAlarmManager.cancel(mScanTimeoutListener);
            reportScanFailure();
            processPendingScans();
            break;
        case WifiMonitor.SCAN_RESULTS_EVENT:
            mAlarmManager.cancel(mScanTimeoutListener);
            pollLatestScanData();
            processPendingScans();
            break;
        default:
            // ignore unknown event
    }
    return true;
}

不管是SCAN_FAILED_EVENT或是SCAN_RESULTS_EVENT都讲调用processPendingScans()进一步处理PNO扫描。

lse if (isHwPnoScanRequired()) {
    newScanSettings.setHwPnoScan(mPnoSettings.networkList, mPnoEventHandler);
    boolean status;
    // If the PNO network list has changed from the previous request, ensure that
    // we bypass the debounce logic and restart PNO scan.
    if (isDifferentPnoScanSettings(newScanSettings)) {
        status = restartHwPnoScan();
    } else {
        status = startHwPnoScan();
    }
    if (status) {
        mLastScanSettings = newScanSettings;
    } else {
        Log.e(TAG, "Failed to start PNO scan");
        // indicate scan failure async
        mEventHandler.post(new Runnable() {
            public void run() {
                if (mPnoEventHandler != null) {
                    mPnoEventHandler.onPnoScanFailed();
                }
                // Clean up PNO state, we don't want to continue PNO scanning.
                mPnoSettings = null;
                mPnoEventHandler = null;
            }
        });
    }

现在看上面代码的一些细节:

一是isHwPnoScanRequired()函数:

private boolean isHwPnoScanRequired(boolean isConnectedPno) {
    return (!isConnectedPno & mHwPnoScanSupported);
}

private boolean isHwPnoScanRequired() {
    if (mPnoSettings == null) return false;
    return isHwPnoScanRequired(mPnoSettings.isConnected);
}

如果mPnoSettings为NULL,返回false,PNO不在执行。然后mPnoSettings.isConnected我们这里是断开wifi时的pno,为false. mPnoSettings在setHwPnoList时设置。

但是当我们PNO扫描成功,并不会把mPnoSettings设置为NULL,这将导致一次PNO扫描之后,将接着下一次PNO扫描。代码如下;

if (status) {
        mLastScanSettings = newScanSettings;
    } else {
        Log.e(TAG, "Failed to start PNO scan");
        // indicate scan failure async
        mEventHandler.post(new Runnable() {
            public void run() {
                if (mPnoEventHandler != null) {
                    mPnoEventHandler.onPnoScanFailed();
                }
                // Clean up PNO state, we don't want to continue PNO scanning.
                mPnoSettings = null;
                mPnoEventHandler = null;
            }
        });

所以我们需要在扫描成功是吧mPnoSettings设置为NULL。解决问题。

你可能感兴趣的:(WIFI开发)