Refactored precise data connection state

Instead of triggering the data connection state changed
event from outside, now the event is triggering from
DataConnection. Whenever detecting data connection
state changes, the event will be triggered. This significantly
reduced the unnecessary redundant events sent to telephony registry,
even though it has a duplicate detection mechanism.

This are also two behavioral changes.

1. Previously if a data connection supports multiple APN types,
there will be multiple data connection state changed event for
each APN type. Now there is only one. The listener should use
PreciseDataConnectionState.getApnSetting().getApnTypeBitmask()
to check which APN types this data connection supports.

2. If setup data call fails before a data connection instance
can be created, there won't be any event with fail cause.

Fix: 161572838
Test: FrameworksTelephonyTests and manual
Merged-In: I9723d5284c2a8fbae9f63179dc30ddc42da1d0fc
Change-Id: I9723d5284c2a8fbae9f63179dc30ddc42da1d0fc
(cherry picked from commit 34a09a4cbd)
This commit is contained in:
Jack Yu
2020-08-01 19:55:31 -07:00
parent b5d6274622
commit 4f2a849e25
4 changed files with 65 additions and 174 deletions

View File

@@ -26,10 +26,8 @@ import android.os.Binder;
import android.os.Build; import android.os.Build;
import android.os.RemoteException; import android.os.RemoteException;
import android.os.ServiceManager; import android.os.ServiceManager;
import android.telephony.Annotation.ApnType;
import android.telephony.Annotation.CallState; import android.telephony.Annotation.CallState;
import android.telephony.Annotation.DataActivityType; import android.telephony.Annotation.DataActivityType;
import android.telephony.Annotation.DataFailureCause;
import android.telephony.Annotation.DisconnectCauses; import android.telephony.Annotation.DisconnectCauses;
import android.telephony.Annotation.NetworkType; import android.telephony.Annotation.NetworkType;
import android.telephony.Annotation.PreciseCallStates; import android.telephony.Annotation.PreciseCallStates;
@@ -37,7 +35,6 @@ import android.telephony.Annotation.PreciseDisconnectCauses;
import android.telephony.Annotation.RadioPowerState; import android.telephony.Annotation.RadioPowerState;
import android.telephony.Annotation.SimActivationState; import android.telephony.Annotation.SimActivationState;
import android.telephony.Annotation.SrvccState; import android.telephony.Annotation.SrvccState;
import android.telephony.data.ApnSetting;
import android.telephony.emergency.EmergencyNumber; import android.telephony.emergency.EmergencyNumber;
import android.telephony.ims.ImsReasonInfo; import android.telephony.ims.ImsReasonInfo;
import android.util.Log; import android.util.Log;
@@ -413,17 +410,16 @@ public class TelephonyRegistryManager {
* @param subId for which data connection state changed. * @param subId for which data connection state changed.
* @param slotIndex for which data connections state changed. Can be derived from subId except * @param slotIndex for which data connections state changed. Can be derived from subId except
* when subId is invalid. * when subId is invalid.
* @param apnType the apn type bitmask, defined with {@code ApnSetting#TYPE_*} flags.
* @param preciseState the PreciseDataConnectionState * @param preciseState the PreciseDataConnectionState
* *
* @see android.telephony.PreciseDataConnection * @see PreciseDataConnectionState
* @see TelephonyManager#DATA_DISCONNECTED * @see TelephonyManager#DATA_DISCONNECTED
*/ */
public void notifyDataConnectionForSubscriber(int slotIndex, int subId, public void notifyDataConnectionForSubscriber(int slotIndex, int subId,
@ApnType int apnType, @Nullable PreciseDataConnectionState preciseState) { @NonNull PreciseDataConnectionState preciseState) {
try { try {
sRegistry.notifyDataConnectionForSubscriber( sRegistry.notifyDataConnectionForSubscriber(
slotIndex, subId, apnType, preciseState); slotIndex, subId, preciseState);
} catch (RemoteException ex) { } catch (RemoteException ex) {
// system process is dead // system process is dead
} }
@@ -622,25 +618,6 @@ public class TelephonyRegistryManager {
} }
} }
/**
* Notify precise data connection failed cause on certain subscription.
*
* @param subId for which data connection failed.
* @param slotIndex for which data conenction failed. Can be derived from subId except when
* subId is invalid.
* @param apnType the apn type bitmask, defined with {@code ApnSetting#TYPE_*} flags.
* @param apn the APN {@link ApnSetting#getApnName()} of this data connection.
* @param failCause data fail cause.
*/
public void notifyPreciseDataConnectionFailed(int subId, int slotIndex, @ApnType int apnType,
@Nullable String apn, @DataFailureCause int failCause) {
try {
sRegistry.notifyPreciseDataConnectionFailed(slotIndex, subId, apnType, apn, failCause);
} catch (RemoteException ex) {
// system process is dead
}
}
/** /**
* Notify single Radio Voice Call Continuity (SRVCC) state change for the currently active call * Notify single Radio Voice Call Continuity (SRVCC) state change for the currently active call
* on certain subscription. * on certain subscription.

View File

@@ -65,9 +65,7 @@ interface ITelephonyRegistry {
void notifyDataActivity(int state); void notifyDataActivity(int state);
void notifyDataActivityForSubscriber(in int subId, int state); void notifyDataActivityForSubscriber(in int subId, int state);
void notifyDataConnectionForSubscriber( void notifyDataConnectionForSubscriber(
int phoneId, int subId, int apnType, in PreciseDataConnectionState preciseState); int phoneId, int subId, in PreciseDataConnectionState preciseState);
@UnsupportedAppUsage
void notifyDataConnectionFailed(String apnType);
// Uses CellIdentity which is Parcelable here; will convert to CellLocation in client. // Uses CellIdentity which is Parcelable here; will convert to CellLocation in client.
void notifyCellLocation(in CellIdentity cellLocation); void notifyCellLocation(in CellIdentity cellLocation);
void notifyCellLocationForSubscriber(in int subId, in CellIdentity cellLocation); void notifyCellLocationForSubscriber(in int subId, in CellIdentity cellLocation);
@@ -77,8 +75,6 @@ interface ITelephonyRegistry {
int foregroundCallState, int backgroundCallState); int foregroundCallState, int backgroundCallState);
void notifyDisconnectCause(int phoneId, int subId, int disconnectCause, void notifyDisconnectCause(int phoneId, int subId, int disconnectCause,
int preciseDisconnectCause); int preciseDisconnectCause);
void notifyPreciseDataConnectionFailed(int phoneId, int subId, int apnType, String apn,
int failCause);
void notifyCellInfoForSubscriber(in int subId, in List<CellInfo> cellInfo); void notifyCellInfoForSubscriber(in int subId, in List<CellInfo> cellInfo);
void notifySrvccStateChanged(in int subId, in int lteState); void notifySrvccStateChanged(in int subId, in int lteState);
void notifySimActivationStateChangedForPhoneId(in int phoneId, in int subId, void notifySimActivationStateChangedForPhoneId(in int phoneId, in int subId,

View File

@@ -34,7 +34,6 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.net.LinkProperties;
import android.os.Binder; import android.os.Binder;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
@@ -46,8 +45,6 @@ import android.os.RemoteException;
import android.os.UserHandle; import android.os.UserHandle;
import android.provider.DeviceConfig; import android.provider.DeviceConfig;
import android.telephony.Annotation; import android.telephony.Annotation;
import android.telephony.Annotation.ApnType;
import android.telephony.Annotation.DataFailureCause;
import android.telephony.Annotation.RadioPowerState; import android.telephony.Annotation.RadioPowerState;
import android.telephony.Annotation.SrvccState; import android.telephony.Annotation.SrvccState;
import android.telephony.BarringInfo; import android.telephony.BarringInfo;
@@ -80,7 +77,9 @@ import android.telephony.TelephonyManager;
import android.telephony.data.ApnSetting; import android.telephony.data.ApnSetting;
import android.telephony.emergency.EmergencyNumber; import android.telephony.emergency.EmergencyNumber;
import android.telephony.ims.ImsReasonInfo; import android.telephony.ims.ImsReasonInfo;
import android.util.ArrayMap;
import android.util.LocalLog; import android.util.LocalLog;
import android.util.Pair;
import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IBatteryStats; import com.android.internal.app.IBatteryStats;
@@ -303,13 +302,18 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
@RadioPowerState @RadioPowerState
private int mRadioPowerState = TelephonyManager.RADIO_POWER_UNAVAILABLE; private int mRadioPowerState = TelephonyManager.RADIO_POWER_UNAVAILABLE;
private final LocalLog mLocalLog = new LocalLog(100); private final LocalLog mLocalLog = new LocalLog(200);
private final LocalLog mListenLog = new LocalLog(100); private final LocalLog mListenLog = new LocalLog(00);
// Per-phoneMap of APN Type to DataConnectionState /**
private List<Map<Integer, PreciseDataConnectionState>> mPreciseDataConnectionStates = * Per-phone map of precise data connection state. The key of the map is the pair of transport
new ArrayList<Map<Integer, PreciseDataConnectionState>>(); * type and APN setting. This is the cache to prevent redundant callbacks to the listeners.
* A precise data connection with state {@link TelephonyManager#DATA_DISCONNECTED} removes
* its entry from the map.
*/
private List<Map<Pair<Integer, ApnSetting>, PreciseDataConnectionState>>
mPreciseDataConnectionStates;
static final int ENFORCE_COARSE_LOCATION_PERMISSION_MASK = static final int ENFORCE_COARSE_LOCATION_PERMISSION_MASK =
PhoneStateListener.LISTEN_REGISTRATION_FAILURE PhoneStateListener.LISTEN_REGISTRATION_FAILURE
@@ -522,7 +526,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
mRingingCallState[i] = PreciseCallState.PRECISE_CALL_STATE_IDLE; mRingingCallState[i] = PreciseCallState.PRECISE_CALL_STATE_IDLE;
mForegroundCallState[i] = PreciseCallState.PRECISE_CALL_STATE_IDLE; mForegroundCallState[i] = PreciseCallState.PRECISE_CALL_STATE_IDLE;
mBackgroundCallState[i] = PreciseCallState.PRECISE_CALL_STATE_IDLE; mBackgroundCallState[i] = PreciseCallState.PRECISE_CALL_STATE_IDLE;
mPreciseDataConnectionStates.add(new HashMap<Integer, PreciseDataConnectionState>()); mPreciseDataConnectionStates.add(new ArrayMap<>());
mBarringInfo.add(i, new BarringInfo()); mBarringInfo.add(i, new BarringInfo());
mTelephonyDisplayInfos[i] = null; mTelephonyDisplayInfos[i] = null;
} }
@@ -611,7 +615,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
mRingingCallState[i] = PreciseCallState.PRECISE_CALL_STATE_IDLE; mRingingCallState[i] = PreciseCallState.PRECISE_CALL_STATE_IDLE;
mForegroundCallState[i] = PreciseCallState.PRECISE_CALL_STATE_IDLE; mForegroundCallState[i] = PreciseCallState.PRECISE_CALL_STATE_IDLE;
mBackgroundCallState[i] = PreciseCallState.PRECISE_CALL_STATE_IDLE; mBackgroundCallState[i] = PreciseCallState.PRECISE_CALL_STATE_IDLE;
mPreciseDataConnectionStates.add(new HashMap<Integer, PreciseDataConnectionState>()); mPreciseDataConnectionStates.add(new ArrayMap<>());
mBarringInfo.add(i, new BarringInfo()); mBarringInfo.add(i, new BarringInfo());
mTelephonyDisplayInfos[i] = null; mTelephonyDisplayInfos[i] = null;
} }
@@ -1688,38 +1692,25 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
* *
* @param phoneId the phoneId carrying the data connection * @param phoneId the phoneId carrying the data connection
* @param subId the subscriptionId for the data connection * @param subId the subscriptionId for the data connection
* @param apnType the apn type bitmask, defined with {@code ApnSetting#TYPE_*} flags.
* @param preciseState a PreciseDataConnectionState that has info about the data connection * @param preciseState a PreciseDataConnectionState that has info about the data connection
*/ */
@Override @Override
public void notifyDataConnectionForSubscriber( public void notifyDataConnectionForSubscriber(int phoneId, int subId,
int phoneId, int subId, @ApnType int apnType, PreciseDataConnectionState preciseState) { @NonNull PreciseDataConnectionState preciseState) {
if (!checkNotifyPermission("notifyDataConnection()" )) { if (!checkNotifyPermission("notifyDataConnection()" )) {
return; return;
} }
String apn = ""; ApnSetting apnSetting = preciseState.getApnSetting();
int state = TelephonyManager.DATA_UNKNOWN;
int networkType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
LinkProperties linkProps = null;
if (preciseState != null) { int apnTypes = apnSetting.getApnTypeBitmask();
apn = preciseState.getDataConnectionApn(); int state = preciseState.getState();
state = preciseState.getState(); int networkType = preciseState.getNetworkType();
networkType = preciseState.getNetworkType();
linkProps = preciseState.getLinkProperties();
}
if (VDBG) {
log("notifyDataConnectionForSubscriber: subId=" + subId
+ " state=" + state + "' apn='" + apn
+ "' apnType=" + apnType + " networkType=" + networkType
+ "' preciseState=" + preciseState);
}
synchronized (mRecords) { synchronized (mRecords) {
if (validatePhoneId(phoneId)) { if (validatePhoneId(phoneId)) {
// We only call the callback when the change is for default APN type. // We only call the callback when the change is for default APN type.
if ((ApnSetting.TYPE_DEFAULT & apnType) != 0 if ((ApnSetting.TYPE_DEFAULT & apnTypes) != 0
&& (mDataConnectionState[phoneId] != state && (mDataConnectionState[phoneId] != state
|| mDataConnectionNetworkType[phoneId] != networkType)) { || mDataConnectionNetworkType[phoneId] != networkType)) {
String str = "onDataConnectionStateChanged(" String str = "onDataConnectionStateChanged("
@@ -1748,19 +1739,11 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
mDataConnectionNetworkType[phoneId] = networkType; mDataConnectionNetworkType[phoneId] = networkType;
} }
boolean needsNotify = false; Pair<Integer, ApnSetting> key = Pair.create(preciseState.getTransportType(),
// State has been cleared for this APN Type preciseState.getApnSetting());
if (preciseState == null) { PreciseDataConnectionState oldState = mPreciseDataConnectionStates.get(phoneId)
// We try clear the state and check if the state was previously not cleared .remove(key);
needsNotify = mPreciseDataConnectionStates.get(phoneId).remove(apnType) != null; if (!Objects.equals(oldState, preciseState)) {
} else {
// We need to check to see if the state actually changed
PreciseDataConnectionState oldPreciseState =
mPreciseDataConnectionStates.get(phoneId).put(apnType, preciseState);
needsNotify = !preciseState.equals(oldPreciseState);
}
if (needsNotify) {
for (Record r : mRecords) { for (Record r : mRecords) {
if (r.matchPhoneStateListenerEvent( if (r.matchPhoneStateListenerEvent(
PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE) PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE)
@@ -1772,57 +1755,25 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
} }
} }
} }
}
}
handleRemoveListLocked(); handleRemoveListLocked();
broadcastDataConnectionStateChanged(phoneId, subId, preciseState);
String str = "notifyDataConnectionForSubscriber: phoneId=" + phoneId + " subId="
+ subId + " " + preciseState;
log(str);
mLocalLog.log(str);
} }
broadcastDataConnectionStateChanged(state, apn, apnType, subId); // If the state is disconnected, it would be the end of life cycle of a data
} // connection, so remove it from the cache.
if (preciseState.getState() != TelephonyManager.DATA_DISCONNECTED) {
/** mPreciseDataConnectionStates.get(phoneId).put(key, preciseState);
* Stub to satisfy the ITelephonyRegistry aidl interface; do not use this function.
* @see #notifyDataConnectionFailedForSubscriber
*/
public void notifyDataConnectionFailed(String apnType) {
loge("This function should not be invoked");
}
private void notifyDataConnectionFailedForSubscriber(int phoneId, int subId, int apnType) {
if (!checkNotifyPermission("notifyDataConnectionFailed()")) {
return;
}
if (VDBG) {
log("notifyDataConnectionFailedForSubscriber: subId=" + subId
+ " apnType=" + apnType);
}
synchronized (mRecords) {
if (validatePhoneId(phoneId)) {
mPreciseDataConnectionStates.get(phoneId).put(
apnType,
new PreciseDataConnectionState.Builder()
.setApnSetting(new ApnSetting.Builder()
.setApnTypeBitmask(apnType)
.build())
.build());
for (Record r : mRecords) {
if (r.matchPhoneStateListenerEvent(
PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE)
&& idMatch(r.subId, subId, phoneId)) {
try {
r.callback.onPreciseDataConnectionStateChanged(
mPreciseDataConnectionStates.get(phoneId).get(apnType));
} catch (RemoteException ex) {
mRemoveList.add(r.binder);
} }
} }
} }
} }
handleRemoveListLocked();
}
}
@Override @Override
public void notifyCellLocation(CellIdentity cellLocation) { public void notifyCellLocation(CellIdentity cellLocation) {
notifyCellLocationForSubscriber(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, cellLocation); notifyCellLocationForSubscriber(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, cellLocation);
@@ -1968,43 +1919,6 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
} }
} }
@Override
public void notifyPreciseDataConnectionFailed(int phoneId, int subId, @ApnType int apnType,
String apn, @DataFailureCause int failCause) {
if (!checkNotifyPermission("notifyPreciseDataConnectionFailed()")) {
return;
}
// precise notify invokes imprecise notify
notifyDataConnectionFailedForSubscriber(phoneId, subId, apnType);
synchronized (mRecords) {
if (validatePhoneId(phoneId)) {
mPreciseDataConnectionStates.get(phoneId).put(
apnType,
new PreciseDataConnectionState.Builder()
.setApnSetting(new ApnSetting.Builder()
.setApnTypeBitmask(apnType)
.build())
.setFailCause(failCause)
.build());
for (Record r : mRecords) {
if (r.matchPhoneStateListenerEvent(
PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE)
&& idMatch(r.subId, subId, phoneId)) {
try {
r.callback.onPreciseDataConnectionStateChanged(
mPreciseDataConnectionStates.get(phoneId).get(apnType));
} catch (RemoteException ex) {
mRemoveList.add(r.binder);
}
}
}
}
handleRemoveListLocked();
}
}
@Override @Override
public void notifySrvccStateChanged(int subId, @SrvccState int state) { public void notifySrvccStateChanged(int subId, @SrvccState int state) {
if (!checkNotifyPermission("notifySrvccStateChanged()")) { if (!checkNotifyPermission("notifySrvccStateChanged()")) {
@@ -2575,16 +2489,18 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
} }
} }
private void broadcastDataConnectionStateChanged(int state, String apn, private void broadcastDataConnectionStateChanged(int slotIndex, int subId,
int apnType, int subId) { @NonNull PreciseDataConnectionState pdcs) {
// Note: not reporting to the battery stats service here, because the // Note: not reporting to the battery stats service here, because the
// status bar takes care of that after taking into account all of the // status bar takes care of that after taking into account all of the
// required info. // required info.
Intent intent = new Intent(ACTION_ANY_DATA_CONNECTION_STATE_CHANGED); Intent intent = new Intent(ACTION_ANY_DATA_CONNECTION_STATE_CHANGED);
intent.putExtra(PHONE_CONSTANTS_STATE_KEY, TelephonyUtils.dataStateToString(state)); intent.putExtra(PHONE_CONSTANTS_STATE_KEY,
intent.putExtra(PHONE_CONSTANTS_DATA_APN_KEY, apn); TelephonyUtils.dataStateToString(pdcs.getState()));
intent.putExtra(PHONE_CONSTANTS_DATA_APN_KEY, pdcs.getApnSetting().getApnName());
intent.putExtra(PHONE_CONSTANTS_DATA_APN_TYPE_KEY, intent.putExtra(PHONE_CONSTANTS_DATA_APN_TYPE_KEY,
ApnSetting.getApnTypesStringFromBitmask(apnType)); ApnSetting.getApnTypesStringFromBitmask(pdcs.getApnSetting().getApnTypeBitmask()));
intent.putExtra(PHONE_CONSTANTS_SLOT_KEY, slotIndex);
intent.putExtra(PHONE_CONSTANTS_SUBSCRIPTION_KEY, subId); intent.putExtra(PHONE_CONSTANTS_SUBSCRIPTION_KEY, subId);
mContext.sendBroadcastAsUser(intent, UserHandle.ALL, Manifest.permission.READ_PHONE_STATE); mContext.sendBroadcastAsUser(intent, UserHandle.ALL, Manifest.permission.READ_PHONE_STATE);
} }
@@ -2941,7 +2857,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
/** /**
* Returns a string representation of the radio technology (network type) * Returns a string representation of the radio technology (network type)
* currently in use on the device. * currently in use on the device.
* @param subId for which network type is returned * @param type for which network type is returned
* @return the name of the radio technology * @return the name of the radio technology
* *
*/ */

