P2P 关机流程服务异常修复完整方案

项目: OpenHarmony 6.1 Miracast投屏
日期: 2026-07-17
涉及修改: 3个文件, 5项修复


一、根因分析

故障现象: 关闭WiFi开关后重新打开,投屏搜索不到设备。

根本原因: 多层故障链导致P2P服务无法正常启动。

故障层1:Manager层中间状态卡死 + 回调条件过宽

层级 问题 表现
修复D AutoStopP2pService遇到OPENING/CLOSING态直接返回,不调DisableP2p midState卡死在中间态,状态机实例未重置
修复E AutoStartP2pService遇到CLOSING态直接返回FAIL 无法从CLOSING恢复启动
修复G DealP2pStateChangedstaState == CLOSED也触发AutoStopP2pService WiFi已关闭时冗余触发P2P关机,干扰状态恢复

故障层2:状态机层指令丢失

层级 问题 表现
修复A CMD_P2P_DISABLE 在P2pGroupRemoveState被静默丢弃 关机流程中断,状态机卡死

故障层3:入口与兜底

层级 问题 表现
权限 CastEngine WFD Sink跨进程EnableP2p被拒 VerifySameProcessPermission仅允许同进程

二、根因详解

根因1(核心):在关机过程中,P2pGroupOperatingState::ProcessCmdDisable 使用 DelayMessage 暂存 CMD_P2P_DISABLE,但在 P2pGroupRemoveState 中 CMD_P2P_DISABLE 只启动了一个计时器,组移除完成后计时器被停止,CMD_P2P_DISABLE 被静默丢弃了,关机流程中断,P2P状态被卡死在CLOSING

P2P关机详细流程:
1、创建组成功后:p2p状态为 GroupFormedState

初始状态机链(从前到后依次为父状态、子状态): P2pDefaultState → P2pEnabledState → P2pGroupFormationState → GroupFormedState

调用DisableP2p
ErrCode WifiP2pService::DisableP2p()
{
    WIFI_LOGI("DisableP2p");
    p2pStateMachine.SendMessage(static_cast<int>(P2P_STATE_MACHINE_CMD::CMD_P2P_DISABLE));
    return ErrCode::WIFI_OPT_SUCCESS;
}

在初始状态处理消息
bool GroupFormedState::ProcessCmdDisable(const InternalMessagePtr msg) const
{
    p2pStateMachine.DelayMessage(msg);// 关键核心点!CMD_P2P_DISABLE
    p2pStateMachine.SwitchState(&p2pStateMachine.p2pGroupOperatingState);
    return EXECUTED;
}

2、切换到状态 p2pGroupOperatingState
消息CMD_P2P_DISABLE在这里处理
此时状态机链:P2pDefaultState → P2pEnabledState → P2pGroupOperatingState

此处先移除组,再处理消息CMD_P2P_DISABLE
bool P2pGroupOperatingState::ProcessCmdDisable(const InternalMessagePtr msg) const
{
    /**
     * Before disabling P2P, you need to remove the group.
     */
    p2pStateMachine.DelayMessage(msg);
    return ProcessCmdRemoveGroup(msg);
}

bool P2pGroupOperatingState::ProcessCmdRemoveGroup(const InternalMessagePtr msg) const
{
    if (group.GetP2pGroupStatus() == P2pGroupStatus::GS_STARTED) {
        WifiNetAgent::GetInstance().DelInterfaceAddress(group.GetInterface(),
            group.IsGroupOwner() ? group.GetGoIpAddress() : group.GetGcIpAddress(), P2P_IP_ADDR_PREFIX_LEN);
        if (WifiP2PHalInterface::GetInstance().GroupRemove(group.GetInterface())) { // 下发删除组的命令到wpa_supplicant
            // .....
        } else {
            p2pStateMachine.SwitchState(&p2pStateMachine.p2pGroupRemoveState); //删除组命令下发成功之后切换到状态p2pGroupRemoveState
        }
    } 
    return EXECUTED;
}

