鸿蒙 WiFi 连接 流程

那当界面上显示扫描到的所有Ap时,我们选择其中的一个Ap发起连接,看下代码流程是怎样的。

c 复制代码
// applications/standard/settings/product/phone/src/main/ets/model/wifiImpl/WifiModel.ts
  connectWiFi(password: string) {
    let apInfo = this.userSelectedAp.getApInfo();
    let ret = false;
    let connectParam: any = {
      "ssid": apInfo.ssid,
      "bssid": apInfo.bssid,
      "preSharedKey": password,
      "isHiddenSsid": false, // we don't support connect to hidden ap yet
      "securityType": apInfo.securityType
    };
    LogUtil.info(MODULE_TAG + 'disconnect WiFi isConnected is ' + wifi.isConnected());     ---》当前如果没有连接就是false
    if (wifi.isConnected() === true) {
      ret = wifi.disconnect();
      LogUtil.info(MODULE_TAG + 'disconnect WiFi ret is ' + ret);
      this.registerWiFiConnectionObserver((code: Number) => {
        if (code === 0) {
          ret = wifi.connectToDevice(connectParam);
          this.unregisterWiFiConnectionObserver();
        }
      })
    }else{
      ret = wifi.connectToDevice(connectParam);                        ---> 就会走这里发起连接
      LogUtil.info(MODULE_TAG + 'connect WiFi ret is ' + ret);
    }
    return ret;
  }
// 有了前面的基础我们知道Wifi_Device的实现是wifi_device_impl, 然后在通过代理和服务端交互,所以我们直接看服务端的实现即可,调用流程这里就省略掉
// foundation/communication/wifi/wifi/services/wifi_standard/wifi_framework/wifi_manage/wifi_device_service_impl.cpp
ErrCode WifiDeviceServiceImpl::ConnectToDevice(const WifiDeviceConfig &config)
{
    if (!WifiAuthCenter::IsSystemAppByToken()) {
        WIFI_LOGE("ConnectToDevice:NOT System APP, PERMISSION_DENIED!");
        return WIFI_OPT_NON_SYSTEMAPP;
    }
    if (WifiPermissionUtils::VerifySetWifiInfoPermission() == PERMISSION_DENIED) {
        WIFI_LOGE("ConnectToDevice:VerifySetWifiInfoPermission PERMISSION_DENIED!");
        return WIFI_OPT_PERMISSION_DENIED;
    }

    if (WifiPermissionUtils::VerifyWifiConnectionPermission() == PERMISSION_DENIED) {
        WIFI_LOGE("ConnectToDevice:VerifyWifiConnectionPermission PERMISSION_DENIED!");
        return WIFI_OPT_PERMISSION_DENIED;
    }

    if (WifiPermissionUtils::VerifySetWifiConfigPermission() == PERMISSION_DENIED) {
        WIFI_LOGE("ConnectToDevice:VerifySetWifiConfigPermission PERMISSION_DENIED!");
        return WIFI_OPT_PERMISSION_DENIED;
    }

    if (!CheckConfigPwd(config)) {
        WIFI_LOGE("CheckConfigPwd failed!");
        return WIFI_OPT_INVALID_PARAM;
    }
    if (!IsStaServiceRunning()) {
        WIFI_LOGE("ConnectToDevice: sta service is not running!");
        return WIFI_OPT_STA_NOT_OPENED;
    }
    IStaService *pService = WifiServiceManager::GetInstance().GetStaServiceInst();
    if (pService == nullptr) {
        WIFI_LOGE("ConnectToNetwork: pService is nullptr!");
        return WIFI_OPT_STA_NOT_OPENED;
    }
    return pService->ConnectToDevice(config);
}

