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 类说明

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 NR
  • NR_STATE_NOT_RESTRICTED:未受限,终端位于5G覆盖区,并有能力接入5G,但可能当前数据业务还在4G
  • NR_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能力 → NSA
  • rat == NR(5G NR)+ 已连接 → SA

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

相关推荐
Python涛哥3 分钟前
go语言基础教程:【2】基础语法:基本数据类型(整形和浮点型)
android·开发语言·golang
你过来啊你22 分钟前
进程线程协程深度对比分析
android·线程·进程·协程
xzkyd outpaper1 小时前
Android中ViewStub和View有什么区别?
android·开发语言·面试·代码
吗喽对你问好12 小时前
Android UI 控件详解实践
android·ui
东风西巷16 小时前
X-plore File Manager v4.34.02 修改版:安卓设备上的全能文件管理器
android·网络·软件需求
yzpyzp16 小时前
Android 15中的16KB大页有何优势?
android
安卓开发者16 小时前
Android Room 持久化库:简化数据库操作
android·数据库
程序视点16 小时前
FadCam安卓后台录制神器:2025最全使用指南(开源/免费/息屏录制)
android
猿小蔡17 小时前
Android ADB命令之内存统计与分析
android