diff options
| author | 2019-12-10 10:12:23 -0800 | |
|---|---|---|
| committer | 2019-12-10 10:12:23 -0800 | |
| commit | 742e21719ac6e6e381ba99952e82b288f6ac5ed2 (patch) | |
| tree | 016066d8217075b4f107e5fdf02c4b2ef7ca3c85 | |
| parent | 021426e9dd4818c96b12b239524ba48483a2a719 (diff) | |
| parent | 87630910190dee7ad03750130e35170fe564655c (diff) | |
Merge "Integrate Signal Threshold for 5G"
am: 8763091019
Change-Id: Ib5c0ae3e01ced133ef1c5a36ad7765371f59cc72
| -rw-r--r-- | api/current.txt | 1 | ||||
| -rw-r--r-- | telephony/java/android/telephony/CarrierConfigManager.java | 43 | ||||
| -rw-r--r-- | telephony/java/android/telephony/CellSignalStrengthNr.java | 167 | ||||
| -rw-r--r-- | telephony/java/android/telephony/SignalThresholdInfo.java | 256 |
4 files changed, 449 insertions, 18 deletions
diff --git a/api/current.txt b/api/current.txt index c10b2b8ecda8..23b5bc6224f1 100644 --- a/api/current.txt +++ b/api/current.txt @@ -44266,7 +44266,6 @@ package android.telephony { field public static final String KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_RSSNR_INT = "opportunistic_network_entry_threshold_rssnr_int"; field public static final String KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_RSRP_INT = "opportunistic_network_exit_threshold_rsrp_int"; field public static final String KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_RSSNR_INT = "opportunistic_network_exit_threshold_rssnr_int"; - field public static final String KEY_PARAMETERS_USE_FOR_5G_NR_SIGNAL_BAR_INT = "parameters_use_for_5g_nr_signal_bar_int"; field public static final String KEY_PREFER_2G_BOOL = "prefer_2g_bool"; field public static final String KEY_PREVENT_CLIR_ACTIVATION_AND_DEACTIVATION_CODE_BOOL = "prevent_clir_activation_and_deactivation_code_bool"; field public static final String KEY_RADIO_RESTART_FAILURE_CAUSES_INT_ARRAY = "radio_restart_failure_causes_int_array"; diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index e2eb9a07c512..4071e957837c 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -2323,7 +2323,7 @@ public class CarrierConfigManager { * Reference: 3GPP TS 38.215 * * 4 threshold integers must be within the boundaries [-20 dB, -3 dB], and the levels are: - * "NONE: [-23, threshold1]" + * "NONE: [-20, threshold1]" * "POOR: (threshold1, threshold2]" * "MODERATE: (threshold2, threshold3]" * "GOOD: (threshold3, threshold4]" @@ -2357,15 +2357,26 @@ public class CarrierConfigManager { /** * Bit-field integer to determine whether to use SS reference signal received power (SSRSRP), * SS reference signal received quality (SSRSRQ), or/and SS signal-to-noise and interference - * ratio (SSSINR) for the number of 5G NR signal bars. If multiple measures are set bit, the - * parameter whose value is smallest is used to indicate the signal bar. + * ratio (SSSINR) for the number of 5G NR signal bars and signal criteria reporting enabling. + * + * <p> If a measure is not set, signal criteria reporting from modem will not be triggered and + * not be used for calculating signal level. If multiple measures are set bit, the parameter + * whose value is smallest is used to indicate the signal level. * * SSRSRP = 1 << 0, * SSRSRQ = 1 << 1, * SSSINR = 1 << 2, * + * The value of this key must be bitwise OR of {@link CellSignalStrengthNr#USE_SSRSRP}, + * {@link CellSignalStrengthNr#USE_SSRSRQ}, {@link CellSignalStrengthNr#USE_SSSINR}. + * + * For example, if both SSRSRP and SSSINR are used, the value of key is 5 (1 << 0 | 1 << 2). + * If the key is invalid or not configured, a default value (SSRSRP = 1 << 0) will apply. + * * Reference: 3GPP TS 38.215, * 3GPP TS 38.133 10.1.16.1 + * + * @hide */ public static final String KEY_PARAMETERS_USE_FOR_5G_NR_SIGNAL_BAR_INT = "parameters_use_for_5g_nr_signal_bar_int"; @@ -3692,6 +3703,32 @@ public class CarrierConfigManager { -95, /* SIGNAL_STRENGTH_GOOD */ -85 /* SIGNAL_STRENGTH_GREAT */ }); + sDefaults.putIntArray(KEY_5G_NR_SSRSRP_THRESHOLDS_INT_ARRAY, + // Boundaries: [-140 dB, -44 dB] + new int[] { + -125, /* SIGNAL_STRENGTH_POOR */ + -115, /* SIGNAL_STRENGTH_MODERATE */ + -105, /* SIGNAL_STRENGTH_GOOD */ + -95, /* SIGNAL_STRENGTH_GREAT */ + }); + sDefaults.putIntArray(KEY_5G_NR_SSRSRQ_THRESHOLDS_INT_ARRAY, + // Boundaries: [-20 dB, -3 dB] + new int[] { + -14, /* SIGNAL_STRENGTH_POOR */ + -12, /* SIGNAL_STRENGTH_MODERATE */ + -10, /* SIGNAL_STRENGTH_GOOD */ + -8 /* SIGNAL_STRENGTH_GREAT */ + }); + sDefaults.putIntArray(KEY_5G_NR_SSSINR_THRESHOLDS_INT_ARRAY, + // Boundaries: [-23 dB, 40 dB] + new int[] { + -8, /* SIGNAL_STRENGTH_POOR */ + 0, /* SIGNAL_STRENGTH_MODERATE */ + 8, /* SIGNAL_STRENGTH_GOOD */ + 16 /* SIGNAL_STRENGTH_GREAT */ + }); + sDefaults.putInt(KEY_PARAMETERS_USE_FOR_5G_NR_SIGNAL_BAR_INT, + CellSignalStrengthNr.USE_SSRSRP); sDefaults.putString(KEY_WCDMA_DEFAULT_SIGNAL_STRENGTH_MEASUREMENT_STRING, "rssi"); sDefaults.putBoolean(KEY_CONFIG_SHOW_ORIG_DIAL_STRING_FOR_CDMA_BOOL, false); sDefaults.putBoolean(KEY_SHOW_CALL_BLOCKING_DISABLED_NOTIFICATION_ALWAYS_BOOL, false); diff --git a/telephony/java/android/telephony/CellSignalStrengthNr.java b/telephony/java/android/telephony/CellSignalStrengthNr.java index f9b7f6dbc193..f31fafe36508 100644 --- a/telephony/java/android/telephony/CellSignalStrengthNr.java +++ b/telephony/java/android/telephony/CellSignalStrengthNr.java @@ -16,11 +16,15 @@ package android.telephony; +import android.annotation.IntDef; import android.annotation.IntRange; import android.os.Parcel; import android.os.Parcelable; import android.os.PersistableBundle; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Arrays; import java.util.Objects; /** @@ -36,13 +40,67 @@ public final class CellSignalStrengthNr extends CellSignalStrength implements Pa private static final String TAG = "CellSignalStrengthNr"; + // Lifted from Default carrier configs and max range of SSRSRP + // Boundaries: [-140 dB, -44 dB] + private int[] mSsRsrpThresholds = new int[] { + -125, /* SIGNAL_STRENGTH_POOR */ + -115, /* SIGNAL_STRENGTH_MODERATE */ + -105, /* SIGNAL_STRENGTH_GOOD */ + -95, /* SIGNAL_STRENGTH_GREAT */ + }; + + // Lifted from Default carrier configs and max range of SSRSRQ + // Boundaries: [-20 dB, -3 dB] + private int[] mSsRsrqThresholds = new int[] { + -14, /* SIGNAL_STRENGTH_POOR */ + -12, /* SIGNAL_STRENGTH_MODERATE */ + -10, /* SIGNAL_STRENGTH_GOOD */ + -8 /* SIGNAL_STRENGTH_GREAT */ + }; + + // Lifted from Default carrier configs and max range of SSSINR + // Boundaries: [-23 dB, 40 dB] + private int[] mSsSinrThresholds = new int[] { + -8, /* SIGNAL_STRENGTH_POOR */ + 0, /* SIGNAL_STRENGTH_MODERATE */ + 8, /* SIGNAL_STRENGTH_GOOD */ + 16 /* SIGNAL_STRENGTH_GREAT */ + }; + + /** + * Indicates SSRSRP is considered for {@link #getLevel()} and reporting from modem. + * + * @hide + */ + public static final int USE_SSRSRP = 1 << 0; + /** + * Indicates SSRSRQ is considered for {@link #getLevel()} and reporting from modem. + * + * @hide + */ + public static final int USE_SSRSRQ = 1 << 1; /** - * These threshold values are copied from LTE. - * TODO: make it configurable via CarrierConfig. + * Indicates SSSINR is considered for {@link #getLevel()} and reporting from modem. + * + * @hide */ - private static final int SIGNAL_GREAT_THRESHOLD = -95; - private static final int SIGNAL_GOOD_THRESHOLD = -105; - private static final int SIGNAL_MODERATE_THRESHOLD = -115; + public static final int USE_SSSINR = 1 << 2; + + /** + * Bit-field integer to determine whether to use SS reference signal received power (SSRSRP), + * SS reference signal received quality (SSRSRQ), or/and SS signal-to-noise and interference + * ratio (SSSINR) for the number of 5G NR signal bars. If multiple measures are set bit, the + * parameter whose value is smallest is used to indicate the signal bar. + * + * @hide + */ + @IntDef(flag = true, prefix = { "USE_" }, value = { + USE_SSRSRP, + USE_SSRSRQ, + USE_SSSINR + }) + @Retention(RetentionPolicy.SOURCE) + public @interface SignalLevelAndReportCriteriaSource {} private int mCsiRsrp; private int mCsiRsrq; @@ -52,6 +110,21 @@ public final class CellSignalStrengthNr extends CellSignalStrength implements Pa private int mSsSinr; private int mLevel; + /** + * Bit-field integer to determine whether to use SS reference signal received power (SSRSRP), + * SS reference signal received quality (SSRSRQ), or/and SS signal-to-noise and interference + * ratio (SSSINR) for the number of 5G NR signal bars. If multiple measures are set bit, the + * parameter whose value is smallest is used to indicate the signal bar. + * + * SSRSRP = 1 << 0, + * SSRSRQ = 1 << 1, + * SSSINR = 1 << 2, + * + * For example, if both SSRSRP and SSSINR are used, the value of key is 5 (1 << 0 | 1 << 2). + * If the key is invalid or not configured, a default value (SSRSRP = 1 << 0) will apply. + */ + private int mParametersUseForLevel; + /** @hide */ public CellSignalStrengthNr() { setDefaultValues(); @@ -182,6 +255,7 @@ public final class CellSignalStrengthNr extends CellSignalStrength implements Pa mSsRsrq = CellInfo.UNAVAILABLE; mSsSinr = CellInfo.UNAVAILABLE; mLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN; + mParametersUseForLevel = USE_SSRSRP; } /** {@inheritDoc} */ @@ -191,20 +265,83 @@ public final class CellSignalStrengthNr extends CellSignalStrength implements Pa return mLevel; } + /** + * Checks if the given parameter type is considered to use for {@link #getLevel()}. + * + * Note: if multiple parameter types are considered, the smaller level for one of the + * parameters would be returned by {@link #getLevel()} + * + * @param parameterType bitwise OR of {@link #USE_SSRSRP}, {@link #USE_SSRSRQ}, + * {@link #USE_SSSINR} + * @return {@code true} if the level is calculated based on the given parameter type; + * {@code false} otherwise. + * + */ + private boolean isLevelForParameter(@SignalLevelAndReportCriteriaSource int parameterType) { + return (parameterType & mParametersUseForLevel) == parameterType; + } + /** @hide */ @Override public void updateLevel(PersistableBundle cc, ServiceState ss) { - if (mSsRsrp == CellInfo.UNAVAILABLE) { - mLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN; - } else if (mSsRsrp >= SIGNAL_GREAT_THRESHOLD) { - mLevel = SIGNAL_STRENGTH_GREAT; - } else if (mSsRsrp >= SIGNAL_GOOD_THRESHOLD) { - mLevel = SIGNAL_STRENGTH_GOOD; - } else if (mSsRsrp >= SIGNAL_MODERATE_THRESHOLD) { - mLevel = SIGNAL_STRENGTH_MODERATE; + if (cc == null) { + mParametersUseForLevel = USE_SSRSRP; + } else { + mParametersUseForLevel = cc.getInt( + CarrierConfigManager.KEY_PARAMETERS_USE_FOR_5G_NR_SIGNAL_BAR_INT, USE_SSRSRP); + Rlog.i(TAG, "Using SSRSRP for Level."); + mSsRsrpThresholds = cc.getIntArray( + CarrierConfigManager.KEY_5G_NR_SSRSRP_THRESHOLDS_INT_ARRAY); + Rlog.i(TAG, "Applying 5G NR SSRSRP Thresholds: " + Arrays.toString(mSsRsrpThresholds)); + mSsRsrqThresholds = cc.getIntArray( + CarrierConfigManager.KEY_5G_NR_SSRSRQ_THRESHOLDS_INT_ARRAY); + Rlog.i(TAG, "Applying 5G NR SSRSRQ Thresholds: " + Arrays.toString(mSsRsrqThresholds)); + mSsSinrThresholds = cc.getIntArray( + CarrierConfigManager.KEY_5G_NR_SSSINR_THRESHOLDS_INT_ARRAY); + Rlog.i(TAG, "Applying 5G NR SSSINR Thresholds: " + Arrays.toString(mSsSinrThresholds)); + } + int ssRsrpLevel = SignalStrength.INVALID; + int ssRsrqLevel = SignalStrength.INVALID; + int ssSinrLevel = SignalStrength.INVALID; + if (isLevelForParameter(USE_SSRSRP)) { + ssRsrpLevel = updateLevelWithMeasure(mSsRsrp, mSsRsrpThresholds); + Rlog.i(TAG, "Updated 5G NR SSRSRP Level: " + ssRsrpLevel); + } + if (isLevelForParameter(USE_SSRSRQ)) { + ssRsrqLevel = updateLevelWithMeasure(mSsRsrq, mSsRsrqThresholds); + Rlog.i(TAG, "Updated 5G NR SSRSRQ Level: " + ssRsrqLevel); + } + if (isLevelForParameter(USE_SSSINR)) { + ssSinrLevel = updateLevelWithMeasure(mSsSinr, mSsSinrThresholds); + Rlog.i(TAG, "Updated 5G NR SSSINR Level: " + ssSinrLevel); + } + // Apply the smaller value among three levels of three measures. + mLevel = Math.min(Math.min(ssRsrpLevel, ssRsrqLevel), ssSinrLevel); + } + + /** + * Update level with corresponding measure and thresholds. + * + * @param measure corresponding signal measure + * @param thresholds corresponding signal thresholds + * @return level of the signal strength + */ + private int updateLevelWithMeasure(int measure, int[] thresholds) { + int level; + if (measure == CellInfo.UNAVAILABLE) { + level = SIGNAL_STRENGTH_NONE_OR_UNKNOWN; + } else if (measure > thresholds[3]) { + level = SIGNAL_STRENGTH_GREAT; + } else if (measure > thresholds[2]) { + level = SIGNAL_STRENGTH_GOOD; + } else if (measure > thresholds[1]) { + level = SIGNAL_STRENGTH_MODERATE; + } else if (measure > thresholds[0]) { + level = SIGNAL_STRENGTH_POOR; } else { - mLevel = SIGNAL_STRENGTH_POOR; + level = SIGNAL_STRENGTH_NONE_OR_UNKNOWN; } + return level; } /** @@ -247,6 +384,7 @@ public final class CellSignalStrengthNr extends CellSignalStrength implements Pa mSsRsrq = s.mSsRsrq; mSsSinr = s.mSsSinr; mLevel = s.mLevel; + mParametersUseForLevel = s.mParametersUseForLevel; } /** @hide */ @@ -290,6 +428,7 @@ public final class CellSignalStrengthNr extends CellSignalStrength implements Pa .append(" ssRsrq = " + mSsRsrq) .append(" ssSinr = " + mSsSinr) .append(" level = " + mLevel) + .append(" parametersUseForLevel = " + mParametersUseForLevel) .append(" }") .toString(); } diff --git a/telephony/java/android/telephony/SignalThresholdInfo.java b/telephony/java/android/telephony/SignalThresholdInfo.java new file mode 100644 index 000000000000..f6f6d75c37c6 --- /dev/null +++ b/telephony/java/android/telephony/SignalThresholdInfo.java @@ -0,0 +1,256 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Arrays; +import java.util.Objects; + +/** + * Defines the threshold value of the signal strength. + * @hide + */ +public class SignalThresholdInfo implements Parcelable { + /** + * Received Signal Strength Indication. + * Range: -113 dBm and -51 dBm + * Used RAN: GERAN, CDMA2000 + * Reference: 3GPP TS 27.007 section 8.5. + */ + public static final int SIGNAL_RSSI = 1; + + /** + * Received Signal Code Power. + * Range: -120 dBm to -25 dBm; + * Used RAN: UTRAN + * Reference: 3GPP TS 25.123, section 9.1.1.1 + */ + public static final int SIGNAL_RSCP = 2; + + /** + * Reference Signal Received Power. + * Range: -140 dBm to -44 dBm; + * Used RAN: EUTRAN + * Reference: 3GPP TS 36.133 9.1.4 + */ + public static final int SIGNAL_RSRP = 3; + + /** + * Reference Signal Received Quality + * Range: -20 dB to -3 dB; + * Used RAN: EUTRAN + * Reference: 3GPP TS 36.133 9.1.7 + */ + public static final int SIGNAL_RSRQ = 4; + + /** + * Reference Signal Signal to Noise Ratio + * Range: -20 dB to 30 dB; + * Used RAN: EUTRAN + */ + public static final int SIGNAL_RSSNR = 5; + + /** + * 5G SS reference signal received power. + * Range: -140 dBm to -44 dBm. + * Used RAN: NGRAN + * Reference: 3GPP TS 38.215. + */ + public static final int SIGNAL_SSRSRP = 6; + + /** + * 5G SS reference signal received quality. + * Range: -20 dB to -3 dB. + * Used RAN: NGRAN + * Reference: 3GPP TS 38.215. + */ + public static final int SIGNAL_SSRSRQ = 7; + + /** + * 5G SS signal-to-noise and interference ratio. + * Range: -23 dB to 40 dB + * Used RAN: NGRAN + * Reference: 3GPP TS 38.215 section 5.1.*, 3GPP TS 38.133 section 10.1.16.1. + */ + public static final int SIGNAL_SSSINR = 8; + + /** @hide */ + @IntDef(prefix = { "SIGNAL_" }, value = { + SIGNAL_RSSI, + SIGNAL_RSCP, + SIGNAL_RSRP, + SIGNAL_RSRQ, + SIGNAL_RSSNR, + SIGNAL_SSRSRP, + SIGNAL_SSRSRQ, + SIGNAL_SSSINR + }) + @Retention(RetentionPolicy.SOURCE) + public @interface SignalMeasurementType {} + + @SignalMeasurementType + private int mSignalMeasurement; + + /** + * A hysteresis time in milliseconds to prevent flapping. + * A value of 0 disables hysteresis + */ + private int mHysteresisMs; + + /** + * An interval in dB defining the required magnitude change between reports. + * hysteresisDb must be smaller than the smallest threshold delta. + * An interval value of 0 disables hysteresis. + */ + private int mHysteresisDb; + + /** + * List of threshold values. + * Range and unit must reference specific SignalMeasurementType + * The threshold values for which to apply criteria. + * A vector size of 0 disables the use of thresholds for reporting. + */ + private int[] mThresholds = null; + + /** + * {@code true} means modem must trigger the report based on the criteria; + * {@code false} means modem must not trigger the report based on the criteria. + */ + private boolean mIsEnabled = true; + + /** + * Indicates the hysteresisMs is disabled. + */ + public static final int HYSTERESIS_MS_DISABLED = 0; + + /** + * Indicates the hysteresisDb is disabled. + */ + public static final int HYSTERESIS_DB_DISABLED = 0; + + /** + * Constructor + * + * @param signalMeasurement Signal Measurement Type + * @param hysteresisMs hysteresisMs + * @param hysteresisDb hysteresisDb + * @param thresholds threshold value + * @param isEnabled isEnabled + */ + public SignalThresholdInfo(@SignalMeasurementType int signalMeasurement, + int hysteresisMs, int hysteresisDb, @NonNull int [] thresholds, boolean isEnabled) { + mSignalMeasurement = signalMeasurement; + mHysteresisMs = hysteresisMs < 0 ? HYSTERESIS_MS_DISABLED : hysteresisMs; + mHysteresisDb = hysteresisDb < 0 ? HYSTERESIS_DB_DISABLED : hysteresisDb; + mThresholds = thresholds == null ? null : thresholds.clone(); + mIsEnabled = isEnabled; + } + + public @SignalMeasurementType int getSignalMeasurement() { + return mSignalMeasurement; + } + + public int getHysteresisMs() { + return mHysteresisMs; + } + + public int getHysteresisDb() { + return mHysteresisDb; + } + + public boolean isEnabled() { + return mIsEnabled; + } + + public int[] getThresholds() { + return mThresholds == null ? null : mThresholds.clone(); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeInt(mSignalMeasurement); + out.writeInt(mHysteresisMs); + out.writeInt(mHysteresisDb); + out.writeIntArray(mThresholds); + out.writeBoolean(mIsEnabled); + } + + private SignalThresholdInfo(Parcel in) { + mSignalMeasurement = in.readInt(); + mHysteresisMs = in.readInt(); + mHysteresisDb = in.readInt(); + mThresholds = in.createIntArray(); + mIsEnabled = in.readBoolean(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + + if (!(o instanceof SignalThresholdInfo)) { + return false; + } + + SignalThresholdInfo other = (SignalThresholdInfo) o; + return mSignalMeasurement == other.mSignalMeasurement + && mHysteresisMs == other.mHysteresisMs + && mHysteresisDb == other.mHysteresisDb + && Arrays.equals(mThresholds, other.mThresholds) + && mIsEnabled == other.mIsEnabled; + } + + @Override + public int hashCode() { + return Objects.hash( + mSignalMeasurement, mHysteresisMs, mHysteresisDb, mThresholds, mIsEnabled); + } + + public static final @NonNull Parcelable.Creator<SignalThresholdInfo> CREATOR = + new Parcelable.Creator<SignalThresholdInfo>() { + @Override + public SignalThresholdInfo createFromParcel(Parcel in) { + return new SignalThresholdInfo(in); + } + + @Override + public SignalThresholdInfo[] newArray(int size) { + return new SignalThresholdInfo[size]; + } + }; + + @Override + public String toString() { + return new StringBuilder("SignalThresholdInfo{") + .append("mSignalMeasurement=").append(mSignalMeasurement) + .append("mHysteresisMs=").append(mSignalMeasurement) + .append("mHysteresisDb=").append(mHysteresisDb) + .append("mThresholds=").append(Arrays.toString(mThresholds)) + .append("mIsEnabled=").append(mIsEnabled) + .append("}").toString(); + } +} |