> 技术文档 > Android 5G NR 状态类型介绍

Android 5G NR 状态类型介绍


基本概念

5G NR(New Radio)是第五代移动通信技术的无线接入标准,相比4G LTE具有更高的速度、更低的延迟和更大的容量。

  • 5G 在部署方面分为 NSA 和 SA 两种。
  • 5G NR 频段也分为:FR1(Sub-6GHz,<6GHz)和FR2(毫米波,24-100GHz)两类,但这属于频谱分组而不是组网分类。

NSA(Non-Standalone,非独立组网)

  • 定义:NSA 指的是5G网络与已有的4G LTE网络协同工作5G+4G混合组网。即5G基站依赖于4G核心网(EPC),通过4G进行信令控制,数据可走5G。
  • 特点
    • 信令面依赖4G:手机注册、认证等信号走4G,数据业务可以走5G。
    • 部署成本较低,演进快:适合运营商初期快速铺开5G网络。
    • 常见应用:早期大部分5G商用网络都是NSA。
  • 基础组成:EN-DC(E-UTRA-NR Dual Connectivity),要求终端和基站同时支持4G和5G频段。

SA(Standalone,独立组网)

  • 定义:SA是指完全基于5G核心网(5GC)和5G基站(gNodeB)构建的独立5G网络,既能控制信令、也能承载数据。
  • 特点
    • 完全5G架构:信令和数据全部走5G网络。
    • 支持新业务和能力:如网络切片、超低延迟、mMTC、URLLC等5G特性。
    • 技术创新、业务创新的基础:支撑自动驾驶、工业物联网等。
    • 演进要求更高:对设备和核心网能力要求增加,投资较大。
  • 成熟案例:目前很多地区开始建设SA网络,逐步替代NSA。
总结对比 组网方式 控制面 用户面 依赖性 典型特征 NSA 4G 5G 需4G网络支撑 初期过渡 SA 5G 5G 不依赖4G网络 目标架构、能力强

 

思路分析

判断5G NR网络状态可以从以下几个关键层面分析:

1. 设备状态检查(用户)

  • 查看设备连接标识:在手机状态栏通常会显示\"5G\"、\"5G+\"或\"5G UW\"(Ultra Wideband)等标识
  • 检查设备设置:在手机设置 > 关于手机 > 状态信息中查看网络类型
  • 确认设备兼容性:确保设备支持5G NR的频段(如n1, n3, n5, n7, n8, n20, n28, n38, n41, n77, n78, n79等)

