前言:

前面简单梳理了下安卓O之前的大概框图扫描流程,正是因为升O之后,wifi的更改还是蛮大的,所以这里还是在O的下一代安卓P上来进行一个梳理。也借鉴了很多网上的资料。

描述

  • 安卓O之前的版本,wifiservice 直接是与wpa_supplicant通信完成wifi状态控制、数据通信,在这次版本已经不是了
  • 安卓O wifi的scan、scan_results是通过wificond去跟kernel通信,不再通过wpa_supplicant了,而是多了个wificond的client跟底层的通信
  • 安卓O wifi direct的p2p find、wifi的连接等相差操作,还是通过wpa-supplicant通信
  • 安卓O 现在scan的命令不是通过wpa_supplicant下发到kernel,而是直接改由wificond传送到kernel,而scan results的结果也是直接由kernel传给wificond,再由wificond传送给上层去显示
  • 安卓O 上层下发的scan,改由SettingsLib去定时下发,不交给apk去做了,在android O之前的版本,scan的定时扫描都是在上层app做的,但从安卓O开始,google把这个定时器改到了framework中的SettingsLib(frameworks/base/packages/SettingsLib)中去了
  • 增加了HIDL :框架可以在无需重新构建 HAL 的情况下进行替换

Andriod O 与Andriod P的wifi框架差异

  • 多了WifiStateMachinePrime(状态机的前处理机制),wifiService的相关cmd 不再是直接send给WifiStateMachine,而是被送到WifiStateMachinePrime先进行处理后,再送往WifiStateMachine
  • 也多了一层ClientModeManager处理

下发扫描流程梳理

application层
WifiSettings --> WifiManager

packages/apps/Settings/src/com/android/settings/wifi/WifiSettings.java

WirelessSettings 在初始化的时候配置了由WifiEnabler 来处理Wifi 按钮,当用户按下Wifi 按钮后,

  • wifiSettings 响应onPreferenceTreeClick 送往WifiEnable
  • WifiEnabler 根据传入的状态,call WifiManager 设置wifi状态 ,开启跳转到WifiServiceImpl