3、当前状态:p2pGroupRemoveState,在上一个状态 p2pGroupOperatingState 还没有处理CMD_P2P_DISABLE消息,在此时继续处理
当前状态链:P2pDefaultState → P2pEnabledState → P2pGroupOperatingState → P2pGroupRemoveState

bool P2pGroupRemoveState::ExecuteStateMsg(InternalMessagePtr msg)
{
    //根因:在此处可以发现,CMD_P2P_DISABLE消息被静默丢弃,没有被真正消费,这里只是起了一个定时器然后就没有后续处理了,后续的状态机也没有真正转换成功。所以需要在这里将流程重新续上
    int msgName = msg->GetMessageName();
    switch (static_cast<P2P_STATE_MACHINE_CMD>(msgName)) {
        case P2P_STATE_MACHINE_CMD::CMD_P2P_DISABLE: {
            p2pStateMachine.DelayMessage(msg);  //关键修改,补全了后续的关机指令
            p2pStateMachine.StartTimer(static_cast<int>(P2P_STATE_MACHINE_CMD::P2P_DISABLE_TIMEOUT),
                GROUP_REMOVE_TIMEOUT);
            return EXECUTED;
        }
        case P2P_STATE_MACHINE_CMD::CMD_REMOVE_GROUP: {
            return EXECUTED;
        }
        default:
            WIFI_LOGE("Failed:The  P2P state machine does not process messages: [%{public}d]", msgName);
            return NOT_EXECUTED;
    }
}

删除组之后:底层会上报GROUP_REMOVED事件,转化为消息:P2P_STATE_MACHINE_CMD::P2P_EVENT_GROUP_REMOVED。

bool P2pGroupRemoveState::ExecuteStateMsg(InternalMessagePtr msg)
没有处理P2P_EVENT_GROUP_REMOVED,冒泡到父状态 P2pGroupOperatingState 来处理

同理前文补充了p2pStateMachine.DelayMessage(msg),该消息也会被 父状态 P2pGroupOperatingState 处理

4、切换到状态 P2pGroupOperatingState
bool P2pGroupOperatingState::ProcessGroupRemovedEvt(...)
{
    // 清理 DHCP / IP / current group ...
    p2pStateMachine.SwitchState(&p2pStateMachine.p2pIdleState);  // 切换到IdleState
    return EXECUTED;
}

5、切换到状态:P2pIdleState 预期想处理:CMD_P2P_DISABLE
当前状态链:P2pDefaultState → P2pEnabledState → P2pIdleState

P2pIdleState没有对消息CMD_P2P_DISABLE进行处理,所以流转到直接父状态P2pEnabledState处理!

6、切换到状态:P2pEnabledState
由 Enabled状态 去真正 StopP2p
bool P2pEnabledState::ProcessCmdDisable(InternalMessagePtr msg) const
{
    p2pStateMachine.BroadcastP2pStatusChanged(P2pState::P2P_STATE_CLOSING); // 广播关闭中
    p2pStateMachine.BroadcastP2pDiscoveryChanged(false); // 停止发现
    WifiP2PHalInterface::GetInstance().StopP2p(); // 调用 HAL 停止 P2P
    p2pStateMachine.SwitchState(&p2pStateMachine.p2pDisablingState); // 切换到禁用中状态
    return EXECUTED;
}

