34 · 忘记网络与删除后的列表时序(竞态窗口解剖)

核心文件:view/MiCarWifiEntryDetailFragment.kt(删除入口)、wifitrackerlib/StandardWifiEntry.java(forget 实现)、WifiPickerTracker.java(配置/状态双广播处理) 代码基线:fuben_micarsettings @ dev,f3 flavor 关联:三个历史 case 的机制底座——2901(删除残留 3.67s)、131987(列表清空 ~1s)、2782(海外删网延迟)

0. 一句话总纲

忘记网络没有任何乐观移除——App 调完 forget(null) 立即返回列表页,条目何时消失完全取决于”框架两路广播(binder 往返)× 库的多波重算 × 扫描缓存窗口”何时收敛。删除已连接网络时存在两个竞态窗口,谁先到决定用户看到的中间态。

1. 入口与 forget 实现

// 详情页负按钮「删除网络」→ confirmDeleteAp 确认框
detailWifiEntryData?.forget(null)      // MiCarWifiEntryDetailFragment.kt:157
fragmentHost.goBack()                  // :158 立即返回,不等 forget 结果
// f3dif StandardWifiEntry#forget:640-649
public synchronized void forget(@Nullable ForgetCallback callback) {
    if (canForget()) {                  // canForget = getWifiConfiguration() != null (:636-638)
        mForgetCallback = callback;
        mWifiManager.forget(mTargetWifiConfig.networkId, new ForgetActionListener(this));  // :646
    }
}
  • 回调传 null:App 层不用 ForgetCallback 刷新列表。
  • ForgetActionListener(WifiEntry.java:1505-1525)弱引用,成功/失败只回调——不做任何本地缓存摘除
  • WifiManager.forget(netId) 异步 binder:框架删配置→断开当前连接(若是本网)→发广播[待验证:广播顺序以 logcat 为准]。

唯一 UI 入口:已保存条目右侧详情按钮(仅 isSaved 且已 provisioned 显示)→ 详情页删除。没有长按忘网路径(已检索确认)。

2. 完整时序:从点删除到列表恢复