@Override
 558     public boolean onPreferenceTreeClick(Preference preference) {
 559         // If the preference has a fragment set, open that
 560         if (preference.getFragment() != null) {
 561             preference.setOnPreferenceClickListener(null);
 562             return super.onPreferenceTreeClick(preference);
 563         }
 564

packages/apps/Settings/src/com/android/settings/wifi/WifiEnabler.java

@Override
    public boolean onSwitchToggled(boolean isChecked) {
        ........
        if (!mWifiManager.setWifiEnabled(isChecked))   //wifiManager设置wifi状态
        ........
        return true;
    }

application framework层
WifiManager --> WifiService --> WifiServiceImpl -->WifiController

  • 逐级进入WifiService,WifiServiceImpl,WifiController,WifiStateMachinePrime,WifiNative等完成CMD_WIFI_TOGGLED的命令传递,
  • 注意随着Android 版本升级后,传递CMD_WIFI_TOGGLED命令顺序也发生了变化
  • Android P 版本WifiServiceImpl 将CMD_WIFI_TOGGLED 先送到了WifiController处理,这里稍微有点儿复杂的状态机(都会涉及到处理此cmd),具体根据设备所处状态来跟就可以。

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

@Deprecated
    public boolean setWifiEnabled(boolean enabled) {
        try {
            return mService.setWifiEnabled(mContext.getOpPackageName(), enabled);//跳转到WifiServiceImpl
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }

frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiServiceImpl.java

public synchronized boolean setWifiEnabled(String packageName, boolean enable){
.... 
mWifiController.sendMessage(CMD_WIFI_TOGGLED);
}

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

class StaDisabledState extends State {
....
   public boolean processMessage(Message msg) {
		switch (msg.what) {
		 case CMD_WIFI_TOGGLED:
		 .....
		 transitionTo(mDeviceActiveState); // 跳转到 DeviceActiveState 
}
}
  class DeviceActiveState extends State {
       public void enter() {
          mWifiStateMachinePrime.enterClientMode(); //跳转到 WifiStateMachinePrime
           mWifiStateMachine.setHighPerfModeEnabled(false);
       }
  }

WifiController --> WifiStateMachinePrime

  • WifiStateMachinePrime传递CMD_START_CLIENT_MODE,先在内部状态机完成一轮转动,ModeStateMachine -->ClientModeActiveState,并送往ClientModeManager 创建WifiClientModeManager 实例

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

public void enterClientMode() {
changeMode(ModeStateMachine.CMD_START_CLIENT_MODE);
}

private class ModeStateMachine extends StateMachine {
...
 private boolean checkForAndHandleModeChange(Message message) {
	 case ModeStateMachine.CMD_START_CLIENT_MODE:
	 mModeStateMachine.transitionTo(mClientModeActiveState); //跳转到ClientModeActiveState
 }
 }

class ClientModeActiveState extends ModeActiveState {
	public void enter() {
	mManager = mWifiInjector.makeClientModeManager(mListener); //创建mClientModeManager实例
	mManager.start(); //
	mActiveModeManagers.add(mManager);
	updateBatteryStatsWifiState(true);
	}
}

WifiStateMachinePrime --> ClientModeManager

  • ClientModeManager 传递ClientModeStateMachine.CMD_START开始wpa_supplicant初始化信号,更新 wifiState updateWifiState,通过WifiNative初始化Client Mode

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

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

private class IdleState extends State {
public boolean processMessage(Message message) {
	 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); //send wifi  Scan Available  Broadcast
	mScanRequestProxy.enableScanningForHiddenNetworks(false);
	mScanRequestProxy.clearScanResults();
	transitionTo(mStartedState);
 }
}

ClientModeManager --> WifiNative

  • ClientModeManager 通过CMD_START 将启动传递给WifiNative
  • WifiNative 启动wpa_supplicant service
  • wifiNative 通过SupplicantStaHal 建立与 wpa_supplicant/hidl/1.1
    的sta_iface.cpp 关联(ifaceName)
  • wifiNative 向NetworkManagementService 注册NetworkObserverInternal
    实例,用于监视 设备iface一举一动
  • wifiNative 开启wifiMonitor , 其开始接手所有的事项(消息以及事件),一有变化就上报
  • framework(wifiStateMachine、wifiServiceImpl、SupplicantStaIfaceHal等等),就像是东厂的小兵,监视着下面的一举一动,一有变化马上上报
frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiNative.java
public String setupInterfaceForClientMode(boolean lowPrioritySta, @NonNull InterfaceCallback interfaceCallback) {
	... 
    	startSupplicant(); // 启动 wpa_supplicant 
    	.. 
    	mWificondControl.setupInterfaceForClientMode(iface.name); // 初始化与wificondcontrol 关联(用于后续wificond 作为framework与 wpa_supplicant 之间的通信信使
    	..
    	mSupplicantStaIfaceHal.setupIface(iface.name); // 
    	mWifiMonitor.startMonitoring(iface.name); // 启动WifiMonitor 上报所有的wpa_supplicant msg&event 
    	initializeNwParamsForClientInterface(iface.name);

WifiNative–> WificondControl

  • WificondControl 通过 binder 连接到wificond (server.cpp),创建ClientInterface
  • WificondControl 通过binder 连接到 client_interface_binder.cpp 获取
    wificondScaner ,用于wifi scan(上报scan results)
  • WificondControl 创建 pnoScan + Scan Event Handler , 且将之与设备 ifaceName 关联

frameworks/opt/net/wifi/service/java/com/android/server/wifi/WificondControl.java

public IClientInterface setupInterfaceForClientMode(@NonNull String ifaceName){
   ... 
    clientInterface = mWificond.createClientInterface(ifaceName); //创建ClientInterface
    ..
    mClientInterfaces.put(ifaceName, clientInterface); 
     IWifiScannerImpl wificondScanner = clientInterface.getWifiScannerImpl(); // 获取WifiScannerImpl
     PnoScanEventHandler pnoScanEventHandler = new PnoScanEventHandler(ifaceName);
     mPnoScanEventHandlers.put(ifaceName,  pnoScanEventHandler);
     wificondScanner.subscribePnoScanEvents(pnoScanEventHandler);
}

Android wifi扫描receiver收到的广播的时间不固定 安卓wifi扫描_扫描流程


上面是扫描下发的简略图

扫描结果上报梳理

NetlinkManager是收到底层的数据报通知才开始流程的

void NetlinkManager::ReceivePacketAndRunHandler(int fd) {
  ssize_t len = read(fd, ReceiveBuffer, kReceiveBufferSize);
  if (len == -1) {
    LOG(ERROR) << "Failed to read packet from buffer";
    return;
  }
  if (len == 0) {
    return;
  }
  // There might be multiple message in one datagram payload.
  uint8_t* ptr = ReceiveBuffer;
  while (ptr < ReceiveBuffer + len) {
    // peek at the header.
    if (ptr + sizeof(nlmsghdr) > ReceiveBuffer + len) {
      LOG(ERROR) << "payload is broken.";
      return;
    }
    const nlmsghdr* nl_header = reinterpret_cast<const nlmsghdr*>(ptr);
    unique_ptr<NL80211Packet> packet(
        new NL80211Packet(vector<uint8_t>(ptr, ptr + nl_header->nlmsg_len)));
    ptr += nl_header->nlmsg_len;
    if (!packet->IsValid()) {
      LOG(ERROR) << "Receive invalid packet";
      return;
    }
    // Some document says message from kernel should have port id equal 0.
    // However in practice this is not always true so we don't check that.
 
    uint32_t sequence_number = packet->GetMessageSequence();
 
    // Handle multicasts.
    if (sequence_number == kBroadcastSequenceNumber) {
      BroadcastHandler(std::move(packet));
      continue;
    }
 
    auto itr = message_handlers_.find(sequence_number);
    // There is no handler for this sequence number.
    if (itr == message_handlers_.end()) {
      LOG(WARNING) << "No handler for message: " << sequence_number;
      return;
    }
    // A multipart message is terminated by NLMSG_DONE.
    // In this case we don't need to run the handler.
    // NLMSG_NOOP means no operation, message must be discarded.
    uint32_t message_type =  packet->GetMessageType();
    if (message_type == NLMSG_DONE || message_type == NLMSG_NOOP) {
      message_handlers_.erase(itr);
      return;
    }
    if (message_type == NLMSG_OVERRUN) {
      LOG(ERROR) << "Get message overrun notification";
      message_handlers_.erase(itr);
      return;
    }
 
    // In case we receive a NLMSG_ERROR message:
    // NLMSG_ERROR could be either an error or an ACK.
    // It is an ACK message only when error code field is set to 0.
    // An ACK could be return when we explicitly request that with NLM_F_ACK.
    // An ERROR could be received on NLM_F_ACK or other failure cases.
    // We should still run handler in this case, leaving it for the caller
    // to decide what to do with the packet.
 
    bool is_multi = packet->IsMulti();
    // Run the handler.
    itr->second(std::move(packet));
    // Remove handler after processing.
    if (!is_multi) {
      message_handlers_.erase(itr);
    }
  }
}

收到消息后,其中ScannerImpl和NetlinkManager的调用联系是通过scan_utils传递过来的

scan_utils_->SubscribeScanResultNotification(
      interface_index_,
      std::bind(&ScannerImpl::OnScanResultsReady, this, _1, _2, _3, _4));

——>netlink_manager.cpp

在netlink_manager.cpp中ReceivePacketAndRunHandler调用了BroadcastHandler,并且数据值

void NetlinkManager::BroadcastHandler(unique_ptr<const NL80211Packet> packet) {
  if (packet->GetMessageType() != GetFamilyId()) {
    LOG(ERROR) << "Wrong family id for multicast message";
    return;
  }
  uint32_t command = packet->GetCommand();
 
  if (command == NL80211_CMD_NEW_SCAN_RESULTS ||
      // Scan was aborted, for unspecified reasons.partial scan results may be
      // available.
      command == NL80211_CMD_SCAN_ABORTED) {
    OnScanResultsReady(std::move(packet));
    return;
  }
 
  if (command == NL80211_CMD_SCHED_SCAN_RESULTS ||
      command == NL80211_CMD_SCHED_SCAN_STOPPED) {
    OnSchedScanResultsReady(std::move(packet));
    return;
  }
 
void NetlinkManager::OnScanResultsReady(unique_ptr<const NL80211Packet> packet) {
  uint32_t if_index;
  if (!packet->GetAttributeValue(NL80211_ATTR_IFINDEX, &if_index)) {
    LOG(ERROR) << "Failed to get interface index from scan result notification";
    return;
  }
  bool aborted = false;
  if (packet->GetCommand() == NL80211_CMD_SCAN_ABORTED) {
    aborted = true;
  }
 
  const auto handler = on_scan_result_ready_handler_.find(if_index);
  if (handler == on_scan_result_ready_handler_.end()) {
    LOG(WARNING) << "No handler for scan result notification from interface"
                 << " with index: " << if_index;
    return;
  }
 
  vector<vector<uint8_t>> ssids;
  NL80211NestedAttr ssids_attr(0);
  if (!packet->GetAttribute(NL80211_ATTR_SCAN_SSIDS, &ssids_attr)) {
    if (!aborted) {
      LOG(WARNING) << "Failed to get scan ssids from scan result notification";
    }
  } else {
    if (!ssids_attr.GetListOfAttributeValues(&ssids)) {
      return;
    }
  }
  vector<uint32_t> freqs;
  NL80211NestedAttr freqs_attr(0);
  if (!packet->GetAttribute(NL80211_ATTR_SCAN_FREQUENCIES, &freqs_attr)) {
    if (!aborted) {
      LOG(WARNING) << "Failed to get scan freqs from scan result notification";
    }
  } else {
    if (!freqs_attr.GetListOfAttributeValues(&freqs)) {
      return;
    }
  }
  // Run scan result notification handler.
  handler->second(if_index, aborted, ssids, freqs);
}

——>scanner_impl.cpp

void ScannerImpl::OnScanResultsReady(uint32_t interface_index, bool aborted,
                                     vector<vector<uint8_t>>& ssids,
                                     vector<uint32_t>& frequencies) {
  if (!scan_started_) {
    LOG(INFO) << "Received external scan result notification from kernel.";
  }
  scan_started_ = false;
  if (scan_event_handler_ != nullptr) {
    // TODO: Pass other parameters back once we find framework needs them.
    if (aborted) {
      LOG(WARNING) << "Scan aborted";
      scan_event_handler_->OnScanFailed();
    } else {
      scan_event_handler_->OnScanResultReady();
    }
  } else {
    LOG(WARNING) << "No scan event handler found.";
  }
}

——>WificondControl.java
这个handler是WiFi初始化wificond的时候传过去的,所以应该是wificond会回调

private class ScanEventHandler extends IScanEvent.Stub {
        private String mIfaceName;
 
   ScanEventHandler(@NonNull String ifaceName) {
            mIfaceName = ifaceName;
        }
 
Override
        public void OnScanResultReady() {
            Log.d(TAG, "Scan result ready event");
            mWifiMonitor.broadcastScanResultEvent(mIfaceName);
        }
Override
        public void OnScanFailed() {
            Log.d(TAG, "Scan failed event");
            mWifiMonitor.broadcastScanFailedEvent(mIfaceName);
        }
    }

——>WifiMonitor
发出SCAN_RESULTS_EVENT
前面的中断函数调用mWifiMonitor.broadcastScanFailedEvent(mIfaceName);这个函数就是这样的:

Broadcast scan result event to all the handlers registered for this event.
 param iface Name of iface on which this occurred.
    public void broadcastScanResultEvent(String iface) {
        sendMessage(iface, SCAN_RESULTS_EVENT);
    }
Override
    public boolean handleMessage(Message msg) {
        switch(msg.what) {
            case WifiMonitor.SCAN_FAILED_EVENT:
                Log.w(TAG, "Scan failed");
                cancelScanTimeout();
                reportScanFailure();
                break;
            case WifiMonitor.PNO_SCAN_RESULTS_EVENT:
                pollLatestScanDataForPno();
                break;
            case WifiMonitor.SCAN_RESULTS_EVENT:
                cancelScanTimeout();
                pollLatestScanData();
                break;
            default:
                // ignore unknown event
        }
        return true;
    }

——>WificondScannerImpl.java
接收SCAN_RESULTS_EVENT,发出WIFI_SCAN_RESULTS_AVAILABLE

private void pollLatestScanData() {
        synchronized (mSettingsLock) {
            if (mLastScanSettings == null) {
                 // got a scan before we started scanning or after scan was canceled
                return;
            }
           mNativeScanResults = mWifiNative.getScanResults(mIfaceName);
            List<ScanResult> singleScanResults = new ArrayList<>();
            int numFilteredScanResults = 0;
            for (int i = 0; i < mNativeScanResults.size(); ++i) {
                ScanResult result = mNativeScanResults.get(i).getScanResult();
                long timestamp_ms = result.timestamp / 1000; // convert us -> ms
                if (timestamp_ms > mLastScanSettings.startTime) {
                    if (mLastScanSettings.singleScanFreqs.containsChannel(
                                    result.frequency)) {
                        singleScanResults.add(result);
                    }
                } else {
                    numFilteredScanResults++;
                }
            }
            if (numFilteredScanResults != 0) {
                Log.d(TAG, "Filtering out " + numFilteredScanResults + " scan results.");
            }
 
            if (mLastScanSettings.singleScanEventHandler != null) {
                if (mLastScanSettings.reportSingleScanFullResults) {
                    for (ScanResult scanResult : singleScanResults) {
                        // ignore buckets scanned since there is only one bucket for a single scan
                        mLastScanSettings.singleScanEventHandler.onFullScanResult(scanResult,
                                /* bucketsScanned */ 0);
                    }
                }
                Collections.sort(singleScanResults, SCAN_RESULT_SORT_COMPARATOR);
                mLatestSingleScanResult = new WifiScanner.ScanData(0, 0, 0,
                        isAllChannelsScanned(mLastScanSettings.singleScanFreqs),
                        singleScanResults.toArray(new ScanResult[singleScanResults.size()]));
                mLastScanSettings.singleScanEventHandler
                        .onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE);
            }
 
            mLastScanSettings = null;
        }
    }

——>WificondScanningImpl.java
接收到WIFI_SCAN_RESULTS_AVAILABLE,发出CMD_SCAN_RESULTS,接着发送广播:SCAN_RESULTS_AVAILABLE_ACTION

public boolean processMessage(Message msg) {
            switch (msg.what) {
                case CMD_SCAN_RESULTS_AVAILABLE:
                    mWifiMetrics.incrementScanReturnEntry(
                            WifiMetricsProto.WifiLog.SCAN_SUCCESS,
                            mActiveScans.size());
                    reportScanResults(mScannerImpl.getLatestSingleScanResults());
                    mActiveScans.clear();
                    transitionTo(mIdleState);
                    return HANDLED;
State machine that holds the state of single scans. Scans should only be active in the
ScanningState. The pending scans and active scans maps are swapped when entering
ScanningState. Any requests queued while scanning will be placed in the pending queue and
executed after transitioning back to IdleState.

class WifiSingleScanStateMachine extends StateMachine implements WifiNative.ScanEventHandler {
...
        void reportScanResults(ScanData results) {
            if (results != null && results.getResults() != null) {
                if (results.getResults().length > 0) {
                    mWifiMetrics.incrementNonEmptyScanResultCount();
                } else {
                    mWifiMetrics.incrementEmptyScanResultCount();
                }
            }
            ScanData[] allResults = new ScanData[] {results};
            for (RequestInfo<ScanSettings> entry : mActiveScans) {
                ScanData[] resultsToDeliver = ScanScheduleUtil.filterResultsForSettings(
                        mChannelHelper, allResults, entry.settings, -1);
                WifiScanner.ParcelableScanData parcelableResultsToDeliver =
                        new WifiScanner.ParcelableScanData(resultsToDeliver);
                logCallback("singleScanResults",  entry.clientInfo, entry.handlerId,
                        describeForLog(resultsToDeliver));
                entry.reportEvent(WifiScanner.CMD_SCAN_RESULT, 0, parcelableResultsToDeliver);
                // make sure the handler is removed
                entry.reportEvent(WifiScanner.CMD_SINGLE_SCAN_COMPLETED, 0, null);
            }
 
 WifiScanner.ParcelableScanData parcelableAllResults =
                    new WifiScanner.ParcelableScanData(allResults);
            for (RequestInfo<Void> entry : mSingleScanListeners) {
                logCallback("singleScanResults",  entry.clientInfo, entry.handlerId,
                        describeForLog(allResults));
                entry.reportEvent(WifiScanner.CMD_SCAN_RESULT, 0, parcelableAllResults);
            }
 
            if (results.isAllChannelsScanned()) {
                mCachedScanResults.clear();
                mCachedScanResults.addAll(Arrays.asList(results.getResults()));
            }
        }
Called to indicate a change in state for the current scan.
Will dispatch a coresponding event to the state machine
Override
public void onScanStatus(int event) {
            if (DBG) localLog("onScanStatus event received, event=" + event);
            switch(event) {
                case WifiNative.WIFI_SCAN_RESULTS_AVAILABLE:
                case WifiNative.WIFI_SCAN_THRESHOLD_NUM_SCANS:
                case WifiNative.WIFI_SCAN_THRESHOLD_PERCENT:
                    sendMessage(CMD_SCAN_RESULTS_AVAILABLE);
                    break;
                case WifiNative.WIFI_SCAN_FAILED:
                    sendMessage(CMD_SCAN_FAILED);
                    break;
                default:
                    Log.e(TAG, "Unknown scan status event: " + event);
                    break;
            }
        }

——>WIfiScanner.java
收到CMD_SCAN_RESULT才算扫描结果,然后各自调用ScanListener的OnResults

case CMD_SCAN_RESULT :
 ((ScanListener) listener).onResults(
  ((ParcelableScanData) msg.obj).getResults());
  return;
//ActionListeners grouped together 
case CMD_OP_SUCCEEDED :
    ((ActionListener) listener).onSuccess();
    break;

nSuccess和onResults不是一回事,onSuccess只是打印了个success的log

WifiTracker是监听到SCAN_RESULTS_AVAILABLE_ACTION即有可用扫描结果时,app层就可以更新ap列表了,这时,app层主动向framework获取信息,可以知道扫描命令的下发和结果的获取是异步的。

画了个简易的流程图

Android wifi扫描receiver收到的广播的时间不固定 安卓wifi扫描_wifi_02

app层主动获取ap信息

借用网上的博主总结的流程(感谢):

Android wifi扫描receiver收到的广播的时间不固定 安卓wifi扫描_扫描流程_03


此时一定要结合自己的源码进行梳理