View File

@@ -36,6 +36,8 @@ import android.telephony.Annotation.NetworkType;
import android.telephony.data.ApnSetting; import android.telephony.data.ApnSetting;
import android.telephony.data.DataCallResponse; import android.telephony.data.DataCallResponse;
import com.android.internal.telephony.util.TelephonyUtils;
import java.util.Objects; import java.util.Objects;
@@ -82,6 +84,7 @@ public final class PreciseDataConnectionState implements Parcelable {
linkProperties, failCause, new ApnSetting.Builder() linkProperties, failCause, new ApnSetting.Builder()
.setApnTypeBitmask(apnTypes) .setApnTypeBitmask(apnTypes)
.setApnName(apn) .setApnName(apn)
.setEntryName(apn)
.build()); .build());
} }
@@ -254,7 +257,9 @@ public final class PreciseDataConnectionState implements Parcelable {
/** /**
* Return the APN Settings for this data connection. * Return the APN Settings for this data connection.
* *
* @return the ApnSetting that was used to configure this data connection. * @return the ApnSetting that was used to configure this data connection. Note that a data
* connection cannot be established without a valid {@link ApnSetting}. The return value would
* never be {@code null} even though it has {@link Nullable} annotation.
*/ */
public @Nullable ApnSetting getApnSetting() { public @Nullable ApnSetting getApnSetting() {
return mApnSetting; return mApnSetting;
@@ -314,17 +319,14 @@ public final class PreciseDataConnectionState implements Parcelable {
public String toString() { public String toString() {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
sb.append("Data Connection state: " + mState); sb.append(" state: " + TelephonyUtils.dataStateToString(mState));
sb.append(", Transport type: " sb.append(", transport: "
+ AccessNetworkConstants.transportTypeToString(mTransportType)); + AccessNetworkConstants.transportTypeToString(mTransportType));
sb.append(", Id: " + mId); sb.append(", id: " + mId);
sb.append(", Network type: " + mNetworkType); sb.append(", network type: " + TelephonyManager.getNetworkTypeName(mNetworkType));
sb.append(", APN types: " + ApnSetting.getApnTypesStringFromBitmask( sb.append(", APN Setting: " + mApnSetting);
getDataConnectionApnTypeBitMask())); sb.append(", link properties: " + mLinkProperties);
sb.append(", APN: " + getDataConnectionApn()); sb.append(", fail cause: " + DataFailCause.toString(mFailCause));
sb.append(", Link properties: " + mLinkProperties);
sb.append(", Fail cause: " + DataFailCause.toString(mFailCause));
sb.append(", Apn Setting: " + mApnSetting);
return sb.toString(); return sb.toString();
} }