// foundation/communication/wifi/wifi/services/wifi_standard/wifi_framework/wifi_manage/wifi_sta/sta_interface.cpp
ErrCode StaInterface::ConnectToDevice(const WifiDeviceConfig &config)
{
    LOGD("Enter StaInterface::Connect.\n");
    CHECK_NULL_AND_RETURN(pStaService, WIFI_OPT_FAILED);
    if (pStaService->ConnectToDevice(config) != WIFI_OPT_SUCCESS) {
        LOGD("ConnectTo failed.\n");
        return WIFI_OPT_FAILED;
    }
    return WIFI_OPT_SUCCESS;
}

// foundation/communication/wifi/wifi/services/wifi_standard/wifi_framework/wifi_manage/wifi_sta/sta_service.cpp
ErrCode StaService::ConnectToDevice(const WifiDeviceConfig &config) const
{
    LOGI("Enter StaService::ConnectToDevice, ssid = %{public}s.\n", SsidAnonymize(config.ssid).c_str());
    CHECK_NULL_AND_RETURN(pStaStateMachine, WIFI_OPT_FAILED);
    int netWorkId = AddDeviceConfig(config);       ---》 对于新的AP,先添加,和Android都是一样的
    if(netWorkId == INVALID_NETWORK_ID) {
        LOGD("StaService::ConnectToDevice, AddDeviceConfig failed!");
        return WIFI_OPT_FAILED;
    }
    LOGI("StaService::ConnectToDevice, netWorkId: %{public}d", netWorkId);
    pStaStateMachine->SendMessage(WIFI_SVR_CMD_STA_CONNECT_NETWORK, netWorkId, NETWORK_SELECTED_BY_USER);    ---> 让状态机处理连接
    return WIFI_OPT_SUCCESS;
}

这里主要是两个函数:

一个是 AddDeviceConfig ,

一个是SendMessage(WIFI_SVR_CMD_STA_CONNECT_NETWORK, netWorkId, NETWORK_SELECTED_BY_USER)

流程都是一样的,通过,通过RPC访问HAL层,然后再把命令发给wpa,wpa收到命令后触发相应动作,这里我们看后者,状态机处理消息

// foundation/communication/wifi/wifi/services/wifi_standard/wifi_framework/wifi_manage/wifi_sta/sta_state_machine.cpp

cpp 复制代码
// 调用的这个函数:
void StaStateMachine::DealConnectToUserSelectedNetwork(InternalMessage *msg)
{
    LOGI("enter DealConnectToUserSelectedNetwork.\n");
    if (msg == nullptr) {
        LOGE("msg is null.\n");
        return;
    }

    int networkId = msg->GetParam1();
    int connTriggerMode = msg->GetParam2();
    if (connTriggerMode != NETWORK_SELECTED_BY_RETRY) {
        linkedInfo.retryedConnCount = 0;
    }

    if (networkId == linkedInfo.networkId) {
        if (linkedInfo.connState == ConnState::CONNECTED) {
            staCallback.OnStaConnChanged(OperateResState::CONNECT_AP_CONNECTED, linkedInfo);
            WIFI_LOGI("This network is in use and does not need to be reconnected.\n");
            return;
        }
        if (linkedInfo.connState == ConnState::CONNECTING &&
            linkedInfo.detailedState == DetailedState::OBTAINING_IPADDR) {
            WIFI_LOGI("This network is connecting and does not need to be reconnected.\n");
            return;
        }
    }

    /* Save connection information. */
    SaveDiscReason(DisconnectedReason::DISC_REASON_DEFAULT);
    SaveLinkstate(ConnState::CONNECTING, DetailedState::CONNECTING);
    /* Callback result to InterfaceService. */
    staCallback.OnStaConnChanged(OperateResState::CONNECT_CONNECTING, linkedInfo);

    if (StartConnectToNetwork(networkId) != WIFI_OPT_SUCCESS) {     ---》 继续看这个流程
        OnConnectFailed(networkId);
        return;
    }

    /* Sets network status. */
    WifiSettings::GetInstance().EnableNetwork(networkId, connTriggerMode == NETWORK_SELECTED_BY_USER);
    WifiSettings::GetInstance().SetDeviceAfterConnect(networkId);
    WifiSettings::GetInstance().SetDeviceState(networkId, (int)WifiDeviceConfigStatus::ENABLED, false);
}

