| /* |
| * Copyright (C) 2006 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.dataconnection; |
| |
| import static android.telephony.data.DataCallResponse.PDU_SESSION_ID_NOT_SET; |
| |
| import static com.android.internal.telephony.dataconnection.DcTracker.REQUEST_TYPE_HANDOVER; |
| |
| import android.annotation.IntDef; |
| import android.annotation.NonNull; |
| import android.annotation.Nullable; |
| import android.app.PendingIntent; |
| import android.content.Context; |
| import android.net.ConnectivityManager; |
| import android.net.InetAddresses; |
| import android.net.KeepalivePacketData; |
| import android.net.LinkAddress; |
| import android.net.LinkProperties; |
| import android.net.NetworkAgentConfig; |
| import android.net.NetworkCapabilities; |
| import android.net.NetworkFactory; |
| import android.net.NetworkProvider; |
| import android.net.NetworkRequest; |
| import android.net.ProxyInfo; |
| import android.net.RouteInfo; |
| import android.net.SocketKeepalive; |
| import android.net.TelephonyNetworkSpecifier; |
| import android.net.vcn.VcnManager; |
| import android.net.vcn.VcnManager.VcnNetworkPolicyChangeListener; |
| import android.net.vcn.VcnNetworkPolicyResult; |
| import android.os.AsyncResult; |
| import android.os.HandlerExecutor; |
| import android.os.Message; |
| import android.os.PersistableBundle; |
| import android.os.SystemClock; |
| import android.os.SystemProperties; |
| import android.provider.Telephony; |
| import android.telephony.AccessNetworkConstants; |
| import android.telephony.AccessNetworkConstants.TransportType; |
| import android.telephony.Annotation.ApnType; |
| import android.telephony.Annotation.DataFailureCause; |
| import android.telephony.Annotation.DataState; |
| import android.telephony.Annotation.NetworkType; |
| import android.telephony.CarrierConfigManager; |
| import android.telephony.DataFailCause; |
| import android.telephony.LinkCapacityEstimate; |
| import android.telephony.NetworkRegistrationInfo; |
| import android.telephony.PreciseDataConnectionState; |
| import android.telephony.ServiceState; |
| import android.telephony.SubscriptionManager; |
| import android.telephony.TelephonyManager; |
| import android.telephony.data.ApnSetting; |
| import android.telephony.data.DataCallResponse; |
| import android.telephony.data.DataCallResponse.HandoverFailureMode; |
| import android.telephony.data.DataProfile; |
| import android.telephony.data.DataService; |
| import android.telephony.data.DataServiceCallback; |
| import android.telephony.data.NetworkSliceInfo; |
| import android.telephony.data.Qos; |
| import android.telephony.data.QosBearerSession; |
| import android.telephony.data.TrafficDescriptor; |
| import android.text.TextUtils; |
| import android.util.LocalLog; |
| import android.util.Pair; |
| import android.util.TimeUtils; |
| |
| import com.android.internal.annotations.VisibleForTesting; |
| import com.android.internal.telephony.CarrierSignalAgent; |
| import com.android.internal.telephony.DctConstants; |
| import com.android.internal.telephony.Phone; |
| import com.android.internal.telephony.PhoneConstants; |
| import com.android.internal.telephony.PhoneFactory; |
| import com.android.internal.telephony.RIL; |
| import com.android.internal.telephony.RILConstants; |
| import com.android.internal.telephony.RetryManager; |
| import com.android.internal.telephony.TelephonyStatsLog; |
| import com.android.internal.telephony.dataconnection.DcTracker.ReleaseNetworkType; |
| import com.android.internal.telephony.dataconnection.DcTracker.RequestNetworkType; |
| import com.android.internal.telephony.metrics.DataCallSessionStats; |
| import com.android.internal.telephony.metrics.TelephonyMetrics; |
| import com.android.internal.telephony.nano.TelephonyProto.RilDataCall; |
| import com.android.internal.telephony.uicc.IccUtils; |
| import com.android.internal.util.AsyncChannel; |
| import com.android.internal.util.IndentingPrintWriter; |
| import com.android.internal.util.Protocol; |
| 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.io.StringWriter; |
| import java.lang.annotation.Retention; |
| import java.lang.annotation.RetentionPolicy; |
| import java.net.InetAddress; |
| import java.net.UnknownHostException; |
| import java.nio.ByteBuffer; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.List; |
| import java.util.Locale; |
| import java.util.Map; |
| import java.util.UUID; |
| import java.util.concurrent.ConcurrentHashMap; |
| import java.util.concurrent.atomic.AtomicInteger; |
| import java.util.function.Consumer; |
| |
| /** |
| * {@hide} |
| * |
| * DataConnection StateMachine. |
| * |
| * This a class for representing a single data connection, with instances of this |
| * class representing a connection via the cellular network. There may be multiple |
| * data connections and all of them are managed by the <code>DataConnectionTracker</code>. |
| * |
| * NOTE: All DataConnection objects must be running on the same looper, which is the default |
| * as the coordinator has members which are used without synchronization. |
| */ |
| public class DataConnection extends StateMachine { |
| private static final boolean DBG = true; |
| private static final boolean VDBG = true; |
| |
| private static final String NETWORK_TYPE = "MOBILE"; |
| |
| private static final String RAT_NAME_5G = "nr"; |
| private static final String RAT_NAME_EVDO = "evdo"; |
| |
| /** |
| * OSId for "Android", using UUID version 5 with namespace ISO OSI. |
| * Prepended to the OsAppId in TrafficDescriptor to use for URSP matching. |
| */ |
| private static final UUID OS_ID = UUID.fromString("97a498e3-fc92-5c94-8986-0333d06e4e47"); |
| |
| private static final int MIN_V6_MTU = 1280; |
| |
| /** |
| * The data connection is not being or been handovered. Note this is the state for the source |
| * data connection, not destination data connection |
| */ |
| private static final int HANDOVER_STATE_IDLE = 1; |
| |
| /** |
| * The data connection is being handovered. Note this is the state for the source |
| * data connection, not destination data connection. |
| */ |
| private static final int HANDOVER_STATE_BEING_TRANSFERRED = 2; |
| |
| /** |
| * The data connection is already handovered. Note this is the state for the source |
| * data connection, not destination data connection. |
| */ |
| private static final int HANDOVER_STATE_COMPLETED = 3; |
| |
| |
| /** @hide */ |
| @Retention(RetentionPolicy.SOURCE) |
| @IntDef(prefix = {"HANDOVER_STATE_"}, value = { |
| HANDOVER_STATE_IDLE, |
| HANDOVER_STATE_BEING_TRANSFERRED, |
| HANDOVER_STATE_COMPLETED}) |
| public @interface HandoverState {} |
| |
| // The data connection providing default Internet connection will have a higher score of 50. |
| // Other connections will have a slightly lower score of 45. The intention is other connections |
| // will not cause ConnectivityService to tear down default internet connection. For example, |
| // to validate Internet connection on non-default data SIM, we'll set up a temporary Internet |
| // connection on that data SIM. In this case, score of 45 is assigned so ConnectivityService |
| // will not replace the default Internet connection with it. |
| private static final int DEFAULT_INTERNET_CONNECTION_SCORE = 50; |
| private static final int OTHER_CONNECTION_SCORE = 45; |
| |
| // The score we report to connectivity service |
| private int mScore; |
| |
| // The subscription id associated with this data connection. |
| private int mSubId; |
| |
| // The data connection controller |
| private DcController mDcController; |
| |
| // The Tester for failing all bringup's |
| private DcTesterFailBringUpAll mDcTesterFailBringUpAll; |
| |
| // Whether or not the data connection should allocate its own pdu session id |
| private final boolean mDoAllocatePduSessionId; |
| |
| private static AtomicInteger mInstanceNumber = new AtomicInteger(0); |
| private AsyncChannel mAc; |
| |
| // The DCT that's talking to us, we only support one! |
| private DcTracker mDct = null; |
| |
| private String[] mPcscfAddr; |
| |
| private final String mTagSuffix; |
| |
| private final LocalLog mHandoverLocalLog = new LocalLog(100); |
| |
| private int[] mAdministratorUids = new int[0]; |
| |
| // stats per data call |
| private DataCallSessionStats mDataCallSessionStats; |
| |
| /** |
| * Used internally for saving connecting parameters. |
| */ |
| public static class ConnectionParams { |
| int mTag; |
| ApnContext mApnContext; |
| int mProfileId; |
| int mRilRat; |
| Message mOnCompletedMsg; |
| final int mConnectionGeneration; |
| @RequestNetworkType |
| final int mRequestType; |
| final int mSubId; |
| final boolean mIsPreferredApn; |
| |
| ConnectionParams(ApnContext apnContext, int profileId, int rilRadioTechnology, |
| Message onCompletedMsg, int connectionGeneration, |
| @RequestNetworkType int requestType, int subId, |
| boolean isPreferredApn) { |
| mApnContext = apnContext; |
| mProfileId = profileId; |
| mRilRat = rilRadioTechnology; |
| mOnCompletedMsg = onCompletedMsg; |
| mConnectionGeneration = connectionGeneration; |
| mRequestType = requestType; |
| mSubId = subId; |
| mIsPreferredApn = isPreferredApn; |
| } |
| |
| @Override |
| public String toString() { |
| return "{mTag=" + mTag + " mApnContext=" + mApnContext |
| + " mProfileId=" + mProfileId |
| + " mRat=" + mRilRat |
| + " mOnCompletedMsg=" + msgToString(mOnCompletedMsg) |
| + " mRequestType=" + DcTracker.requestTypeToString(mRequestType) |
| + " mSubId=" + mSubId |
| + " mIsPreferredApn=" + mIsPreferredApn |
| + "}"; |
| } |
| } |
| |
| /** |
| * Used internally for saving disconnecting parameters. |
| */ |
| public static class DisconnectParams { |
| int mTag; |
| public ApnContext mApnContext; |
| String mReason; |
| @ReleaseNetworkType |
| final int mReleaseType; |
| Message mOnCompletedMsg; |
| |
| DisconnectParams(ApnContext apnContext, String reason, @ReleaseNetworkType int releaseType, |
| Message onCompletedMsg) { |
| mApnContext = apnContext; |
| mReason = reason; |
| mReleaseType = releaseType; |
| mOnCompletedMsg = onCompletedMsg; |
| } |
| |
| @Override |
| public String toString() { |
| return "{mTag=" + mTag + " mApnContext=" + mApnContext |
| + " mReason=" + mReason |
| + " mReleaseType=" + DcTracker.releaseTypeToString(mReleaseType) |
| + " mOnCompletedMsg=" + msgToString(mOnCompletedMsg) + "}"; |
| } |
| } |
| |
| private volatile ApnSetting mApnSetting; |
| private ConnectionParams mConnectionParams; |
| private DisconnectParams mDisconnectParams; |
| @DataFailureCause |
| private int mDcFailCause; |
| |
| @HandoverFailureMode |
| private int mHandoverFailureMode; |
| |
| private Phone mPhone; |
| private DataServiceManager mDataServiceManager; |
| private VcnManager mVcnManager; |
| private final int mTransportType; |
| private LinkProperties mLinkProperties = new LinkProperties(); |
| private int mPduSessionId; |
| private long mCreateTime; |
| private long mLastFailTime; |
| @DataFailureCause |
| private int mLastFailCause; |
| private static final String NULL_IP = "0.0.0.0"; |
| private Object mUserData; |
| private boolean mCongestedOverride; |
| private boolean mUnmeteredOverride; |
| private int mRilRat = ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN; |
| private int mDataRegState = Integer.MAX_VALUE; |
| // Indicating data connection is suspended due to temporary reasons, for example, out of |
| // service, concurrency voice/data not supported, etc.. Note this flag is only meaningful when |
| // data is in active state. When data is in inactive, connecting, or disconnecting, this flag |
| // is unmeaningful. |
| private boolean mIsSuspended; |
| private int mDownlinkBandwidth = 14; |
| private int mUplinkBandwidth = 14; |
| private Qos mDefaultQos = null; |
| private List<QosBearerSession> mQosBearerSessions = new ArrayList<>(); |
| private NetworkSliceInfo mSliceInfo; |
| private List<TrafficDescriptor> mTrafficDescriptors = new ArrayList<>(); |
| |
| /** The corresponding network agent for this data connection. */ |
| private DcNetworkAgent mNetworkAgent; |
| |
| /** |
| * The network agent from handover source data connection. This is the potential network agent |
| * that will be transferred here after handover completed. |
| */ |
| private DcNetworkAgent mHandoverSourceNetworkAgent; |
| |
| private int mDisabledApnTypeBitMask = 0; |
| |
| int mTag; |
| |
| /** Data connection id assigned by the modem. This is unique across transports */ |
| public int mCid; |
| |
| @HandoverState |
| private int mHandoverState = HANDOVER_STATE_IDLE; |
| private final Map<ApnContext, ConnectionParams> mApnContexts = new ConcurrentHashMap<>(); |
| PendingIntent mReconnectIntent = null; |
| |
| /** Class used to track VCN-defined Network policies for this DcNetworkAgent. */ |
| private final VcnNetworkPolicyChangeListener mVcnPolicyChangeListener = |
| new DataConnectionVcnNetworkPolicyChangeListener(); |
| |
| // ***** Event codes for driving the state machine, package visible for Dcc |
| static final int BASE = Protocol.BASE_DATA_CONNECTION; |
| static final int EVENT_CONNECT = BASE + 0; |
| static final int EVENT_SETUP_DATA_CONNECTION_DONE = BASE + 1; |
| static final int EVENT_DEACTIVATE_DONE = BASE + 3; |
| static final int EVENT_DISCONNECT = BASE + 4; |
| static final int EVENT_DISCONNECT_ALL = BASE + 6; |
| static final int EVENT_DATA_STATE_CHANGED = BASE + 7; |
| static final int EVENT_TEAR_DOWN_NOW = BASE + 8; |
| static final int EVENT_LOST_CONNECTION = BASE + 9; |
| static final int EVENT_DATA_CONNECTION_DRS_OR_RAT_CHANGED = BASE + 11; |
| static final int EVENT_DATA_CONNECTION_ROAM_ON = BASE + 12; |
| static final int EVENT_DATA_CONNECTION_ROAM_OFF = BASE + 13; |
| static final int EVENT_BW_REFRESH_RESPONSE = BASE + 14; |
| static final int EVENT_DATA_CONNECTION_VOICE_CALL_STARTED = BASE + 15; |
| static final int EVENT_DATA_CONNECTION_VOICE_CALL_ENDED = BASE + 16; |
| static final int EVENT_DATA_CONNECTION_CONGESTEDNESS_CHANGED = BASE + 17; |
| static final int EVENT_KEEPALIVE_STATUS = BASE + 18; |
| static final int EVENT_KEEPALIVE_STARTED = BASE + 19; |
| static final int EVENT_KEEPALIVE_STOPPED = BASE + 20; |
| static final int EVENT_KEEPALIVE_START_REQUEST = BASE + 21; |
| static final int EVENT_KEEPALIVE_STOP_REQUEST = BASE + 22; |
| static final int EVENT_LINK_CAPACITY_CHANGED = BASE + 23; |
| static final int EVENT_RESET = BASE + 24; |
| static final int EVENT_REEVALUATE_RESTRICTED_STATE = BASE + 25; |
| static final int EVENT_REEVALUATE_DATA_CONNECTION_PROPERTIES = BASE + 26; |
| static final int EVENT_NR_STATE_CHANGED = BASE + 27; |
| static final int EVENT_DATA_CONNECTION_METEREDNESS_CHANGED = BASE + 28; |
| static final int EVENT_NR_FREQUENCY_CHANGED = BASE + 29; |
| static final int EVENT_CARRIER_CONFIG_LINK_BANDWIDTHS_CHANGED = BASE + 30; |
| static final int EVENT_CARRIER_PRIVILEGED_UIDS_CHANGED = BASE + 31; |
| static final int EVENT_CSS_INDICATOR_CHANGED = BASE + 32; |
| static final int EVENT_UPDATE_SUSPENDED_STATE = BASE + 33; |
| static final int EVENT_START_HANDOVER = BASE + 34; |
| static final int EVENT_CANCEL_HANDOVER = BASE + 35; |
| static final int EVENT_START_HANDOVER_ON_TARGET = BASE + 36; |
| static final int EVENT_ALLOCATE_PDU_SESSION_ID = BASE + 37; |
| static final int EVENT_RELEASE_PDU_SESSION_ID = BASE + 38; |
| static final int EVENT_LINK_BANDWIDTH_ESTIMATOR_UPDATE = BASE + 39; |
| private static final int CMD_TO_STRING_COUNT = EVENT_LINK_BANDWIDTH_ESTIMATOR_UPDATE - BASE + 1; |
| |
| private static String[] sCmdToString = new String[CMD_TO_STRING_COUNT]; |
| static { |
| sCmdToString[EVENT_CONNECT - BASE] = "EVENT_CONNECT"; |
| sCmdToString[EVENT_SETUP_DATA_CONNECTION_DONE - BASE] = |
| "EVENT_SETUP_DATA_CONNECTION_DONE"; |
| sCmdToString[EVENT_DEACTIVATE_DONE - BASE] = "EVENT_DEACTIVATE_DONE"; |
| sCmdToString[EVENT_DISCONNECT - BASE] = "EVENT_DISCONNECT"; |
| sCmdToString[EVENT_DISCONNECT_ALL - BASE] = "EVENT_DISCONNECT_ALL"; |
| sCmdToString[EVENT_DATA_STATE_CHANGED - BASE] = "EVENT_DATA_STATE_CHANGED"; |
| sCmdToString[EVENT_TEAR_DOWN_NOW - BASE] = "EVENT_TEAR_DOWN_NOW"; |
| sCmdToString[EVENT_LOST_CONNECTION - BASE] = "EVENT_LOST_CONNECTION"; |
| sCmdToString[EVENT_DATA_CONNECTION_DRS_OR_RAT_CHANGED - BASE] = |
| "EVENT_DATA_CONNECTION_DRS_OR_RAT_CHANGED"; |
| sCmdToString[EVENT_DATA_CONNECTION_ROAM_ON - BASE] = "EVENT_DATA_CONNECTION_ROAM_ON"; |
| sCmdToString[EVENT_DATA_CONNECTION_ROAM_OFF - BASE] = "EVENT_DATA_CONNECTION_ROAM_OFF"; |
| sCmdToString[EVENT_BW_REFRESH_RESPONSE - BASE] = "EVENT_BW_REFRESH_RESPONSE"; |
| sCmdToString[EVENT_DATA_CONNECTION_VOICE_CALL_STARTED - BASE] = |
| "EVENT_DATA_CONNECTION_VOICE_CALL_STARTED"; |
| sCmdToString[EVENT_DATA_CONNECTION_VOICE_CALL_ENDED - BASE] = |
| "EVENT_DATA_CONNECTION_VOICE_CALL_ENDED"; |
| sCmdToString[EVENT_DATA_CONNECTION_CONGESTEDNESS_CHANGED - BASE] = |
| "EVENT_DATA_CONNECTION_CONGESTEDNESS_CHANGED"; |
| sCmdToString[EVENT_KEEPALIVE_STATUS - BASE] = "EVENT_KEEPALIVE_STATUS"; |
| sCmdToString[EVENT_KEEPALIVE_STARTED - BASE] = "EVENT_KEEPALIVE_STARTED"; |
| sCmdToString[EVENT_KEEPALIVE_STOPPED - BASE] = "EVENT_KEEPALIVE_STOPPED"; |
| sCmdToString[EVENT_KEEPALIVE_START_REQUEST - BASE] = "EVENT_KEEPALIVE_START_REQUEST"; |
| sCmdToString[EVENT_KEEPALIVE_STOP_REQUEST - BASE] = "EVENT_KEEPALIVE_STOP_REQUEST"; |
| sCmdToString[EVENT_LINK_CAPACITY_CHANGED - BASE] = "EVENT_LINK_CAPACITY_CHANGED"; |
| sCmdToString[EVENT_RESET - BASE] = "EVENT_RESET"; |
| sCmdToString[EVENT_REEVALUATE_RESTRICTED_STATE - BASE] = |
| "EVENT_REEVALUATE_RESTRICTED_STATE"; |
| sCmdToString[EVENT_REEVALUATE_DATA_CONNECTION_PROPERTIES - BASE] = |
| "EVENT_REEVALUATE_DATA_CONNECTION_PROPERTIES"; |
| sCmdToString[EVENT_NR_STATE_CHANGED - BASE] = "EVENT_NR_STATE_CHANGED"; |
| sCmdToString[EVENT_DATA_CONNECTION_METEREDNESS_CHANGED - BASE] = |
| "EVENT_DATA_CONNECTION_METEREDNESS_CHANGED"; |
| sCmdToString[EVENT_NR_FREQUENCY_CHANGED - BASE] = "EVENT_NR_FREQUENCY_CHANGED"; |
| sCmdToString[EVENT_CARRIER_CONFIG_LINK_BANDWIDTHS_CHANGED - BASE] = |
| "EVENT_CARRIER_CONFIG_LINK_BANDWIDTHS_CHANGED"; |
| sCmdToString[EVENT_CARRIER_PRIVILEGED_UIDS_CHANGED - BASE] = |
| "EVENT_CARRIER_PRIVILEGED_UIDS_CHANGED"; |
| sCmdToString[EVENT_CSS_INDICATOR_CHANGED - BASE] = "EVENT_CSS_INDICATOR_CHANGED"; |
| sCmdToString[EVENT_UPDATE_SUSPENDED_STATE - BASE] = "EVENT_UPDATE_SUSPENDED_STATE"; |
| sCmdToString[EVENT_START_HANDOVER - BASE] = "EVENT_START_HANDOVER"; |
| sCmdToString[EVENT_CANCEL_HANDOVER - BASE] = "EVENT_CANCEL_HANDOVER"; |
| sCmdToString[EVENT_START_HANDOVER_ON_TARGET - BASE] = "EVENT_START_HANDOVER_ON_TARGET"; |
| sCmdToString[EVENT_ALLOCATE_PDU_SESSION_ID - BASE] = "EVENT_ALLOCATE_PDU_SESSION_ID"; |
| sCmdToString[EVENT_RELEASE_PDU_SESSION_ID - BASE] = "EVENT_RELEASE_PDU_SESSION_ID"; |
| sCmdToString[EVENT_LINK_BANDWIDTH_ESTIMATOR_UPDATE - BASE] = |
| "EVENT_LINK_BANDWIDTH_ESTIMATOR_UPDATE"; |
| } |
| // Convert cmd to string or null if unknown |
| static String cmdToString(int cmd) { |
| String value = null; |
| cmd -= BASE; |
| if ((cmd >= 0) && (cmd < sCmdToString.length)) { |
| value = sCmdToString[cmd]; |
| } |
| if (value == null) { |
| value = "0x" + Integer.toHexString(cmd + BASE); |
| } |
| return value; |
| } |
| |
| /** |
| * Create the connection object |
| * |
| * @param phone the Phone |
| * @param id the connection id |
| * @return DataConnection that was created. |
| */ |
| public static DataConnection makeDataConnection(Phone phone, int id, DcTracker dct, |
| DataServiceManager dataServiceManager, |
| DcTesterFailBringUpAll failBringUpAll, |
| DcController dcc, |
| boolean doAllocatePduSessionId) { |
| String transportType = (dataServiceManager.getTransportType() |
| == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) |
| ? "C" // Cellular |
| : "I"; // IWLAN |
| DataConnection dc = new DataConnection(phone, transportType + "-" |
| + mInstanceNumber.incrementAndGet(), id, dct, dataServiceManager, failBringUpAll, |
| dcc, doAllocatePduSessionId); |
| dc.start(); |
| if (DBG) dc.log("Made " + dc.getName()); |
| return dc; |
| } |
| |
| void dispose() { |
| log("dispose: call quiteNow()"); |
| quitNow(); |
| } |
| |
| /* Getter functions */ |
| |
| LinkProperties getLinkProperties() { |
| return new LinkProperties(mLinkProperties); |
| } |
| |
| boolean isDisconnecting() { |
| return getCurrentState() == mDisconnectingState |
| || getCurrentState() == mDisconnectingErrorCreatingConnection; |
| } |
| |
| @VisibleForTesting |
| public boolean isActive() { |
| return getCurrentState() == mActiveState; |
| } |
| |
| @VisibleForTesting |
| public boolean isInactive() { |
| return getCurrentState() == mInactiveState; |
| } |
| |
| boolean isActivating() { |
| return getCurrentState() == mActivatingState; |
| } |
| |
| boolean hasBeenTransferred() { |
| return mHandoverState == HANDOVER_STATE_COMPLETED; |
| } |
| |
| int getCid() { |
| return mCid; |
| } |
| |
| /** |
| * @return DataConnection's ApnSetting. |
| */ |
| public ApnSetting getApnSetting() { |
| return mApnSetting; |
| } |
| |
| /** |
| * Update http proxy of link properties based on current apn setting |
| */ |
| private void updateLinkPropertiesHttpProxy() { |
| if (mApnSetting == null |
| || TextUtils.isEmpty(mApnSetting.getProxyAddressAsString())) { |
| return; |
| } |
| try { |
| int port = mApnSetting.getProxyPort(); |
| if (port == -1) { |
| port = 8080; |
| } |
| ProxyInfo proxy = ProxyInfo.buildDirectProxy( |
| mApnSetting.getProxyAddressAsString(), port); |
| mLinkProperties.setHttpProxy(proxy); |
| } catch (NumberFormatException e) { |
| loge("onDataSetupComplete: NumberFormatException making ProxyProperties (" |
| + mApnSetting.getProxyPort() + "): " + e); |
| } |
| } |
| |
| public static class UpdateLinkPropertyResult { |
| public SetupResult setupResult = SetupResult.SUCCESS; |
| public LinkProperties oldLp; |
| public LinkProperties newLp; |
| public UpdateLinkPropertyResult(LinkProperties curLp) { |
| oldLp = curLp; |
| newLp = curLp; |
| } |
| } |
| |
| /** |
| * Class returned by onSetupConnectionCompleted. |
| */ |
| public enum SetupResult { |
| SUCCESS, |
| ERROR_RADIO_NOT_AVAILABLE, |
| ERROR_INVALID_ARG, |
| ERROR_STALE, |
| ERROR_DATA_SERVICE_SPECIFIC_ERROR, |
| ERROR_DUPLICATE_CID, |
| ERROR_NO_DEFAULT_CONNECTION; |
| |
| public int mFailCause; |
| |
| SetupResult() { |
| mFailCause = DataFailCause.getFailCause(0); |
| } |
| |
| @Override |
| public String toString() { |
| return name() + " SetupResult.mFailCause=" + DataFailCause.toString(mFailCause); |
| } |
| } |
| |
| public boolean isIpv4Connected() { |
| boolean ret = false; |
| Collection <InetAddress> addresses = mLinkProperties.getAddresses(); |
| |
| for (InetAddress addr: addresses) { |
| if (addr instanceof java.net.Inet4Address) { |
| java.net.Inet4Address i4addr = (java.net.Inet4Address) addr; |
| if (!i4addr.isAnyLocalAddress() && !i4addr.isLinkLocalAddress() && |
| !i4addr.isLoopbackAddress() && !i4addr.isMulticastAddress()) { |
| ret = true; |
| break; |
| } |
| } |
| } |
| return ret; |
| } |
| |
| public boolean isIpv6Connected() { |
| boolean ret = false; |
| Collection <InetAddress> addresses = mLinkProperties.getAddresses(); |
| |
| for (InetAddress addr: addresses) { |
| if (addr instanceof java.net.Inet6Address) { |
| java.net.Inet6Address i6addr = (java.net.Inet6Address) addr; |
| if (!i6addr.isAnyLocalAddress() && !i6addr.isLinkLocalAddress() && |
| !i6addr.isLoopbackAddress() && !i6addr.isMulticastAddress()) { |
| ret = true; |
| break; |
| } |
| } |
| } |
| return ret; |
| } |
| |
| public int getPduSessionId() { |
| return mPduSessionId; |
| } |
| |
| public NetworkSliceInfo getSliceInfo() { |
| return mSliceInfo; |
| } |
| |
| public List<TrafficDescriptor> getTrafficDescriptors() { |
| return mTrafficDescriptors; |
| } |
| |
| /** |
| * Update DC fields based on a new DataCallResponse |
| * @param response the response to use to update DC fields |
| */ |
| public void updateResponseFields(DataCallResponse response) { |
| updateQosParameters(response); |
| updateSliceInfo(response); |
| updateTrafficDescriptors(response); |
| } |
| |
| public void updateQosParameters(final @Nullable DataCallResponse response) { |
| if (response == null) { |
| mDefaultQos = null; |
| mQosBearerSessions.clear(); |
| return; |
| } |
| |
| mDefaultQos = response.getDefaultQos(); |
| mQosBearerSessions = response.getQosBearerSessions(); |
| |
| if (mNetworkAgent != null) { |
| syncQosToNetworkAgent(); |
| } |
| } |
| |
| private void syncQosToNetworkAgent() { |
| final DcNetworkAgent networkAgent = mNetworkAgent; |
| final List<QosBearerSession> qosBearerSessions = mQosBearerSessions; |
| if (qosBearerSessions == null) { |
| networkAgent.updateQosBearerSessions(new ArrayList<>()); |
| return; |
| } |
| networkAgent.updateQosBearerSessions(qosBearerSessions); |
| } |
| |
| /** |
| * Update the latest slice info on this data connection with |
| * {@link DataCallResponse#getSliceInfo}. |
| */ |
| public void updateSliceInfo(DataCallResponse response) { |
| mSliceInfo = response.getSliceInfo(); |
| } |
| |
| /** |
| * Update the latest traffic descriptor on this data connection with |
| * {@link DataCallResponse#getTrafficDescriptors}. |
| */ |
| public void updateTrafficDescriptors(DataCallResponse response) { |
| mTrafficDescriptors = response.getTrafficDescriptors(); |
| } |
| |
| @VisibleForTesting |
| public UpdateLinkPropertyResult updateLinkProperty(DataCallResponse newState) { |
| UpdateLinkPropertyResult result = new UpdateLinkPropertyResult(mLinkProperties); |
| |
| if (newState == null) return result; |
| |
| result.newLp = new LinkProperties(); |
| |
| // set link properties based on data call response |
| result.setupResult = setLinkProperties(newState, result.newLp); |
| if (result.setupResult != SetupResult.SUCCESS) { |
| if (DBG) log("updateLinkProperty failed : " + result.setupResult); |
| return result; |
| } |
| // copy HTTP proxy as it is not part DataCallResponse. |
| result.newLp.setHttpProxy(mLinkProperties.getHttpProxy()); |
| |
| checkSetMtu(mApnSetting, result.newLp); |
| |
| mLinkProperties = result.newLp; |
| |
| updateTcpBufferSizes(mRilRat); |
| |
| if (DBG && (! result.oldLp.equals(result.newLp))) { |
| log("updateLinkProperty old LP=" + result.oldLp); |
| log("updateLinkProperty new LP=" + result.newLp); |
| } |
| |
| if (result.newLp.equals(result.oldLp) == false && |
| mNetworkAgent != null) { |
| mNetworkAgent.sendLinkProperties(mLinkProperties, DataConnection.this); |
| } |
| |
| return result; |
| } |
| |
| /** |
| * Sets the pdu session id of the data connection |
| * @param pduSessionId pdu session id to set |
| */ |
| @VisibleForTesting |
| public void setPduSessionId(int pduSessionId) { |
| if (mPduSessionId != pduSessionId) { |
| logd("Changing pdu session id from: " + mPduSessionId + " to: " + pduSessionId + ", " |
| + "Handover state: " + handoverStateToString(this.mHandoverState)); |
| mPduSessionId = pduSessionId; |
| } |
| } |
| |
| /** |
| * Read the MTU value from link properties where it can be set from network. In case |
| * not set by the network, set it again using the mtu szie value defined in the APN |
| * database for the connected APN |
| */ |
| private void checkSetMtu(ApnSetting apn, LinkProperties lp) { |
| if (lp == null) return; |
| |
| if (apn == null || lp == null) return; |
| |
| if (lp.getMtu() != PhoneConstants.UNSET_MTU) { |
| if (DBG) log("MTU set by call response to: " + lp.getMtu()); |
| return; |
| } |
| |
| if (apn != null && apn.getMtu() != PhoneConstants.UNSET_MTU) { |
| lp.setMtu(apn.getMtu()); |
| if (DBG) log("MTU set by APN to: " + apn.getMtu()); |
| return; |
| } |
| |
| int mtu = mPhone.getContext().getResources().getInteger( |
| com.android.internal.R.integer.config_mobile_mtu); |
| if (mtu != PhoneConstants.UNSET_MTU) { |
| lp.setMtu(mtu); |
| if (DBG) log("MTU set by config resource to: " + mtu); |
| } |
| } |
| |
| //***** Constructor (NOTE: uses dcc.getHandler() as its Handler) |
| private DataConnection(Phone phone, String tagSuffix, int id, |
| DcTracker dct, DataServiceManager dataServiceManager, |
| DcTesterFailBringUpAll failBringUpAll, DcController dcc, |
| boolean doAllocatePduSessionId) { |
| super("DC-" + tagSuffix, dcc); |
| mTagSuffix = tagSuffix; |
| setLogRecSize(300); |
| setLogOnlyTransitions(true); |
| if (DBG) log("DataConnection created"); |
| |
| mPhone = phone; |
| mDct = dct; |
| mDataServiceManager = dataServiceManager; |
| mVcnManager = mPhone.getContext().getSystemService(VcnManager.class); |
| mTransportType = dataServiceManager.getTransportType(); |
| mDcTesterFailBringUpAll = failBringUpAll; |
| mDcController = dcc; |
| mId = id; |
| mCid = -1; |
| mDataRegState = mPhone.getServiceState().getDataRegistrationState(); |
| mIsSuspended = false; |
| mDataCallSessionStats = new DataCallSessionStats(mPhone); |
| mDoAllocatePduSessionId = doAllocatePduSessionId; |
| |
| int networkType = getNetworkType(); |
| mRilRat = ServiceState.networkTypeToRilRadioTechnology(networkType); |
| updateLinkBandwidthsFromCarrierConfig(mRilRat); |
| |
| addState(mDefaultState); |
| addState(mInactiveState, mDefaultState); |
| addState(mActivatingState, mDefaultState); |
| addState(mActiveState, mDefaultState); |
| addState(mDisconnectingState, mDefaultState); |
| addState(mDisconnectingErrorCreatingConnection, mDefaultState); |
| setInitialState(mInactiveState); |
| } |
| |
| private @NetworkType int getNetworkType() { |
| ServiceState ss = mPhone.getServiceState(); |
| int networkType = TelephonyManager.NETWORK_TYPE_UNKNOWN; |
| |
| NetworkRegistrationInfo nri = ss.getNetworkRegistrationInfo( |
| NetworkRegistrationInfo.DOMAIN_PS, mTransportType); |
| if (nri != null) { |
| networkType = nri.getAccessNetworkTechnology(); |
| } |
| |
| return networkType; |
| } |
| |
| /** |
| * Get the source transport for handover. For example, handover from WWAN to WLAN, WWAN is the |
| * source transport, and vice versa. |
| */ |
| private @TransportType int getHandoverSourceTransport() { |
| return mTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN |
| ? AccessNetworkConstants.TRANSPORT_TYPE_WLAN |
| : AccessNetworkConstants.TRANSPORT_TYPE_WWAN; |
| } |
| |
| /** |
| * API to generate the OsAppId for enterprise traffic category. |
| * @return byte[] representing OsId + length of OsAppId + OsAppId |
| */ |
| @VisibleForTesting |
| public static byte[] getEnterpriseOsAppId() { |
| byte[] osAppId = NetworkCapabilities.getCapabilityCarrierName( |
| NetworkCapabilities.NET_CAPABILITY_ENTERPRISE).getBytes(); |
| // 16 bytes for UUID, 1 byte for length of osAppId, and up to 255 bytes for osAppId |
| ByteBuffer bb = ByteBuffer.allocate(16 + 1 + osAppId.length); |
| bb.putLong(OS_ID.getMostSignificantBits()); |
| bb.putLong(OS_ID.getLeastSignificantBits()); |
| bb.put((byte) osAppId.length); |
| bb.put(osAppId); |
| if (VDBG) { |
| Rlog.d("DataConnection", "getEnterpriseOsAppId: " |
| + IccUtils.bytesToHexString(bb.array())); |
| } |
| return bb.array(); |
| } |
| |
| /** |
| * Begin setting up a data connection, calls setupDataCall |
| * and the ConnectionParams will be returned with the |
| * EVENT_SETUP_DATA_CONNECTION_DONE |
| * |
| * @param cp is the connection parameters |
| * |
| * @return Fail cause if failed to setup data connection. {@link DataFailCause#NONE} if success. |
| */ |
| private @DataFailureCause int connect(ConnectionParams cp) { |
| log("connect: carrier='" + mApnSetting.getEntryName() |
| + "' APN='" + mApnSetting.getApnName() |
| + "' proxy='" + mApnSetting.getProxyAddressAsString() |
| + "' port='" + mApnSetting.getProxyPort() + "'"); |
| if (cp.mApnContext != null) cp.mApnContext.requestLog("DataConnection.connect"); |
| |
| // Check if we should fake an error. |
| if (mDcTesterFailBringUpAll.getDcFailBringUp().mCounter > 0) { |
| DataCallResponse response = new DataCallResponse.Builder() |
| .setCause(mDcTesterFailBringUpAll.getDcFailBringUp().mFailCause) |
| .setRetryDurationMillis( |
| mDcTesterFailBringUpAll.getDcFailBringUp().mSuggestedRetryTime) |
| .setMtuV4(PhoneConstants.UNSET_MTU) |
| .setMtuV6(PhoneConstants.UNSET_MTU) |
| .build(); |
| |
| Message msg = obtainMessage(EVENT_SETUP_DATA_CONNECTION_DONE, cp); |
| AsyncResult.forMessage(msg, response, null); |
| sendMessage(msg); |
| if (DBG) { |
| log("connect: FailBringUpAll=" + mDcTesterFailBringUpAll.getDcFailBringUp() |
| + " send error response=" + response); |
| } |
| mDcTesterFailBringUpAll.getDcFailBringUp().mCounter -= 1; |
| return DataFailCause.NONE; |
| } |
| |
| mCreateTime = -1; |
| mLastFailTime = -1; |
| mLastFailCause = DataFailCause.NONE; |
| |
| Message msg = obtainMessage(EVENT_SETUP_DATA_CONNECTION_DONE, cp); |
| msg.obj = cp; |
| |
| DataProfile dp = DcTracker.createDataProfile(mApnSetting, cp.mProfileId, |
| cp.mIsPreferredApn); |
| |
| // 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(); |
| |
| // If the apn is NOT metered, we will allow data roaming regardless of the setting. |
| boolean isUnmeteredApnType = !ApnSettingUtils.isMeteredApnType( |
| cp.mApnContext.getApnTypeBitmask(), mPhone); |
| |
| // 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() |
| || isUnmeteredApnType)); |
| |
| String dnn = null; |
| byte[] osAppId = null; |
| if (cp.mApnContext.getApnTypeBitmask() == ApnSetting.TYPE_ENTERPRISE) { |
| osAppId = getEnterpriseOsAppId(); |
| } else { |
| dnn = mApnSetting.getApnName(); |
| } |
| final TrafficDescriptor td = osAppId == null && dnn == null ? null |
| : new TrafficDescriptor(dnn, osAppId); |
| final boolean matchAllRuleAllowed = td == null || td.getOsAppId() == null; |
| |
| if (DBG) { |
| log("allowRoaming=" + allowRoaming |
| + ", mPhone.getDataRoamingEnabled()=" + mPhone.getDataRoamingEnabled() |
| + ", isModemRoaming=" + isModemRoaming |
| + ", mPhone.getServiceState().getDataRoaming()=" |
| + mPhone.getServiceState().getDataRoaming() |
| + ", isUnmeteredApnType=" + isUnmeteredApnType |
| + ", trafficDescriptor=" + td |
| + ", matchAllRuleAllowed=" + matchAllRuleAllowed |
| ); |
| } |
| |
| // Check if this data setup is a handover. |
| LinkProperties linkProperties = null; |
| int reason = DataService.REQUEST_REASON_NORMAL; |
| if (cp.mRequestType == REQUEST_TYPE_HANDOVER) { |
| // If this is a data setup for handover, we need to pass the link properties |
| // of the existing data connection to the modem. |
| DcTracker srcDcTracker = mPhone.getDcTracker(getHandoverSourceTransport()); |
| if (srcDcTracker == null || cp.mApnContext == null) { |
| loge("connect: Handover failed. dcTracker=" + srcDcTracker + ", apnContext=" |
| + cp.mApnContext); |
| return DataFailCause.HANDOVER_FAILED; |
| } |
| |
| |
| // srcDc is the source data connection while the current instance is the target |
| DataConnection srcDc = |
| srcDcTracker.getDataConnectionByApnType(cp.mApnContext.getApnType()); |
| if (srcDc == null) { |
| loge("connect: Can't find data connection for handover."); |
| return DataFailCause.HANDOVER_FAILED; |
| } |
| |
| // Helpful for logging purposes |
| DataServiceManager srcDsm = srcDc.mDataServiceManager; |
| String srcDsmTag = (srcDsm == null ? "(null)" : srcDsm.getTag()); |
| logd("connect: REQUEST_TYPE_HANDOVER - Request handover from " + srcDc.getName() |
| + ", targetDsm=" + mDataServiceManager.getTag() |
| + ", sourceDsm=" + srcDsmTag); |
| |
| |
| /* startHandover is called on the source data connection, and if successful, |
| we ask the target data connection (which is the current instance) to call |
| #setupDataCall with request type handover. |
| */ |
| Consumer<Integer> onCompleted = (dataServiceCallbackResultCode) -> |
| /* startHandover is called on the srcDc handler, but the callback needs to |
| be called on the current (which is the targetDc) handler which is why we |
| call sendRunnableMessage. */ |
| sendRunnableMessage(EVENT_START_HANDOVER_ON_TARGET, |
| (inCorrectState) -> requestHandover(inCorrectState, srcDc, |
| dataServiceCallbackResultCode, |
| cp, msg, dp, isModemRoaming, allowRoaming)); |
| srcDc.startHandover(onCompleted); |
| return DataFailCause.NONE; |
| } |
| |
| // setup data call for REQUEST_TYPE_NORMAL |
| allocatePduSessionId(psi -> { |
| this.setPduSessionId(psi); |
| mDataServiceManager.setupDataCall( |
| ServiceState.rilRadioTechnologyToAccessNetworkType(cp.mRilRat), |
| dp, |
| isModemRoaming, |
| allowRoaming, |
| reason, |
| linkProperties, |
| psi, |
| null, //slice info is null since this is not a handover |
| td, |
| matchAllRuleAllowed, |
| msg); |
| TelephonyMetrics.getInstance().writeSetupDataCall(mPhone.getPhoneId(), cp.mRilRat, |
| dp.getProfileId(), dp.getApn(), dp.getProtocolType()); |
| }); |
| return DataFailCause.NONE; |
| } |
| |
| private void allocatePduSessionId(Consumer<Integer> allocateCallback) { |
| if (getDoAllocatePduSessionId()) { |
| Message msg = this.obtainMessage(EVENT_ALLOCATE_PDU_SESSION_ID); |
| msg.obj = allocateCallback; |
| mPhone.mCi.allocatePduSessionId(msg); |
| } else { |
| allocateCallback.accept(PDU_SESSION_ID_NOT_SET); |
| } |
| } |
| |
| private void onRquestHandoverFailed(ConnectionParams cp) { |
| sendMessage(obtainMessage(EVENT_CANCEL_HANDOVER)); |
| notifyConnectCompleted(cp, DataFailCause.UNKNOWN, |
| DataCallResponse.HANDOVER_FAILURE_MODE_UNKNOWN, false); |
| } |
| |
| private void requestHandover(boolean inCorrectState, DataConnection srcDc, |
| @DataServiceCallback.ResultCode int resultCode, |
| ConnectionParams cp, Message msg, DataProfile dp, boolean isModemRoaming, |
| boolean allowRoaming) { |
| |
| if (!inCorrectState) { |
| logd("requestHandover: Not in correct state"); |
| if (isResultCodeSuccess(resultCode)) { |
| if (srcDc != null) { |
| logd("requestHandover: Not in correct state - Success result code"); |
| // We need to cancel the handover on source if we ended up in the wrong state. |
| srcDc.cancelHandover(); |
| } else { |
| logd("requestHandover: Not in correct state - Success result code - " |
| + "srcdc = null"); |
| } |
| } |
| onRquestHandoverFailed(cp); |
| return; |
| } else if (!isResultCodeSuccess(resultCode)) { |
| if (DBG) { |
| logd("requestHandover: Non success result code from DataService, " |
| + "setupDataCall will not be called, result code = " |
| + DataServiceCallback.resultCodeToString(resultCode)); |
| } |
| onRquestHandoverFailed(cp); |
| return; |
| } |
| |
| if (srcDc == null) { |
| loge("requestHandover: Cannot find source data connection."); |
| onRquestHandoverFailed(cp); |
| return; |
| } |
| |
| LinkProperties linkProperties; |
| int reason; |
| |
| // Preserve the potential network agent from the source data connection. The ownership |
| // is not transferred at this moment. |
| mHandoverSourceNetworkAgent = srcDc.getNetworkAgent(); |
| if (mHandoverSourceNetworkAgent == null) { |
| loge("requestHandover: Cannot get network agent from the source dc " + srcDc.getName()); |
| onRquestHandoverFailed(cp); |
| return; |
| } |
| |
| linkProperties = srcDc.getLinkProperties(); |
| if (linkProperties == null || linkProperties.getLinkAddresses().isEmpty()) { |
| loge("requestHandover: Can't find link properties of handover data connection. dc=" |
| + srcDc); |
| onRquestHandoverFailed(cp); |
| return; |
| } |
| |
| mHandoverLocalLog.log("Handover started. Preserved the agent."); |
| log("Get the handover source network agent: " + mHandoverSourceNetworkAgent); |
| |
| reason = DataService.REQUEST_REASON_HANDOVER; |
| |
| TrafficDescriptor td = dp.getApn() == null ? null |
| : new TrafficDescriptor(dp.getApn(), null); |
| boolean matchAllRuleAllowed = true; |
| |
| mDataServiceManager.setupDataCall( |
| ServiceState.rilRadioTechnologyToAccessNetworkType(cp.mRilRat), |
| dp, |
| isModemRoaming, |
| allowRoaming, |
| reason, |
| linkProperties, |
| srcDc.getPduSessionId(), |
| srcDc.getSliceInfo(), |
| td, |
| matchAllRuleAllowed, |
| msg); |
| TelephonyMetrics.getInstance().writeSetupDataCall(mPhone.getPhoneId(), cp.mRilRat, |
| dp.getProfileId(), dp.getApn(), dp.getProtocolType()); |
| } |
| |
| /** |
| * Called on the source data connection from the target data connection. |
| */ |
| @VisibleForTesting |
| public void startHandover(Consumer<Integer> onTargetDcComplete) { |
| logd("startHandover: " + toStringSimple()); |
| // Set the handover state to being transferred on "this" data connection which is the src. |
| setHandoverState(HANDOVER_STATE_BEING_TRANSFERRED); |
| |
| Consumer<Integer> onSrcDcComplete = |
| resultCode -> onHandoverStarted(resultCode, onTargetDcComplete); |
| /* |
| The flow here is: |
| srcDc#startHandover -> dataService#startHandover -> (onHandoverStarted) -> |
| onSrcDcComplete -> onTargetDcComplete |
| */ |
| mDataServiceManager.startHandover(mCid, |
| this.obtainMessage(EVENT_START_HANDOVER, |
| onSrcDcComplete)); |
| } |
| |
| /** |
| * Called on the source data connection when the async call to start handover is complete |
| */ |
| private void onHandoverStarted(@DataServiceCallback.ResultCode int resultCode, |
| Consumer<Integer> onTargetDcComplete) { |
| logd("onHandoverStarted: " + toStringSimple()); |
| if (!isResultCodeSuccess(resultCode)) { |
| setHandoverState(HANDOVER_STATE_IDLE); |
| } |
| onTargetDcComplete.accept(resultCode); |
| } |
| |
| private void cancelHandover() { |
| if (mHandoverState != HANDOVER_STATE_BEING_TRANSFERRED) { |
| logd("cancelHandover: handover state is " + handoverStateToString(mHandoverState) |
| + ", expecting HANDOVER_STATE_BEING_TRANSFERRED"); |
| } |
| mDataServiceManager.cancelHandover(mCid, this.obtainMessage(EVENT_CANCEL_HANDOVER)); |
| setHandoverState(HANDOVER_STATE_IDLE); |
| } |
| |
| /** |
| * Update NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED based on congested override |
| * @param isCongested whether this DC should be set to congested or not |
| */ |
| public void onCongestednessChanged(boolean isCongested) { |
| sendMessage(obtainMessage(EVENT_DATA_CONNECTION_CONGESTEDNESS_CHANGED, isCongested)); |
| } |
| |
| /** |
| * Update NetworkCapabilities.NET_CAPABILITY_NOT_METERED based on metered override |
| * @param isUnmetered whether this DC should be set to unmetered or not |
| */ |
| public void onMeterednessChanged(boolean isUnmetered) { |
| sendMessage(obtainMessage(EVENT_DATA_CONNECTION_METEREDNESS_CHANGED, isUnmetered)); |
| } |
| |
| /** |
| * TearDown the data connection when the deactivation is complete a Message with |
| * msg.what == EVENT_DEACTIVATE_DONE |
| * |
| * @param o is the object returned in the AsyncResult.obj. |
| */ |
| private void tearDownData(Object o) { |
| int discReason = DataService.REQUEST_REASON_NORMAL; |
| ApnContext apnContext = null; |
| if ((o != null) && (o instanceof DisconnectParams)) { |
| DisconnectParams dp = (DisconnectParams) o; |
| apnContext = dp.mApnContext; |
| if (TextUtils.equals(dp.mReason, Phone.REASON_RADIO_TURNED_OFF) |
| || TextUtils.equals(dp.mReason, Phone.REASON_PDP_RESET)) { |
| discReason = DataService.REQUEST_REASON_SHUTDOWN; |
| } else if (dp.mReleaseType == DcTracker.RELEASE_TYPE_HANDOVER) { |
| discReason = DataService.REQUEST_REASON_HANDOVER; |
| } |
| } |
| |
| String str = "tearDownData. mCid=" + mCid + ", reason=" + discReason; |
| if (DBG) log(str); |
| if (apnContext != null) apnContext.requestLog(str); |
| |
| |
| //Needed to be final to work in a closure |
| final int fDiscReason = discReason; |
| releasePduSessionId(() -> { |
| // This is run after release pdu session id is complete |
| this.setPduSessionId(PDU_SESSION_ID_NOT_SET); |
| mDataServiceManager.deactivateDataCall(mCid, fDiscReason, |
| obtainMessage(EVENT_DEACTIVATE_DONE, mTag, 0, o)); |
| mDataCallSessionStats.setDeactivateDataCallReason(fDiscReason); |
| }); |
| } |
| |
| private void releasePduSessionId(Runnable releaseCallback) { |
| // If we are not in the middle of a handover and have a real pdu session id, then we release |
| if (mHandoverState != HANDOVER_STATE_BEING_TRANSFERRED |
| && this.getPduSessionId() != PDU_SESSION_ID_NOT_SET) { |
| Message msg = this.obtainMessage(EVENT_RELEASE_PDU_SESSION_ID); |
| msg.obj = releaseCallback; |
| mPhone.mCi.releasePduSessionId(msg, this.getPduSessionId()); |
| } else { |
| // Just go and run the callback since we either have no pdu session id to release |
| // or we are in the middle of a handover |
| releaseCallback.run(); |
| } |
| } |
| |
| private void notifyAllWithEvent(ApnContext alreadySent, int event, String reason) { |
| for (ConnectionParams cp : mApnContexts.values()) { |
| ApnContext apnContext = cp.mApnContext; |
| if (apnContext == alreadySent) continue; |
| if (reason != null) apnContext.setReason(reason); |
| Pair<ApnContext, Integer> pair = new Pair<>(apnContext, cp.mConnectionGeneration); |
| Message msg = mDct.obtainMessage(event, cp.mRequestType, |
| DataCallResponse.HANDOVER_FAILURE_MODE_UNKNOWN, pair); |
| AsyncResult.forMessage(msg); |
| msg.sendToTarget(); |
| } |
| } |
| |
| /** |
| * Send the connectionCompletedMsg. |
| * |
| * @param cp is the ConnectionParams |
| * @param cause and if no error the cause is DataFailCause.NONE |
| * @param handoverFailureMode The action on handover failure |
| * @param sendAll is true if all contexts are to be notified |
| */ |
| private void notifyConnectCompleted(ConnectionParams cp, @DataFailureCause int cause, |
| @HandoverFailureMode int handoverFailureMode, boolean sendAll) { |
| ApnContext alreadySent = null; |
| |
| if (cp != null && cp.mOnCompletedMsg != null) { |
| // Get the completed message but only use it once |
| Message connectionCompletedMsg = cp.mOnCompletedMsg; |
| cp.mOnCompletedMsg = null; |
| alreadySent = cp.mApnContext; |
| |
| long timeStamp = System.currentTimeMillis(); |
| connectionCompletedMsg.arg1 = cp.mRequestType; |
| connectionCompletedMsg.arg2 = handoverFailureMode; |
| |
| if (cause == DataFailCause.NONE) { |
| mCreateTime = timeStamp; |
| AsyncResult.forMessage(connectionCompletedMsg); |
| } else { |
| mLastFailCause = cause; |
| mLastFailTime = timeStamp; |
| |
| // Return message with a Throwable exception to signify an error. |
| if (cause == DataFailCause.NONE) cause = DataFailCause.UNKNOWN; |
| AsyncResult.forMessage(connectionCompletedMsg, cause, |
| new Throwable(DataFailCause.toString(cause))); |
| } |
| if (DBG) { |
| log("notifyConnectCompleted at " + timeStamp + " cause=" |
| + DataFailCause.toString(cause) + " connectionCompletedMsg=" |
| + msgToString(connectionCompletedMsg)); |
| } |
| |
| connectionCompletedMsg.sendToTarget(); |
| } |
| if (sendAll) { |
| log("Send to all. " + alreadySent + " " + DataFailCause.toString(cause)); |
| notifyAllWithEvent(alreadySent, DctConstants.EVENT_DATA_SETUP_COMPLETE_ERROR, |
| DataFailCause.toString(cause)); |
| } |
| } |
| |
| /** |
| * Send ar.userObj if its a message, which is should be back to originator. |
| * |
| * @param dp is the DisconnectParams. |
| */ |
| private void notifyDisconnectCompleted(DisconnectParams dp, boolean sendAll) { |
| if (VDBG) log("NotifyDisconnectCompleted"); |
| |
| ApnContext alreadySent = null; |
| String reason = null; |
| |
| if (dp != null && dp.mOnCompletedMsg != null) { |
| // Get the completed message but only use it once |
| Message msg = dp.mOnCompletedMsg; |
| dp.mOnCompletedMsg = null; |
| if (msg.obj instanceof ApnContext) { |
| alreadySent = (ApnContext)msg.obj; |
| } |
| reason = dp.mReason; |
| if (VDBG) { |
| log(String.format("msg=%s msg.obj=%s", msg.toString(), |
| ((msg.obj instanceof String) ? (String) msg.obj : "<no-reason>"))); |
| } |
| AsyncResult.forMessage(msg); |
| msg.sendToTarget(); |
| } |
| if (sendAll) { |
| if (reason == null) { |
| reason = DataFailCause.toString(DataFailCause.UNKNOWN); |
| } |
| notifyAllWithEvent(alreadySent, DctConstants.EVENT_DISCONNECT_DONE, reason); |
| } |
| if (DBG) log("NotifyDisconnectCompleted DisconnectParams=" + dp); |
| } |
| |
| private void sendRunnableMessage(int eventCode, @NonNull final Consumer<Boolean> r) { |
| sendMessage(eventCode, r); |
| } |
| |
| /* |
| * ************************************************************************** |
| * Begin Members and methods owned by DataConnectionTracker but stored |
| * in a DataConnection because there is one per connection. |
| * ************************************************************************** |
| */ |
| |
| /* |
| * The id is owned by DataConnectionTracker. |
| */ |
| private int mId; |
| |
| /** |
| * Get the DataConnection ID |
| */ |
| public int getDataConnectionId() { |
| return mId; |
| } |
| |
| /* |
| * ************************************************************************** |
| * End members owned by DataConnectionTracker |
| * ************************************************************************** |
| */ |
| |
| /** |
| * Clear all settings called when entering mInactiveState. |
| */ |
| private void clearSettings() { |
| if (DBG) log("clearSettings"); |
| |
| mCreateTime = -1; |
| mLastFailTime = -1; |
| mLastFailCause = DataFailCause.NONE; |
| mCid = -1; |
| |
| mPcscfAddr = new String[5]; |
| |
| mLinkProperties = new LinkProperties(); |
| mApnContexts.clear(); |
| mApnSetting = null; |
| mUnmeteredUseOnly = false; |
| mMmsUseOnly = false; |
| mEnterpriseUse = false; |
| mRestrictedNetworkOverride = false; |
| mDcFailCause = DataFailCause.NONE; |
| mDisabledApnTypeBitMask = 0; |
| mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; |
| mCongestedOverride = false; |
| mUnmeteredOverride = false; |
| mDownlinkBandwidth = 14; |
| mUplinkBandwidth = 14; |
| mIsSuspended = false; |
| mHandoverState = HANDOVER_STATE_IDLE; |
| mHandoverFailureMode = DataCallResponse.HANDOVER_FAILURE_MODE_UNKNOWN; |
| mSliceInfo = null; |
| mDefaultQos = null; |
| mQosBearerSessions.clear(); |
| mTrafficDescriptors.clear(); |
| } |
| |
| /** |
| * Process setup data completion result from data service |
| * |
| * @param resultCode The result code returned by data service |
| * @param response Data call setup response from data service |
| * @param cp The original connection params used for data call setup |
| * @return Setup result |
| */ |
| private SetupResult onSetupConnectionCompleted(@DataServiceCallback.ResultCode int resultCode, |
| DataCallResponse response, |
| ConnectionParams cp) { |
| SetupResult result; |
| |
| log("onSetupConnectionCompleted: resultCode=" + resultCode + ", response=" + response); |
| if (cp.mTag != mTag) { |
| if (DBG) { |
| log("onSetupConnectionCompleted stale cp.tag=" + cp.mTag + ", mtag=" + mTag); |
| } |
| result = SetupResult.ERROR_STALE; |
| } else if (resultCode == DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE) { |
| result = SetupResult.ERROR_RADIO_NOT_AVAILABLE; |
| result.mFailCause = DataFailCause.RADIO_NOT_AVAILABLE; |
| } else if (resultCode == DataServiceCallback.RESULT_ERROR_TEMPORARILY_UNAVAILABLE) { |
| result = SetupResult.ERROR_DATA_SERVICE_SPECIFIC_ERROR; |
| result.mFailCause = DataFailCause.SERVICE_TEMPORARILY_UNAVAILABLE; |
| } else if (resultCode == DataServiceCallback.RESULT_ERROR_INVALID_ARG) { |
| result = SetupResult.ERROR_INVALID_ARG; |
| result.mFailCause = DataFailCause.UNACCEPTABLE_NETWORK_PARAMETER; |
| } else if (response.getCause() != 0) { |
| if (response.getCause() == DataFailCause.RADIO_NOT_AVAILABLE) { |
| result = SetupResult.ERROR_RADIO_NOT_AVAILABLE; |
| result.mFailCause = DataFailCause.RADIO_NOT_AVAILABLE; |
| } else { |
| result = SetupResult.ERROR_DATA_SERVICE_SPECIFIC_ERROR; |
| result.mFailCause = DataFailCause.getFailCause(response.getCause()); |
| } |
| } else if (cp.mApnContext.getApnTypeBitmask() == ApnSetting.TYPE_ENTERPRISE |
| && mDcController.getActiveDcByCid(response.getId()) != null) { |
| if (DBG) log("DataConnection already exists for cid: " + response.getId()); |
| result = SetupResult.ERROR_DUPLICATE_CID; |
| result.mFailCause = DataFailCause.DUPLICATE_CID; |
| } else if (cp.mApnContext.getApnTypeBitmask() == ApnSetting.TYPE_ENTERPRISE |
| && !mDcController.isDefaultDataActive()) { |
| if (DBG) log("No default data connection currently active"); |
| mCid = response.getId(); |
| result = SetupResult.ERROR_NO_DEFAULT_CONNECTION; |
| result.mFailCause = DataFailCause.NO_DEFAULT_DATA; |
| } else { |
| if (DBG) log("onSetupConnectionCompleted received successful DataCallResponse"); |
| mCid = response.getId(); |
| |
| if (response.getPduSessionId() != getPduSessionId()) { |
| if (getDoAllocatePduSessionId()) { |
| loge("The pdu session id on DataCallResponse is different than the one " |
| + "allocated. response psi=" + response.getPduSessionId() |
| + ", allocated psi=" + getPduSessionId()); |
| } else { |
| setPduSessionId(response.getPduSessionId()); |
| } |
| } |
| |
| updatePcscfAddr(response); |
| updateResponseFields(response); |
| result = updateLinkProperty(response).setupResult; |
| } |
| |
| return result; |
| } |
| |
| private static boolean isResultCodeSuccess(int resultCode) { |
| return resultCode == DataServiceCallback.RESULT_SUCCESS |
| || resultCode == DataServiceCallback.RESULT_ERROR_UNSUPPORTED; |
| } |
| |
| private boolean isDnsOk(String[] domainNameServers) { |
| if (NULL_IP.equals(domainNameServers[0]) && NULL_IP.equals(domainNameServers[1]) |
| && !mPhone.isDnsCheckDisabled()) { |
| // Work around a race condition where QMI does not fill in DNS: |
| // Deactivate PDP and let DataConnectionTracker retry. |
| // Do not apply the race condition workaround for MMS APN |
| // if Proxy is an IP-address. |
| // Otherwise, the default APN will not be restored anymore. |
| if (!isIpAddress(mApnSetting.getMmsProxyAddressAsString())) { |
| log(String.format( |
| "isDnsOk: return false apn.types=%d APN_TYPE_MMS=%s isIpAddress(%s)=%s", |
| mApnSetting.getApnTypeBitmask(), ApnSetting.TYPE_MMS_STRING, |
| mApnSetting.getMmsProxyAddressAsString(), |
| isIpAddress(mApnSetting.getMmsProxyAddressAsString()))); |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| /** |
| * TCP buffer size config based on the ril technology. There are 6 parameters |
| * read_min, read_default, read_max, write_min, write_default, write_max in the TCP buffer |
| * config string and they are separated by a comma. The unit of these parameters is byte. |
| */ |
| private static final String TCP_BUFFER_SIZES_GPRS = "4092,8760,48000,4096,8760,48000"; |
| private static final String TCP_BUFFER_SIZES_EDGE = "4093,26280,70800,4096,16384,70800"; |
| private static final String TCP_BUFFER_SIZES_UMTS = "58254,349525,1048576,58254,349525,1048576"; |
| private static final String TCP_BUFFER_SIZES_1XRTT = "16384,32768,131072,4096,16384,102400"; |
| private static final String TCP_BUFFER_SIZES_EVDO = "4094,87380,262144,4096,16384,262144"; |
| private static final String TCP_BUFFER_SIZES_EHRPD = "131072,262144,1048576,4096,16384,524288"; |
| private static final String TCP_BUFFER_SIZES_HSDPA = "61167,367002,1101005,8738,52429,262114"; |
| private static final String TCP_BUFFER_SIZES_HSPA = "40778,244668,734003,16777,100663,301990"; |
| private static final String TCP_BUFFER_SIZES_LTE = |
| "524288,1048576,2097152,262144,524288,1048576"; |
| private static final String TCP_BUFFER_SIZES_HSPAP = |
| "122334,734003,2202010,32040,192239,576717"; |
| private static final String TCP_BUFFER_SIZES_NR = |
| "2097152,6291456,16777216,512000,2097152,8388608"; |
| private static final String TCP_BUFFER_SIZES_LTE_CA = |
| "4096,6291456,12582912,4096,1048576,2097152"; |
| |
| private void updateTcpBufferSizes(int rilRat) { |
| String sizes = null; |
| ServiceState ss = mPhone.getServiceState(); |
| if (rilRat == ServiceState.RIL_RADIO_TECHNOLOGY_LTE && |
| ss.isUsingCarrierAggregation()) { |
| rilRat = ServiceState.RIL_RADIO_TECHNOLOGY_LTE_CA; |
| } |
| String ratName = ServiceState.rilRadioTechnologyToString(rilRat).toLowerCase(Locale.ROOT); |
| // ServiceState gives slightly different names for EVDO tech ("evdo-rev.0" for ex) |
| // - patch it up: |
| if (rilRat == ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_0 || |
| rilRat == ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_A || |
| rilRat == ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_B) { |
| ratName = RAT_NAME_EVDO; |
| } |
| |
| // NR 5G Non-Standalone use LTE cell as the primary cell, the ril technology is LTE in this |
| // case. We use NR 5G TCP buffer size when connected to NR 5G Non-Standalone network. |
| if (mTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN |
| && ((rilRat == ServiceState.RIL_RADIO_TECHNOLOGY_LTE || |
| rilRat == ServiceState.RIL_RADIO_TECHNOLOGY_LTE_CA) && isNRConnected()) |
| && mPhone.getServiceStateTracker().getNrContextIds().contains(mCid)) { |
| ratName = RAT_NAME_5G; |
| } |
| |
| log("updateTcpBufferSizes: " + ratName); |
| |
| // in the form: "ratname:rmem_min,rmem_def,rmem_max,wmem_min,wmem_def,wmem_max" |
| String[] configOverride = mPhone.getContext().getResources().getStringArray( |
| com.android.internal.R.array.config_mobile_tcp_buffers); |
| for (int i = 0; i < configOverride.length; i++) { |
| String[] split = configOverride[i].split(":"); |
| if (ratName.equals(split[0]) && split.length == 2) { |
| sizes = split[1]; |
| break; |
| } |
| } |
| |
| if (sizes == null) { |
| // no override - use telephony defaults |
| // doing it this way allows device or carrier to just override the types they |
| // care about and inherit the defaults for the others. |
| switch (rilRat) { |
| case ServiceState.RIL_RADIO_TECHNOLOGY_GPRS: |
| sizes = TCP_BUFFER_SIZES_GPRS; |
| break; |
| case ServiceState.RIL_RADIO_TECHNOLOGY_EDGE: |
| sizes = TCP_BUFFER_SIZES_EDGE; |
| break; |
| case ServiceState.RIL_RADIO_TECHNOLOGY_UMTS: |
| sizes = TCP_BUFFER_SIZES_UMTS; |
| break; |
| case ServiceState.RIL_RADIO_TECHNOLOGY_1xRTT: |
| sizes = TCP_BUFFER_SIZES_1XRTT; |
| break; |
| case ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_0: |
| case ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_A: |
| case ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_B: |
| sizes = TCP_BUFFER_SIZES_EVDO; |
| break; |
| case ServiceState.RIL_RADIO_TECHNOLOGY_EHRPD: |
| sizes = TCP_BUFFER_SIZES_EHRPD; |
| break; |
| case ServiceState.RIL_RADIO_TECHNOLOGY_HSDPA: |
| sizes = TCP_BUFFER_SIZES_HSDPA; |
| break; |
| case ServiceState.RIL_RADIO_TECHNOLOGY_HSPA: |
| case ServiceState.RIL_RADIO_TECHNOLOGY_HSUPA: |
| sizes = TCP_BUFFER_SIZES_HSPA; |
| break; |
| case ServiceState.RIL_RADIO_TECHNOLOGY_LTE: |
| // Use NR 5G TCP buffer size when connected to NR 5G Non-Standalone network. |
| if (RAT_NAME_5G.equals(ratName)) { |
| sizes = TCP_BUFFER_SIZES_NR; |
| } else { |
| sizes = TCP_BUFFER_SIZES_LTE; |
| } |
| break; |
| case ServiceState.RIL_RADIO_TECHNOLOGY_LTE_CA: |
| // Use NR 5G TCP buffer size when connected to NR 5G Non-Standalone network. |
| if (RAT_NAME_5G.equals(ratName)) { |
| sizes = TCP_BUFFER_SIZES_NR; |
| } else { |
| sizes = TCP_BUFFER_SIZES_LTE_CA; |
| } |
| break; |
| case ServiceState.RIL_RADIO_TECHNOLOGY_HSPAP: |
| sizes = TCP_BUFFER_SIZES_HSPAP; |
| break; |
| case ServiceState.RIL_RADIO_TECHNOLOGY_NR: |
| sizes = TCP_BUFFER_SIZES_NR; |
| break; |
| default: |
| // Leave empty - this will let ConnectivityService use the system default. |
| break; |
| } |
| } |
| mLinkProperties.setTcpBufferSizes(sizes); |
| } |
| |
| private void updateLinkBandwidthsFromCarrierConfig(int rilRat) { |
| String ratName = ServiceState.rilRadioTechnologyToString(rilRat); |
| if (rilRat == ServiceState.RIL_RADIO_TECHNOLOGY_LTE && isNRConnected()) { |
| ratName = mPhone.getServiceState().getNrFrequencyRange() |
| == ServiceState.FREQUENCY_RANGE_MMWAVE |
| ? DctConstants.RAT_NAME_NR_NSA_MMWAVE : DctConstants.RAT_NAME_NR_NSA; |
| } |
| |
| if (DBG) log("updateLinkBandwidthsFromCarrierConfig: " + ratName); |
| |
| Pair<Integer, Integer> values = mDct.getLinkBandwidthsFromCarrierConfig(ratName); |
| if (values == null) { |
| values = new Pair<>(14, 14); |
| } |
| mDownlinkBandwidth = values.first; |
| mUplinkBandwidth = values.second; |
| } |
| |
| |
| private void updateLinkBandwidthsFromModem(List<LinkCapacityEstimate> lceList) { |
| if (DBG) log("updateLinkBandwidthsFromModem: lceList=" + lceList); |
| boolean downlinkUpdated = false; |
| boolean uplinkUpdated = false; |
| LinkCapacityEstimate lce = lceList.get(0); |
| // LCE status deprecated in IRadio 1.2, so only check for IRadio < 1.2 |
| if (mPhone.getHalVersion().greaterOrEqual(RIL.RADIO_HAL_VERSION_1_2) |
| || mPhone.getLceStatus() == RILConstants.LCE_ACTIVE) { |
| if (lce.getDownlinkCapacityKbps() != LinkCapacityEstimate.INVALID) { |
| mDownlinkBandwidth = lce.getDownlinkCapacityKbps(); |
| downlinkUpdated = true; |
| } |
| if (lce.getUplinkCapacityKbps() != LinkCapacityEstimate.INVALID) { |
| mUplinkBandwidth = lce.getUplinkCapacityKbps(); |
| uplinkUpdated = true; |
| } |
| } |
| |
| if (!downlinkUpdated || !uplinkUpdated) { |
| fallBackToCarrierConfigValues(downlinkUpdated, uplinkUpdated); |
| } |
| |
| if (mNetworkAgent != null) { |
| mNetworkAgent.sendNetworkCapabilities(getNetworkCapabilities(), DataConnection.this); |
| } |
| } |
| |
| private void updateLinkBandwidthsFromBandwidthEstimator(int uplinkBandwidthKbps, |
| int downlinkBandwidthKbps) { |
| if (DBG) { |
| log("updateLinkBandwidthsFromBandwidthEstimator, UL= " |
| + uplinkBandwidthKbps + " DL= " + downlinkBandwidthKbps); |
| } |
| boolean downlinkUpdated = false; |
| boolean uplinkUpdated = false; |
| if (downlinkBandwidthKbps > 0) { |
| mDownlinkBandwidth = downlinkBandwidthKbps; |
| downlinkUpdated = true; |
| } |
| if (uplinkBandwidthKbps > 0) { |
| mUplinkBandwidth = uplinkBandwidthKbps; |
| uplinkUpdated = true; |
| } |
| |
| if (!downlinkUpdated || !uplinkUpdated) { |
| fallBackToCarrierConfigValues(downlinkUpdated, uplinkUpdated); |
| } |
| if (mNetworkAgent != null) { |
| mNetworkAgent.sendNetworkCapabilities(getNetworkCapabilities(), DataConnection.this); |
| } |
| } |
| |
| private void fallBackToCarrierConfigValues(boolean downlinkUpdated, boolean uplinkUpdated) { |
| String ratName = ServiceState.rilRadioTechnologyToString(mRilRat); |
| if (mRilRat == ServiceState.RIL_RADIO_TECHNOLOGY_LTE && isNRConnected()) { |
| ratName = mPhone.getServiceState().getNrFrequencyRange() |
| == ServiceState.FREQUENCY_RANGE_MMWAVE |
| ? DctConstants.RAT_NAME_NR_NSA_MMWAVE : DctConstants.RAT_NAME_NR_NSA; |
| } |
| Pair<Integer, Integer> values = mDct.getLinkBandwidthsFromCarrierConfig(ratName); |
| if (values != null) { |
| if (!downlinkUpdated) { |
| mDownlinkBandwidth = values.first; |
| } |
| if (!uplinkUpdated) { |
| mUplinkBandwidth = values.second; |
| } |
| } |
| } |
| |
| private boolean isBandwidthSourceKey(String source) { |
| return source.equals(mPhone.getContext().getResources().getString( |
| com.android.internal.R.string.config_bandwidthEstimateSource)); |
| } |
| |
| /** |
| * Indicates if this data connection was established for unmetered use only. Note that this |
| * flag should be populated when data becomes active. And if it is set to true, it can be set to |
| * false later when we are reevaluating the data connection. But if it is set to false, it |
| * can never become true later because setting it to true will cause this data connection |
| * losing some immutable network capabilities, which can cause issues in connectivity service. |
| */ |
| private boolean mUnmeteredUseOnly = false; |
| |
| /** |
| * Indicates if this data connection was established for MMS use only. This is true only when |
| * mobile data is disabled but the user allows sending and receiving MMS messages. If the data |
| * enabled settings indicate that MMS data is allowed unconditionally, MMS can be sent when data |
| * is disabled even if it is a metered APN type. |
| */ |
| private boolean mMmsUseOnly = false; |
| |
| /** |
| * Indicates if when this connection was established we had a restricted/privileged |
| * NetworkRequest and needed it to overcome data-enabled limitations. |
| * |
| * This flag overrides the APN-based restriction capability, restricting the network |
| * based on both having a NetworkRequest with restricted AND needing a restricted |
| * bit to overcome user-disabled status. This allows us to handle the common case |
| * of having both restricted requests and unrestricted requests for the same apn: |
| * if conditions require a restricted network to overcome user-disabled then it must |
| * be restricted, otherwise it is unrestricted (or restricted based on APN type). |
| * |
| * This supports a privileged app bringing up a network without general apps having access |
| * to it when the network is otherwise unavailable (hipri). The first use case is |
| * pre-paid SIM reprovisioning over internet, where the carrier insists on no traffic |
| * other than from the privileged carrier-app. |
| * |
| * Note that the data connection cannot go from unrestricted to restricted because the |
| * connectivity service does not support dynamically closing TCP connections at this point. |
| */ |
| private boolean mRestrictedNetworkOverride = false; |
| |
| /** |
| * Indicates if this data connection supports enterprise use. Note that this flag should be |
| * populated when data becomes active. Once it is set, the value cannot be changed because |
| * setting it will cause this data connection to lose immutable network capabilities, which can |
| * cause issues in connectivity service. |
| */ |
| private boolean mEnterpriseUse = false; |
| |
| /** |
| * Check if this data connection should be restricted. We should call this when data connection |
| * becomes active, or when we want to re-evaluate the conditions to decide if we need to |
| * unstrict the data connection. |
| * |
| * @return True if this data connection needs to be restricted. |
| */ |
| private boolean shouldRestrictNetwork() { |
| // first, check if there is any network request that containing restricted capability |
| // (i.e. Do not have NET_CAPABILITY_NOT_RESTRICTED in the request) |
| boolean isAnyRestrictedRequest = false; |
| for (ApnContext apnContext : mApnContexts.keySet()) { |
| if (apnContext.hasRestrictedRequests(true /* exclude DUN */)) { |
| isAnyRestrictedRequest = true; |
| break; |
| } |
| } |
| |
| // If all of the network requests are non-restricted, then we don't need to restrict |
| // the network. |
| if (!isAnyRestrictedRequest) { |
| return false; |
| } |
| |
| // If the network is unmetered, then we don't need to restrict the network because users |
| // won't be charged anyway. |
| if (!ApnSettingUtils.isMetered(mApnSetting, mPhone)) { |
| return false; |
| } |
| |
| // If the data is disabled, then we need to restrict the network so only privileged apps can |
| // use the restricted network while data is disabled. |
| if (!mPhone.getDataEnabledSettings().isDataEnabled()) { |
| return true; |
| } |
| |
| // If the device is roaming, and the user does not turn on data roaming, then we need to |
| // restrict the network so only privileged apps can use it. |
| if (!mDct.getDataRoamingEnabled() && mPhone.getServiceState().getDataRoaming()) { |
| return true; |
| } |
| |
| // Otherwise we should not restrict the network so anyone who requests can use it. |
| return false; |
| } |
| |
| /** |
| * @return True if this data connection should only be used for unmetered purposes. |
| */ |
| private boolean isUnmeteredUseOnly() { |
| // If this data connection is on IWLAN, then it's unmetered and can be used by everyone. |
| // Should not be for unmetered used only. |
| if (mTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WLAN) { |
| return false; |
| } |
| |
| // If data is enabled, this data connection can't be for unmetered used only because |
| // everyone should be able to use it if: |
| // 1. Device is not roaming, or |
| // 2. Device is roaming and data roaming is turned on |
| if (mPhone.getDataEnabledSettings().isDataEnabled()) { |
| if (!mPhone.getServiceState().getDataRoaming() || mDct.getDataRoamingEnabled()) { |
| return false; |
| } |
| } |
| |
| // The data connection can only be unmetered used only if all attached APN contexts |
| // attached to this data connection are unmetered. |
| for (ApnContext apnContext : mApnContexts.keySet()) { |
| if (ApnSettingUtils.isMeteredApnType(apnContext.getApnTypeBitmask(), mPhone)) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| /** |
| * @return True if this data connection should only be used for MMS purposes. |
| */ |
| private boolean isMmsUseOnly() { |
| // MMS use only if data is disabled, MMS is allowed unconditionally, and MMS is the only |
| // APN type for this data connection. |
| DataEnabledSettings des = mPhone.getDataEnabledSettings(); |
| boolean mmsAllowedUnconditionally = !des.isDataEnabled() && des.isMmsAlwaysAllowed(); |
| boolean mmsApnOnly = isApnContextAttached(ApnSetting.TYPE_MMS, true); |
| return mmsAllowedUnconditionally && mmsApnOnly; |
| } |
| |
| /** |
| * Check if this data connection supports enterprise use. We call this when the data connection |
| * becomes active or when we want to reevaluate the conditions to decide if we need to update |
| * the network agent capabilities. |
| * |
| * @return True if this data connection supports enterprise use. |
| */ |
| private boolean isEnterpriseUse() { |
| boolean enterpriseTrafficDescriptor = mTrafficDescriptors |
| .stream() |
| .anyMatch(td -> td.getOsAppId() != null && Arrays.equals(td.getOsAppId(), |
| getEnterpriseOsAppId())); |
| boolean enterpriseApnContext = mApnContexts.keySet() |
| .stream() |
| .anyMatch(ac -> ac.getApnTypeBitmask() == ApnSetting.TYPE_ENTERPRISE); |
| return enterpriseTrafficDescriptor || enterpriseApnContext; |
| } |
| |
| /** |
| * Get the network capabilities for this data connection. |
| * |
| * @return the {@link NetworkCapabilities} of this data connection. |
| */ |
| public NetworkCapabilities getNetworkCapabilities() { |
| final NetworkCapabilities.Builder builder = new NetworkCapabilities.Builder() |
| .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR); |
| boolean unmeteredApns = false; |
| |
| if (mApnSetting != null && !mEnterpriseUse && !mMmsUseOnly) { |
| final int[] types = ApnSetting.getApnTypesFromBitmask( |
| mApnSetting.getApnTypeBitmask() & ~mDisabledApnTypeBitMask); |
| for (int type : types) { |
| if ((!mRestrictedNetworkOverride && mUnmeteredUseOnly) |
| && ApnSettingUtils.isMeteredApnType(type, mPhone)) { |
| log("Dropped the metered " + ApnSetting.getApnTypeString(type) |
| + " type for the unmetered data call."); |
| continue; |
| } |
| switch (type) { |
| case ApnSetting.TYPE_ALL: { |
| builder.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET); |
| builder.addCapability(NetworkCapabilities.NET_CAPABILITY_MMS); |
| builder.addCapability(NetworkCapabilities.NET_CAPABILITY_SUPL); |
| builder.addCapability(NetworkCapabilities.NET_CAPABILITY_FOTA); |
| builder.addCapability(NetworkCapabilities.NET_CAPABILITY_IMS); |
| builder.addCapability(NetworkCapabilities.NET_CAPABILITY_CBS); |
| builder.addCapability(NetworkCapabilities.NET_CAPABILITY_IA); |
| builder.addCapability(NetworkCapabilities.NET_CAPABILITY_DUN); |
| break; |
| } |
| case ApnSetting.TYPE_DEFAULT: { |
| builder.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET); |
| break; |
| } |
| case ApnSetting.TYPE_MMS: { |
| builder.addCapability(NetworkCapabilities.NET_CAPABILITY_MMS); |
| break; |
| } |
| case ApnSetting.TYPE_SUPL: { |
| builder.addCapability(NetworkCapabilities.NET_CAPABILITY_SUPL); |
| break; |
| } |
| case ApnSetting.TYPE_DUN: { |
| builder.addCapability(NetworkCapabilities.NET_CAPABILITY_DUN); |
| break; |
| } |
| case ApnSetting.TYPE_FOTA: { |
| builder.addCapability(NetworkCapabilities.NET_CAPABILITY_FOTA); |
| break; |
| } |
| case ApnSetting.TYPE_IMS: { |
| builder.addCapability(NetworkCapabilities.NET_CAPABILITY_IMS); |
| break; |
| } |
| case ApnSetting.TYPE_CBS: { |
| builder.addCapability(NetworkCapabilities.NET_CAPABILITY_CBS); |
| break; |
| } |
| case ApnSetting.TYPE_IA: { |
| builder.addCapability(NetworkCapabilities.NET_CAPABILITY_IA); |
| break; |
| } |
| case ApnSetting.TYPE_EMERGENCY: { |
| builder.addCapability(NetworkCapabilities.NET_CAPABILITY_EIMS); |
| break; |
| } |
| case ApnSetting.TYPE_MCX: { |
| builder.addCapability(NetworkCapabilities.NET_CAPABILITY_MCX); |
| break; |
| } |
| case ApnSetting.TYPE_XCAP: { |
| builder.addCapability(NetworkCapabilities.NET_CAPABILITY_XCAP); |
| break; |
| } |
| default: |
| } |
| } |
| |
| if (!ApnSettingUtils.isMetered(mApnSetting, mPhone)) { |
| unmeteredApns = true; |
| } |
| } |
| |
| // Mark NOT_METERED in the following cases: |
| // 1. All APNs in the APN settings are unmetered. |
| // 2. The non-restricted data is intended for unmetered use only. |
| if (unmeteredApns || (mUnmeteredUseOnly && !mRestrictedNetworkOverride)) { |
| builder.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED); |
| } else { |
| builder.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED); |
| } |
| |
| if (mEnterpriseUse) { |
| builder.addCapability(NetworkCapabilities.NET_CAPABILITY_ENTERPRISE); |
| builder.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET); |
| } |
| |
| if (NetworkCapabilitiesUtils.inferRestrictedCapability(builder.build())) { |
| builder.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED); |
| } |
| |
| if (mMmsUseOnly) { |
| if (ApnSettingUtils.isMeteredApnType(ApnSetting.TYPE_MMS, mPhone)) { |
| log("Adding unmetered capability for the unmetered MMS-only data connection"); |
| builder.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED); |
| } |
| log("Adding MMS capability for the MMS-only data connection"); |
| builder.addCapability(NetworkCapabilities.NET_CAPABILITY_MMS); |
| } |
| |
| if (mRestrictedNetworkOverride) { |
| builder.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED); |
| // don't use dun on restriction-overriden networks. |
| builder.removeCapability(NetworkCapabilities.NET_CAPABILITY_DUN); |
| } |
| |
| builder.setLinkDownstreamBandwidthKbps(mDownlinkBandwidth); |
| builder.setLinkUpstreamBandwidthKbps(mUplinkBandwidth); |
| |
| builder.setNetworkSpecifier(new TelephonyNetworkSpecifier.Builder() |
| .setSubscriptionId(mSubId).build()); |
| builder.setSubscriptionIds(Collections.singleton(mSubId)); |
| |
| if (!mPhone.getServiceState().getDataRoaming()) { |
| builder.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING); |
| } |
| |
| if (!mCongestedOverride) { |
| builder.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED); |
| } |
| |
| if (mUnmeteredOverride) { |
| builder.addCapability(NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED); |
| } |
| |
| if (!mIsSuspended) { |
| builder.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED); |
| } |
| |
| builder.setAdministratorUids(mAdministratorUids); |
| |
| // 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); |
| } |
| |
| return builder.build(); |
| } |
| |
| /** |
| * Returns whether the Network represented by this DataConnection is VCN-managed. |
| * |
| * <p>Determining if the Network is VCN-managed requires polling VcnManager. |
| */ |
| private boolean isVcnManaged(NetworkCapabilities networkCapabilities) { |
| 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); |
| } |
| |
| /** @return {@code true} if validation is required, {@code false} otherwise. */ |
| public boolean isValidationRequired() { |
| final NetworkCapabilities nc = getNetworkCapabilities(); |
| return nc != null |
| && nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) |
| && nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED) |
| && nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED) |
| && nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN); |
| } |
| |
| /** |
| * @return {@code True} if 464xlat should be skipped. |
| */ |
| @VisibleForTesting |
| public boolean shouldSkip464Xlat() { |
| switch (mApnSetting.getSkip464Xlat()) { |
| case Telephony.Carriers.SKIP_464XLAT_ENABLE: |
| return true; |
| case Telephony.Carriers.SKIP_464XLAT_DISABLE: |
| return false; |
| case Telephony.Carriers.SKIP_464XLAT_DEFAULT: |
| default: |
| break; |
| } |
| |
| // As default, return true if ims and no internet |
| final NetworkCapabilities nc = getNetworkCapabilities(); |
| return nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_IMS) |
| && !nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET); |
| } |
| |
| /** |
| * @return {@code} true iff. {@code address} is a literal IPv4 or IPv6 address. |
| */ |
| @VisibleForTesting |
| public static boolean isIpAddress(String address) { |
| if (address == null) return false; |
| |
| // Accept IPv6 addresses (only) in square brackets for compatibility. |
| if (address.startsWith("[") && address.endsWith("]") && address.indexOf(':') != -1) { |
| address = address.substring(1, address.length() - 1); |
| } |
| return InetAddresses.isNumericAddress(address); |
| } |
| |
| private SetupResult setLinkProperties(DataCallResponse response, |
| LinkProperties linkProperties) { |
| // Check if system property dns usable |
| String propertyPrefix = "net." + response.getInterfaceName() + "."; |
| String dnsServers[] = new String[2]; |
| dnsServers[0] = SystemProperties.get(propertyPrefix + "dns1"); |
| dnsServers[1] = SystemProperties.get(propertyPrefix + "dns2"); |
| boolean okToUseSystemPropertyDns = isDnsOk(dnsServers); |
| |
| SetupResult result; |
| |
| // Start with clean network properties and if we have |
| // a failure we'll clear again at the bottom of this code. |
| linkProperties.clear(); |
| |
| if (response.getCause() == DataFailCause.NONE) { |
| try { |
| // set interface name |
| linkProperties.setInterfaceName(response.getInterfaceName()); |
| |
| // set link addresses |
| if (response.getAddresses().size() > 0) { |
| for (LinkAddress la : response.getAddresses()) { |
| if (!la.getAddress().isAnyLocalAddress()) { |
| if (DBG) { |
| log("addr/pl=" + la.getAddress() + "/" |
| + la.getPrefixLength()); |
| } |
| linkProperties.addLinkAddress(la); |
| } |
| } |
| } else { |
| throw new UnknownHostException("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 if (okToUseSystemPropertyDns) { |
| for (String dnsAddr : dnsServers) { |
| dnsAddr = dnsAddr.trim(); |
| if (dnsAddr.isEmpty()) continue; |
| InetAddress ia; |
| try { |
| ia = InetAddresses.parseNumericAddress(dnsAddr); |
| } catch (IllegalArgumentException e) { |
| throw new UnknownHostException("Non-numeric dns addr=" + dnsAddr); |
| } |
| if (!ia.isAnyLocalAddress()) { |
| linkProperties.addDnsServer(ia); |
| } |
| } |
| } else { |
| throw new UnknownHostException("Empty dns response and no system default dns"); |
| } |
| |
| // set pcscf |
| if (response.getPcscfAddresses().size() > 0) { |
| for (InetAddress pcscf : response.getPcscfAddresses()) { |
| linkProperties.addPcscfServer(pcscf); |
| } |
| } |
| |
| boolean useLowerMtuValue = false; |
| CarrierConfigManager configManager = (CarrierConfigManager) |
| mPhone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE); |
| if (configManager != null) { |
| PersistableBundle bundle = configManager.getConfigForSubId(mSubId); |
| if (bundle != null) { |
| useLowerMtuValue = bundle.getBoolean( |
| CarrierConfigManager.KEY_USE_LOWER_MTU_VALUE_IF_BOTH_RECEIVED) |
| && response.getMtuV4() != PhoneConstants.UNSET_MTU |
| && response.getMtuV6() != PhoneConstants.UNSET_MTU; |
| } |
| } |
| |
| int interfaceMtu = response.getMtu(); |
| for (InetAddress gateway : response.getGatewayAddresses()) { |
| int mtu = gateway instanceof java.net.Inet6Address ? response.getMtuV6() |
| : response.getMtuV4(); |
| if (useLowerMtuValue) { |
| mtu = Math.min(response.getMtuV4(), response.getMtuV6()); |
| // Never set an MTU below MIN_V6_MTU on a network that has IPv6. |
| if (mtu < MIN_V6_MTU) { |
| mtu = MIN_V6_MTU; |
| } |
| interfaceMtu = mtu; |
| } |
| // Allow 0.0.0.0 or :: as a gateway; |
| // this indicates a point-to-point interface. |
| linkProperties.addRoute(new RouteInfo(null, gateway, null, |
| RouteInfo.RTN_UNICAST, mtu)); |
| } |
| |
| // set interface MTU |
| // this may clobber the setting read from the APN db, but that's ok |
| // TODO: remove once LinkProperties#setMtu is deprecated |
| linkProperties.setMtu(interfaceMtu); |
| |
| result = SetupResult.SUCCESS; |
| } catch (UnknownHostException e) { |
| log("setLinkProperties: UnknownHostException " + e); |
| result = SetupResult.ERROR_INVALID_ARG; |
| } |
| } else { |
| result = SetupResult.ERROR_DATA_SERVICE_SPECIFIC_ERROR; |
| } |
| |
| // An error occurred so clear properties |
| if (result != SetupResult.SUCCESS) { |
| if (DBG) { |
| log("setLinkProperties: error clearing LinkProperties status=" |
| + response.getCause() + " result=" + result); |
| } |
| linkProperties.clear(); |
| } |
| |
| return result; |
| } |
| |
| private boolean getDoAllocatePduSessionId() { |
| return mDoAllocatePduSessionId; |
| } |
| |
| /** |
| * Initialize connection, this will fail if the |
| * apnSettings are not compatible. |
| * |
| * @param cp the Connection parameters |
| * @return true if initialization was successful. |
| */ |
| private boolean initConnection(ConnectionParams cp) { |
| ApnContext apnContext = cp.mApnContext; |
| if (mApnSetting == null) { |
| // Only change apn setting if it isn't set, it will |
| // only NOT be set only if we're in DcInactiveState. |
| mApnSetting = apnContext.getApnSetting(); |
| } |
| if (mApnSetting == null || (!mApnSetting.canHandleType(apnContext.getApnTypeBitmask()) |
| && apnContext.getApnTypeBitmask() != ApnSetting.TYPE_ENTERPRISE)) { |
| if (DBG) { |
| log("initConnection: incompatible apnSetting in ConnectionParams cp=" + cp |
| + " dc=" + DataConnection.this); |
| } |
| return false; |
| } |
| mTag += 1; |
| mConnectionParams = cp; |
| mConnectionParams.mTag = mTag; |
| |
| // always update the ConnectionParams with the latest or the |
| // connectionGeneration gets stale |
| mApnContexts.put(apnContext, cp); |
| |
| if (DBG) { |
| log("initConnection: " |
| + " RefCount=" + mApnContexts.size() |
| + " mApnList=" + mApnContexts |
| + " mConnectionParams=" + mConnectionParams); |
| } |
| return true; |
| } |
| |
| /** |
| * The parent state for all other states. |
| */ |
| private class DcDefaultState extends State { |
| @Override |
| public void enter() { |
| if (DBG) log("DcDefaultState: enter"); |
| |
| // Register for DRS or RAT change |
| mPhone.getServiceStateTracker().registerForDataRegStateOrRatChanged( |
| mTransportType, getHandler(), |
| DataConnection.EVENT_DATA_CONNECTION_DRS_OR_RAT_CHANGED, null); |
| |
| mPhone.getServiceStateTracker().registerForDataRoamingOn(getHandler(), |
| DataConnection.EVENT_DATA_CONNECTION_ROAM_ON, null); |
| mPhone.getServiceStateTracker().registerForDataRoamingOff(getHandler(), |
| DataConnection.EVENT_DATA_CONNECTION_ROAM_OFF, null, true); |
| mPhone.getServiceStateTracker().registerForNrStateChanged(getHandler(), |
| DataConnection.EVENT_NR_STATE_CHANGED, null); |
| mPhone.getServiceStateTracker().registerForNrFrequencyChanged(getHandler(), |
| DataConnection.EVENT_NR_FREQUENCY_CHANGED, null); |
| mPhone.getServiceStateTracker().registerForCssIndicatorChanged(getHandler(), |
| DataConnection.EVENT_CSS_INDICATOR_CHANGED, null); |
| if (isBandwidthSourceKey(DctConstants.BANDWIDTH_SOURCE_BANDWIDTH_ESTIMATOR_KEY)) { |
| mPhone.getLinkBandwidthEstimator().registerForBandwidthChanged(getHandler(), |
| DataConnection.EVENT_LINK_BANDWIDTH_ESTIMATOR_UPDATE, null); |
| } |
| |
| // Add ourselves to the list of data connections |
| mDcController.addDc(DataConnection.this); |
| } |
| @Override |
| public void exit() { |
| if (DBG) log("DcDefaultState: exit"); |
| |
| // Unregister for DRS or RAT change. |
| mPhone.getServiceStateTracker().unregisterForDataRegStateOrRatChanged( |
| mTransportType, getHandler()); |
| |
| mPhone.getServiceStateTracker().unregisterForDataRoamingOn(getHandler()); |
| mPhone.getServiceStateTracker().unregisterForDataRoamingOff(getHandler()); |
| mPhone.getServiceStateTracker().unregisterForNrStateChanged(getHandler()); |
| mPhone.getServiceStateTracker().unregisterForNrFrequencyChanged(getHandler()); |
| mPhone.getServiceStateTracker().unregisterForCssIndicatorChanged(getHandler()); |
| if (isBandwidthSourceKey(DctConstants.BANDWIDTH_SOURCE_BANDWIDTH_ESTIMATOR_KEY)) { |
| mPhone.getLinkBandwidthEstimator().unregisterForBandwidthChanged(getHandler()); |
| } |
| |
| // Remove ourselves from the DC lists |
| mDcController.removeDc(DataConnection.this); |
| |
| if (mAc != null) { |
| mAc.disconnected(); |
| mAc = null; |
| } |
| mApnContexts.clear(); |
| mReconnectIntent = null; |
| mDct = null; |
| mApnSetting = null; |
| mPhone = null; |
| mDataServiceManager = null; |
| mLinkProperties = null; |
| mLastFailCause = DataFailCause.NONE; |
| mUserData = null; |
| mDcController = null; |
| mDcTesterFailBringUpAll = null; |
| } |
| |
| @Override |
| public boolean processMessage(Message msg) { |
| boolean retVal = HANDLED; |
| |
| if (VDBG) { |
| log("DcDefault msg=" + getWhatToString(msg.what) |
| + " RefCount=" + mApnContexts.size()); |
| } |
| switch (msg.what) { |
| case EVENT_RESET: |
| if (VDBG) log("DcDefaultState: msg.what=REQ_RESET"); |
| transitionTo(mInactiveState); |
| break; |
| case EVENT_CONNECT: |
| if (DBG) log("DcDefaultState: msg.what=EVENT_CONNECT, fail not expected"); |
| ConnectionParams cp = (ConnectionParams) msg.obj; |
| notifyConnectCompleted(cp, DataFailCause.UNKNOWN, |
| DataCallResponse.HANDOVER_FAILURE_MODE_UNKNOWN, false); |
| break; |
| |
| case EVENT_DISCONNECT: |
| case EVENT_DISCONNECT_ALL: |
| case EVENT_REEVALUATE_RESTRICTED_STATE: |
| if (DBG) { |
| log("DcDefaultState deferring msg.what=" + getWhatToString(msg.what) |
| + " RefCount=" + mApnContexts.size()); |
| } |
| deferMessage(msg); |
| break; |
| case EVENT_TEAR_DOWN_NOW: |
| if (DBG) log("DcDefaultState EVENT_TEAR_DOWN_NOW"); |
| mDataServiceManager.deactivateDataCall(mCid, DataService.REQUEST_REASON_NORMAL, |
| null); |
| mDataCallSessionStats.setDeactivateDataCallReason( |
| DataService.REQUEST_REASON_NORMAL); |
| break; |
| case EVENT_LOST_CONNECTION: |
| if (DBG) { |
| String s = "DcDefaultState ignore EVENT_LOST_CONNECTION" |
| + " tag=" + msg.arg1 + ":mTag=" + mTag; |
| logAndAddLogRec(s); |
| } |
| break; |
| case EVENT_DATA_CONNECTION_DRS_OR_RAT_CHANGED: |
| AsyncResult ar = (AsyncResult)msg.obj; |
| Pair<Integer, Integer> drsRatPair = (Pair<Integer, Integer>)ar.result; |
| mDataRegState = drsRatPair.first; |
| updateTcpBufferSizes(drsRatPair.second); |
| if (isBandwidthSourceKey(DctConstants.BANDWIDTH_SOURCE_CARRIER_CONFIG_KEY)) { |
| updateLinkBandwidthsFromCarrierConfig(drsRatPair.second); |
| } |
| mRilRat = drsRatPair.second; |
| if (DBG) { |
| log("DcDefaultState: EVENT_DATA_CONNECTION_DRS_OR_RAT_CHANGED" |
| + " regState=" + ServiceState.rilServiceStateToString(mDataRegState) |
| + " RAT=" + ServiceState.rilRadioTechnologyToString(mRilRat)); |
| } |
| mDataCallSessionStats.onDrsOrRatChanged(mRilRat); |
| break; |
| |
| case EVENT_START_HANDOVER: //calls startHandover() |
| if (DBG) { |
| log("DcDefaultState: EVENT_START_HANDOVER not expected."); |
| } |
| Consumer<Integer> r = (Consumer<Integer>) msg.obj; |
| r.accept(DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE); |
| break; |
| case EVENT_START_HANDOVER_ON_TARGET: |
| if (DBG) { |
| log("DcDefaultState: EVENT_START_HANDOVER not expected, but will " |
| + "clean up, result code: " |
| + DataServiceCallback.resultCodeToString(msg.arg1)); |
| } |
| ((Consumer<Boolean>) msg.obj).accept(false /* is in correct state*/); |
| break; |
| case EVENT_CANCEL_HANDOVER: |
| // We don't need to do anything in this case |
| if (DBG) { |
| log("DcDefaultState: EVENT_CANCEL_HANDOVER resultCode=" |
| + DataServiceCallback.resultCodeToString(msg.arg1)); |
| } |
| break; |
| case EVENT_RELEASE_PDU_SESSION_ID: { |
| // We do the same thing in all state in order to preserve the existing workflow |
| final AsyncResult asyncResult = (AsyncResult) msg.obj; |
| if (asyncResult == null) { |
| loge("EVENT_RELEASE_PDU_SESSION_ID: asyncResult is null!"); |
| } else { |
| if (msg.obj != null) { |
| if (DBG) logd("EVENT_RELEASE_PDU_SESSION_ID: id released"); |
| Runnable runnable = (Runnable) asyncResult.userObj; |
| runnable.run(); |
| } else { |
| loge("EVENT_RELEASE_PDU_SESSION_ID: no runnable set"); |
| } |
| } |
| retVal = HANDLED; |
| break; |
| } |
| case EVENT_ALLOCATE_PDU_SESSION_ID: { |
| // We do the same thing in all state in order to preserve the existing workflow |
| final AsyncResult asyncResult = (AsyncResult) msg.obj; |
| if (asyncResult == null) { |
| loge("EVENT_ALLOCATE_PDU_SESSION_ID: asyncResult is null!"); |
| } else { |
| Consumer<Integer> onAllocated = (Consumer<Integer>) asyncResult.userObj; |
| if (asyncResult.exception != null) { |
| loge("EVENT_ALLOCATE_PDU_SESSION_ID: exception", |
| asyncResult.exception); |
| onAllocated.accept(PDU_SESSION_ID_NOT_SET); |
| } else if (asyncResult.result == null) { |
| loge("EVENT_ALLOCATE_PDU_SESSION_ID: result null, no id"); |
| onAllocated.accept(PDU_SESSION_ID_NOT_SET); |
| } else { |
| int psi = (int) asyncResult.result; |
| if (DBG) logd("EVENT_ALLOCATE_PDU_SESSION_ID: psi=" + psi); |
| onAllocated.accept(psi); |
| } |
| } |
| retVal = HANDLED; |
| break; |
| } |
| default: |
| if (DBG) { |
| log("DcDefaultState: ignore msg.what=" + getWhatToString(msg.what)); |
| } |
| break; |
| } |
| |
| return retVal; |
| } |
| } |
| |
| private void updateSuspendState() { |
| if (mNetworkAgent == null) { |
| Rlog.d(getName(), "Setting suspend state without a NetworkAgent"); |
| } |
| |
| boolean newSuspendedState = false; |
| // Data can only be (temporarily) suspended while data is in active state |
| if (getCurrentState() == mActiveState) { |
| // Never set suspended for emergency apn. Emergency data connection |
| // can work while device is not in service. |
| if (mApnSetting != null && mApnSetting.isEmergencyApn()) { |
| newSuspendedState = false; |
| // If we are not in service, change to suspended. |
| } else if (mDataRegState != ServiceState.STATE_IN_SERVICE) { |
| newSuspendedState = true; |
| // Check voice/data concurrency. |
| } else if (!mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) { |
| newSuspendedState = mPhone.getCallTracker().getState() != PhoneConstants.State.IDLE; |
| } |
| } |
| |
| // Only notify when there is a change. |
| if (mIsSuspended != newSuspendedState) { |
| mIsSuspended = newSuspendedState; |
| |
| // If data connection is active, we need to notify the new data connection state |
| // changed event reflecting the latest suspended state. |
| if (isActive()) { |
| notifyDataConnectionState(); |
| } |
| } |
| } |
| |
| private void notifyDataConnectionState() { |
| // The receivers of this have no way to differentiate between default and enterprise |
| // connections. Do not notify for enterprise. |
| if (!isEnterpriseUse()) { |
| mPhone.notifyDataConnection(getPreciseDataConnectionState()); |
| } else { |
| log("notifyDataConnectionState: Skipping for enterprise; state=" + getState()); |
| } |
| } |
| |
| private DcDefaultState mDefaultState = new DcDefaultState(); |
| |
| private int getApnTypeBitmask() { |
| return isEnterpriseUse() ? ApnSetting.TYPE_ENTERPRISE : |
| mApnSetting != null ? mApnSetting.getApnTypeBitmask() : 0; |
| } |
| |
| private boolean canHandleDefault() { |
| return !isEnterpriseUse() && mApnSetting != null |
| ? mApnSetting.canHandleType(ApnSetting.TYPE_DEFAULT) : false; |
| } |
| |
| /** |
| * The state machine is inactive and expects a EVENT_CONNECT. |
| */ |
| private class DcInactiveState extends State { |
| // Inform all contexts we've failed connecting |
| public void setEnterNotificationParams(ConnectionParams cp, @DataFailureCause int cause, |
| @HandoverFailureMode int handoverFailureMode) { |
| if (VDBG) log("DcInactiveState: setEnterNotificationParams cp,cause"); |
| mConnectionParams = cp; |
| mDisconnectParams = null; |
| mDcFailCause = cause; |
| mHandoverFailureMode = handoverFailureMode; |
| } |
| |
| // Inform all contexts we've failed disconnected |
| public void setEnterNotificationParams(DisconnectParams dp) { |
| if (VDBG) log("DcInactiveState: setEnterNotificationParams dp"); |
| mConnectionParams = null; |
| mDisconnectParams = dp; |
| mDcFailCause = DataFailCause.NONE; |
| } |
| |
| // Inform all contexts of the failure cause |
| public void setEnterNotificationParams(@DataFailureCause int cause) { |
| mConnectionParams = null; |
| mDisconnectParams = null; |
| mDcFailCause = cause; |
| } |
| |
| @Override |
| public void enter() { |
| mTag += 1; |
| if (DBG) log("DcInactiveState: enter() mTag=" + mTag); |
| TelephonyStatsLog.write(TelephonyStatsLog.MOBILE_CONNECTION_STATE_CHANGED, |
| TelephonyStatsLog.MOBILE_CONNECTION_STATE_CHANGED__STATE__INACTIVE, |
| mPhone.getPhoneId(), mId, getApnTypeBitmask(), canHandleDefault()); |
| mDataCallSessionStats.onDataCallDisconnected(mDcFailCause); |
| if (mHandoverState == HANDOVER_STATE_BEING_TRANSFERRED) { |
| // This is from source data connection to set itself's state |
| setHandoverState(HANDOVER_STATE_COMPLETED); |
| } |
| |
| // Check for dangling agent. Ideally the handover source agent should be null if |
| // handover process is smooth. When it's not null, that means handover failed. The |
| // agent was not successfully transferred to the new data connection. We should |
| // gracefully notify connectivity service the network was disconnected. |
| if (mHandoverSourceNetworkAgent != null) { |
| DataConnection sourceDc = mHandoverSourceNetworkAgent.getDataConnection(); |
| if (sourceDc != null) { |
| // If the source data connection still owns this agent, then just reset the |
| // handover state back to idle because handover is already failed. |
| mHandoverLocalLog.log( |
| "Handover failed. Reset the source dc " + sourceDc.getName() |
| + " state to idle"); |
| sourceDc.cancelHandover(); |
| } else { |
| // The agent is now a dangling agent. No data connection owns this agent. |
| // Gracefully notify connectivity service disconnected. |
| mHandoverLocalLog.log( |
| "Handover failed and dangling agent found."); |
| mHandoverSourceNetworkAgent.acquireOwnership( |
| DataConnection.this, mTransportType); |
| log("Cleared dangling network agent. " + mHandoverSourceNetworkAgent); |
| mHandoverSourceNetworkAgent.unregister(DataConnection.this); |
| mHandoverSourceNetworkAgent.releaseOwnership(DataConnection.this); |
| } |
| mHandoverSourceNetworkAgent = null; |
| } |
| |
| if (mConnectionParams != null) { |
| if (DBG) { |
| log("DcInactiveState: enter notifyConnectCompleted +ALL failCause=" |
| + DataFailCause.toString(mDcFailCause)); |
| } |
| notifyConnectCompleted(mConnectionParams, mDcFailCause, mHandoverFailureMode, |
| true); |
| } |
| if (mDisconnectParams != null) { |
| if (DBG) { |
| log("DcInactiveState: enter notifyDisconnectCompleted +ALL failCause=" |
| + DataFailCause.toString(mDcFailCause)); |
| } |
| notifyDisconnectCompleted(mDisconnectParams, true); |
| } |
| if (mDisconnectParams == null && mConnectionParams == null |
| && mDcFailCause != DataFailCause.NONE) { |
| if (DBG) { |
| log("DcInactiveState: enter notifyAllDisconnectCompleted failCause=" |
| + DataFailCause.toString(mDcFailCause)); |
| } |
| notifyAllWithEvent(null, DctConstants.EVENT_DISCONNECT_DONE, |
| DataFailCause.toString(mDcFailCause)); |
| } |
| |
| // Remove ourselves from cid mapping, before clearSettings |
| mDcController.removeActiveDcByCid(DataConnection.this); |
| |
| // For the first time entering here (idle state before setup), do not notify |
| // disconnected state. Only notify data connection disconnected for data that is |
| // actually moving from disconnecting to disconnected, or setup failed. In both cases, |
| // APN setting will not be null. |
| if (mApnSetting != null) { |
| notifyDataConnectionState(); |
| } |
| clearSettings(); |
| } |
| |
| @Override |
| public void exit() { |
| } |
| |
| @Override |
| public boolean processMessage(Message msg) { |
| switch (msg.what) { |
| case EVENT_RESET: |
| case EVENT_REEVALUATE_RESTRICTED_STATE: |
| if (DBG) { |
| log("DcInactiveState: msg.what=" + getWhatToString(msg.what) |
| + ", ignore we're already done"); |
| } |
| return HANDLED; |
| case EVENT_CONNECT: |
| if (DBG) log("DcInactiveState: mag.what=EVENT_CONNECT"); |
| ConnectionParams cp = (ConnectionParams) msg.obj; |
| |
| if (!initConnection(cp)) { |
| log("DcInactiveState: msg.what=EVENT_CONNECT initConnection failed"); |
| notifyConnectCompleted(cp, DataFailCause.UNACCEPTABLE_NETWORK_PARAMETER, |
| DataCallResponse.HANDOVER_FAILURE_MODE_UNKNOWN, false); |
| transitionTo(mInactiveState); |
| return HANDLED; |
| } |
| |
| int cause = connect(cp); |
| if (cause != DataFailCause.NONE) { |
| log("DcInactiveState: msg.what=EVENT_CONNECT connect failed"); |
| notifyConnectCompleted(cp, cause, |
| DataCallResponse.HANDOVER_FAILURE_MODE_UNKNOWN, false); |
| transitionTo(mInactiveState); |
| return HANDLED; |
| } |
| |
| if (mSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) { |
| mSubId = cp.mSubId; |
| } |
| |
| transitionTo(mActivatingState); |
| return HANDLED; |
| case EVENT_DISCONNECT: |
| if (DBG) log("DcInactiveState: msg.what=EVENT_DISCONNECT"); |
| notifyDisconnectCompleted((DisconnectParams)msg.obj, false); |
| return HANDLED; |
| case EVENT_DISCONNECT_ALL: |
| if (DBG) log("DcInactiveState: msg.what=EVENT_DISCONNECT_ALL"); |
| notifyDisconnectCompleted((DisconnectParams)msg.obj, false); |
| return HANDLED; |
| default: |
| if (VDBG) { |
| log("DcInactiveState not handled msg.what=" + getWhatToString(msg.what)); |
| } |
| return NOT_HANDLED; |
| } |
| } |
| } |
| private DcInactiveState mInactiveState = new DcInactiveState(); |
| |
| /** |
| * The state machine is activating a connection. |
| */ |
| private class DcActivatingState extends State { |
| @Override |
| public void enter() { |
| int apnTypeBitmask = getApnTypeBitmask(); |
| TelephonyStatsLog.write(TelephonyStatsLog.MOBILE_CONNECTION_STATE_CHANGED, |
| TelephonyStatsLog.MOBILE_CONNECTION_STATE_CHANGED__STATE__ACTIVATING, |
| mPhone.getPhoneId(), mId, apnTypeBitmask, canHandleDefault()); |
| setHandoverState(HANDOVER_STATE_IDLE); |
| // restricted evaluation depends on network requests from apnContext. The evaluation |
| // should happen once entering connecting state rather than active state because it's |
| // possible that restricted network request can be released during the connecting window |
| // and if we wait for connection established, then we might mistakenly |
| // consider it as un-restricted. ConnectivityService then will immediately |
| // tear down the connection through networkAgent unwanted callback if all requests for |
| // this connection are going away. |
| mRestrictedNetworkOverride = shouldRestrictNetwork(); |
| |
| mPhone.getCarrierPrivilegesTracker() |
| .registerCarrierPrivilegesListener( |
| getHandler(), EVENT_CARRIER_PRIVILEGED_UIDS_CHANGED, null); |
| notifyDataConnectionState(); |
| mDataCallSessionStats.onSetupDataCall(apnTypeBitmask); |
| } |
| @Override |
| public boolean processMessage(Message msg) { |
| boolean retVal; |
| AsyncResult ar; |
| ConnectionParams cp; |
| |
| if (DBG) log("DcActivatingState: msg=" + msgToString(msg)); |
| switch (msg.what) { |
| case EVENT_DATA_CONNECTION_DRS_OR_RAT_CHANGED: |
| case EVENT_CONNECT: |
| // Activating can't process until we're done. |
| deferMessage(msg); |
| retVal = HANDLED; |
| break; |
| |
| case EVENT_SETUP_DATA_CONNECTION_DONE: |
| cp = (ConnectionParams) msg.obj; |
| |
| DataCallResponse dataCallResponse = |
| msg.getData().getParcelable(DataServiceManager.DATA_CALL_RESPONSE); |
| SetupResult result = onSetupConnectionCompleted(msg.arg1, dataCallResponse, cp); |
| if (result != SetupResult.ERROR_STALE) { |
| if (mConnectionParams != cp) { |
| loge("DcActivatingState: WEIRD mConnectionsParams:"+ mConnectionParams |
| + " != cp:" + cp); |
| } |
| } |
| if (DBG) { |
| log("DcActivatingState onSetupConnectionCompleted result=" + result |
| + " dc=" + DataConnection.this); |
| } |
| if (cp.mApnContext != null) { |
| cp.mApnContext.requestLog("onSetupConnectionCompleted result=" + result); |
| } |
| switch (result) { |
| case SUCCESS: |
| // All is well |
| mDcFailCause = DataFailCause.NONE; |
| transitionTo(mActiveState); |
| break; |
| case ERROR_RADIO_NOT_AVAILABLE: |
| // Vendor ril rejected the command and didn't connect. |
| // Transition to inactive but send notifications after |
| // we've entered the mInactive state. |
| mInactiveState.setEnterNotificationParams(cp, result.mFailCause, |
| DataCallResponse.HANDOVER_FAILURE_MODE_UNKNOWN); |
| transitionTo(mInactiveState); |
| break; |
| case ERROR_DUPLICATE_CID: |
| // TODO (b/180988471): Properly handle the case when an existing cid is |
| // returned by tearing down the network agent if enterprise changed. |
| long retry = RetryManager.NO_SUGGESTED_RETRY_DELAY; |
| if (cp.mApnContext != null) { |
| retry = RetryManager.NO_RETRY; |
| mDct.getDataThrottler().setRetryTime( |
| cp.mApnContext.getApnTypeBitmask(), |
| retry, DcTracker.REQUEST_TYPE_NORMAL); |
| } |
| String logStr = "DcActivatingState: " |
| + DataFailCause.toString(result.mFailCause) |
| + " retry=" + retry; |
| if (DBG) log(logStr); |
| if (cp.mApnContext != null) cp.mApnContext.requestLog(logStr); |
| mInactiveState.setEnterNotificationParams(cp, result.mFailCause, |
| DataCallResponse.HANDOVER_FAILURE_MODE_UNKNOWN); |
| transitionTo(mInactiveState); |
| break; |
| case ERROR_NO_DEFAULT_CONNECTION: |
| // TODO (b/180988471): Properly handle the case when a default data |
| // connection doesn't exist (tear down connection and retry). |
| // Currently, this just tears down the connection without retry. |
| if (DBG) log("DcActivatingState: NO_DEFAULT_DATA"); |
| case ERROR_INVALID_ARG: |
| // The addresses given from the RIL are bad |
| tearDownData(cp); |
| transitionTo(mDisconnectingErrorCreatingConnection); |
| break; |
| case ERROR_DATA_SERVICE_SPECIFIC_ERROR: |
| |
| // Retrieve the suggested retry delay from the modem and save it. |
| // If the modem want us to retry the current APN again, it will |
| // suggest a positive delay value (in milliseconds). Otherwise we'll get |
| // NO_SUGGESTED_RETRY_DELAY here. |
| |
| long delay = getSuggestedRetryDelay(dataCallResponse); |
| long retryTime = RetryManager.NO_SUGGESTED_RETRY_DELAY; |
| if (delay == RetryManager.NO_RETRY) { |
| retryTime = RetryManager.NO_RETRY; |
| } else if (delay >= 0) { |
| retryTime = SystemClock.elapsedRealtime() + delay; |
| } |
| int newRequestType = DcTracker.calculateNewRetryRequestType( |
| mHandoverFailureMode, cp.mRequestType, mDcFailCause); |
| mDct.getDataThrottler().setRetryTime(getApnTypeBitmask(), |
| retryTime, newRequestType); |
| |
| String str = "DcActivatingState: ERROR_DATA_SERVICE_SPECIFIC_ERROR " |
| + " delay=" + delay |
| + " result=" + result |
| + " result.isRadioRestartFailure=" |
| + DataFailCause.isRadioRestartFailure(mPhone.getContext(), |
| result.mFailCause, mPhone.getSubId()) |
| + " isPermanentFailure=" + |
| mDct.isPermanentFailure(result.mFailCause); |
| if (DBG) log(str); |
| if (cp.mApnContext != null) cp.mApnContext.requestLog(str); |
| |
| // Save the cause. DcTracker.onDataSetupComplete will check this |
| // failure cause and determine if we need to retry this APN later |
| // or not. |
| mInactiveState.setEnterNotificationParams(cp, result.mFailCause, |
| dataCallResponse != null |
| ? dataCallResponse.getHandoverFailureMode() |
| : DataCallResponse.HANDOVER_FAILURE_MODE_UNKNOWN); |
| transitionTo(mInactiveState); |
| break; |
| case ERROR_STALE: |
| loge("DcActivatingState: stale EVENT_SETUP_DATA_CONNECTION_DONE" |
| + " tag:" + cp.mTag + " != mTag:" + mTag); |
| break; |
| default: |
| throw new RuntimeException("Unknown SetupResult, should not happen"); |
| } |
| retVal = HANDLED; |
| mDataCallSessionStats |
| .onSetupDataCallResponse(dataCallResponse, cp.mRilRat, |
| getApnTypeBitmask(), mApnSetting.getProtocol(), |
| result.mFailCause); |
| break; |
| case EVENT_CARRIER_PRIVILEGED_UIDS_CHANGED: |
| AsyncResult asyncResult = (AsyncResult) msg.obj; |
| int[] administratorUids = (int[]) asyncResult.result; |
| mAdministratorUids = Arrays.copyOf(administratorUids, administratorUids.length); |
| retVal = HANDLED; |
| break; |
| case EVENT_START_HANDOVER_ON_TARGET: |
| //called after startHandover on target transport |
| ((Consumer<Boolean>) msg.obj).accept(true /* is in correct state*/); |
| retVal = HANDLED; |
| break; |
| case EVENT_CANCEL_HANDOVER: |
| transitionTo(mInactiveState); |
| retVal = HANDLED; |
| break; |
| default: |
| if (VDBG) { |
| log("DcActivatingState not handled msg.what=" + |
| getWhatToString(msg.what) + " RefCount=" + mApnContexts.size()); |
| } |
| retVal = NOT_HANDLED; |
| break; |
| } |
| return retVal; |
| } |
| } |
| private DcActivatingState mActivatingState = new DcActivatingState(); |
| |
| /** |
| * The state machine is connected, expecting an EVENT_DISCONNECT. |
| */ |
| private class DcActiveState extends State { |
| |
| @Override public void enter() { |
| if (DBG) log("DcActiveState: enter dc=" + DataConnection.this); |
| TelephonyStatsLog.write(TelephonyStatsLog.MOBILE_CONNECTION_STATE_CHANGED, |
| TelephonyStatsLog.MOBILE_CONNECTION_STATE_CHANGED__STATE__ACTIVE, |
| mPhone.getPhoneId(), mId, getApnTypeBitmask(), canHandleDefault()); |
| // If we were retrying there maybe more than one, otherwise they'll only be one. |
| notifyAllWithEvent(null, DctConstants.EVENT_DATA_SETUP_COMPLETE, |
| Phone.REASON_CONNECTED); |
| |
| mPhone.getCallTracker().registerForVoiceCallStarted(getHandler(), |
| DataConnection.EVENT_DATA_CONNECTION_VOICE_CALL_STARTED, null); |
| mPhone.getCallTracker().registerForVoiceCallEnded(getHandler(), |
| DataConnection.EVENT_DATA_CONNECTION_VOICE_CALL_ENDED, null); |
| |
| // If the EVENT_CONNECT set the current max retry restore it here |
| // if it didn't then this is effectively a NOP. |
| mDcController.addActiveDcByCid(DataConnection.this); |
| |
| updateTcpBufferSizes(mRilRat); |
| updateLinkBandwidthsFromCarrierConfig(mRilRat); |
| |
| final NetworkAgentConfig.Builder configBuilder = new NetworkAgentConfig.Builder(); |
| configBuilder.setLegacyType(ConnectivityManager.TYPE_MOBILE); |
| configBuilder.setLegacyTypeName(NETWORK_TYPE); |
| int networkType = getNetworkType(); |
| configBuilder.setLegacySubType(networkType); |
| configBuilder.setLegacySubTypeName(TelephonyManager.getNetworkTypeName(networkType)); |
| configBuilder.setLegacyExtraInfo(mApnSetting.getApnName()); |
| final CarrierSignalAgent carrierSignalAgent = mPhone.getCarrierSignalAgent(); |
| if (carrierSignalAgent.hasRegisteredReceivers(TelephonyManager |
| .ACTION_CARRIER_SIGNAL_REDIRECTED)) { |
| // carrierSignal Receivers will place the carrier-specific provisioning notification |
| configBuilder.setProvisioningNotificationEnabled(false); |
| } |
| |
| final String subscriberId = mPhone.getSubscriberId(); |
| if (!TextUtils.isEmpty(subscriberId)) { |
| configBuilder.setSubscriberId(subscriberId); |
| } |
| |
| // set skip464xlat if it is not default otherwise |
| if (shouldSkip464Xlat()) { |
| configBuilder.setNat64DetectionEnabled(false); |
| } |
| |
| mUnmeteredUseOnly = isUnmeteredUseOnly(); |
| mMmsUseOnly = isMmsUseOnly(); |
| mEnterpriseUse = isEnterpriseUse(); |
| |
| if (DBG) { |
| log("mRestrictedNetworkOverride = " + mRestrictedNetworkOverride |
| + ", mUnmeteredUseOnly = " + mUnmeteredUseOnly |
| + ", mMmsUseOnly = " + mMmsUseOnly |
| + ", mEnterpriseUse = " + mEnterpriseUse); |
| } |
| |
| // Always register a VcnNetworkPolicyChangeListener, regardless of whether this is a |
| // handover |
| // or new Network. |
| mVcnManager.addVcnNetworkPolicyChangeListener( |
| new HandlerExecutor(getHandler()), mVcnPolicyChangeListener); |
| |
| if (mConnectionParams != null |
| && mConnectionParams.mRequestType == REQUEST_TYPE_HANDOVER) { |
| // If this is a data setup for handover, we need to reuse the existing network agent |
| // instead of creating a new one. This should be transparent to connectivity |
| // service. |
| DcTracker dcTracker = mPhone.getDcTracker(getHandoverSourceTransport()); |
| DataConnection dc = dcTracker.getDataConnectionByApnType( |
| mConnectionParams.mApnContext.getApnType()); |
| // It's possible that the source data connection has been disconnected by the modem |
| // already. If not, set its handover state to completed. |
| if (dc != null) { |
| // Transfer network agent from the original data connection as soon as the |
| // new handover data connection is connected. |
| dc.setHandoverState(HANDOVER_STATE_COMPLETED); |
| } |
| |
| if (mHandoverSourceNetworkAgent != null) { |
| String logStr = "Transfer network agent " + mHandoverSourceNetworkAgent.getTag() |
| + " successfully."; |
| log(logStr); |
| mHandoverLocalLog.log(logStr); |
| mNetworkAgent = mHandoverSourceNetworkAgent; |
| mNetworkAgent.acquireOwnership(DataConnection.this, mTransportType); |
| |
| // TODO: Should evaluate mDisabledApnTypeBitMask again after handover. We don't |
| // do it now because connectivity service does not support dynamically removing |
| // immutable capabilities. |
| |
| mNetworkAgent.updateLegacySubtype(DataConnection.this); |
| // Update the capability after handover |
| mNetworkAgent.sendNetworkCapabilities(getNetworkCapabilities(), |
| DataConnection.this); |
| mNetworkAgent.sendLinkProperties(mLinkProperties, DataConnection.this); |
| mHandoverSourceNetworkAgent = null; |
| } else { |
| String logStr = "Failed to get network agent from original data connection"; |
| loge(logStr); |
| mHandoverLocalLog.log(logStr); |
| return; |
| } |
| } else { |
| mScore = calculateScore(); |
| final NetworkFactory factory = PhoneFactory.getNetworkFactory( |
| mPhone.getPhoneId()); |
| final NetworkProvider provider = (null == factory) ? null : factory.getProvider(); |
| |
| mDisabledApnTypeBitMask |= getDisallowedApnTypes(); |
| updateLinkPropertiesHttpProxy(); |
| mNetworkAgent = new DcNetworkAgent(DataConnection.this, mPhone, mScore, |
| configBuilder.build(), provider, mTransportType); |
| |
| VcnNetworkPolicyResult policyResult = |
| mVcnManager.applyVcnNetworkPolicy( |
| getNetworkCapabilities(), getLinkProperties()); |
| if (policyResult.isTeardownRequested()) { |
| tearDownAll( |
| Phone.REASON_VCN_REQUESTED_TEARDOWN, |
| DcTracker.RELEASE_TYPE_DETACH, |
| null /* onCompletedMsg */); |
| } else { |
| // All network agents start out in CONNECTING mode, but DcNetworkAgents are |
| // created when the network is already connected. Hence, send the connected |
| // notification immediately. |
| mNetworkAgent.markConnected(); |
| } |
| |
| // The network agent is always created with NOT_SUSPENDED capability, but the |
| // network might be already out of service (or voice call is ongoing) just right |
| // before data connection is created. Connectivity service would not allow a network |
| // created with suspended state, so we create a non-suspended network first, and |
| // then immediately evaluate the suspended state. |
| sendMessage(obtainMessage(EVENT_UPDATE_SUSPENDED_STATE)); |
| } |
| |
| // The qos parameters are set when the call is connected |
| syncQosToNetworkAgent(); |
| |
| if (mTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) { |
| mPhone.mCi.registerForNattKeepaliveStatus( |
| getHandler(), DataConnection.EVENT_KEEPALIVE_STATUS, null); |
| mPhone.mCi.registerForLceInfo( |
| getHandler(), DataConnection.EVENT_LINK_CAPACITY_CHANGED, null); |
| } |
| notifyDataConnectionState(); |
| TelephonyMetrics.getInstance().writeRilDataCallEvent(mPhone.getPhoneId(), |
| mCid, getApnTypeBitmask(), RilDataCall.State.CONNECTED); |
| } |
| |
| @Override |
| public void exit() { |
| if (DBG) log("DcActiveState: exit dc=" + this); |
| mPhone.getCallTracker().unregisterForVoiceCallStarted(getHandler()); |
| mPhone.getCallTracker().unregisterForVoiceCallEnded(getHandler()); |
| |
| if (mTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) { |
| mPhone.mCi.unregisterForNattKeepaliveStatus(getHandler()); |
| mPhone.mCi.unregisterForLceInfo(getHandler()); |
| } |
| |
| // If we are still owning this agent, then we should inform connectivity service the |
| // data connection is disconnected. There is one exception that we shouldn't unregister, |
| // which is when IWLAN handover is ongoing. Instead of unregistering, the agent will |
| // be transferred to the new data connection on the other transport. |
| if (mNetworkAgent != null) { |
| syncQosToNetworkAgent(); |
| if (mHandoverState == HANDOVER_STATE_IDLE) { |
| mNetworkAgent.unregister(DataConnection.this); |
| } |
| mNetworkAgent.releaseOwnership(DataConnection.this); |
| } |
| mNetworkAgent = null; |
| |
| TelephonyMetrics.getInstance().writeRilDataCallEvent(mPhone.getPhoneId(), |
| mCid, getApnTypeBitmask(), RilDataCall.State.DISCONNECTED); |
| |
| mVcnManager.removeVcnNetworkPolicyChangeListener(mVcnPolicyChangeListener); |
| |
| mPhone.getCarrierPrivilegesTracker().unregisterCarrierPrivilegesListener(getHandler()); |
| } |
| |
| @Override |
| public boolean processMessage(Message msg) { |
| boolean retVal; |
| |
| switch (msg.what) { |
| case EVENT_CONNECT: { |
| ConnectionParams cp = (ConnectionParams) msg.obj; |
| // either add this new apn context to our set or |
| // update the existing cp with the latest connection generation number |
| mApnContexts.put(cp.mApnContext, cp); |
| // TODO (b/118347948): evaluate if it's still needed after assigning |
| // different scores to different Cellular network. |
| mDisabledApnTypeBitMask &= ~cp.mApnContext.getApnTypeBitmask(); |
| mNetworkAgent.sendNetworkCapabilities(getNetworkCapabilities(), |
| DataConnection.this); |
| if (DBG) { |
| log("DcActiveState: EVENT_CONNECT cp=" + cp + " dc=" + DataConnection.this); |
| } |
| notifyConnectCompleted(cp, DataFailCause.NONE, |
| DataCallResponse.HANDOVER_FAILURE_MODE_UNKNOWN, false); |
| retVal = HANDLED; |
| break; |
| } |
| case EVENT_DISCONNECT: { |
| DisconnectParams dp = (DisconnectParams) msg.obj; |
| if (DBG) { |
| log("DcActiveState: EVENT_DISCONNECT dp=" + dp |
| + " dc=" + DataConnection.this); |
| } |
| if (mApnContexts.containsKey(dp.mApnContext)) { |
| if (DBG) { |
| log("DcActiveState msg.what=EVENT_DISCONNECT RefCount=" |
| + mApnContexts.size()); |
| } |
| |
| if (mApnContexts.size() == 1) { |
| mApnContexts.clear(); |
| mDisconnectParams = dp; |
| mConnectionParams = null; |
| dp.mTag = mTag; |
| tearDownData(dp); |
| transitionTo(mDisconnectingState); |
| } else { |
| mApnContexts.remove(dp.mApnContext); |
| // TODO (b/118347948): evaluate if it's still needed after assigning |
| // different scores to different Cellular network. |
| mDisabledApnTypeBitMask |= dp.mApnContext.getApnTypeBitmask(); |
| mNetworkAgent.sendNetworkCapabilities(getNetworkCapabilities(), |
| DataConnection.this); |
| notifyDisconnectCompleted(dp, false); |
| } |
| } else { |
| log("DcActiveState ERROR no such apnContext=" + dp.mApnContext |
| + " in this dc=" + DataConnection.this); |
| notifyDisconnectCompleted(dp, false); |
| } |
| retVal = HANDLED; |
| break; |
| } |
| case EVENT_DISCONNECT_ALL: { |
| if (DBG) { |
| log("DcActiveState EVENT_DISCONNECT clearing apn contexts," |
| + " dc=" + DataConnection.this); |
| } |
| DisconnectParams dp = (DisconnectParams) msg.obj; |
| mDisconnectParams = dp; |
| mConnectionParams = null; |
| dp.mTag = mTag; |
| tearDownData(dp); |
| transitionTo(mDisconnectingState); |
| retVal = HANDLED; |
| break; |
| } |
| case EVENT_LOST_CONNECTION: { |
| if (DBG) { |
| log("DcActiveState EVENT_LOST_CONNECTION dc=" + DataConnection.this); |
| } |
| |
| mInactiveState.setEnterNotificationParams(DataFailCause.LOST_CONNECTION); |
| transitionTo(mInactiveState); |
| retVal = HANDLED; |
| break; |
| } |
| case EVENT_DATA_CONNECTION_DRS_OR_RAT_CHANGED: { |
| AsyncResult ar = (AsyncResult) msg.obj; |
| Pair<Integer, Integer> drsRatPair = (Pair<Integer, Integer>) ar.result; |
| mDataRegState = drsRatPair.first; |
| updateTcpBufferSizes(drsRatPair.second); |
| if (isBandwidthSourceKey(DctConstants.BANDWIDTH_SOURCE_CARRIER_CONFIG_KEY)) { |
| updateLinkBandwidthsFromCarrierConfig(drsRatPair.second); |
| } |
| mRilRat = drsRatPair.second; |
| if (DBG) { |
| log("DcActiveState: EVENT_DATA_CONNECTION_DRS_OR_RAT_CHANGED" |
| + " drs=" + mDataRegState |
| + " mRilRat=" + mRilRat); |
| } |
| updateSuspendState(); |
| if (mNetworkAgent != null) { |
| mNetworkAgent.updateLegacySubtype(DataConnection.this); |
| // The new suspended state will be passed through connectivity service |
| // through NET_CAPABILITY_NOT_SUSPENDED. |
| mNetworkAgent.sendNetworkCapabilities(getNetworkCapabilities(), |
| DataConnection.this); |
| mNetworkAgent.sendLinkProperties(mLinkProperties, DataConnection.this); |
| } |
| retVal = HANDLED; |
| mDataCallSessionStats.onDrsOrRatChanged(mRilRat); |
| break; |
| } |
| case EVENT_NR_FREQUENCY_CHANGED: |
| // fallthrough |
| case EVENT_CARRIER_CONFIG_LINK_BANDWIDTHS_CHANGED: |
| if (isBandwidthSourceKey(DctConstants.BANDWIDTH_SOURCE_CARRIER_CONFIG_KEY)) { |
| updateLinkBandwidthsFromCarrierConfig(mRilRat); |
| } |
| if (mNetworkAgent != null) { |
| mNetworkAgent.sendNetworkCapabilities(getNetworkCapabilities(), |
| DataConnection.this); |
| } |
| retVal = HANDLED; |
| break; |
| case EVENT_DATA_CONNECTION_METEREDNESS_CHANGED: |
| boolean isUnmetered = (boolean) msg.obj; |
| if (isUnmetered == mUnmeteredOverride) { |
| retVal = HANDLED; |
| break; |
| } |
| mUnmeteredOverride = isUnmetered; |
| if (mNetworkAgent != null) { |
| mNetworkAgent.updateLegacySubtype(DataConnection.this); |
| mNetworkAgent.sendNetworkCapabilities(getNetworkCapabilities(), |
| DataConnection.this); |
| } |
| retVal = HANDLED; |
| break; |
| case EVENT_DATA_CONNECTION_CONGESTEDNESS_CHANGED: |
| boolean isCongested = (boolean) msg.obj; |
| if (isCongested == mCongestedOverride) { |
| retVal = HANDLED; |
| break; |
| } |
| mCongestedOverride = isCongested; |
| if (mNetworkAgent != null) { |
| mNetworkAgent.updateLegacySubtype(DataConnection.this); |
| mNetworkAgent.sendNetworkCapabilities(getNetworkCapabilities(), |
| DataConnection.this); |
| } |
| retVal = HANDLED; |
| break; |
| case EVENT_DATA_CONNECTION_ROAM_ON: |
| case EVENT_DATA_CONNECTION_ROAM_OFF: { |
| if (mNetworkAgent != null) { |
| mNetworkAgent.updateLegacySubtype(DataConnection.this); |
| mNetworkAgent.sendNetworkCapabilities(getNetworkCapabilities(), |
| DataConnection.this); |
| } |
| retVal = HANDLED; |
| break; |
| } |
| case EVENT_DATA_CONNECTION_VOICE_CALL_STARTED: |
| case EVENT_DATA_CONNECTION_VOICE_CALL_ENDED: |
| case EVENT_CSS_INDICATOR_CHANGED: |
| case EVENT_UPDATE_SUSPENDED_STATE: { |
| updateSuspendState(); |
| if (mNetworkAgent != null) { |
| mNetworkAgent.sendNetworkCapabilities(getNetworkCapabilities(), |
| DataConnection.this); |
| } |
| retVal = HANDLED; |
| break; |
| } |
| case EVENT_BW_REFRESH_RESPONSE: { |
| AsyncResult ar = (AsyncResult)msg.obj; |
| if (ar.exception != null) { |
| log("EVENT_BW_REFRESH_RESPONSE: error ignoring, e=" + ar.exception); |
| } else { |
| if (isBandwidthSourceKey(DctConstants.BANDWIDTH_SOURCE_MODEM_KEY)) { |
| updateLinkBandwidthsFromModem((List<LinkCapacityEstimate>) ar.result); |
| } |
| } |
| retVal = HANDLED; |
| break; |
| } |
| case EVENT_KEEPALIVE_START_REQUEST: { |
| KeepalivePacketData pkt = (KeepalivePacketData) msg.obj; |
| int slotId = msg.arg1; |
| int intervalMillis = msg.arg2 * 1000; |
| if (mTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) { |
| mPhone.mCi.startNattKeepalive( |
| DataConnection.this.mCid, pkt, intervalMillis, |
| DataConnection.this.obtainMessage( |
| EVENT_KEEPALIVE_STARTED, slotId, 0, null)); |
| } else { |
| // We currently do not support NATT Keepalive requests using the |
| // DataService API, so unless the request is WWAN (always bound via |
| // the CommandsInterface), the request cannot be honored. |
| // |
| // TODO: b/72331356 to add support for Keepalive to the DataService |
| // so that keepalive requests can be handled (if supported) by the |
| // underlying transport. |
| if (mNetworkAgent != null) { |
| mNetworkAgent.sendSocketKeepaliveEvent( |
| msg.arg1, SocketKeepalive.ERROR_INVALID_NETWORK); |
| } |
| } |
| retVal = HANDLED; |
| break; |
| } |
| case EVENT_KEEPALIVE_STOP_REQUEST: { |
| int slotId = msg.arg1; |
| int handle = mNetworkAgent.keepaliveTracker.getHandleForSlot(slotId); |
| if (handle < 0) { |
| loge("No slot found for stopSocketKeepalive! " + slotId); |
| mNetworkAgent.sendSocketKeepaliveEvent( |
| slotId, SocketKeepalive.ERROR_NO_SUCH_SLOT); |
| retVal = HANDLED; |
| break; |
| } else { |
| logd("Stopping keepalive with handle: " + handle); |
| } |
| |
| mPhone.mCi.stopNattKeepalive( |
| handle, DataConnection.this.obtainMessage( |
| EVENT_KEEPALIVE_STOPPED, handle, slotId, null)); |
| retVal = HANDLED; |
| break; |
| } |
| case EVENT_KEEPALIVE_STARTED: { |
| AsyncResult ar = (AsyncResult) msg.obj; |
| final int slot = msg.arg1; |
| if (ar.exception != null || ar.result == null) { |
| loge("EVENT_KEEPALIVE_STARTED: error starting keepalive, e=" |
| + ar.exception); |
| mNetworkAgent.sendSocketKeepaliveEvent( |
| slot, SocketKeepalive.ERROR_HARDWARE_ERROR); |
| } else { |
| KeepaliveStatus ks = (KeepaliveStatus) ar.result; |
| if (ks == null) { |
| loge("Null KeepaliveStatus received!"); |
| } else { |
| mNetworkAgent.keepaliveTracker.handleKeepaliveStarted(slot, ks); |
| } |
| } |
| retVal = HANDLED; |
| break; |
| } |
| case EVENT_KEEPALIVE_STATUS: { |
| AsyncResult ar = (AsyncResult) msg.obj; |
| if (ar.exception != null) { |
| loge("EVENT_KEEPALIVE_STATUS: error in keepalive, e=" + ar.exception); |
| // We have no way to notify connectivity in this case. |
| } |
| if (ar.result != null) { |
| KeepaliveStatus ks = (KeepaliveStatus) ar.result; |
| mNetworkAgent.keepaliveTracker.handleKeepaliveStatus(ks); |
| } |
| |
| retVal = HANDLED; |
| break; |
| } |
| case EVENT_KEEPALIVE_STOPPED: { |
| AsyncResult ar = (AsyncResult) msg.obj; |
| final int handle = msg.arg1; |
| final int slotId = msg.arg2; |
| |
| if (ar.exception != null) { |
| loge("EVENT_KEEPALIVE_STOPPED: error stopping keepalive for handle=" |
| + handle + " e=" + ar.exception); |
| mNetworkAgent.keepaliveTracker.handleKeepaliveStatus( |
| new KeepaliveStatus(KeepaliveStatus.ERROR_UNKNOWN)); |
| } else { |
| log("Keepalive Stop Requested for handle=" + handle); |
| mNetworkAgent.keepaliveTracker.handleKeepaliveStatus( |
| new KeepaliveStatus(handle, KeepaliveStatus.STATUS_INACTIVE)); |
| } |
| retVal = HANDLED; |
| break; |
| } |
| case EVENT_LINK_CAPACITY_CHANGED: { |
| AsyncResult ar = (AsyncResult) msg.obj; |
| if (ar.exception != null) { |
| loge("EVENT_LINK_CAPACITY_CHANGED e=" + ar.exception); |
| } else { |
| if (isBandwidthSourceKey(DctConstants.BANDWIDTH_SOURCE_MODEM_KEY)) { |
| updateLinkBandwidthsFromModem((List<LinkCapacityEstimate>) ar.result); |
| } |
| } |
| retVal = HANDLED; |
| break; |
| } |
| case EVENT_LINK_BANDWIDTH_ESTIMATOR_UPDATE: { |
| AsyncResult ar = (AsyncResult) msg.obj; |
| if (ar.exception != null) { |
| loge("EVENT_LINK_BANDWIDTH_ESTIMATOR_UPDATE e=" + ar.exception); |
| } else { |
| Pair<Integer, Integer> pair = (Pair<Integer, Integer>) ar.result; |
| if (isBandwidthSourceKey( |
| DctConstants.BANDWIDTH_SOURCE_BANDWIDTH_ESTIMATOR_KEY)) { |
| updateLinkBandwidthsFromBandwidthEstimator(pair.first, pair.second); |
| } |
| } |
| retVal = HANDLED; |
| break; |
| } |
| case EVENT_REEVALUATE_RESTRICTED_STATE: { |
| // If the network was restricted, and now it does not need to be restricted |
| // anymore, we should add the NET_CAPABILITY_NOT_RESTRICTED capability. |
| if (mRestrictedNetworkOverride && !shouldRestrictNetwork()) { |
| if (DBG) { |
| log("Data connection becomes not-restricted. dc=" + this); |
| } |
| // Note we only do this when network becomes non-restricted. When a |
| // non-restricted becomes restricted (e.g. users disable data, or turn off |
| // data roaming), DCT will explicitly tear down the networks (because |
| // connectivity service does not support force-close TCP connections today). |
| // Also note that NET_CAPABILITY_NOT_RESTRICTED is an immutable capability |
| // (see {@link NetworkCapabilities}) once we add it to the network, we can't |
| // remove it through the entire life cycle of the connection. |
| mRestrictedNetworkOverride = false; |
| mNetworkAgent.sendNetworkCapabilities(getNetworkCapabilities(), |
| DataConnection.this); |
| } |
| |
| // If the data does need to be unmetered use only (e.g. users turn on data, or |
| // device is not roaming anymore assuming data roaming is off), then we can |
| // dynamically add those metered APN type capabilities back. (But not the |
| // other way around because most of the APN-type capabilities are immutable |
| // capabilities.) |
| if (mUnmeteredUseOnly && !isUnmeteredUseOnly()) { |
| mUnmeteredUseOnly = false; |
| mNetworkAgent.sendNetworkCapabilities(getNetworkCapabilities(), |
| DataConnection.this); |
| } |
| |
| mMmsUseOnly = isMmsUseOnly(); |
| |
| retVal = HANDLED; |
| break; |
| } |
| case EVENT_REEVALUATE_DATA_CONNECTION_PROPERTIES: { |
| // Update other properties like link properties if needed in future. |
| updateScore(); |
| retVal = HANDLED; |
| break; |
| } |
| case EVENT_NR_STATE_CHANGED: { |
| updateTcpBufferSizes(mRilRat); |
| if (isBandwidthSourceKey(DctConstants.BANDWIDTH_SOURCE_CARRIER_CONFIG_KEY)) { |
| updateLinkBandwidthsFromCarrierConfig(mRilRat); |
| } |
| if (mNetworkAgent != null) { |
| mNetworkAgent.sendLinkProperties(mLinkProperties, DataConnection.this); |
| mNetworkAgent.sendNetworkCapabilities(getNetworkCapabilities(), |
| DataConnection.this); |
| } |
| retVal = HANDLED; |
| break; |
| } |
| case EVENT_CARRIER_PRIVILEGED_UIDS_CHANGED: |
| AsyncResult asyncResult = (AsyncResult) msg.obj; |
| int[] administratorUids = (int[]) asyncResult.result; |
| mAdministratorUids = Arrays.copyOf(administratorUids, administratorUids.length); |
| |
| // Administrator UIDs changed, so update NetworkAgent with new |
| // NetworkCapabilities |
| if (mNetworkAgent != null) { |
| mNetworkAgent.sendNetworkCapabilities( |
| getNetworkCapabilities(), DataConnection.this); |
| } |
| retVal = HANDLED; |
| break; |
| case EVENT_START_HANDOVER: //calls startHandover() |
| Consumer<Integer> r = (Consumer<Integer>) msg.obj; |
| r.accept(msg.arg1); |
| retVal = HANDLED; |
| break; |
| |
| default: |
| if (VDBG) { |
| log("DcActiveState not handled msg.what=" + getWhatToString(msg.what)); |
| } |
| retVal = NOT_HANDLED; |
| break; |
| } |
| return retVal; |
| } |
| } |
| private DcActiveState mActiveState = new DcActiveState(); |
| |
| /** |
| * The state machine is disconnecting. |
| */ |
| private class DcDisconnectingState extends State { |
| @Override |
| public void enter() { |
| TelephonyStatsLog.write(TelephonyStatsLog.MOBILE_CONNECTION_STATE_CHANGED, |
| TelephonyStatsLog.MOBILE_CONNECTION_STATE_CHANGED__STATE__DISCONNECTING, |
| mPhone.getPhoneId(), mId, getApnTypeBitmask(), canHandleDefault()); |
| notifyDataConnectionState(); |
| } |
| @Override |
| public boolean processMessage(Message msg) { |
| boolean retVal; |
| |
| switch (msg.what) { |
| case EVENT_CONNECT: |
| if (DBG) log("DcDisconnectingState msg.what=EVENT_CONNECT. Defer. RefCount = " |
| + mApnContexts.size()); |
| deferMessage(msg); |
| retVal = HANDLED; |
| break; |
| |
| case EVENT_DEACTIVATE_DONE: |
| DisconnectParams dp = (DisconnectParams) msg.obj; |
| |
| String str = "DcDisconnectingState msg.what=EVENT_DEACTIVATE_DONE RefCount=" |
| + mApnContexts.size(); |
| |
| if (DBG) log(str); |
| if (dp.mApnContext != null) dp.mApnContext.requestLog(str); |
| |
| // Clear out existing qos sessions |
| updateQosParameters(null); |
| |
| if (dp.mTag == mTag) { |
| // Transition to inactive but send notifications after |
| // we've entered the mInactive state. |
| mInactiveState.setEnterNotificationParams(dp); |
| transitionTo(mInactiveState); |
| } else { |
| if (DBG) log("DcDisconnectState stale EVENT_DEACTIVATE_DONE" |
| + " dp.tag=" + dp.mTag + " mTag=" + mTag); |
| } |
| retVal = HANDLED; |
| break; |
| |
| default: |
| if (VDBG) { |
| log("DcDisconnectingState not handled msg.what=" |
| + getWhatToString(msg.what)); |
| } |
| retVal = NOT_HANDLED; |
| break; |
| } |
| return retVal; |
| } |
| } |
| private DcDisconnectingState mDisconnectingState = new DcDisconnectingState(); |
| |
| /** |
| * The state machine is disconnecting after an creating a connection. |
| */ |
| private class DcDisconnectionErrorCreatingConnection extends State { |
| @Override |
| public void enter() { |
| TelephonyStatsLog.write(TelephonyStatsLog.MOBILE_CONNECTION_STATE_CHANGED, |
| TelephonyStatsLog |
| .MOBILE_CONNECTION_STATE_CHANGED__STATE__DISCONNECTION_ERROR_CREATING_CONNECTION, |
| mPhone.getPhoneId(), mId, getApnTypeBitmask(), canHandleDefault()); |
| notifyDataConnectionState(); |
| } |
| @Override |
| public boolean processMessage(Message msg) { |
| boolean retVal; |
| |
| switch (msg.what) { |
| case EVENT_DEACTIVATE_DONE: |
| ConnectionParams cp = (ConnectionParams) msg.obj; |
| if (cp.mTag == mTag) { |
| String str = "DcDisconnectionErrorCreatingConnection" + |
| " msg.what=EVENT_DEACTIVATE_DONE"; |
| if (DBG) log(str); |
| if (cp.mApnContext != null) cp.mApnContext.requestLog(str); |
| |
| // Transition to inactive but send notifications after |
| // we've entered the mInactive state. |
| mInactiveState.setEnterNotificationParams(cp, |
| DataFailCause.UNACCEPTABLE_NETWORK_PARAMETER, |
| DataCallResponse.HANDOVER_FAILURE_MODE_UNKNOWN); |
| transitionTo(mInactiveState); |
| } else { |
| if (DBG) { |
| log("DcDisconnectionErrorCreatingConnection stale EVENT_DEACTIVATE_DONE" |
| + " dp.tag=" + cp.mTag + ", mTag=" + mTag); |
| } |
| } |
| retVal = HANDLED; |
| break; |
| |
| default: |
| if (VDBG) { |
| log("DcDisconnectionErrorCreatingConnection not handled msg.what=" |
| + getWhatToString(msg.what)); |
| } |
| retVal = NOT_HANDLED; |
| break; |
| } |
| return retVal; |
| } |
| } |
| private DcDisconnectionErrorCreatingConnection mDisconnectingErrorCreatingConnection = |
| new DcDisconnectionErrorCreatingConnection(); |
| |
| /** |
| * Bring up a connection to the apn and return an AsyncResult in onCompletedMsg. |
| * Used for cellular networks that use Access Point Names (APN) such |
| * as GSM networks. |
| * |
| * @param apnContext is the Access Point Name to bring up a connection to |
| * @param profileId for the connection |
| * @param rilRadioTechnology Radio technology for the data connection |
| * @param onCompletedMsg is sent with its msg.obj as an AsyncResult object. |
| * With AsyncResult.userObj set to the original msg.obj, |
| * AsyncResult.result = FailCause and AsyncResult.exception = Exception(). |
| * @param connectionGeneration used to track a single connection request so disconnects can get |
| * ignored if obsolete. |
| * @param requestType Data request type |
| * @param subId the subscription id associated with this data connection. |
| * @param isApnPreferred whether or not the apn is preferred. |
| */ |
| public void bringUp(ApnContext apnContext, int profileId, int rilRadioTechnology, |
| Message onCompletedMsg, int connectionGeneration, |
| @RequestNetworkType int requestType, int subId, boolean isApnPreferred) { |
| if (DBG) { |
| log("bringUp: apnContext=" + apnContext + " onCompletedMsg=" + onCompletedMsg); |
| } |
| |
| if (mApnSetting == null) { |
| mApnSetting = apnContext.getApnSetting(); |
| } |
| |
| sendMessage(DataConnection.EVENT_CONNECT, |
| new ConnectionParams(apnContext, profileId, rilRadioTechnology, onCompletedMsg, |
| connectionGeneration, requestType, subId, isApnPreferred)); |
| } |
| |
| /** |
| * Tear down the connection through the apn on the network. |
| * |
| * @param apnContext APN context |
| * @param reason reason to tear down |
| * @param onCompletedMsg is sent with its msg.obj as an AsyncResult object. |
| * With AsyncResult.userObj set to the original msg.obj. |
| */ |
| public void tearDown(ApnContext apnContext, String reason, Message onCompletedMsg) { |
| if (DBG) { |
| log("tearDown: apnContext=" + apnContext + " reason=" + reason + " onCompletedMsg=" |
| + onCompletedMsg); |
| } |
| sendMessage(DataConnection.EVENT_DISCONNECT, |
| new DisconnectParams(apnContext, reason, DcTracker.RELEASE_TYPE_DETACH, |
| onCompletedMsg)); |
| } |
| |
| // ******* "public" interface |
| |
| /** |
| * Used for testing purposes. |
| */ |
| void tearDownNow() { |
| if (DBG) log("tearDownNow()"); |
| sendMessage(obtainMessage(EVENT_TEAR_DOWN_NOW)); |
| } |
| |
| /** |
| * Tear down the connection through the apn on the network. Ignores reference count and |
| * and always tears down. |
| * |
| * @param releaseType Data release type |
| * @param onCompletedMsg is sent with its msg.obj as an AsyncResult object. |
| * With AsyncResult.userObj set to the original msg.obj. |
| */ |
| public void tearDownAll(String reason, @ReleaseNetworkType int releaseType, |
| Message onCompletedMsg) { |
| if (DBG) { |
| log("tearDownAll: reason=" + reason + ", releaseType=" |
| + DcTracker.releaseTypeToString(releaseType)); |
| } |
| sendMessage(DataConnection.EVENT_DISCONNECT_ALL, |
| new DisconnectParams(null, reason, releaseType, onCompletedMsg)); |
| } |
| |
| /** |
| * Reset the data connection to inactive state. |
| */ |
| public void reset() { |
| sendMessage(EVENT_RESET); |
| if (DBG) log("reset"); |
| } |
| |
| /** |
| * Re-evaluate the restricted state. If the restricted data connection does not need to be |
| * restricted anymore, we need to dynamically change the network's capability. |
| */ |
| void reevaluateRestrictedState() { |
| sendMessage(EVENT_REEVALUATE_RESTRICTED_STATE); |
| if (DBG) log("reevaluate restricted state"); |
| } |
| |
| /** |
| * Re-evaluate the data connection properties. For example, it will recalculate data connection |
| * score and update through network agent it if changed. |
| */ |
| void reevaluateDataConnectionProperties() { |
| sendMessage(EVENT_REEVALUATE_DATA_CONNECTION_PROPERTIES); |
| if (DBG) log("reevaluate data connection properties"); |
| } |
| |
| /** |
| * @return The parameters used for initiating a data connection. |
| */ |
| public ConnectionParams getConnectionParams() { |
| return mConnectionParams; |
| } |
| |
| /** |
| * Update PCSCF addresses |
| * |
| * @param response |
| */ |
| public void updatePcscfAddr(DataCallResponse response) { |
| mPcscfAddr = response.getPcscfAddresses().stream() |
| .map(InetAddress::getHostAddress).toArray(String[]::new); |
| } |
| |
| /** |
| * @return The list of PCSCF addresses |
| */ |
| public String[] getPcscfAddresses() { |
| return mPcscfAddr; |
| } |
| |
| /** |
| * Using the result of the SETUP_DATA_CALL determine the retry delay. |
| * |
| * @param response The response from setup data call |
| * @return {@link RetryManager#NO_SUGGESTED_RETRY_DELAY} if not suggested. |
| * {@link RetryManager#NO_RETRY} if retry should not happen. Otherwise the delay in milliseconds |
| * to the next SETUP_DATA_CALL. |
| */ |
| private long getSuggestedRetryDelay(DataCallResponse response) { |
| /** According to ril.h |
| * The value < 0 means no value is suggested |
| * The value 0 means retry should be done ASAP. |
| * The value of Long.MAX_VALUE(0x7fffffffffffffff) means no retry. |
| */ |
| if (response == null) { |
| return RetryManager.NO_SUGGESTED_RETRY_DELAY; |
| } |
| |
| long suggestedRetryTime = response.getRetryDurationMillis(); |
| |
| // The value < 0 means no value is suggested |
| if (suggestedRetryTime < 0) { |
| if (DBG) log("No suggested retry delay."); |
| return RetryManager.NO_SUGGESTED_RETRY_DELAY; |
| } else if (mPhone.getHalVersion().greaterOrEqual(RIL.RADIO_HAL_VERSION_1_6) |
| && suggestedRetryTime == Long.MAX_VALUE) { |
| if (DBG) log("Network suggested not retrying."); |
| return RetryManager.NO_RETRY; |
| } else if (mPhone.getHalVersion().less(RIL.RADIO_HAL_VERSION_1_6) |
| && suggestedRetryTime == Integer.MAX_VALUE) { |
| if (DBG) log("Network suggested not retrying."); |
| return RetryManager.NO_RETRY; |
| } |
| |
| return suggestedRetryTime; |
| } |
| |
| public List<ApnContext> getApnContexts() { |
| return new ArrayList<>(mApnContexts.keySet()); |
| } |
| |
| /** |
| * Return whether there is an ApnContext for the given type in this DataConnection. |
| * @param type APN type to check |
| * @param exclusive true if the given APN type should be the only APN type that exists |
| * @return True if there is an ApnContext for the given type |
| */ |
| private boolean isApnContextAttached(@ApnType int type, boolean exclusive) { |
| boolean attached = mApnContexts.keySet().stream() |
| .map(ApnContext::getApnTypeBitmask) |
| .anyMatch(bitmask -> bitmask == type); |
| if (exclusive) { |
| attached &= mApnContexts.size() == 1; |
| } |
| return attached; |
| } |
| |
| /** Get the network agent of the data connection */ |
| @Nullable |
| DcNetworkAgent getNetworkAgent() { |
| return mNetworkAgent; |
| } |
| |
| void setHandoverState(@HandoverState int state) { |
| if (mHandoverState != state) { |
| String logStr = "State changed from " + handoverStateToString(mHandoverState) |
| + " to " + handoverStateToString(state); |
| mHandoverLocalLog.log(logStr); |
| logd(logStr); |
| mHandoverState = state; |
| } |
| } |
| |
| /** Sets the {@link DataCallSessionStats} mock for this phone ID during unit testing. */ |
| @VisibleForTesting |
| public void setDataCallSessionStats(DataCallSessionStats dataCallSessionStats) { |
| mDataCallSessionStats = dataCallSessionStats; |
| } |
| |
| /** |
| * @return the string for msg.what as our info. |
| */ |
| @Override |
| protected String getWhatToString(int what) { |
| return cmdToString(what); |
| } |
| |
| private static String msgToString(Message msg) { |
| String retVal; |
| if (msg == null) { |
| retVal = "null"; |
| } else { |
| StringBuilder b = new StringBuilder(); |
| |
| b.append("{what="); |
| b.append(cmdToString(msg.what)); |
| |
| b.append(" when="); |
| TimeUtils.formatDuration(msg.getWhen() - SystemClock.uptimeMillis(), b); |
| |
| if (msg.arg1 != 0) { |
| b.append(" arg1="); |
| b.append(msg.arg1); |
| } |
| |
| if (msg.arg2 != 0) { |
| b.append(" arg2="); |
| b.append(msg.arg2); |
| } |
| |
| if (msg.obj != null) { |
| b.append(" obj="); |
| b.append(msg.obj); |
| } |
| |
| b.append(" target="); |
| b.append(msg.getTarget()); |
| |
| b.append(" replyTo="); |
| b.append(msg.replyTo); |
| |
| b.append("}"); |
| |
| retVal = b.toString(); |
| } |
| return retVal; |
| } |
| |
| static void slog(String s) { |
| Rlog.d("DC", s); |
| } |
| |
| /** |
| * Log with debug |
| * |
| * @param s is string log |
| */ |
| @Override |
| protected void log(String s) { |
| Rlog.d(getName(), s); |
| } |
| |
| /** |
| * Log with debug attribute |
| * |
| * @param s is string log |
| */ |
| @Override |
| protected void logd(String s) { |
| Rlog.d(getName(), s); |
| } |
| |
| /** |
| * Log with verbose attribute |
| * |
| * @param s is string log |
| */ |
| @Override |
| protected void logv(String s) { |
| Rlog.v(getName(), s); |
| } |
| |
| /** |
| * Log with info attribute |
| * |
| * @param s is string log |
| */ |
| @Override |
| protected void logi(String s) { |
| Rlog.i(getName(), s); |
| } |
| |
| /** |
| * Log with warning attribute |
| * |
| * @param s is string log |
| */ |
| @Override |
| protected void logw(String s) { |
| Rlog.w(getName(), s); |
| } |
| |
| /** |
| * Log with error attribute |
| * |
| * @param s is string log |
| */ |
| @Override |
| protected void loge(String s) { |
| Rlog.e(getName(), s); |
| } |
| |
| /** |
| * Log with error attribute |
| * |
| * @param s is string log |
| * @param e is a Throwable which logs additional information. |
| */ |
| @Override |
| protected void loge(String s, Throwable e) { |
| Rlog.e(getName(), s, e); |
| } |
| |
| /** Doesn't print mApnList of ApnContext's which would be recursive */ |
| public String toStringSimple() { |
| return getName() + ": State=" + getCurrentState().getName() |
| + " mApnSetting=" + mApnSetting + " RefCount=" + mApnContexts.size() |
| + " mCid=" + mCid + " mCreateTime=" + mCreateTime |
| + " mLastastFailTime=" + mLastFailTime |
| + " mLastFailCause=" + DataFailCause.toString(mLastFailCause) |
| + " mTag=" + mTag |
| + " mLinkProperties=" + mLinkProperties |
| + " linkCapabilities=" + getNetworkCapabilities() |
| + " mRestrictedNetworkOverride=" + mRestrictedNetworkOverride; |
| } |
| |
| @Override |
| public String toString() { |
| return "{" + toStringSimple() + " mApnContexts=" + mApnContexts + "}"; |
| } |
| |
| /** Check if the device is connected to NR 5G Non-Standalone network. */ |
| private boolean isNRConnected() { |
| return mPhone.getServiceState().getNrState() |
| == NetworkRegistrationInfo.NR_STATE_CONNECTED; |
| } |
| |
| /** |
| * @return The disallowed APN types bitmask |
| */ |
| private @ApnType int getDisallowedApnTypes() { |
| CarrierConfigManager configManager = (CarrierConfigManager) |
| mPhone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE); |
| int apnTypesBitmask = 0; |
| if (configManager != null) { |
| PersistableBundle bundle = configManager.getConfigForSubId(mSubId); |
| if (bundle != null) { |
| String key = (mTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) |
| ? CarrierConfigManager.KEY_CARRIER_WWAN_DISALLOWED_APN_TYPES_STRING_ARRAY |
| : CarrierConfigManager.KEY_CARRIER_WLAN_DISALLOWED_APN_TYPES_STRING_ARRAY; |
| if (bundle.getStringArray(key) != null) { |
| String disallowedApnTypesString = |
| TextUtils.join(",", bundle.getStringArray(key)); |
| if (!TextUtils.isEmpty(disallowedApnTypesString)) { |
| apnTypesBitmask = ApnSetting.getApnTypesBitmaskFromString( |
| disallowedApnTypesString); |
| } |
| } |
| } |
| } |
| |
| return apnTypesBitmask; |
| } |
| |
| private void dumpToLog() { |
| dump(null, new PrintWriter(new StringWriter(0)) { |
| @Override |
| public void println(String s) { |
| DataConnection.this.logd(s); |
| } |
| |
| @Override |
| public void flush() { |
| } |
| }, null); |
| } |
| |
| /** |
| * Re-calculate score and update through network agent if it changes. |
| */ |
| private void updateScore() { |
| int oldScore = mScore; |
| mScore = calculateScore(); |
| if (oldScore != mScore && mNetworkAgent != null) { |
| log("Updating score from " + oldScore + " to " + mScore); |
| mNetworkAgent.sendNetworkScore(mScore, this); |
| } |
| } |
| |
| private int calculateScore() { |
| int score = OTHER_CONNECTION_SCORE; |
| |
| // If it's serving a network request that asks NET_CAPABILITY_INTERNET and doesn't have |
| // specify a subId, this dataConnection 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 connections accidentally in DSDS usecase. |
| for (ApnContext apnContext : mApnContexts.keySet()) { |
| for (NetworkRequest networkRequest : apnContext.getNetworkRequests()) { |
| if (networkRequest.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) |
| && networkRequest.getNetworkSpecifier() == null) { |
| score = DEFAULT_INTERNET_CONNECTION_SCORE; |
| break; |
| } |
| } |
| } |
| |
| return score; |
| } |
| |
| private String handoverStateToString(@HandoverState int state) { |
| switch (state) { |
| case HANDOVER_STATE_IDLE: return "IDLE"; |
| case HANDOVER_STATE_BEING_TRANSFERRED: return "BEING_TRANSFERRED"; |
| case HANDOVER_STATE_COMPLETED: return "COMPLETED"; |
| default: return "UNKNOWN"; |
| } |
| } |
| |
| private @DataState int getState() { |
| if (isInactive()) { |
| return TelephonyManager.DATA_DISCONNECTED; |
| } else if (isActivating()) { |
| return TelephonyManager.DATA_CONNECTING; |
| } else if (isActive()) { |
| // The data connection can only be suspended when it's in active state. |
| if (mIsSuspended) { |
| return TelephonyManager.DATA_SUSPENDED; |
| } |
| return TelephonyManager.DATA_CONNECTED; |
| } else if (isDisconnecting()) { |
| return TelephonyManager.DATA_DISCONNECTING; |
| } |
| |
| return TelephonyManager.DATA_UNKNOWN; |
| } |
| |
| /** |
| * Get precise data connection state |
| * |
| * @return The {@link PreciseDataConnectionState} |
| */ |
| public PreciseDataConnectionState getPreciseDataConnectionState() { |
| return new PreciseDataConnectionState.Builder() |
| .setTransportType(mTransportType) |
| .setId(mCid) |
| .setState(getState()) |
| .setApnSetting(mApnSetting) |
| .setLinkProperties(mLinkProperties) |
| .setNetworkType(getNetworkType()) |
| .setFailCause(mDcFailCause) |
| .build(); |
| } |
| |
| /** |
| * Dump the current state. |
| * |
| * @param fd |
| * @param printWriter |
| * @param args |
| */ |
| @Override |
| public void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) { |
| IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, " "); |
| pw.print("DataConnection "); |
| super.dump(fd, pw, args); |
| pw.flush(); |
| pw.increaseIndent(); |
| pw.println("transport type=" |
| + AccessNetworkConstants.transportTypeToString(mTransportType)); |
| pw.println("mApnContexts.size=" + mApnContexts.size()); |
| pw.println("mApnContexts=" + mApnContexts); |
| pw.println("mApnSetting=" + mApnSetting); |
| pw.println("mTag=" + mTag); |
| pw.println("mCid=" + mCid); |
| pw.println("mConnectionParams=" + mConnectionParams); |
| pw.println("mDisconnectParams=" + mDisconnectParams); |
| pw.println("mDcFailCause=" + DataFailCause.toString(mDcFailCause)); |
| pw.println("mPhone=" + mPhone); |
| pw.println("mSubId=" + mSubId); |
| pw.println("mLinkProperties=" + mLinkProperties); |
| pw.flush(); |
| pw.println("mDataRegState=" + mDataRegState); |
| pw.println("mHandoverState=" + handoverStateToString(mHandoverState)); |
| pw.println("mRilRat=" + mRilRat); |
| pw.println("mNetworkCapabilities=" + getNetworkCapabilities()); |
| pw.println("mCreateTime=" + TimeUtils.logTimeOfDay(mCreateTime)); |
| pw.println("mLastFailTime=" + TimeUtils.logTimeOfDay(mLastFailTime)); |
| pw.println("mLastFailCause=" + DataFailCause.toString(mLastFailCause)); |
| pw.println("mUserData=" + mUserData); |
| pw.println("mRestrictedNetworkOverride=" + mRestrictedNetworkOverride); |
| pw.println("mUnmeteredUseOnly=" + mUnmeteredUseOnly); |
| pw.println("mMmsUseOnly=" + mMmsUseOnly); |
| pw.println("mEnterpriseUse=" + mEnterpriseUse); |
| pw.println("mUnmeteredOverride=" + mUnmeteredOverride); |
| pw.println("mCongestedOverride=" + mCongestedOverride); |
| pw.println("mDownlinkBandwidth" + mDownlinkBandwidth); |
| pw.println("mUplinkBandwidth=" + mUplinkBandwidth); |
| pw.println("mDefaultQos=" + mDefaultQos); |
| pw.println("mQosBearerSessions=" + mQosBearerSessions); |
| pw.println("disallowedApnTypes=" |
| + ApnSetting.getApnTypesStringFromBitmask(getDisallowedApnTypes())); |
| pw.println("mInstanceNumber=" + mInstanceNumber); |
| pw.println("mAc=" + mAc); |
| pw.println("mScore=" + mScore); |
| if (mNetworkAgent != null) { |
| mNetworkAgent.dump(fd, pw, args); |
| } |
| pw.println("handover local log:"); |
| pw.increaseIndent(); |
| mHandoverLocalLog.dump(fd, pw, args); |
| pw.decreaseIndent(); |
| pw.decreaseIndent(); |
| pw.println(); |
| pw.flush(); |
| } |
| |
| /** |
| * Class used to track VCN-defined Network policies for this DataConnection. |
| * |
| * <p>MUST be registered with the associated DataConnection's Handler. |
| */ |
| private class DataConnectionVcnNetworkPolicyChangeListener |
| implements VcnNetworkPolicyChangeListener { |
| @Override |
| public void onPolicyChanged() { |
| // Poll the current underlying Network policy from VcnManager and send to NetworkAgent. |
| final NetworkCapabilities networkCapabilities = getNetworkCapabilities(); |
| VcnNetworkPolicyResult policyResult = |
| mVcnManager.applyVcnNetworkPolicy( |
| networkCapabilities, getLinkProperties()); |
| if (policyResult.isTeardownRequested()) { |
| tearDownAll( |
| Phone.REASON_VCN_REQUESTED_TEARDOWN, |
| DcTracker.RELEASE_TYPE_DETACH, |
| null /* onCompletedMsg */); |
| } |
| |
| if (mNetworkAgent != null) { |
| mNetworkAgent.sendNetworkCapabilities(networkCapabilities, DataConnection.this); |
| } |
| } |
| } |
| } |
| |