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以后出现)

相关推荐
*才华有限公司*1 分钟前
安卓前后端连接教程
android
氦客28 分钟前
Android Compose中的附带效应
android·compose·effect·jetpack·composable·附带效应·side effect
雨白1 小时前
Kotlin 协程的灵魂:结构化并发详解
android·kotlin
我命由我123451 小时前
Android 开发问题:getLeft、getRight、getTop、getBottom 方法返回的值都为 0
android·java·java-ee·android studio·android jetpack·android-studio·android runtime
Modu_MrLiu1 小时前
Android实战进阶 - 用户闲置超时自动退出登录功能详解
android·超时保护·实战进阶·长时间未操作超时保护·闲置超时
Jeled1 小时前
Android 网络层最佳实践:Retrofit + OkHttp 封装与实战
android·okhttp·kotlin·android studio·retrofit
信田君95271 小时前
瑞莎星瑞(Radxa Orion O6) 基于 Android OS 使用 NPU的图片模糊查找APP 开发
android·人工智能·深度学习·神经网络
tangweiguo030519872 小时前
Kotlin 实现 Android 网络状态检测工具类
android·网络·kotlin
nvvas3 小时前
Android Studio JAVA开发按钮跳转功能
android·java·android studio
怪兽20143 小时前
Android多进程通信机制
android·面试