//处理禁用过程中的结果事件
bool P2pDisablingState::ExecuteStateMsg(InternalMessagePtr msg)
{
    switch (static_cast<P2P_STATE_MACHINE_CMD>(msg->GetMessageName())) {
        // WPA 驱动完成禁用操作后发送事件 disaplep2p成功!
        case P2P_STATE_MACHINE_CMD::WPA_CONNECTED_EVENT: {
            p2pStateMachine.StopTimer(static_cast<int>(P2P_STATE_MACHINE_CMD::DISABLE_P2P_TIMED_OUT));
            const int DISABLE_P2P_SUCCEED = 0;
            if (msg->GetParam1() == DISABLE_P2P_SUCCEED) {
                p2pStateMachine.p2pMonitor.MonitorEnds(p2pStateMachine.p2pIface);
                p2pStateMachine.SwitchState(&p2pStateMachine.p2pDisabledState); //一切完成,切换到DisabledState
                p2pStateMachine.BroadcastP2pStatusChanged(P2pState::P2P_STATE_IDLE); //广播已切换到IDLE_STATE
            } else {
                WIFI_LOGW("something wrong happened when try to disable p2p");
            }
            break;
        }
        // 超时!
        case P2P_STATE_MACHINE_CMD::DISABLE_P2P_TIMED_OUT: {
            WIFI_LOGE("P2P disable time out!");
            p2pStateMachine.p2pMonitor.MonitorEnds(p2pStateMachine.p2pIface);
            p2pStateMachine.SwitchState(&p2pStateMachine.p2pDisabledState);
            p2pStateMachine.BroadcastP2pStatusChanged(P2pState::P2P_STATE_IDLE); //依旧切换状态
            break;
        }
        default:
            return NOT_EXECUTED;
    }
    return EXECUTED;
}

广播监听到P2P状态切换,调用回调
void WifiP2pManager::DealP2pStateChanged(P2pState state)
{
    if (state == P2pState::P2P_STATE_IDLE) {
        // close p2p service sync here to avoid p2p service not closed when p2p service opened Again
        CloseP2pService();
        p2pEnableCond.notify_all();
    }
    return;
}

void WifiP2pManager::CloseP2pService(void)
{
    //至此,P2P状态彻底变更完成
    WifiConfigCenter::GetInstance().SetP2pMidState(WifiOprMidState::CLOSED);
    WifiConfigCenter::GetInstance().SetP2pState(static_cast<int>(P2pState::P2P_STATE_CLOSED));
}
根因2:由于根因1,P2P关机流程异常中断,P2P状态卡死在CLOSING状态,AutoStopP2pService遇到OPENING/CLOSING态直接返回,AutoStartP2pService遇到CLOSING态直接返回FAIL,框架层状态机卡死,无法恢复
根因3:DealP2pStateChanged中staState == CLOSED也触发AutoStopP2pService,在打开WIFI的时候如果P2P在STA前面启动,那P2P会被误杀
根因4:CastEngine WFD Sink跨进程EnableP2p被拒,VerifySameProcessPermission仅允许同进程

三、逐文件修改详情

3.1 修复A:p2p_group_remove_state.cpp — ExecuteStateMsg(InternalMessagePtr msg)

函数: ExecuteStateMsg()

根因:CMD_P2P_DISABLE消息传递到P2pGroupRemoveState状态机时被静默丢弃,没有被真正消费,这里只是起了一个定时器然后就没有后续处理了,后续的状态机也没有真正转换成功

问题: CMD_P2P_DISABLE消息在处理过程中被丢弃,导致底层没有真正执行关闭P2P的动作,框架状态机也没有正常变更,关闭失败

修改前:

bool P2pGroupRemoveState::ExecuteStateMsg(InternalMessagePtr msg)
{
    int msgName = msg->GetMessageName();
    switch (static_cast<P2P_STATE_MACHINE_CMD>(msgName)) {
        case P2P_STATE_MACHINE_CMD::CMD_P2P_DISABLE: {
            p2pStateMachine.StartTimer(static_cast<int>(P2P_STATE_MACHINE_CMD::P2P_DISABLE_TIMEOUT),
                GROUP_REMOVE_TIMEOUT);
            return EXECUTED;
        }
    }
}

修改后:

bool P2pGroupRemoveState::ExecuteStateMsg(InternalMessagePtr msg)
{
    int msgName = msg->GetMessageName();
    switch (static_cast<P2P_STATE_MACHINE_CMD>(msgName)) {
        case P2P_STATE_MACHINE_CMD::CMD_P2P_DISABLE: {
            p2pStateMachine.DelayMessage(msg);  //关键修改,补全了后续的关机指令
            p2pStateMachine.StartTimer(static_cast<int>(P2P_STATE_MACHINE_CMD::P2P_DISABLE_TIMEOUT),
                GROUP_REMOVE_TIMEOUT);
            return EXECUTED;
        }
    }
}

