| /* |
| * Copyright 2021 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 com.android.internal.telephony.data; |
| |
| import android.annotation.CallbackExecutor; |
| import android.annotation.IntDef; |
| import android.annotation.NonNull; |
| import android.annotation.Nullable; |
| import android.content.Intent; |
| import android.net.ConnectivityManager; |
| import android.net.LinkAddress; |
| import android.net.LinkProperties; |
| import android.net.NetworkAgent; |
| import android.net.NetworkAgentConfig; |
| import android.net.NetworkCapabilities; |
| import android.net.NetworkFactory; |
| import android.net.NetworkProvider; |
| import android.net.NetworkScore; |
| import android.net.ProxyInfo; |
| import android.net.RouteInfo; |
| import android.net.TelephonyNetworkSpecifier; |
| import android.net.Uri; |
| import android.net.vcn.VcnManager; |
| import android.net.vcn.VcnManager.VcnNetworkPolicyChangeListener; |
| import android.net.vcn.VcnNetworkPolicyResult; |
| import android.os.AsyncResult; |
| import android.os.Looper; |
| import android.os.Message; |
| import android.os.Process; |
| import android.os.SystemClock; |
| import android.telephony.AccessNetworkConstants; |
| import android.telephony.AccessNetworkConstants.AccessNetworkType; |
| import android.telephony.AccessNetworkConstants.TransportType; |
| import android.telephony.Annotation.DataFailureCause; |
| import android.telephony.Annotation.DataState; |
| import android.telephony.Annotation.NetCapability; |
| import android.telephony.Annotation.NetworkType; |
| import android.telephony.Annotation.ValidationStatus; |
| import android.telephony.DataFailCause; |
| import android.telephony.DataSpecificRegistrationInfo; |
| import android.telephony.LinkCapacityEstimate; |
| import android.telephony.NetworkRegistrationInfo; |
| import android.telephony.PcoData; |
| import android.telephony.PreciseDataConnectionState; |
| import android.telephony.ServiceState; |
| import android.telephony.SubscriptionPlan; |
| import android.telephony.TelephonyManager; |
| import android.telephony.data.ApnSetting; |
| import android.telephony.data.DataCallResponse; |
| import android.telephony.data.DataCallResponse.HandoverFailureMode; |
| import android.telephony.data.DataCallResponse.LinkStatus; |
| import android.telephony.data.DataProfile; |
| import android.telephony.data.DataService; |
| import android.telephony.data.DataServiceCallback; |
| import android.telephony.data.NetworkSliceInfo; |
| import android.telephony.data.QosBearerSession; |
| import android.telephony.data.TrafficDescriptor; |
| import android.telephony.data.TrafficDescriptor.OsAppId; |
| import android.text.TextUtils; |
| import android.util.ArrayMap; |
| import android.util.IndentingPrintWriter; |
| import android.util.LocalLog; |
| import android.util.Pair; |
| import android.util.SparseArray; |
| import android.util.SparseIntArray; |
| |
| import com.android.internal.telephony.CommandsInterface; |
| import com.android.internal.telephony.Phone; |
| import com.android.internal.telephony.PhoneConstants; |
| import com.android.internal.telephony.PhoneFactory; |
| import com.android.internal.telephony.data.DataEvaluation.DataAllowedReason; |
| import com.android.internal.telephony.data.DataNetworkController.NetworkRequestList; |
| import com.android.internal.telephony.data.DataRetryManager.DataHandoverRetryEntry; |
| import com.android.internal.telephony.data.DataRetryManager.DataRetryEntry; |
| import com.android.internal.telephony.data.TelephonyNetworkAgent.TelephonyNetworkAgentCallback; |
| import com.android.internal.telephony.metrics.DataCallSessionStats; |
| import com.android.internal.telephony.metrics.TelephonyMetrics; |
| import com.android.internal.util.ArrayUtils; |
| import com.android.internal.util.IState; |
| import com.android.internal.util.State; |
| import com.android.internal.util.StateMachine; |
| import com.android.net.module.util.NetworkCapabilitiesUtils; |
| import com.android.telephony.Rlog; |
| |
| import java.io.FileDescriptor; |
| import java.io.PrintWriter; |
| import java.math.BigInteger; |
| import java.net.InetAddress; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collections; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Objects; |
| import java.util.Set; |
| import java.util.concurrent.Executor; |
| import java.util.function.Consumer; |
| import java.util.stream.Collectors; |
| |
| /** |
| * DataNetwork class represents a single PDN (Packet Data Network). |
| * |
| * The life cycle of a data network starts from {@link ConnectingState}. If setup data request |
| * succeeds, then it enters {@link ConnectedState}, otherwise it enters |
| * {@link DisconnectedState}. |
| * |
| * When data network is in {@link ConnectingState}, it can enter {@link HandoverState} if handover |
| * between IWLAN and cellular occurs. After handover completes or fails, it return back to |
| * {@link ConnectedState}. When the data network is about to be disconnected, it first enters |
| * {@link DisconnectingState} when performing graceful tear down or when sending the data |
| * deactivation request. At the end, it enters {@link DisconnectedState} when {@link DataService} |
| * notifies data disconnected. Note that an unsolicited disconnected event from {@link DataService} |
| * or a RADIO_NOT_AVAILABLE response can immediately move data network from {@link ConnectedState} |
| * to {@link DisconnectedState}. {@link DisconnectedState} is the final state of a data network. |
| * |
| * State machine diagram: |
| * |
| * |
| * ┌─────────┐ |
| * │Handover │ |
| * └─▲────┬──┘ |
| * │ │ |
| * ┌───────────┐ ┌─┴────▼──┐ ┌──────────────┐ |
| * │Connecting ├────────►Connected├────────►Disconnecting │ |
| * └─────┬─────┘ └────┬────┘ └───────┬──────┘ |
| * │ │ │ |
| * │ ┌─────▼──────┐ │ |
| * └─────────────►Disconnected◄──────────────┘ |
| * └────────────┘ |
| * |
| */ |
| public class DataNetwork extends StateMachine { |
| private static final boolean VDBG = false; |
| /** Event for data config updated. */ |
| private static final int EVENT_DATA_CONFIG_UPDATED = 1; |
| |
| /** Event for attaching a network request. */ |
| private static final int EVENT_ATTACH_NETWORK_REQUEST = 2; |
| |
| /** Event for detaching a network request. */ |
| private static final int EVENT_DETACH_NETWORK_REQUEST = 3; |
| |
| /** Event for allocating PDU session id response. */ |
| private static final int EVENT_ALLOCATE_PDU_SESSION_ID_RESPONSE = 5; |
| |
| /** Event for setup data network response. */ |
| private static final int EVENT_SETUP_DATA_NETWORK_RESPONSE = 6; |
| |
| /** Event for tearing down data network. */ |
| private static final int EVENT_TEAR_DOWN_NETWORK = 7; |
| |
| /** Event triggered by {@link DataServiceCallback#onDataCallListChanged(List)}. */ |
| private static final int EVENT_DATA_STATE_CHANGED = 8; |
| |
| /** Data network service state changed event. */ |
| private static final int EVENT_SERVICE_STATE_CHANGED = 9; |
| |
| /** Event for detaching all network requests. */ |
| private static final int EVENT_DETACH_ALL_NETWORK_REQUESTS = 10; |
| |
| /** Event for bandwidth estimation from the modem changed. */ |
| private static final int EVENT_BANDWIDTH_ESTIMATE_FROM_MODEM_CHANGED = 11; |
| |
| /** Event for bandwidth estimation from the bandwidth estimator changed. */ |
| private static final int EVENT_BANDWIDTH_ESTIMATE_FROM_BANDWIDTH_ESTIMATOR_CHANGED = 12; |
| |
| /** Event for display info changed. This is for getting 5G NSA or mmwave information. */ |
| private static final int EVENT_DISPLAY_INFO_CHANGED = 13; |
| |
| /** Event for initiating an handover between cellular and IWLAN. */ |
| private static final int EVENT_START_HANDOVER = 14; |
| |
| /** Event for setup data call (for handover) response from the data service. */ |
| private static final int EVENT_HANDOVER_RESPONSE = 15; |
| |
| /** Event for subscription plan changed or unmetered/congested override set. */ |
| private static final int EVENT_SUBSCRIPTION_PLAN_OVERRIDE = 16; |
| |
| /** Event for PCO data received from network. */ |
| private static final int EVENT_PCO_DATA_RECEIVED = 17; |
| |
| /** Event for carrier privileged UIDs changed. */ |
| private static final int EVENT_CARRIER_PRIVILEGED_UIDS_CHANGED = 18; |
| |
| /** Event for deactivate data network response. */ |
| private static final int EVENT_DEACTIVATE_DATA_NETWORK_RESPONSE = 19; |
| |
| /** The default MTU for IPv4 network. */ |
| private static final int DEFAULT_MTU_V4 = 1280; |
| |
| /** The default MTU for IPv6 network. */ |
| private static final int DEFAULT_MTU_V6 = 1280; |
| |
| /** Invalid context id. */ |
| private static final int INVALID_CID = -1; |
| |
| /** |
| * The data network providing default internet will have a higher score of 50. Other network |
| * will have a slightly lower score of 45. The intention is other connections will not cause |
| * connectivity service to tear down default internet connection. For example, to validate |
| * internet connection on non-default data SIM, we'll set up a temporary internet network on |
| * that data SIM. In this case, score of 45 is assigned so connectivity service will not replace |
| * the default internet network with it. |
| */ |
| private static final int DEFAULT_INTERNET_NETWORK_SCORE = 50; |
| private static final int OTHER_NETWORK_SCORE = 45; |
| |
| @IntDef(prefix = {"DEACTIVATION_REASON_"}, |
| value = { |
| TEAR_DOWN_REASON_CONNECTIVITY_SERVICE_UNWANTED, |
| TEAR_DOWN_REASON_SIM_REMOVAL, |
| TEAR_DOWN_REASON_AIRPLANE_MODE_ON, |
| TEAR_DOWN_REASON_DATA_DISABLED, |
| TEAR_DOWN_REASON_NO_LIVE_REQUEST, |
| TEAR_DOWN_REASON_RAT_NOT_ALLOWED, |
| TEAR_DOWN_REASON_ROAMING_DISABLED, |
| TEAR_DOWN_REASON_CONCURRENT_VOICE_DATA_NOT_ALLOWED, |
| TEAR_DOWN_REASON_DATA_RESTRICTED_BY_NETWORK, |
| TEAR_DOWN_REASON_DATA_SERVICE_NOT_READY, |
| TEAR_DOWN_REASON_POWER_OFF_BY_CARRIER, |
| TEAR_DOWN_REASON_DATA_STALL, |
| TEAR_DOWN_REASON_HANDOVER_FAILED, |
| TEAR_DOWN_REASON_HANDOVER_NOT_ALLOWED, |
| TEAR_DOWN_REASON_VCN_REQUESTED, |
| TEAR_DOWN_REASON_VOPS_NOT_SUPPORTED, |
| TEAR_DOWN_REASON_DEFAULT_DATA_UNSELECTED, |
| TEAR_DOWN_REASON_NOT_IN_SERVICE, |
| TEAR_DOWN_REASON_DATA_CONFIG_NOT_READY, |
| TEAR_DOWN_REASON_PENDING_TEAR_DOWN_ALL, |
| TEAR_DOWN_REASON_NO_SUITABLE_DATA_PROFILE, |
| TEAR_DOWN_REASON_EMERGENCY_CALL, |
| TEAR_DOWN_REASON_RETRY_SCHEDULED, |
| TEAR_DOWN_REASON_DATA_THROTTLED, |
| TEAR_DOWN_REASON_DATA_PROFILE_INVALID, |
| TEAR_DOWN_REASON_DATA_PROFILE_NOT_PREFERRED, |
| TEAR_DOWN_REASON_NOT_ALLOWED_BY_POLICY, |
| TEAR_DOWN_REASON_ILLEGAL_STATE, |
| TEAR_DOWN_ONLY_ALLOWED_SINGLE_NETWORK, |
| }) |
| public @interface TearDownReason {} |
| |
| /** Data network tear down requested by connectivity service. */ |
| public static final int TEAR_DOWN_REASON_CONNECTIVITY_SERVICE_UNWANTED = 1; |
| |
| /** Data network tear down due to SIM removal. */ |
| public static final int TEAR_DOWN_REASON_SIM_REMOVAL = 2; |
| |
| /** Data network tear down due to airplane mode turned on. */ |
| public static final int TEAR_DOWN_REASON_AIRPLANE_MODE_ON = 3; |
| |
| /** Data network tear down due to data disabled (by user, policy, carrier, etc...). */ |
| public static final int TEAR_DOWN_REASON_DATA_DISABLED = 4; |
| |
| /** Data network tear down due to no live network request. */ |
| public static final int TEAR_DOWN_REASON_NO_LIVE_REQUEST = 5; |
| |
| /** Data network tear down due to current RAT is not allowed by the data profile. */ |
| public static final int TEAR_DOWN_REASON_RAT_NOT_ALLOWED = 6; |
| |
| /** Data network tear down due to data roaming not enabled. */ |
| public static final int TEAR_DOWN_REASON_ROAMING_DISABLED = 7; |
| |
| /** Data network tear down due to concurrent voice/data not allowed. */ |
| public static final int TEAR_DOWN_REASON_CONCURRENT_VOICE_DATA_NOT_ALLOWED = 8; |
| |
| /** Data network tear down due to network restricted. */ |
| public static final int TEAR_DOWN_REASON_DATA_RESTRICTED_BY_NETWORK = 9; |
| |
| /** Data network tear down due to data service unbound. */ |
| public static final int TEAR_DOWN_REASON_DATA_SERVICE_NOT_READY = 10; |
| |
| /** Data network tear down due to radio turned off by the carrier. */ |
| public static final int TEAR_DOWN_REASON_POWER_OFF_BY_CARRIER = 11; |
| |
| /** Data network tear down due to data stall. */ |
| public static final int TEAR_DOWN_REASON_DATA_STALL = 12; |
| |
| /** Data network tear down due to handover failed. */ |
| public static final int TEAR_DOWN_REASON_HANDOVER_FAILED = 13; |
| |
| /** Data network tear down due to handover not allowed. */ |
| public static final int TEAR_DOWN_REASON_HANDOVER_NOT_ALLOWED = 14; |
| |
| /** Data network tear down due to VCN service requested. */ |
| public static final int TEAR_DOWN_REASON_VCN_REQUESTED = 15; |
| |
| /** Data network tear down due to VOPS no longer supported. */ |
| public static final int TEAR_DOWN_REASON_VOPS_NOT_SUPPORTED = 16; |
| |
| /** Data network tear down due to default data unselected. */ |
| public static final int TEAR_DOWN_REASON_DEFAULT_DATA_UNSELECTED = 17; |
| |
| /** Data network tear down due to device not in service. */ |
| public static final int TEAR_DOWN_REASON_NOT_IN_SERVICE = 18; |
| |
| /** Data network tear down due to data config not ready. */ |
| public static final int TEAR_DOWN_REASON_DATA_CONFIG_NOT_READY = 19; |
| |
| /** Data network tear down due to tear down all pending. */ |
| public static final int TEAR_DOWN_REASON_PENDING_TEAR_DOWN_ALL = 20; |
| |
| /** Data network tear down due to no suitable data profile. */ |
| public static final int TEAR_DOWN_REASON_NO_SUITABLE_DATA_PROFILE = 21; |
| |
| /** Data network tear down due to emergency call. */ |
| public static final int TEAR_DOWN_REASON_EMERGENCY_CALL = 22; |
| |
| /** Data network tear down due to retry scheduled. */ |
| public static final int TEAR_DOWN_REASON_RETRY_SCHEDULED = 23; |
| |
| /** Data network tear down due to data throttled. */ |
| public static final int TEAR_DOWN_REASON_DATA_THROTTLED = 24; |
| |
| /** Data network tear down due to data profile invalid. */ |
| public static final int TEAR_DOWN_REASON_DATA_PROFILE_INVALID = 25; |
| |
| /** Data network tear down due to data profile not preferred. */ |
| public static final int TEAR_DOWN_REASON_DATA_PROFILE_NOT_PREFERRED = 26; |
| |
| /** Data network tear down due to not allowed by policy. */ |
| public static final int TEAR_DOWN_REASON_NOT_ALLOWED_BY_POLICY = 27; |
| |
| /** Data network tear down due to illegal state. */ |
| public static final int TEAR_DOWN_REASON_ILLEGAL_STATE = 28; |
| |
| /** Data network tear down due to only allowed single network. */ |
| public static final int TEAR_DOWN_ONLY_ALLOWED_SINGLE_NETWORK = 29; |
| |
| @IntDef(prefix = {"BANDWIDTH_SOURCE_"}, |
| value = { |
| BANDWIDTH_SOURCE_UNKNOWN, |
| BANDWIDTH_SOURCE_MODEM, |
| BANDWIDTH_SOURCE_CARRIER_CONFIG, |
| BANDWIDTH_SOURCE_BANDWIDTH_ESTIMATOR, |
| }) |
| public @interface BandwidthEstimationSource {} |
| |
| /** Indicates the bandwidth estimation source is unknown. This must be a configuration error. */ |
| public static final int BANDWIDTH_SOURCE_UNKNOWN = 0; |
| |
| /** Indicates the bandwidth estimation source is from the modem. */ |
| public static final int BANDWIDTH_SOURCE_MODEM = 1; |
| |
| /** Indicates the bandwidth estimation source is from the static carrier config. */ |
| public static final int BANDWIDTH_SOURCE_CARRIER_CONFIG = 2; |
| |
| /** Indicates the bandwidth estimation source is from {@link LinkBandwidthEstimator}. */ |
| public static final int BANDWIDTH_SOURCE_BANDWIDTH_ESTIMATOR = 3; |
| |
| /** |
| * The capabilities that are allowed to changed dynamically during the life cycle of network. |
| * This is copied from {@code NetworkCapabilities#MUTABLE_CAPABILITIES}. There is no plan to |
| * make this a connectivity manager API since in the future, immutable network capabilities |
| * would be allowed to changed dynamically. (i.e. not immutable anymore.) |
| */ |
| private static final List<Integer> MUTABLE_CAPABILITIES = List.of( |
| NetworkCapabilities.NET_CAPABILITY_TRUSTED, |
| NetworkCapabilities.NET_CAPABILITY_VALIDATED, |
| NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL, |
| NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, |
| NetworkCapabilities.NET_CAPABILITY_FOREGROUND, |
| NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED, |
| NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED, |
| NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY, |
| NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED, |
| NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED, |
| NetworkCapabilities.NET_CAPABILITY_HEAD_UNIT); |
| |
| /** The parent state. Any messages not handled by the child state fallback to this. */ |
| private final DefaultState mDefaultState = new DefaultState(); |
| |
| /** |
| * The connecting state. This is the initial state of a data network. |
| * |
| * @see DataNetwork for the state machine diagram. |
| */ |
| private final ConnectingState mConnectingState = new ConnectingState(); |
| |
| /** |
| * The connected state. This is the state when data network becomes usable. |
| * |
| * @see DataNetwork for the state machine diagram. |
| */ |
| private final ConnectedState mConnectedState = new ConnectedState(); |
| |
| /** |
| * The handover state. This is the state when data network handover between IWLAN and cellular. |
| * |
| * @see DataNetwork for the state machine diagram. |
| */ |
| private final HandoverState mHandoverState = new HandoverState(); |
| |
| /** |
| * The disconnecting state. This is the state when data network is about to be disconnected. |
| * The network is still usable in this state, but the clients should be prepared to lose the |
| * network in any moment. This state is particular useful for IMS graceful tear down, where |
| * the network enters disconnecting state while waiting for IMS de-registration signal. |
| * |
| * @see DataNetwork for the state machine diagram. |
| */ |
| private final DisconnectingState mDisconnectingState = new DisconnectingState(); |
| |
| /** |
| * The disconnected state. This is the final state of a data network. |
| * |
| * @see DataNetwork for the state machine diagram. |
| */ |
| private final DisconnectedState mDisconnectedState = new DisconnectedState(); |
| |
| /** The phone instance. */ |
| private final @NonNull Phone mPhone; |
| |
| /** |
| * The subscription id. This is assigned when the network is created, and not supposed to |
| * change afterwards. |
| */ |
| private final int mSubId; |
| |
| /** |
| * Indicates that |
| * {@link DataService.DataServiceProvider#deactivateDataCall(int, int, DataServiceCallback)} |
| * has been called. This flag can be only changed from {@code false} to {@code true}. |
| */ |
| private boolean mInvokedDataDeactivation = false; |
| |
| /** |
| * Indicates that if the data network has ever entered {@link ConnectedState}. |
| */ |
| private boolean mEverConnected = false; |
| |
| /** RIL interface. */ |
| private final @NonNull CommandsInterface mRil; |
| |
| /** Local log. */ |
| private final LocalLog mLocalLog = new LocalLog(128); |
| |
| /** The callback to receives data network state update. */ |
| private final @NonNull DataNetworkCallback mDataNetworkCallback; |
| |
| /** The log tag. */ |
| private String mLogTag; |
| |
| /** Metrics of per data network connection. */ |
| private final DataCallSessionStats mDataCallSessionStats; |
| |
| |
| /** |
| * The unique context id assigned by the data service in {@link DataCallResponse#getId()}. One |
| * for {@link AccessNetworkConstants#TRANSPORT_TYPE_WWAN} and one for |
| * {@link AccessNetworkConstants#TRANSPORT_TYPE_WLAN}. The reason for storing both is that |
| * during handover, both cid will be used. |
| */ |
| private final SparseIntArray mCid = new SparseIntArray(2); |
| |
| /** |
| * The initial network agent id. The network agent can be re-created due to immutable capability |
| * changed. This is to preserve the initial network agent id so the id in the logging tag won't |
| * change for the entire life cycle of data network. |
| */ |
| private int mInitialNetworkAgentId; |
| |
| /** PDU session id. */ |
| private int mPduSessionId = DataCallResponse.PDU_SESSION_ID_NOT_SET; |
| |
| /** |
| * Data service managers for accessing {@link AccessNetworkConstants#TRANSPORT_TYPE_WWAN} and |
| * {@link AccessNetworkConstants#TRANSPORT_TYPE_WLAN} data services. |
| */ |
| private final @NonNull SparseArray<DataServiceManager> mDataServiceManagers; |
| |
| /** Access networks manager. */ |
| private final @NonNull AccessNetworksManager mAccessNetworksManager; |
| |
| /** Data network controller. */ |
| private final @NonNull DataNetworkController mDataNetworkController; |
| |
| /** Data config manager. */ |
| private final @NonNull DataConfigManager mDataConfigManager; |
| |
| /** VCN manager. */ |
| private final @Nullable VcnManager mVcnManager; |
| |
| /** VCN policy changed listener. */ |
| private @Nullable VcnNetworkPolicyChangeListener mVcnPolicyChangeListener; |
| |
| /** The network agent associated with this data network. */ |
| private @NonNull TelephonyNetworkAgent mNetworkAgent; |
| |
| /** QOS callback tracker. This is only created after network connected on WWAN. */ |
| private @Nullable QosCallbackTracker mQosCallbackTracker; |
| |
| /** NAT keepalive tracker. */ |
| private @Nullable KeepaliveTracker mKeepaliveTracker; |
| |
| /** The data profile used to establish this data network. */ |
| private final @NonNull DataProfile mDataProfile; |
| |
| /** The network capabilities of this data network. */ |
| private @NonNull NetworkCapabilities mNetworkCapabilities; |
| |
| /** The matched traffic descriptor returned from setup data call request. */ |
| private final @NonNull List<TrafficDescriptor> mTrafficDescriptors = new ArrayList<>(); |
| |
| /** The link properties of this data network. */ |
| private @NonNull LinkProperties mLinkProperties; |
| |
| /** The network slice info. */ |
| private @Nullable NetworkSliceInfo mNetworkSliceInfo; |
| |
| /** The link status (i.e. RRC state). */ |
| private @LinkStatus int mLinkStatus = DataCallResponse.LINK_STATUS_UNKNOWN; |
| |
| /** The network bandwidth. */ |
| private @NonNull NetworkBandwidth mNetworkBandwidth = new NetworkBandwidth(14, 14); |
| |
| /** The TCP buffer sizes config. */ |
| private @NonNull String mTcpBufferSizes; |
| |
| /** Whether {@link NetworkCapabilities#NET_CAPABILITY_TEMPORARILY_NOT_METERED} is supported. */ |
| private boolean mTempNotMeteredSupported = false; |
| |
| /** Whether the current data network is temporarily not metered. */ |
| private boolean mTempNotMetered = false; |
| |
| /** Whether the current data network is congested. */ |
| private boolean mCongested = false; |
| |
| /** The network requests associated with this data network */ |
| private final @NonNull NetworkRequestList mAttachedNetworkRequestList = |
| new NetworkRequestList(); |
| |
| /** |
| * The latest data call response received from either |
| * {@link DataServiceCallback#onSetupDataCallComplete(int, DataCallResponse)} or |
| * {@link DataServiceCallback#onDataCallListChanged(List)}. The very first update must be |
| * from {@link DataServiceCallback#onSetupDataCallComplete(int, DataCallResponse)}. |
| */ |
| private @Nullable DataCallResponse mDataCallResponse = null; |
| |
| /** |
| * The fail cause from either setup data failure or unsolicited disconnect reported by data |
| * service. |
| */ |
| private @DataFailureCause int mFailCause = DataFailCause.NONE; |
| |
| /** |
| * Indicates if data network is suspended. Note this is slightly different from the |
| * {@link TelephonyManager#DATA_SUSPENDED}, which is only possible when data network is in |
| * connected state. This flag reflects to the |
| * {@link NetworkCapabilities#NET_CAPABILITY_NOT_SUSPENDED} which can happen when data network |
| * is in connected or disconnecting state. |
| */ |
| private boolean mSuspended = false; |
| |
| /** |
| * The current transport of the data network. For handover, the current transport will be set |
| * after handover completes. |
| */ |
| private @TransportType int mTransport; |
| |
| /** The reason that why setting up this data network is allowed. */ |
| private @NonNull DataAllowedReason mDataAllowedReason; |
| |
| /** |
| * PCO (Protocol Configuration Options) data received from the network. Key is the PCO id, value |
| * is the PCO content. |
| */ |
| private final @NonNull Map<Integer, PcoData> mPcoData = new ArrayMap<>(); |
| |
| /** The QOS bearer sessions. */ |
| private final @NonNull List<QosBearerSession> mQosBearerSessions = new ArrayList<>(); |
| |
| /** |
| * The UIDs of packages that have carrier privilege. These UIDs will not change through the |
| * life cycle of data network. |
| */ |
| private @NonNull int[] mAdministratorUids = new int[0]; |
| |
| /** |
| * Carrier service package uid. This UID will not change through the life cycle of data network. |
| */ |
| private int mCarrierServicePackageUid = Process.INVALID_UID; |
| |
| /** |
| * The network bandwidth. |
| */ |
| public static class NetworkBandwidth { |
| /** The downlink bandwidth in Kbps. */ |
| public final int downlinkBandwidthKbps; |
| |
| /** The uplink Bandwidth in Kbps. */ |
| public final int uplinkBandwidthKbps; |
| |
| /** |
| * Constructor. |
| * |
| * @param downlinkBandwidthKbps The downlink bandwidth in Kbps. |
| * @param uplinkBandwidthKbps The uplink Bandwidth in Kbps. |
| */ |
| public NetworkBandwidth(int downlinkBandwidthKbps, int uplinkBandwidthKbps) { |
| this.downlinkBandwidthKbps = downlinkBandwidthKbps; |
| this.uplinkBandwidthKbps = uplinkBandwidthKbps; |
| } |
| |
| @Override |
| public String toString() { |
| return String.format("NetworkBandwidth=[downlink=%d, uplink=%d]", |
| downlinkBandwidthKbps, uplinkBandwidthKbps); |
| } |
| } |
| |
| /** |
| * Data network callback. Should only be used by {@link DataNetworkController}. |
| */ |
| public abstract static class DataNetworkCallback extends DataCallback { |
| /** |
| * Constructor |
| * |
| * @param executor The executor of the callback. |
| */ |
| public DataNetworkCallback(@NonNull @CallbackExecutor Executor executor) { |
| super(executor); |
| } |
| |
| /** |
| * Called when data setup failed. |
| * |
| * @param dataNetwork The data network. |
| * @param requestList The network requests attached to this data network. |
| * @param cause The fail cause of setup data network. |
| * @param retryDurationMillis The network suggested data retry duration in milliseconds as |
| * specified in 3GPP TS 24.302 section 8.2.9.1. The {@link DataProfile} associated to this |
| * data network will be throttled for the specified duration unless |
| * {@link DataServiceCallback#onApnUnthrottled} is called. {@link Long#MAX_VALUE} indicates |
| * data retry should not occur. {@link DataCallResponse#RETRY_DURATION_UNDEFINED} indicates |
| * network did not suggest any retry duration. |
| */ |
| public abstract void onSetupDataFailed(@NonNull DataNetwork dataNetwork, |
| @NonNull NetworkRequestList requestList, @DataFailureCause int cause, |
| long retryDurationMillis); |
| |
| /** |
| * Called when data network enters {@link ConnectedState}. |
| * |
| * @param dataNetwork The data network. |
| */ |
| public abstract void onConnected(@NonNull DataNetwork dataNetwork); |
| |
| /** |
| * Called when data network validation status changed. |
| * |
| * @param dataNetwork The data network. |
| * @param status one of {@link NetworkAgent#VALIDATION_STATUS_VALID} or |
| * {@link NetworkAgent#VALIDATION_STATUS_NOT_VALID}. |
| * @param redirectUri If internet connectivity is being redirected (e.g., on a captive |
| * portal), this is the destination the probes are being redirected to, otherwise |
| * {@code null}. |
| */ |
| public abstract void onValidationStatusChanged(@NonNull DataNetwork dataNetwork, |
| @ValidationStatus int status, @Nullable Uri redirectUri); |
| |
| /** |
| * Called when data network suspended state changed. |
| * |
| * @param dataNetwork The data network. |
| * @param suspended {@code true} if data is suspended. |
| */ |
| public abstract void onSuspendedStateChanged(@NonNull DataNetwork dataNetwork, |
| boolean suspended); |
| |
| /** |
| * Called when network requests were failed to attach to the data network. |
| * |
| * @param dataNetwork The data network. |
| * @param requestList The requests failed to attach. |
| */ |
| public abstract void onAttachFailed(@NonNull DataNetwork dataNetwork, |
| @NonNull NetworkRequestList requestList); |
| |
| /** |
| * Called when data network enters {@link DisconnectedState}. Note this is only called |
| * when the data network was previously connected. For setup data network failed, |
| * {@link #onSetupDataFailed(DataNetwork, NetworkRequestList, int, long)} is called. |
| * |
| * @param dataNetwork The data network. |
| * @param cause The disconnect cause. |
| */ |
| public abstract void onDisconnected(@NonNull DataNetwork dataNetwork, |
| @DataFailureCause int cause); |
| |
| /** |
| * Called when handover between IWLAN and cellular network succeeded. |
| * |
| * @param dataNetwork The data network. |
| */ |
| public abstract void onHandoverSucceeded(@NonNull DataNetwork dataNetwork); |
| |
| /** |
| * Called when data network handover between IWLAN and cellular network failed. |
| * |
| * @param dataNetwork The data network. |
| * @param cause The fail cause. |
| * @param retryDurationMillis Network suggested retry time in milliseconds. |
| * {@link Long#MAX_VALUE} indicates data retry should not occur. |
| * {@link DataCallResponse#RETRY_DURATION_UNDEFINED} indicates network did not suggest any |
| * retry duration. |
| * @param handoverFailureMode The handover failure mode that determine the behavior of |
| * how frameworks should handle the handover failure. |
| */ |
| public abstract void onHandoverFailed(@NonNull DataNetwork dataNetwork, |
| @DataFailureCause int cause, long retryDurationMillis, |
| @HandoverFailureMode int handoverFailureMode); |
| |
| /** |
| * Called when data network link status (i.e. RRC state) changed. |
| * |
| * @param dataNetwork The data network. |
| * @param linkStatus The link status (i.e. RRC state). |
| */ |
| public abstract void onLinkStatusChanged(@NonNull DataNetwork dataNetwork, |
| @LinkStatus int linkStatus); |
| |
| /** |
| * Called when PCO data changed. |
| * |
| * @param dataNetwork The data network. |
| */ |
| public abstract void onPcoDataChanged(@NonNull DataNetwork dataNetwork); |
| |
| /** |
| * Called when network capabilities changed. |
| * |
| * @param dataNetwork The data network. |
| */ |
| public abstract void onNetworkCapabilitiesChanged(@NonNull DataNetwork dataNetwork); |
| } |
| |
| /** |
| * Constructor |
| * |
| * @param phone The phone instance. |
| * @param looper The looper to be used by the state machine. Currently the handler thread is the |
| * phone process's main thread. |
| * @param dataServiceManagers Data service managers. |
| * @param dataProfile The data profile for establishing the data network. |
| * @param networkRequestList The initial network requests attached to this data network. |
| * @param transport The initial transport of the data network. |
| * @param dataAllowedReason The reason that why setting up this data network is allowed. |
| * @param callback The callback to receives data network state update. |
| */ |
| public DataNetwork(@NonNull Phone phone, @NonNull Looper looper, |
| @NonNull SparseArray<DataServiceManager> dataServiceManagers, |
| @NonNull DataProfile dataProfile, |
| @NonNull NetworkRequestList networkRequestList, |
| @TransportType int transport, |
| @NonNull DataAllowedReason dataAllowedReason, |
| @NonNull DataNetworkCallback callback) { |
| super("DataNetwork", looper); |
| mPhone = phone; |
| mSubId = phone.getSubId(); |
| mRil = mPhone.mCi; |
| mLinkProperties = new LinkProperties(); |
| mDataServiceManagers = dataServiceManagers; |
| mAccessNetworksManager = phone.getAccessNetworksManager(); |
| mVcnManager = mPhone.getContext().getSystemService(VcnManager.class); |
| mDataNetworkController = phone.getDataNetworkController(); |
| mDataNetworkController.registerDataNetworkControllerCallback( |
| new DataNetworkController.DataNetworkControllerCallback(getHandler()::post) { |
| @Override |
| public void onSubscriptionPlanOverride() { |
| sendMessage(EVENT_SUBSCRIPTION_PLAN_OVERRIDE); |
| }}); |
| mDataConfigManager = mDataNetworkController.getDataConfigManager(); |
| mDataCallSessionStats = new DataCallSessionStats(mPhone); |
| mDataNetworkCallback = callback; |
| mDataProfile = dataProfile; |
| mTransport = transport; |
| mDataAllowedReason = dataAllowedReason; |
| dataProfile.setLastSetupTimestamp(SystemClock.elapsedRealtime()); |
| mAttachedNetworkRequestList.addAll(networkRequestList); |
| mCid.put(AccessNetworkConstants.TRANSPORT_TYPE_WWAN, INVALID_CID); |
| mCid.put(AccessNetworkConstants.TRANSPORT_TYPE_WLAN, INVALID_CID); |
| mTcpBufferSizes = mDataConfigManager.getDefaultTcpConfigString(); |
| |
| for (TelephonyNetworkRequest networkRequest : networkRequestList) { |
| networkRequest.setAttachedNetwork(DataNetwork.this); |
| networkRequest.setState(TelephonyNetworkRequest.REQUEST_STATE_SATISFIED); |
| } |
| |
| addState(mDefaultState); |
| addState(mConnectingState, mDefaultState); |
| addState(mConnectedState, mDefaultState); |
| addState(mHandoverState, mDefaultState); |
| addState(mDisconnectingState, mDefaultState); |
| addState(mDisconnectedState, mDefaultState); |
| setInitialState(mConnectingState); |
| |
| /** |
| * This will trigger {@link DefaultState#enter()}, and then {@link ConnectingState#enter()}. |
| * Check {@link StateMachine} class to see how Android state machine works. |
| */ |
| start(); |
| // Do not add more stuffs here. |
| } |
| |
| /** |
| * Create the telephony network agent. |
| * |
| * @return The telephony network agent. |
| */ |
| private @NonNull TelephonyNetworkAgent createNetworkAgent() { |
| final NetworkAgentConfig.Builder configBuilder = new NetworkAgentConfig.Builder(); |
| configBuilder.setLegacyType(ConnectivityManager.TYPE_MOBILE); |
| configBuilder.setLegacyTypeName("MOBILE"); |
| int networkType = getDataNetworkType(); |
| configBuilder.setLegacySubType(networkType); |
| configBuilder.setLegacySubTypeName(TelephonyManager.getNetworkTypeName(networkType)); |
| if (mDataProfile.getApnSetting() != null) { |
| configBuilder.setLegacyExtraInfo(mDataProfile.getApnSetting().getApnName()); |
| } |
| |
| final NetworkFactory factory = PhoneFactory.getNetworkFactory( |
| mPhone.getPhoneId()); |
| final NetworkProvider provider = (null == factory) ? null : factory.getProvider(); |
| |
| return new TelephonyNetworkAgent(mPhone, getHandler().getLooper(), this, |
| getNetworkScore(), configBuilder.build(), provider, |
| new TelephonyNetworkAgentCallback(getHandler()::post) { |
| @Override |
| public void onValidationStatus(@ValidationStatus int status, |
| @Nullable Uri redirectUri) { |
| mDataNetworkCallback.invokeFromExecutor( |
| () -> mDataNetworkCallback.onValidationStatusChanged( |
| DataNetwork.this, status, redirectUri)); |
| } |
| }); |
| } |
| |
| /** |
| * The default state. Any events that were not handled by the child states fallback to this |
| * state. |
| * |
| * @see DataNetwork for the state machine diagram. |
| */ |
| private final class DefaultState extends State { |
| @Override |
| public void enter() { |
| logv("Registering all events."); |
| mDataConfigManager.registerForConfigUpdate(getHandler(), EVENT_DATA_CONFIG_UPDATED); |
| mPhone.getDisplayInfoController().registerForTelephonyDisplayInfoChanged( |
| getHandler(), EVENT_DISPLAY_INFO_CHANGED, null); |
| for (int transport : mAccessNetworksManager.getAvailableTransports()) { |
| mDataServiceManagers.get(transport) |
| .registerForDataCallListChanged(getHandler(), EVENT_DATA_STATE_CHANGED); |
| mPhone.getServiceStateTracker().registerForDataRegStateOrRatChanged( |
| transport, getHandler(), EVENT_SERVICE_STATE_CHANGED, transport); |
| } |
| |
| mPhone.getCarrierPrivilegesTracker().registerCarrierPrivilegesListener(getHandler(), |
| EVENT_CARRIER_PRIVILEGED_UIDS_CHANGED, null); |
| |
| // Only add symmetric code here, for example, registering and unregistering. |
| // DefaultState.enter() is the starting point in the life cycle of the DataNetwork, |
| // and DefaultState.exit() is the end. For non-symmetric initializing works, put them |
| // in ConnectingState.enter(). |
| } |
| |
| @Override |
| public void exit() { |
| logv("Unregistering all events."); |
| for (int transport : mAccessNetworksManager.getAvailableTransports()) { |
| mDataServiceManagers.get(transport) |
| .unregisterForDataCallListChanged(getHandler()); |
| mPhone.getServiceStateTracker().unregisterForDataRegStateOrRatChanged( |
| transport, getHandler()); |
| } |
| mPhone.getDisplayInfoController().unregisterForTelephonyDisplayInfoChanged( |
| getHandler()); |
| mDataConfigManager.unregisterForConfigUpdate(getHandler()); |
| } |
| |
| @Override |
| public boolean processMessage(Message msg) { |
| switch (msg.what) { |
| case EVENT_DATA_CONFIG_UPDATED: |
| onDataConfigUpdated(); |
| break; |
| case EVENT_SERVICE_STATE_CHANGED: { |
| // TODO: Should update suspend state when CSS indicator changes. |
| // TODO: Should update suspend state when call started/ended. |
| updateSuspendState(); |
| updateTcpBufferSizes(); |
| updateBandwidthFromDataConfig(); |
| updateDataCallSessionStatsOfDrsOrRatChange((AsyncResult) msg.obj); |
| break; |
| } |
| case EVENT_ATTACH_NETWORK_REQUEST: { |
| NetworkRequestList requestList = (NetworkRequestList) msg.obj; |
| NetworkRequestList failedList = new NetworkRequestList(); |
| for (TelephonyNetworkRequest networkRequest : requestList) { |
| if (networkRequest.canBeSatisfiedBy(getNetworkCapabilities())) { |
| mAttachedNetworkRequestList.add(networkRequest); |
| networkRequest.setAttachedNetwork(DataNetwork.this); |
| networkRequest.setState( |
| TelephonyNetworkRequest.REQUEST_STATE_SATISFIED); |
| log("Successfully attached network request " + networkRequest); |
| } else { |
| failedList.add(networkRequest); |
| log("Attached failed. Cannot satisfy the network request " |
| + networkRequest); |
| } |
| if (failedList.size() > 0) { |
| mDataNetworkCallback.invokeFromExecutor(() -> mDataNetworkCallback |
| .onAttachFailed(DataNetwork.this, failedList)); |
| } |
| } |
| break; |
| } |
| case EVENT_DETACH_NETWORK_REQUEST: { |
| TelephonyNetworkRequest networkRequest = (TelephonyNetworkRequest) msg.obj; |
| mAttachedNetworkRequestList.remove(networkRequest); |
| networkRequest.setState(TelephonyNetworkRequest.REQUEST_STATE_UNSATISFIED); |
| networkRequest.setAttachedNetwork(null); |
| break; |
| } |
| case EVENT_DETACH_ALL_NETWORK_REQUESTS: { |
| for (TelephonyNetworkRequest networkRequest : mAttachedNetworkRequestList) { |
| networkRequest.setState(TelephonyNetworkRequest.REQUEST_STATE_UNSATISFIED); |
| networkRequest.setAttachedNetwork(null); |
| } |
| mAttachedNetworkRequestList.clear(); |
| break; |
| } |
| case EVENT_DATA_STATE_CHANGED: { |
| AsyncResult ar = (AsyncResult) msg.obj; |
| int transport = (int) ar.userObj; |
| onDataStateChanged(transport, (List<DataCallResponse>) ar.result); |
| break; |
| } |
| case EVENT_CARRIER_PRIVILEGED_UIDS_CHANGED: { |
| AsyncResult asyncResult = (AsyncResult) msg.obj; |
| int[] administratorUids = (int[]) asyncResult.result; |
| mAdministratorUids = Arrays.copyOf(administratorUids, administratorUids.length); |
| break; |
| } |
| case EVENT_START_HANDOVER: |
| case EVENT_BANDWIDTH_ESTIMATE_FROM_MODEM_CHANGED: |
| case EVENT_BANDWIDTH_ESTIMATE_FROM_BANDWIDTH_ESTIMATOR_CHANGED: |
| case EVENT_TEAR_DOWN_NETWORK: |
| case EVENT_PCO_DATA_RECEIVED: |
| // Ignore the events when not in the correct state. |
| break; |
| default: |
| loge("Unhandled event " + eventToString(msg.what)); |
| break; |
| } |
| return HANDLED; |
| } |
| } |
| |
| /** |
| * The connecting state. This is the initial state of a data network. |
| * |
| * @see DataNetwork for the state machine diagram. |
| */ |
| private final class ConnectingState extends State { |
| @Override |
| public void enter() { |
| // Need to calculate the initial capabilities before creating the network agent. |
| updateNetworkCapabilities(); |
| mNetworkAgent = createNetworkAgent(); |
| mInitialNetworkAgentId = mNetworkAgent.getId(); |
| mLogTag = "DN-" + mInitialNetworkAgentId + "-" |
| + ((mTransport == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) ? "C" : "I"); |
| |
| // Get carrier config package uid. Note that this uid will not change through the life |
| // cycle of this data network. So there is no need to listen to the change event. |
| mCarrierServicePackageUid = mPhone.getCarrierPrivilegesTracker() |
| .getCarrierServicePackageUid(); |
| |
| notifyPreciseDataConnectionState(); |
| if (mTransport == AccessNetworkConstants.TRANSPORT_TYPE_WLAN) { |
| allocatePduSessionId(); |
| return; |
| } |
| |
| setupData(); |
| } |
| |
| @Override |
| public void exit() { |
| } |
| |
| @Override |
| public boolean processMessage(Message msg) { |
| logv("event=" + eventToString(msg.what)); |
| switch (msg.what) { |
| case EVENT_ALLOCATE_PDU_SESSION_ID_RESPONSE: |
| AsyncResult ar = (AsyncResult) msg.obj; |
| if (ar.exception == null) { |
| mPduSessionId = (int) ar.result; |
| log("Set PDU session id to " + mPduSessionId); |
| } else { |
| loge("Failed to allocate PDU session id. e=" + ar.exception); |
| } |
| setupData(); |
| break; |
| case EVENT_SETUP_DATA_NETWORK_RESPONSE: |
| int resultCode = msg.arg1; |
| DataCallResponse dataCallResponse = |
| msg.getData().getParcelable(DataServiceManager.DATA_CALL_RESPONSE); |
| onSetupResponse(resultCode, dataCallResponse); |
| break; |
| case EVENT_START_HANDOVER: |
| case EVENT_TEAR_DOWN_NETWORK: |
| case EVENT_PCO_DATA_RECEIVED: |
| // Defer the request until connected or disconnected. |
| deferMessage(msg); |
| break; |
| default: |
| return NOT_HANDLED; |
| } |
| return HANDLED; |
| } |
| } |
| |
| /** |
| * The connected state. This is the state when data network becomes usable. |
| * |
| * @see DataNetwork for the state machine diagram. |
| */ |
| private final class ConnectedState extends State { |
| @Override |
| public void enter() { |
| // Note that reaching here could mean from connecting -> connected, or from |
| // handover -> connected. |
| if (!mEverConnected) { |
| // Transited from ConnectingState |
| log("network connected."); |
| mEverConnected = true; |
| mNetworkAgent.markConnected(); |
| mDataNetworkCallback.invokeFromExecutor( |
| () -> mDataNetworkCallback.onConnected(DataNetwork.this)); |
| |
| mQosCallbackTracker = new QosCallbackTracker(mNetworkAgent, mPhone); |
| mQosCallbackTracker.updateSessions(mQosBearerSessions); |
| mKeepaliveTracker = new KeepaliveTracker(mPhone, |
| getHandler().getLooper(), DataNetwork.this, mNetworkAgent); |
| if (mTransport == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) { |
| registerForWwanEvents(); |
| } |
| |
| // Create the VCN policy changed listener. When the policy changed, we might need |
| // to tear down the VCN-managed network. |
| if (mVcnManager != null) { |
| mVcnPolicyChangeListener = () -> { |
| log("VCN policy changed."); |
| if (mVcnManager.applyVcnNetworkPolicy(mNetworkCapabilities, mLinkProperties) |
| .isTeardownRequested()) { |
| tearDown(TEAR_DOWN_REASON_VCN_REQUESTED); |
| } else { |
| updateNetworkCapabilities(); |
| } |
| }; |
| mVcnManager.addVcnNetworkPolicyChangeListener( |
| getHandler()::post, mVcnPolicyChangeListener); |
| } |
| } |
| |
| notifyPreciseDataConnectionState(); |
| updateSuspendState(); |
| } |
| |
| @Override |
| public void exit() { |
| } |
| |
| @Override |
| public boolean processMessage(Message msg) { |
| logv("event=" + eventToString(msg.what)); |
| switch (msg.what) { |
| case EVENT_TEAR_DOWN_NETWORK: |
| if (mInvokedDataDeactivation) { |
| log("Ignore tear down request because network is being torn down."); |
| break; |
| } |
| removeMessages(EVENT_TEAR_DOWN_NETWORK); |
| removeDeferredMessages(EVENT_TEAR_DOWN_NETWORK); |
| transitionTo(mDisconnectingState); |
| onTearDown(msg.arg1); |
| break; |
| case EVENT_BANDWIDTH_ESTIMATE_FROM_MODEM_CHANGED: |
| AsyncResult ar = (AsyncResult) msg.obj; |
| if (ar.exception != null) { |
| log("EVENT_BANDWIDTH_ESTIMATE_FROM_MODEM_CHANGED: error ignoring, e=" |
| + ar.exception); |
| break; |
| } |
| onBandwidthUpdatedFromModem((List<LinkCapacityEstimate>) ar.result); |
| break; |
| case EVENT_BANDWIDTH_ESTIMATE_FROM_BANDWIDTH_ESTIMATOR_CHANGED: |
| ar = (AsyncResult) msg.obj; |
| Pair<Integer, Integer> pair = (Pair<Integer, Integer>) ar.result; |
| onBandwidthUpdated(pair.first, pair.second); |
| break; |
| case EVENT_DISPLAY_INFO_CHANGED: |
| onDisplayInfoChanged(); |
| break; |
| case EVENT_START_HANDOVER: |
| onStartHandover(msg.arg1, (DataHandoverRetryEntry) msg.obj); |
| break; |
| case EVENT_SUBSCRIPTION_PLAN_OVERRIDE: |
| updateMeteredAndCongested(); |
| break; |
| case EVENT_PCO_DATA_RECEIVED: |
| ar = (AsyncResult) msg.obj; |
| onPcoDataReceived((PcoData) ar.result); |
| break; |
| case EVENT_DEACTIVATE_DATA_NETWORK_RESPONSE: |
| int resultCode = msg.arg1; |
| onDeactivateResponse(resultCode); |
| break; |
| default: |
| return NOT_HANDLED; |
| } |
| return HANDLED; |
| } |
| } |
| |
| /** |
| * The handover state. This is the state when data network handover between IWLAN and cellular. |
| * |
| * @see DataNetwork for the state machine diagram. |
| */ |
| private final class HandoverState extends State { |
| @Override |
| public void enter() { |
| notifyPreciseDataConnectionState(); |
| } |
| |
| @Override |
| public void exit() { |
| } |
| |
| @Override |
| public boolean processMessage(Message msg) { |
| logv("event=" + eventToString(msg.what)); |
| switch (msg.what) { |
| case EVENT_DATA_STATE_CHANGED: |
| // The data call list changed event should be conditionally deferred. |
| // Otherwise the deferred message might be incorrectly treated as "disconnected" |
| // signal. So we only defer the related data call list changed event, and drop |
| // the unrelated. |
| if (shouldDeferDataStateChangedEvent(msg)) { |
| deferMessage(msg); |
| } |
| break; |
| case EVENT_START_HANDOVER: |
| case EVENT_TEAR_DOWN_NETWORK: |
| // Defer the request until handover succeeds or fails. |
| deferMessage(msg); |
| break; |
| case EVENT_HANDOVER_RESPONSE: |
| int resultCode = msg.arg1; |
| DataCallResponse dataCallResponse = |
| msg.getData().getParcelable(DataServiceManager.DATA_CALL_RESPONSE); |
| onHandoverResponse(resultCode, dataCallResponse, |
| (DataHandoverRetryEntry) msg.obj); |
| break; |
| case EVENT_PCO_DATA_RECEIVED: |
| AsyncResult ar = (AsyncResult) msg.obj; |
| onPcoDataReceived((PcoData) ar.result); |
| break; |
| default: |
| return NOT_HANDLED; |
| } |
| return HANDLED; |
| } |
| |
| /** |
| * Check if the data call list changed event should be deferred or dropped when handover |
| * is in progress. |
| * |
| * @param msg The data call list changed message. |
| * |
| * @return {@code true} if the message should be deferred. |
| */ |
| private boolean shouldDeferDataStateChangedEvent(@NonNull Message msg) { |
| // The data call list changed event should be conditionally deferred. |
| // Otherwise the deferred message might be incorrectly treated as "disconnected" |
| // signal. So we only defer the related data call list changed event, and drop |
| // the unrelated. |
| AsyncResult ar = (AsyncResult) msg.obj; |
| int transport = (int) ar.userObj; |
| List<DataCallResponse> responseList = (List<DataCallResponse>) ar.result; |
| if (transport != mTransport) { |
| log("Dropped unrelated " + AccessNetworkConstants.transportTypeToString(transport) |
| + " data call list changed event. " + responseList); |
| return false; |
| } |
| |
| // Check if the data call list changed event are related to the current data network. |
| boolean related = responseList.stream().anyMatch( |
| r -> mCid.get(mTransport) == r.getId()); |
| if (related) { |
| log("Deferred the related data call list changed event." + responseList); |
| } else { |
| log("Dropped unrelated data call list changed event. " + responseList); |
| } |
| return related; |
| } |
| } |
| |
| /** |
| * The disconnecting state. This is the state when data network is about to be disconnected. |
| * The network is still usable in this state, but the clients should be prepared to lose the |
| * network in any moment. This state is particular useful for IMS graceful tear down, where |
| * the network enters disconnecting state while waiting for IMS de-registration signal. |
| * |
| * @see DataNetwork for the state machine diagram. |
| */ |
| private final class DisconnectingState extends State { |
| @Override |
| public void enter() { |
| notifyPreciseDataConnectionState(); |
| } |
| |
| @Override |
| public boolean processMessage(Message msg) { |
| logv("event=" + eventToString(msg.what)); |
| switch (msg.what) { |
| case EVENT_DEACTIVATE_DATA_NETWORK_RESPONSE: |
| int resultCode = msg.arg1; |
| onDeactivateResponse(resultCode); |
| break; |
| default: |
| return NOT_HANDLED; |
| } |
| return HANDLED; |
| } |
| } |
| |
| /** |
| * The disconnected state. This is the final state of a data network. |
| * |
| * @see DataNetwork for the state machine diagram. |
| */ |
| private final class DisconnectedState extends State { |
| @Override |
| public void enter() { |
| logl("Data network disconnected."); |
| // The detach all network requests must be tge last message to handle. |
| sendMessage(EVENT_DETACH_ALL_NETWORK_REQUESTS); |
| // Gracefully handle all the un-processed events then quit the state machine. |
| // quit() throws a QUIT event to the end of message queue. All the events before quit() |
| // will be processed. Events after quit() will not be processed. |
| quit(); |
| notifyPreciseDataConnectionState(); |
| mNetworkAgent.unregister(); |
| mDataCallSessionStats.onDataCallDisconnected(mFailCause); |
| |
| if (mTransport == AccessNetworkConstants.TRANSPORT_TYPE_WLAN |
| && mPduSessionId != DataCallResponse.PDU_SESSION_ID_NOT_SET) { |
| mRil.releasePduSessionId(null, mPduSessionId); |
| } |
| |
| if (mTransport == AccessNetworkConstants.TRANSPORT_TYPE_WWAN && mEverConnected) { |
| unregisterForWwanEvents(); |
| } |
| |
| if (mVcnManager != null && mVcnPolicyChangeListener != null) { |
| mVcnManager.removeVcnNetworkPolicyChangeListener(mVcnPolicyChangeListener); |
| } |
| } |
| |
| @Override |
| public boolean processMessage(Message msg) { |
| logv("event=" + eventToString(msg.what)); |
| return NOT_HANDLED; |
| } |
| } |
| |
| /** |
| * Register for events that can only happen on cellular networks. |
| */ |
| private void registerForWwanEvents() { |
| registerForBandwidthUpdate(); |
| mKeepaliveTracker.registerForKeepaliveStatus(); |
| mRil.registerForPcoData(this.getHandler(), EVENT_PCO_DATA_RECEIVED, null); |
| } |
| |
| /** |
| * Unregister for events that can only happen on cellular networks. |
| */ |
| private void unregisterForWwanEvents() { |
| unregisterForBandwidthUpdate(); |
| mKeepaliveTracker.unregisterForKeepaliveStatus(); |
| mRil.unregisterForPcoData(this.getHandler()); |
| } |
| |
| @Override |
| protected void unhandledMessage(Message msg) { |
| IState state = getCurrentState(); |
| loge("Unhandled message " + msg.what + " in state " |
| + (state == null ? "null" : state.getName())); |
| } |
| |
| /** |
| * Attempt to attach the network request list to this data network. Whether the network can |
| * satisfy the request or not will be checked when EVENT_ATTACH_NETWORK_REQUEST is processed. |
| * If the request can't be attached, {@link DataNetworkCallback#onAttachFailed( |
| * DataNetwork, NetworkRequestList)} will be called, and retry should be scheduled. |
| * |
| * @param requestList Network request list to attach. |
| * @return {@code false} if the network is already disconnected. {@code true} means the request |
| * has been scheduled to attach to the network. If attach succeeds, the network request's state |
| * will be set to {@link TelephonyNetworkRequest#REQUEST_STATE_SATISFIED}. If failed, the |
| * callback {@link DataNetworkCallback#onAttachFailed(DataNetwork, NetworkRequestList)} will |
| * be called, and retry should be scheduled. |
| */ |
| public boolean attachNetworkRequests(@NonNull NetworkRequestList requestList) { |
| // If the network is already ended, we still attach the network request to the data network, |
| // so it can be retried later by data network controller. |
| if (getCurrentState() == null || isDisconnected()) { |
| // The state machine has already stopped. This is due to data network is disconnected. |
| return false; |
| } |
| sendMessage(obtainMessage(EVENT_ATTACH_NETWORK_REQUEST, requestList)); |
| return true; |
| } |
| |
| /** |
| * Detach the network request from this data network. Note that this will not tear down the |
| * network. |
| * |
| * @param networkRequest Network request to detach. |
| */ |
| public void detachNetworkRequest(@NonNull TelephonyNetworkRequest networkRequest) { |
| if (getCurrentState() == null || isDisconnected()) { |
| mAttachedNetworkRequestList.remove(networkRequest); |
| return; |
| } |
| sendMessage(obtainMessage(EVENT_DETACH_NETWORK_REQUEST, networkRequest)); |
| } |
| |
| /** |
| * Register for bandwidth update. |
| */ |
| private void registerForBandwidthUpdate() { |
| int bandwidthEstimateSource = mDataConfigManager.getBandwidthEstimateSource(); |
| if (bandwidthEstimateSource == BANDWIDTH_SOURCE_MODEM) { |
| mPhone.mCi.registerForLceInfo( |
| getHandler(), EVENT_BANDWIDTH_ESTIMATE_FROM_MODEM_CHANGED, null); |
| } else if (bandwidthEstimateSource == BANDWIDTH_SOURCE_BANDWIDTH_ESTIMATOR) { |
| mPhone.getLinkBandwidthEstimator().registerForBandwidthChanged( |
| getHandler(), EVENT_BANDWIDTH_ESTIMATE_FROM_BANDWIDTH_ESTIMATOR_CHANGED, null); |
| } else { |
| loge("Invalid bandwidth source configuration: " + bandwidthEstimateSource); |
| } |
| } |
| |
| /** |
| * Unregister bandwidth update. |
| */ |
| private void unregisterForBandwidthUpdate() { |
| int bandwidthEstimateSource = mDataConfigManager.getBandwidthEstimateSource(); |
| if (bandwidthEstimateSource == BANDWIDTH_SOURCE_MODEM) { |
| mPhone.mCi.unregisterForLceInfo(getHandler()); |
| } else if (bandwidthEstimateSource == BANDWIDTH_SOURCE_BANDWIDTH_ESTIMATOR) { |
| mPhone.getLinkBandwidthEstimator().unregisterForBandwidthChanged(getHandler()); |
| } else { |
| loge("Invalid bandwidth source configuration: " + bandwidthEstimateSource); |
| } |
| } |
| |
| /** |
| * Remove network requests that can't be satisfied anymore. |
| */ |
| private void removeUnsatisfiedNetworkRequests() { |
| for (TelephonyNetworkRequest networkRequest : mAttachedNetworkRequestList) { |
| if (!networkRequest.canBeSatisfiedBy(mNetworkCapabilities)) { |
| log("removeUnsatisfiedNetworkRequests: " + networkRequest |
| + " can't be satisfied anymore. Will be detached."); |
| detachNetworkRequest(networkRequest); |
| } |
| } |
| } |
| |
| /** |
| * Check if there are immutable capabilities changed. The connectivity service is not able |
| * to handle immutable capabilities changed, but in very rare scenarios, immutable capabilities |
| * need to be changed dynamically, such as in setup data call response, modem responded with the |
| * same cid. In that case, we need to merge the new capabilities into the existing data network. |
| * |
| * @param oldCapabilities The old network capabilities. |
| * @param newCapabilities The new network capabilities. |
| * @return {@code true} if there are immutable network capabilities changed. |
| */ |
| private static boolean areImmutableCapabilitiesChanged( |
| @NonNull NetworkCapabilities oldCapabilities, |
| @NonNull NetworkCapabilities newCapabilities) { |
| if (oldCapabilities == null |
| || ArrayUtils.isEmpty(oldCapabilities.getCapabilities())) return false; |
| |
| // Remove mutable capabilities from both old and new capabilities, the remaining |
| // capabilities would be immutable capabilities. |
| List<Integer> oldImmutableCapabilities = Arrays.stream(oldCapabilities.getCapabilities()) |
| .boxed().collect(Collectors.toList()); |
| oldImmutableCapabilities.removeAll(MUTABLE_CAPABILITIES); |
| List<Integer> newImmutableCapabilities = Arrays.stream(newCapabilities.getCapabilities()) |
| .boxed().collect(Collectors.toList()); |
| newImmutableCapabilities.removeAll(MUTABLE_CAPABILITIES); |
| return oldImmutableCapabilities.size() != newImmutableCapabilities.size() |
| || !oldImmutableCapabilities.containsAll(newImmutableCapabilities); |
| } |
| |
| /** |
| * Update the network capabilities. |
| */ |
| private void updateNetworkCapabilities() { |
| final NetworkCapabilities.Builder builder = new NetworkCapabilities.Builder() |
| .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR); |
| boolean roaming = mPhone.getServiceState().getDataRoaming(); |
| |
| builder.setNetworkSpecifier(new TelephonyNetworkSpecifier.Builder() |
| .setSubscriptionId(mSubId).build()); |
| builder.setSubscriptionIds(Collections.singleton(mSubId)); |
| |
| ApnSetting apnSetting = mDataProfile.getApnSetting(); |
| |
| if (apnSetting != null) { |
| apnSetting.getApnTypes().stream() |
| .map(DataUtils::apnTypeToNetworkCapability) |
| .filter(cap -> cap >= 0) |
| .forEach(builder::addCapability); |
| } |
| |
| // If voice call is on-going, do not change MMTEL capability, which is a immutable |
| // capability. Changing it will result in re-recreating network agent below, and the voice |
| // call will drop. Whether tearing down an IMS network or not when VoPS is lost |
| if (mPhone.getImsPhone() != null && mPhone.getImsPhone().getCallTracker().getState() |
| != PhoneConstants.State.IDLE && mNetworkCapabilities != null |
| && mNetworkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_MMTEL)) { |
| // Previous capability has MMTEL, so add it again. |
| builder.addCapability(NetworkCapabilities.NET_CAPABILITY_MMTEL); |
| } else { |
| // Always add MMTEL capability on IMS network unless network explicitly indicates VoPS |
| // not supported. |
| if (mDataProfile.canSatisfy(NetworkCapabilities.NET_CAPABILITY_IMS)) { |
| builder.addCapability(NetworkCapabilities.NET_CAPABILITY_MMTEL); |
| if (mTransport == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) { |
| NetworkRegistrationInfo nri = mPhone.getServiceStateTracker().getServiceState() |
| .getNetworkRegistrationInfo(NetworkRegistrationInfo.DOMAIN_PS, |
| AccessNetworkConstants.TRANSPORT_TYPE_WWAN); |
| if (nri != null) { |
| DataSpecificRegistrationInfo dsri = nri.getDataSpecificInfo(); |
| // Check if VoPS is supported by the network. |
| if (dsri != null && dsri.getVopsSupportInfo() != null |
| && !dsri.getVopsSupportInfo().isVopsSupported()) { |
| builder.removeCapability(NetworkCapabilities.NET_CAPABILITY_MMTEL); |
| } |
| } |
| } |
| } |
| } |
| |
| // Extract network capabilities from the traffic descriptor. |
| for (TrafficDescriptor trafficDescriptor : mTrafficDescriptors) { |
| try { |
| OsAppId osAppId = new OsAppId(trafficDescriptor.getOsAppId()); |
| if (!osAppId.getOsId().equals(OsAppId.ANDROID_OS_ID)) { |
| loge("Received non-Android OS id " + osAppId.getOsId()); |
| continue; |
| } |
| int networkCapability = DataUtils.getNetworkCapabilityFromString( |
| osAppId.getAppId()); |
| switch (networkCapability) { |
| case NetworkCapabilities.NET_CAPABILITY_ENTERPRISE: |
| builder.addCapability(networkCapability); |
| // Enterprise is the only capability supporting differentiator. |
| if (networkCapability == NetworkCapabilities.NET_CAPABILITY_ENTERPRISE) { |
| builder.addEnterpriseId(osAppId.getDifferentiator()); |
| } |
| break; |
| case NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_LATENCY: |
| case NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_BANDWIDTH: |
| case NetworkCapabilities.NET_CAPABILITY_CBS: |
| builder.addCapability(networkCapability); |
| break; |
| default: |
| loge("Invalid app id " + osAppId.getAppId()); |
| } |
| } catch (Exception e) { |
| loge("Exception: " + e + ". Failed to create osAppId from " |
| + new BigInteger(1, trafficDescriptor.getOsAppId()).toString(16)); |
| } |
| } |
| |
| if (!mCongested) { |
| builder.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED); |
| } |
| |
| if (mTempNotMeteredSupported && mTempNotMetered) { |
| builder.addCapability(NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED); |
| } |
| |
| // Always start with NOT_VCN_MANAGED, then remove if VcnManager indicates this is part of a |
| // VCN. |
| builder.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED); |
| if (isVcnManaged(builder.build())) { |
| builder.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED); |
| } |
| |
| if (!roaming) { |
| builder.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING); |
| } |
| |
| if (!mSuspended) { |
| builder.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED); |
| } |
| |
| if (mCarrierServicePackageUid != Process.INVALID_UID |
| && ArrayUtils.contains(mAdministratorUids, mCarrierServicePackageUid)) { |
| builder.setOwnerUid(mCarrierServicePackageUid); |
| builder.setAllowedUids(Collections.singleton(mCarrierServicePackageUid)); |
| } |
| builder.setAdministratorUids(mAdministratorUids); |
| |
| Set<Integer> meteredCapabilities = mDataConfigManager |
| .getMeteredNetworkCapabilities(roaming).stream() |
| .filter(cap -> mAccessNetworksManager.getPreferredTransportByNetworkCapability(cap) |
| == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) |
| .collect(Collectors.toSet()); |
| boolean unmeteredNetwork = meteredCapabilities.stream().noneMatch( |
| Arrays.stream(builder.build().getCapabilities()).boxed() |
| .collect(Collectors.toSet())::contains); |
| |
| if (unmeteredNetwork) { |
| builder.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED); |
| } |
| |
| // Always start with not-restricted, and then remove if needed. |
| builder.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED); |
| |
| // When data is disabled, or data roaming is disabled and the device is roaming, we need |
| // to remove certain capabilities depending on scenarios. |
| if (!mDataNetworkController.getDataSettingsManager().isDataEnabled() |
| || (mPhone.getServiceState().getDataRoaming() |
| && !mDataNetworkController.getDataSettingsManager().isDataRoamingEnabled())) { |
| // If data is allowed because the request is a restricted network request, we need |
| // to mark the network as restricted when data is disabled or data roaming is disabled |
| // and the device is roaming. If we don't do that, non-privileged apps will be able |
| // to use this network when data is disabled. |
| if (mDataAllowedReason == DataAllowedReason.RESTRICTED_REQUEST) { |
| builder.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED); |
| } else if (mDataAllowedReason == DataAllowedReason.UNMETERED_USAGE |
| || mDataAllowedReason == DataAllowedReason.MMS_REQUEST) { |
| // If data is allowed due to unmetered usage, or MMS always-allowed, we need to |
| // remove unrelated-but-metered capabilities. |
| for (int capability : meteredCapabilities) { |
| // 1. If it's unmetered usage, remove all metered capabilities. |
| // 2. if it's MMS always-allowed, then remove all metered capabilities but MMS. |
| if (capability != NetworkCapabilities.NET_CAPABILITY_MMS |
| || mDataAllowedReason != DataAllowedReason.MMS_REQUEST) { |
| builder.removeCapability(capability); |
| } |
| } |
| } |
| } |
| |
| // If one of the capabilities are for special use, for example, IMS, CBS, then this |
| // network should be restricted, regardless data is enabled or not. |
| if (NetworkCapabilitiesUtils.inferRestrictedCapability(builder.build())) { |
| builder.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED); |
| } |
| |
| // Set the bandwidth information. |
| builder.setLinkDownstreamBandwidthKbps(mNetworkBandwidth.downlinkBandwidthKbps); |
| builder.setLinkUpstreamBandwidthKbps(mNetworkBandwidth.uplinkBandwidthKbps); |
| |
| NetworkCapabilities nc = builder.build(); |
| if (mNetworkCapabilities == null || mNetworkAgent == null) { |
| // This is the first time when network capabilities is created. The agent is not created |
| // at this time. Just return here. The network capabilities will be used when network |
| // agent is created. |
| mNetworkCapabilities = nc; |
| return; |
| } |
| |
| if (!nc.equals(mNetworkCapabilities)) { |
| // Check if we are changing the immutable capabilities. Note that we should be very |
| // careful and limit the use cases of changing immutable capabilities. Connectivity |
| // service would not close sockets for clients if a network request becomes |
| // unsatisfiable. |
| if (mEverConnected && areImmutableCapabilitiesChanged(mNetworkCapabilities, nc) |
| && (isConnected() || isHandoverInProgress())) { |
| // Before connectivity service supports making all capabilities mutable, it is |
| // suggested to de-register and re-register the network agent if it is needed to |
| // add/remove immutable capabilities. |
| logl("updateNetworkCapabilities: Immutable capabilities changed. Re-create the " |
| + "network agent. Attempted to change from " + mNetworkCapabilities + " to " |
| + nc); |
| // Abandon the network agent because we are going to create a new one. |
| mNetworkAgent.abandon(); |
| // Update the capabilities first so the new network agent would be created with the |
| // new capabilities. |
| mNetworkCapabilities = nc; |
| mNetworkAgent = createNetworkAgent(); |
| mNetworkAgent.markConnected(); |
| } else { |
| // Now we need to inform connectivity service and data network controller |
| // about the capabilities changed. |
| mNetworkCapabilities = nc; |
| mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities); |
| } |
| |
| removeUnsatisfiedNetworkRequests(); |
| mDataNetworkCallback.invokeFromExecutor(() -> mDataNetworkCallback |
| .onNetworkCapabilitiesChanged(DataNetwork.this)); |
| } |
| } |
| |
| /** |
| * @return The network capabilities of this data network. |
| */ |
| public @NonNull NetworkCapabilities getNetworkCapabilities() { |
| return mNetworkCapabilities; |
| } |
| |
| /** |
| * @return The link properties of this data network. |
| */ |
| public @NonNull LinkProperties getLinkProperties() { |
| return mLinkProperties; |
| } |
| |
| /** |
| * @return The data profile of this data network. |
| */ |
| public @NonNull DataProfile getDataProfile() { |
| return mDataProfile; |
| } |
| |
| /** |
| * Once RIL Data Radio Technology changes, the new radio technology will be returned in |
| * AsyncResult. |
| * See |
| * {@link com.android.internal.telephony.ServiceStateTracker#registerForDataRegStateOrRatChanged} |
| * |
| * @param ar RegistrationInfo: {@code Pair(drs, rat)} |
| */ |
| private void updateDataCallSessionStatsOfDrsOrRatChange(AsyncResult ar) { |
| Pair<Integer, Integer> drsRatPair = (Pair<Integer, Integer>) ar.result; |
| mDataCallSessionStats.onDrsOrRatChanged(drsRatPair.second); |
| } |
| |
| /** |
| * Update data suspended state. |
| */ |
| private void updateSuspendState() { |
| if (isConnecting() || isDisconnected()) { |
| // Return if not in the right state. |
| return; |
| } |
| |
| boolean newSuspendedState = false; |
| // Get the uncombined service state directly. |
| NetworkRegistrationInfo nri = mPhone.getServiceStateTracker().getServiceState() |
| .getNetworkRegistrationInfo(NetworkRegistrationInfo.DOMAIN_PS, mTransport); |
| if (nri == null) { |
| loge("Can't get network registration info for " |
| + AccessNetworkConstants.transportTypeToString(mTransport)); |
| return; |
| } |
| |
| // Never set suspended for emergency apn. Emergency data connection |
| // can work while device is not in service. |
| if (mNetworkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_EIMS)) { |
| newSuspendedState = false; |
| // If we are not in service, change to suspended. |
| } else if (nri.getRegistrationState() |
| != NetworkRegistrationInfo.REGISTRATION_STATE_HOME |
| && nri.getRegistrationState() |
| != NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING) { |
| newSuspendedState = true; |
| // Check voice/data concurrency. |
| } else if (!mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed() |
| && mTransport == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) { |
| newSuspendedState = mPhone.getCallTracker().getState() != PhoneConstants.State.IDLE; |
| } |
| |
| // Only notify when there is a change. |
| if (mSuspended != newSuspendedState) { |
| mSuspended = newSuspendedState; |
| logl("Network becomes " + (mSuspended ? "suspended" : "unsuspended")); |
| // To update NOT_SUSPENDED capability. |
| updateNetworkCapabilities(); |
| notifyPreciseDataConnectionState(); |
| mDataNetworkCallback.invokeFromExecutor(() -> |
| mDataNetworkCallback.onSuspendedStateChanged(DataNetwork.this, mSuspended)); |
| } |
| } |
| |
| /** |
| * Allocate PDU session ID from the modem. This is only needed when the data network is |
| * initiated on IWLAN. |
| */ |
| private void allocatePduSessionId() { |
| mRil.allocatePduSessionId(obtainMessage(EVENT_ALLOCATE_PDU_SESSION_ID_RESPONSE)); |
| } |
| |
| /** |
| * Setup a data network. |
| */ |
| private void setupData() { |
| int dataNetworkType = getDataNetworkType(); |
| |
| // We need to use the actual modem roaming state instead of the framework roaming state |
| // here. This flag is only passed down to ril_service for picking the correct protocol (for |
| // old modem backward compatibility). |
| boolean isModemRoaming = mPhone.getServiceState().getDataRoamingFromRegistration(); |
| |
| // Set this flag to true if the user turns on data roaming. Or if we override the roaming |
| // state in framework, we should set this flag to true as well so the modem will not reject |
| // the data call setup (because the modem actually thinks the device is roaming). |
| boolean allowRoaming = mPhone.getDataRoamingEnabled() |
| || (isModemRoaming && (!mPhone.getServiceState().getDataRoaming() |
| /*|| isUnmeteredUseOnly()*/)); |
| |
| TrafficDescriptor trafficDescriptor = mDataProfile.getTrafficDescriptor(); |
| final boolean matchAllRuleAllowed = trafficDescriptor == null |
| || !TextUtils.isEmpty(trafficDescriptor.getDataNetworkName()); |
| |
| int accessNetwork = DataUtils.networkTypeToAccessNetworkType(dataNetworkType); |
| |
| mDataServiceManagers.get(mTransport) |
| .setupDataCall(accessNetwork, mDataProfile, isModemRoaming, allowRoaming, |
| DataService.REQUEST_REASON_NORMAL, null, mPduSessionId, null, |
| trafficDescriptor, matchAllRuleAllowed, |
| obtainMessage(EVENT_SETUP_DATA_NETWORK_RESPONSE)); |
| |
| int apnTypeBitmask = mDataProfile.getApnSetting() != null |
| ? mDataProfile.getApnSetting().getApnTypeBitmask() : ApnSetting.TYPE_NONE; |
| mDataCallSessionStats.onSetupDataCall(apnTypeBitmask); |
| |
| logl("setupData: accessNetwork=" |
| + AccessNetworkType.toString(accessNetwork) + ", " + mDataProfile |
| + ", isModemRoaming=" + isModemRoaming + ", allowRoaming=" + allowRoaming |
| + ", PDU session id=" + mPduSessionId + ", matchAllRuleAllowed=" |
| + matchAllRuleAllowed); |
| TelephonyMetrics.getInstance().writeSetupDataCall(mPhone.getPhoneId(), |
| ServiceState.networkTypeToRilRadioTechnology(dataNetworkType), |
| mDataProfile.getProfileId(), mDataProfile.getApn(), mDataProfile.getProtocolType()); |
| } |
| |
| /** |
| * Get fail cause from {@link DataCallResponse} and the result code. |
| * |
| * @param resultCode The result code returned from |
| * {@link DataServiceCallback#onSetupDataCallComplete(int, DataCallResponse)}. |
| * @param response The data call response returned from |
| * {@link DataServiceCallback#onSetupDataCallComplete(int, DataCallResponse)}. |
| * |
| * @return The fail cause. {@link DataFailCause#NONE} if succeeds. |
| */ |
| private @DataFailureCause int getFailCauseFromDataCallResponse( |
| @DataServiceCallback.ResultCode int resultCode, @Nullable DataCallResponse response) { |
| int failCause = DataFailCause.NONE; |
| switch (resultCode) { |
| case DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE: |
| failCause = DataFailCause.RADIO_NOT_AVAILABLE; |
| break; |
| case DataServiceCallback.RESULT_ERROR_BUSY: |
| case DataServiceCallback.RESULT_ERROR_TEMPORARILY_UNAVAILABLE: |
| failCause = DataFailCause.SERVICE_TEMPORARILY_UNAVAILABLE; |
| break; |
| case DataServiceCallback.RESULT_ERROR_INVALID_ARG: |
| failCause = DataFailCause.UNACCEPTABLE_NETWORK_PARAMETER; |
| break; |
| case DataServiceCallback.RESULT_ERROR_UNSUPPORTED: |
| failCause = DataFailCause.REQUEST_NOT_SUPPORTED; |
| break; |
| case DataServiceCallback.RESULT_SUCCESS: |
| if (response != null) { |
| failCause = DataFailCause.getFailCause(response.getCause()); |
| } |
| break; |
| } |
| return failCause; |
| } |
| |
| /** |
| * Update data network based on the latest {@link DataCallResponse}. |
| * |
| * @param response The data call response from data service. |
| */ |
| private void updateDataNetwork(@NonNull DataCallResponse response) { |
| mCid.put(mTransport, response.getId()); |
| LinkProperties linkProperties = new LinkProperties(); |
| |
| // Set interface name |
| linkProperties.setInterfaceName(response.getInterfaceName()); |
| |
| // Set PDU session id |
| if (mPduSessionId != response.getPduSessionId()) { |
| mPduSessionId = response.getPduSessionId(); |
| log("PDU session id updated to " + mPduSessionId); |
| } |
| |
| // Set the link status |
| if (mLinkStatus != response.getLinkStatus()) { |
| mLinkStatus = response.getLinkStatus(); |
| log("Link status updated to " + DataUtils.linkStatusToString(mLinkStatus)); |
| mDataNetworkCallback.invokeFromExecutor( |
| () -> mDataNetworkCallback.onLinkStatusChanged(DataNetwork.this, mLinkStatus)); |
| } |
| |
| // Set link addresses |
| if (response.getAddresses().size() > 0) { |
| for (LinkAddress la : response.getAddresses()) { |
| if (!la.getAddress().isAnyLocalAddress()) { |
| logv("addr/pl=" + la.getAddress() + "/" + la.getPrefixLength()); |
| linkProperties.addLinkAddress(la); |
| } |
| } |
| } else { |
| loge("no address for ifname=" + response.getInterfaceName()); |
| } |
| |
| // Set DNS servers |
| if (response.getDnsAddresses().size() > 0) { |
| for (InetAddress dns : response.getDnsAddresses()) { |
| if (!dns.isAnyLocalAddress()) { |
| linkProperties.addDnsServer(dns); |
| } |
| } |
| } else { |
| loge("Empty dns response"); |
| } |
| |
| // Set PCSCF |
| if (response.getPcscfAddresses().size() > 0) { |
| for (InetAddress pcscf : response.getPcscfAddresses()) { |
| linkProperties.addPcscfServer(pcscf); |
| } |
| } |
| |
| // For backwards compatibility, use getMtu() if getMtuV4() is not available. |
| int mtuV4 = response.getMtuV4() > 0 ? response.getMtuV4() : response.getMtu(); |
| |
| if (mtuV4 <= 0) { |
| // Use back up value from data profile. |
| if (mDataProfile.getApnSetting() != null) { |
| mtuV4 = mDataProfile.getApnSetting().getMtuV4(); |
| } |
| if (mtuV4 <= 0) { |
| mtuV4 = DEFAULT_MTU_V4; |
| } |
| } |
| |
| // For backwards compatibility, use getMtu() if getMtuV6() is not available. |
| int mtuV6 = response.getMtuV6() > 0 ? response.getMtuV6() : response.getMtu(); |
| if (mtuV6 <= 0) { |
| // Use back up value from data profile. |
| if (mDataProfile.getApnSetting() != null) { |
| mtuV6 = mDataProfile.getApnSetting().getMtuV6(); |
| } |
| if (mtuV6 <= 0) { |
| mtuV6 = DEFAULT_MTU_V6; |
| } |
| } |
| |
| // Set MTU for each route. |
| for (InetAddress gateway : response.getGatewayAddresses()) { |
| int mtu = gateway instanceof java.net.Inet6Address ? mtuV6 : mtuV4; |
| linkProperties.addRoute(new RouteInfo(null, gateway, null, |
| RouteInfo.RTN_UNICAST, mtu)); |
| } |
| |
| // LinkProperties.setMtu should be deprecated. The mtu for each route has been already |
| // provided in addRoute() above. For backwards compatibility, we still need to provide |
| // a value for the legacy MTU. Use the lower value of v4 and v6 value here. |
| linkProperties.setMtu(Math.min(mtuV4, mtuV6)); |
| |
| if (mDataProfile.getApnSetting() != null |
| && !TextUtils.isEmpty(mDataProfile.getApnSetting().getProxyAddressAsString())) { |
| int port = mDataProfile.getApnSetting().getProxyPort(); |
| if (port == -1) { |
| port = 8080; |
| } |
| ProxyInfo proxy = ProxyInfo.buildDirectProxy( |
| mDataProfile.getApnSetting().getProxyAddressAsString(), port); |
| linkProperties.setHttpProxy(proxy); |
| } |
| |
| linkProperties.setTcpBufferSizes(mTcpBufferSizes); |
| |
| mNetworkSliceInfo = response.getSliceInfo(); |
| |
| mTrafficDescriptors.clear(); |
| mTrafficDescriptors.addAll(response.getTrafficDescriptors()); |
| |
| mQosBearerSessions.clear(); |
| mQosBearerSessions.addAll(response.getQosBearerSessions()); |
| if (mQosCallbackTracker != null) { |
| mQosCallbackTracker.updateSessions(mQosBearerSessions); |
| } |
| |
| if (!linkProperties.equals(mLinkProperties)) { |
| mLinkProperties = linkProperties; |
| log("sendLinkProperties " + mLinkProperties); |
| mNetworkAgent.sendLinkProperties(mLinkProperties); |
| } |
| |
| updateNetworkCapabilities(); |
| } |
| |
| /** |
| * @return {@code true} if the device is connected to NR cell in 5G NSA mode, and the current |
| * data network is using the NR cell. |
| */ |
| private boolean isNrConnected() { |
| return mPhone.getServiceState().getNrState() == NetworkRegistrationInfo.NR_STATE_CONNECTED |
| && mPhone.getServiceStateTracker().getNrContextIds().contains( |
| mCid.get(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)); |
| } |
| |
| /** |
| * Get the TCP buffer sizes config string. |
| * |
| * @return The TCP buffer sizes config used in {@link LinkProperties#setTcpBufferSizes(String)}. |
| */ |
| private @Nullable String getTcpConfig() { |
| ServiceState ss = mPhone.getServiceState(); |
| NetworkRegistrationInfo nrs = ss.getNetworkRegistrationInfo( |
| NetworkRegistrationInfo.DOMAIN_PS, mTransport); |
| int networkType = TelephonyManager.NETWORK_TYPE_UNKNOWN; |
| if (nrs != null) { |
| networkType = nrs.getAccessNetworkTechnology(); |
| if (networkType == TelephonyManager.NETWORK_TYPE_LTE |
| && nrs.isUsingCarrierAggregation()) { |
| // Although LTE_CA is not a real RAT, but since LTE CA generally has higher speed |
| // we use LTE_CA to get a different TCP config for LTE CA. |
| networkType = TelephonyManager.NETWORK_TYPE_LTE_CA; |
| } |
| } |
| return mDataConfigManager.getTcpConfigString(networkType, ss); |
| } |
| |
| /** |
| * Called when receiving setup data network response from the data service. |
| * |
| * @param resultCode The result code. |
| * @param response The response. |
| */ |
| private void onSetupResponse(@DataServiceCallback.ResultCode int resultCode, |
| @Nullable DataCallResponse response) { |
| logl("onSetupResponse: resultCode=" + DataServiceCallback.resultCodeToString(resultCode) |
| + ", response=" + response); |
| mFailCause = getFailCauseFromDataCallResponse(resultCode, response); |
| if (mFailCause == DataFailCause.NONE) { |
| updateDataNetwork(response); |
| |
| // TODO: Check if the cid already exists. If yes, should notify DNC and let it force |
| // attach the network requests to that existing data network. |
| |
| // TODO: Evaluate all network requests and see if each request still can be satisfied. |
| // For requests that can't be satisfied anymore, we need to put them back to the |
| // unsatisfied pool. If none of network requests can be satisfied, then there is no |
| // need to mark network agent connected. Just silently deactivate the data network. |
| if (mAttachedNetworkRequestList.size() == 0) { |
| log("Tear down the network since there is no live network request."); |
| // Directly call onTearDown here. Calling tearDown will cause deadlock because |
| // EVENT_TEAR_DOWN_NETWORK is deferred until state machine enters connected state, |
| // which will never happen in this case. |
| onTearDown(TEAR_DOWN_REASON_NO_LIVE_REQUEST); |
| return; |
| } |
| |
| if (mVcnManager != null && mVcnManager.applyVcnNetworkPolicy(mNetworkCapabilities, |
| mLinkProperties).isTeardownRequested()) { |
| log("VCN service requested to tear down the network."); |
| // Directly call onTearDown here. Calling tearDown will cause deadlock because |
| // EVENT_TEAR_DOWN_NETWORK is deferred until state machine enters connected state, |
| // which will never happen in this case. |
| onTearDown(TEAR_DOWN_REASON_VCN_REQUESTED); |
| return; |
| } |
| |
| transitionTo(mConnectedState); |
| } else { |
| // Setup data failed. |
| long retryDelayMillis = response != null ? response.getRetryDurationMillis() |
| : DataCallResponse.RETRY_DURATION_UNDEFINED; |
| NetworkRequestList requestList = new NetworkRequestList(mAttachedNetworkRequestList); |
| mDataNetworkCallback.invokeFromExecutor(() |
| -> mDataNetworkCallback.onSetupDataFailed( |
| DataNetwork.this, requestList, mFailCause, retryDelayMillis)); |
| transitionTo(mDisconnectedState); |
| } |
| |
| int apnTypeBitmask = ApnSetting.TYPE_NONE; |
| int protocol = ApnSetting.PROTOCOL_UNKNOWN; |
| if (mDataProfile.getApnSetting() != null) { |
| apnTypeBitmask = mDataProfile.getApnSetting().getApnTypeBitmask(); |
| protocol = mDataProfile.getApnSetting().getProtocol(); |
| } |
| mDataCallSessionStats.onSetupDataCallResponse(response, |
| getDataNetworkType(), |
| apnTypeBitmask, |
| protocol, |
| mFailCause); |
| } |
| |
| /** |
| * Called when receiving deactivate data network response from the data service. |
| * |
| * @param resultCode The result code. |
| */ |
| private void onDeactivateResponse(@DataServiceCallback.ResultCode int resultCode) { |
| logl("onDeactivateResponse: resultCode=" |
| + DataServiceCallback.resultCodeToString(resultCode)); |
| if (resultCode == DataServiceCallback.RESULT_ERROR_RADIO_NOT_AVAILABLE) { |
| log("Remove network since deactivate request returned RADIO_NOT_AVAILABLE."); |
| mDataNetworkCallback.invokeFromExecutor( |
| () -> mDataNetworkCallback.onDisconnected( |
| DataNetwork.this, DataFailCause.RADIO_NOT_AVAILABLE)); |
| transitionTo(mDisconnectedState); |
| } |
| } |
| |
| /** |
| * Tear down the data network immediately. |
| * |
| * @param reason The reason of tearing down the network. |
| */ |
| public void tearDown(@TearDownReason int reason) { |
| if (getCurrentState() == null || isDisconnected()) { |
| return; |
| } |
| sendMessage(obtainMessage(EVENT_TEAR_DOWN_NETWORK, reason)); |
| } |
| |
| private void onTearDown(@TearDownReason int reason) { |
| logl("onTearDown: reason=" + tearDownReasonToString(reason)); |
| if (mDataConfigManager.isImsDelayTearDownEnabled() |
| && mNetworkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_IMS) |
| && reason == TEAR_DOWN_REASON_CONNECTIVITY_SERVICE_UNWANTED |
| && mPhone.getImsPhone() != null |
| && mPhone.getImsPhone().getCallTracker().getState() != PhoneConstants.State.IDLE) { |
| logl("onTearDown: Delay IMS tear down until call ends."); |
| return; |
| } |
| |
| // TODO: Need to support DataService.REQUEST_REASON_SHUTDOWN |
| mDataServiceManagers.get(mTransport).deactivateDataCall(mCid.get(mTransport), |
| DataService.REQUEST_REASON_NORMAL, |
| obtainMessage(EVENT_DEACTIVATE_DATA_NETWORK_RESPONSE)); |
| mDataCallSessionStats.setDeactivateDataCallReason(DataService.REQUEST_REASON_NORMAL); |
| mInvokedDataDeactivation = true; |
| } |
| |
| /** |
| * Tear down the data network when condition is met or timed out. Data network will enter |
| * {@link DisconnectingState} immediately and waiting for condition met. When condition is met, |
| * {@link DataNetworkController} should invoke {@link Consumer#accept(Object)} so the actual |
| * tear down work can be performed. |
| * |
| * This is primarily used for IMS graceful tear down. {@link DataNetworkController} inform |
| * {@link DataNetwork} to enter {@link DisconnectingState}. IMS service can observe this |
| * through {@link PreciseDataConnectionState#getState()} and then perform IMS de-registration |
| * work. After IMS de-registered, {@link DataNetworkController} informs {@link DataNetwork} |
| * that it's okay to tear down the network. |
| * |
| * @param reason The tear down reason. |
| * |
| * @param timeoutMillis Timeout in milliseconds. Within the time window, clients will have to |
| * call {@link Consumer#accept(Object)}, otherwise, data network will be torn down when |
| * timed out. |
| * |
| * @return The runnable for client to execute when condition is met. When executed, tear down |
| * will be performed. {@code null} if the data network is already disconnected or being |
| * disconnected. |
| */ |
| public @Nullable Runnable tearDownWithCondition(@TearDownReason int reason, |
| long timeoutMillis) { |
| if (getCurrentState() == null || isDisconnected() || isDisconnecting()) { |
| loge("tearDownGracefully: Not in the right state. State=" + getCurrentState()); |
| return null; |
| } |
| logl("tearDownWithCondition: reason=" + tearDownReasonToString(reason) + ", timeout=" |
| + timeoutMillis + "ms."); |
| sendMessageDelayed(EVENT_TEAR_DOWN_NETWORK, reason, timeoutMillis); |
| return () -> this.tearDown(reason); |
| } |
| |
| /** |
| * Called when receiving {@link DataServiceCallback#onDataCallListChanged(List)} from the data |
| * service. |
| * |
| * @param transport The transport where this event from. |
| * @param responseList The data call response list. |
| */ |
| private void onDataStateChanged(@TransportType int transport, |
| @NonNull List<DataCallResponse> responseList) { |
| // Ignore the update if it's not from the data service on the right transport. |
| // Also if never received data call response from setup call response, which updates the |
| // cid, ignore the update here. |
| logv("onDataStateChanged: " + responseList); |
| if (transport != mTransport || mCid.get(mTransport) == INVALID_CID || isDisconnected()) { |
| return; |
| } |
| |
| DataCallResponse response = responseList.stream() |
| .filter(r -> mCid.get(mTransport) == r.getId()) |
| .findFirst() |
| .orElse(null); |
| if (response != null) { |
| if (!response.equals(mDataCallResponse)) { |
| log("onDataStateChanged: " + response); |
| mDataCallResponse = response; |
| if (response.getLinkStatus() != DataCallResponse.LINK_STATUS_INACTIVE) { |
| updateDataNetwork(response); |
| } else { |
| log("onDataStateChanged: PDN inactive reported by " |
| + AccessNetworkConstants.transportTypeToString(mTransport) |
| + " data service."); |
| if (mEverConnected) { |
| mDataNetworkCallback.invokeFromExecutor( |
| () -> mDataNetworkCallback.onDisconnected( |
| DataNetwork.this, response.getCause())); |
| } else { |
| log("onDataStateChanged: never in connected state. Treated as a setup " |
| + "failure."); |
| mDataNetworkCallback.invokeFromExecutor(() -> mDataNetworkCallback |
| .onSetupDataFailed(DataNetwork.this, mAttachedNetworkRequestList, |
| DataFailCause.NO_RETRY_FAILURE, |
| DataCallResponse.RETRY_DURATION_UNDEFINED)); |
| } |
| transitionTo(mDisconnectedState); |
| } |
| } |
| } else { |
| // The data call response is missing from the list. This means the PDN is gone. This |
| // is the PDN lost reported by the modem. We don't send another DEACTIVATE_DATA request |
| // for that |
| log("onDataStateChanged: PDN disconnected reported by " |
| + AccessNetworkConstants.transportTypeToString(mTransport) + " data service."); |
| if (mEverConnected) { |
| mDataNetworkCallback.invokeFromExecutor(() -> mDataNetworkCallback |
| .onDisconnected(DataNetwork.this, DataFailCause.LOST_CONNECTION)); |
| } else { |
| log("onDataStateChanged: never in connected state. Treated as a setup failure."); |
| mDataNetworkCallback.invokeFromExecutor(() -> mDataNetworkCallback |
| .onSetupDataFailed(DataNetwork.this, mAttachedNetworkRequestList, |
| DataFailCause.NO_RETRY_FAILURE, |
| DataCallResponse.RETRY_DURATION_UNDEFINED)); |
| } |
| transitionTo(mDisconnectedState); |
| } |
| } |
| |
| /** |
| * Called when data config updated. |
| */ |
| private void onDataConfigUpdated() { |
| log("onDataConfigUpdated"); |
| |
| updateBandwidthFromDataConfig(); |
| updateTcpBufferSizes(); |
| updateMeteredAndCongested(); |
| } |
| |
| /** |
| * Called when receiving bandwidth update from the modem. |
| * |
| * @param linkCapacityEstimates The link capacity estimate list from the modem. |
| */ |
| private void onBandwidthUpdatedFromModem( |
| @NonNull List<LinkCapacityEstimate> linkCapacityEstimates) { |
| Objects.requireNonNull(linkCapacityEstimates); |
| if (linkCapacityEstimates.isEmpty()) return; |
| |
| int uplinkBandwidthKbps = 0, downlinkBandwidthKbps = 0; |
| for (LinkCapacityEstimate linkCapacityEstimate : linkCapacityEstimates) { |
| if (linkCapacityEstimate.getType() == LinkCapacityEstimate.LCE_TYPE_COMBINED) { |
| uplinkBandwidthKbps = linkCapacityEstimate.getUplinkCapacityKbps(); |
| downlinkBandwidthKbps = linkCapacityEstimate.getDownlinkCapacityKbps(); |
| break; |
| } else if (linkCapacityEstimate.getType() == LinkCapacityEstimate.LCE_TYPE_PRIMARY |
| || linkCapacityEstimate.getType() == LinkCapacityEstimate.LCE_TYPE_SECONDARY) { |
| uplinkBandwidthKbps += linkCapacityEstimate.getUplinkCapacityKbps(); |
| downlinkBandwidthKbps += linkCapacityEstimate.getDownlinkCapacityKbps(); |
| } else { |
| loge("Invalid LinkCapacityEstimate type " + linkCapacityEstimate.getType()); |
| } |
| } |
| onBandwidthUpdated(uplinkBandwidthKbps, downlinkBandwidthKbps); |
| } |
| |
| /** |
| * Called when bandwidth estimation updated from either modem or the bandwidth estimator. |
| * |
| * @param uplinkBandwidthKbps Uplink bandwidth estimate in Kbps. |
| * @param downlinkBandwidthKbps Downlink bandwidth estimate in Kbps. |
| */ |
| private void onBandwidthUpdated(int uplinkBandwidthKbps, int downlinkBandwidthKbps) { |
| log("onBandwidthUpdated: downlinkBandwidthKbps=" + downlinkBandwidthKbps |
| + ", uplinkBandwidthKbps=" + uplinkBandwidthKbps); |
| NetworkBandwidth bandwidthFromConfig = mDataConfigManager.getBandwidthForNetworkType( |
| getDataNetworkType(), mPhone.getServiceState()); |
| |
| if (downlinkBandwidthKbps == LinkCapacityEstimate.INVALID && bandwidthFromConfig != null) { |
| // Fallback to carrier config. |
| downlinkBandwidthKbps = bandwidthFromConfig.downlinkBandwidthKbps; |
| } |
| |
| if (uplinkBandwidthKbps == LinkCapacityEstimate.INVALID && bandwidthFromConfig != null) { |
| // Fallback to carrier config. |
| uplinkBandwidthKbps = bandwidthFromConfig.uplinkBandwidthKbps; |
| } |
| |
| // Make sure uplink is not greater than downlink. |
| uplinkBandwidthKbps = Math.min(uplinkBandwidthKbps, downlinkBandwidthKbps); |
| mNetworkBandwidth = new NetworkBandwidth(downlinkBandwidthKbps, uplinkBandwidthKbps); |
| |
| updateNetworkCapabilities(); |
| } |
| |
| /** |
| * Called when display info changed. This can happen when network types changed or override |
| * types (5G NSA, 5G MMWAVE) changes. |
| */ |
| private void onDisplayInfoChanged() { |
| updateBandwidthFromDataConfig(); |
| updateTcpBufferSizes(); |
| updateMeteredAndCongested(); |
| } |
| |
| /** |
| * Update the bandwidth from carrier config. Note this is no-op if the bandwidth source is not |
| * carrier config. |
| */ |
| private void updateBandwidthFromDataConfig() { |
| if (mDataConfigManager.getBandwidthEstimateSource() != BANDWIDTH_SOURCE_CARRIER_CONFIG) { |
| return; |
| } |
| log("updateBandwidthFromDataConfig"); |
| mNetworkBandwidth = mDataConfigManager.getBandwidthForNetworkType( |
| getDataNetworkType(), mPhone.getServiceState()); |
| updateNetworkCapabilities(); |
| } |
| |
| /** |
| * Update the TCP buffer sizes from resource overlays. |
| */ |
| private void updateTcpBufferSizes() { |
| log("updateTcpBufferSizes"); |
| mTcpBufferSizes = getTcpConfig(); |
| LinkProperties linkProperties = new LinkProperties(mLinkProperties); |
| linkProperties.setTcpBufferSizes(mTcpBufferSizes); |
| if (!linkProperties.equals(mLinkProperties)) { |
| mLinkProperties = linkProperties; |
| log("sendLinkProperties " + mLinkProperties); |
| mNetworkAgent.sendLinkProperties(mLinkProperties); |
| } |
| } |
| |
| /** |
| * Update the metered and congested values from carrier configs and subscription overrides |
| */ |
| private void updateMeteredAndCongested() { |
| int networkType = isNrConnected() ? TelephonyManager.NETWORK_TYPE_NR : getDataNetworkType(); |
| log("updateMeteredAndCongested: networkType=" |
| + TelephonyManager.getNetworkTypeName(networkType)); |
| boolean changed = false; |
| if (mDataConfigManager.isTempNotMeteredSupportedByCarrier() != mTempNotMeteredSupported) { |
| mTempNotMeteredSupported = !mTempNotMeteredSupported; |
| changed = true; |
| log("updateMeteredAndCongested: mTempNotMeteredSupported changed to " |
| + mTempNotMeteredSupported); |
| } |
| if ((mDataNetworkController.getUnmeteredOverrideNetworkTypes().contains(networkType) |
| || isNetworkTypeUnmetered(networkType)) != mTempNotMetered) { |
| mTempNotMetered = !mTempNotMetered; |
| changed = true; |
| log("updateMeteredAndCongested: mTempNotMetered changed to " + mTempNotMetered); |
| } |
| if (mDataNetworkController.getCongestedOverrideNetworkTypes().contains(networkType) |
| != mCongested) { |
| mCongested = !mCongested; |
| changed = true; |
| log("updateMeteredAndCongested: mCongested changed to " + mCongested); |
| } |
| if (changed) { |
| updateNetworkCapabilities(); |
| } |
| } |
| |
| /** |
| * Get whether the network type is unmetered from SubscriptionPlans, from either an unmetered |
| * general plan or specific plan for the given network type. |
| * |
| * @param networkType The network type to check meteredness for |
| * @return Whether the given network type is unmetered based on SubscriptionPlans |
| */ |
| private boolean isNetworkTypeUnmetered(@NetworkType int networkType) { |
| List<SubscriptionPlan> plans = mDataNetworkController.getSubscriptionPlans(); |
| if (plans.isEmpty()) return false; |
| boolean isGeneralUnmetered = true; |
| Set<Integer> allNetworkTypes = Arrays.stream(TelephonyManager.getAllNetworkTypes()) |
| .boxed().collect(Collectors.toSet()); |
| for (SubscriptionPlan plan : plans) { |
| // Check if plan is general (applies to all network types) or specific |
| if (Arrays.stream(plan.getNetworkTypes()).boxed().collect(Collectors.toSet()) |
| .containsAll(allNetworkTypes)) { |
| if (plan.getDataLimitBytes() != SubscriptionPlan.BYTES_UNLIMITED) { |
| // Metered takes precedence over unmetered for safety |
| isGeneralUnmetered = false; |
| } |
| } else { |
| // Check if plan applies to given network type |
| if (networkType != TelephonyManager.NETWORK_TYPE_UNKNOWN) { |
| for (int planNetworkType : plan.getNetworkTypes()) { |
| if (planNetworkType == networkType) { |
| return plan.getDataLimitBytes() == SubscriptionPlan.BYTES_UNLIMITED; |
| } |
| } |
| } |
| } |
| } |
| return isGeneralUnmetered; |
| } |
| |
| /** |
| * @return The unique context id assigned by the data service in |
| * {@link DataCallResponse#getId()}. |
| */ |
| public int getId() { |
| return mCid.get(mTransport); |
| } |
| |
| /** |
| * @return The current network type reported by the network service. |
| */ |
| private @NetworkType int getDataNetworkType() { |
| return getDataNetworkType(mTransport); |
| } |
| |
| /** |
| * Get the data network type on the specified transport. |
| * |
| * @param transport The transport. |
| * @return The data network type. |
| */ |
| private @NetworkType int getDataNetworkType(@TransportType int transport) { |
| ServiceState ss = mPhone.getServiceState(); |
| NetworkRegistrationInfo nrs = ss.getNetworkRegistrationInfo( |
| NetworkRegistrationInfo.DOMAIN_PS, transport); |
| if (nrs != null) { |
| return nrs.getAccessNetworkTechnology(); |
| } |
| return TelephonyManager.NETWORK_TYPE_UNKNOWN; |
| } |
| |
| /** |
| * @return The physical link status (i.e. RRC state). |
| */ |
| public @LinkStatus int getLinkStatus() { |
| return mLinkStatus; |
| } |
| |
| /** |
| * @return The network score. The higher score of the network has higher chance to be |
| * selected by the connectivity service as active network. |
| */ |
| private @NonNull NetworkScore getNetworkScore() { |
| // If it's serving a network request that asks NET_CAPABILITY_INTERNET and doesn't have |
| // specify a sub id, this data network is considered to be default internet data |
| // connection. In this case we assign a slightly higher score of 50. The intention is |
| // it will not be replaced by other data networks accidentally in DSDS use case. |
| int score = OTHER_NETWORK_SCORE; |
| // TODO: Should update the score when attached list changed. |
| for (TelephonyNetworkRequest networkRequest : mAttachedNetworkRequestList) { |
| if (networkRequest.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) |
| && networkRequest.getNetworkSpecifier() == null) { |
| score = DEFAULT_INTERNET_NETWORK_SCORE; |
| } |
| } |
| |
| return new NetworkScore.Builder().setLegacyInt(score).build(); |
| } |
| |
| /** |
| * Get the highest priority network capability from the network request. This is used to get |
| * the representative APN-type capability for different purposes. It will never return a |
| * non-APN-type capability. |
| * |
| * @return The highest priority network capability from this network. |
| */ |
| public @NetCapability int getHighestPriorityNetworkCapability() { |
| int highestPriority = 0; |
| int highestPriorityCapability = -1; |
| for (int capability : getNetworkCapabilities().getCapabilities()) { |
| // Convert the capability to APN type. For non-APN-type capabilities, TYPE_NONE is |
| // returned. |
| int apnType = DataUtils.networkCapabilityToApnType(capability); |
| if (apnType != ApnSetting.TYPE_NONE) { |
| int priority = mDataConfigManager.getNetworkCapabilityPriority(capability); |
| if (priority > highestPriority) { |
| highestPriority = priority; |
| highestPriorityCapability = capability; |
| } |
| } |
| } |
| return highestPriorityCapability; |
| } |
| |
| /** |
| * @return The priority of the network. The priority is derived from the highest priority |
| * capability of the network. |
| */ |
| public int getPriority() { |
| return Arrays.stream(getNetworkCapabilities().getCapabilities()).boxed() |
| .map(mDataConfigManager::getNetworkCapabilityPriority) |
| .max(Integer::compare) |
| .orElse(-1); |
| } |
| |
| /** |
| * @return The attached network request list. |
| */ |
| public @NonNull NetworkRequestList getAttachedNetworkRequestList() { |
| return mAttachedNetworkRequestList; |
| } |
| |
| /** |
| * @return {@code true} if in connecting state. |
| */ |
| public boolean isConnecting() { |
| return getCurrentState() == mConnectingState; |
| } |
| |
| /** |
| * @return {@code true} if in connected state. |
| */ |
| public boolean isConnected() { |
| return getCurrentState() == mConnectedState; |
| } |
| |
| /** |
| * @return {@code true} if in disconnecting state. |
| */ |
| public boolean isDisconnecting() { |
| return getCurrentState() == mDisconnectingState; |
| } |
| |
| /** |
| * @return {@code true} if in disconnected state. |
| */ |
| public boolean isDisconnected() { |
| return getCurrentState() == mDisconnectedState; |
| } |
| |
| /** |
| * @return {@code true} if in handover state. |
| */ |
| public boolean isHandoverInProgress() { |
| return getCurrentState() == mHandoverState; |
| } |
| |
| /** |
| * @return {@code true} if the data network is suspended. |
| */ |
| public boolean isSuspended() { |
| return getState() == TelephonyManager.DATA_SUSPENDED; |
| } |
| |
| /** |
| * @return The current transport of the data network. |
| */ |
| public @TransportType int getTransport() { |
| return mTransport; |
| } |
| |
| private @DataState int getState() { |
| IState state = getCurrentState(); |
| if (state == null || isDisconnected()) { |
| return TelephonyManager.DATA_DISCONNECTED; |
| } else if (isConnecting()) { |
| return TelephonyManager.DATA_CONNECTING; |
| } else if (isConnected()) { |
| // The data connection can only be suspended when it's in active state. |
| if (mSuspended) { |
| return TelephonyManager.DATA_SUSPENDED; |
| } |
| return TelephonyManager.DATA_CONNECTED; |
| } else if (isDisconnecting()) { |
| return TelephonyManager.DATA_DISCONNECTING; |
| } else if (isHandoverInProgress()) { |
| return TelephonyManager.DATA_HANDOVER_IN_PROGRESS; |
| } |
| |
| return TelephonyManager.DATA_UNKNOWN; |
| } |
| |
| /** |
| * @return {@code true} if this data network supports internet. |
| */ |
| public boolean isInternetSupported() { |
| return mNetworkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) |
| && mNetworkCapabilities.hasCapability( |
| NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED) |
| && mNetworkCapabilities.hasCapability( |
| NetworkCapabilities.NET_CAPABILITY_TRUSTED) |
| && mNetworkCapabilities.hasCapability( |
| NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED) |
| && mNetworkCapabilities.hasCapability( |
| NetworkCapabilities.NET_CAPABILITY_NOT_VPN); |
| } |
| |
| /** |
| * Get precise data connection state |
| * |
| * @return The {@link PreciseDataConnectionState} |
| */ |
| private PreciseDataConnectionState getPreciseDataConnectionState() { |
| return new PreciseDataConnectionState.Builder() |
| .setTransportType(mTransport) |
| .setId(mCid.get(mTransport)) |
| .setState(getState()) |
| .setApnSetting(mDataProfile.getApnSetting()) |
| .setLinkProperties(mLinkProperties) |
| .setNetworkType(getDataNetworkType()) |
| .setFailCause(mFailCause) |
| .build(); |
| } |
| |
| /** |
| * Send the precise data connection state to the listener of |
| * {@link android.telephony.TelephonyCallback.PreciseDataConnectionStateListener}. |
| */ |
| private void notifyPreciseDataConnectionState() { |
| PreciseDataConnectionState pdcs = getPreciseDataConnectionState(); |
| logv("notifyPreciseDataConnectionState=" + pdcs); |
| mPhone.notifyDataConnection(pdcs); |
| } |
| |
| /** |
| * Request the data network to handover to the target transport. |
| * |
| * @param targetTransport The target transport. |
| * @param retryEntry Data handover retry entry. This would be {@code null} for first time |
| * handover attempt. |
| * @return {@code true} if the request has been accepted. |
| */ |
| public boolean startHandover(@TransportType int targetTransport, |
| @Nullable DataHandoverRetryEntry retryEntry) { |
| if (getCurrentState() == null || isDisconnected() || isDisconnecting()) { |
| // Fail the request if not in the appropriate state. |
| if (retryEntry != null) retryEntry.setState(DataRetryEntry.RETRY_STATE_CANCELLED); |
| return false; |
| } |
| sendMessage(obtainMessage(EVENT_START_HANDOVER, targetTransport, 0, retryEntry)); |
| return true; |
| } |
| |
| /** |
| * Called when handover between IWLAN and cellular is needed. |
| * |
| * @param targetTransport The target transport. |
| * @param retryEntry Data handover retry entry. This would be {@code null} for first time |
| * handover attempt. |
| */ |
| private void onStartHandover(@TransportType int targetTransport, |
| @Nullable DataHandoverRetryEntry retryEntry) { |
| if (mTransport == targetTransport) { |
| log("onStartHandover: The network is already on " |
| + AccessNetworkConstants.transportTypeToString(mTransport) |
| + ", handover is not needed."); |
| if (retryEntry != null) retryEntry.setState(DataRetryEntry.RETRY_STATE_CANCELLED); |
| return; |
| } |
| |
| // We need to use the actual modem roaming state instead of the framework roaming state |
| // here. This flag is only passed down to ril_service for picking the correct protocol (for |
| // old modem backward compatibility). |
| boolean isModemRoaming = mPhone.getServiceState().getDataRoamingFromRegistration(); |
| |
| // Set this flag to true if the user turns on data roaming. Or if we override the roaming |
| // state in framework, we should set this flag to true as well so the modem will not reject |
| // the data call setup (because the modem actually thinks the device is roaming). |
| boolean allowRoaming = mPhone.getDataRoamingEnabled() |
| || (isModemRoaming && (!mPhone.getServiceState().getDataRoaming() |
| /*|| isUnmeteredUseOnly()*/)); |
| |
| logl("Start handover from " + AccessNetworkConstants.transportTypeToString(mTransport) |
| + " to " + AccessNetworkConstants.transportTypeToString(targetTransport)); |
| // Send the handover request to the target transport data service. |
| mDataServiceManagers.get(targetTransport).setupDataCall( |
| DataUtils.networkTypeToAccessNetworkType(getDataNetworkType(targetTransport)), |
| mDataProfile, isModemRoaming, allowRoaming, |
| DataService.REQUEST_REASON_HANDOVER, mLinkProperties, mPduSessionId, |
| mNetworkSliceInfo, mDataProfile.getTrafficDescriptor(), true, |
| obtainMessage(EVENT_HANDOVER_RESPONSE, retryEntry)); |
| transitionTo(mHandoverState); |
| } |
| |
| /** |
| * Called when receiving handover response from the data service. |
| * |
| * @param resultCode The result code. |
| * @param response The response. |
| * @param retryEntry Data handover retry entry. This would be {@code null} for first time |
| * handover attempt. |
| */ |
| private void onHandoverResponse(@DataServiceCallback.ResultCode int resultCode, |
| @Nullable DataCallResponse response, @Nullable DataHandoverRetryEntry retryEntry) { |
| logl("onHandoverResponse: resultCode=" + DataServiceCallback.resultCodeToString(resultCode) |
| + ", response=" + response); |
| mFailCause = getFailCauseFromDataCallResponse(resultCode, response); |
| if (mFailCause == DataFailCause.NONE) { |
| // Handover succeeded. |
| |
| // Clean up on the source transport. |
| mDataServiceManagers.get(mTransport).deactivateDataCall(mCid.get(mTransport), |
| DataService.REQUEST_REASON_HANDOVER, |
| obtainMessage(EVENT_DEACTIVATE_DATA_NETWORK_RESPONSE)); |
| // Switch the transport to the target. |
| mTransport = DataUtils.getTargetTransport(mTransport); |
| // Update the logging tag |
| mLogTag = "DN-" + mInitialNetworkAgentId + "-" |
| + ((mTransport == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) ? "C" : "I"); |
| updateDataNetwork(response); |
| if (mTransport != AccessNetworkConstants.TRANSPORT_TYPE_WWAN) { |
| // Handover from WWAN to WLAN |
| mPcoData.clear(); |
| unregisterForWwanEvents(); |
| } else { |
| // Handover from WLAN to WWAN |
| registerForWwanEvents(); |
| } |
| if (retryEntry != null) retryEntry.setState(DataRetryEntry.RETRY_STATE_SUCCEEDED); |
| mDataNetworkCallback.invokeFromExecutor( |
| () -> mDataNetworkCallback.onHandoverSucceeded(DataNetwork.this)); |
| } else { |
| // Handover failed. |
| long retry = response != null ? response.getRetryDurationMillis() |
| : DataCallResponse.RETRY_DURATION_UNDEFINED; |
| int handoverFailureMode = response != null ? response.getHandoverFailureMode() |
| : DataCallResponse.HANDOVER_FAILURE_MODE_LEGACY; |
| if (retryEntry != null) retryEntry.setState(DataRetryEntry.RETRY_STATE_FAILED); |
| mDataNetworkCallback.invokeFromExecutor( |
| () -> mDataNetworkCallback.onHandoverFailed(DataNetwork.this, |
| mFailCause, retry, handoverFailureMode)); |
| } |
| |
| // No matter handover succeeded or not, transit back to connected state. |
| transitionTo(mConnectedState); |
| } |
| |
| /** |
| * Called when receiving PCO (Protocol Configuration Options) data from the cellular network. |
| * |
| * @param pcoData PCO data. |
| */ |
| private void onPcoDataReceived(@NonNull PcoData pcoData) { |
| if (pcoData.cid != getId()) return; |
| PcoData oldData = mPcoData.put(pcoData.pcoId, pcoData); |
| if (!Objects.equals(oldData, pcoData)) { |
| log("onPcoDataReceived: " + pcoData); |
| mDataNetworkCallback.invokeFromExecutor( |
| () -> mDataNetworkCallback.onPcoDataChanged(DataNetwork.this)); |
| if (mDataProfile.getApnSetting() != null) { |
| for (int apnType : mDataProfile.getApnSetting().getApnTypes()) { |
| Intent intent = new Intent(TelephonyManager.ACTION_CARRIER_SIGNAL_PCO_VALUE); |
| intent.putExtra(TelephonyManager.EXTRA_APN_TYPE, apnType); |
| intent.putExtra(TelephonyManager.EXTRA_APN_PROTOCOL, |
| ApnSetting.getProtocolIntFromString(pcoData.bearerProto)); |
| intent.putExtra(TelephonyManager.EXTRA_PCO_ID, pcoData.pcoId); |
| intent.putExtra(TelephonyManager.EXTRA_PCO_VALUE, pcoData.contents); |
| mPhone.getCarrierSignalAgent().notifyCarrierSignalReceivers(intent); |
| } |
| } |
| } |
| } |
| |
| /** |
| * @return The PCO data received from the network. |
| */ |
| public @NonNull Map<Integer, PcoData> getPcoData() { |
| return mPcoData; |
| } |
| |
| /** |
| * Check if the this data network is VCN-managed. |
| * |
| * @param networkCapabilities The network capabilities of this data network. |
| * @return {@code true} if this data network is VCN-managed. |
| */ |
| private boolean isVcnManaged(NetworkCapabilities networkCapabilities) { |
| if (mVcnManager == null) return false; |
| VcnNetworkPolicyResult policyResult = |
| mVcnManager.applyVcnNetworkPolicy(networkCapabilities, getLinkProperties()); |
| |
| // if the Network does have capability NOT_VCN_MANAGED, return false to indicate it's not |
| // VCN-managed |
| return !policyResult |
| .getNetworkCapabilities() |
| .hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED); |
| } |
| |
| /** |
| * Convert the data tear down reason to string. |
| * |
| * @param reason Data deactivation reason. |
| * @return The deactivation reason in string format. |
| */ |
| public static @NonNull String tearDownReasonToString(@TearDownReason int reason) { |
| switch (reason) { |
| case TEAR_DOWN_REASON_CONNECTIVITY_SERVICE_UNWANTED: |
| return "CONNECTIVITY_SERVICE_UNWANTED"; |
| case TEAR_DOWN_REASON_SIM_REMOVAL: |
| return "SIM_REMOVAL"; |
| case TEAR_DOWN_REASON_AIRPLANE_MODE_ON: |
| return "AIRPLANE_MODE_ON"; |
| case TEAR_DOWN_REASON_DATA_DISABLED: |
| return "DATA_DISABLED"; |
| case TEAR_DOWN_REASON_NO_LIVE_REQUEST: |
| return "TEAR_DOWN_REASON_NO_LIVE_REQUEST"; |
| case TEAR_DOWN_REASON_RAT_NOT_ALLOWED: |
| return "TEAR_DOWN_REASON_RAT_NOT_ALLOWED"; |
| case TEAR_DOWN_REASON_ROAMING_DISABLED: |
| return "TEAR_DOWN_REASON_ROAMING_DISABLED"; |
| case TEAR_DOWN_REASON_CONCURRENT_VOICE_DATA_NOT_ALLOWED: |
| return "TEAR_DOWN_REASON_CONCURRENT_VOICE_DATA_NOT_ALLOWED"; |
| case TEAR_DOWN_REASON_DATA_RESTRICTED_BY_NETWORK: |
| return "TEAR_DOWN_REASON_DATA_RESTRICTED_BY_NETWORK"; |
| case TEAR_DOWN_REASON_DATA_SERVICE_NOT_READY: |
| return "TEAR_DOWN_REASON_DATA_SERVICE_NOT_READY"; |
| case TEAR_DOWN_REASON_POWER_OFF_BY_CARRIER: |
| return "TEAR_DOWN_REASON_POWER_OFF_BY_CARRIER"; |
| case TEAR_DOWN_REASON_DATA_STALL: |
| return "TEAR_DOWN_REASON_DATA_STALL"; |
| case TEAR_DOWN_REASON_HANDOVER_FAILED: |
| return "TEAR_DOWN_REASON_HANDOVER_FAILED"; |
| case TEAR_DOWN_REASON_HANDOVER_NOT_ALLOWED: |
| return "TEAR_DOWN_REASON_HANDOVER_NOT_ALLOWED"; |
| case TEAR_DOWN_REASON_VCN_REQUESTED: |
| return "TEAR_DOWN_REASON_VCN_REQUESTED"; |
| case TEAR_DOWN_REASON_VOPS_NOT_SUPPORTED: |
| return "TEAR_DOWN_REASON_VOPS_NOT_SUPPORTED"; |
| case TEAR_DOWN_REASON_DEFAULT_DATA_UNSELECTED: |
| return "TEAR_DOWN_REASON_DEFAULT_DATA_UNSELECTED"; |
| case TEAR_DOWN_REASON_NOT_IN_SERVICE: |
| return "TEAR_DOWN_REASON_NOT_IN_SERVICE"; |
| case TEAR_DOWN_REASON_DATA_CONFIG_NOT_READY: |
| return "TEAR_DOWN_REASON_DATA_CONFIG_NOT_READY"; |
| case TEAR_DOWN_REASON_PENDING_TEAR_DOWN_ALL: |
| return "TEAR_DOWN_REASON_PENDING_TEAR_DOWN_ALL"; |
| case TEAR_DOWN_REASON_NO_SUITABLE_DATA_PROFILE: |
| return "TEAR_DOWN_REASON_NO_SUITABLE_DATA_PROFILE"; |
| case TEAR_DOWN_REASON_EMERGENCY_CALL: |
| return "TEAR_DOWN_REASON_EMERGENCY_CALL"; |
| case TEAR_DOWN_REASON_RETRY_SCHEDULED: |
| return "TEAR_DOWN_REASON_RETRY_SCHEDULED"; |
| case TEAR_DOWN_REASON_DATA_THROTTLED: |
| return "TEAR_DOWN_REASON_DATA_THROTTLED"; |
| case TEAR_DOWN_REASON_DATA_PROFILE_INVALID: |
| return "TEAR_DOWN_REASON_DATA_PROFILE_INVALID"; |
| case TEAR_DOWN_REASON_DATA_PROFILE_NOT_PREFERRED: |
| return "TEAR_DOWN_REASON_DATA_PROFILE_NOT_PREFERRED"; |
| case TEAR_DOWN_REASON_NOT_ALLOWED_BY_POLICY: |
| return "TEAR_DOWN_REASON_NOT_ALLOWED_BY_POLICY"; |
| case TEAR_DOWN_REASON_ILLEGAL_STATE: |
| return "TEAR_DOWN_REASON_ILLEGAL_STATE"; |
| case TEAR_DOWN_ONLY_ALLOWED_SINGLE_NETWORK: |
| return "TEAR_DOWN_ONLY_ALLOWED_SINGLE_NETWORK"; |
| default: |
| return "UNKNOWN(" + reason + ")"; |
| } |
| } |
| |
| /** |
| * Convert event to string |
| * |
| * @param event The event |
| * @return The event in string format. |
| */ |
| private static @NonNull String eventToString(int event) { |
| switch (event) { |
| case EVENT_DATA_CONFIG_UPDATED: |
| return "EVENT_DATA_CONFIG_UPDATED"; |
| case EVENT_ATTACH_NETWORK_REQUEST: |
| return "EVENT_ATTACH_NETWORK_REQUEST"; |
| case EVENT_DETACH_NETWORK_REQUEST: |
| return "EVENT_DETACH_NETWORK_REQUEST"; |
| case EVENT_ALLOCATE_PDU_SESSION_ID_RESPONSE: |
| return "EVENT_ALLOCATE_PDU_SESSION_ID_RESPONSE"; |
| case EVENT_SETUP_DATA_NETWORK_RESPONSE: |
| return "EVENT_SETUP_DATA_NETWORK_RESPONSE"; |
| case EVENT_TEAR_DOWN_NETWORK: |
| return "EVENT_TEAR_DOWN_NETWORK"; |
| case EVENT_DATA_STATE_CHANGED: |
| return "EVENT_DATA_STATE_CHANGED"; |
| case EVENT_SERVICE_STATE_CHANGED: |
| return "EVENT_DATA_NETWORK_TYPE_REG_STATE_CHANGED"; |
| case EVENT_DETACH_ALL_NETWORK_REQUESTS: |
| return "EVENT_DETACH_ALL_NETWORK_REQUESTS"; |
| case EVENT_BANDWIDTH_ESTIMATE_FROM_MODEM_CHANGED: |
| return "EVENT_BANDWIDTH_ESTIMATE_FROM_MODEM_CHANGED"; |
| case EVENT_BANDWIDTH_ESTIMATE_FROM_BANDWIDTH_ESTIMATOR_CHANGED: |
| return "EVENT_BANDWIDTH_ESTIMATE_FROM_BANDWIDTH_ESTIMATOR_CHANGED"; |
| case EVENT_DISPLAY_INFO_CHANGED: |
| return "EVENT_DISPLAY_INFO_CHANGED"; |
| case EVENT_START_HANDOVER: |
| return "EVENT_START_HANDOVER"; |
| case EVENT_HANDOVER_RESPONSE: |
| return "EVENT_HANDOVER_RESPONSE"; |
| case EVENT_SUBSCRIPTION_PLAN_OVERRIDE: |
| return "EVENT_SUBSCRIPTION_PLAN_OVERRIDE"; |
| case EVENT_PCO_DATA_RECEIVED: |
| return "EVENT_PCO_DATA_RECEIVED"; |
| case EVENT_CARRIER_PRIVILEGED_UIDS_CHANGED: |
| return "EVENT_CARRIER_PRIVILEGED_UIDS_CHANGED"; |
| case EVENT_DEACTIVATE_DATA_NETWORK_RESPONSE: |
| return "EVENT_DEACTIVATE_DATA_NETWORK_RESPONSE"; |
| default: |
| return "Unknown(" + event + ")"; |
| } |
| } |
| |
| @Override |
| public String toString() { |
| return "[DataNetwork: " + mLogTag + ", " + (mDataProfile.getApnSetting() != null |
| ? mDataProfile.getApnSetting().getApnName() : null) + ", state=" |
| + (getCurrentState() != null ? getCurrentState().getName() : null) + "]"; |
| } |
| |
| /** |
| * @return The short name of the data network (e.g. DN-C-1) |
| */ |
| public @NonNull String name() { |
| return mLogTag; |
| } |
| |
| /** |
| * Log debug messages. |
| * @param s debug messages |
| */ |
| @Override |
| protected void log(@NonNull String s) { |
| Rlog.d(mLogTag, (getCurrentState() != null |
| ? (getCurrentState().getName() + ": ") : "") + s); |
| } |
| |
| /** |
| * Log error messages. |
| * @param s error messages |
| */ |
| @Override |
| protected void loge(@NonNull String s) { |
| Rlog.e(mLogTag, (getCurrentState() != null |
| ? (getCurrentState().getName() + ": ") : "") + s); |
| } |
| |
| /** |
| * Log verbose messages. |
| * @param s error messages |
| */ |
| @Override |
| protected void logv(@NonNull String s) { |
| if (VDBG) { |
| Rlog.v(mLogTag, (getCurrentState() != null |
| ? (getCurrentState().getName() + ": ") : "") + s); |
| } |
| } |
| |
| /** |
| * Log debug messages and also log into the local log. |
| * @param s debug messages |
| */ |
| private void logl(@NonNull String s) { |
| log(s); |
| mLocalLog.log(s); |
| } |
| |
| /** |
| * Dump the state of DataNetwork |
| * |
| * @param fd File descriptor |
| * @param printWriter Print writer |
| * @param args Arguments |
| */ |
| public void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) { |
| IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, " "); |
| super.dump(fd, pw, args); |
| pw.println("Tag: " + name()); |
| pw.increaseIndent(); |
| pw.println("mSubId=" + mSubId); |
| pw.println("mTransport=" + AccessNetworkConstants.transportTypeToString(mTransport)); |
| pw.println("WWAN cid=" + mCid.get(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)); |
| pw.println("WLAN cid=" + mCid.get(AccessNetworkConstants.TRANSPORT_TYPE_WLAN)); |
| pw.println("mDataAllowedReason=" + mDataAllowedReason); |
| pw.println("mPduSessionId=" + mPduSessionId); |
| pw.println("mDataProfile=" + mDataProfile); |
| pw.println("mNetworkCapabilities" + mNetworkCapabilities); |
| pw.println("mLinkProperties=" + mLinkProperties); |
| pw.println("mNetworkSliceInfo=" + mNetworkSliceInfo); |
| pw.println("mNetworkBandwidth=" + mNetworkBandwidth); |
| pw.println("mTcpBufferSizes=" + mTcpBufferSizes); |
| pw.println("mTempNotMeteredSupported=" + mTempNotMeteredSupported); |
| pw.println("mTempNotMetered=" + mTempNotMetered); |
| pw.println("mCongested=" + mCongested); |
| pw.println("mSuspended" + mSuspended); |
| pw.println("mDataCallResponse=" + mDataCallResponse); |
| pw.println("mFailCause=" + DataFailCause.toString(mFailCause)); |
| pw.println("mAdministratorUids=" + Arrays.toString(mAdministratorUids)); |
| pw.println("mCarrierServicePackageUid=" + mCarrierServicePackageUid); |
| pw.println("mEverConnected=" + mEverConnected); |
| pw.println("mInvokedDataDeactivation=" + mInvokedDataDeactivation); |
| |
| pw.println("Attached network requests:"); |
| pw.increaseIndent(); |
| for (TelephonyNetworkRequest request : mAttachedNetworkRequestList) { |
| pw.println(request); |
| } |
| pw.decreaseIndent(); |
| pw.println("mQosBearerSessions=" + mQosBearerSessions); |
| |
| mNetworkAgent.dump(fd, pw, args); |
| pw.println("Local logs:"); |
| pw.increaseIndent(); |
| mLocalLog.dump(fd, pw, args); |
| pw.decreaseIndent(); |
| pw.decreaseIndent(); |
| pw.println("---------------"); |
| } |
| } |