sequenceDiagram
    participant U as 用户
    participant D as 详情页
    participant F as WifiService
    participant T as WifiPickerTracker(worker)
    participant L as 列表UI

    U->>D: 删除网络 → 确认
    D->>F: WifiManager.forget(netId)
    D->>L: goBack()(UI 立即返回,显示旧状态)
    Note over L: 列表fragment view重建<br/>tracker onStart→handleOnStart 重拉
    F->>F: 删config→断链
    F-->>T: 波1: CONFIGURED_NETWORKS_CHANGED
    T->>T: 重拉配置,updateConfig→isSaved=false
    T->>L: onWifiEntriesChanged(重建#1)
    F-->>T: 波2: NETWORK_STATE_CHANGED / onLost
    T->>T: connectedState→DISCONNECTED
    T->>L: onWifiEntriesChanged(重建#2)
    Note over L: 条目按新状态落入可用组<br/>(前提:扫描仍可达)
    F-->>T: 波3: 下一轮 SCAN_RESULTS(≤10s)
    T->>L: onWifiEntriesChanged(重建#3,信号格刷新)

代码调用链(对照上图,波 1 展开):

MiCarWifiEntryDetailFragment#confirmDeleteAp:157
→ StandardWifiEntry#forget:641-646 → WifiManager.forget(netId)   [binder 异步]
→ [框架广播 CONFIGURED_NETWORKS_CHANGED]
→ BaseWifiTracker$mBroadcastReceiver#onReceive:177
→ WifiPickerTracker#handleConfiguredNetworksChangedAction:377 → processConfiguredNetworksChanged:385-393
→ updateWifiConfigurationsInternal:1353(binder 重拉 getConfiguredNetworks)
→ updateWifiConfigurations:1291-1350(config 缓存 clear+重建;被忘 netId 已不在)
→ StandardWifiEntry#updateConfig(null→emptyList):1054-1088 → mMatchingWifiConfigs.clear()
   → isSaved 变 false(共享网络无 config 的 entry 直接删缓存 :1331-1337,仅跨用户)
→ conditionallyUpdateScanResults(false,false):389(不 poll 新扫描、5min 容错窗不清旧扫描)
→ updateWifiEntries:610 → notifyOnWifiEntriesChanged:790/1554(main post)
→ MiWifiTrackerUtils:51 → MiCarBaseWifiEntryController#updateState:96 → 子类 removeAll+重建

波 2:NETWORK_STATE_CHANGED → handleNetworkStateChangedAction:397-434 → onPrimaryWifiInfoChanged:1152(WifiInfo 不再匹配 → mNetworkInfo=null :1154-1159 → connectedState 回 DISCONNECTED);另有 NetworkCallback.onLost → handleNetworkLost:471-482 → clearConnectionInfo(true)(:1255-1286 清 mNetwork/mNetworkInfo/mNetworkCapabilities/mWifiInfoLevel)。

3. 两个竞态窗口(谁先到,中间态不同)

窗口前提用户看到
A:config-changed 先到isSaved=false 但 connectedState 仍 CONNECTED → 仍算进 activeWifiEntries(:620-621 只摘 DISCONNECTED),connectedWifiEntry 还是它(:776-787)已保存组仍显示”已连接”高亮卡,等断链波到达才落回普通条目
B:network-state 先到DISCONNECTED 但 isSaved 仍 true(配置广播未到)→ 进 mWifiEntries;可用组跳过它(只计 mStartIndex),已保存组当普通 saved 显示”已连接卡 → 短暂未高亮已保存条目 → 消失/落入可用组”

4. 无乐观移除的机制事实

  • App 层:forget(null) 后立即 goBack,不先摘条目;
  • tracker 层:entry 从缓存消失只有两条路——①updateWifiConfigurations 的跨用户 removeIf(:1329-1337,单用户不触发);②updateStandardWifiEntryScansremoveIf(level==UNREACHABLE)(:938-939)。普通 forget 走的是”属性翻转”:isSaved→false + connectedState→DISCONNECTED,条目对象在扫描仍可达期间继续存在,只是换组。

5. 三个历史 case 在本机制的落点

Case现象机制落点
MANNPROB-2901删网络后条目残留 3.67s无乐观移除 + 等 CONFIGURED_NETWORKS_CHANGED 的 binder 往返(多波)
N801PROB-131987删已连接 AP 后列表清空 ~0.8-0.9sforget 触发断连 → 瞬间 scan 数据为空 → updateStandardWifiEntryScans 全置 UNREACHABLE 清缓存 → getWifiEntries 空 → App 当时无”空列表保留上一帧”防护 → 整表白屏(现已由 guardTransientEmpty 兜住)
MIICOSBUG-2782(海外 27B)删网后列表刷新延迟 ~1s第一次 CONFIGURED_NETWORKS_CHANGED(22ms)用缓存旧 scan 重算(内容陈旧),新 startScan 受系统 throttle(实测 7879ms defer 波动)9291427ms 后才到第二波;再叠 2.4G/5G 双 SSID 异步迁移

共同根因模式:App/tracker 双层都假设”广播最终会来”,但没有对”多久来、来几波、中间态长什么样”做任何控制——这就是删除类体验问题的结构性来源。

6. 恢复条件:被忘 AP 何时回到”可用组”

  • 要求它的扫描结果仍可达:updateScanResultInfo 维持 mScanResultLevel;
  • 扫描缓存窗口 = mMaxScanAgeMillis=15s(MiWifiTrackerUtils.kt:29)+ 失败容错 5min(ScanResultUpdater.java:36);
  • 若 AP 已出范围/扫描过期 → 条目直接从列表消失(不会留在”可用”)。

7. 修改密码的三个入口(共用 WifiPasswordDialog)

  1. 详情页「修改密码」(仅加密网;开放网禁用):带原 networkId connect(MiCarWifiEntryDetailFragment:100-129);
  2. 列表点击认证失败的已保存网(3405 修复):shouldEditBeforeConnect → 直弹带错误提示(MiCarBaseWifiEntryController:218-223);
  3. 连接无配置加密网:NO_CONFIG 回调 → 弹密码框(:324-329)。

[待验证]:①框架侧 forget 后三路事件(CONFIGURED/NETWORK_STATE/onLost)的确切顺序与间隔;②两个竞态窗口在实车上的实际时长。