默认APN匹配异常问题说明

问题现象

插入正常电信 SIM 卡后,移动数据无法上网。排查发现当 apnTypes 包含 * 的 APN 被优先选中时,默认数据连接建立失败。

问题根因

ApnItem::CanDealWithType() 的设计是“能力匹配”,*DATA_CONTEXT_ROLE_ALL)对非 IA 类型返回 true 属于兜底语义,本身并非逻辑错误。

真正问题在于 ApnManager::FilterMatchedApns()

  • 仅按 CanDealWithType() 过滤,不区分“精确匹配”和“兜底匹配”;
  • 返回顺序依赖 APN 原始列表顺序;
  • * APN 排在 default APN 前面时,连接流程会优先尝试 * APN,导致默认数据连接失败。

问题解决

采用“选择策略修复”,不修改 CanDealWithType() 的通用语义:

  1. FilterMatchedApns() 中为候选 APN 增加匹配评分:
    • 精确匹配(如 default -> default)优先级最高;
    • 别名匹配(internal_default -> default)次之;
    • * 兜底匹配最低。
  2. 对匹配结果按评分进行稳定排序(stable_sort),保证在相同评分下仍保持原有顺序。
  3. 这样既能保证 default/internal_default 优先选择更合适的 APN,又保留 * 作为兜底能力,避免影响 DUN/MMS/SUPL 等其他场景。

该方案比直接将 * 分支改为 false 更稳妥,避免引入全局功能回归。

修改的patch如下:

--- a/cellular_data/services/src/apn_manager/apn_manager.cpp
+++ b/cellular_data/services/src/apn_manager/apn_manager.cpp
@@ -15,6 +15,9 @@
 
 #include "apn_manager.h"
 
+#include <algorithm>
+#include <cctype>
+
 #include "cellular_data_hisysevent.h"
 #include "core_manager_inner.h"
 #include "telephony_ext_wrapper.h"
@@ -81,6 +84,39 @@ constexpr const char *MO_ICCID_1 = "8985302";
 constexpr const char *MO_ICCID_2 = "8985307";
 constexpr const char *MO_UNICOM_MCCMNC = "46001";
 constexpr int32_t ICCID_LEN_MINIMUM = 7;
+
+namespace {
+constexpr int32_t APN_MATCH_SCORE_NONE = 0;
+constexpr int32_t APN_MATCH_SCORE_WILDCARD = 1;
+constexpr int32_t APN_MATCH_SCORE_ALIAS = 2;
+constexpr int32_t APN_MATCH_SCORE_EXACT = 3;
+
+std::string ToLowerCase(std::string value)
+{
+    std::transform(value.begin(), value.end(), value.begin(),
+        [](unsigned char c) { return static_cast<char>(std::tolower(c)); });
+    return value;
+}
+
+int32_t GetApnMatchScore(const sptr<ApnItem> &apnItem, const std::string &requestApnType)
+{
+    if (apnItem == nullptr) {
+        return APN_MATCH_SCORE_NONE;
+    }
+    int32_t maxScore = APN_MATCH_SCORE_NONE;
+    for (std::string apnType : apnItem->GetApnTypes()) {
+        apnType = ToLowerCase(apnType);
+        if (requestApnType == apnType) {
+            return APN_MATCH_SCORE_EXACT;
+        }
+        if (requestApnType == DATA_CONTEXT_ROLE_INTERNAL_DEFAULT && apnType == DATA_CONTEXT_ROLE_DEFAULT) {
+            maxScore = std::max(maxScore, APN_MATCH_SCORE_ALIAS);
+            continue;
+        }
+        if (requestApnType != DATA_CONTEXT_ROLE_IA && apnType == DATA_CONTEXT_ROLE_ALL) {
+            maxScore = std::max(maxScore, APN_MATCH_SCORE_WILDCARD);
+        }
+    }
+    return maxScore;
+}
+}
 
 ApnManager::ApnManager() = default;
 
@@ -458,11 +494,18 @@ std::vector<sptr<ApnItem>> ApnManager::FilterMatchedApns(const std::string &req
         FetchBipApns(matchApnItemList);
         return matchApnItemList;
     }
+    std::vector<std::pair<int32_t, sptr<ApnItem>>> scoredApnItems;
     std::shared_lock<std::shared_mutex> lock(mutex_);
     for (const sptr<ApnItem> &apnItem : allApnItem_) {
-        if (apnItem->CanDealWithType(requestApnType)) {
-            matchApnItemList.push_back(apnItem);
+        if (apnItem != nullptr && apnItem->CanDealWithType(requestApnType)) {
+            scoredApnItems.emplace_back(GetApnMatchScore(apnItem, requestApnType), apnItem);
         }
     }
+    std::stable_sort(scoredApnItems.begin(), scoredApnItems.end(),
+        [](const auto &left, const auto &right) { return left.first > right.first; });
+    for (const auto &item : scoredApnItems) {
+        matchApnItemList.emplace_back(item.second);
+    }
     TELEPHONY_LOGD("apn size is :%{public}zu", matchApnItemList.size());
     return matchApnItemList;
 }

Logo

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

更多推荐