修改点: CMD_P2P_DISABLE消息被静默丢弃,没有被真正消费,这里只是起了一个定时器然后就没有后续处理了,后续的状态机也没有真正转换成功。所以需要在这里将流程重新续上


3.2 修复G:wifi_p2p_manager.cpp — DealP2pStateChanged

函数: DealP2pStateChanged() (L345-380)

根因:WIFI重启时P2P可能先于STA启动导致被误杀

问题: 原代码在 P2P_STATE_STARTED 回调中,当 staState == CLOSING || staState == CLOSED 时都触发 AutoStopP2pService()。但 WiFi 已经处于 CLOSED 状态时,再触发 P2P 关机可能干扰状态恢复流程,比如CLOSED 可能是 WiFi 还没开(比如 P2P 先启动的时序),此时不应该误杀 P2P。事实上在AutoStopStaService()中已经包含了P2P关机的动作,这里完全是冗余操作

修改前 (L362):

        if (staState == WifiOprMidState::CLOSING || staState == WifiOprMidState::CLOSED) {
            AutoStopP2pService();
        }

修改后:

        if (staState == WifiOprMidState::CLOSING) {
            AutoStopP2pService();
        }

修改点: 移除 || staState == WifiOprMidState::CLOSED 条件,仅在 WiFi 正在关闭(CLOSING)时触发 P2P 关机。WiFi 已完全关闭(CLOSED)时不再冗余触发。


3.3 权限修复:wifi_p2p_service_impl.cpp

文件: foundation/communication/communication/wifi/wifi/services/wifi_standard/wifi_framework/wifi_manage/wifi_p2p_sa/wifi_p2p_service_impl.cpp

根因:跨进程EnableP2p权限被拒
WFD Sink进程(PID=3442)通过IPC调用P2P SA(PID=3998)的EnableP2p,VerifySameProcessPermission检查PID不匹配 → PERMISSION_DENIED

函数: CheckCanEnableP2p() (L108-128)

修改前 (L108-113):

ErrCode WifiP2pServiceImpl::CheckCanEnableP2p(void)
{
    if (WifiPermissionUtils::VerifySameProcessPermission() == PERMISSION_DENIED) {
        WIFI_LOGE("EnableP2p:VerifySameProcessPermission PERMISSION_DENIED!");
        return WIFI_OPT_PERMISSION_DENIED;
    }

修改后:增加是否是系统进程的判定

ErrCode WifiP2pServiceImpl::CheckCanEnableP2p(void)
{
    if (WifiPermissionUtils::VerifySameProcessPermission() == PERMISSION_DENIED) {
        if (!WifiAuthCenter::IsSystemAccess()) {
            WIFI_LOGE("EnableP2p: permission denied (not same process, not system access)");
            return WIFI_OPT_PERMISSION_DENIED;
        }
    }

前置条件: wifi_auth_center.h已在L21通过#include引入,无需额外添加头文件。


四、副作用分析

4.1 修复G对回调流程的影响

场景 影响 结论
WiFi正在关闭(CLOSING) + P2P刚启动 仍触发AutoStopP2pService 无变化:与原逻辑一致
WiFi已关闭(CLOSED) + P2P刚启动 不再触发AutoStopP2pService 正面改进:避免冗余关机干扰恢复
WiFi正常运行(RUNNING/OPENING) 不触发(原逻辑也不触发) 无影响

4.2 修复A对状态及变更的影响

场景 影响 结论
修复A:ExecuteStateMsg(InternalMessagePtr msg) 重发 CMD_P2P_DISABLE P2P关机流程缺失的CMD_P2P_DISABLE指令被续上 正面修复:关机路径的正面修复,根源上解决问题

五、问题总结

该问题是一个以状态机流转过程中指令丢失为核心,叠加了不合理的权限限制和功能耦合扩大化的综合性问题,依次进行针对性修改之后问题解决

Logo

社区规范:仅讨论OpenHarmony相关问题。

更多推荐