基本概念
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 类说明
java
/*
* 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<Integer> 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<Integer> 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<Integer> 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<Integer> 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 NRNR_STATE_NOT_RESTRICTED
:未受限,终端位于5G覆盖区,并有能力接入5G,但可能当前数据业务还在4GNR_STATE_CONNECTED
:已经连接上5G NR
常见误区&疑问
起初,看到 NrState 的定义,以为是用于区分 SA 和 NSA 的,实则不然。
可以理解NRState是为了判断NSA的,如果已经注册到 5G 小区,其实就是 Rat = NR了,即 SA,不需要额外的判断。
java
rat == LTE && NrState == (NR_STATE_NOT_RESTRICTED || NR_STATE_CONNECTED)
容易误认为 NR_STATE_RESTRICTED 属于 NSA,实际上受限不可用就等于不在 NSA了,应该是仅 LTE。而 NR_STATE_CONNECTED 容易错误理解为 SA。
java
// 前提是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);
}
}
参考方案
java
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能力
→ NSArat == NR
(5G NR)+已连接
→ SA
(Android TelephonyManager.NETWORK_TYPE_NR 在API 29以后出现)