//
ErrCode StaStateMachine::StartConnectToNetwork(int networkId)
{
    targetNetworkId = networkId;
    SetRandomMac(targetNetworkId);     ---> 随机mac地址

    WifiDeviceConfig deviceConfig;
    if (WifiSettings::GetInstance().GetDeviceConfig(networkId, deviceConfig) != 0) {
        LOGE("StartConnectToNetwork get GetDeviceConfig failed!");
        return WIFI_OPT_FAILED;
    }

    WifiStaHalInterface::GetInstance().SetBssid(networkId, deviceConfig.userSelectBssid.c_str());
	// 使能ap
    if (WifiStaHalInterface::GetInstance().EnableNetwork(targetNetworkId) != WIFI_IDL_OPT_OK) {
        LOGE("EnableNetwork() failed!");
        return WIFI_OPT_FAILED;
    }
	// 连接
    if (WifiStaHalInterface::GetInstance().Connect(targetNetworkId) != WIFI_IDL_OPT_OK) {
        LOGE("Connect failed!");
        staCallback.OnStaConnChanged(OperateResState::CONNECT_SELECT_NETWORK_FAILED, linkedInfo);
        return WIFI_OPT_FAILED;
    }
	// 保存
    if (WifiStaHalInterface::GetInstance().SaveDeviceConfig() != WIFI_IDL_OPT_OK) {
        /* OHOS's wpa don't support save command, so don't judge as failure */
        LOGE("SaveDeviceConfig() failed!");
    }

    StopTimer(static_cast<int>(CMD_NETWORK_CONNECT_TIMEOUT));
    StartTimer(static_cast<int>(CMD_NETWORK_CONNECT_TIMEOUT), STA_NETWORK_CONNECTTING_DELAY);
    return WIFI_OPT_SUCCESS;

// 上面的三步骤和Android都差不多,基本上大家都熟悉
// foundation/communication/wifi/wifi/services/wifi_standard/wifi_framework/wifi_manage/idl_client/wifi_sta_hal_interface.cpp
WifiErrorNo WifiStaHalInterface::Connect(int networkId)
{
    CHECK_NULL_AND_RETURN(mIdlClient, WIFI_IDL_OPT_FAILED);
    return mIdlClient->ReqConnect(networkId);
}
// idl_client , 前面几篇都讲过了,这里直接贴HAL层的代码了
// foundation/communication/wifi/wifi/services/wifi_standard/wifi_hal/wifi_hal_crpc_sta.c
int RpcConnect(RpcServer *server, Context *context)
{
    if (server == NULL || context == NULL) {
        return HAL_FAILURE;
    }
    int networkId = 0;
    if (ReadInt(context, &networkId) < 0) {
        return HAL_FAILURE;
    }
    WifiErrorNo err = Connect(networkId);    ---> 看这个的实现
    WriteBegin(context, 0);
    WriteInt(context, err);
    WriteEnd(context);
    return HAL_SUCCESS;
}

// foundation/communication/wifi/wifi/services/wifi_standard/wifi_hal/wifi_hal_sta_interface.c
WifiErrorNo Connect(int networkId)
{
    LOGD("Connect() networkid %{public}d", networkId);
    WifiWpaStaInterface *pStaIfc = GetWifiStaInterface(0);
    if (pStaIfc == NULL) {
        return WIFI_HAL_SUPPLICANT_NOT_INIT;
    }
    int ret = pStaIfc->wpaCliCmdSelectNetwork(pStaIfc, networkId);
    if (ret < 0) {
        LOGE("WpaCliCmdSelectNetwork failed! ret=%{public}d", ret);
        return WIFI_HAL_FAILED;
    }
    return WIFI_HAL_SUCCESS;
}

继续看 pStaIfc->wpaCliCmdSelectNetwork 的调用,

cpp 复制代码
//foundation/communication/wifi/wifi/services/wifi_standard/wifi_hal/wifi_hal_module/wpa_supplicant_hal/wpa_sta_hal/wifi_supplicant_hal.c 
static int WpaCliCmdSelectNetwork(WifiWpaStaInterface *this, int networkId)
{
    if (this == NULL) {
        return -1;
    }
    char cmd[CMD_BUFFER_SIZE] = {0};
    char buf[REPLY_BUF_SMALL_LENGTH] = {0};
    if (snprintf_s(cmd, sizeof(cmd), sizeof(cmd) - 1, "IFNAME=%s SELECT_NETWORK %d", this->ifname, networkId) < 0) {
        LOGE("snprintf err");
        return -1;
    }
    return WpaCliCmd(cmd, buf, sizeof(buf));
}

// foundation/communication/wifi/wifi/services/wifi_standard/wifi_hal/wifi_hal_module/wpa_supplicant_hal/wifi_wpa_common.c

int WpaCliCmd(const char *cmd, char *buf, size_t bufLen)
{
    if (cmd == NULL || buf == NULL || bufLen <= 0) {
        LOGE("WpaCliCmd, invalid parameters!");
        return -1;
    }
    WpaCtrl *ctrl = GetWpaCtrl();
    if (ctrl == NULL || ctrl->pSend == NULL) {
        LOGE("WpaCliCmd, ctrl/ctrl->pSend is NULL!");
        return -1;
    }
    size_t len = bufLen - 1;
    LOGI("wpa_ctrl_request -> cmd: %{private}s", cmd);
    int ret = wpa_ctrl_request(ctrl->pSend, cmd, strlen(cmd), buf, &len, NULL);    ---》 发送给wpa
    if (ret == WPA_CMD_RETURN_TIMEOUT) {
        LOGE("[%{private}s] command timed out.", cmd);
        return WPA_CMD_RETURN_TIMEOUT;
    } else if (ret < 0) {
        LOGE("[%{private}s] command failed.", cmd);
        return -1;
    }
    buf[len] = '\0';
    LOGI("wpa_ctrl_request -> buf: %{private}s", buf);
    if (strncmp(buf, "FAIL\n", strlen("FAIL\n")) == 0 ||
        strncmp(buf, "UNKNOWN COMMAND\n", strlen("UNKNOWN COMMAND\n")) == 0) {
        LOGE("%{private}s request success, but response %{public}s", cmd, buf);
        return -1;
    }
    return 0;
}

至此,连接的命令就发给wpa了,接下来就是协议上的连接了(四次握手),由wpa去交互完成,连接成功后,wpa上报 CTRL-EVENT-CONNECTED 事件,上层就收到,就开始走DHCP/或者是静态ip的流程,下一篇继续梳理这个。

相关推荐
dawn5 小时前
鸿蒙ArkTS中的获取网络数据
华为·harmonyos
桃花键神5 小时前
鸿蒙5.0时代:原生鸿蒙应用市场引领开发者服务新篇章
华为·harmonyos
鸿蒙自习室5 小时前
鸿蒙多线程开发——并发模型对比(Actor与内存共享)
华为·harmonyos
JavaPub-rodert6 小时前
鸿蒙生态崛起:开发者的机遇与挑战
华为·harmonyos
帅比九日8 小时前
【HarmonyOS Next】封装一个网络请求模块
前端·harmonyos
yilylong10 小时前
鸿蒙(Harmony)实现滑块验证码
华为·harmonyos·鸿蒙
baby_hua10 小时前
HarmonyOS第一课——DevEco Studio的使用
华为·harmonyos
HarmonyOS_SDK10 小时前
融合虚拟与现实,AR Engine为用户提供沉浸式交互体验
harmonyos
- 羊羊不超越 -11 小时前
App渠道来源追踪方案全面分析(iOS/Android/鸿蒙)
android·ios·harmonyos
长弓三石13 小时前
鸿蒙网络编程系列44-仓颉版HttpRequest上传文件示例
前端·网络·华为·harmonyos·鸿蒙