2. 网络信号强度测量(工程师)

  • 信号强度指标
    • RSRP(参考信号接收功率):理想值 > -80dBm
    • RSRQ(参考信号接收质量):理想值 > -10dB
    • SINR(信号与干扰加噪声比):理想值 > 20dB
  • 测量工具
    • 使用工程模式(不同手机进入方式不同,如*##4636##*等)
    • 专业测试APP如Network Signal Guru、CellMapper等
    • 运营商提供的网络测试工具

3. 网络性能测试

  • 速度测试

    • 使用Speedtest、Fast.com等工具测量下载/上传速度
    • 5G NR理论峰值速率可达1-10Gbps(实际使用通常在100Mbps-1Gbps)
  • 延迟测试

    • 使用ping命令测试网络延迟
    • 5G NR理想延迟应<30ms

问题

如何判断 network 5G NR 信息状态?

规范的NSA/SA判别方式

  • 厂商或芯片原生API
    • 需要用诸如 Qualcomm QMI log、三星、小米的定制API。
  • Android 13+ 部分API 支持(如TelephonyManager#getNrAdvancedCapable)
  • 常规方案
    • 看小区结构(LTE+NR锚点是NSA,只NR是SA)
    • 查看RAT (RadioAccessTechnology)类型

代码解读

结合 Android 相关功能的 NetworkRegistrationInfo 类说明

/* * Copyright 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the \"License\"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an \"AS IS\" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */package android.telephony;import android.annotation.FlaggedApi;import android.annotation.IntDef;import android.annotation.NonNull;import android.annotation.Nullable;import android.annotation.SystemApi;import android.app.compat.CompatChanges;import android.compat.annotation.ChangeId;import android.compat.annotation.EnabledSince;import android.os.Build;import android.os.Parcel;import android.os.Parcelable;import android.telephony.AccessNetworkConstants.TransportType;import android.telephony.Annotation.NetworkType;import android.text.TextUtils;import com.android.internal.telephony.flags.Flags;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.util.ArrayList;import java.util.Collections;import java.util.List;import java.util.Objects;import java.util.stream.Collectors;/** * Description of a mobile network registration info */public final class NetworkRegistrationInfo implements Parcelable { /** * A new registration state, REGISTRATION_STATE_EMERGENCY, is added to * {@link NetworkRegistrationInfo}. This change will affect the result of getRegistration(). * @hide */ @ChangeId @EnabledSince(targetSdkVersion = android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE) public static final long RETURN_REGISTRATION_STATE_EMERGENCY = 255938466L; /** * Network domain * @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = \"DOMAIN_\", value = {DOMAIN_UNKNOWN, DOMAIN_CS, DOMAIN_PS, DOMAIN_CS_PS}) public @interface Domain {} /** Unknown / Unspecified domain */ public static final int DOMAIN_UNKNOWN = 0; /** Circuit switched domain */ public static final int DOMAIN_CS = android.hardware.radio.network.Domain.CS; /** Packet switched domain */ public static final int DOMAIN_PS = android.hardware.radio.network.Domain.PS; /** Applicable to both CS and PS Domain */ public static final int DOMAIN_CS_PS = DOMAIN_CS | DOMAIN_PS; /** * Network registration state * @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = \"REGISTRATION_STATE_\", value = {REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING, REGISTRATION_STATE_HOME,  REGISTRATION_STATE_NOT_REGISTERED_SEARCHING, REGISTRATION_STATE_DENIED,  REGISTRATION_STATE_UNKNOWN, REGISTRATION_STATE_ROAMING,  REGISTRATION_STATE_EMERGENCY}) public @interface RegistrationState {} /** * Not registered. The device is not currently searching a new operator to register. * @hide */ @SystemApi public static final int REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING = 0; /** * Registered on home network. * @hide */ @SystemApi public static final int REGISTRATION_STATE_HOME = 1; /** * Not registered. The device is currently searching a new operator to register. * @hide */ @SystemApi public static final int REGISTRATION_STATE_NOT_REGISTERED_SEARCHING = 2; /** * Registration denied. * @hide */ @SystemApi public static final int REGISTRATION_STATE_DENIED = 3; /** * Registration state is unknown. * @hide */ @SystemApi public static final int REGISTRATION_STATE_UNKNOWN = 4; /** * Registered on roaming network. * @hide */ @SystemApi public static final int REGISTRATION_STATE_ROAMING = 5; /** * Emergency attached in EPS or in 5GS. * IMS service will skip emergency registration if the device is in * emergency attached state. {@link #mEmergencyOnly} can be true * even in case it\'s not in emergency attached state. * * Reference: 3GPP TS 24.301 9.9.3.11 EPS attach type. * Reference: 3GPP TS 24.501 9.11.3.6 5GS registration result. * @hide */ @SystemApi public static final int REGISTRATION_STATE_EMERGENCY = 6; /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = \"NR_STATE_\", value = {NR_STATE_NONE, NR_STATE_RESTRICTED, NR_STATE_NOT_RESTRICTED,  NR_STATE_CONNECTED}) public @interface NRState {} /** * The device isn\'t camped on an LTE cell or the LTE cell doesn\'t support E-UTRA-NR * Dual Connectivity(EN-DC). */ public static final int NR_STATE_NONE = 0; /** * The device is camped on an LTE cell that supports E-UTRA-NR Dual Connectivity(EN-DC) but * either the use of dual connectivity with NR(DCNR) is restricted or NR is not supported by * the selected PLMN. */ public static final int NR_STATE_RESTRICTED = 1; /** * The device is camped on an LTE cell that supports E-UTRA-NR Dual Connectivity(EN-DC) and both * the use of dual connectivity with NR(DCNR) is not restricted and NR is supported by the * selected PLMN. */ public static final int NR_STATE_NOT_RESTRICTED = 2; /** * The device is camped on an LTE cell that supports E-UTRA-NR Dual Connectivity(EN-DC) and * also connected to at least one 5G cell as a secondary serving cell. */ public static final int NR_STATE_CONNECTED = 3; /** * Supported service type * @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = \"SERVICE_TYPE_\", value = {SERVICE_TYPE_UNKNOWN, SERVICE_TYPE_VOICE, SERVICE_TYPE_DATA, SERVICE_TYPE_SMS,  SERVICE_TYPE_VIDEO, SERVICE_TYPE_EMERGENCY, SERVICE_TYPE_MMS}) public @interface ServiceType {} /** * Unknown service */ public static final int SERVICE_TYPE_UNKNOWN = 0; /** * Voice service */ public static final int SERVICE_TYPE_VOICE = 1; /** * Data service */ public static final int SERVICE_TYPE_DATA = 2; /** * SMS service */ public static final int SERVICE_TYPE_SMS = 3; /** * Video service */ public static final int SERVICE_TYPE_VIDEO = 4; /** * Emergency service */ public static final int SERVICE_TYPE_EMERGENCY = 5; /** * MMS service */ public static final int SERVICE_TYPE_MMS = 6; /** @hide */ public static final int FIRST_SERVICE_TYPE = SERVICE_TYPE_VOICE; /** @hide */ public static final int LAST_SERVICE_TYPE = SERVICE_TYPE_MMS; @Domain private final int mDomain; @TransportType private final int mTransportType; /** * The true registration state of network, This is not affected by any carrier config or * resource overlay. */ @RegistrationState private final int mNetworkRegistrationState; /** * The registration state that might have been overridden by config */ @RegistrationState private int mRegistrationState; /** * Save the {@link ServiceState.RoamingType roaming type}. it can be overridden roaming type * from resource overlay or carrier config. */ @ServiceState.RoamingType private int mRoamingType; @NetworkType private int mAccessNetworkTechnology; @NRState private int mNrState; private final int mRejectCause; private final boolean mEmergencyOnly; @ServiceType private ArrayList mAvailableServices; @Nullable private CellIdentity mCellIdentity; @Nullable private VoiceSpecificRegistrationInfo mVoiceSpecificInfo; @Nullable private DataSpecificRegistrationInfo mDataSpecificInfo; @NonNull private String mRplmn; // Updated based on the accessNetworkTechnology private boolean mIsUsingCarrierAggregation; // Set to {@code true} when network is a non-terrestrial network. private boolean mIsNonTerrestrialNetwork; /** * @param domain Network domain. Must be a {@link Domain}. For transport type * {@link AccessNetworkConstants#TRANSPORT_TYPE_WLAN}, this must set to {@link #DOMAIN_PS}. * @param transportType Transport type. * @param registrationState Network registration state. For transport type * {@link AccessNetworkConstants#TRANSPORT_TYPE_WLAN}, only * {@link #REGISTRATION_STATE_HOME} and {@link #REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING} * are valid states. * @param accessNetworkTechnology Access network technology.For transport type * {@link AccessNetworkConstants#TRANSPORT_TYPE_WLAN}, set to * {@link TelephonyManager#NETWORK_TYPE_IWLAN}. * @param rejectCause Reason for denial if the registration state is * {@link #REGISTRATION_STATE_DENIED}. Depending on {@code accessNetworkTechnology}, the values * are defined in 3GPP TS 24.008 10.5.3.6 for UMTS, 3GPP TS 24.301 9.9.3.9 for LTE, and 3GPP2 * A.S0001 6.2.2.44 for CDMA. If the reject cause is not supported or unknown, set it to 0. * // TODO: Add IWLAN reject cause reference * @param emergencyOnly True if this registration is for emergency only. * @param availableServices The list of the supported services. * @param cellIdentity The identity representing a unique cell or wifi AP. Set to null if the * information is not available. * @param rplmn the registered plmn or the last plmn for attempted registration if reg failed. * @param voiceSpecificInfo Voice specific registration information. * @param dataSpecificInfo Data specific registration information. * @param isNonTerrestrialNetwork {@code true} if network is a non-terrestrial network. */ private NetworkRegistrationInfo(@Domain int domain, @TransportType int transportType, @RegistrationState int registrationState, @NetworkType int accessNetworkTechnology, int rejectCause, boolean emergencyOnly, @Nullable @ServiceType List availableServices, @Nullable CellIdentity cellIdentity, @Nullable String rplmn, @Nullable VoiceSpecificRegistrationInfo voiceSpecificInfo, @Nullable DataSpecificRegistrationInfo dataSpecificInfo, boolean isNonTerrestrialNetwork) { mDomain = domain; mTransportType = transportType; mRegistrationState = registrationState; mNetworkRegistrationState = registrationState; mRoamingType = (registrationState == REGISTRATION_STATE_ROAMING) ? ServiceState.ROAMING_TYPE_UNKNOWN : ServiceState.ROAMING_TYPE_NOT_ROAMING; setAccessNetworkTechnology(accessNetworkTechnology); mRejectCause = rejectCause; mAvailableServices = (availableServices != null) ? new ArrayList(availableServices) : new ArrayList(); mCellIdentity = cellIdentity; mEmergencyOnly = emergencyOnly; mNrState = NR_STATE_NONE; mRplmn = rplmn; mVoiceSpecificInfo = voiceSpecificInfo; mDataSpecificInfo = dataSpecificInfo; mIsNonTerrestrialNetwork = isNonTerrestrialNetwork; updateNrState(); } /** * Constructor for voice network registration info. * @hide */ public NetworkRegistrationInfo(int domain, @TransportType int transportType, int registrationState, int accessNetworkTechnology, int rejectCause, boolean emergencyOnly, @Nullable List availableServices, @Nullable CellIdentity cellIdentity, @Nullable String rplmn, boolean cssSupported, int roamingIndicator, int systemIsInPrl, int defaultRoamingIndicator) { this(domain, transportType, registrationState, accessNetworkTechnology, rejectCause, emergencyOnly, availableServices, cellIdentity, rplmn, new VoiceSpecificRegistrationInfo(cssSupported, roamingIndicator, systemIsInPrl, defaultRoamingIndicator), null, false); } /** * Constructor for data network registration info. * @hide */ public NetworkRegistrationInfo(int domain, @TransportType int transportType, int registrationState, int accessNetworkTechnology, int rejectCause, boolean emergencyOnly, @Nullable List availableServices, @Nullable CellIdentity cellIdentity, @Nullable String rplmn, int maxDataCalls, boolean isDcNrRestricted, boolean isNrAvailable, boolean isEndcAvailable, @Nullable VopsSupportInfo vopsSupportInfo) { this(domain, transportType, registrationState, accessNetworkTechnology, rejectCause, emergencyOnly, availableServices, cellIdentity, rplmn, null, new DataSpecificRegistrationInfo.Builder(maxDataCalls) .setDcNrRestricted(isDcNrRestricted) .setNrAvailable(isNrAvailable) .setEnDcAvailable(isEndcAvailable) .setVopsSupportInfo(vopsSupportInfo) .build(), false); } private NetworkRegistrationInfo(Parcel source) { mDomain = source.readInt(); mTransportType = source.readInt(); mRegistrationState = source.readInt(); mNetworkRegistrationState = source.readInt(); mRoamingType = source.readInt(); mAccessNetworkTechnology = source.readInt(); mRejectCause = source.readInt(); mEmergencyOnly = source.readBoolean(); mAvailableServices = new ArrayList(); source.readList(mAvailableServices, Integer.class.getClassLoader(), java.lang.Integer.class); mCellIdentity = source.readParcelable(CellIdentity.class.getClassLoader(), android.telephony.CellIdentity.class); mVoiceSpecificInfo = source.readParcelable( VoiceSpecificRegistrationInfo.class.getClassLoader(), android.telephony.VoiceSpecificRegistrationInfo.class); mDataSpecificInfo = source.readParcelable( DataSpecificRegistrationInfo.class.getClassLoader(), android.telephony.DataSpecificRegistrationInfo.class); mNrState = source.readInt(); mRplmn = source.readString(); mIsUsingCarrierAggregation = source.readBoolean(); mIsNonTerrestrialNetwork = source.readBoolean(); } /** * Constructor from another network registration info * * @param nri Another network registration info * @hide */ public NetworkRegistrationInfo(NetworkRegistrationInfo nri) { mDomain = nri.mDomain; mTransportType = nri.mTransportType; mRegistrationState = nri.mRegistrationState; mNetworkRegistrationState = nri.mNetworkRegistrationState; mRoamingType = nri.mRoamingType; mAccessNetworkTechnology = nri.mAccessNetworkTechnology; mIsUsingCarrierAggregation = nri.mIsUsingCarrierAggregation; mIsNonTerrestrialNetwork = nri.mIsNonTerrestrialNetwork; mRejectCause = nri.mRejectCause; mEmergencyOnly = nri.mEmergencyOnly; mAvailableServices = new ArrayList(nri.mAvailableServices); if (nri.mCellIdentity != null) { Parcel p = Parcel.obtain(); nri.mCellIdentity.writeToParcel(p, 0); p.setDataPosition(0); // TODO: Instead of doing this, we should create a formal way for cloning cell identity. // Cell identity is not an immutable object so we have to deep copy it. mCellIdentity = CellIdentity.CREATOR.createFromParcel(p); p.recycle(); } if (nri.mVoiceSpecificInfo != null) { mVoiceSpecificInfo = new VoiceSpecificRegistrationInfo(nri.mVoiceSpecificInfo); } if (nri.mDataSpecificInfo != null) { mDataSpecificInfo = new DataSpecificRegistrationInfo(nri.mDataSpecificInfo); } mNrState = nri.mNrState; mRplmn = nri.mRplmn; } // 省略 getXX 等功能接口,仅保留对象信息和构造方法}

 

  • NR_STATE_NONE:没有5G NR连接
  • NR_STATE_RESTRICTED:受限接入,终端当前没有接入5G,但设备支持5G NR
  • NR_STATE_NOT_RESTRICTED:未受限,终端位于5G覆盖区,并有能力接入5G,但可能当前数据业务还在4G
  • NR_STATE_CONNECTED:已经连接上5G NR

常见误区&疑问

起初,看到 NrState 的定义,以为是用于区分 SA 和 NSA 的,实则不然。

可以理解NRState是为了判断NSA的,如果已经注册到 5G 小区,其实就是 Rat = NR了,即 SA,不需要额外的判断。

rat == LTE && NrState == (NR_STATE_NOT_RESTRICTED || NR_STATE_CONNECTED)

容易误认为 NR_STATE_RESTRICTED 属于 NSA,实际上受限不可用就等于不在 NSA了,应该是仅 LTE。而 NR_STATE_CONNECTED 容易错误理解为 SA。

 // 前提是Rat = LTE private String nrStateCovertor(int nrState) { switch (nrState) { case NetworkRegistrationInfo.NR_STATE_RESTRICTED:  return \"RESTRICTED\"; case NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED: case NetworkRegistrationInfo.NR_STATE_CONNECTED:  return \"NSA\"; case NetworkRegistrationInfo.NR_STATE_NONE: default:  return Integer.toString(GET_FEATURE_FAIL_INT); } } 

参考方案

if (rat == TelephonyManager.NETWORK_TYPE_LTE && (nrState == NR_STATE_NOT_RESTRICTED || nrState == NR_STATE_CONNECTED)) { // 大概率属于NSA。可以把90%以上的情况准确作为NSA识别。 return \"NSA\";} else if (rat == TelephonyManager.NETWORK_TYPE_NR &&  nrState == NR_STATE_CONNECTED) { // 属于SA return \"SA\";}
  • rat == LTE + 有NR能力 → NSA
  • rat == NR(5G NR)+ 已连接 → SA

(Android TelephonyManager.NETWORK_TYPE_NR 在API 29以后出现)