diff options
Diffstat (limited to 'telephony/java/android')
260 files changed, 27020 insertions, 3657 deletions
diff --git a/telephony/java/android/service/euicc/EuiccProfileInfo.java b/telephony/java/android/service/euicc/EuiccProfileInfo.java index 8450a9018634..8ec500b4d49d 100644 --- a/telephony/java/android/service/euicc/EuiccProfileInfo.java +++ b/telephony/java/android/service/euicc/EuiccProfileInfo.java @@ -20,6 +20,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; +import android.os.Build; import android.os.Parcel; import android.os.Parcelable; import android.service.carrier.CarrierIdentifier; @@ -29,6 +30,7 @@ import android.text.TextUtils; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.Objects; @@ -145,7 +147,7 @@ public final class EuiccProfileInfo implements Parcelable { * @deprecated - Do not use. */ @Deprecated - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public EuiccProfileInfo(String iccid, @Nullable UiccAccessRule[] accessRules, @Nullable String nickname) { if (!TextUtils.isDigitsOnly(iccid)) { @@ -231,7 +233,9 @@ public final class EuiccProfileInfo implements Parcelable { mState = baseProfile.mState; mCarrierIdentifier = baseProfile.mCarrierIdentifier; mPolicyRules = baseProfile.mPolicyRules; - mAccessRules = Arrays.asList(baseProfile.mAccessRules); + mAccessRules = baseProfile.mAccessRules == null + ? Collections.emptyList() + : Arrays.asList(baseProfile.mAccessRules); } /** Builds the profile instance. */ diff --git a/telephony/java/android/service/euicc/EuiccService.java b/telephony/java/android/service/euicc/EuiccService.java index 93155865c166..fcbb008c79b3 100644 --- a/telephony/java/android/service/euicc/EuiccService.java +++ b/telephony/java/android/service/euicc/EuiccService.java @@ -328,8 +328,7 @@ public abstract class EuiccService extends Service { * or when an number is bigger than 15 */ public int encodeSmdxSubjectAndReasonCode(@Nullable String subjectCode, - @Nullable String reasonCode) - throws NumberFormatException, IllegalArgumentException, UnsupportedOperationException { + @Nullable String reasonCode) { final int maxSupportedSection = 3; final int maxSupportedDigit = 15; final int bitsPerSection = 4; diff --git a/telephony/java/android/service/euicc/GetDefaultDownloadableSubscriptionListResult.java b/telephony/java/android/service/euicc/GetDefaultDownloadableSubscriptionListResult.java index 2382f657c9ee..58e1d08a2009 100644 --- a/telephony/java/android/service/euicc/GetDefaultDownloadableSubscriptionListResult.java +++ b/telephony/java/android/service/euicc/GetDefaultDownloadableSubscriptionListResult.java @@ -18,6 +18,7 @@ package android.service.euicc; import android.annotation.Nullable; import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; +import android.os.Build; import android.os.Parcel; import android.os.Parcelable; import android.telephony.euicc.DownloadableSubscription; @@ -50,7 +51,7 @@ public final class GetDefaultDownloadableSubscriptionListResult implements Parce * @deprecated - Do no use. Use getResult() instead. */ @Deprecated - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public final int result; @Nullable diff --git a/telephony/java/android/service/euicc/GetDownloadableSubscriptionMetadataResult.java b/telephony/java/android/service/euicc/GetDownloadableSubscriptionMetadataResult.java index d0fb51180c1d..6417c0dfcf05 100644 --- a/telephony/java/android/service/euicc/GetDownloadableSubscriptionMetadataResult.java +++ b/telephony/java/android/service/euicc/GetDownloadableSubscriptionMetadataResult.java @@ -18,6 +18,7 @@ package android.service.euicc; import android.annotation.Nullable; import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; +import android.os.Build; import android.os.Parcel; import android.os.Parcelable; import android.telephony.euicc.DownloadableSubscription; @@ -47,7 +48,7 @@ public final class GetDownloadableSubscriptionMetadataResult implements Parcelab * @deprecated - Do no use. Use getResult() instead. */ @Deprecated - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public final int result; @Nullable diff --git a/telephony/java/android/service/euicc/IDeleteSubscriptionCallback.aidl b/telephony/java/android/service/euicc/IDeleteSubscriptionCallback.aidl index aff8f1b7b346..a55f019bec23 100644 --- a/telephony/java/android/service/euicc/IDeleteSubscriptionCallback.aidl +++ b/telephony/java/android/service/euicc/IDeleteSubscriptionCallback.aidl @@ -18,6 +18,6 @@ package android.service.euicc; /** @hide */ oneway interface IDeleteSubscriptionCallback { - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) void onComplete(int result); }
\ No newline at end of file diff --git a/telephony/java/android/service/euicc/IEraseSubscriptionsCallback.aidl b/telephony/java/android/service/euicc/IEraseSubscriptionsCallback.aidl index 34b53cc71dfb..da26045be3ac 100644 --- a/telephony/java/android/service/euicc/IEraseSubscriptionsCallback.aidl +++ b/telephony/java/android/service/euicc/IEraseSubscriptionsCallback.aidl @@ -18,6 +18,6 @@ package android.service.euicc; /** @hide */ oneway interface IEraseSubscriptionsCallback { - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) void onComplete(int result); }
\ No newline at end of file diff --git a/telephony/java/android/service/euicc/IGetDefaultDownloadableSubscriptionListCallback.aidl b/telephony/java/android/service/euicc/IGetDefaultDownloadableSubscriptionListCallback.aidl index ad69ef132428..db73f8e0f0f4 100644 --- a/telephony/java/android/service/euicc/IGetDefaultDownloadableSubscriptionListCallback.aidl +++ b/telephony/java/android/service/euicc/IGetDefaultDownloadableSubscriptionListCallback.aidl @@ -20,6 +20,6 @@ import android.service.euicc.GetDefaultDownloadableSubscriptionListResult; /** @hide */ oneway interface IGetDefaultDownloadableSubscriptionListCallback { - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) void onComplete(in GetDefaultDownloadableSubscriptionListResult result); }
\ No newline at end of file diff --git a/telephony/java/android/service/euicc/IGetDownloadableSubscriptionMetadataCallback.aidl b/telephony/java/android/service/euicc/IGetDownloadableSubscriptionMetadataCallback.aidl index 01f187ed11e2..102ee3096c34 100644 --- a/telephony/java/android/service/euicc/IGetDownloadableSubscriptionMetadataCallback.aidl +++ b/telephony/java/android/service/euicc/IGetDownloadableSubscriptionMetadataCallback.aidl @@ -20,6 +20,6 @@ import android.service.euicc.GetDownloadableSubscriptionMetadataResult; /** @hide */ oneway interface IGetDownloadableSubscriptionMetadataCallback { - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) void onComplete(in GetDownloadableSubscriptionMetadataResult result); }
\ No newline at end of file diff --git a/telephony/java/android/service/euicc/IGetEidCallback.aidl b/telephony/java/android/service/euicc/IGetEidCallback.aidl index e405a981c85a..c47cf13f75c6 100644 --- a/telephony/java/android/service/euicc/IGetEidCallback.aidl +++ b/telephony/java/android/service/euicc/IGetEidCallback.aidl @@ -18,6 +18,6 @@ package android.service.euicc; /** @hide */ oneway interface IGetEidCallback { - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) void onSuccess(String eid); }
\ No newline at end of file diff --git a/telephony/java/android/service/euicc/IGetEuiccInfoCallback.aidl b/telephony/java/android/service/euicc/IGetEuiccInfoCallback.aidl index c0611825ff0f..291c058dfd2e 100644 --- a/telephony/java/android/service/euicc/IGetEuiccInfoCallback.aidl +++ b/telephony/java/android/service/euicc/IGetEuiccInfoCallback.aidl @@ -20,6 +20,6 @@ import android.telephony.euicc.EuiccInfo; /** @hide */ oneway interface IGetEuiccInfoCallback { - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) void onSuccess(in EuiccInfo euiccInfo); }
\ No newline at end of file diff --git a/telephony/java/android/service/euicc/IGetEuiccProfileInfoListCallback.aidl b/telephony/java/android/service/euicc/IGetEuiccProfileInfoListCallback.aidl index 0485f7be29d3..eadddb1193e8 100644 --- a/telephony/java/android/service/euicc/IGetEuiccProfileInfoListCallback.aidl +++ b/telephony/java/android/service/euicc/IGetEuiccProfileInfoListCallback.aidl @@ -20,6 +20,6 @@ import android.service.euicc.GetEuiccProfileInfoListResult; /** @hide */ oneway interface IGetEuiccProfileInfoListCallback { - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) void onComplete(in GetEuiccProfileInfoListResult result); }
\ No newline at end of file diff --git a/telephony/java/android/service/euicc/IRetainSubscriptionsForFactoryResetCallback.aidl b/telephony/java/android/service/euicc/IRetainSubscriptionsForFactoryResetCallback.aidl index 340401fe89cb..ade1ccdb6b06 100644 --- a/telephony/java/android/service/euicc/IRetainSubscriptionsForFactoryResetCallback.aidl +++ b/telephony/java/android/service/euicc/IRetainSubscriptionsForFactoryResetCallback.aidl @@ -18,6 +18,6 @@ package android.service.euicc; /** @hide */ oneway interface IRetainSubscriptionsForFactoryResetCallback { - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) void onComplete(int result); }
\ No newline at end of file diff --git a/telephony/java/android/service/euicc/ISwitchToSubscriptionCallback.aidl b/telephony/java/android/service/euicc/ISwitchToSubscriptionCallback.aidl index b8f984d1c28b..1b4b658f6211 100644 --- a/telephony/java/android/service/euicc/ISwitchToSubscriptionCallback.aidl +++ b/telephony/java/android/service/euicc/ISwitchToSubscriptionCallback.aidl @@ -18,6 +18,6 @@ package android.service.euicc; /** @hide */ oneway interface ISwitchToSubscriptionCallback { - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) void onComplete(int result); }
\ No newline at end of file diff --git a/telephony/java/android/service/euicc/IUpdateSubscriptionNicknameCallback.aidl b/telephony/java/android/service/euicc/IUpdateSubscriptionNicknameCallback.aidl index 0aa66978bb91..fda73497674c 100644 --- a/telephony/java/android/service/euicc/IUpdateSubscriptionNicknameCallback.aidl +++ b/telephony/java/android/service/euicc/IUpdateSubscriptionNicknameCallback.aidl @@ -18,6 +18,6 @@ package android.service.euicc; /** @hide */ oneway interface IUpdateSubscriptionNicknameCallback { - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) void onComplete(int result); }
\ No newline at end of file diff --git a/telephony/java/android/service/euicc/OWNERS b/telephony/java/android/service/euicc/OWNERS new file mode 100644 index 000000000000..6aa399d9ebfb --- /dev/null +++ b/telephony/java/android/service/euicc/OWNERS @@ -0,0 +1,5 @@ +# Bug component: 20868 + +rgreenwalt@google.com +tgunn@google.com +amitmahajan@google.com diff --git a/telephony/java/android/service/sms/OWNERS b/telephony/java/android/service/sms/OWNERS new file mode 100644 index 000000000000..6aa399d9ebfb --- /dev/null +++ b/telephony/java/android/service/sms/OWNERS @@ -0,0 +1,5 @@ +# Bug component: 20868 + +rgreenwalt@google.com +tgunn@google.com +amitmahajan@google.com diff --git a/telephony/java/android/telephony/AccessNetworkConstants.java b/telephony/java/android/telephony/AccessNetworkConstants.java index d01297147fdb..1d7a4761dec6 100644 --- a/telephony/java/android/telephony/AccessNetworkConstants.java +++ b/telephony/java/android/telephony/AccessNetworkConstants.java @@ -18,10 +18,7 @@ package android.telephony; import android.annotation.IntDef; import android.annotation.SystemApi; -import android.hardware.radio.V1_1.GeranBands; import android.hardware.radio.V1_5.AccessNetwork; -import android.hardware.radio.V1_5.EutranBands; -import android.hardware.radio.V1_5.UtranBands; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -65,6 +62,7 @@ public final class AccessNetworkConstants { switch (transportType) { case TRANSPORT_TYPE_WWAN: return "WWAN"; case TRANSPORT_TYPE_WLAN: return "WLAN"; + case TRANSPORT_TYPE_INVALID: return "INVALID"; default: return Integer.toString(transportType); } } @@ -117,52 +115,120 @@ public final class AccessNetworkConstants { * http://www.etsi.org/deliver/etsi_ts/145000_145099/145005/14.00.00_60/ts_145005v140000p.pdf */ public static final class GeranBand { - public static final int BAND_T380 = GeranBands.BAND_T380; - public static final int BAND_T410 = GeranBands.BAND_T410; - public static final int BAND_450 = GeranBands.BAND_450; - public static final int BAND_480 = GeranBands.BAND_480; - public static final int BAND_710 = GeranBands.BAND_710; - public static final int BAND_750 = GeranBands.BAND_750; - public static final int BAND_T810 = GeranBands.BAND_T810; - public static final int BAND_850 = GeranBands.BAND_850; - public static final int BAND_P900 = GeranBands.BAND_P900; - public static final int BAND_E900 = GeranBands.BAND_E900; - public static final int BAND_R900 = GeranBands.BAND_R900; - public static final int BAND_DCS1800 = GeranBands.BAND_DCS1800; - public static final int BAND_PCS1900 = GeranBands.BAND_PCS1900; - public static final int BAND_ER900 = GeranBands.BAND_ER900; + public static final int BAND_T380 = android.hardware.radio.V1_1.GeranBands.BAND_T380; + public static final int BAND_T410 = android.hardware.radio.V1_1.GeranBands.BAND_T410; + public static final int BAND_450 = android.hardware.radio.V1_1.GeranBands.BAND_450; + public static final int BAND_480 = android.hardware.radio.V1_1.GeranBands.BAND_480; + public static final int BAND_710 = android.hardware.radio.V1_1.GeranBands.BAND_710; + public static final int BAND_750 = android.hardware.radio.V1_1.GeranBands.BAND_750; + public static final int BAND_T810 = android.hardware.radio.V1_1.GeranBands.BAND_T810; + public static final int BAND_850 = android.hardware.radio.V1_1.GeranBands.BAND_850; + public static final int BAND_P900 = android.hardware.radio.V1_1.GeranBands.BAND_P900; + public static final int BAND_E900 = android.hardware.radio.V1_1.GeranBands.BAND_E900; + public static final int BAND_R900 = android.hardware.radio.V1_1.GeranBands.BAND_R900; + public static final int BAND_DCS1800 = android.hardware.radio.V1_1.GeranBands.BAND_DCS1800; + public static final int BAND_PCS1900 = android.hardware.radio.V1_1.GeranBands.BAND_PCS1900; + public static final int BAND_ER900 = android.hardware.radio.V1_1.GeranBands.BAND_ER900; + + /** + * GeranBand + * + * @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"BAND_"}, + value = {BAND_T380, + BAND_T410, + BAND_450, + BAND_480, + BAND_710, + BAND_750, + BAND_T810, + BAND_850, + BAND_P900, + BAND_E900, + BAND_R900, + BAND_DCS1800, + BAND_PCS1900, + BAND_ER900}) + + public @interface GeranBands {} /** @hide */ private GeranBand() {} } /** + * 3GPP TS 45.005 Table 2-1 Dynamically mapped ARFCN. + * 3GPP TS 45.005 Table 2-2 Fixed designation of ARFCN. + * @hide + */ + enum GeranBandArfcnFrequency { + + // Dynamically mapped ARFCN +// GERAN_ARFCN_FREQUENCY_BAND_T380(GeranBand.BAND_T380, 380.2, 0), +// GERAN_ARFCN_FREQUENCY_BAND_T410(GeranBand.BAND_T410, 410.2, 0), +// GERAN_ARFCN_FREQUENCY_BAND_710(GeranBand.BAND_710, 698, 0), +// GERAN_ARFCN_FREQUENCY_BAND_750(GeranBand.BAND_750, 747, 438, 30), +// GERAN_ARFCN_FREQUENCY_BAND_T810(GeranBand.BAND_T810, 806, 350), + // Fixed designation of ARFCN + GERAN_ARFCN_FREQUENCY_BAND_450(GeranBand.BAND_450, 450600, 259, 259, 293, 10), + GERAN_ARFCN_FREQUENCY_BAND_480(GeranBand.BAND_480, 479000, 306, 306, 340, 10), + GERAN_ARFCN_FREQUENCY_BAND_850(GeranBand.BAND_850, 824200, 128, 128, 251, 45), + GERAN_ARFCN_FREQUENCY_BAND_DCS1800(GeranBand.BAND_DCS1800, 1710200, 512, 512, 885, 95), + GERAN_ARFCN_FREQUENCY_BAND_PCS1900(GeranBand.BAND_PCS1900, 1850200, 512, 512, 810, 80), + GERAN_ARFCN_FREQUENCY_BAND_E900_1(GeranBand.BAND_E900, 890000, 0, 0, 124, 45), + GERAN_ARFCN_FREQUENCY_BAND_E900_2(GeranBand.BAND_E900, 890000, 1024, 975, 1023, 45), + GERAN_ARFCN_FREQUENCY_BAND_R900_1(GeranBand.BAND_R900, 890000, 0, 0, 124, 45), + GERAN_ARFCN_FREQUENCY_BAND_R900_2(GeranBand.BAND_R900, 890000, 1024, 955, 1023, 45), + GERAN_ARFCN_FREQUENCY_BAND_P900(GeranBand.BAND_P900, 890000, 0, 1, 124, 45), + GERAN_ARFCN_FREQUENCY_BAND_ER900_1(GeranBand.BAND_ER900, 890000, 0, 0, 124, 45), + GERAN_ARFCN_FREQUENCY_BAND_ER900_2(GeranBand.BAND_ER900, 890000, 1024, 940, 1023, 1024); + + GeranBandArfcnFrequency(int band, int uplinkFrequencyFirstKhz, int arfcnOffset, + int arfcnRangeFirst, int arfcnRangeLast, int downlinkOffset) { + this.band = band; + this.uplinkFrequencyFirst = uplinkFrequencyFirstKhz; + this.arfcnOffset = arfcnOffset; + this.arfcnRangeFirst = arfcnRangeFirst; + this.arfcnRangeLast = arfcnRangeLast; + this.downlinkOffset = downlinkOffset; + } + + int band; + int uplinkFrequencyFirst; + int arfcnOffset; + int arfcnRangeFirst; + int arfcnRangeLast; + int downlinkOffset; + } + + /** * Frequency bands for UTRAN. * http://www.etsi.org/deliver/etsi_ts/125100_125199/125104/13.03.00_60/ts_125104v130p.pdf */ public static final class UtranBand { - public static final int BAND_1 = UtranBands.BAND_1; - public static final int BAND_2 = UtranBands.BAND_2; - public static final int BAND_3 = UtranBands.BAND_3; - public static final int BAND_4 = UtranBands.BAND_4; - public static final int BAND_5 = UtranBands.BAND_5; - public static final int BAND_6 = UtranBands.BAND_6; - public static final int BAND_7 = UtranBands.BAND_7; - public static final int BAND_8 = UtranBands.BAND_8; - public static final int BAND_9 = UtranBands.BAND_9; - public static final int BAND_10 = UtranBands.BAND_10; - public static final int BAND_11 = UtranBands.BAND_11; - public static final int BAND_12 = UtranBands.BAND_12; - public static final int BAND_13 = UtranBands.BAND_13; - public static final int BAND_14 = UtranBands.BAND_14; + public static final int BAND_1 = android.hardware.radio.V1_5.UtranBands.BAND_1; + public static final int BAND_2 = android.hardware.radio.V1_5.UtranBands.BAND_2; + public static final int BAND_3 = android.hardware.radio.V1_5.UtranBands.BAND_3; + public static final int BAND_4 = android.hardware.radio.V1_5.UtranBands.BAND_4; + public static final int BAND_5 = android.hardware.radio.V1_5.UtranBands.BAND_5; + public static final int BAND_6 = android.hardware.radio.V1_5.UtranBands.BAND_6; + public static final int BAND_7 = android.hardware.radio.V1_5.UtranBands.BAND_7; + public static final int BAND_8 = android.hardware.radio.V1_5.UtranBands.BAND_8; + public static final int BAND_9 = android.hardware.radio.V1_5.UtranBands.BAND_9; + public static final int BAND_10 = android.hardware.radio.V1_5.UtranBands.BAND_10; + public static final int BAND_11 = android.hardware.radio.V1_5.UtranBands.BAND_11; + public static final int BAND_12 = android.hardware.radio.V1_5.UtranBands.BAND_12; + public static final int BAND_13 = android.hardware.radio.V1_5.UtranBands.BAND_13; + public static final int BAND_14 = android.hardware.radio.V1_5.UtranBands.BAND_14; // band 15, 16, 17, 18 are reserved - public static final int BAND_19 = UtranBands.BAND_19; - public static final int BAND_20 = UtranBands.BAND_20; - public static final int BAND_21 = UtranBands.BAND_21; - public static final int BAND_22 = UtranBands.BAND_22; + public static final int BAND_19 = android.hardware.radio.V1_5.UtranBands.BAND_19; + public static final int BAND_20 = android.hardware.radio.V1_5.UtranBands.BAND_20; + public static final int BAND_21 = android.hardware.radio.V1_5.UtranBands.BAND_21; + public static final int BAND_22 = android.hardware.radio.V1_5.UtranBands.BAND_22; // band 23, 24 are reserved - public static final int BAND_25 = UtranBands.BAND_25; - public static final int BAND_26 = UtranBands.BAND_26; + public static final int BAND_25 = android.hardware.radio.V1_5.UtranBands.BAND_25; + public static final int BAND_26 = android.hardware.radio.V1_5.UtranBands.BAND_26; // Frequency bands for TD-SCDMA. Defined in 3GPP TS 25.102, Table 5.2. @@ -171,115 +237,423 @@ public final class AccessNetworkConstants { * 1900 - 1920 MHz: Uplink and downlink transmission * 2010 - 2025 MHz: Uplink and downlink transmission */ - public static final int BAND_A = UtranBands.BAND_A; + public static final int BAND_A = android.hardware.radio.V1_5.UtranBands.BAND_A; /** * Band B * 1850 - 1910 MHz: Uplink and downlink transmission * 1930 - 1990 MHz: Uplink and downlink transmission */ - public static final int BAND_B = UtranBands.BAND_B; + public static final int BAND_B = android.hardware.radio.V1_5.UtranBands.BAND_B; /** * Band C * 1910 - 1930 MHz: Uplink and downlink transmission */ - public static final int BAND_C = UtranBands.BAND_C; + public static final int BAND_C = android.hardware.radio.V1_5.UtranBands.BAND_C; /** * Band D * 2570 - 2620 MHz: Uplink and downlink transmission */ - public static final int BAND_D = UtranBands.BAND_D; + public static final int BAND_D = android.hardware.radio.V1_5.UtranBands.BAND_D; /** * Band E * 2300—2400 MHz: Uplink and downlink transmission */ - public static final int BAND_E = UtranBands.BAND_E; + public static final int BAND_E = android.hardware.radio.V1_5.UtranBands.BAND_E; /** * Band F * 1880 - 1920 MHz: Uplink and downlink transmission */ - public static final int BAND_F = UtranBands.BAND_F; + public static final int BAND_F = android.hardware.radio.V1_5.UtranBands.BAND_F; + + /** + * UtranBand + * + * @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"BAND_"}, + value = {BAND_1, + BAND_2, + BAND_3, + BAND_4, + BAND_5, + BAND_6, + BAND_7, + BAND_8, + BAND_9, + BAND_10, + BAND_11, + BAND_12, + BAND_13, + BAND_14, + BAND_19, + BAND_20, + BAND_21, + BAND_22, + BAND_25, + BAND_26, + BAND_A, + BAND_B, + BAND_C, + BAND_D, + BAND_E, + BAND_F}) + + public @interface UtranBands {} /** @hide */ private UtranBand() {} } /** + * 3GPP TS 25.101, Table 5.1 UARFCN definition (general) + * 3GPP TS 25.102, Table 5.2 UTRA Absolute Radio Frequency Channel Number 1.28 Mcps TDD Option. + * + * @hide + */ + enum UtranBandArfcnFrequency { + + UTRAN_ARFCN_FREQUENCY_BAND_1(UtranBand.BAND_1, 0, 10562, 10838, 0, 9612, 9888), + UTRAN_ARFCN_FREQUENCY_BAND_2(UtranBand.BAND_2, 0, 9662, 9938, 0, 9262, 9538), + UTRAN_ARFCN_FREQUENCY_BAND_3(UtranBand.BAND_3, 1575000, 1162, 1513, 1525000, 937, 1288), + UTRAN_ARFCN_FREQUENCY_BAND_4(UtranBand.BAND_4, 1805000, 1537, 1738, 1450000, 1312, 1513), + UTRAN_ARFCN_FREQUENCY_BAND_5(UtranBand.BAND_5, 0, 4357, 4458, 0, 4132, 4233), + UTRAN_ARFCN_FREQUENCY_BAND_6(UtranBand.BAND_6, 0, 4387, 4413, 0, 4162, 4188), + UTRAN_ARFCN_FREQUENCY_BAND_7(UtranBand.BAND_7, 2175000, 2237, 2563, 2100000, 2012, 2338), + UTRAN_ARFCN_FREQUENCY_BAND_8(UtranBand.BAND_8, 340000, 2937, 3088, 340000, 2712, 2863), + UTRAN_ARFCN_FREQUENCY_BAND_9(UtranBand.BAND_9, 0, 9327, 9837, 0, 8762, 8912), + UTRAN_ARFCN_FREQUENCY_BAND_10(UtranBand.BAND_10, 1490000, 3112, 3388, 1135000, 2887, 3163), + UTRAN_ARFCN_FREQUENCY_BAND_11(UtranBand.BAND_11, 736000, 3712, 3787, 733000, 3487, 3562), + UTRAN_ARFCN_FREQUENCY_BAND_12(UtranBand.BAND_12, -37000, 3842, 3903, -22000, 3617, 3678), + UTRAN_ARFCN_FREQUENCY_BAND_13(UtranBand.BAND_13, -55000, 4017, 4043, 21000, 3792, 3818), + UTRAN_ARFCN_FREQUENCY_BAND_14(UtranBand.BAND_14, -63000, 4117, 4143, 12000, 3892, 3918), + UTRAN_ARFCN_FREQUENCY_BAND_19(UtranBand.BAND_19, 735000, 712, 763, 770000, 312, 363), + UTRAN_ARFCN_FREQUENCY_BAND_20(UtranBand.BAND_20, -109000, 4512, 4638, -23000, 4287, 4413), + UTRAN_ARFCN_FREQUENCY_BAND_21(UtranBand.BAND_21, 1326000, 862, 912, 1358000, 462, 512), + UTRAN_ARFCN_FREQUENCY_BAND_22(UtranBand.BAND_22, 2580000, 4662, 5038, 2525000, 4437, 4813), + UTRAN_ARFCN_FREQUENCY_BAND_25(UtranBand.BAND_25, 910000, 5112, 5413, 875000, 4887, 5188), + UTRAN_ARFCN_FREQUENCY_BAND_A(UtranBand.BAND_A, 0, 10054, 10121, 0, 9504, 9596), + UTRAN_ARFCN_FREQUENCY_BAND_B(UtranBand.BAND_B, 0, 9654, 9946, 0, 9254, 9546), + UTRAN_ARFCN_FREQUENCY_BAND_C(UtranBand.BAND_C, 0, 0, 0, 0, 9554, 9646), + UTRAN_ARFCN_FREQUENCY_BAND_D(UtranBand.BAND_D, 0, 0, 0, 0, 12854, 13096), + UTRAN_ARFCN_FREQUENCY_BAND_E(UtranBand.BAND_E, 0, 0, 0, 0, 11504, 11996), + UTRAN_ARFCN_FREQUENCY_BAND_F(UtranBand.BAND_F, 0, 0, 0, 0, 9404, 9596); + + UtranBandArfcnFrequency(int band, int downlinkOffsetKhz, int downlinkRangeFirst, + int downlinkRangeLast, int uplinkOffsetKhz, int uplinkRangeFirst, + int uplinkRangeLast) { + this.band = band; + this.downlinkOffset = downlinkOffsetKhz; + this.downlinkRangeFirst = downlinkRangeFirst; + this.downlinkRangeLast = downlinkRangeLast; + this.uplinkOffset = uplinkOffsetKhz; + this.uplinkRangeFirst = uplinkRangeFirst; + this.uplinkRangeLast = uplinkRangeLast; + } + + int band; + int downlinkOffset; + int downlinkRangeFirst; + int downlinkRangeLast; + int uplinkOffset; + int uplinkRangeFirst; + int uplinkRangeLast; + } + + /** * Frequency bands for EUTRAN. * 3GPP TS 36.101, Version 16.4.0, Table 5.5: Operating bands * https://www.etsi.org/deliver/etsi_ts/136100_136199/136101/15.09.00_60/ts_136101v150900p.pdf */ public static final class EutranBand { - public static final int BAND_1 = EutranBands.BAND_1; - public static final int BAND_2 = EutranBands.BAND_2; - public static final int BAND_3 = EutranBands.BAND_3; - public static final int BAND_4 = EutranBands.BAND_4; - public static final int BAND_5 = EutranBands.BAND_5; - public static final int BAND_6 = EutranBands.BAND_6; - public static final int BAND_7 = EutranBands.BAND_7; - public static final int BAND_8 = EutranBands.BAND_8; - public static final int BAND_9 = EutranBands.BAND_9; - public static final int BAND_10 = EutranBands.BAND_10; - public static final int BAND_11 = EutranBands.BAND_11; - public static final int BAND_12 = EutranBands.BAND_12; - public static final int BAND_13 = EutranBands.BAND_13; - public static final int BAND_14 = EutranBands.BAND_14; - public static final int BAND_17 = EutranBands.BAND_17; - public static final int BAND_18 = EutranBands.BAND_18; - public static final int BAND_19 = EutranBands.BAND_19; - public static final int BAND_20 = EutranBands.BAND_20; - public static final int BAND_21 = EutranBands.BAND_21; - public static final int BAND_22 = EutranBands.BAND_22; - public static final int BAND_23 = EutranBands.BAND_23; - public static final int BAND_24 = EutranBands.BAND_24; - public static final int BAND_25 = EutranBands.BAND_25; - public static final int BAND_26 = EutranBands.BAND_26; - public static final int BAND_27 = EutranBands.BAND_27; - public static final int BAND_28 = EutranBands.BAND_28; - public static final int BAND_30 = EutranBands.BAND_30; - public static final int BAND_31 = EutranBands.BAND_31; - public static final int BAND_33 = EutranBands.BAND_33; - public static final int BAND_34 = EutranBands.BAND_34; - public static final int BAND_35 = EutranBands.BAND_35; - public static final int BAND_36 = EutranBands.BAND_36; - public static final int BAND_37 = EutranBands.BAND_37; - public static final int BAND_38 = EutranBands.BAND_38; - public static final int BAND_39 = EutranBands.BAND_39; - public static final int BAND_40 = EutranBands.BAND_40; - public static final int BAND_41 = EutranBands.BAND_41; - public static final int BAND_42 = EutranBands.BAND_42; - public static final int BAND_43 = EutranBands.BAND_43; - public static final int BAND_44 = EutranBands.BAND_44; - public static final int BAND_45 = EutranBands.BAND_45; - public static final int BAND_46 = EutranBands.BAND_46; - public static final int BAND_47 = EutranBands.BAND_47; - public static final int BAND_48 = EutranBands.BAND_48; - public static final int BAND_49 = EutranBands.BAND_49; - public static final int BAND_50 = EutranBands.BAND_50; - public static final int BAND_51 = EutranBands.BAND_51; - public static final int BAND_52 = EutranBands.BAND_52; - public static final int BAND_53 = EutranBands.BAND_53; - public static final int BAND_65 = EutranBands.BAND_65; - public static final int BAND_66 = EutranBands.BAND_66; - public static final int BAND_68 = EutranBands.BAND_68; - public static final int BAND_70 = EutranBands.BAND_70; - public static final int BAND_71 = EutranBands.BAND_71; - public static final int BAND_72 = EutranBands.BAND_72; - public static final int BAND_73 = EutranBands.BAND_73; - public static final int BAND_74 = EutranBands.BAND_74; - public static final int BAND_85 = EutranBands.BAND_85; - public static final int BAND_87 = EutranBands.BAND_87; - public static final int BAND_88 = EutranBands.BAND_88; + public static final int BAND_1 = android.hardware.radio.V1_5.EutranBands.BAND_1; + public static final int BAND_2 = android.hardware.radio.V1_5.EutranBands.BAND_2; + public static final int BAND_3 = android.hardware.radio.V1_5.EutranBands.BAND_3; + public static final int BAND_4 = android.hardware.radio.V1_5.EutranBands.BAND_4; + public static final int BAND_5 = android.hardware.radio.V1_5.EutranBands.BAND_5; + public static final int BAND_6 = android.hardware.radio.V1_5.EutranBands.BAND_6; + public static final int BAND_7 = android.hardware.radio.V1_5.EutranBands.BAND_7; + public static final int BAND_8 = android.hardware.radio.V1_5.EutranBands.BAND_8; + public static final int BAND_9 = android.hardware.radio.V1_5.EutranBands.BAND_9; + public static final int BAND_10 = android.hardware.radio.V1_5.EutranBands.BAND_10; + public static final int BAND_11 = android.hardware.radio.V1_5.EutranBands.BAND_11; + public static final int BAND_12 = android.hardware.radio.V1_5.EutranBands.BAND_12; + public static final int BAND_13 = android.hardware.radio.V1_5.EutranBands.BAND_13; + public static final int BAND_14 = android.hardware.radio.V1_5.EutranBands.BAND_14; + public static final int BAND_17 = android.hardware.radio.V1_5.EutranBands.BAND_17; + public static final int BAND_18 = android.hardware.radio.V1_5.EutranBands.BAND_18; + public static final int BAND_19 = android.hardware.radio.V1_5.EutranBands.BAND_19; + public static final int BAND_20 = android.hardware.radio.V1_5.EutranBands.BAND_20; + public static final int BAND_21 = android.hardware.radio.V1_5.EutranBands.BAND_21; + public static final int BAND_22 = android.hardware.radio.V1_5.EutranBands.BAND_22; + public static final int BAND_23 = android.hardware.radio.V1_5.EutranBands.BAND_23; + public static final int BAND_24 = android.hardware.radio.V1_5.EutranBands.BAND_24; + public static final int BAND_25 = android.hardware.radio.V1_5.EutranBands.BAND_25; + public static final int BAND_26 = android.hardware.radio.V1_5.EutranBands.BAND_26; + public static final int BAND_27 = android.hardware.radio.V1_5.EutranBands.BAND_27; + public static final int BAND_28 = android.hardware.radio.V1_5.EutranBands.BAND_28; + public static final int BAND_30 = android.hardware.radio.V1_5.EutranBands.BAND_30; + public static final int BAND_31 = android.hardware.radio.V1_5.EutranBands.BAND_31; + public static final int BAND_33 = android.hardware.radio.V1_5.EutranBands.BAND_33; + public static final int BAND_34 = android.hardware.radio.V1_5.EutranBands.BAND_34; + public static final int BAND_35 = android.hardware.radio.V1_5.EutranBands.BAND_35; + public static final int BAND_36 = android.hardware.radio.V1_5.EutranBands.BAND_36; + public static final int BAND_37 = android.hardware.radio.V1_5.EutranBands.BAND_37; + public static final int BAND_38 = android.hardware.radio.V1_5.EutranBands.BAND_38; + public static final int BAND_39 = android.hardware.radio.V1_5.EutranBands.BAND_39; + public static final int BAND_40 = android.hardware.radio.V1_5.EutranBands.BAND_40; + public static final int BAND_41 = android.hardware.radio.V1_5.EutranBands.BAND_41; + public static final int BAND_42 = android.hardware.radio.V1_5.EutranBands.BAND_42; + public static final int BAND_43 = android.hardware.radio.V1_5.EutranBands.BAND_43; + public static final int BAND_44 = android.hardware.radio.V1_5.EutranBands.BAND_44; + public static final int BAND_45 = android.hardware.radio.V1_5.EutranBands.BAND_45; + public static final int BAND_46 = android.hardware.radio.V1_5.EutranBands.BAND_46; + public static final int BAND_47 = android.hardware.radio.V1_5.EutranBands.BAND_47; + public static final int BAND_48 = android.hardware.radio.V1_5.EutranBands.BAND_48; + public static final int BAND_49 = android.hardware.radio.V1_5.EutranBands.BAND_49; + public static final int BAND_50 = android.hardware.radio.V1_5.EutranBands.BAND_50; + public static final int BAND_51 = android.hardware.radio.V1_5.EutranBands.BAND_51; + public static final int BAND_52 = android.hardware.radio.V1_5.EutranBands.BAND_52; + public static final int BAND_53 = android.hardware.radio.V1_5.EutranBands.BAND_53; + public static final int BAND_65 = android.hardware.radio.V1_5.EutranBands.BAND_65; + public static final int BAND_66 = android.hardware.radio.V1_5.EutranBands.BAND_66; + public static final int BAND_68 = android.hardware.radio.V1_5.EutranBands.BAND_68; + public static final int BAND_70 = android.hardware.radio.V1_5.EutranBands.BAND_70; + public static final int BAND_71 = android.hardware.radio.V1_5.EutranBands.BAND_71; + public static final int BAND_72 = android.hardware.radio.V1_5.EutranBands.BAND_72; + public static final int BAND_73 = android.hardware.radio.V1_5.EutranBands.BAND_73; + public static final int BAND_74 = android.hardware.radio.V1_5.EutranBands.BAND_74; + public static final int BAND_85 = android.hardware.radio.V1_5.EutranBands.BAND_85; + public static final int BAND_87 = android.hardware.radio.V1_5.EutranBands.BAND_87; + public static final int BAND_88 = android.hardware.radio.V1_5.EutranBands.BAND_88; + + /** + * EutranBands + * + * @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"BAND_"}, + value = {BAND_1, + BAND_2, + BAND_3, + BAND_4, + BAND_5, + BAND_6, + BAND_7, + BAND_8, + BAND_9, + BAND_10, + BAND_11, + BAND_12, + BAND_13, + BAND_14, + BAND_17, + BAND_18, + BAND_19, + BAND_20, + BAND_21, + BAND_22, + BAND_23, + BAND_24, + BAND_25, + BAND_26, + BAND_27, + BAND_28, + BAND_30, + BAND_31, + BAND_33, + BAND_34, + BAND_35, + BAND_36, + BAND_37, + BAND_38, + BAND_39, + BAND_40, + BAND_41, + BAND_42, + BAND_43, + BAND_44, + BAND_45, + BAND_46, + BAND_47, + BAND_48, + BAND_49, + BAND_50, + BAND_51, + BAND_52, + BAND_53, + BAND_65, + BAND_66, + BAND_68, + BAND_70, + BAND_71, + BAND_72, + BAND_73, + BAND_74, + BAND_85, + BAND_87, + BAND_88}) + + public @interface EutranBands {} /** @hide */ private EutranBand() {}; } /** + * 3GPP TS 36.101 Table 5.7.3-1 E-UTRA channel numbers. + * + * @hide + */ + enum EutranBandArfcnFrequency { + + EUTRAN_ARFCN_FREQUENCY_BAND_1( + EutranBand.BAND_1, 2110000, 0, 599, 1920000, 18800, 18599), + EUTRAN_ARFCN_FREQUENCY_BAND_2( + EutranBand.BAND_2, 1930000, 600, 1199, 1850000, 18600, 19199), + EUTRAN_ARFCN_FREQUENCY_BAND_3( + EutranBand.BAND_3, 1805000, 1200, 1949, 1710000, 19200, 19949), + EUTRAN_ARFCN_FREQUENCY_BAND_4( + EutranBand.BAND_4, 2110000, 1950, 2399, 1710000, 19950, 20399), + EUTRAN_ARFCN_FREQUENCY_BAND_5( + EutranBand.BAND_5, 869000, 2400, 2649, 824000, 20400, 20649), + EUTRAN_ARFCN_FREQUENCY_BAND_6( + EutranBand.BAND_6, 875000, 2650, 2749, 830000, 20650, 20749), + EUTRAN_ARFCN_FREQUENCY_BAND_7( + EutranBand.BAND_7, 2620000, 2750, 3449, 2500000, 20750, 21449), + EUTRAN_ARFCN_FREQUENCY_BAND_8( + EutranBand.BAND_8, 925000, 3450, 3799, 880000, 21450, 21799), + EUTRAN_ARFCN_FREQUENCY_BAND_9( + EutranBand.BAND_9, 1844900, 3800, 4149, 1749900, 21800, 22149), + EUTRAN_ARFCN_FREQUENCY_BAND_10( + EutranBand.BAND_10, 2110000, 4150, 4749, 1710000, 22150, 22749), + EUTRAN_ARFCN_FREQUENCY_BAND_11( + EutranBand.BAND_11, 1475900, 4750, 4949, 1427900, 22750, 22949), + EUTRAN_ARFCN_FREQUENCY_BAND_12( + EutranBand.BAND_12, 729000, 5010, 5179, 699000, 23010, 23179), + EUTRAN_ARFCN_FREQUENCY_BAND_13( + EutranBand.BAND_13, 746000, 5180, 5279, 777000, 23180, 23279), + EUTRAN_ARFCN_FREQUENCY_BAND_14( + EutranBand.BAND_14, 758000, 5280, 5379, 788000, 23230, 23379), + EUTRAN_ARFCN_FREQUENCY_BAND_17( + EutranBand.BAND_17, 734000, 5730, 5849, 704000, 23730, 23849), + EUTRAN_ARFCN_FREQUENCY_BAND_18( + EutranBand.BAND_18, 860000, 5850, 5999, 815000, 23850, 23999), + EUTRAN_ARFCN_FREQUENCY_BAND_19( + EutranBand.BAND_19, 875000, 6000, 6149, 830000, 24000, 24149), + EUTRAN_ARFCN_FREQUENCY_BAND_20( + EutranBand.BAND_20, 791000, 6150, 6449, 832000, 24150, 24449), + EUTRAN_ARFCN_FREQUENCY_BAND_21( + EutranBand.BAND_21, 1495900, 6450, 6599, 1447900, 24450, 24599), + EUTRAN_ARFCN_FREQUENCY_BAND_22( + EutranBand.BAND_22, 3510000, 6600, 7399, 3410000, 24600, 25399), + EUTRAN_ARFCN_FREQUENCY_BAND_23( + EutranBand.BAND_23, 2180000, 7500, 7699, 2000000, 25500, 25699), + EUTRAN_ARFCN_FREQUENCY_BAND_24( + EutranBand.BAND_24, 1525000, 7700, 8039, 1626500, 25700, 26039), + EUTRAN_ARFCN_FREQUENCY_BAND_25( + EutranBand.BAND_25, 1930000, 8040, 8689, 1850000, 26040, 26689), + EUTRAN_ARFCN_FREQUENCY_BAND_26( + EutranBand.BAND_26, 859000, 8690, 9039, 814000, 26690, 27039), + EUTRAN_ARFCN_FREQUENCY_BAND_27( + EutranBand.BAND_27, 852000, 9040, 9209, 807000, 27040, 27209), + EUTRAN_ARFCN_FREQUENCY_BAND_28( + EutranBand.BAND_28, 758000, 9210, 9659, 703000, 27210, 27659), + EUTRAN_ARFCN_FREQUENCY_BAND_30( + EutranBand.BAND_30, 2350000, 9770, 9869, 2305000, 27660, 27759), + EUTRAN_ARFCN_FREQUENCY_BAND_31( + EutranBand.BAND_31, 462500, 9870, 9919, 452500, 27760, 27809), + EUTRAN_ARFCN_FREQUENCY_BAND_33( + EutranBand.BAND_33, 1900000, 36000, 36199, 1900000, 36000, 36199), + EUTRAN_ARFCN_FREQUENCY_BAND_34( + EutranBand.BAND_34, 2010000, 36200, 36349, 2010000, 36200, 36349), + EUTRAN_ARFCN_FREQUENCY_BAND_35( + EutranBand.BAND_35, 1850000, 36350, 36949, 1850000, 36350, 36949), + EUTRAN_ARFCN_FREQUENCY_BAND_36( + EutranBand.BAND_36, 1930000, 36950, 37549, 1930000, 36950, 37549), + EUTRAN_ARFCN_FREQUENCY_BAND_37( + EutranBand.BAND_37, 1910000, 37550, 37749, 1910000, 37550, 37749), + EUTRAN_ARFCN_FREQUENCY_BAND_38( + EutranBand.BAND_38, 2570000, 37750, 38249, 2570000, 37750, 38249), + EUTRAN_ARFCN_FREQUENCY_BAND_39( + EutranBand.BAND_39, 1880000, 38250, 38649, 1880000, 38250, 38649), + EUTRAN_ARFCN_FREQUENCY_BAND_40( + EutranBand.BAND_40, 2300000, 38650, 39649, 2300000, 38650, 39649), + EUTRAN_ARFCN_FREQUENCY_BAND_41( + EutranBand.BAND_41, 2496000, 39650, 41589, 2496000, 39650, 41589), + EUTRAN_ARFCN_FREQUENCY_BAND_42( + EutranBand.BAND_42, 3400000, 41950, 43589, 3400000, 41950, 43589), + EUTRAN_ARFCN_FREQUENCY_BAND_43( + EutranBand.BAND_43, 3600000, 43950, 45589, 3600000, 43950, 45589), + EUTRAN_ARFCN_FREQUENCY_BAND_44( + EutranBand.BAND_44, 703000, 45590, 46589, 703000, 45590, 46589), + EUTRAN_ARFCN_FREQUENCY_BAND_45( + EutranBand.BAND_45, 1447000, 46590, 46789, 1447000, 46590, 46789), + EUTRAN_ARFCN_FREQUENCY_BAND_46( + EutranBand.BAND_46, 5150000, 46790, 54539, 5150000, 46790, 54539), + EUTRAN_ARFCN_FREQUENCY_BAND_47( + EutranBand.BAND_47, 5855000, 54540, 55239, 5855000, 54540, 55239), + EUTRAN_ARFCN_FREQUENCY_BAND_48( + EutranBand.BAND_48, 3550000, 55240, 56739, 3550000, 55240, 56739), + EUTRAN_ARFCN_FREQUENCY_BAND_49( + EutranBand.BAND_49, 3550000, 56740, 58239, 3550000, 56740, 58239), + EUTRAN_ARFCN_FREQUENCY_BAND_50( + EutranBand.BAND_50, 1432000, 58240, 59089, 1432000, 58240, 59089), + EUTRAN_ARFCN_FREQUENCY_BAND_51( + EutranBand.BAND_51, 1427000, 59090, 59139, 1427000, 59090, 59139), + EUTRAN_ARFCN_FREQUENCY_BAND_52( + EutranBand.BAND_52, 3300000, 59140, 60139, 3300000, 59140, 60139), + EUTRAN_ARFCN_FREQUENCY_BAND_53( + EutranBand.BAND_53, 2483500, 60140, 60254, 2483500, 60140, 60254), + EUTRAN_ARFCN_FREQUENCY_BAND_65( + EutranBand.BAND_65, 2110000, 65536, 66435, 1920000, 131072, 131971), + EUTRAN_ARFCN_FREQUENCY_BAND_66( + EutranBand.BAND_66, 2110000, 66436, 67335, 1710000, 131972, 132671), + EUTRAN_ARFCN_FREQUENCY_BAND_68( + EutranBand.BAND_68, 753000, 67536, 67835, 698000, 132672, 132971), + EUTRAN_ARFCN_FREQUENCY_BAND_70( + EutranBand.BAND_70, 1995000, 68336, 68585, 1695000, 132972, 133121), + EUTRAN_ARFCN_FREQUENCY_BAND_71( + EutranBand.BAND_71, 617000, 68586, 68935, 663000, 133122, 133471), + EUTRAN_ARFCN_FREQUENCY_BAND_72( + EutranBand.BAND_72, 461000, 68936, 68985, 451000, 133472, 133521), + EUTRAN_ARFCN_FREQUENCY_BAND_73( + EutranBand.BAND_73, 460000, 68986, 69035, 450000, 133522, 133571), + EUTRAN_ARFCN_FREQUENCY_BAND_74( + EutranBand.BAND_74, 1475000, 69036, 69465, 1427000, 133572, 134001), + EUTRAN_ARFCN_FREQUENCY_BAND_85( + EutranBand.BAND_85, 728000, 70366, 70545, 698000, 134002, 134181), + EUTRAN_ARFCN_FREQUENCY_BAND_87( + EutranBand.BAND_87, 420000, 70546, 70595, 410000, 134182, 134231), + EUTRAN_ARFCN_FREQUENCY_BAND_88( + EutranBand.BAND_88, 422000, 70596, 70645, 412000, 134231, 134280); + + EutranBandArfcnFrequency(int band, int downlinkLowKhz, int downlinkOffset, + int downlinkRange, int uplinkLowKhz, int uplinkOffset, + int uplinkRange) { + this.band = band; + this.downlinkLowKhz = downlinkLowKhz; + this.downlinkOffset = downlinkOffset; + this.downlinkRange = downlinkRange; + this.uplinkLowKhz = uplinkLowKhz; + this.uplinkOffset = uplinkOffset; + this.uplinkRange = uplinkRange; + } + + int band; + int downlinkLowKhz; + int downlinkOffset; + int downlinkRange; + int uplinkLowKhz; + int uplinkOffset; + int uplinkRange; + } + + /** * Frequency bands for CDMA2000. * http://www.3gpp2.org/Public_html/Specs/C.S0057-E_v1.0_Bandclass_Specification.pdf * @hide @@ -320,7 +694,7 @@ public final class AccessNetworkConstants { * https://www.etsi.org/deliver/etsi_ts/138100_138199/13810102/15.08.00_60/ts_13810102v150800p.pdf */ public static final class NgranBands { - /** 3GPP TS 38.101-1, Version 16.2.0, Table 5.2-1: FR1 bands */ + /** 3GPP TS 38.101-1, Version 16.5.0, Table 5.2-1: FR1 bands */ public static final int BAND_1 = android.hardware.radio.V1_5.NgranBands.BAND_1; public static final int BAND_2 = android.hardware.radio.V1_5.NgranBands.BAND_2; public static final int BAND_3 = android.hardware.radio.V1_5.NgranBands.BAND_3; @@ -332,6 +706,7 @@ public final class AccessNetworkConstants { public static final int BAND_18 = android.hardware.radio.V1_5.NgranBands.BAND_18; public static final int BAND_20 = android.hardware.radio.V1_5.NgranBands.BAND_20; public static final int BAND_25 = android.hardware.radio.V1_5.NgranBands.BAND_25; + public static final int BAND_26 = android.hardware.radio.V1_6.NgranBands.BAND_26; public static final int BAND_28 = android.hardware.radio.V1_5.NgranBands.BAND_28; public static final int BAND_29 = android.hardware.radio.V1_5.NgranBands.BAND_29; public static final int BAND_30 = android.hardware.radio.V1_5.NgranBands.BAND_30; @@ -340,9 +715,11 @@ public final class AccessNetworkConstants { public static final int BAND_39 = android.hardware.radio.V1_5.NgranBands.BAND_39; public static final int BAND_40 = android.hardware.radio.V1_5.NgranBands.BAND_40; public static final int BAND_41 = android.hardware.radio.V1_5.NgranBands.BAND_41; + public static final int BAND_46 = android.hardware.radio.V1_6.NgranBands.BAND_46; public static final int BAND_48 = android.hardware.radio.V1_5.NgranBands.BAND_48; public static final int BAND_50 = android.hardware.radio.V1_5.NgranBands.BAND_50; public static final int BAND_51 = android.hardware.radio.V1_5.NgranBands.BAND_51; + public static final int BAND_53 = android.hardware.radio.V1_6.NgranBands.BAND_53; public static final int BAND_65 = android.hardware.radio.V1_5.NgranBands.BAND_65; public static final int BAND_66 = android.hardware.radio.V1_5.NgranBands.BAND_66; public static final int BAND_70 = android.hardware.radio.V1_5.NgranBands.BAND_70; @@ -366,6 +743,7 @@ public final class AccessNetworkConstants { public static final int BAND_93 = android.hardware.radio.V1_5.NgranBands.BAND_93; public static final int BAND_94 = android.hardware.radio.V1_5.NgranBands.BAND_94; public static final int BAND_95 = android.hardware.radio.V1_5.NgranBands.BAND_95; + public static final int BAND_96 = android.hardware.radio.V1_6.NgranBands.BAND_96; /** 3GPP TS 38.101-2, Version 16.2.0, Table 5.2-1: FR2 bands */ public static final int BAND_257 = android.hardware.radio.V1_5.NgranBands.BAND_257; @@ -390,6 +768,7 @@ public final class AccessNetworkConstants { BAND_18, BAND_20, BAND_25, + BAND_26, BAND_28, BAND_29, BAND_30, @@ -398,9 +777,11 @@ public final class AccessNetworkConstants { BAND_39, BAND_40, BAND_41, + BAND_46, BAND_48, BAND_50, BAND_51, + BAND_53, BAND_65, BAND_66, BAND_70, @@ -424,6 +805,7 @@ public final class AccessNetworkConstants { BAND_93, BAND_94, BAND_95, + BAND_96, BAND_257, BAND_258, BAND_260, @@ -464,7 +846,8 @@ public final class AccessNetworkConstants { value = { FREQUENCY_RANGE_GROUP_UNKNOWN, FREQUENCY_RANGE_GROUP_1, - FREQUENCY_RANGE_GROUP_2}) + FREQUENCY_RANGE_GROUP_2 + }) public @interface FrequencyRangeGroup {} /** @@ -489,6 +872,7 @@ public final class AccessNetworkConstants { case BAND_18: case BAND_20: case BAND_25: + case BAND_26: case BAND_28: case BAND_29: case BAND_30: @@ -497,9 +881,11 @@ public final class AccessNetworkConstants { case BAND_39: case BAND_40: case BAND_41: + case BAND_46: case BAND_48: case BAND_50: case BAND_51: + case BAND_53: case BAND_65: case BAND_66: case BAND_70: @@ -523,6 +909,7 @@ public final class AccessNetworkConstants { case BAND_93: case BAND_94: case BAND_95: + case BAND_96: return FREQUENCY_RANGE_GROUP_1; case BAND_257: case BAND_258: @@ -538,6 +925,33 @@ public final class AccessNetworkConstants { private NgranBands() {} } + /** + * 3GPP TS 38.104 Table 5.4.2.1-1 NR-ARFCN parameters for the global frequency raster. + * + * @hide + */ + enum NgranArfcnFrequency { + + NGRAN_ARFCN_FREQUENCY_RANGE_1(5, 0, 0, 0, 599999), + NGRAN_ARFCN_FREQUENCY_RANGE_2(15, 3000000, 600000, 600000, 2016666), + NGRAN_ARFCN_FREQUENCY_RANGE_3(60, 24250080, 2016667, 2016667, 3279165); + + NgranArfcnFrequency(int globalKhz, int rangeOffset, int arfcnOffset, + int rangeFirst, int rangeLast) { + this.globalKhz = globalKhz; + this.rangeOffset = rangeOffset; + this.arfcnOffset = arfcnOffset; + this.rangeFirst = rangeFirst; + this.rangeLast = rangeLast; + } + + int globalKhz; + int rangeOffset; + int arfcnOffset; + int rangeFirst; + int rangeLast; + } + /** @hide */ private AccessNetworkConstants() {}; } diff --git a/telephony/java/android/telephony/AccessNetworkUtils.java b/telephony/java/android/telephony/AccessNetworkUtils.java index 981ed450004a..6b820459be98 100644 --- a/telephony/java/android/telephony/AccessNetworkUtils.java +++ b/telephony/java/android/telephony/AccessNetworkUtils.java @@ -4,9 +4,20 @@ import static android.telephony.ServiceState.DUPLEX_MODE_FDD; import static android.telephony.ServiceState.DUPLEX_MODE_TDD; import static android.telephony.ServiceState.DUPLEX_MODE_UNKNOWN; +import android.telephony.AccessNetworkConstants.EutranBandArfcnFrequency; import android.telephony.AccessNetworkConstants.EutranBand; +import android.telephony.AccessNetworkConstants.GeranBand; +import android.telephony.AccessNetworkConstants.GeranBandArfcnFrequency; +import android.telephony.AccessNetworkConstants.NgranArfcnFrequency; +import android.telephony.AccessNetworkConstants.NgranBands; +import android.telephony.AccessNetworkConstants.UtranBand; +import android.telephony.AccessNetworkConstants.UtranBandArfcnFrequency; import android.telephony.ServiceState.DuplexMode; +import android.util.Log; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; /** * Utilities to map between radio constants. @@ -19,6 +30,27 @@ public class AccessNetworkUtils { private AccessNetworkUtils() {} public static final int INVALID_BAND = -1; + public static final int INVALID_FREQUENCY = -1; + + /** ISO country code of Japan. */ + private static final String JAPAN_ISO_COUNTRY_CODE = "jp"; + private static final String TAG = "AccessNetworkUtils"; + + private static final int FREQUENCY_KHZ = 1000; + private static final int FREQUENCY_RANGE_LOW_KHZ = 1000000; + private static final int FREQUENCY_RANGE_MID_KHZ = 3000000; + private static final int FREQUENCY_RANGE_HIGH_KHZ = 6000000; + + private static final Set<Integer> UARFCN_NOT_GENERAL_BAND; + static { + UARFCN_NOT_GENERAL_BAND = new HashSet<Integer>(); + UARFCN_NOT_GENERAL_BAND.add(UtranBand.BAND_A); + UARFCN_NOT_GENERAL_BAND.add(UtranBand.BAND_B); + UARFCN_NOT_GENERAL_BAND.add(UtranBand.BAND_C); + UARFCN_NOT_GENERAL_BAND.add(UtranBand.BAND_D); + UARFCN_NOT_GENERAL_BAND.add(UtranBand.BAND_E); + UARFCN_NOT_GENERAL_BAND.add(UtranBand.BAND_F); + } /** * Gets the duplex mode for the given EUTRAN operating band. @@ -50,7 +82,7 @@ public class AccessNetworkUtils { /** * Gets the EUTRAN Operating band for a given downlink EARFCN. * - * <p>See 3GPP 36.101 sec 5.7.3-1 for calculation. + * <p>See 3GPP TS 36.101 clause 5.7.3-1 for calculation. * * @param earfcn The downlink EARFCN * @return Operating band number, or {@link #INVALID_BAND} if no corresponding band exists @@ -198,4 +230,527 @@ public class AccessNetworkUtils { return INVALID_BAND; } + + /** + * Gets the GERAN Operating band for a given ARFCN. + * + * <p>See 3GPP TS 45.005 clause 2 for calculation. + * + * @param arfcn The ARFCN + * @return Operating band number, or {@link #INVALID_BAND} if no corresponding band exists + */ + public static int getOperatingBandForArfcn(int arfcn) { + if (arfcn >= 0 && arfcn <= 124) { + return GeranBand.BAND_E900; + } else if (arfcn >= 128 && arfcn <= 251) { + return GeranBand.BAND_850; + } else if (arfcn >= 259 && arfcn <= 293) { + return GeranBand.BAND_450; + } else if (arfcn >= 306 && arfcn <= 340) { + return GeranBand.BAND_480; + } else if (arfcn >= 438 && arfcn <= 511) { + return GeranBand.BAND_750; + } else if (arfcn >= 512 && arfcn <= 885) { + // ARFCN between 512 and 810 are also part of BAND_PCS1900. + // Returning BAND_DCS1800 in both cases. + return GeranBand.BAND_DCS1800; + } else if (arfcn >= 940 && arfcn <= 974) { + return GeranBand.BAND_ER900; + } else if (arfcn >= 975 && arfcn <= 1023) { + return GeranBand.BAND_E900; + } + return INVALID_BAND; + } + + /** + * Gets the UTRAN Operating band for a given downlink UARFCN. + * + * <p>See 3GPP TS 25.101 clause 5.4.4 for calculation. + * + * @param uarfcn The downlink UARFCN + * @return Operating band number, or {@link #INVALID_BAND} if no corresponding band exists + */ + public static int getOperatingBandForUarfcn(int uarfcn) { + // List of additional bands defined in TS 25.101. + int[] addlBand2 = {412, 437, 462, 487, 512, 537, 562, 587, 612, 637, 662, 687}; + int[] addlBand4 = {1887, 1912, 1937, 1962, 1987, 2012, 2037, 2062, 2087}; + int[] addlBand5 = {1007, 1012, 1032, 1037, 1062, 1087}; + int[] addlBand6 = {1037, 1062}; + int[] addlBand7 = + {2587, 2612, 2637, 2662, 2687, 2712, 2737, 2762, 2787, 2812, 2837, 2862, + 2887, 2912}; + int[] addlBand10 = + {3412, 3437, 3462, 3487, 3512, 3537, 3562, 3587, 3612, 3637, 3662, 3687}; + int[] addlBand12 = {3932, 3957, 3962, 3987, 3992}; + int[] addlBand13 = {4067, 4092}; + int[] addlBand14 = {4167, 4192}; + int[] addlBand19 = {787, 812, 837}; + int[] addlBand25 = + {6292, 6317, 6342, 6367, 6392, 6417, 6442, 6467, 6492, 6517, 6542, 6567, 6592}; + int[] addlBand26 = {5937, 5962, 5987, 5992, 6012, 6017, 6037, 6042, 6062, 6067, 6087}; + + if (uarfcn >= 10562 && uarfcn <= 10838) { + return UtranBand.BAND_1; + } else if ((uarfcn >= 9662 && uarfcn <= 9938) + || Arrays.binarySearch(addlBand2, uarfcn) >= 0) { + return UtranBand.BAND_2; + } else if (uarfcn >= 1162 && uarfcn <= 1513) { + return UtranBand.BAND_3; + } else if ((uarfcn >= 1537 && uarfcn <= 1738) + || Arrays.binarySearch(addlBand4, uarfcn) >= 0) { + return UtranBand.BAND_4; + } else if (uarfcn >= 4387 && uarfcn <= 4413) { + // Band 6 is a subset of band 5. Only Japan uses band 6 and Japan does not have band 5. + String country = TelephonyManager.getDefault().getNetworkCountryIso(); + if (JAPAN_ISO_COUNTRY_CODE.compareToIgnoreCase(country) == 0) { + return UtranBand.BAND_6; + } else { + return UtranBand.BAND_5; + } + } else if ((uarfcn >= 4357 && uarfcn <= 4458) + || Arrays.binarySearch(addlBand5, uarfcn) >= 0) { + return UtranBand.BAND_5; + } else if (Arrays.binarySearch(addlBand6, uarfcn) >= 0) { + return UtranBand.BAND_6; + } else if ((uarfcn >= 2237 && uarfcn <= 2563) + || Arrays.binarySearch(addlBand7, uarfcn) >= 0) { + return UtranBand.BAND_7; + } else if (uarfcn >= 2937 && uarfcn <= 3088) { + return UtranBand.BAND_8; + } else if (uarfcn >= 9237 && uarfcn <= 9387) { + return UtranBand.BAND_9; + } else if ((uarfcn >= 3112 && uarfcn <= 3388) + || Arrays.binarySearch(addlBand10, uarfcn) >= 0) { + return UtranBand.BAND_10; + } else if (uarfcn >= 3712 && uarfcn <= 3787) { + return UtranBand.BAND_11; + } else if ((uarfcn >= 3842 && uarfcn <= 3903) + || Arrays.binarySearch(addlBand12, uarfcn) >= 0) { + return UtranBand.BAND_12; + } else if ((uarfcn >= 4017 && uarfcn <= 4043) + || Arrays.binarySearch(addlBand13, uarfcn) >= 0) { + return UtranBand.BAND_13; + } else if ((uarfcn >= 4117 && uarfcn <= 4143) + || Arrays.binarySearch(addlBand14, uarfcn) >= 0) { + return UtranBand.BAND_14; + } else if ((uarfcn >= 712 && uarfcn <= 763) + || Arrays.binarySearch(addlBand19, uarfcn) >= 0) { + return UtranBand.BAND_19; + } else if (uarfcn >= 4512 && uarfcn <= 4638) { + return UtranBand.BAND_20; + } else if (uarfcn >= 862 && uarfcn <= 912) { + return UtranBand.BAND_21; + } else if (uarfcn >= 4662 && uarfcn <= 5038) { + return UtranBand.BAND_22; + } else if ((uarfcn >= 5112 && uarfcn <= 5413) + || Arrays.binarySearch(addlBand25, uarfcn) >= 0) { + return UtranBand.BAND_25; + } else if ((uarfcn >= 5762 && uarfcn <= 5913) + || Arrays.binarySearch(addlBand26, uarfcn) >= 0) { + return UtranBand.BAND_26; + } + return INVALID_BAND; + } + + /** + * Get geran bands from {@link PhysicalChannelConfig#getBand()} + */ + public static int getFrequencyRangeGroupFromGeranBand(@GeranBand.GeranBands int band) { + switch (band) { + case GeranBand.BAND_T380: + case GeranBand.BAND_T410: + case GeranBand.BAND_450: + case GeranBand.BAND_480: + case GeranBand.BAND_710: + case GeranBand.BAND_750: + case GeranBand.BAND_T810: + case GeranBand.BAND_850: + case GeranBand.BAND_P900: + case GeranBand.BAND_E900: + case GeranBand.BAND_R900: + case GeranBand.BAND_ER900: + return ServiceState.FREQUENCY_RANGE_LOW; + case GeranBand.BAND_DCS1800: + case GeranBand.BAND_PCS1900: + return ServiceState.FREQUENCY_RANGE_MID; + default: + return ServiceState.FREQUENCY_RANGE_UNKNOWN; + } + } + + /** + * Get utran bands from {@link PhysicalChannelConfig#getBand()} + */ + public static int getFrequencyRangeGroupFromUtranBand(@UtranBand.UtranBands int band) { + switch (band) { + case UtranBand.BAND_5: + case UtranBand.BAND_6: + case UtranBand.BAND_8: + case UtranBand.BAND_12: + case UtranBand.BAND_13: + case UtranBand.BAND_14: + case UtranBand.BAND_19: + case UtranBand.BAND_20: + case UtranBand.BAND_26: + return ServiceState.FREQUENCY_RANGE_LOW; + case UtranBand.BAND_1: + case UtranBand.BAND_2: + case UtranBand.BAND_3: + case UtranBand.BAND_4: + case UtranBand.BAND_7: + case UtranBand.BAND_9: + case UtranBand.BAND_10: + case UtranBand.BAND_11: + case UtranBand.BAND_21: + case UtranBand.BAND_25: + case UtranBand.BAND_A: + case UtranBand.BAND_B: + case UtranBand.BAND_C: + case UtranBand.BAND_D: + case UtranBand.BAND_E: + case UtranBand.BAND_F: + return ServiceState.FREQUENCY_RANGE_MID; + case UtranBand.BAND_22: + return ServiceState.FREQUENCY_RANGE_HIGH; + default: + return ServiceState.FREQUENCY_RANGE_UNKNOWN; + } + } + + /** + * Get eutran bands from {@link PhysicalChannelConfig#getBand()} + * 3GPP TS 36.101 Table 5.5 EUTRA operating bands + */ + public static int getFrequencyRangeGroupFromEutranBand(@EutranBand.EutranBands int band) { + switch (band) { + case EutranBand.BAND_5: + case EutranBand.BAND_6: + case EutranBand.BAND_8: + case EutranBand.BAND_12: + case EutranBand.BAND_13: + case EutranBand.BAND_14: + case EutranBand.BAND_17: + case EutranBand.BAND_18: + case EutranBand.BAND_19: + case EutranBand.BAND_20: + case EutranBand.BAND_26: + case EutranBand.BAND_27: + case EutranBand.BAND_28: + case EutranBand.BAND_31: + case EutranBand.BAND_44: + case EutranBand.BAND_50: + case EutranBand.BAND_51: + case EutranBand.BAND_68: + case EutranBand.BAND_71: + case EutranBand.BAND_72: + case EutranBand.BAND_73: + case EutranBand.BAND_85: + case EutranBand.BAND_87: + case EutranBand.BAND_88: + return ServiceState.FREQUENCY_RANGE_LOW; + case EutranBand.BAND_1: + case EutranBand.BAND_2: + case EutranBand.BAND_3: + case EutranBand.BAND_4: + case EutranBand.BAND_7: + case EutranBand.BAND_9: + case EutranBand.BAND_10: + case EutranBand.BAND_11: + case EutranBand.BAND_21: + case EutranBand.BAND_23: + case EutranBand.BAND_24: + case EutranBand.BAND_25: + case EutranBand.BAND_30: + case EutranBand.BAND_33: + case EutranBand.BAND_34: + case EutranBand.BAND_35: + case EutranBand.BAND_36: + case EutranBand.BAND_37: + case EutranBand.BAND_38: + case EutranBand.BAND_39: + case EutranBand.BAND_40: + case EutranBand.BAND_41: + case EutranBand.BAND_45: + case EutranBand.BAND_53: + case EutranBand.BAND_65: + case EutranBand.BAND_66: + case EutranBand.BAND_70: + case EutranBand.BAND_74: + return ServiceState.FREQUENCY_RANGE_MID; + case EutranBand.BAND_22: + case EutranBand.BAND_42: + case EutranBand.BAND_43: + case EutranBand.BAND_46: + case EutranBand.BAND_47: + case EutranBand.BAND_48: + case EutranBand.BAND_49: + case EutranBand.BAND_52: + return ServiceState.FREQUENCY_RANGE_HIGH; + default: + return ServiceState.FREQUENCY_RANGE_UNKNOWN; + } + } + + /** + * Get ngran band from {@link PhysicalChannelConfig#getBand()} + * 3GPP TS 38.104 Table 5.2-1 NR operating bands in FR1 + * 3GPP TS 38.104 Table 5.2-2 NR operating bands in FR2 + */ + public static int getFrequencyRangeGroupFromNrBand(@NgranBands.NgranBand int band) { + switch (band) { + case NgranBands.BAND_5: + case NgranBands.BAND_8: + case NgranBands.BAND_12: + case NgranBands.BAND_14: + case NgranBands.BAND_18: + case NgranBands.BAND_20: + case NgranBands.BAND_26: + case NgranBands.BAND_28: + case NgranBands.BAND_29: + case NgranBands.BAND_71: + case NgranBands.BAND_81: + case NgranBands.BAND_82: + case NgranBands.BAND_83: + case NgranBands.BAND_89: + return ServiceState.FREQUENCY_RANGE_LOW; + case NgranBands.BAND_1: + case NgranBands.BAND_2: + case NgranBands.BAND_3: + case NgranBands.BAND_7: + case NgranBands.BAND_25: + case NgranBands.BAND_30: + case NgranBands.BAND_34: + case NgranBands.BAND_38: + case NgranBands.BAND_39: + case NgranBands.BAND_40: + case NgranBands.BAND_41: + case NgranBands.BAND_50: + case NgranBands.BAND_51: + case NgranBands.BAND_53: + case NgranBands.BAND_65: + case NgranBands.BAND_66: + case NgranBands.BAND_70: + case NgranBands.BAND_74: + case NgranBands.BAND_75: + case NgranBands.BAND_76: + case NgranBands.BAND_80: + case NgranBands.BAND_84: + case NgranBands.BAND_86: + case NgranBands.BAND_90: + case NgranBands.BAND_91: + case NgranBands.BAND_92: + case NgranBands.BAND_93: + case NgranBands.BAND_94: + case NgranBands.BAND_95: + return ServiceState.FREQUENCY_RANGE_MID; + case NgranBands.BAND_46: + case NgranBands.BAND_48: + case NgranBands.BAND_77: + case NgranBands.BAND_78: + case NgranBands.BAND_79: + return ServiceState.FREQUENCY_RANGE_HIGH; + case NgranBands.BAND_96: + case NgranBands.BAND_257: + case NgranBands.BAND_258: + case NgranBands.BAND_260: + case NgranBands.BAND_261: + return ServiceState.FREQUENCY_RANGE_MMWAVE; + default: + return ServiceState.FREQUENCY_RANGE_UNKNOWN; + } + } + + /** + * 3GPP TS 38.104 Table 5.4.2.1-1 NR-ARFCN parameters for the global frequency raster. + * Formula of NR-ARFCN convert to actual frequency: + * Actual frequency(kHz) = (RANGE_OFFSET + GLOBAL_KHZ * (ARFCN - ARFCN_OFFSET)) + */ + public static int getFrequencyFromNrArfcn(int nrArfcn) { + + int globalKhz = 0; + int rangeOffset = 0; + int arfcnOffset = 0; + for (NgranArfcnFrequency nrArfcnFrequency : AccessNetworkConstants. + NgranArfcnFrequency.values()) { + if (nrArfcn >= nrArfcnFrequency.rangeFirst + && nrArfcn <= nrArfcnFrequency.rangeLast) { + globalKhz = nrArfcnFrequency.globalKhz; + rangeOffset = nrArfcnFrequency.rangeOffset; + arfcnOffset = nrArfcnFrequency.arfcnOffset; + break; + } + } + return rangeOffset + globalKhz * (nrArfcn - arfcnOffset); + } + + /** + * Get actual frequency from E-UTRA ARFCN. + */ + public static int getFrequencyFromEarfcn(int band, int earfcn, boolean isUplink) { + + int low = 0; + int offset = 0; + for (EutranBandArfcnFrequency earfcnFrequency : EutranBandArfcnFrequency.values()) { + if (band == earfcnFrequency.band) { + if (isInEarfcnRange(earfcn, earfcnFrequency, isUplink)) { + low = isUplink ? earfcnFrequency.uplinkLowKhz : earfcnFrequency.downlinkLowKhz; + offset = isUplink ? earfcnFrequency.uplinkOffset + : earfcnFrequency.downlinkOffset; + break; + } else { + Rlog.w(TAG,"Band and the range of EARFCN are not consistent: band = " + band + + " ,earfcn = " + earfcn + " ,isUplink = " + isUplink); + return INVALID_FREQUENCY; + } + } + } + return convertEarfcnToFrequency(low, earfcn, offset); + } + + /** + * 3GPP TS 36.101 Table 5.7.3-1 E-UTRA channel numbers. + * Formula of E-UTRA ARFCN convert to actual frequency: + * Actual frequency(kHz) = (DOWNLINK_LOW + 0.1 * (ARFCN - DOWNLINK_OFFSET)) * FREQUENCY_KHZ + * Actual frequency(kHz) = (UPLINK_LOW + 0.1 * (ARFCN - UPLINK_OFFSET)) * FREQUENCY_KHZ + */ + private static int convertEarfcnToFrequency(int low, int earfcn, int offset) { + return low + 100 * (earfcn - offset); + } + + private static boolean isInEarfcnRange(int earfcn, EutranBandArfcnFrequency earfcnFrequency, + boolean isUplink) { + if (isUplink) { + return earfcn >= earfcnFrequency.uplinkOffset && earfcn <= earfcnFrequency.uplinkRange; + } else { + return earfcn >= earfcnFrequency.downlinkOffset + && earfcn <= earfcnFrequency.downlinkRange; + } + } + + /** + * Get actual frequency from UTRA ARFCN. + */ + public static int getFrequencyFromUarfcn(int band, int uarfcn, boolean isUplink) { + + int offsetKhz = 0; + for (UtranBandArfcnFrequency uarfcnFrequency : AccessNetworkConstants. + UtranBandArfcnFrequency.values()) { + if (band == uarfcnFrequency.band) { + if (isInUarfcnRange(uarfcn, uarfcnFrequency, isUplink)) { + offsetKhz = isUplink ? uarfcnFrequency.uplinkOffset + : uarfcnFrequency.downlinkOffset; + break; + } else { + Rlog.w(TAG,"Band and the range of UARFCN are not consistent: band = " + band + + " ,uarfcn = " + uarfcn + " ,isUplink = " + isUplink); + return INVALID_FREQUENCY; + } + } + } + + if (!UARFCN_NOT_GENERAL_BAND.contains(band)) { + return convertUarfcnToFrequency(offsetKhz, uarfcn); + } else { + return convertUarfcnTddToFrequency(band, uarfcn); + } + } + + /** + * 3GPP TS 25.101, Table 5.1 UARFCN definition (general). + * Formula of UTRA ARFCN convert to actual frequency: + * For general bands: + * Downlink actual frequency(kHz) = (DOWNLINK_OFFSET + 0.2 * ARFCN) * FREQUENCY_KHZ + * Uplink actual frequency(kHz) = (UPLINK_OFFSET + 0.2 * ARFCN) * FREQUENCY_KHZ + */ + private static int convertUarfcnToFrequency(int offsetKhz, int uarfcn) { + return offsetKhz + (200 * uarfcn); + } + + /** + * 3GPP TS 25.102, Table 5.2 UTRA Absolute Radio Frequency Channel Number 1.28 Mcps TDD Option. + * For FDD bands A, B, C, E, F: + * Actual frequency(kHz) = 5 * ARFCN * FREQUENCY_KHZ + * For TDD bands D: + * Actual frequency(kHz) = (5 * (ARFCN - 2150.1MHz)) * FREQUENCY_KHZ + */ + private static int convertUarfcnTddToFrequency(int band, int uarfcn) { + if (band != UtranBand.BAND_D) { + return 5 * uarfcn * FREQUENCY_KHZ; + } else { + return 5 * ((FREQUENCY_KHZ * uarfcn) - 2150100); + } + } + + private static boolean isInUarfcnRange(int uarfcn, UtranBandArfcnFrequency uarfcnFrequency, + boolean isUplink) { + if (isUplink) { + return uarfcn >= uarfcnFrequency.uplinkRangeFirst + && uarfcn <= uarfcnFrequency.uplinkRangeLast; + } else { + if (uarfcnFrequency.downlinkRangeFirst != 0 && uarfcnFrequency.downlinkRangeLast != 0) { + return uarfcn >= uarfcnFrequency.downlinkRangeFirst + && uarfcn <= uarfcnFrequency.downlinkRangeLast; + } else { + // BAND_C, BAND_D, BAND_E and BAND_F do not have the downlink range. + return true; + } + } + } + + /** + * Get actual frequency from GERAN ARFCN. + */ + public static int getFrequencyFromArfcn(int band, int arfcn, boolean isUplink) { + + int uplinkFrequencyFirst = 0; + int arfcnOffset = 0; + int downlinkOffset = 0; + int frequency = 0; + for (GeranBandArfcnFrequency arfcnFrequency : AccessNetworkConstants. + GeranBandArfcnFrequency.values()) { + if (band == arfcnFrequency.band) { + if (arfcn >= arfcnFrequency.arfcnRangeFirst + && arfcn <= arfcnFrequency.arfcnRangeLast) { + uplinkFrequencyFirst = arfcnFrequency.uplinkFrequencyFirst; + downlinkOffset = arfcnFrequency.downlinkOffset; + arfcnOffset = arfcnFrequency.arfcnOffset; + frequency = convertArfcnToFrequency(arfcn, uplinkFrequencyFirst, + arfcnOffset); + break; + } else { + Rlog.w(TAG,"Band and the range of ARFCN are not consistent: band = " + band + + " ,arfcn = " + arfcn + " ,isUplink = " + isUplink); + return INVALID_FREQUENCY; + } + } + } + + return isUplink ? frequency : frequency + downlinkOffset; + } + + /** + * 3GPP TS 45.005 Table 2-1 Dynamically mapped ARFCN + * Formula of Geran ARFCN convert to actual frequency: + * Uplink actual frequency(kHz) = + * (UPLINK_FREQUENCY_FIRST + 0.2 * (ARFCN - ARFCN_RANGE_FIRST)) * FREQUENCY_KHZ + * Downlink actual frequency(kHz) = Uplink actual frequency + 10 + */ + private static int convertArfcnToFrequency(int arfcn, int uplinkFrequencyFirstKhz, + int arfcnOffset) { + return uplinkFrequencyFirstKhz + 200 * (arfcn - arfcnOffset); + } + + public static int getFrequencyRangeFromArfcn(int frequency) { + if (frequency < FREQUENCY_RANGE_LOW_KHZ) { + return ServiceState.FREQUENCY_RANGE_LOW; + } else if (frequency < FREQUENCY_RANGE_MID_KHZ + && frequency >= FREQUENCY_RANGE_LOW_KHZ) { + return ServiceState.FREQUENCY_RANGE_MID; + } else if (frequency < FREQUENCY_RANGE_HIGH_KHZ + && frequency >= FREQUENCY_RANGE_MID_KHZ) { + return ServiceState.FREQUENCY_RANGE_HIGH; + } else { + return ServiceState.FREQUENCY_RANGE_MMWAVE; + } + } } diff --git a/telephony/java/android/telephony/Annotation.java b/telephony/java/android/telephony/Annotation.java index 031c3376f3ba..23cf5116b2da 100644 --- a/telephony/java/android/telephony/Annotation.java +++ b/telephony/java/android/telephony/Annotation.java @@ -111,6 +111,7 @@ public class Annotation { public @interface NetworkType { } + // TODO(b/180542000): remove and replace references with @ApnSetting.ApnType @IntDef(flag = true, prefix = {"TYPE_"}, value = { ApnSetting.TYPE_DEFAULT, ApnSetting.TYPE_MMS, @@ -124,6 +125,7 @@ public class Annotation { ApnSetting.TYPE_EMERGENCY, ApnSetting.TYPE_MCX, ApnSetting.TYPE_XCAP, + // ApnSetting.TYPE_ENTERPRISE }) @Retention(RetentionPolicy.SOURCE) public @interface ApnType { @@ -445,6 +447,9 @@ public class Annotation { DataFailCause.VSNCP_RECONNECT_NOT_ALLOWED, DataFailCause.IPV6_PREFIX_UNAVAILABLE, DataFailCause.HANDOFF_PREFERENCE_CHANGED, + DataFailCause.SLICE_REJECTED, + DataFailCause.MATCH_ALL_RULE_NOT_ALLOWED, + DataFailCause.ALL_MATCHING_RULES_FAILED, DataFailCause.OEM_DCFAILCAUSE_1, DataFailCause.OEM_DCFAILCAUSE_2, DataFailCause.OEM_DCFAILCAUSE_3, @@ -624,6 +629,20 @@ public class Annotation { public @interface UiccAppType{} /** + * UICC SIM Application Types including UNKNOWN + */ + @IntDef(prefix = { "APPTYPE_" }, value = { + TelephonyManager.APPTYPE_UNKNOWN, + TelephonyManager.APPTYPE_SIM, + TelephonyManager.APPTYPE_USIM, + TelephonyManager.APPTYPE_RUIM, + TelephonyManager.APPTYPE_CSIM, + TelephonyManager.APPTYPE_ISIM + }) + @Retention(RetentionPolicy.SOURCE) + public @interface UiccAppTypeExt{} + + /** * Override network type */ @Retention(RetentionPolicy.SOURCE) @@ -632,6 +651,17 @@ public class Annotation { TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA, TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_ADVANCED_PRO, TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA, - TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE}) + TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED}) public @interface OverrideNetworkType {} + + /** + * Result of a thermal mitigation request. + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = { "THERMAL_MITIGATION_RESULT_" }, value = { + TelephonyManager.THERMAL_MITIGATION_RESULT_SUCCESS, + TelephonyManager.THERMAL_MITIGATION_RESULT_MODEM_ERROR, + TelephonyManager.THERMAL_MITIGATION_RESULT_INVALID_STATE, + TelephonyManager.THERMAL_MITIGATION_RESULT_UNKNOWN_ERROR}) + public @interface ThermalMitigationResult {} } diff --git a/telephony/java/android/telephony/BinderCacheManager.java b/telephony/java/android/telephony/BinderCacheManager.java new file mode 100644 index 000000000000..0d3e2fe7591c --- /dev/null +++ b/telephony/java/android/telephony/BinderCacheManager.java @@ -0,0 +1,197 @@ +/* + * Copyright (C) 2020 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.NonNull; +import android.os.IBinder; +import android.os.IInterface; +import android.os.RemoteException; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.NoSuchElementException; +import java.util.concurrent.atomic.AtomicReference; + +/** + * Keeps track of the connection to a Binder node, refreshes the cache if the node dies, and lets + * interested parties register listeners on the node to be notified when the node has died via the + * registered {@link Runnable}. + * @param <T> The IInterface representing the Binder type that this manager will be managing the + * cache of. + * @hide + */ +public class BinderCacheManager<T extends IInterface> { + + /** + * Factory class for creating new IInterfaces in the case that {@link #getBinder()} is + * called and there is no active binder available. + * @param <T> The IInterface that should be cached and returned to the caller when + * {@link #getBinder()} is called until the Binder node dies. + */ + public interface BinderInterfaceFactory<T> { + /** + * @return A new instance of the Binder node, which will be cached until it dies. + */ + T create(); + } + + /** + * Tracks the cached Binder node as well as the listeners that were associated with that + * Binder node during its lifetime. If the Binder node dies, the listeners will be called and + * then this tracker will be unlinked and cleaned up. + */ + private class BinderDeathTracker implements IBinder.DeathRecipient { + + private final T mConnection; + private final HashMap<Object, Runnable> mListeners = new HashMap<>(); + + /** + * Create a tracker to cache the Binder node and add the ability to listen for the cached + * interface's death. + */ + BinderDeathTracker(@NonNull T connection) { + mConnection = connection; + try { + mConnection.asBinder().linkToDeath(this, 0 /*flags*/); + } catch (RemoteException e) { + // isAlive will return false. + } + } + + public boolean addListener(Object key, Runnable r) { + synchronized (mListeners) { + if (!isAlive()) return false; + mListeners.put(key, r); + return true; + } + } + + public void removeListener(Object runnableKey) { + synchronized (mListeners) { + mListeners.remove(runnableKey); + } + } + + @Override + public void binderDied() { + ArrayList<Runnable> listeners; + synchronized (mListeners) { + listeners = new ArrayList<>(mListeners.values()); + mListeners.clear(); + try { + mConnection.asBinder().unlinkToDeath(this, 0 /*flags*/); + } catch (NoSuchElementException e) { + // No need to worry about this, this means the death recipient was never linked. + } + } + listeners.forEach(Runnable::run); + } + + /** + * @return The cached Binder. + */ + public T getConnection() { + return mConnection; + } + + /** + * @return true if the cached Binder is alive at the time of calling, false otherwise. + */ + public boolean isAlive() { + return mConnection.asBinder().isBinderAlive(); + } + } + + private final BinderInterfaceFactory<T> mBinderInterfaceFactory; + private final AtomicReference<BinderDeathTracker> mCachedConnection; + + /** + * Create a new instance, which manages a cached IInterface and creates new ones using the + * provided factory when the cached IInterface dies. + * @param factory The factory used to create new Instances of the cached IInterface when it + * dies. + */ + public BinderCacheManager(BinderInterfaceFactory<T> factory) { + mBinderInterfaceFactory = factory; + mCachedConnection = new AtomicReference<>(); + } + + /** + * Get the binder node connection and add a Runnable to be run if this Binder dies. Once this + * Runnable is run, the Runnable itself is discarded and must be added again. + * <p> + * Note: There should be no assumptions here as to which Thread this Runnable is called on. If + * the Runnable should be called on a specific thread, it should be up to the caller to handle + * that in the runnable implementation. + * @param runnableKey The Key associated with this runnable so that it can be removed later + * using {@link #removeRunnable(Object)} if needed. + * @param deadRunnable The runnable that will be run if the cached Binder node dies. + * @return T if the runnable was added or {@code null} if the connection is not alive right now + * and the associated runnable was never added. + */ + public T listenOnBinder(Object runnableKey, Runnable deadRunnable) { + if (runnableKey == null || deadRunnable == null) return null; + BinderDeathTracker tracker = getTracker(); + if (tracker == null) return null; + + boolean addSucceeded = tracker.addListener(runnableKey, deadRunnable); + return addSucceeded ? tracker.getConnection() : null; + } + + /** + * @return The cached Binder node. May return null if the requested Binder node is not currently + * available. + */ + public T getBinder() { + BinderDeathTracker tracker = getTracker(); + return (tracker != null) ? tracker.getConnection() : null; + } + + /** + * Removes a previously registered runnable associated with the returned cached Binder node + * using the key it was registered with in {@link #listenOnBinder} if the runnable still exists. + * @param runnableKey The key that was used to register the Runnable earlier. + * @return The cached Binder node that the runnable used to registered to or null if the cached + * Binder node is not alive anymore. + */ + public T removeRunnable(Object runnableKey) { + if (runnableKey == null) return null; + BinderDeathTracker tracker = getTracker(); + if (tracker == null) return null; + tracker.removeListener(runnableKey); + return tracker.getConnection(); + } + + /** + * @return The BinderDeathTracker container, which contains the cached IInterface instance or + * null if it is not available right now. + */ + private BinderDeathTracker getTracker() { + return mCachedConnection.updateAndGet((oldVal) -> { + BinderDeathTracker tracker = oldVal; + // Update cache if no longer alive. BinderDied will eventually be called on the tracker, + // which will call listeners & clean up. + if (tracker == null || !tracker.isAlive()) { + T binder = mBinderInterfaceFactory.create(); + tracker = (binder != null) ? new BinderDeathTracker(binder) : null; + + } + return (tracker != null && tracker.isAlive()) ? tracker : null; + }); + } + +} diff --git a/telephony/java/android/telephony/CallForwardingInfo.java b/telephony/java/android/telephony/CallForwardingInfo.java index 7e777fae46eb..aeac36e3a5f5 100644 --- a/telephony/java/android/telephony/CallForwardingInfo.java +++ b/telephony/java/android/telephony/CallForwardingInfo.java @@ -20,6 +20,7 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; +import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; @@ -33,50 +34,14 @@ import java.util.Objects; * Defines the call forwarding information. * @hide */ +@SystemApi public final class CallForwardingInfo implements Parcelable { private static final String TAG = "CallForwardingInfo"; /** - * Indicates the call forwarding status is inactive. - * - * @hide - */ - public static final int STATUS_INACTIVE = 0; - - /** - * Indicates the call forwarding status is active. - * - * @hide - */ - public static final int STATUS_ACTIVE = 1; - - /** - * Indicates the call forwarding could not be enabled because the recipient is not on - * Fixed Dialing Number (FDN) list. - * - * @hide - */ - public static final int STATUS_FDN_CHECK_FAILURE = 2; - - /** - * Indicates the call forwarding status is with an unknown error. - * - * @hide - */ - public static final int STATUS_UNKNOWN_ERROR = 3; - - /** - * Indicates the call forwarding is not supported (e.g. called via CDMA). - * - * @hide - */ - public static final int STATUS_NOT_SUPPORTED = 4; - - /** - * Indicates the call forwarding reason is "unconditional". + * Indicates that call forwarding reason is "unconditional". * Reference: 3GPP TS 27.007 version 10.3.0 Release 10 - 7.11 Call forwarding number * and conditions +CCFC - * @hide */ public static final int REASON_UNCONDITIONAL = 0; @@ -84,7 +49,6 @@ public final class CallForwardingInfo implements Parcelable { * Indicates the call forwarding status is "busy". * Reference: 3GPP TS 27.007 version 10.3.0 Release 10 - 7.11 Call forwarding number * and conditions +CCFC - * @hide */ public static final int REASON_BUSY = 1; @@ -92,7 +56,6 @@ public final class CallForwardingInfo implements Parcelable { * Indicates the call forwarding reason is "no reply". * Reference: 3GPP TS 27.007 version 10.3.0 Release 10 - 7.11 Call forwarding number * and conditions +CCFC - * @hide */ public static final int REASON_NO_REPLY = 2; @@ -100,7 +63,6 @@ public final class CallForwardingInfo implements Parcelable { * Indicates the call forwarding reason is "not reachable". * Reference: 3GPP TS 27.007 version 10.3.0 Release 10 - 7.11 Call forwarding number * and conditions +CCFC - * @hide */ public static final int REASON_NOT_REACHABLE = 3; @@ -109,7 +71,6 @@ public final class CallForwardingInfo implements Parcelable { * simultaneously (unconditional, busy, no reply, and not reachable). * Reference: 3GPP TS 27.007 version 10.3.0 Release 10 - 7.11 Call forwarding number * and conditions +CCFC - * @hide */ public static final int REASON_ALL = 4; @@ -118,28 +79,14 @@ public final class CallForwardingInfo implements Parcelable { * forwarding reasons simultaneously (busy, no reply, and not reachable). * Reference: 3GPP TS 27.007 version 10.3.0 Release 10 - 7.11 Call forwarding number * and conditions +CCFC - * @hide */ public static final int REASON_ALL_CONDITIONAL = 5; /** - * Call forwarding function status - */ - @IntDef(prefix = { "STATUS_" }, value = { - STATUS_ACTIVE, - STATUS_INACTIVE, - STATUS_UNKNOWN_ERROR, - STATUS_NOT_SUPPORTED, - STATUS_FDN_CHECK_FAILURE - }) - @Retention(RetentionPolicy.SOURCE) - public @interface CallForwardingStatus { - } - - /** * Call forwarding reason types + * @hide */ - @IntDef(flag = true, prefix = { "REASON_" }, value = { + @IntDef(prefix = { "REASON_" }, value = { REASON_UNCONDITIONAL, REASON_BUSY, REASON_NO_REPLY, @@ -152,9 +99,9 @@ public final class CallForwardingInfo implements Parcelable { } /** - * The call forwarding status. + * Whether call forwarding is enabled for this reason. */ - private int mStatus; + private boolean mEnabled; /** * The call forwarding reason indicates the condition under which calls will be forwarded. @@ -178,39 +125,35 @@ public final class CallForwardingInfo implements Parcelable { /** * Construct a CallForwardingInfo. * - * @param status the call forwarding status + * @param enabled Whether to enable call forwarding for the reason specified + * in {@link #getReason()}. * @param reason the call forwarding reason * @param number the phone number to which calls will be forwarded * @param timeSeconds the timeout (in seconds) before the forwarding is attempted - * @hide */ - public CallForwardingInfo(@CallForwardingStatus int status, @CallForwardingReason int reason, + public CallForwardingInfo(boolean enabled, @CallForwardingReason int reason, @Nullable String number, int timeSeconds) { - mStatus = status; + mEnabled = enabled; mReason = reason; mNumber = number; mTimeSeconds = timeSeconds; } /** - * Returns the call forwarding status. + * Whether call forwarding is enabled for the reason from {@link #getReason()}. * - * @return the call forwarding status. - * - * @hide + * @return {@code true} if enabled, {@code false} otherwise. */ - public @CallForwardingStatus int getStatus() { - return mStatus; + public boolean isEnabled() { + return mEnabled; } /** * Returns the call forwarding reason. The call forwarding reason indicates the condition - * under which calls will be forwarded. For example, {@link #REASON_NO_REPLY} indicates - * that calls will be forward to {@link #getNumber()} when the user fails to answer the call. + * under which calls will be forwarded. For example, {@link #REASON_NO_REPLY} indicates + * that calls will be forwarded when the user fails to answer the call. * * @return the call forwarding reason. - * - * @hide */ public @CallForwardingReason int getReason() { return mReason; @@ -220,9 +163,7 @@ public final class CallForwardingInfo implements Parcelable { * Returns the phone number to which calls will be forwarded. * * @return the number calls will be forwarded to, or {@code null} if call forwarding - * is being disabled. - * - * @hide + * is disabled. */ @Nullable public String getNumber() { @@ -230,16 +171,14 @@ public final class CallForwardingInfo implements Parcelable { } /** - * Gets the timeout (in seconds) before the forwarding is attempted. For example, + * Gets the timeout (in seconds) before forwarding is attempted. For example, * if {@link #REASON_NO_REPLY} is the call forwarding reason, the device will wait this - * duration of time before forwarding the call to {@link #getNumber()}. + * duration of time before forwarding the call to the number returned by {@link #getNumber()}. * * Reference: 3GPP TS 27.007 version 10.3.0 Release 10 * 7.11 Call forwarding number and conditions +CCFC * * @return the timeout (in seconds) before the forwarding is attempted. - * - * @hide */ @SuppressLint("MethodNameUnits") public int getTimeoutSeconds() { @@ -257,14 +196,14 @@ public final class CallForwardingInfo implements Parcelable { @Override public void writeToParcel(Parcel out, int flags) { out.writeString(mNumber); - out.writeInt(mStatus); + out.writeBoolean(mEnabled); out.writeInt(mReason); out.writeInt(mTimeSeconds); } private CallForwardingInfo(Parcel in) { mNumber = in.readString(); - mStatus = in.readInt(); + mEnabled = in.readBoolean(); mReason = in.readInt(); mTimeSeconds = in.readInt(); } @@ -281,7 +220,7 @@ public final class CallForwardingInfo implements Parcelable { } CallForwardingInfo other = (CallForwardingInfo) o; - return mStatus == other.mStatus + return mEnabled == other.mEnabled && mNumber == other.mNumber && mReason == other.mReason && mTimeSeconds == other.mTimeSeconds; @@ -292,7 +231,7 @@ public final class CallForwardingInfo implements Parcelable { */ @Override public int hashCode() { - return Objects.hash(mStatus, mNumber, mReason, mTimeSeconds); + return Objects.hash(mEnabled, mNumber, mReason, mTimeSeconds); } public static final @NonNull Parcelable.Creator<CallForwardingInfo> CREATOR = @@ -313,7 +252,7 @@ public final class CallForwardingInfo implements Parcelable { */ @Override public String toString() { - return "[CallForwardingInfo: status=" + mStatus + return "[CallForwardingInfo: enabled=" + mEnabled + ", reason= " + mReason + ", timeSec= " + mTimeSeconds + " seconds" + ", number=" + Rlog.pii(TAG, mNumber) + "]"; diff --git a/telephony/java/android/telephony/CallQuality.java b/telephony/java/android/telephony/CallQuality.java index ff6a9bb5ba4b..fa70c33965ed 100644 --- a/telephony/java/android/telephony/CallQuality.java +++ b/telephony/java/android/telephony/CallQuality.java @@ -343,8 +343,8 @@ public final class CallQuality implements Parcelable { + " averageRoundTripTime=" + mAverageRoundTripTime + " codecType=" + mCodecType + " rtpInactivityDetected=" + mRtpInactivityDetected - + " txSilenceDetected=" + mRxSilenceDetected - + " rxSilenceDetected=" + mTxSilenceDetected + + " txSilenceDetected=" + mTxSilenceDetected + + " rxSilenceDetected=" + mRxSilenceDetected + "}"; } diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 11af8e05955d..1e9e0bd5a343 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -17,6 +17,7 @@ package android.telephony; import android.Manifest; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; @@ -26,11 +27,19 @@ import android.annotation.SystemService; import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Context; +import android.net.ipsec.ike.SaProposal; +import android.os.Build; import android.os.PersistableBundle; import android.os.RemoteException; import android.service.carrier.CarrierService; import android.telecom.TelecomManager; +import android.telephony.gba.TlsParams; +import android.telephony.gba.UaSecurityProtocolIdentifier; import android.telephony.ims.ImsReasonInfo; +import android.telephony.ims.ImsRegistrationAttributes; +import android.telephony.ims.ImsSsData; +import android.telephony.ims.feature.MmTelFeature; +import android.telephony.ims.feature.RcsFeature; import com.android.internal.telephony.ICarrierConfigLoader; import com.android.telephony.Rlog; @@ -51,9 +60,13 @@ public class CarrierConfigManager { public static final String EXTRA_SLOT_INDEX = "android.telephony.extra.SLOT_INDEX"; /** - * Extra included in {@link #ACTION_CARRIER_CONFIG_CHANGED} to indicate whether this is a - * rebroadcast on unlock. Defaults to {@code false} if not specified. - * @hide + * {@link #ACTION_CARRIER_CONFIG_CHANGED} is broadcast once on device bootup and then again when + * the device is unlocked. Direct-Boot-aware applications may use the first broadcast as an + * early signal that the carrier config has been loaded, but other applications will only + * receive the second broadcast, when the device is unlocked. + * + * This extra is included in {@link #ACTION_CARRIER_CONFIG_CHANGED} to indicate whether this is + * a rebroadcast on unlock. */ public static final String EXTRA_REBROADCAST_ON_UNLOCK = "android.telephony.extra.REBROADCAST_ON_UNLOCK"; @@ -65,6 +78,54 @@ public class CarrierConfigManager { public static final String EXTRA_SUBSCRIPTION_INDEX = SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX; + /** + * Service class flag if no specific service class is specified. + * Reference: 3GPP TS 27.007 Section 7.4 Facility lock +CLCK + */ + public static final int SERVICE_CLASS_NONE = ImsSsData.SERVICE_CLASS_NONE; + + /** + * Service class flag for voice telephony. + * Reference: 3GPP TS 27.007 Section 7.4 Facility lock +CLCK + */ + public static final int SERVICE_CLASS_VOICE = ImsSsData.SERVICE_CLASS_VOICE; + + /** + * Only send USSD over IMS while CS is out of service, otherwise send USSD over CS. + * {@link #KEY_CARRIER_USSD_METHOD_INT} + */ + public static final int USSD_OVER_CS_PREFERRED = 0; + + /** + * Send USSD over IMS or CS while IMS is out of service or silent redial over CS if needed. + * {@link #KEY_CARRIER_USSD_METHOD_INT} + */ + public static final int USSD_OVER_IMS_PREFERRED = 1; + + /** + * Only send USSD over CS. + * {@link #KEY_CARRIER_USSD_METHOD_INT} + */ + public static final int USSD_OVER_CS_ONLY = 2; + + /** + * Only send USSD over IMS and disallow silent redial over CS. + * {@link #KEY_CARRIER_USSD_METHOD_INT} + */ + public static final int USSD_OVER_IMS_ONLY = 3; + + /** + * Indicates CARRIER_NR_AVAILABILITY_NSA determine that the carrier enable the non-standalone + * (NSA) mode of 5G NR. + */ + public static final int CARRIER_NR_AVAILABILITY_NSA = 1; + + /** + * Indicates CARRIER_NR_AVAILABILITY_SA determine that the carrier enable the standalone (SA) + * mode of 5G NR. + */ + public static final int CARRIER_NR_AVAILABILITY_SA = 2; + private final Context mContext; /** @@ -211,6 +272,18 @@ public class CarrierConfigManager { "call_barring_supports_deactivate_all_bool"; /** + * Specifies the service class for call barring service. Default value is + * {@link #SERVICE_CLASS_VOICE}. + * The value set as below: + * <ul> + * <li>0: {@link #SERVICE_CLASS_NONE}</li> + * <li>1: {@link #SERVICE_CLASS_VOICE}</li> + * </ul> + */ + public static final String KEY_CALL_BARRING_DEFAULT_SERVICE_CLASS_INT = + "call_barring_default_service_class_int"; + + /** * Flag indicating whether the Phone app should ignore EVENT_SIM_NETWORK_LOCKED * events from the Sim. * If true, this will prevent the IccNetworkDepersonalizationPanel from being shown, and @@ -353,6 +426,34 @@ public class CarrierConfigManager { "only_auto_select_in_home_network"; /** + * Flag indicating whether to show single operator row in the choose network setting. + * + * The device configuration value {@code config_enableNewAutoSelectNetworkUI} ultimately + * controls whether this carrier configuration option is used. Where + * {@code config_enableNewAutoSelectNetworkUI} is false, the value of the + * {@link #KEY_SHOW_SINGLE_OPERATOR_ROW_IN_CHOOSE_NETWORK_SETTING_BOOL} carrier configuration + * option is ignored. + * + * If {@code true}, default value, merge the duplicate networks which with the same plmn, keep + * the one that with the higher signal strength level. + * If {@code false}, show all operators without merging. + * @hide + */ + public static final String KEY_SHOW_SINGLE_OPERATOR_ROW_IN_CHOOSE_NETWORK_SETTING_BOOL = + "show_single_operator_row_in_choose_network_setting_bool"; + + /** + * Flag indicating whether to display SPN as network name for home network in choose + * network setting. + * + * If {@code true}, display SPN as network name in choose network setting. + * If {@code false}, display PLMN in choose network setting. + * @hide + */ + public static final String KEY_SHOW_SPN_FOR_HOME_IN_CHOOSE_NETWORK_SETTING_BOOL = + "show_spn_for_home_in_choose_network_setting_bool"; + + /** * Control whether users receive a simplified network settings UI and improved network * selection. */ @@ -530,6 +631,20 @@ public class CarrierConfigManager { public static final String KEY_CARRIER_VT_AVAILABLE_BOOL = "carrier_vt_available_bool"; /** + * Specify the method of selection for UE sending USSD requests. The default value is + * {@link #USSD_OVER_CS_PREFERRED}. + * <p> Available options: + * <ul> + * <li>0: {@link #USSD_OVER_CS_PREFERRED} </li> + * <li>1: {@link #USSD_OVER_IMS_PREFERRED} </li> + * <li>2: {@link #USSD_OVER_CS_ONLY} </li> + * <li>3: {@link #USSD_OVER_IMS_ONLY} </li> + * </ul> + */ + public static final String KEY_CARRIER_USSD_METHOD_INT = + "carrier_ussd_method_int"; + + /** * Flag specifying whether to show an alert dialog for 5G disable when the user disables VoLTE. * By default this value is {@code false}. * @@ -646,6 +761,14 @@ public class CarrierConfigManager { public static final String KEY_CARRIER_WFC_IMS_AVAILABLE_BOOL = "carrier_wfc_ims_available_bool"; /** + * Flag specifying whether Cross SIM over IMS should be available for carrier. + * When {@code false} the carrier does not support cross SIM calling. + * When {@code true} the carrier does support cross sim calling, where available + */ + public static final String KEY_CARRIER_CROSS_SIM_IMS_AVAILABLE_BOOL = + "carrier_cross_sim_ims_available_bool"; + + /** * Specifies a map from dialstrings to replacements for roaming network service numbers which * cannot be replaced on the carrier side. * <p> @@ -743,7 +866,7 @@ public class CarrierConfigManager { * {@link #KEY_CARRIER_UT_PROVISIONING_REQUIRED_BOOL}). If false, this device will fallback to * circuit switch for supplementary services and will disable this capability for IMS entirely. * - * The default value for this key is {@code true}. + * The default value for this key is {@code false}. */ public static final String KEY_CARRIER_SUPPORTS_SS_OVER_UT_BOOL = "carrier_supports_ss_over_ut_bool"; @@ -1056,6 +1179,15 @@ public class CarrierConfigManager { "show_signal_strength_in_sim_status_bool"; /** + * Flag specifying if we should interpret all signal strength as one bar higher + * This is a replacement for the former resource config_inflateSignalStrength + * The default value is false. + * @hide + */ + public static final String KEY_INFLATE_SIGNAL_STRENGTH_BOOL = + "inflate_signal_strength_bool"; + + /** * Flag specifying whether an additional (client initiated) intent needs to be sent on System * update */ @@ -1127,15 +1259,14 @@ public class CarrierConfigManager { /** * Determines whether adhoc conference calls are supported by a carrier. When {@code true}, * adhoc conference calling is supported, {@code false otherwise}. - * @hide */ public static final String KEY_SUPPORT_ADHOC_CONFERENCE_CALLS_BOOL = "support_adhoc_conference_calls_bool"; /** - * Determines whether conference participants can be added to existing call. When {@code true}, + * Determines whether conference participants can be added to existing call to form an adhoc + * conference call (in contrast to merging calls to form a conference). When {@code true}, * adding conference participants to existing call is supported, {@code false otherwise}. - * @hide */ public static final String KEY_SUPPORT_ADD_CONFERENCE_PARTICIPANTS_BOOL = "support_add_conference_participants_bool"; @@ -1196,6 +1327,38 @@ public class CarrierConfigManager { "support_ims_conference_event_package_on_peer_bool"; /** + * Indicates whether the carrier supports the use of RFC8285 compliant RTP header extensions for + * the purpose of device to device communication while in a call. + * <p> + * See also {@link #KEY_SUPPORTS_SDP_NEGOTIATION_OF_D2D_RTP_HEADER_EXTENSIONS_BOOL}. + */ + public static final String KEY_SUPPORTS_DEVICE_TO_DEVICE_COMMUNICATION_USING_RTP_BOOL = + "supports_device_to_device_communication_using_rtp_bool"; + + /** + * Indicates whether the carrier supports the negotiations of RFC8285 compliant RTP header + * extensions supported on a call during the Session Description Protocol (SDP). This option + * is only used when {@link #KEY_SUPPORTS_DEVICE_TO_DEVICE_COMMUNICATION_USING_RTP_BOOL} is + * {@code true}. + * <p> + * When {@code true}, the RTP header extensions the platform uses for device to device + * communication will be offered to the remote end during the SDP negotiation process. + * When {@code false}, the RTP header extensions will not be negotiated during the SDP + * negotiation process and the platform will send RTP header extensions without prior + * negotiation if {@link #KEY_SUPPORTS_DEVICE_TO_DEVICE_COMMUNICATION_USING_RTP_BOOL} is + * {@code true}. + */ + public static final String KEY_SUPPORTS_SDP_NEGOTIATION_OF_D2D_RTP_HEADER_EXTENSIONS_BOOL = + "supports_sdp_negotiation_of_d2d_rtp_header_extensions_bool"; + + /** + * Indicates whether the carrier supports the use of DTMF digits A-D for the purpose of device + * to device communication while in a call. + */ + public static final String KEY_SUPPORTS_DEVICE_TO_DEVICE_COMMUNICATION_USING_DTMF_BOOL = + "supports_device_to_device_communication_using_dtmf_bool"; + + /** * Determines whether High Definition audio property is displayed in the dialer UI. * If {@code false}, remove the HD audio property from the connection so that HD audio related * UI is not displayed. If {@code true}, keep HD audio property as it is configured. @@ -1392,6 +1555,29 @@ public class CarrierConfigManager { "wfc_carrier_name_override_by_pnn_bool"; /** + * Specifies SPN format of displaying carrier name only. + * + */ + public static final int CROSS_SIM_SPN_FORMAT_CARRIER_NAME_ONLY = 0; + + /** + * Specifies SPN format of displaying carrier name along with "Cross-SIM calling". + */ + public static final int CROSS_SIM_SPN_FORMAT_CARRIER_NAME_WITH_BRANDING = 1; + + /** + * Indexes of SPN format strings in crossSimSpnFormats. + * + * <p>Available options are: + * <ul> + * <li> {@link #CROSS_SIM_SPN_FORMAT_CARRIER_NAME_ONLY}: %s</li> + * <li> {@link #CROSS_SIM_SPN_FORMAT_CARRIER_NAME_WITH_BRANDING}: %s Cross-SIM Calling</li> + * </ul> + * %s will be filled with carrier name + */ + public static final String KEY_CROSS_SIM_SPN_FORMAT_INT = "cross_sim_spn_format_int"; + + /** * Override the SPN Display Condition 2 integer bits (lsb). B2, B1 is the last two bits of the * spn display condition coding. * @@ -1551,7 +1737,13 @@ public class CarrierConfigManager { * Configs used for APN setup. */ public static final class Apn { - /** Prefix of all Apn.KEY_* constants. */ + /** + * Prefix of all Apn.KEY_* constants. + * + * @deprecated Since KEY_PREFIX is unnecessary to public, it will modify to private + * next android generation. + */ + @Deprecated public static final String KEY_PREFIX = "apn."; /** IPv4 internet protocol */ @@ -1641,6 +1833,15 @@ public class CarrierConfigManager { "hide_lte_plus_data_icon_bool"; /** + * The combined channel bandwidth threshold (non-inclusive) in KHz required to display the + * LTE+ data icon. It is 20000 by default, meaning the LTE+ icon will be shown if the device is + * using carrier aggregation and the combined channel bandwidth is strictly greater than 20 MHz. + * @hide + */ + public static final String KEY_LTE_PLUS_THRESHOLD_BANDWIDTH_KHZ_INT = + "lte_plus_threshold_bandwidth_khz_int"; + + /** * The string is used to filter redundant string from PLMN Network Name that's supplied by * specific carrier. * @@ -1665,10 +1866,20 @@ public class CarrierConfigManager { "show_precise_failed_cause_bool"; /** - * Boolean to decide whether NR is enabled. - * @hide + * A list of carrier nr availability is used to determine whether the carrier enable the + * non-standalone (NSA) mode of 5G NR, standalone (SA) mode of 5G NR + * + * <p> The value of list is + * {@link #CARRIER_NR_AVAILABILITY_NSA}, or {@link #CARRIER_NR_AVAILABILITY_SA}. + * + * <p> For example, if both NSA and SA are used, the list value is { + * {@link #CARRIER_NR_AVAILABILITY_NSA},{@link #CARRIER_NR_AVAILABILITY_SA}}. + * If the carrier doesn't support 5G NR, the value is the empty array. + * If the key is invalid or not configured, the default value { + * {@link #CARRIER_NR_AVAILABILITY_NSA},{@link #CARRIER_NR_AVAILABILITY_SA}} will apply. */ - public static final String KEY_NR_ENABLED_BOOL = "nr_enabled_bool"; + public static final String KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY = + "carrier_nr_availabilities_int_array"; /** * Boolean to decide whether LTE is enabled. @@ -1772,9 +1983,8 @@ public class CarrierConfigManager { * "APN_1, ERROR_CODE_1 : CARRIER_ACTION_IDX_1, CARRIER_ACTION_IDX_2...", * "APN_1, ERROR_CODE_2 : CARRIER_ACTION_IDX_1 " * } - * Where {@code APN_1} is a string defined in - * com.android.internal.telephony.PhoneConstants - * Example: "default" + * Where {@code APN_1} is an integer defined in {@link android.telephony.data.ApnSetting} + * (e.g. {@link android.telephony.data.ApnSetting#TYPE_DEFAULT} * * {@code ERROR_CODE_1} is an integer defined in android.telephony.DataFailCause * Example: @@ -1921,15 +2131,29 @@ public class CarrierConfigManager { "allow_hold_call_during_emergency_bool"; /** - * Flag indicating whether the carrier supports RCS presence indication for - * User Capability Exchange (UCE). When presence is supported, the device should use the + * Flag indicating whether or not the carrier supports the periodic exchange of phone numbers + * in the user's address book with the carrier's presence server in order to retrieve the RCS + * capabilities for each contact used in the RCS User Capability Exchange (UCE) procedure. See + * RCC.71, section 3 for more information. + * <p> + * The flag {@link Ims#KEY_ENABLE_PRESENCE_PUBLISH_BOOL} must also be enabled if this flag is + * enabled, as sending a periodic SIP PUBLISH with this device's RCS capabilities is a + * requirement for capability exchange to begin. + * <p> + * When presence is supported, the device should use the * {@link android.provider.ContactsContract.Data#CARRIER_PRESENCE} bit mask and set the * {@link android.provider.ContactsContract.Data#CARRIER_PRESENCE_VT_CAPABLE} bit to indicate * whether each contact supports video calling. The UI is made aware that presence is enabled * via {@link android.telecom.PhoneAccount#CAPABILITY_VIDEO_CALLING_RELIES_ON_PRESENCE} * and can choose to hide or show the video calling icon based on whether a contact supports * video. + * + * @deprecated No longer used in framework code, however it may still be used by applications + * that have not updated their code. This config should still be set to {@code true} if + * {@link Ims#KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL} is set to {@code true} and + * {@link Ims#KEY_ENABLE_PRESENCE_CAPABILITY_EXCHANGE_BOOL} is set to {@code true}. */ + @Deprecated public static final String KEY_USE_RCS_PRESENCE_BOOL = "use_rcs_presence_bool"; /** @@ -2246,7 +2470,8 @@ public class CarrierConfigManager { "show_blocking_pay_phone_option_bool"; /** - * Flag specifying whether the carrier will use the WFC home network mode in roaming network. + * Flag specifying whether the carrier will use the + * WFC home network mode in roaming network. * {@code false} - roaming preference can be selected separately from the home preference. * {@code true} - roaming preference is the same as home preference and * {@link #KEY_CARRIER_DEFAULT_WFC_IMS_MODE_INT} is used as the default value. @@ -2344,6 +2569,16 @@ public class CarrierConfigManager { "call_forwarding_blocks_while_roaming_string_array"; /** + * Call forwarding number prefixes defined by {@link + * #KEY_CALL_FORWARDING_BLOCKS_WHILE_ROAMING_STRING_ARRAY} which will be allowed while the + * device is reporting that it is roaming and IMS is registered over LTE or Wi-Fi. + * By default this value is {@code true}. + * @hide + */ + public static final String KEY_SUPPORT_IMS_CALL_FORWARDING_WHILE_ROAMING_BOOL = + "support_ims_call_forwarding_while_roaming_bool"; + + /** * The day of the month (1-31) on which the data cycle rolls over. * <p> * If the current month does not have this day, the cycle will roll over at @@ -2511,15 +2746,15 @@ public class CarrierConfigManager { /** * List of 4 customized 5G SS reference signal received quality (SSRSRQ) thresholds. * <p> - * Reference: 3GPP TS 38.215 + * Reference: 3GPP TS 38.215; 3GPP TS 38.133 section 10 * <p> - * 4 threshold integers must be within the boundaries [-20 dB, -3 dB], and the levels are: + * 4 threshold integers must be within the boundaries [-43 dB, 20 dB], and the levels are: * <UL> - * <LI>"NONE: [-20, threshold1]"</LI> + * <LI>"NONE: [-43, threshold1]"</LI> * <LI>"POOR: (threshold1, threshold2]"</LI> * <LI>"MODERATE: (threshold2, threshold3]"</LI> * <LI>"GOOD: (threshold3, threshold4]"</LI> - * <LI>"EXCELLENT: (threshold4, -3]"</LI> + * <LI>"EXCELLENT: (threshold4, 20]"</LI> * </UL> * <p> * This key is considered invalid if the format is violated. If the key is invalid or @@ -2605,7 +2840,7 @@ public class CarrierConfigManager { * Key identifying if voice call barring notification is required to be shown to the user. * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public static final String KEY_DISABLE_VOICE_BARRING_NOTIFICATION_BOOL = "disable_voice_barring_notification_bool"; @@ -2641,6 +2876,30 @@ public class CarrierConfigManager { public static final String IMSI_KEY_DOWNLOAD_URL_STRING = "imsi_key_download_url_string"; /** + * String representation of a carrier's public key used for IMSI encryption for ePDG. If this + * is provided, the device will use it as a fallback when no key exists on device, but the key + * download will still initiate. + * Example string: + * "-----BEGIN CERTIFICATE-----\nabcde12345abcde12345abcde12345abcde1234 + * 5abcde12345abcde12345\nabcde12345abcde12345abcde12345abcde12345a\n-----END CERTIFICATE-----" + * @hide + */ + public static final String IMSI_CARRIER_PUBLIC_KEY_EPDG_STRING = + "imsi_carrier_public_key_epdg_string"; + + /** + * String representation of a carrier's public key used for IMSI encryption for WLAN. If this + * is provided, the device will use it as a fallback when no key exists on device, but the key + * download will still initiate. + * Example string: + * "-----BEGIN CERTIFICATE-----\nabcde12345abcde12345abcde12345abcde1234 + * 5abcde12345abcde12345\nabcde12345abcde12345abcde12345abcde12345a\n-----END CERTIFICATE-----" + * @hide + */ + public static final String IMSI_CARRIER_PUBLIC_KEY_WLAN_STRING = + "imsi_carrier_public_key_wlan_string"; + + /** * Identifies if the key is available for WLAN or EPDG or both. The value is a bitmask. * 0 indicates that neither EPDG or WLAN is enabled. * 1 indicates that key type TelephonyManager#KEY_TYPE_EPDG is enabled. @@ -2687,32 +2946,39 @@ public class CarrierConfigManager { /** * Indicates if the carrier supports auto-upgrading a call to RTT when receiving a call from a * RTT-supported device. - * @hide */ public static final String KEY_RTT_AUTO_UPGRADE_BOOL = "rtt_auto_upgrade_bool"; /** * Indicates if the carrier supports RTT during a video call. - * @hide */ public static final String KEY_RTT_SUPPORTED_FOR_VT_BOOL = "rtt_supported_for_vt_bool"; /** + * Indicates if the carrier supports upgrading a call that was previously an RTT call to VT. + */ + public static final String KEY_VT_UPGRADE_SUPPORTED_FOR_DOWNGRADED_RTT_CALL_BOOL = + "vt_upgrade_supported_for_downgraded_rtt_call"; + + /** + * Indicates if the carrier supports upgrading a call that was previously a VT call to RTT. + */ + public static final String KEY_RTT_UPGRADE_SUPPORTED_FOR_DOWNGRADED_VT_CALL_BOOL = + "rtt_upgrade_supported_for_downgraded_vt_call"; + + /** * Indicates if the carrier supports upgrading a voice call to an RTT call during the call. - * @hide */ public static final String KEY_RTT_UPGRADE_SUPPORTED_BOOL = "rtt_upgrade_supported_bool"; /** * Indicates if the carrier supports downgrading a RTT call to a voice call during the call. - * @hide */ public static final String KEY_RTT_DOWNGRADE_SUPPORTED_BOOL = "rtt_downgrade_supported_bool"; /** * Indicates if the TTY HCO and VCO options should be hidden in the accessibility menu * if the device is capable of RTT. - * @hide */ public static final String KEY_HIDE_TTY_HCO_VCO_WITH_RTT_BOOL = "hide_tty_hco_vco_with_rtt"; @@ -3032,6 +3298,14 @@ public class CarrierConfigManager { public static final String KEY_USE_CALLER_ID_USSD_BOOL = "use_caller_id_ussd_bool"; /** + * Call waiting uses USSD command without SS command. + * When {@code true}, the call waiting query/set by ussd command. + * When {@code false}, doesn't use USSD to query/set call waiting. + * @hide + */ + public static final String KEY_USE_CALL_WAITING_USSD_BOOL = "use_call_waiting_ussd_bool"; + + /** * Specifies the service class for call waiting service. * Default value is * {@link com.android.internal.telephony.CommandsInterface#SERVICE_CLASS_VOICE}. @@ -3178,6 +3452,42 @@ public class CarrierConfigManager { "5g_icon_display_secondary_grace_period_string"; /** + * Whether device reset all of NR timers when device camped on a network that haven't 5G + * capability and RRC currently in IDLE state. + * + * The default value is false; + * + * @hide + */ + public static final String KEY_NR_TIMERS_RESET_IF_NON_ENDC_AND_RRC_IDLE_BOOL = + "nr_timers_reset_if_non_endc_and_rrc_idle_bool"; + + /** + * A list of additional NR advanced band would map to + * {@link TelephonyDisplayInfo#OVERRIDE_NETWORK_TYPE_NR_ADVANCED} when the device is on that + * band. + * + * @hide + */ + public static final String KEY_ADDITIONAL_NR_ADVANCED_BANDS_INT_ARRAY = + "additional_nr_advanced_bands_int_array"; + + /** + * This configuration allows the framework to control the NR advanced capable by protocol + * configuration options(PCO). + * + * If this config is 0, then the nr advanced capable is enabled. + * If this config is not 0 and PCO container with this config's address is 1, then the nr + * advanced capable is enabled. + * If this config is not 0 and PCO container with this config's address is 0, then the nr + * advanced capable is disabled. + * + * @hide + */ + public static final String KEY_NR_ADVANCED_CAPABLE_PCO_ID_INT = + "nr_advanced_capable_pco_id_int"; + + /** * Controls time in milliseconds until DcTracker reevaluates 5G connection state. * @hide */ @@ -3415,6 +3725,142 @@ public class CarrierConfigManager { public static final String ENABLE_EAP_METHOD_PREFIX_BOOL = "enable_eap_method_prefix_bool"; /** + * Indicates that GBA_ME should be used for GBA authentication, as defined in 3GPP TS 33.220. + * @hide + */ + @SystemApi + public static final int GBA_ME = 1; + + /** + * Indicates that GBA_U should be used for GBA authentication, as defined in 3GPP TS 33.220. + * @hide + */ + @SystemApi + public static final int GBA_U = 2; + + /** + * Indicates that GBA_Digest should be used for GBA authentication, as defined + * in 3GPP TS 33.220. + * @hide + */ + @SystemApi + public static final int GBA_DIGEST = 3; + + /** + * An integer representing the GBA mode to use for requesting credentials + * via {@link TelephonyManager#bootstrapAuthenticationRequest}. + * + * One of {@link #GBA_ME}, {@link #GBA_U}, or {@link #GBA_DIGEST}. + * @hide + */ + @SystemApi + public static final String KEY_GBA_MODE_INT = "gba_mode_int"; + + /** + * An integer representing the organization code to be used when building the + * {@link UaSecurityProtocolIdentifier} used when requesting GBA authentication. + * + * See the {@code ORG_} constants in {@link UaSecurityProtocolIdentifier}. + * @hide + */ + @SystemApi + public static final String KEY_GBA_UA_SECURITY_ORGANIZATION_INT = + "gba_ua_security_organization_int"; + + /** + * An integer representing the security protocol to be used when building the + * {@link UaSecurityProtocolIdentifier} used when requesting GBA authentication. + * + * See the {@code UA_SECURITY_PROTOCOL_} constants in {@link UaSecurityProtocolIdentifier}. + * @hide + */ + @SystemApi + public static final String KEY_GBA_UA_SECURITY_PROTOCOL_INT = + "gba_ua_security_protocol_int"; + + /** + * An integer representing the cipher suite to be used when building the + * {@link UaSecurityProtocolIdentifier} used when requesting GBA authentication. + * + * See the {@code TLS_} constants in {@link android.telephony.gba.TlsParams}. + * @hide + */ + @SystemApi + public static final String KEY_GBA_UA_TLS_CIPHER_SUITE_INT = + "gba_ua_tls_cipher_suite_int"; + + /** + * Configs used by ImsServiceEntitlement. + */ + public static final class ImsServiceEntitlement { + private ImsServiceEntitlement() {} + + /** Prefix of all ImsServiceEntitlement.KEY_* constants. */ + public static final String KEY_PREFIX = "imsserviceentitlement."; + + /** + * The address of the entitlement configuration server. + * + * Reference: GSMA TS.43-v5, section 2.1 Default Entitlement Configuration Server. + */ + public static final String KEY_ENTITLEMENT_SERVER_URL_STRING = + KEY_PREFIX + "entitlement_server_url_string"; + + /** + * For some carriers, end-users may be presented with a web portal of the carrier before + * being allowed to use the VoWiFi service. + * To support this feature, the app hosts a {@link android.webkit.WebView} in the foreground + * VoWiFi entitlement configuration flow to show the web portal. + * + * {@code true} - show the VoWiFi portal in a webview. + * + * Note: this is effective only if the {@link #KEY_WFC_EMERGENCY_ADDRESS_CARRIER_APP_STRING} + * is set to this app. + * + * Reference: GSMA TS.43-v5, section 3, VoWiFi entitlement configuration. + */ + public static final String KEY_SHOW_VOWIFI_WEBVIEW_BOOL = + KEY_PREFIX + "show_vowifi_webview_bool"; + + /** + * For some carriers, the network is not provisioned by default to support + * IMS (VoLTE/VoWiFi/SMSoIP) service for all end users. Some type of network-side + * provisioning must then take place before offering the IMS service to the end-user. + * + * {@code true} - need this ImsServiceEntitlement app to do IMS (VoLTE/VoWiFi/SMSoIP) + * provisioning in the background before offering the IMS service to the end-user. + * + * Note: this is effective only if the carrier needs IMS provisioning, i.e. + * {@link #KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL} is set to true. + * + * Reference: GSMA TS.43-v5, section 3 - 5, VoWiFi/VoLTE/SMSoIP entitlement configuration. + */ + public static final String KEY_IMS_PROVISIONING_BOOL = KEY_PREFIX + "ims_provisioning_bool"; + + /** + * The FCM sender ID for the carrier. + * Used to trigger a carrier network requested entitlement configuration + * via Firebase Cloud Messaging (FCM). Do not set if the carrier doesn't use FCM for network + * requested entitlement configuration. + * + * Reference: GSMA TS.43-v5, section 2.4, Network Requested Entitlement Configuration. + * + * @see <a href="https://firebase.google.com/docs/cloud-messaging/concept-options#senderid"> + * About FCM messages - Credentials</a> + */ + public static final String KEY_FCM_SENDER_ID_STRING = KEY_PREFIX + "fcm_sender_id_string"; + + private static PersistableBundle getDefaults() { + PersistableBundle defaults = new PersistableBundle(); + defaults.putString(KEY_ENTITLEMENT_SERVER_URL_STRING, ""); + defaults.putString(KEY_FCM_SENDER_ID_STRING, ""); + defaults.putBoolean(KEY_SHOW_VOWIFI_WEBVIEW_BOOL, false); + defaults.putBoolean(KEY_IMS_PROVISIONING_BOOL, false); + return defaults; + } + } + + /** * GPS configs. See the GNSS HAL documentation for more details. */ public static final class Gps { @@ -3696,6 +4142,22 @@ public class CarrierConfigManager { "is_opportunistic_subscription_bool"; /** + * The flatten string {@link android.content.ComponentName componentName} of carrier + * provisioning app receiver. + * + * <p> + * The RadioInfo activity(*#*#INFO#*#*) will broadcast an intent to this receiver when the + * "Carrier Provisioning Info" or "Trigger Carrier Provisioning" button clicked. + * + * <p> + * e.g, com.google.android.carrierPackageName/.CarrierReceiverName + * + * @hide + */ + public static final String KEY_CARRIER_PROVISIONING_APP_STRING = + "carrier_provisioning_app_string"; + + /** * Configs used by the IMS stack. */ public static final class Ims { @@ -3708,11 +4170,540 @@ public class CarrierConfigManager { public static final String KEY_WIFI_OFF_DEFERRING_TIME_MILLIS_INT = KEY_PREFIX + "wifi_off_deferring_time_millis_int"; + /** + * A boolean flag specifying whether or not this carrier requires one IMS registration for + * all IMS services (MMTEL and RCS). + * <p> + * If set to {@code true}, the IMS Service must use one IMS registration for all IMS + * services. If set to {@code false}, IMS services may use separate IMS registrations for + * MMTEL and RCS. + * <p> + * The default value for this configuration is {@code false}. + * @see android.telephony.ims.SipDelegateManager + */ + public static final String KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL = + KEY_PREFIX + "ims_single_registration_required_bool"; + + /** + * A boolean flag specifying whether or not this carrier supports the device notifying the + * network of its RCS capabilities using the SIP PUBLISH procedure defined for User + * Capability Exchange (UCE). See RCC.71, section 3 for more information. + * <p> + * If this key's value is set to false, the procedure for RCS contact capability exchange + * via SIP SUBSCRIBE/NOTIFY will also be disabled internally, and + * {@link Ims#KEY_ENABLE_PRESENCE_PUBLISH_BOOL} must also be set to false to ensure + * apps do not improperly think that capability exchange via SIP PUBLISH is enabled. + * <p> The default value for this key is {@code false}. + */ + public static final String KEY_ENABLE_PRESENCE_PUBLISH_BOOL = + KEY_PREFIX + "enable_presence_publish_bool"; + + /** + * Each string in this array contains a mapping between the service-id and version portion + * of the service-description element and the associated IMS feature tag(s) that are + * associated with each element (see RCC.07 Table 7). + * <p> + * Each string contains 3 parts, which define the mapping between service-description and + * feature tag(s) that must be present in the IMS REGISTER for the RCS service to be + * published as part of the RCS PUBLISH procedure: + * [service-id]|[version]|[desc]|[feature_tag];[feature_tag];... + * <ul> + * <li>[service-id]: the service-id element associated with the RCS capability.</li> + * <li>[version]: The version element associated with that service-id</li> + * <li>[desc]: The optional desecription element associated with that service-id</li> + * <li>[feature_tag];[feature_tag]: The list of all feature tags associated with this + * capability that MUST ALL be present in the IMS registration for this this + * capability to be published to the network.</li> + * </ul> + * <p> + * Features managed by the framework will be considered capable when the ImsService reports + * that those services are capable via the + * {@link MmTelFeature#notifyCapabilitiesStatusChanged(MmTelFeature.MmTelCapabilities)} or + * {@link RcsFeature#notifyCapabilitiesStatusChanged(RcsFeature.RcsImsCapabilities)} APIs. + * For RCS services not managed by the framework, the capability of these services are + * determined by looking at the feature tags associated with the IMS registration using the + * {@link ImsRegistrationAttributes} API and mapping them to the service-description map. + * <p> + * The framework contains a default value of this key, which is based off of RCC.07 + * specification. Capabilities based of carrier extensions may be added to this list on a + * carrier-by-carrier basis as required in order to support additional services in the + * PUBLISH. If this list contains a service-id and version that overlaps with the default, + * it will override the framework default. + * @hide + */ + public static final String KEY_PUBLISH_SERVICE_DESC_FEATURE_TAG_MAP_OVERRIDE_STRING_ARRAY = + KEY_PREFIX + "publish_service_desc_feature_tag_map_override_string_array"; + + /** + * Flag indicating whether or not this carrier supports the exchange of phone numbers with + * the carrier's RCS presence server in order to retrieve the RCS capabilities of requested + * contacts used in the RCS User Capability Exchange (UCE) procedure. See RCC.71, section 3 + * for more information. + * <p> + * When presence is supported, the device uses the SIP SUBSCRIBE/NOTIFY procedure internally + * to retrieve the requested RCS capabilities. See + * {@link android.telephony.ims.RcsUceAdapter} for more information on how RCS capabilities + * can be retrieved from the carrier's network. + */ + public static final String KEY_ENABLE_PRESENCE_CAPABILITY_EXCHANGE_BOOL = + KEY_PREFIX + "enable_presence_capability_exchange_bool"; + + + /** + * Flag indicating whether or not the carrier expects the RCS UCE service to periodically + * refresh the RCS capabilities cache of the user's contacts as well as request the + * capabilities of call contacts when the SIM card is first inserted or when a new contact + * is added, removed, or modified. This corresponds to the RCC.07 A.19 + * "DISABLE INITIAL ADDRESS BOOK SCAN" parameter. + * <p> + * If this flag is disabled, the capabilities cache will not be refreshed internally at all + * and will only be updated if the cached capabilities are stale when an application + * requests them. + */ + public static final String KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL = + KEY_PREFIX + "rcs_bulk_capability_exchange_bool"; + + /** + * Flag indicating whether or not the carrier supports capability exchange with a list of + * contacts. When {@code true}, the device will batch together multiple requests and + * construct a RLMI document in the SIP SUBSCRIBE request (see RFC 4662). If {@code false}, + * the request will be split up into one SIP SUBSCRIBE request per contact. + */ + public static final String KEY_ENABLE_PRESENCE_GROUP_SUBSCRIBE_BOOL = + KEY_PREFIX + "enable_presence_group_subscribe_bool"; + + /** + * An integer key associated with the period of time in seconds the non-rcs capability + * information of each contact is cached on the device. + * <p> + * The rcs capability cache expiration sec is managed by + * {@code android.telephony.ims.ProvisioningManager} but non-rcs capability is managed by + * {@link CarrierConfigManager} since non-rcs capability will be provided via ACS or carrier + * config. + * <p> + * The default value is 2592000 secs (30 days), see RCC.07 Annex A.1.9. + */ + public static final String KEY_NON_RCS_CAPABILITIES_CACHE_EXPIRATION_SEC_INT = + KEY_PREFIX + "non_rcs_capabilities_cache_expiration_sec_int"; + + /** + * Specifies the RCS feature tag allowed for the carrier. + * + * <p>The values refer to RCC.07 2.4.4. + */ + public static final String KEY_RCS_FEATURE_TAG_ALLOWED_STRING_ARRAY = + KEY_PREFIX + "rcs_feature_tag_allowed_string_array"; + + /** + * Flag indicating whether or not carrier forbids device send the RCS request when the + * device receive the network response with the SIP code 489 BAD EVENT. + * <p> + * The default value for this key is {@code false}. + * @hide + */ + public static final String KEY_RCS_REQUEST_FORBIDDEN_BY_SIP_489_BOOL = + KEY_PREFIX + "rcs_request_forbidden_by_sip_489_bool"; + + /** + * Indicates the interval that SUBSCRIBE requests from applications will be retried at when + * the carrier network has responded to a previous request with a forbidden error. + * <p> + * The default value for this key is 20 minutes. + * @hide + */ + public static final String KEY_RCS_REQUEST_RETRY_INTERVAL_MILLIS_LONG = + KEY_PREFIX + "rcs_request_retry_interval_millis_long"; + private Ims() {} private static PersistableBundle getDefaults() { PersistableBundle defaults = new PersistableBundle(); defaults.putInt(KEY_WIFI_OFF_DEFERRING_TIME_MILLIS_INT, 4000); + defaults.putBoolean(KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL, false); + defaults.putBoolean(KEY_ENABLE_PRESENCE_PUBLISH_BOOL, false); + defaults.putStringArray(KEY_PUBLISH_SERVICE_DESC_FEATURE_TAG_MAP_OVERRIDE_STRING_ARRAY, + new String[] {}); + defaults.putBoolean(KEY_ENABLE_PRESENCE_CAPABILITY_EXCHANGE_BOOL, false); + defaults.putBoolean(KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL, false); + defaults.putBoolean(KEY_ENABLE_PRESENCE_GROUP_SUBSCRIBE_BOOL, true); + defaults.putInt(KEY_NON_RCS_CAPABILITIES_CACHE_EXPIRATION_SEC_INT, 30 * 24 * 60 * 60); + defaults.putBoolean(KEY_RCS_REQUEST_FORBIDDEN_BY_SIP_489_BOOL, false); + defaults.putLong(KEY_RCS_REQUEST_RETRY_INTERVAL_MILLIS_LONG, 20 * 60 * 1000); + defaults.putStringArray(KEY_RCS_FEATURE_TAG_ALLOWED_STRING_ARRAY, new String[]{ + "+g.3gpp.icsi-ref=\"urn%3Aurn-7%3A3gpp-service.ims.icsi.oma.cpm.msg\"", + "+g.3gpp.icsi-ref=\"urn%3Aurn-7%3A3gpp-service.ims.icsi.oma.cpm.largemsg\"", + "+g.3gpp.icsi-ref=\"urn%3Aurn-7%3A3gpp-service.ims.icsi.oma.cpm.deferred\"", + "+g.gsma.rcs.cpm.pager-large", + "+g.3gpp.icsi-ref=\"urn%3Aurn-7%3A3gpp-service.ims.icsi.oma.cpm.session\"", + "+g.3gpp.icsi-ref=\"urn%3Aurn-7%3A3gpp-service.ims.icsi.oma.cpm.session\"", + "+g.3gpp.iari-ref=\"urn%3Aurn-7%3A3gpp-application.ims.iari.rcs.fthttp\"", + "+g.3gpp.iari-ref=\"urn%3Aurn-7%3A3gpp-application.ims.iari.rcs.ftsms\"", + "+g.3gpp.iari-ref=\"urn%3Aurn-7%3A3gpp-service.ims.icsi.gsma.callcomposer\"", + "+g.gsma.callcomposer", + "+g.3gpp.icsi-ref=\"urn%3Aurn-7%3A3gpp-service.ims.icsi.gsma.callunanswered\"", + "+g.3gpp.icsi-ref=\"urn%3Aurn-7%3A3gpp-service.ims.icsi.gsma.sharedmap\"", + "+g.3gpp.icsi-ref=\"urn%3Aurn-7%3A3gpp-service.ims.icsi.gsma.sharedsketch\"", + "+g.3gpp.iari-ref=\"urn%3Aurn-7%3A3gpp-application.ims.iari.rcs.geopush\"", + "+g.3gpp.iari-ref=\"urn%3Aurn-7%3A3gpp-application.ims.iari.rcs.geosms\"", + "+g.3gpp.iari-ref=\"urn%3Aurn-7%3A3gpp-application.ims.iari.rcs.chatbot\"", + "+g.3gpp.iari-ref=\"urn%3Aurn-7%3A3gpp-application.ims.iari.rcs.chatbot.sa\"", + "+g.gsma.rcs.botversion=\"#=1,#=2\"", + "+g.gsma.rcs.cpimext"}); + + return defaults; + } + } + + /** + * Configs used for epdg tunnel bring up. + * + * @see <a href="https://tools.ietf.org/html/rfc7296">RFC 7296, Internet Key Exchange Protocol + * Version 2 (IKEv2)</a> + */ + public static final class Iwlan { + /** Prefix of all Epdg.KEY_* constants. */ + public static final String KEY_PREFIX = "iwlan."; + + /** + * Time in seconds after which the child security association session is terminated if rekey + * procedure is not successful. If not set or set to <= 0, the default value is 3600 + * seconds. + */ + public static final String KEY_CHILD_SA_REKEY_HARD_TIMER_SEC_INT = + KEY_PREFIX + "child_sa_rekey_hard_timer_sec_int"; + + /** + * Time in seconds after which the child session rekey procedure is started. If not set or + * set to <= 0, default value is 3000 seconds. + */ + public static final String KEY_CHILD_SA_REKEY_SOFT_TIMER_SEC_INT = + KEY_PREFIX + "child_sa_rekey_soft_timer_sec_int"; + + /** + * Supported DH groups for IKE negotiation. Possible values are: + * {@link android.net.ipsec.ike.SaProposal#DH_GROUP_NONE}, + * {@link android.net.ipsec.ike.SaProposal#DH_GROUP_1024_BIT_MODP}, + * {@link android.net.ipsec.ike.SaProposal#DH_GROUP_1536_BIT_MODP}, + * {@link android.net.ipsec.ike.SaProposal#DH_GROUP_2048_BIT_MODP} + */ + public static final String KEY_DIFFIE_HELLMAN_GROUPS_INT_ARRAY = + KEY_PREFIX + "diffie_hellman_groups_int_array"; + + /** + * Time in seconds after which a dead peer detection (DPD) request is sent. If not set or + * set to <= 0, default value is 120 seconds. + */ + public static final String KEY_DPD_TIMER_SEC_INT = KEY_PREFIX + "dpd_timer_sec_int"; + + /** + * Method used to authenticate epdg server. Possible values are {@link + * #AUTHENTICATION_METHOD_EAP_ONLY}, {@link #AUTHENTICATION_METHOD_CERT} + */ + public static final String KEY_EPDG_AUTHENTICATION_METHOD_INT = + KEY_PREFIX + "epdg_authentication_method_int"; + + /** + * A priority list of ePDG addresses to be used. Possible values are {@link + * #EPDG_ADDRESS_STATIC}, {@link #EPDG_ADDRESS_PLMN}, {@link #EPDG_ADDRESS_PCO}, {@link + * #EPDG_ADDRESS_CELLULAR_LOC} + */ + public static final String KEY_EPDG_ADDRESS_PRIORITY_INT_ARRAY = + KEY_PREFIX + "epdg_address_priority_int_array"; + + /** Epdg static IP address or FQDN */ + public static final String KEY_EPDG_STATIC_ADDRESS_STRING = + KEY_PREFIX + "epdg_static_address_string"; + + /** Epdg static IP address or FQDN for roaming */ + public static final String KEY_EPDG_STATIC_ADDRESS_ROAMING_STRING = + KEY_PREFIX + "epdg_static_address_roaming_string"; + + /** + * List of supported key sizes for AES Cipher Block Chaining (CBC) encryption mode of child + * session. Possible values are: + * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_UNUSED}, + * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_AES_128}, + * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_AES_192}, + * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_AES_256} + */ + public static final String KEY_CHILD_SESSION_AES_CBC_KEY_SIZE_INT_ARRAY = + KEY_PREFIX + "child_session_aes_cbc_key_size_int_array"; + + /** + * List of supported key sizes for AES Counter (CTR) encryption mode of child session. + * Possible values are: + * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_UNUSED}, + * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_AES_128}, + * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_AES_192}, + * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_AES_256} + */ + public static final String KEY_CHILD_SESSION_AES_CTR_KEY_SIZE_INT_ARRAY = + KEY_PREFIX + "child_session_aes_ctr_key_size_int_array"; + + /** + * List of supported encryption algorithms for child session. Possible values are + * {@link android.net.ipsec.ike.SaProposal#ENCRYPTION_ALGORITHM_AES_CBC} + */ + public static final String KEY_SUPPORTED_CHILD_SESSION_ENCRYPTION_ALGORITHMS_INT_ARRAY = + KEY_PREFIX + "supported_child_session_encryption_algorithms_int_array"; + + /** + * Time in seconds after which the IKE session is terminated if rekey procedure is not + * successful. If not set or set to <= 0, default value is 3600 seconds. + */ + public static final String KEY_IKE_REKEY_HARD_TIMER_SEC_INT = + KEY_PREFIX + "ike_rekey_hard_timer_in_sec"; + + /** + * Time in seconds after which the IKE session rekey procedure is started. If not set or set + * to <= 0, default value is 3000 seconds. + */ + public static final String KEY_IKE_REKEY_SOFT_TIMER_SEC_INT = + KEY_PREFIX + "ike_rekey_soft_timer_sec_int"; + + /** + * List of supported key sizes for AES Cipher Block Chaining (CBC) encryption mode of IKE + * session. Possible values: + * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_UNUSED}, + * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_AES_128}, + * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_AES_192}, + * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_AES_256} + */ + public static final String KEY_IKE_SESSION_AES_CBC_KEY_SIZE_INT_ARRAY = + KEY_PREFIX + "ike_session_encryption_aes_cbc_key_size_int_array"; + + + /** + * List of supported key sizes for AES Counter (CTR) encryption mode of IKE session. + * Possible values - + * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_UNUSED}, + * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_AES_128}, + * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_AES_192}, + * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_AES_256} + */ + public static final String KEY_IKE_SESSION_AES_CTR_KEY_SIZE_INT_ARRAY = + KEY_PREFIX + "ike_session_encryption_aes_ctr_key_size_int_array"; + + /** + * List of supported encryption algorithms for IKE session. Possible values are + * {@link android.net.ipsec.ike.SaProposal#ENCRYPTION_ALGORITHM_AES_CBC}, + * {@link android.net.ipsec.ike.SaProposal#ENCRYPTION_ALGORITHM_AES_CTR} + */ + public static final String KEY_SUPPORTED_IKE_SESSION_ENCRYPTION_ALGORITHMS_INT_ARRAY = + KEY_PREFIX + "supported_ike_session_encryption_algorithms_int_array"; + + /** + * List of supported integrity algorithms for IKE session. Possible values are + * {@link android.net.ipsec.ike.SaProposal#INTEGRITY_ALGORITHM_NONE}, + * {@link android.net.ipsec.ike.SaProposal#INTEGRITY_ALGORITHM_HMAC_SHA1_96}, + * {@link android.net.ipsec.ike.SaProposal#INTEGRITY_ALGORITHM_AES_XCBC_96}, + * {@link android.net.ipsec.ike.SaProposal#INTEGRITY_ALGORITHM_HMAC_SHA2_256_128}, + * {@link android.net.ipsec.ike.SaProposal#INTEGRITY_ALGORITHM_HMAC_SHA2_384_192}, + * {@link android.net.ipsec.ike.SaProposal#INTEGRITY_ALGORITHM_HMAC_SHA2_512_256} + */ + public static final String KEY_SUPPORTED_INTEGRITY_ALGORITHMS_INT_ARRAY = + KEY_PREFIX + "supported_integrity_algorithms_int_array"; + + /** Maximum number of retries for tunnel establishment. */ + public static final String KEY_MAX_RETRIES_INT = KEY_PREFIX + "max_retries_int"; + + /** + * Time in seconds after which a NATT keep alive message is sent. If not set or set to <= 0, + * default value is 20 seconds. + */ + public static final String KEY_NATT_KEEP_ALIVE_TIMER_SEC_INT = + KEY_PREFIX + "natt_keep_alive_timer_sec_int"; + + /** List of '-' separated MCC/MNCs used to create ePDG FQDN as per 3GPP TS 23.003 */ + public static final String KEY_MCC_MNCS_STRING_ARRAY = KEY_PREFIX + "mcc_mncs_string_array"; + + /** + * List of supported pseudo random function algorithms for IKE session. Possible values are + * {@link android.net.ipsec.ike.SaProposal#PSEUDORANDOM_FUNCTION_HMAC_SHA1}, + * {@link android.net.ipsec.ike.SaProposal#PSEUDORANDOM_FUNCTION_AES128_XCBC}, + * {@link android.net.ipsec.ike.SaProposal#PSEUDORANDOM_FUNCTION_SHA2_256}, + * {@link android.net.ipsec.ike.SaProposal#PSEUDORANDOM_FUNCTION_SHA2_384}, + * {@link android.net.ipsec.ike.SaProposal#PSEUDORANDOM_FUNCTION_SHA2_512} + */ + public static final String KEY_SUPPORTED_PRF_ALGORITHMS_INT_ARRAY = + KEY_PREFIX + "supported_prf_algorithms_int_array"; + + /** + * List of IKE message retransmission timeouts in milliseconds, where each timeout + * is the waiting time before next retry, except the last timeout which is the waiting time + * before terminating the IKE Session. Min list length = 1, Max + * list length = 10 Min timeout = 500 ms, Max timeout = 1800000 ms + */ + public static final String KEY_RETRANSMIT_TIMER_MSEC_INT_ARRAY = + KEY_PREFIX + "retransmit_timer_sec_int_array"; + + /** + * Specifies the local identity type for IKE negotiations. Possible values are {@link + * #ID_TYPE_FQDN}, {@link #ID_TYPE_RFC822_ADDR}, {@link #ID_TYPE_KEY_ID} + */ + public static final String KEY_IKE_LOCAL_ID_TYPE_INT = KEY_PREFIX + "ike_local_id_type_int"; + + /** + * Specifies the remote identity type for IKE negotiations. Possible values are {@link + * #ID_TYPE_FQDN}, {@link #ID_TYPE_RFC822_ADDR}, {@link #ID_TYPE_KEY_ID} + */ + public static final String KEY_IKE_REMOTE_ID_TYPE_INT = + KEY_PREFIX + "ike_remote_id_type_int"; + + /** Controls if KE payload should be added during child session local rekey procedure. */ + public static final String KEY_ADD_KE_TO_CHILD_SESSION_REKEY_BOOL = + KEY_PREFIX + "add_ke_to_child_session_rekey_bool"; + + /** Specifies the PCO id for IPv6 Epdg server address */ + public static final String KEY_EPDG_PCO_ID_IPV6_INT = KEY_PREFIX + "epdg_pco_id_ipv6_int"; + + /** Specifies the PCO id for IPv4 Epdg server address */ + public static final String KEY_EPDG_PCO_ID_IPV4_INT = KEY_PREFIX + "epdg_pco_id_ipv4_int"; + + /** @hide */ + @IntDef({AUTHENTICATION_METHOD_EAP_ONLY, AUTHENTICATION_METHOD_CERT}) + public @interface AuthenticationMethodType {} + + /** + * Certificate sent from the server is ignored. Only Extensible Authentication Protocol + * (EAP) is used to authenticate the server. EAP_ONLY_AUTH payload is added to IKE_AUTH + * request if supported. + * + * @see <a href="https://tools.ietf.org/html/rfc5998">RFC 5998</a> + */ + public static final int AUTHENTICATION_METHOD_EAP_ONLY = 0; + /** Server is authenticated using its certificate. */ + public static final int AUTHENTICATION_METHOD_CERT = 1; + + /** @hide */ + @IntDef({ + EPDG_ADDRESS_STATIC, + EPDG_ADDRESS_PLMN, + EPDG_ADDRESS_PCO, + EPDG_ADDRESS_CELLULAR_LOC + }) + public @interface EpdgAddressType {} + + /** Use static epdg address. */ + public static final int EPDG_ADDRESS_STATIC = 0; + /** Construct the epdg address using plmn. */ + public static final int EPDG_ADDRESS_PLMN = 1; + /** + * Use the epdg address received in protocol configuration options (PCO) from the network. + */ + public static final int EPDG_ADDRESS_PCO = 2; + /** Use cellular location to chose epdg server */ + public static final int EPDG_ADDRESS_CELLULAR_LOC = 3; + + /** @hide */ + @IntDef({ID_TYPE_FQDN, ID_TYPE_RFC822_ADDR, ID_TYPE_KEY_ID}) + public @interface IkeIdType {} + + /** + * Ike Identification Fully Qualified Domain Name + * + * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.5">RFC 7296, Internet Key + * Exchange Protocol Version 2 (IKEv2)</a> + */ + public static final int ID_TYPE_FQDN = 2; + /** + * Ike Identification Fully Qualified RFC 822 email address. + * + * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.5">RFC 7296, Internet Key + * Exchange Protocol Version 2 (IKEv2)</a> + */ + public static final int ID_TYPE_RFC822_ADDR = 3; + /** + * Ike Identification opaque octet stream for vendor specific information + * + * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.5">RFC 7296, Internet Key + * Exchange Protocol Version 2 (IKEv2)</a> + */ + public static final int ID_TYPE_KEY_ID = 11; + + private Iwlan() {} + + private static PersistableBundle getDefaults() { + PersistableBundle defaults = new PersistableBundle(); + defaults.putInt(KEY_IKE_REKEY_SOFT_TIMER_SEC_INT, 7200); + defaults.putInt(KEY_IKE_REKEY_HARD_TIMER_SEC_INT, 14400); + defaults.putInt(KEY_CHILD_SA_REKEY_SOFT_TIMER_SEC_INT, 3600); + defaults.putInt(KEY_CHILD_SA_REKEY_HARD_TIMER_SEC_INT, 7200); + defaults.putIntArray( + KEY_RETRANSMIT_TIMER_MSEC_INT_ARRAY, new int[] {500, 1000, 2000, 4000, 8000}); + defaults.putInt(KEY_DPD_TIMER_SEC_INT, 120); + defaults.putInt(KEY_MAX_RETRIES_INT, 3); + defaults.putIntArray( + KEY_DIFFIE_HELLMAN_GROUPS_INT_ARRAY, + new int[] { + SaProposal.DH_GROUP_1024_BIT_MODP, + SaProposal.DH_GROUP_1536_BIT_MODP, + SaProposal.DH_GROUP_2048_BIT_MODP + }); + defaults.putIntArray( + KEY_SUPPORTED_IKE_SESSION_ENCRYPTION_ALGORITHMS_INT_ARRAY, + new int[] {SaProposal.ENCRYPTION_ALGORITHM_AES_CBC}); + defaults.putIntArray( + KEY_SUPPORTED_CHILD_SESSION_ENCRYPTION_ALGORITHMS_INT_ARRAY, + new int[] {SaProposal.ENCRYPTION_ALGORITHM_AES_CBC}); + defaults.putIntArray( + KEY_SUPPORTED_INTEGRITY_ALGORITHMS_INT_ARRAY, + new int[] { + SaProposal.INTEGRITY_ALGORITHM_AES_XCBC_96, + SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA1_96, + SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_256_128, + SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_384_192, + SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_512_256, + }); + defaults.putIntArray( + KEY_SUPPORTED_PRF_ALGORITHMS_INT_ARRAY, + new int[] { + SaProposal.PSEUDORANDOM_FUNCTION_HMAC_SHA1, + SaProposal.PSEUDORANDOM_FUNCTION_AES128_XCBC, + SaProposal.PSEUDORANDOM_FUNCTION_SHA2_256, + SaProposal.PSEUDORANDOM_FUNCTION_SHA2_384, + SaProposal.PSEUDORANDOM_FUNCTION_SHA2_512 + }); + + defaults.putInt(KEY_EPDG_AUTHENTICATION_METHOD_INT, AUTHENTICATION_METHOD_EAP_ONLY); + defaults.putString(KEY_EPDG_STATIC_ADDRESS_STRING, ""); + defaults.putString(KEY_EPDG_STATIC_ADDRESS_ROAMING_STRING, ""); + // will be used after b/158036773 is fixed + defaults.putInt(KEY_NATT_KEEP_ALIVE_TIMER_SEC_INT, 20); + defaults.putIntArray( + KEY_IKE_SESSION_AES_CBC_KEY_SIZE_INT_ARRAY, + new int[] { + SaProposal.KEY_LEN_AES_128, + SaProposal.KEY_LEN_AES_192, + SaProposal.KEY_LEN_AES_256}); + defaults.putIntArray( + KEY_CHILD_SESSION_AES_CBC_KEY_SIZE_INT_ARRAY, + new int[] { + SaProposal.KEY_LEN_AES_128, + SaProposal.KEY_LEN_AES_192, + SaProposal.KEY_LEN_AES_256}); + defaults.putIntArray( + KEY_IKE_SESSION_AES_CTR_KEY_SIZE_INT_ARRAY, + new int[] { + SaProposal.KEY_LEN_AES_128, + SaProposal.KEY_LEN_AES_192, + SaProposal.KEY_LEN_AES_256}); + defaults.putIntArray( + KEY_CHILD_SESSION_AES_CTR_KEY_SIZE_INT_ARRAY, + new int[] { + SaProposal.KEY_LEN_AES_128, + SaProposal.KEY_LEN_AES_192, + SaProposal.KEY_LEN_AES_256}); + defaults.putIntArray( + KEY_EPDG_ADDRESS_PRIORITY_INT_ARRAY, + new int[] {EPDG_ADDRESS_PLMN, EPDG_ADDRESS_STATIC}); + defaults.putStringArray(KEY_MCC_MNCS_STRING_ARRAY, new String[] {}); + defaults.putInt(KEY_IKE_LOCAL_ID_TYPE_INT, ID_TYPE_RFC822_ADDR); + defaults.putInt(KEY_IKE_REMOTE_ID_TYPE_INT, ID_TYPE_FQDN); + defaults.putBoolean(KEY_ADD_KE_TO_CHILD_SESSION_REKEY_BOOL, false); + defaults.putInt(KEY_EPDG_PCO_ID_IPV6_INT, 0); + defaults.putInt(KEY_EPDG_PCO_ID_IPV4_INT, 0); + return defaults; } } @@ -3750,13 +4741,23 @@ public class CarrierConfigManager { "mmi_two_digit_number_pattern_string_array"; /** - * Holds the list of carrier certificate hashes. - * Note that each carrier has its own certificates. + * Holds the list of carrier certificate hashes, followed by optional package names. + * Format: "sha1/256" or "sha1/256:package1,package2,package3..." + * Note that each carrier has its own hashes. */ public static final String KEY_CARRIER_CERTIFICATE_STRING_ARRAY = "carrier_certificate_string_array"; /** + * Flag specifying whether the incoming call number should be formatted to national number + * for Japan. @return {@code true} convert to the national format, {@code false} otherwise. + * e.g. "+819012345678" -> "09012345678" + * @hide + */ + public static final String KEY_FORMAT_INCOMING_NUMBER_TO_NATIONAL_FOR_JP_BOOL = + "format_incoming_number_to_national_for_jp_bool"; + + /** * DisconnectCause array to play busy tone. Value should be array of * {@link android.telephony.DisconnectCause}. */ @@ -3814,6 +4815,165 @@ public class CarrierConfigManager { public static final String KEY_MISSED_INCOMING_CALL_SMS_PATTERN_STRING_ARRAY = "missed_incoming_call_sms_pattern_string_array"; + /** + * Indicating whether DUN APN should be disabled when the device is roaming. In that case, + * the default APN (i.e. internet) will be used for tethering. + * + * This config is only available when using Preset APN(not user edited) as Preferred APN. + * + * @hide + */ + public static final String KEY_DISABLE_DUN_APN_WHILE_ROAMING_WITH_PRESET_APN_BOOL = + "disable_dun_apn_while_roaming_with_preset_apn_bool"; + + /** + * Where there is no preferred APN, specifies the carrier's default preferred APN. + * Specifies the {@link android.provider.Telephony.Carriers.APN} of the default preferred apn. + * + * This config is only available with Preset APN(not user edited). + * + * @hide + */ + public static final String KEY_DEFAULT_PREFERRED_APN_NAME_STRING = + "default_preferred_apn_name_string"; + + /** + * Indicates if the carrier supports call composer. + */ + public static final String KEY_SUPPORTS_CALL_COMPOSER_BOOL = "supports_call_composer_bool"; + + /** + * Indicates the carrier server url that serves the call composer picture. + */ + public static final String KEY_CALL_COMPOSER_PICTURE_SERVER_URL_STRING = + "call_composer_picture_server_url_string"; + + /** + * For Android 11, provide a temporary solution for OEMs to use the lower of the two MTU values + * for IPv4 and IPv6 if both are sent. + * TODO: remove in later release + * + * @hide + */ + public static final String KEY_USE_LOWER_MTU_VALUE_IF_BOTH_RECEIVED = + "use_lower_mtu_value_if_both_received"; + + /** + * Determines the default RTT mode. + * + * Upon first boot, when the user has not yet set a value for their preferred RTT mode, + * the value of this config will be sent to the IMS stack. Valid values are the same as for + * {@link Settings.Secure#RTT_CALLING_MODE}. + * + * @hide + */ + public static final String KEY_DEFAULT_RTT_MODE_INT = + "default_rtt_mode_int"; + + /** + * Indicates whether RTT is supported while roaming. + */ + public static final String KEY_RTT_SUPPORTED_WHILE_ROAMING_BOOL = + "rtt_supported_while_roaming_bool"; + + /** + * Indicates if auto-configuration server is used for the RCS config + * Reference: GSMA RCC.14 + */ + public static final String KEY_USE_ACS_FOR_RCS_BOOL = "use_acs_for_rcs_bool"; + + /** + * Indicates temporarily unmetered mobile data is supported by the carrier. + * @hide + */ + public static final String KEY_NETWORK_TEMP_NOT_METERED_SUPPORTED_BOOL = + "network_temp_not_metered_supported_bool"; + + /** + * Boolean indicating whether the SIM PIN can be stored and verified + * seamlessly after an unattended reboot. + * + * The device configuration value {@code config_allow_pin_storage_for_unattended_reboot} + * ultimately controls whether this carrier configuration option is used. Where + * {@code config_allow_pin_storage_for_unattended_reboot} is false, the value of the + * {@link #KEY_STORE_SIM_PIN_FOR_UNATTENDED_REBOOT_BOOL} carrier configuration option is + * ignored. + * + * @hide + */ + public static final String KEY_STORE_SIM_PIN_FOR_UNATTENDED_REBOOT_BOOL = + "store_sim_pin_for_unattended_reboot_bool"; + + /** + * Determine whether "Enable 2G" toggle can be shown. + * + * Used to trade privacy/security against potentially reduced carrier coverage for some + * carriers. + */ + public static final String KEY_HIDE_ENABLE_2G = "hide_enable_2g_bool"; + + /** + * Indicates the allowed APN types that can be used for LTE initial attach. The order of APN + * types in the configuration is the order of APN types that will be used for initial attach. + * Empty list indicates that no APN types are allowed for initial attach. + * + * @hide + */ + public static final String KEY_ALLOWED_INITIAL_ATTACH_APN_TYPES_STRING_ARRAY = + "allowed_initial_attach_apn_types_string_array"; + + /** + * Indicates whether or not the carrier will provision merged carrier Wi-Fi offload networks. + * Such networks are considered part of the core carrier network. + * + * This configuration will be use to gate whether such configurations are allowed to the carrier + * and correspondingly enable UI elements which are required for such configurations. + */ + public static final String KEY_CARRIER_PROVISIONS_WIFI_MERGED_NETWORKS_BOOL = + "carrier_provisions_wifi_merged_networks_bool"; + + /** + * Determines whether or not to use (IP) data connectivity as a supplemental condition to + * control the visibility of the no-calling indicator for this carrier in the System UI. Setting + * the configuration to true may make sense for carriers that provide OTT calling. + * + * Config = true: show no-calling indication only if telephony does not have voice registration + * and if no (IP) data connectivity is available. + * Config = false: show no-calling indication only if telephony does not have voice + * registration. + */ + public static final String KEY_USE_IP_FOR_CALLING_INDICATOR_BOOL = + "use_ip_for_calling_indicator_bool"; + + /** + * Determine whether or not to display a call strength indicator for this carrier in the System + * UI. Disabling the indication may be reasonable if the carrier's calling is not integrated + * into the Android telephony stack (e.g. it is OTT). + * + * true: Use telephony APIs to detect the current networking medium of calling and display a + * UI indication based on the current strength (e.g. signal level) of that medium. + * false: Do not display the call strength indicator. + */ + public static final String KEY_DISPLAY_CALL_STRENGTH_INDICATOR_BOOL = + "display_call_strength_indicator_bool"; + + /** + * Determine whether or not to display no data notification when data setup is permanently + * failed. + * + * @hide + */ + public static final String KEY_DISPLAY_NO_DATA_NOTIFICATION_ON_PERMANENT_FAILURE_BOOL = + "display_no_data_notification_on_permanent_failure_bool"; + + /** + * Determine whether unthrottle data retry when tracking area code (TAC/LAC) from cell changes + * + * @hide + */ + public static final String KEY_UNTHROTTLE_DATA_RETRY_WHEN_TAC_CHANGES_BOOL = + "unthrottle_data_retry_when_tac_changes_bool"; + /** The default value for every variable. */ private final static PersistableBundle sDefaults; @@ -3835,6 +4995,7 @@ public class CarrierConfigManager { sDefaults.putBoolean(KEY_CARRIER_SETTINGS_ENABLE_BOOL, false); sDefaults.putBoolean(KEY_CARRIER_VOLTE_AVAILABLE_BOOL, false); sDefaults.putBoolean(KEY_CARRIER_VT_AVAILABLE_BOOL, false); + sDefaults.putInt(KEY_CARRIER_USSD_METHOD_INT, USSD_OVER_CS_PREFERRED); sDefaults.putBoolean(KEY_VOLTE_5G_LIMITED_ALERT_DIALOG_BOOL, false); sDefaults.putBoolean(KEY_NOTIFY_HANDOVER_VIDEO_FROM_WIFI_TO_LTE_BOOL, false); sDefaults.putBoolean(KEY_ALLOW_MERGING_RTT_CALLS_BOOL, false); @@ -3847,6 +5008,7 @@ public class CarrierConfigManager { sDefaults.putBoolean(KEY_IGNORE_DATA_ENABLED_CHANGED_FOR_VIDEO_CALLS, true); sDefaults.putBoolean(KEY_VILTE_DATA_IS_METERED_BOOL, true); sDefaults.putBoolean(KEY_CARRIER_WFC_IMS_AVAILABLE_BOOL, false); + sDefaults.putBoolean(KEY_CARRIER_CROSS_SIM_IMS_AVAILABLE_BOOL, false); sDefaults.putBoolean(KEY_CARRIER_WFC_SUPPORTS_WIFI_ONLY_BOOL, false); sDefaults.putBoolean(KEY_CARRIER_DEFAULT_WFC_IMS_ENABLED_BOOL, false); sDefaults.putBoolean(KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_ENABLED_BOOL, false); @@ -3879,6 +5041,8 @@ public class CarrierConfigManager { sDefaults.putBoolean(KEY_HAS_IN_CALL_NOISE_SUPPRESSION_BOOL, false); sDefaults.putBoolean(KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL, false); sDefaults.putBoolean(KEY_ONLY_AUTO_SELECT_IN_HOME_NETWORK_BOOL, false); + sDefaults.putBoolean(KEY_SHOW_SINGLE_OPERATOR_ROW_IN_CHOOSE_NETWORK_SETTING_BOOL, true); + sDefaults.putBoolean(KEY_SHOW_SPN_FOR_HOME_IN_CHOOSE_NETWORK_SETTING_BOOL, false); sDefaults.putBoolean(KEY_SIMPLIFIED_NETWORK_SETTINGS_BOOL, false); sDefaults.putBoolean(KEY_HIDE_SIM_LOCK_SETTINGS_BOOL, false); @@ -3886,6 +5050,7 @@ public class CarrierConfigManager { sDefaults.putBoolean(KEY_CALL_BARRING_VISIBILITY_BOOL, false); sDefaults.putBoolean(KEY_CALL_BARRING_SUPPORTS_PASSWORD_CHANGE_BOOL, true); sDefaults.putBoolean(KEY_CALL_BARRING_SUPPORTS_DEACTIVATE_ALL_BOOL, true); + sDefaults.putInt(KEY_CALL_BARRING_DEFAULT_SERVICE_CLASS_INT, SERVICE_CLASS_VOICE); sDefaults.putBoolean(KEY_CALL_FORWARDING_VISIBILITY_BOOL, true); sDefaults.putBoolean(KEY_CALL_FORWARDING_WHEN_UNREACHABLE_SUPPORTED_BOOL, true); sDefaults.putBoolean(KEY_CALL_FORWARDING_WHEN_UNANSWERED_SUPPORTED_BOOL, true); @@ -3930,6 +5095,7 @@ public class CarrierConfigManager { sDefaults.putStringArray(KEY_CARRIER_VVM_PACKAGE_NAME_STRING_ARRAY, null); sDefaults.putBoolean(KEY_SHOW_ICCID_IN_SIM_STATUS_BOOL, false); sDefaults.putBoolean(KEY_SHOW_SIGNAL_STRENGTH_IN_SIM_STATUS_BOOL, true); + sDefaults.putBoolean(KEY_INFLATE_SIGNAL_STRENGTH_BOOL, false); sDefaults.putBoolean(KEY_CI_ACTION_ON_SYS_UPDATE_BOOL, false); sDefaults.putString(KEY_CI_ACTION_ON_SYS_UPDATE_INTENT_STRING, ""); sDefaults.putString(KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_STRING, ""); @@ -3948,6 +5114,7 @@ public class CarrierConfigManager { + "320000:5000,640000:5000,1280000:5000,1800000:5000", "mms:default_randomization=2000,5000,10000,20000,40000,80000:5000,160000:5000," + "320000:5000,640000:5000,1280000:5000,1800000:5000", + "ims:max_retries=10, 5000, 5000, 5000", "others:max_retries=3, 5000, 5000, 5000"}); sDefaults.putLong(KEY_CARRIER_DATA_CALL_APN_DELAY_DEFAULT_LONG, 20000); sDefaults.putLong(KEY_CARRIER_DATA_CALL_APN_DELAY_FASTER_LONG, 3000); @@ -3994,6 +5161,9 @@ public class CarrierConfigManager { sDefaults.putBoolean(KEY_SUPPORT_MANAGE_IMS_CONFERENCE_CALL_BOOL, true); sDefaults.putBoolean(KEY_SUPPORT_IMS_CONFERENCE_EVENT_PACKAGE_BOOL, true); sDefaults.putBoolean(KEY_SUPPORT_IMS_CONFERENCE_EVENT_PACKAGE_ON_PEER_BOOL, true); + sDefaults.putBoolean(KEY_SUPPORTS_DEVICE_TO_DEVICE_COMMUNICATION_USING_RTP_BOOL, false); + sDefaults.putBoolean(KEY_SUPPORTS_SDP_NEGOTIATION_OF_D2D_RTP_HEADER_EXTENSIONS_BOOL, false); + sDefaults.putBoolean(KEY_SUPPORTS_DEVICE_TO_DEVICE_COMMUNICATION_USING_DTMF_BOOL, false); sDefaults.putBoolean(KEY_SUPPORT_VIDEO_CONFERENCE_CALL_BOOL, false); sDefaults.putBoolean(KEY_IS_IMS_CONFERENCE_SIZE_ENFORCED_BOOL, false); sDefaults.putInt(KEY_IMS_CONFERENCE_SIZE_LIMIT_INT, 5); @@ -4016,6 +5186,7 @@ public class CarrierConfigManager { sDefaults.putBoolean(KEY_CARRIER_NAME_OVERRIDE_BOOL, false); sDefaults.putString(KEY_CARRIER_NAME_STRING, ""); sDefaults.putBoolean(KEY_WFC_CARRIER_NAME_OVERRIDE_BY_PNN_BOOL, false); + sDefaults.putInt(KEY_CROSS_SIM_SPN_FORMAT_INT, 1); sDefaults.putInt(KEY_SPN_DISPLAY_CONDITION_OVERRIDE_INT, -1); sDefaults.putStringArray(KEY_SPDI_OVERRIDE_STRING_ARRAY, null); sDefaults.putStringArray(KEY_PNN_OVERRIDE_STRING_ARRAY, null); @@ -4153,12 +5324,15 @@ public class CarrierConfigManager { sDefaults.putBoolean(KEY_SHOW_VIDEO_CALL_CHARGES_ALERT_DIALOG_BOOL, false); sDefaults.putStringArray(KEY_CALL_FORWARDING_BLOCKS_WHILE_ROAMING_STRING_ARRAY, null); + sDefaults.putBoolean(KEY_SUPPORT_IMS_CALL_FORWARDING_WHILE_ROAMING_BOOL, true); sDefaults.putInt(KEY_LTE_EARFCNS_RSRP_BOOST_INT, 0); sDefaults.putStringArray(KEY_BOOSTED_LTE_EARFCNS_STRING_ARRAY, null); sDefaults.putBoolean(KEY_USE_ONLY_RSRP_FOR_LTE_SIGNAL_BAR_BOOL, false); sDefaults.putBoolean(KEY_DISABLE_VOICE_BARRING_NOTIFICATION_BOOL, false); sDefaults.putInt(IMSI_KEY_AVAILABILITY_INT, 0); sDefaults.putString(IMSI_KEY_DOWNLOAD_URL_STRING, null); + sDefaults.putString(IMSI_CARRIER_PUBLIC_KEY_EPDG_STRING, null); + sDefaults.putString(IMSI_CARRIER_PUBLIC_KEY_WLAN_STRING, null); sDefaults.putBoolean(KEY_CONVERT_CDMA_CALLER_ID_MMI_CODES_WHILE_ROAMING_ON_3GPP_BOOL, false); sDefaults.putStringArray(KEY_NON_ROAMING_OPERATOR_STRING_ARRAY, null); @@ -4167,6 +5341,9 @@ public class CarrierConfigManager { sDefaults.putBoolean(KEY_RTT_SUPPORTED_BOOL, false); sDefaults.putBoolean(KEY_TTY_SUPPORTED_BOOL, true); sDefaults.putBoolean(KEY_HIDE_TTY_HCO_VCO_WITH_RTT_BOOL, false); + sDefaults.putBoolean(KEY_RTT_SUPPORTED_WHILE_ROAMING_BOOL, false); + sDefaults.putBoolean(KEY_RTT_UPGRADE_SUPPORTED_FOR_DOWNGRADED_VT_CALL_BOOL, true); + sDefaults.putBoolean(KEY_VT_UPGRADE_SUPPORTED_FOR_DOWNGRADED_RTT_CALL_BOOL, true); sDefaults.putBoolean(KEY_DISABLE_CHARGE_INDICATION_BOOL, false); sDefaults.putBoolean(KEY_SUPPORT_NO_REPLY_TIMER_FOR_CFNRY_BOOL, true); sDefaults.putStringArray(KEY_FEATURE_ACCESS_CODES_STRING_ARRAY, null); @@ -4179,7 +5356,9 @@ public class CarrierConfigManager { sDefaults.putString(KEY_OPERATOR_NAME_FILTER_PATTERN_STRING, ""); sDefaults.putString(KEY_SHOW_CARRIER_DATA_ICON_PATTERN_STRING, ""); sDefaults.putBoolean(KEY_HIDE_LTE_PLUS_DATA_ICON_BOOL, true); - sDefaults.putBoolean(KEY_NR_ENABLED_BOOL, true); + sDefaults.putInt(KEY_LTE_PLUS_THRESHOLD_BANDWIDTH_KHZ_INT, 20000); + sDefaults.putIntArray(KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY, + new int[]{CARRIER_NR_AVAILABILITY_NSA, CARRIER_NR_AVAILABILITY_SA}); sDefaults.putBoolean(KEY_LTE_ENABLED_BOOL, true); sDefaults.putBoolean(KEY_SUPPORT_TDSCDMA_BOOL, false); sDefaults.putStringArray(KEY_SUPPORT_TDSCDMA_ROAMING_NETWORKS_STRING_ARRAY, null); @@ -4225,12 +5404,12 @@ public class CarrierConfigManager { -65, /* SIGNAL_STRENGTH_GREAT */ }); sDefaults.putIntArray(KEY_5G_NR_SSRSRQ_THRESHOLDS_INT_ARRAY, - // Boundaries: [-20 dB, -3 dB] + // Boundaries: [-43 dB, 20 dB] new int[] { - -16, /* SIGNAL_STRENGTH_POOR */ - -12, /* SIGNAL_STRENGTH_MODERATE */ - -9, /* SIGNAL_STRENGTH_GOOD */ - -6 /* SIGNAL_STRENGTH_GREAT */ + -31, /* SIGNAL_STRENGTH_POOR */ + -19, /* SIGNAL_STRENGTH_MODERATE */ + -7, /* SIGNAL_STRENGTH_GOOD */ + 6 /* SIGNAL_STRENGTH_GREAT */ }); sDefaults.putIntArray(KEY_5G_NR_SSSINR_THRESHOLDS_INT_ARRAY, // Boundaries: [-23 dB, 40 dB] @@ -4261,14 +5440,18 @@ public class CarrierConfigManager { sDefaults.putBoolean(KEY_SUPPORT_EMERGENCY_DIALER_SHORTCUT_BOOL, true); sDefaults.putBoolean(KEY_USE_CALL_FORWARDING_USSD_BOOL, false); sDefaults.putBoolean(KEY_USE_CALLER_ID_USSD_BOOL, false); + sDefaults.putBoolean(KEY_USE_CALL_WAITING_USSD_BOOL, false); sDefaults.putInt(KEY_CALL_WAITING_SERVICE_CLASS_INT, 1 /* SERVICE_CLASS_VOICE */); sDefaults.putString(KEY_5G_ICON_CONFIGURATION_STRING, "connected_mmwave:5G,connected:5G,not_restricted_rrc_idle:5G," + "not_restricted_rrc_con:5G"); sDefaults.putString(KEY_5G_ICON_DISPLAY_GRACE_PERIOD_STRING, ""); sDefaults.putString(KEY_5G_ICON_DISPLAY_SECONDARY_GRACE_PERIOD_STRING, ""); + sDefaults.putBoolean(KEY_NR_TIMERS_RESET_IF_NON_ENDC_AND_RRC_IDLE_BOOL, false); /* Default value is 1 hour. */ sDefaults.putLong(KEY_5G_WATCHDOG_TIME_MS_LONG, 3600000); + sDefaults.putIntArray(KEY_ADDITIONAL_NR_ADVANCED_BANDS_INT_ARRAY, new int[0]); + sDefaults.putInt(KEY_NR_ADVANCED_CAPABLE_PCO_ID_INT, 0); sDefaults.putBoolean(KEY_UNMETERED_NR_NSA_BOOL, false); sDefaults.putBoolean(KEY_UNMETERED_NR_NSA_MMWAVE_BOOL, false); sDefaults.putBoolean(KEY_UNMETERED_NR_NSA_SUB6_BOOL, false); @@ -4302,6 +5485,7 @@ public class CarrierConfigManager { sDefaults.putLong(KEY_OPPORTUNISTIC_NETWORK_BACKOFF_TIME_LONG, 10000); /* Default value is 60 seconds. */ sDefaults.putLong(KEY_OPPORTUNISTIC_NETWORK_MAX_BACKOFF_TIME_LONG, 60000); + sDefaults.putAll(ImsServiceEntitlement.getDefaults()); sDefaults.putAll(Gps.getDefaults()); sDefaults.putIntArray(KEY_CDMA_ENHANCED_ROAMING_INDICATOR_FOR_HOME_NETWORK_INT_ARRAY, new int[] { @@ -4325,7 +5509,9 @@ public class CarrierConfigManager { }); sDefaults.putBoolean(KEY_SUPPORT_WPS_OVER_IMS_BOOL, true); sDefaults.putAll(Ims.getDefaults()); - sDefaults.putStringArray(KEY_CARRIER_CERTIFICATE_STRING_ARRAY, null); + sDefaults.putAll(Iwlan.getDefaults()); + sDefaults.putStringArray(KEY_CARRIER_CERTIFICATE_STRING_ARRAY, new String[0]); + sDefaults.putBoolean(KEY_FORMAT_INCOMING_NUMBER_TO_NATIONAL_FOR_JP_BOOL, false); sDefaults.putIntArray(KEY_DISCONNECT_CAUSE_PLAY_BUSYTONE_INT_ARRAY, new int[] {4 /* BUSY */}); sDefaults.putBoolean(KEY_PREVENT_CLIR_ACTIVATION_AND_DEACTIVATION_CODE_BOOL, false); @@ -4336,15 +5522,40 @@ public class CarrierConfigManager { // Default wifi configurations. sDefaults.putAll(Wifi.getDefaults()); sDefaults.putBoolean(ENABLE_EAP_METHOD_PREFIX_BOOL, false); + sDefaults.putInt(KEY_GBA_MODE_INT, GBA_ME); + sDefaults.putInt(KEY_GBA_UA_SECURITY_ORGANIZATION_INT, + UaSecurityProtocolIdentifier.ORG_3GPP); + sDefaults.putInt(KEY_GBA_UA_SECURITY_PROTOCOL_INT, + UaSecurityProtocolIdentifier.UA_SECURITY_PROTOCOL_3GPP_TLS_DEFAULT); + sDefaults.putInt(KEY_GBA_UA_TLS_CIPHER_SUITE_INT, TlsParams.TLS_NULL_WITH_NULL_NULL); + sDefaults.putBoolean(KEY_SHOW_FORWARDED_NUMBER_BOOL, false); sDefaults.putLong(KEY_DATA_SWITCH_VALIDATION_MIN_GAP_LONG, TimeUnit.DAYS.toMillis(1)); sDefaults.putStringArray(KEY_MISSED_INCOMING_CALL_SMS_ORIGINATOR_STRING_ARRAY, new String[0]); sDefaults.putStringArray(KEY_APN_PRIORITY_STRING_ARRAY, new String[] { - "default:0", "mms:2", "supl:2", "dun:2", "hipri:3", "fota:2", + "enterprise:0", "default:1", "mms:2", "supl:2", "dun:2", "hipri:3", "fota:2", "ims:2", "cbs:2", "ia:2", "emergency:2", "mcx:3", "xcap:3" }); sDefaults.putStringArray(KEY_MISSED_INCOMING_CALL_SMS_PATTERN_STRING_ARRAY, new String[0]); + sDefaults.putBoolean(KEY_DISABLE_DUN_APN_WHILE_ROAMING_WITH_PRESET_APN_BOOL, false); + sDefaults.putString(KEY_DEFAULT_PREFERRED_APN_NAME_STRING, ""); + sDefaults.putBoolean(KEY_SUPPORTS_CALL_COMPOSER_BOOL, false); + sDefaults.putString(KEY_CALL_COMPOSER_PICTURE_SERVER_URL_STRING, ""); + sDefaults.putBoolean(KEY_USE_LOWER_MTU_VALUE_IF_BOTH_RECEIVED, false); + sDefaults.putBoolean(KEY_USE_ACS_FOR_RCS_BOOL, false); + sDefaults.putBoolean(KEY_NETWORK_TEMP_NOT_METERED_SUPPORTED_BOOL, true); + sDefaults.putInt(KEY_DEFAULT_RTT_MODE_INT, 0); + sDefaults.putBoolean(KEY_STORE_SIM_PIN_FOR_UNATTENDED_REBOOT_BOOL, true); + sDefaults.putBoolean(KEY_HIDE_ENABLE_2G, false); + sDefaults.putStringArray(KEY_ALLOWED_INITIAL_ATTACH_APN_TYPES_STRING_ARRAY, + new String[]{"ia", "default", "mms", "dun"}); + sDefaults.putBoolean(KEY_CARRIER_PROVISIONS_WIFI_MERGED_NETWORKS_BOOL, false); + sDefaults.putBoolean(KEY_USE_IP_FOR_CALLING_INDICATOR_BOOL, false); + sDefaults.putBoolean(KEY_DISPLAY_CALL_STRENGTH_INDICATOR_BOOL, true); + sDefaults.putString(KEY_CARRIER_PROVISIONING_APP_STRING, ""); + sDefaults.putBoolean(KEY_DISPLAY_NO_DATA_NOTIFICATION_ON_PERMANENT_FAILURE_BOOL, false); + sDefaults.putBoolean(KEY_UNTHROTTLE_DATA_RETRY_WHEN_TAC_CHANGES_BOOL, false); } /** @@ -4363,9 +5574,42 @@ public class CarrierConfigManager { public static final String KEY_HOTSPOT_MAX_CLIENT_COUNT = KEY_PREFIX + "hotspot_maximum_client_count"; + /** + * This configuration is intended to be a narrow exception for provisioning + * {@link android.net.wifi.WifiNetworkSuggestion} of widely-known carrier networks that do + * not support using randomized MAC address. + * Carrier provisioned {@link android.net.wifi.WifiNetworkSuggestion} with SSIDs included + * in this list will have MAC randomization disabled. + * + * Note: the SSIDs in the list are expected to be interpreted as is - do not add double + * quotes to the SSIDs. + */ + public static final String KEY_SUGGESTION_SSID_LIST_WITH_MAC_RANDOMIZATION_DISABLED = + KEY_PREFIX + "suggestion_ssid_list_with_mac_randomization_disabled"; + + /** + * Avoid SoftAp in 5GHz if cellular is on unlicensed 5Ghz using License Assisted Access + * (LAA). + */ + public static final String KEY_AVOID_5GHZ_SOFTAP_FOR_LAA_BOOL = + KEY_PREFIX + "avoid_5ghz_softap_for_laa_bool"; + + /** + * Avoid Wifi Direct in 5GHz if cellular is on unlicensed 5Ghz using License Assisted + * Access (LAA). + */ + public static final String KEY_AVOID_5GHZ_WIFI_DIRECT_FOR_LAA_BOOL = + KEY_PREFIX + "avoid_5ghz_wifi_direct_for_laa_bool"; + + private static PersistableBundle getDefaults() { PersistableBundle defaults = new PersistableBundle(); defaults.putInt(KEY_HOTSPOT_MAX_CLIENT_COUNT, 0); + defaults.putStringArray(KEY_SUGGESTION_SSID_LIST_WITH_MAC_RANDOMIZATION_DISABLED, + new String[0]); + defaults.putBoolean(KEY_AVOID_5GHZ_SOFTAP_FOR_LAA_BOOL, false); + defaults.putBoolean(KEY_AVOID_5GHZ_WIFI_DIRECT_FOR_LAA_BOOL, false); + return defaults; } @@ -4587,6 +5831,7 @@ public class CarrierConfigManager { } catch (RemoteException ex) { Rlog.e(TAG, "getDefaultCarrierServicePackageName ICarrierConfigLoader is null" + ex.toString()); + ex.rethrowAsRuntimeException(); } return ""; } @@ -4598,7 +5843,7 @@ public class CarrierConfigManager { */ @NonNull @SystemApi - @SuppressLint("Doclava125") + @SuppressLint("RequiresPermission") public static PersistableBundle getDefaultConfig() { return new PersistableBundle(sDefaults); } diff --git a/telephony/java/android/telephony/CdmaEriInformation.java b/telephony/java/android/telephony/CdmaEriInformation.java deleted file mode 100644 index fd0b905e9c3e..000000000000 --- a/telephony/java/android/telephony/CdmaEriInformation.java +++ /dev/null @@ -1,169 +0,0 @@ -/** - * Copyright (C) 2020 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.annotation.SystemApi; -import android.os.Parcel; -import android.os.Parcelable; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * CDMA ERI (Enhanced Roaming Indicator) information. - * - * This contains the following ERI information - * - * 1. ERI (Enhanced Roaming Indicator) icon index. The number is assigned by - * 3GPP2 C.R1001-H v1.0 Table 8.1-1. Additionally carriers define their own - * ERI icon index. - * 2. CDMA ERI icon mode. This represents how the icon should be displayed. - * Its one of the following CDMA ERI icon mode - * {@link android.telephony.CdmaEriInformation#ERI_ICON_MODE_NORMAL} - * {@link android.telephony.CdmaEriInformation#ERI_ICON_MODE_FLASH} - * - * @hide - */ -public final class CdmaEriInformation implements Parcelable { - /** @hide */ - @Retention(RetentionPolicy.SOURCE) - @IntDef(prefix = {"ERI_"}, value = { - ERI_ON, - ERI_OFF, - ERI_FLASH - }) - public @interface EriIconIndex {} - - /** - * ERI (Enhanced Roaming Indicator) is ON i.e value 0 defined by - * 3GPP2 C.R1001-H v1.0 Table 8.1-1. - */ - public static final int ERI_ON = 0; - - /** - * ERI (Enhanced Roaming Indicator) is OFF i.e value 1 defined by - * 3GPP2 C.R1001-H v1.0 Table 8.1-1. - */ - public static final int ERI_OFF = 1; - - /** - * ERI (Enhanced Roaming Indicator) is FLASH i.e value 2 defined by - * 3GPP2 C.R1001-H v1.0 Table 8.1-1. - */ - public static final int ERI_FLASH = 2; - - /** @hide */ - @Retention(RetentionPolicy.SOURCE) - @IntDef(prefix = {"ERI_ICON_MODE_"}, value = { - ERI_ICON_MODE_NORMAL, - ERI_ICON_MODE_FLASH - }) - public @interface EriIconMode {} - - /** - * ERI (Enhanced Roaming Indicator) icon mode is normal. This constant represents that - * the ERI icon should be displayed normally. - * - * Note: ERI is defined 3GPP2 C.R1001-H Table 8.1-1 - */ - public static final int ERI_ICON_MODE_NORMAL = 0; - - /** - * ERI (Enhanced Roaming Indicator) icon mode flash. This constant represents that - * the ERI icon should be flashing. - * - * Note: ERI is defined 3GPP2 C.R1001-H Table 8.1-1 - */ - public static final int ERI_ICON_MODE_FLASH = 1; - - private @EriIconIndex int mIconIndex; - private @EriIconMode int mIconMode; - - /** - * Creates CdmaEriInformation from iconIndex and iconMode - * - * @hide - */ - public CdmaEriInformation(@EriIconIndex int iconIndex, @EriIconMode int iconMode) { - mIconIndex = iconIndex; - mIconMode = iconMode; - } - - /** Gets the ERI icon index */ - public @EriIconIndex int getEriIconIndex() { - return mIconIndex; - } - - /** - * Sets the ERI icon index - * - * @hide - */ - public void setEriIconIndex(@EriIconIndex int iconIndex) { - mIconIndex = iconIndex; - } - - /** Gets the ERI icon mode */ - public @EriIconMode int getEriIconMode() { - return mIconMode; - } - - /** - * Sets the ERI icon mode - * - * @hide - */ - public void setEriIconMode(@EriIconMode int iconMode) { - mIconMode = iconMode; - } - /** Implement the Parcelable interface */ - @Override - public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeInt(mIconIndex); - dest.writeInt(mIconMode); - } - - /** Implement the Parcelable interface */ - @Override - public int describeContents() { - return 0; - } - - /** - * Construct a CdmaEriInformation object from the given parcel - */ - private CdmaEriInformation(Parcel in) { - mIconIndex = in.readInt(); - mIconMode = in.readInt(); - } - - /** Implement the Parcelable interface */ - public static final @android.annotation.NonNull Parcelable.Creator<CdmaEriInformation> CREATOR = - new Parcelable.Creator<CdmaEriInformation>() { - @Override - public CdmaEriInformation createFromParcel(Parcel in) { - return new CdmaEriInformation(in); - } - - @Override - public CdmaEriInformation[] newArray(int size) { - return new CdmaEriInformation[size]; - } - }; -} diff --git a/telephony/java/android/telephony/CellIdentity.java b/telephony/java/android/telephony/CellIdentity.java index 1e5ce05ff28a..15147da925b8 100644 --- a/telephony/java/android/telephony/CellIdentity.java +++ b/telephony/java/android/telephony/CellIdentity.java @@ -37,7 +37,7 @@ import java.util.UUID; public abstract class CellIdentity implements Parcelable { /** @hide */ - public static final int INVALID_CHANNEL_NUMBER = -1; + public static final int INVALID_CHANNEL_NUMBER = Integer.MAX_VALUE; /** * parameters for validation @@ -70,8 +70,7 @@ public abstract class CellIdentity implements Parcelable { /** @hide */ protected String mAlphaShort; - // For GSM, WCDMA, TDSCDMA, LTE and NR, Cell Global ID is defined in 3GPP TS 23.003. - // For CDMA, its defined as System Id + Network Id + Basestation Id. + // Cell Global, 3GPP TS 23.003 /** @hide */ protected String mGlobalCellId; @@ -206,7 +205,6 @@ public abstract class CellIdentity implements Parcelable { public boolean isSameCell(@Nullable CellIdentity ci) { if (ci == null) return false; if (this.getClass() != ci.getClass()) return false; - if (this.getGlobalCellId() == null || ci.getGlobalCellId() == null) return false; return TextUtils.equals(this.getGlobalCellId(), ci.getGlobalCellId()); } diff --git a/telephony/java/android/telephony/CellIdentityCdma.java b/telephony/java/android/telephony/CellIdentityCdma.java index 68c833c37c99..58a01e9d01af 100644 --- a/telephony/java/android/telephony/CellIdentityCdma.java +++ b/telephony/java/android/telephony/CellIdentityCdma.java @@ -16,6 +16,8 @@ package android.telephony; +import static android.text.TextUtils.formatSimple; + import android.annotation.NonNull; import android.annotation.Nullable; import android.os.Parcel; @@ -145,7 +147,7 @@ public final class CellIdentityCdma extends CellIdentity { if (mNetworkId == CellInfo.UNAVAILABLE || mSystemId == CellInfo.UNAVAILABLE || mBasestationId == CellInfo.UNAVAILABLE) return; - mGlobalCellId = String.format("%04x%04x%04x", mSystemId, mNetworkId, mBasestationId); + mGlobalCellId = formatSimple("%04x%04x%04x", mSystemId, mNetworkId, mBasestationId); } /** diff --git a/telephony/java/android/telephony/CellIdentityGsm.java b/telephony/java/android/telephony/CellIdentityGsm.java index 849c613da748..a3bec339794b 100644 --- a/telephony/java/android/telephony/CellIdentityGsm.java +++ b/telephony/java/android/telephony/CellIdentityGsm.java @@ -16,9 +16,12 @@ package android.telephony; +import static android.text.TextUtils.formatSimple; + import android.annotation.NonNull; import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; +import android.os.Build; import android.os.Parcel; import android.telephony.gsm.GsmCellLocation; import android.text.TextUtils; @@ -56,7 +59,7 @@ public final class CellIdentityGsm extends CellIdentity { /** * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public CellIdentityGsm() { super(TAG, CellInfo.TYPE_GSM, null, null, null, null); mLac = CellInfo.UNAVAILABLE; @@ -147,7 +150,7 @@ public final class CellIdentityGsm extends CellIdentity { if (mLac == CellInfo.UNAVAILABLE || mCid == CellInfo.UNAVAILABLE) return; - mGlobalCellId = plmn + String.format("%04x%04x", mLac, mCid); + mGlobalCellId = plmn + formatSimple("%04x%04x", mLac, mCid); } /** diff --git a/telephony/java/android/telephony/CellIdentityLte.java b/telephony/java/android/telephony/CellIdentityLte.java index e6279dc977ee..bd92d00a0321 100644 --- a/telephony/java/android/telephony/CellIdentityLte.java +++ b/telephony/java/android/telephony/CellIdentityLte.java @@ -16,6 +16,8 @@ package android.telephony; +import static android.text.TextUtils.formatSimple; + import android.annotation.NonNull; import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; @@ -65,7 +67,7 @@ public final class CellIdentityLte extends CellIdentity { /** * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public CellIdentityLte() { super(TAG, CellInfo.TYPE_LTE, null, null, null, null); mCi = CellInfo.UNAVAILABLE; @@ -185,7 +187,7 @@ public final class CellIdentityLte extends CellIdentity { if (mCi == CellInfo.UNAVAILABLE) return; - mGlobalCellId = plmn + String.format("%07x", mCi); + mGlobalCellId = plmn + formatSimple("%07x", mCi); } /** diff --git a/telephony/java/android/telephony/CellIdentityNr.java b/telephony/java/android/telephony/CellIdentityNr.java index e34bbfcde492..4f5052151af5 100644 --- a/telephony/java/android/telephony/CellIdentityNr.java +++ b/telephony/java/android/telephony/CellIdentityNr.java @@ -16,6 +16,8 @@ package android.telephony; +import static android.text.TextUtils.formatSimple; + import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; @@ -37,7 +39,7 @@ public final class CellIdentityNr extends CellIdentity { private static final String TAG = "CellIdentityNr"; private static final int MAX_PCI = 1007; - private static final int MAX_TAC = 65535; + private static final int MAX_TAC = 16777215; // 0xffffff private static final int MAX_NRARFCN = 3279165; private static final long MAX_NCI = 68719476735L; @@ -50,10 +52,22 @@ public final class CellIdentityNr extends CellIdentity { // a list of additional PLMN-IDs reported for this cell private final ArraySet<String> mAdditionalPlmns; + /** @hide */ + public CellIdentityNr() { + super(TAG, CellInfo.TYPE_NR, null, null, null, null); + mNrArfcn = CellInfo.UNAVAILABLE; + mPci = CellInfo.UNAVAILABLE; + mTac = CellInfo.UNAVAILABLE; + mNci = CellInfo.UNAVAILABLE; + mBands = new int[] {}; + mAdditionalPlmns = new ArraySet(); + mGlobalCellId = null; + } + /** * * @param pci Physical Cell Id in range [0, 1007]. - * @param tac 16-bit Tracking Area Code. + * @param tac 24-bit Tracking Area Code. * @param nrArfcn NR Absolute Radio Frequency Channel Number, in range [0, 3279165]. * @param bands Bands used by the cell. Band number defined in 3GPP TS 38.101-1 and TS 38.101-2. * @param mccStr 3-digit Mobile Country Code in string format. @@ -104,7 +118,7 @@ public final class CellIdentityNr extends CellIdentity { @Override public @NonNull CellIdentityNr sanitizeLocationInfo() { return new CellIdentityNr(CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE, mNrArfcn, - mBands, mMccStr, mMncStr, CellInfo.UNAVAILABLE, mAlphaLong, mAlphaShort, + mBands, mMccStr, mMncStr, CellInfo.UNAVAILABLE_LONG, mAlphaLong, mAlphaShort, mAdditionalPlmns); } @@ -116,7 +130,7 @@ public final class CellIdentityNr extends CellIdentity { if (mNci == CellInfo.UNAVAILABLE_LONG) return; - mGlobalCellId = plmn + String.format("%09x", mNci); + mGlobalCellId = plmn + formatSimple("%09x", mNci); } /** @@ -126,7 +140,11 @@ public final class CellIdentityNr extends CellIdentity { @NonNull @Override public CellLocation asCellLocation() { - return new GsmCellLocation(); + GsmCellLocation cl = new GsmCellLocation(); + int tac = mTac != CellInfo.UNAVAILABLE ? mTac : -1; + cl.setLacAndCid(tac, -1); + cl.setPsc(0); + return cl; } @Override @@ -199,9 +217,9 @@ public final class CellIdentityNr extends CellIdentity { /** * Get the tracking area code. - * @return a 16 bit integer or {@link CellInfo#UNAVAILABLE} if unknown. + * @return a 24 bit integer or {@link CellInfo#UNAVAILABLE} if unknown. */ - @IntRange(from = 0, to = 65535) + @IntRange(from = 0, to = 16777215) public int getTac() { return mTac; } @@ -215,7 +233,7 @@ public final class CellIdentityNr extends CellIdentity { } /** - * @return Mobile Network Code in string fomrat, or {@code null} if unknown. + * @return Mobile Network Code in string format, or {@code null} if unknown. */ @Nullable public String getMncString() { diff --git a/telephony/java/android/telephony/CellIdentityTdscdma.java b/telephony/java/android/telephony/CellIdentityTdscdma.java index e74b70939d14..ec07d5494849 100644 --- a/telephony/java/android/telephony/CellIdentityTdscdma.java +++ b/telephony/java/android/telephony/CellIdentityTdscdma.java @@ -16,6 +16,8 @@ package android.telephony; +import static android.text.TextUtils.formatSimple; + import android.annotation.NonNull; import android.annotation.Nullable; import android.os.Parcel; @@ -156,7 +158,7 @@ public final class CellIdentityTdscdma extends CellIdentity { if (mLac == CellInfo.UNAVAILABLE || mCid == CellInfo.UNAVAILABLE) return; - mGlobalCellId = plmn + String.format("%04x%04x", mLac, mCid); + mGlobalCellId = plmn + formatSimple("%04x%04x", mLac, mCid); } /** diff --git a/telephony/java/android/telephony/CellIdentityWcdma.java b/telephony/java/android/telephony/CellIdentityWcdma.java index 40cb27e95151..b04a51d238c1 100644 --- a/telephony/java/android/telephony/CellIdentityWcdma.java +++ b/telephony/java/android/telephony/CellIdentityWcdma.java @@ -16,6 +16,8 @@ package android.telephony; +import static android.text.TextUtils.formatSimple; + import android.annotation.NonNull; import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; @@ -155,7 +157,7 @@ public final class CellIdentityWcdma extends CellIdentity { if (mLac == CellInfo.UNAVAILABLE || mCid == CellInfo.UNAVAILABLE) return; - mGlobalCellId = plmn + String.format("%04x%04x", mLac, mCid); + mGlobalCellId = plmn + formatSimple("%04x%04x", mLac, mCid); } /** diff --git a/telephony/java/android/telephony/CellInfo.java b/telephony/java/android/telephony/CellInfo.java index b381ccecde47..189a4b898886 100644 --- a/telephony/java/android/telephony/CellInfo.java +++ b/telephony/java/android/telephony/CellInfo.java @@ -21,7 +21,6 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.compat.annotation.UnsupportedAppUsage; import android.hardware.radio.V1_4.CellInfo.Info; -import android.hardware.radio.V1_5.CellInfo.CellInfoRatSpecificInfo; import android.os.Parcel; import android.os.Parcelable; @@ -352,6 +351,13 @@ public abstract class CellInfo implements Parcelable { } /** @hide */ + protected CellInfo(android.hardware.radio.V1_6.CellInfo ci, long timeStamp) { + this.mRegistered = ci.registered; + this.mTimeStamp = timeStamp; + this.mCellConnectionStatus = ci.connectionStatus; + } + + /** @hide */ public static CellInfo create(android.hardware.radio.V1_0.CellInfo ci) { if (ci == null) return null; switch(ci.cellInfoType) { @@ -395,17 +401,49 @@ public abstract class CellInfo implements Parcelable { public static CellInfo create(android.hardware.radio.V1_5.CellInfo ci, long timeStamp) { if (ci == null) return null; switch (ci.ratSpecificInfo.getDiscriminator()) { - case CellInfoRatSpecificInfo.hidl_discriminator.gsm: + case android.hardware.radio.V1_5.CellInfo + .CellInfoRatSpecificInfo.hidl_discriminator.gsm: + return new CellInfoGsm(ci, timeStamp); + case android.hardware.radio.V1_5.CellInfo + .CellInfoRatSpecificInfo.hidl_discriminator.cdma: + return new CellInfoCdma(ci, timeStamp); + case android.hardware.radio.V1_5.CellInfo + .CellInfoRatSpecificInfo.hidl_discriminator.lte: + return new CellInfoLte(ci, timeStamp); + case android.hardware.radio.V1_5.CellInfo + .CellInfoRatSpecificInfo.hidl_discriminator.wcdma: + return new CellInfoWcdma(ci, timeStamp); + case android.hardware.radio.V1_5.CellInfo + .CellInfoRatSpecificInfo.hidl_discriminator.tdscdma: + return new CellInfoTdscdma(ci, timeStamp); + case android.hardware.radio.V1_5.CellInfo + .CellInfoRatSpecificInfo.hidl_discriminator.nr: + return new CellInfoNr(ci, timeStamp); + default: return null; + } + } + + /** @hide */ + public static CellInfo create(android.hardware.radio.V1_6.CellInfo ci, long timeStamp) { + if (ci == null) return null; + switch (ci.ratSpecificInfo.getDiscriminator()) { + case android.hardware.radio.V1_6.CellInfo + .CellInfoRatSpecificInfo.hidl_discriminator.gsm: return new CellInfoGsm(ci, timeStamp); - case CellInfoRatSpecificInfo.hidl_discriminator.cdma: + case android.hardware.radio.V1_6.CellInfo + .CellInfoRatSpecificInfo.hidl_discriminator.cdma: return new CellInfoCdma(ci, timeStamp); - case CellInfoRatSpecificInfo.hidl_discriminator.lte: + case android.hardware.radio.V1_6.CellInfo + .CellInfoRatSpecificInfo.hidl_discriminator.lte: return new CellInfoLte(ci, timeStamp); - case CellInfoRatSpecificInfo.hidl_discriminator.wcdma: + case android.hardware.radio.V1_6.CellInfo + .CellInfoRatSpecificInfo.hidl_discriminator.wcdma: return new CellInfoWcdma(ci, timeStamp); - case CellInfoRatSpecificInfo.hidl_discriminator.tdscdma: + case android.hardware.radio.V1_6.CellInfo + .CellInfoRatSpecificInfo.hidl_discriminator.tdscdma: return new CellInfoTdscdma(ci, timeStamp); - case CellInfoRatSpecificInfo.hidl_discriminator.nr: + case android.hardware.radio.V1_6.CellInfo + .CellInfoRatSpecificInfo.hidl_discriminator.nr: return new CellInfoNr(ci, timeStamp); default: return null; } diff --git a/telephony/java/android/telephony/CellInfoCdma.java b/telephony/java/android/telephony/CellInfoCdma.java index 1bef681619ed..dbb30d29eb87 100644 --- a/telephony/java/android/telephony/CellInfoCdma.java +++ b/telephony/java/android/telephony/CellInfoCdma.java @@ -36,7 +36,7 @@ public final class CellInfoCdma extends CellInfo implements Parcelable { private CellSignalStrengthCdma mCellSignalStrengthCdma; /** @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public CellInfoCdma() { super(); mCellIdentityCdma = new CellIdentityCdma(); @@ -87,6 +87,15 @@ public final class CellInfoCdma extends CellInfo implements Parcelable { new CellSignalStrengthCdma(cic.signalStrengthCdma, cic.signalStrengthEvdo); } + /** @hide */ + public CellInfoCdma(android.hardware.radio.V1_6.CellInfo ci, long timeStamp) { + super(ci, timeStamp); + final android.hardware.radio.V1_2.CellInfoCdma cic = ci.ratSpecificInfo.cdma(); + mCellIdentityCdma = new CellIdentityCdma(cic.cellIdentityCdma); + mCellSignalStrengthCdma = + new CellSignalStrengthCdma(cic.signalStrengthCdma, cic.signalStrengthEvdo); + } + /** * @return a {@link CellIdentityCdma} instance. */ diff --git a/telephony/java/android/telephony/CellInfoGsm.java b/telephony/java/android/telephony/CellInfoGsm.java index c19521fdfa99..e1d996e87fcf 100644 --- a/telephony/java/android/telephony/CellInfoGsm.java +++ b/telephony/java/android/telephony/CellInfoGsm.java @@ -18,6 +18,7 @@ package android.telephony; import android.annotation.NonNull; import android.compat.annotation.UnsupportedAppUsage; +import android.os.Build; import android.os.Parcel; import android.os.Parcelable; @@ -35,7 +36,7 @@ public final class CellInfoGsm extends CellInfo implements Parcelable { private CellSignalStrengthGsm mCellSignalStrengthGsm; /** @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public CellInfoGsm() { super(); mCellIdentityGsm = new CellIdentityGsm(); @@ -81,6 +82,14 @@ public final class CellInfoGsm extends CellInfo implements Parcelable { mCellSignalStrengthGsm = new CellSignalStrengthGsm(cig.signalStrengthGsm); } + /** @hide */ + public CellInfoGsm(android.hardware.radio.V1_6.CellInfo ci, long timeStamp) { + super(ci, timeStamp); + final android.hardware.radio.V1_5.CellInfoGsm cig = ci.ratSpecificInfo.gsm(); + mCellIdentityGsm = new CellIdentityGsm(cig.cellIdentityGsm); + mCellSignalStrengthGsm = new CellSignalStrengthGsm(cig.signalStrengthGsm); + } + /** * @return a {@link CellIdentityGsm} instance. */ diff --git a/telephony/java/android/telephony/CellInfoLte.java b/telephony/java/android/telephony/CellInfoLte.java index 320925ea63ae..39b320afdac9 100644 --- a/telephony/java/android/telephony/CellInfoLte.java +++ b/telephony/java/android/telephony/CellInfoLte.java @@ -39,7 +39,7 @@ public final class CellInfoLte extends CellInfo implements Parcelable { private CellConfigLte mCellConfig; /** @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public CellInfoLte() { super(); mCellIdentityLte = new CellIdentityLte(); @@ -91,6 +91,15 @@ public final class CellInfoLte extends CellInfo implements Parcelable { mCellConfig = new CellConfigLte(); } + /** @hide */ + public CellInfoLte(android.hardware.radio.V1_6.CellInfo ci, long timeStamp) { + super(ci, timeStamp); + final android.hardware.radio.V1_6.CellInfoLte cil = ci.ratSpecificInfo.lte(); + mCellIdentityLte = new CellIdentityLte(cil.cellIdentityLte); + mCellSignalStrengthLte = new CellSignalStrengthLte(cil.signalStrengthLte); + mCellConfig = new CellConfigLte(); + } + /** * @return a {@link CellIdentityLte} instance. */ diff --git a/telephony/java/android/telephony/CellInfoNr.java b/telephony/java/android/telephony/CellInfoNr.java index a7e79f93ae89..12e6a38bfdc0 100644 --- a/telephony/java/android/telephony/CellInfoNr.java +++ b/telephony/java/android/telephony/CellInfoNr.java @@ -29,9 +29,16 @@ import java.util.Objects; public final class CellInfoNr extends CellInfo { private static final String TAG = "CellInfoNr"; - private final CellIdentityNr mCellIdentity; + private CellIdentityNr mCellIdentity; private final CellSignalStrengthNr mCellSignalStrength; + /** @hide */ + public CellInfoNr() { + super(); + mCellIdentity = new CellIdentityNr(); + mCellSignalStrength = new CellSignalStrengthNr(); + } + private CellInfoNr(Parcel in) { super(in); mCellIdentity = CellIdentityNr.CREATOR.createFromParcel(in); @@ -61,6 +68,14 @@ public final class CellInfoNr extends CellInfo { mCellSignalStrength = new CellSignalStrengthNr(cil.signalStrengthNr); } + /** @hide */ + public CellInfoNr(android.hardware.radio.V1_6.CellInfo ci, long timeStamp) { + super(ci, timeStamp); + final android.hardware.radio.V1_6.CellInfoNr cil = ci.ratSpecificInfo.nr(); + mCellIdentity = new CellIdentityNr(cil.cellIdentityNr); + mCellSignalStrength = new CellSignalStrengthNr(cil.signalStrengthNr); + } + /** * @return a {@link CellIdentityNr} instance. */ @@ -71,6 +86,11 @@ public final class CellInfoNr extends CellInfo { return mCellIdentity; } + /** @hide */ + public void setCellIdentity(CellIdentityNr cid) { + mCellIdentity = cid; + } + /** * @return a {@link CellSignalStrengthNr} instance. */ diff --git a/telephony/java/android/telephony/CellInfoTdscdma.java b/telephony/java/android/telephony/CellInfoTdscdma.java index 038c49ac37ee..994b317a47fd 100644 --- a/telephony/java/android/telephony/CellInfoTdscdma.java +++ b/telephony/java/android/telephony/CellInfoTdscdma.java @@ -85,6 +85,14 @@ public final class CellInfoTdscdma extends CellInfo implements Parcelable { mCellSignalStrengthTdscdma = new CellSignalStrengthTdscdma(cit.signalStrengthTdscdma); } + /** @hide */ + public CellInfoTdscdma(android.hardware.radio.V1_6.CellInfo ci, long timeStamp) { + super(ci, timeStamp); + final android.hardware.radio.V1_5.CellInfoTdscdma cit = ci.ratSpecificInfo.tdscdma(); + mCellIdentityTdscdma = new CellIdentityTdscdma(cit.cellIdentityTdscdma); + mCellSignalStrengthTdscdma = new CellSignalStrengthTdscdma(cit.signalStrengthTdscdma); + } + /** * @return a {@link CellIdentityTdscdma} instance. */ diff --git a/telephony/java/android/telephony/CellInfoWcdma.java b/telephony/java/android/telephony/CellInfoWcdma.java index c74955f807b0..62ac0b8ae04e 100644 --- a/telephony/java/android/telephony/CellInfoWcdma.java +++ b/telephony/java/android/telephony/CellInfoWcdma.java @@ -80,6 +80,14 @@ public final class CellInfoWcdma extends CellInfo implements Parcelable { mCellSignalStrengthWcdma = new CellSignalStrengthWcdma(ciw.signalStrengthWcdma); } + /** @hide */ + public CellInfoWcdma(android.hardware.radio.V1_6.CellInfo ci, long timeStamp) { + super(ci, timeStamp); + final android.hardware.radio.V1_5.CellInfoWcdma ciw = ci.ratSpecificInfo.wcdma(); + mCellIdentityWcdma = new CellIdentityWcdma(ciw.cellIdentityWcdma); + mCellSignalStrengthWcdma = new CellSignalStrengthWcdma(ciw.signalStrengthWcdma); + } + /** * @return a {@link CellIdentityWcdma} instance. */ diff --git a/telephony/java/android/telephony/CellLocation.java b/telephony/java/android/telephony/CellLocation.java index 2d0bd52f84ee..e595002d175b 100644 --- a/telephony/java/android/telephony/CellLocation.java +++ b/telephony/java/android/telephony/CellLocation.java @@ -16,7 +16,9 @@ package android.telephony; +import android.app.ActivityThread; import android.compat.annotation.UnsupportedAppUsage; +import android.content.Context; import android.os.Bundle; import android.os.RemoteException; import android.telephony.cdma.CdmaCellLocation; @@ -27,15 +29,34 @@ import com.android.internal.telephony.PhoneConstants; /** * Abstract class that represents the location of the device. {@more} + * + * @deprecated use {@link android.telephony.CellIdentity CellIdentity}. */ +@Deprecated public abstract class CellLocation { /** - * Request an update of the current location. If the location has changed, - * a broadcast will be sent to everyone registered with {@link - * PhoneStateListener#LISTEN_CELL_LOCATION}. + * Request an updated CellLocation for callers targeting SDK 30 or older. + * + * Whenever Android is aware of location changes, a callback will automatically be sent to + * all registrants of {@link PhoneStateListener#LISTEN_CELL_LOCATION}. This API requests an + * additional location update for cases where power saving might cause location updates to be + * missed. + * + * <p>This method is a no-op for callers targeting SDK level 31 or greater. + * <p>This method is a no-op for callers that target SDK level 29 or 30 and lack + * {@link android.Manifest.permission#ACCESS_FINE_LOCATION}. + * <p>This method is a no-op for callers that target SDK level 28 or below and lack + * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}. + * + * @deprecated use {@link TelephonyManager#requestCellInfoUpdate}. */ + @Deprecated public static void requestLocationUpdate() { + // Since this object doesn't have a context, this is the best we can do. + final Context appContext = ActivityThread.currentApplication(); + if (appContext == null) return; // should never happen + try { ITelephony phone = ITelephony.Stub.asInterface( TelephonyFrameworkInitializer @@ -43,7 +64,7 @@ public abstract class CellLocation { .getTelephonyServiceRegisterer() .get()); if (phone != null) { - phone.updateServiceLocation(); + phone.updateServiceLocationWithPackageName(appContext.getOpPackageName()); } } catch (RemoteException ex) { // ignore it @@ -77,12 +98,14 @@ public abstract class CellLocation { /** * @hide */ + @SuppressWarnings("HiddenAbstractMethod") @UnsupportedAppUsage public abstract void fillInNotifierBundle(Bundle bundle); /** * @hide */ + @SuppressWarnings("HiddenAbstractMethod") @UnsupportedAppUsage public abstract boolean isEmpty(); @@ -90,6 +113,7 @@ public abstract class CellLocation { * Invalidate this object. The location area code and the cell id are set to -1. * @hide */ + @SuppressWarnings("HiddenAbstractMethod") public abstract void setStateInvalid(); /** diff --git a/telephony/java/android/telephony/CellSignalStrength.java b/telephony/java/android/telephony/CellSignalStrength.java index 2e7bde3b3d89..e0896570d3ed 100644 --- a/telephony/java/android/telephony/CellSignalStrength.java +++ b/telephony/java/android/telephony/CellSignalStrength.java @@ -17,6 +17,7 @@ package android.telephony; import android.annotation.IntRange; +import android.annotation.SystemApi; import android.os.PersistableBundle; /** @@ -155,11 +156,12 @@ public abstract class CellSignalStrength { /** * Returns the number of signal strength levels. - * @return Number of signal strength levels, enforced to be 5 + * @return Number of signal strength levels, currently defined in the HAL as 5. * * @hide */ - public static final int getNumSignalStrengthLevels() { + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + public static int getNumSignalStrengthLevels() { return NUM_SIGNAL_STRENGTH_BINS; } } diff --git a/telephony/java/android/telephony/CellSignalStrengthGsm.java b/telephony/java/android/telephony/CellSignalStrengthGsm.java index 9d55f109f751..51e1ebc63cf2 100644 --- a/telephony/java/android/telephony/CellSignalStrengthGsm.java +++ b/telephony/java/android/telephony/CellSignalStrengthGsm.java @@ -53,7 +53,7 @@ public final class CellSignalStrengthGsm extends CellSignalStrength implements P private int mLevel; /** @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public CellSignalStrengthGsm() { setDefaultValues(); } @@ -158,7 +158,8 @@ public final class CellSignalStrengthGsm extends CellSignalStrength implements P * * Asu is calculated based on 3GPP RSSI. Refer to 3GPP 27.007 (Ver 10.3.0) Sec 8.69 * - * @return RSSI in ASU 0..31, 99, or UNAVAILABLE + * @return RSSI in ASU 0..31, 99, or + * {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE}. */ @Override public int getAsuLevel() { diff --git a/telephony/java/android/telephony/CellSignalStrengthLte.java b/telephony/java/android/telephony/CellSignalStrengthLte.java index c26936e4bdd0..7addf334e967 100644 --- a/telephony/java/android/telephony/CellSignalStrengthLte.java +++ b/telephony/java/android/telephony/CellSignalStrengthLte.java @@ -18,6 +18,7 @@ package android.telephony; import android.annotation.IntRange; import android.compat.annotation.UnsupportedAppUsage; +import android.os.Build; import android.os.Parcel; import android.os.Parcelable; import android.os.PersistableBundle; @@ -85,6 +86,15 @@ public final class CellSignalStrengthLte extends CellSignalStrength implements P private int mRsrq; @UnsupportedAppUsage(maxTargetSdk = android.os.Build.VERSION_CODES.P) private int mRssnr; + /** + * CSI channel quality indicator (CQI) table index. There are multiple CQI tables. + * The definition of CQI in each table is different. + * + * Reference: 3GPP TS 136.213 section 7.2.3. + * + * Range [1, 6]. + */ + private int mCqiTableIndex; @UnsupportedAppUsage(maxTargetSdk = android.os.Build.VERSION_CODES.P) private int mCqi; @UnsupportedAppUsage(maxTargetSdk = android.os.Build.VERSION_CODES.P) @@ -107,7 +117,7 @@ public final class CellSignalStrengthLte extends CellSignalStrength implements P private int mParametersUseForLevel; /** @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public CellSignalStrengthLte() { setDefaultValues(); } @@ -119,24 +129,42 @@ public final class CellSignalStrengthLte extends CellSignalStrength implements P * @param rsrp in dBm [-140,-43], UNKNOWN * @param rsrq in dB [-34, 3], UNKNOWN * @param rssnr in dB [-20, +30], UNKNOWN + * @param cqiTableIndex [1, 6], UNKNOWN * @param cqi [0, 15], UNKNOWN * @param timingAdvance [0, 1282], UNKNOWN * */ /** @hide */ - public CellSignalStrengthLte(int rssi, int rsrp, int rsrq, int rssnr, int cqi, - int timingAdvance) { - + public CellSignalStrengthLte(int rssi, int rsrp, int rsrq, int rssnr, int cqiTableIndex, + int cqi, int timingAdvance) { mRssi = inRangeOrUnavailable(rssi, -113, -51); mSignalStrength = mRssi; mRsrp = inRangeOrUnavailable(rsrp, -140, -43); mRsrq = inRangeOrUnavailable(rsrq, -34, 3); mRssnr = inRangeOrUnavailable(rssnr, -20, 30); + mCqiTableIndex = inRangeOrUnavailable(cqiTableIndex, 1, 6); mCqi = inRangeOrUnavailable(cqi, 0, 15); mTimingAdvance = inRangeOrUnavailable(timingAdvance, 0, 1282); updateLevel(null, null); } + /** + * Construct a cell signal strength + * + * @param rssi in dBm [-113,-51], UNKNOWN + * @param rsrp in dBm [-140,-43], UNKNOWN + * @param rsrq in dB [-34, 3], UNKNOWN + * @param rssnr in dB [-20, +30], UNKNOWN + * @param cqi [0, 15], UNKNOWN + * @param timingAdvance [0, 1282], UNKNOWN + * + */ + /** @hide */ + public CellSignalStrengthLte(int rssi, int rsrp, int rsrq, int rssnr, int cqi, + int timingAdvance) { + this(rssi, rsrp, rsrq, rssnr, CellInfo.UNAVAILABLE, cqi, timingAdvance); + } + /** @hide */ public CellSignalStrengthLte(android.hardware.radio.V1_0.LteSignalStrength lte) { // Convert from HAL values as part of construction. @@ -147,6 +175,16 @@ public final class CellSignalStrengthLte extends CellSignalStrength implements P } /** @hide */ + public CellSignalStrengthLte(android.hardware.radio.V1_6.LteSignalStrength lte) { + // Convert from HAL values as part of construction. + this(convertRssiAsuToDBm(lte.base.signalStrength), + lte.base.rsrp != CellInfo.UNAVAILABLE ? -lte.base.rsrp : lte.base.rsrp, + lte.base.rsrq != CellInfo.UNAVAILABLE ? -lte.base.rsrq : lte.base.rsrq, + convertRssnrUnitFromTenDbToDB(lte.base.rssnr), lte.cqiTableIndex, lte.base.cqi, + lte.base.timingAdvance); + } + + /** @hide */ public CellSignalStrengthLte(CellSignalStrengthLte s) { copyFrom(s); } @@ -158,6 +196,7 @@ public final class CellSignalStrengthLte extends CellSignalStrength implements P mRsrp = s.mRsrp; mRsrq = s.mRsrq; mRssnr = s.mRssnr; + mCqiTableIndex = s.mCqiTableIndex; mCqi = s.mCqi; mTimingAdvance = s.mTimingAdvance; mLevel = s.mLevel; @@ -178,6 +217,7 @@ public final class CellSignalStrengthLte extends CellSignalStrength implements P mRsrp = CellInfo.UNAVAILABLE; mRsrq = CellInfo.UNAVAILABLE; mRssnr = CellInfo.UNAVAILABLE; + mCqiTableIndex = CellInfo.UNAVAILABLE; mCqi = CellInfo.UNAVAILABLE; mTimingAdvance = CellInfo.UNAVAILABLE; mLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN; @@ -401,11 +441,27 @@ public final class CellSignalStrengthLte extends CellSignalStrength implements P } /** + * Get table index for channel quality indicator + * + * Reference: 3GPP TS 136.213 section 7.2.3. + * + * @return the CQI table index if available or + * {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable. + */ + @IntRange(from = 1, to = 6) + public int getCqiTableIndex() { + return mCqiTableIndex; + } + + /** * Get channel quality indicator * + * Reference: 3GPP TS 136.213 section 7.2.3. + * * @return the CQI if available or * {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable. */ + @IntRange(from = 0, to = 15) public int getCqi() { return mCqi; } @@ -453,7 +509,8 @@ public final class CellSignalStrengthLte extends CellSignalStrength implements P @Override public int hashCode() { - return Objects.hash(mRssi, mRsrp, mRsrq, mRssnr, mCqi, mTimingAdvance, mLevel); + return Objects.hash(mRssi, mRsrp, mRsrq, mRssnr, mCqiTableIndex, mCqi, mTimingAdvance, + mLevel); } private static final CellSignalStrengthLte sInvalid = new CellSignalStrengthLte(); @@ -475,6 +532,7 @@ public final class CellSignalStrengthLte extends CellSignalStrength implements P && mRsrp == s.mRsrp && mRsrq == s.mRsrq && mRssnr == s.mRssnr + && mCqiTableIndex == s.mCqiTableIndex && mCqi == s.mCqi && mTimingAdvance == s.mTimingAdvance && mLevel == s.mLevel; @@ -490,6 +548,7 @@ public final class CellSignalStrengthLte extends CellSignalStrength implements P + " rsrp=" + mRsrp + " rsrq=" + mRsrq + " rssnr=" + mRssnr + + " cqiTableIndex=" + mCqiTableIndex + " cqi=" + mCqi + " ta=" + mTimingAdvance + " level=" + mLevel @@ -507,6 +566,7 @@ public final class CellSignalStrengthLte extends CellSignalStrength implements P dest.writeInt(mRsrp); dest.writeInt(mRsrq); dest.writeInt(mRssnr); + dest.writeInt(mCqiTableIndex); dest.writeInt(mCqi); dest.writeInt(mTimingAdvance); dest.writeInt(mLevel); @@ -522,6 +582,7 @@ public final class CellSignalStrengthLte extends CellSignalStrength implements P mRsrp = in.readInt(); mRsrq = in.readInt(); mRssnr = in.readInt(); + mCqiTableIndex = in.readInt(); mCqi = in.readInt(); mTimingAdvance = in.readInt(); mLevel = in.readInt(); diff --git a/telephony/java/android/telephony/CellSignalStrengthNr.java b/telephony/java/android/telephony/CellSignalStrengthNr.java index 95fe90a47654..ac01afa51729 100644 --- a/telephony/java/android/telephony/CellSignalStrengthNr.java +++ b/telephony/java/android/telephony/CellSignalStrengthNr.java @@ -18,6 +18,7 @@ package android.telephony; import android.annotation.IntDef; import android.annotation.IntRange; +import android.annotation.NonNull; import android.os.Parcel; import android.os.Parcelable; import android.os.PersistableBundle; @@ -27,7 +28,10 @@ import com.android.telephony.Rlog; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Arrays; +import java.util.Collections; +import java.util.List; import java.util.Objects; +import java.util.stream.Collectors; /** * 5G NR signal strength related information. @@ -54,12 +58,12 @@ public final class CellSignalStrengthNr extends CellSignalStrength implements Pa }; // Lifted from Default carrier configs and max range of SSRSRQ - // Boundaries: [-20 dB, -3 dB] + // Boundaries: [-43 dB, 20 dB] private int[] mSsRsrqThresholds = new int[] { - -16, /* SIGNAL_STRENGTH_POOR */ - -12, /* SIGNAL_STRENGTH_MODERATE */ - -9, /* SIGNAL_STRENGTH_GOOD */ - -6 /* SIGNAL_STRENGTH_GREAT */ + -31, /* SIGNAL_STRENGTH_POOR */ + -19, /* SIGNAL_STRENGTH_MODERATE */ + -7, /* SIGNAL_STRENGTH_GOOD */ + 6 /* SIGNAL_STRENGTH_GREAT */ }; // Lifted from Default carrier configs and max range of SSSINR @@ -109,6 +113,28 @@ public final class CellSignalStrengthNr extends CellSignalStrength implements Pa private int mCsiRsrp; private int mCsiRsrq; private int mCsiSinr; + /** + * CSI channel quality indicator (CQI) table index. There are multiple CQI tables. + * The definition of CQI in each table is different. + * + * Reference: 3GPP TS 138.214 section 5.2.2.1. + * + * Range [1, 3]. + */ + private int mCsiCqiTableIndex; + /** + * CSI channel quality indicators (CQI) for all subbands. + * + * If the CQI report is for the entire wideband, a single CQI index is provided. + * If the CQI report is for all subbands, one CQI index is provided for each subband, + * in ascending order of subband index. + * If CQI is not available, the CQI report is empty. + * + * Reference: 3GPP TS 138.214 section 5.2.2.1. + * + * Range [0, 15] for each CQI. + */ + private List<Integer> mCsiCqiReport; private int mSsRsrp; private int mSsRsrq; private int mSsSinr; @@ -138,23 +164,44 @@ public final class CellSignalStrengthNr extends CellSignalStrength implements Pa * @param csiRsrp CSI reference signal received power. * @param csiRsrq CSI reference signal received quality. * @param csiSinr CSI signal-to-noise and interference ratio. + * @param csiCqiTableIndex CSI CSI channel quality indicator (CQI) table index. + * @param csiCqiReport CSI channel quality indicators (CQI) for all subbands. * @param ssRsrp SS reference signal received power. * @param ssRsrq SS reference signal received quality. * @param ssSinr SS signal-to-noise and interference ratio. * @hide */ - public CellSignalStrengthNr( - int csiRsrp, int csiRsrq, int csiSinr, int ssRsrp, int ssRsrq, int ssSinr) { + public CellSignalStrengthNr(int csiRsrp, int csiRsrq, int csiSinr, int csiCqiTableIndex, + List<Byte> csiCqiReport, int ssRsrp, int ssRsrq, int ssSinr) { mCsiRsrp = inRangeOrUnavailable(csiRsrp, -140, -44); mCsiRsrq = inRangeOrUnavailable(csiRsrq, -20, -3); mCsiSinr = inRangeOrUnavailable(csiSinr, -23, 23); + mCsiCqiTableIndex = inRangeOrUnavailable(csiCqiTableIndex, 1, 3); + mCsiCqiReport = csiCqiReport.stream() + .map(cqi -> new Integer(inRangeOrUnavailable(Byte.toUnsignedInt(cqi), 1, 3))) + .collect(Collectors.toList()); mSsRsrp = inRangeOrUnavailable(ssRsrp, -140, -44); - mSsRsrq = inRangeOrUnavailable(ssRsrq, -20, -3); + mSsRsrq = inRangeOrUnavailable(ssRsrq, -43, 20); mSsSinr = inRangeOrUnavailable(ssSinr, -23, 40); updateLevel(null, null); } /** + * @param csiRsrp CSI reference signal received power. + * @param csiRsrq CSI reference signal received quality. + * @param csiSinr CSI signal-to-noise and interference ratio. + * @param ssRsrp SS reference signal received power. + * @param ssRsrq SS reference signal received quality. + * @param ssSinr SS signal-to-noise and interference ratio. + * @hide + */ + public CellSignalStrengthNr( + int csiRsrp, int csiRsrq, int csiSinr, int ssRsrp, int ssRsrq, int ssSinr) { + this(csiRsrp, csiRsrq, csiSinr, CellInfo.UNAVAILABLE, Collections.emptyList(), + ssRsrp, ssRsrq, ssSinr); + } + + /** * @hide * @param ss signal strength from modem. */ @@ -164,6 +211,15 @@ public final class CellSignalStrengthNr extends CellSignalStrength implements Pa } /** + * @hide + * @param ss signal strength from modem. + */ + public CellSignalStrengthNr(android.hardware.radio.V1_6.NrSignalStrength ss) { + this(flip(ss.base.csiRsrp), flip(ss.base.csiRsrq), ss.base.csiSinr, ss.csiCqiTableIndex, + ss.csiCqiReport, flip(ss.base.ssRsrp), flip(ss.base.ssRsrq), ss.base.ssSinr); + } + + /** * Flip sign cell strength value when taking in the value from hal * @param val cell strength value * @return flipped value @@ -183,8 +239,8 @@ public final class CellSignalStrengthNr extends CellSignalStrength implements Pa } /** - * Reference: 3GPP TS 38.215. - * Range: -20 dB to -3 dB. + * Reference: 3GPP TS 38.215; 3GPP TS 38.133 section 10 + * Range: -43 dB to 20 dB. * @return SS reference signal received quality, {@link CellInfo#UNAVAILABLE} means unreported * value. */ @@ -232,6 +288,37 @@ public final class CellSignalStrengthNr extends CellSignalStrength implements Pa return mCsiSinr; } + /** + * Return CSI channel quality indicator (CQI) table index. There are multiple CQI tables. + * The definition of CQI in each table is different. + * + * Reference: 3GPP TS 138.214 section 5.2.2.1. + * + * @return the CQI table index if available or + * {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable. + */ + @IntRange(from = 1, to = 3) + public int getCsiCqiTableIndex() { + return mCsiCqiTableIndex; + } + /** + * Return a list of CSI channel quality indicators (CQI) for all subbands. + * + * If the CQI report is for the entire wideband, a single CQI index is provided. + * If the CQI report is for all subbands, one CQI index is provided for each subband, + * in ascending order of subband index. + * If CQI is not available, the CQI report is empty. + * + * Reference: 3GPP TS 138.214 section 5.2.2.1. + * + * @return the CQIs for all subbands if available or empty list if unavailable. + */ + @NonNull + @IntRange(from = 0, to = 15) + public List<Integer> getCsiCqiReport() { + return mCsiCqiReport; + } + @Override public int describeContents() { return 0; @@ -243,6 +330,8 @@ public final class CellSignalStrengthNr extends CellSignalStrength implements Pa dest.writeInt(mCsiRsrp); dest.writeInt(mCsiRsrq); dest.writeInt(mCsiSinr); + dest.writeInt(mCsiCqiTableIndex); + dest.writeList(mCsiCqiReport); dest.writeInt(mSsRsrp); dest.writeInt(mSsRsrq); dest.writeInt(mSsSinr); @@ -253,6 +342,8 @@ public final class CellSignalStrengthNr extends CellSignalStrength implements Pa mCsiRsrp = in.readInt(); mCsiRsrq = in.readInt(); mCsiSinr = in.readInt(); + mCsiCqiTableIndex = in.readInt(); + mCsiCqiReport = in.readArrayList(Integer.class.getClassLoader()); mSsRsrp = in.readInt(); mSsRsrq = in.readInt(); mSsSinr = in.readInt(); @@ -265,6 +356,8 @@ public final class CellSignalStrengthNr extends CellSignalStrength implements Pa mCsiRsrp = CellInfo.UNAVAILABLE; mCsiRsrq = CellInfo.UNAVAILABLE; mCsiSinr = CellInfo.UNAVAILABLE; + mCsiCqiTableIndex = CellInfo.UNAVAILABLE; + mCsiCqiReport = Collections.emptyList(); mSsRsrp = CellInfo.UNAVAILABLE; mSsRsrq = CellInfo.UNAVAILABLE; mSsSinr = CellInfo.UNAVAILABLE; @@ -408,6 +501,8 @@ public final class CellSignalStrengthNr extends CellSignalStrength implements Pa mCsiRsrp = s.mCsiRsrp; mCsiRsrq = s.mCsiRsrq; mCsiSinr = s.mCsiSinr; + mCsiCqiTableIndex = s.mCsiCqiTableIndex; + mCsiCqiReport = s.mCsiCqiReport; mSsRsrp = s.mSsRsrp; mSsRsrq = s.mSsRsrq; mSsSinr = s.mSsSinr; @@ -423,7 +518,8 @@ public final class CellSignalStrengthNr extends CellSignalStrength implements Pa @Override public int hashCode() { - return Objects.hash(mCsiRsrp, mCsiRsrq, mCsiSinr, mSsRsrp, mSsRsrq, mSsSinr, mLevel); + return Objects.hash(mCsiRsrp, mCsiRsrq, mCsiSinr, mCsiCqiTableIndex, + mCsiCqiReport, mSsRsrp, mSsRsrq, mSsSinr, mLevel); } private static final CellSignalStrengthNr sInvalid = new CellSignalStrengthNr(); @@ -439,6 +535,8 @@ public final class CellSignalStrengthNr extends CellSignalStrength implements Pa if (obj instanceof CellSignalStrengthNr) { CellSignalStrengthNr o = (CellSignalStrengthNr) obj; return mCsiRsrp == o.mCsiRsrp && mCsiRsrq == o.mCsiRsrq && mCsiSinr == o.mCsiSinr + && mCsiCqiTableIndex == o.mCsiCqiTableIndex + && mCsiCqiReport.equals(o.mCsiCqiReport) && mSsRsrp == o.mSsRsrp && mSsRsrq == o.mSsRsrq && mSsSinr == o.mSsSinr && mLevel == o.mLevel; } @@ -451,7 +549,8 @@ public final class CellSignalStrengthNr extends CellSignalStrength implements Pa .append(TAG + ":{") .append(" csiRsrp = " + mCsiRsrp) .append(" csiRsrq = " + mCsiRsrq) - .append(" csiSinr = " + mCsiSinr) + .append(" csiCqiTableIndex = " + mCsiCqiTableIndex) + .append(" csiCqiReport = " + mCsiCqiReport) .append(" ssRsrp = " + mSsRsrp) .append(" ssRsrq = " + mSsRsrq) .append(" ssSinr = " + mSsSinr) diff --git a/telephony/java/android/telephony/DataFailCause.java b/telephony/java/android/telephony/DataFailCause.java index 8b7a243c30e6..4d5b6ace39ab 100644 --- a/telephony/java/android/telephony/DataFailCause.java +++ b/telephony/java/android/telephony/DataFailCause.java @@ -915,6 +915,90 @@ public final class DataFailCause { public static final int IPV6_PREFIX_UNAVAILABLE = 0x8CA; /** System preference change back to SRAT during handoff */ public static final int HANDOFF_PREFERENCE_CHANGED = 0x8CB; + /** Data call fail due to the slice not being allowed for the data call. */ + public static final int SLICE_REJECTED = 0x8CC; + /** No matching rule available for the request, and match-all rule is not allowed for it. */ + public static final int MATCH_ALL_RULE_NOT_ALLOWED = 0x8CD; + /** If connection failed for all matching URSP rules. */ + public static final int ALL_MATCHING_RULES_FAILED = 0x8CE; + + //IKE error notifications message as specified in 3GPP TS 24.302 (Section 8.1.2.2). + + /** The PDN connection corresponding to the requested APN has been rejected. */ + public static final int IWLAN_PDN_CONNECTION_REJECTION = 0x2000; + /** + * The PDN connection has been rejected. No additional PDN connections can be established + * for the UE due to the network policies or capabilities. + */ + public static final int IWLAN_MAX_CONNECTION_REACHED = 0x2001; + /** + * The PDN connection has been rejected due to a semantic error in TFT operation. + */ + public static final int IWLAN_SEMANTIC_ERROR_IN_THE_TFT_OPERATION = 0x2031; + /** + * The PDN connection has been rejected due to a syntactic error in TFT operation. + */ + public static final int IWLAN_SYNTACTICAL_ERROR_IN_THE_TFT_OPERATION = 0x2032; + /** + * The PDN connection has been rejected due to sematic errors in the packet filter. + */ + public static final int IWLAN_SEMANTIC_ERRORS_IN_PACKET_FILTERS = 0x2034; + /** + * The PDN connection has been rejected due to syntactic errors in the packet filter. + */ + public static final int IWLAN_SYNTACTICAL_ERRORS_IN_PACKET_FILTERS = 0x2035; + /** + * No non-3GPP subscription is associated with the IMSI. + * The UE is not allowed to use non-3GPP access to EPC. + */ + public static final int IWLAN_NON_3GPP_ACCESS_TO_EPC_NOT_ALLOWED = 0x2328; + /** The user identified by the IMSI is unknown. */ + public static final int IWLAN_USER_UNKNOWN = 0x2329; + /** + * The requested APN is not included in the user's profile, + * and therefore is not authorized for that user. + */ + public static final int IWLAN_NO_APN_SUBSCRIPTION = 0x232A; + /** The user is barred from using the non-3GPP access or the subscribed APN. */ + public static final int IWLAN_AUTHORIZATION_REJECTED = 0x232B; + /** The Mobile Equipment used is not acceptable to the network */ + public static final int IWLAN_ILLEGAL_ME = 0x232E; + /** + * The network has determined that the requested procedure cannot be completed successfully + * due to network failure. + */ + public static final int IWLAN_NETWORK_FAILURE = 0x2904; + /** The access type is restricted to the user. */ + public static final int IWLAN_RAT_TYPE_NOT_ALLOWED = 0x2AF9; + /** The network does not accept emergency PDN bringup request using an IMEI */ + public static final int IWLAN_IMEI_NOT_ACCEPTED = 0x2AFD; + /** + * The ePDG performs PLMN filtering (based on roaming agreements) and rejects + * the request from the UE. + * The UE requests service in a PLMN where the UE is not allowed to operate. + */ + public static final int IWLAN_PLMN_NOT_ALLOWED = 0x2B03; + /** The ePDG does not support un-authenticated IMSI based emergency PDN bringup **/ + public static final int IWLAN_UNAUTHENTICATED_EMERGENCY_NOT_SUPPORTED = 0x2B2F; + + // Device is unable to establish an IPSec tunnel with the ePDG for any reason + // e.g authentication fail or certificate validation or DNS Resolution and timeout failure. + + /** IKE configuration error resulting in failure */ + public static final int IWLAN_IKEV2_CONFIG_FAILURE = 0x4000; + /** + * Sent in the response to an IKE_AUTH message when, for some reason, + * the authentication failed. + */ + public static final int IWLAN_IKEV2_AUTH_FAILURE = 0x4001; + /** IKE message timeout, tunnel setup failed due to no response from EPDG */ + public static final int IWLAN_IKEV2_MSG_TIMEOUT = 0x4002; + /** IKE Certification validation failure */ + public static final int IWLAN_IKEV2_CERT_INVALID = 0x4003; + /** Unable to resolve FQDN for the ePDG to an IP address */ + public static final int IWLAN_DNS_RESOLUTION_NAME_FAILURE = 0x4004; + /** No response received from the DNS Server due to a timeout*/ + public static final int IWLAN_DNS_RESOLUTION_TIMEOUT = 0x4005; // OEM sepecific error codes. To be used by OEMs when they don't // want to reveal error code which would be replaced by ERROR_UNSPECIFIED @@ -971,6 +1055,20 @@ public final class DataFailCause { */ public static final int HANDOVER_FAILED = 0x10006; + /** + * Enterprise setup failure: duplicate CID in DataCallResponse. + * + * @hide + */ + public static final int DUPLICATE_CID = 0x10007; + + /** + * Enterprise setup failure: no default data connection set up yet. + * + * @hide + */ + public static final int NO_DEFAULT_DATA = 0x10008; + private static final Map<Integer, String> sFailCauseMap; static { sFailCauseMap = new HashMap<>(); @@ -1341,6 +1439,37 @@ public final class DataFailCause { sFailCauseMap.put(VSNCP_RECONNECT_NOT_ALLOWED, "VSNCP_RECONNECT_NOT_ALLOWED"); sFailCauseMap.put(IPV6_PREFIX_UNAVAILABLE, "IPV6_PREFIX_UNAVAILABLE"); sFailCauseMap.put(HANDOFF_PREFERENCE_CHANGED, "HANDOFF_PREFERENCE_CHANGED"); + sFailCauseMap.put(SLICE_REJECTED, "SLICE_REJECTED"); + sFailCauseMap.put(MATCH_ALL_RULE_NOT_ALLOWED, "MATCH_ALL_RULE_NOT_ALLOWED"); + sFailCauseMap.put(ALL_MATCHING_RULES_FAILED, "ALL_MATCHING_RULES_FAILED"); + sFailCauseMap.put(IWLAN_PDN_CONNECTION_REJECTION, "IWLAN_PDN_CONNECTION_REJECTION"); + sFailCauseMap.put(IWLAN_MAX_CONNECTION_REACHED, "IWLAN_MAX_CONNECTION_REACHED"); + sFailCauseMap.put(IWLAN_SEMANTIC_ERROR_IN_THE_TFT_OPERATION, + "IWLAN_SEMANTIC_ERROR_IN_THE_TFT_OPERATION"); + sFailCauseMap.put(IWLAN_SYNTACTICAL_ERROR_IN_THE_TFT_OPERATION, + "IWLAN_SYNTACTICAL_ERROR_IN_THE_TFT_OPERATION"); + sFailCauseMap.put(IWLAN_SEMANTIC_ERRORS_IN_PACKET_FILTERS, + "IWLAN_SEMANTIC_ERRORS_IN_PACKET_FILTERS"); + sFailCauseMap.put(IWLAN_SYNTACTICAL_ERRORS_IN_PACKET_FILTERS, + "IWLAN_SYNTACTICAL_ERRORS_IN_PACKET_FILTERS"); + sFailCauseMap.put(IWLAN_NON_3GPP_ACCESS_TO_EPC_NOT_ALLOWED, + "IWLAN_NON_3GPP_ACCESS_TO_EPC_NOT_ALLOWED"); + sFailCauseMap.put(IWLAN_USER_UNKNOWN, "IWLAN_USER_UNKNOWN"); + sFailCauseMap.put(IWLAN_NO_APN_SUBSCRIPTION, "IWLAN_NO_APN_SUBSCRIPTION"); + sFailCauseMap.put(IWLAN_AUTHORIZATION_REJECTED, "IWLAN_AUTHORIZATION_REJECTED"); + sFailCauseMap.put(IWLAN_ILLEGAL_ME, "IWLAN_ILLEGAL_ME"); + sFailCauseMap.put(IWLAN_NETWORK_FAILURE, "IWLAN_NETWORK_FAILURE"); + sFailCauseMap.put(IWLAN_RAT_TYPE_NOT_ALLOWED, "IWLAN_RAT_TYPE_NOT_ALLOWED"); + sFailCauseMap.put(IWLAN_IMEI_NOT_ACCEPTED, "IWLAN_IMEI_NOT_ACCEPTED"); + sFailCauseMap.put(IWLAN_PLMN_NOT_ALLOWED, "IWLAN_PLMN_NOT_ALLOWED"); + sFailCauseMap.put(IWLAN_UNAUTHENTICATED_EMERGENCY_NOT_SUPPORTED, + "IWLAN_UNAUTHENTICATED_EMERGENCY_NOT_SUPPORTED"); + sFailCauseMap.put(IWLAN_IKEV2_CONFIG_FAILURE, "IWLAN_IKEV2_CONFIG_FAILURE"); + sFailCauseMap.put(IWLAN_IKEV2_AUTH_FAILURE, "IWLAN_IKEV2_AUTH_FAILURE"); + sFailCauseMap.put(IWLAN_IKEV2_MSG_TIMEOUT, "IWLAN_IKEV2_MSG_TIMEOUT"); + sFailCauseMap.put(IWLAN_IKEV2_CERT_INVALID, "IWLAN_IKEV2_CERT_INVALID"); + sFailCauseMap.put(IWLAN_DNS_RESOLUTION_NAME_FAILURE, "IWLAN_DNS_RESOLUTION_NAME_FAILURE"); + sFailCauseMap.put(IWLAN_DNS_RESOLUTION_TIMEOUT, "IWLAN_DNS_RESOLUTION_TIMEOUT"); sFailCauseMap.put(OEM_DCFAILCAUSE_1, "OEM_DCFAILCAUSE_1"); sFailCauseMap.put(OEM_DCFAILCAUSE_2, "OEM_DCFAILCAUSE_2"); sFailCauseMap.put(OEM_DCFAILCAUSE_3, "OEM_DCFAILCAUSE_3"); @@ -1368,6 +1497,9 @@ public final class DataFailCause { sFailCauseMap.put(UNACCEPTABLE_NETWORK_PARAMETER, "UNACCEPTABLE_NETWORK_PARAMETER"); sFailCauseMap.put(LOST_CONNECTION, "LOST_CONNECTION"); + sFailCauseMap.put(HANDOVER_FAILED, "HANDOVER_FAILED"); + sFailCauseMap.put(DUPLICATE_CID, "DUPLICATE_CID"); + sFailCauseMap.put(NO_DEFAULT_DATA, "NO_DEFAULT_DATA"); } private DataFailCause() { @@ -1467,6 +1599,9 @@ public final class DataFailCause { add(RADIO_NOT_AVAILABLE); add(UNACCEPTABLE_NETWORK_PARAMETER); add(SIGNAL_LOST); + add(DUPLICATE_CID); + add(MATCH_ALL_RULE_NOT_ALLOWED); + add(ALL_MATCHING_RULES_FAILED); } }; } diff --git a/telephony/java/android/telephony/DataSpecificRegistrationInfo.java b/telephony/java/android/telephony/DataSpecificRegistrationInfo.java index 0e30225a3321..957f683292f7 100644 --- a/telephony/java/android/telephony/DataSpecificRegistrationInfo.java +++ b/telephony/java/android/telephony/DataSpecificRegistrationInfo.java @@ -68,30 +68,22 @@ public final class DataSpecificRegistrationInfo implements Parcelable { public final boolean isEnDcAvailable; /** - * Provides network support info for LTE VoPS and LTE Emergency bearer support + * Provides network support info for VoPS and Emergency bearer support */ - private final LteVopsSupportInfo mLteVopsSupportInfo; - - /** - * Indicates if it's using carrier aggregation - * - * @hide - */ - public boolean mIsUsingCarrierAggregation; + @Nullable + private final VopsSupportInfo mVopsSupportInfo; /** * @hide */ DataSpecificRegistrationInfo( int maxDataCalls, boolean isDcNrRestricted, boolean isNrAvailable, - boolean isEnDcAvailable, LteVopsSupportInfo lteVops, - boolean isUsingCarrierAggregation) { + boolean isEnDcAvailable, @Nullable VopsSupportInfo vops) { this.maxDataCalls = maxDataCalls; this.isDcNrRestricted = isDcNrRestricted; this.isNrAvailable = isNrAvailable; this.isEnDcAvailable = isEnDcAvailable; - this.mLteVopsSupportInfo = lteVops; - this.mIsUsingCarrierAggregation = isUsingCarrierAggregation; + this.mVopsSupportInfo = vops; } /** @@ -100,32 +92,29 @@ public final class DataSpecificRegistrationInfo implements Parcelable { * @param dsri another data specific registration info * @hide */ - DataSpecificRegistrationInfo(DataSpecificRegistrationInfo dsri) { + DataSpecificRegistrationInfo(@NonNull DataSpecificRegistrationInfo dsri) { maxDataCalls = dsri.maxDataCalls; isDcNrRestricted = dsri.isDcNrRestricted; isNrAvailable = dsri.isNrAvailable; isEnDcAvailable = dsri.isEnDcAvailable; - mLteVopsSupportInfo = dsri.mLteVopsSupportInfo; - mIsUsingCarrierAggregation = dsri.mIsUsingCarrierAggregation; + mVopsSupportInfo = dsri.mVopsSupportInfo; } - private DataSpecificRegistrationInfo(Parcel source) { + private DataSpecificRegistrationInfo(/* @NonNull */ Parcel source) { maxDataCalls = source.readInt(); isDcNrRestricted = source.readBoolean(); isNrAvailable = source.readBoolean(); isEnDcAvailable = source.readBoolean(); - mLteVopsSupportInfo = LteVopsSupportInfo.CREATOR.createFromParcel(source); - mIsUsingCarrierAggregation = source.readBoolean(); + mVopsSupportInfo = source.readParcelable(VopsSupportInfo.class.getClassLoader()); } @Override - public void writeToParcel(Parcel dest, int flags) { + public void writeToParcel(/* @NonNull */ Parcel dest, int flags) { dest.writeInt(maxDataCalls); dest.writeBoolean(isDcNrRestricted); dest.writeBoolean(isNrAvailable); dest.writeBoolean(isEnDcAvailable); - mLteVopsSupportInfo.writeToParcel(dest, flags); - dest.writeBoolean(mIsUsingCarrierAggregation); + dest.writeParcelable(mVopsSupportInfo, flags); } @Override @@ -142,16 +131,15 @@ public final class DataSpecificRegistrationInfo implements Parcelable { .append(" isDcNrRestricted = " + isDcNrRestricted) .append(" isNrAvailable = " + isNrAvailable) .append(" isEnDcAvailable = " + isEnDcAvailable) - .append(" " + mLteVopsSupportInfo.toString()) - .append(" mIsUsingCarrierAggregation = " + mIsUsingCarrierAggregation) + .append(" " + mVopsSupportInfo) .append(" }") .toString(); } @Override public int hashCode() { - return Objects.hash(maxDataCalls, isDcNrRestricted, isNrAvailable, isEnDcAvailable, - mLteVopsSupportInfo, mIsUsingCarrierAggregation); + return Objects.hash(maxDataCalls, isDcNrRestricted, isNrAvailable, + isEnDcAvailable, mVopsSupportInfo); } @Override @@ -165,8 +153,7 @@ public final class DataSpecificRegistrationInfo implements Parcelable { && this.isDcNrRestricted == other.isDcNrRestricted && this.isNrAvailable == other.isNrAvailable && this.isEnDcAvailable == other.isEnDcAvailable - && this.mLteVopsSupportInfo.equals(other.mLteVopsSupportInfo) - && this.mIsUsingCarrierAggregation == other.mIsUsingCarrierAggregation; + && Objects.equals(mVopsSupportInfo, other.mVopsSupportInfo); } public static final @NonNull Parcelable.Creator<DataSpecificRegistrationInfo> CREATOR = @@ -184,29 +171,26 @@ public final class DataSpecificRegistrationInfo implements Parcelable { /** * @return The LTE VOPS (Voice over Packet Switched) support information + * + * @deprecated use {@link #getVopsSupportInfo()} */ + @Deprecated @NonNull public LteVopsSupportInfo getLteVopsSupportInfo() { - return mLteVopsSupportInfo; + return mVopsSupportInfo instanceof LteVopsSupportInfo + ? (LteVopsSupportInfo) mVopsSupportInfo + : new LteVopsSupportInfo(LteVopsSupportInfo.LTE_STATUS_NOT_AVAILABLE, + LteVopsSupportInfo.LTE_STATUS_NOT_AVAILABLE); } /** - * Set the flag indicating if using carrier aggregation. + * @return The VOPS (Voice over Packet Switched) support information. * - * @param isUsingCarrierAggregation {@code true} if using carrier aggregation. - * @hide - */ - public void setIsUsingCarrierAggregation(boolean isUsingCarrierAggregation) { - mIsUsingCarrierAggregation = isUsingCarrierAggregation; - } - - /** - * Get whether network has configured carrier aggregation or not. - * - * @return {@code true} if using carrier aggregation. - * @hide + * The instance of {@link LTEVopsSupportInfo}, or {@link NrVopsSupportInfo}, + * null if there is there is no VOPS support information available. */ - public boolean isUsingCarrierAggregation() { - return mIsUsingCarrierAggregation; + @Nullable + public VopsSupportInfo getVopsSupportInfo() { + return mVopsSupportInfo; } } diff --git a/telephony/java/android/telephony/DataThrottlingRequest.aidl b/telephony/java/android/telephony/DataThrottlingRequest.aidl new file mode 100644 index 000000000000..e1a3b66d2433 --- /dev/null +++ b/telephony/java/android/telephony/DataThrottlingRequest.aidl @@ -0,0 +1,19 @@ +/* +* Copyright (C) 2020 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; + +parcelable DataThrottlingRequest;
\ No newline at end of file diff --git a/telephony/java/android/telephony/DataThrottlingRequest.java b/telephony/java/android/telephony/DataThrottlingRequest.java new file mode 100644 index 000000000000..2827e8dc8539 --- /dev/null +++ b/telephony/java/android/telephony/DataThrottlingRequest.java @@ -0,0 +1,254 @@ +/* + * Copyright (C) 2020 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.annotation.RequiresFeature; +import android.annotation.SystemApi; +import android.os.Parcel; +import android.os.Parcelable; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + + +/** + * Class stores information related to the type of data throttling request. Must be populated as + * field in {@link ThermalMitigationRequest} for sending of thermal mitigation request at {@link + * TelephonyManager#sendThermalMitigationRequest(ThermalMitigationResult)}. + * @hide + */ +@SystemApi +public final class DataThrottlingRequest implements Parcelable { + /** + * Clear all existing data throttling, enable data, and attempt to enable radio for thermal + * mitigation all within the requested completion window. Note that attempting to enable radio + * will not guarantee that radio will actually be enabled. + * + * @hide + */ + @SystemApi + public static final int DATA_THROTTLING_ACTION_NO_DATA_THROTTLING = 0; + + /** + * Enact secondary carrier data throttling within specified completion window. This also + * attempts to enables radio if currently disabled for thermal mitigation, enables data, and + * removes any existing data throttling on primary carrier. Note that attempting to enable radio + * will not guarantee that radio will actually be enabled. + * + * @hide + */ + @SystemApi + @RequiresFeature( + enforcement = "android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported", + value = TelephonyManager.CAPABILITY_THERMAL_MITIGATION_DATA_THROTTLING) + public static final int DATA_THROTTLING_ACTION_THROTTLE_SECONDARY_CARRIER = 1; + + /** + * Enact primary carrier data throttling within specified completion window. This also attempts + * to enable radio if currently disabled for thermal mitigation and disables data on secondary + * carrier if currently enabled. Note that attempting to enable radio will not guarantee that + * radio will actually be enabled. + * + * @hide + */ + @SystemApi + @RequiresFeature( + enforcement = "android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported", + value = TelephonyManager.CAPABILITY_THERMAL_MITIGATION_DATA_THROTTLING) + public static final int DATA_THROTTLING_ACTION_THROTTLE_PRIMARY_CARRIER = 2; + + /** + * Immediately hold on to the current level of data throttling indicating that the current level + * of data throttling has alleviated the thermal concerns which caused the original data + * throttling request. A thermal module should remain actively monitoring the temperature levels + * and request an appropriate thermal mitigation action. {@link + * #THERMAL_MITIGATION_RESULT_INVALID_PARAMETERS} will be returned if completion window is not + * 0. + * + * @hide + */ + @SystemApi + @RequiresFeature( + enforcement = "android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported", + value = TelephonyManager.CAPABILITY_THERMAL_MITIGATION_DATA_THROTTLING) + public static final int DATA_THROTTLING_ACTION_HOLD = 3; + + /** + * Type of data throttling action to carry out. + * @hide + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = { "DATA_THROTTLING_ACTION_" }, value = { + DATA_THROTTLING_ACTION_NO_DATA_THROTTLING, + DATA_THROTTLING_ACTION_THROTTLE_SECONDARY_CARRIER, + DATA_THROTTLING_ACTION_THROTTLE_PRIMARY_CARRIER, + DATA_THROTTLING_ACTION_HOLD}) + public @interface DataThrottlingAction {} + + /** + * Represents the data throttling action that will be requested. See {@link + * DATA_THROTTLING_ACTION_NO_DATA_THROTTLING}, {@link + * #DATA_THROTTLING_ACTION_THROTTLE_SECONDARY_CARRIER}, {@link + * #DATA_THROTTLING_ACTION_THROTTLE_PRIMARY_CARRIER}, and {@link + * #DATA_THROTTLING_ACTION_HOLD} for more details. + **/ + private @DataThrottlingAction int mDataThrottlingAction; + /** + * Represents the time over which modem should gradually execute the data thorttling request. + */ + private long mCompletionDurationMillis; + + private DataThrottlingRequest(@NonNull int dataThrottlingAction, + long completionDurationMillis) { + mDataThrottlingAction = dataThrottlingAction; + mCompletionDurationMillis = completionDurationMillis; + } + + private DataThrottlingRequest(Parcel in) { + mDataThrottlingAction = in.readInt(); + mCompletionDurationMillis = in.readLong(); + } + + /** + * Implement the Parcelable interface + */ + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mDataThrottlingAction); + dest.writeLong(mCompletionDurationMillis); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public String toString() { + return "[DataThrottlingRequest " + + ", DataThrottlingAction=" + mDataThrottlingAction + + ", completionDurationMillis=" + mCompletionDurationMillis + + "]"; + } + + /** + * @return the dataThrottlingAction. + */ + public @DataThrottlingAction int getDataThrottlingAction() { + return mDataThrottlingAction; + } + + /** + * @return the completionDurationMillis which represents the time over which modem should + * gradually execute the data thorttling request. + */ + public long getCompletionDurationMillis() { + return mCompletionDurationMillis; + } + + public static final @NonNull Parcelable.Creator<DataThrottlingRequest> CREATOR = + new Parcelable.Creator<DataThrottlingRequest>() { + + @Override + public DataThrottlingRequest createFromParcel(Parcel in) { + return new DataThrottlingRequest(in); + } + + @Override + public DataThrottlingRequest[] newArray(int size) { + return new DataThrottlingRequest[size]; + } + }; + + /** + * Provides a convenient way to set the fields of a {@link DataThrottlingRequest} when creating + * a new instance. + * + * <p>The example below shows how you might create a new {@code DataThrottlingRequest}: + * + * <pre><code> + * + * DataThrottlingRequest dp = new DataThrottlingRequest.Builder() + * .setDataThrottlingAction( + * DataThrottlingRequest.DATA_THROTTLING_ACTION_THROTTLE_SECONDARY_CARRIER) + * .setCompletionDurationMillis(10000L) + * .build(); + * </code></pre> + * + * @hide + */ + @SystemApi + public static final class Builder { + private @DataThrottlingAction int mDataThrottlingAction; + private long mCompletionDurationMillis; + + /** + * Default constructor for Builder. + */ + public Builder() {} + + /** + * Set the data throttling action. + * + * @param dataThrottlingAction data throttling action. + * + * @return The same instance of the builder. + */ + public @NonNull Builder setDataThrottlingAction( + @DataThrottlingAction int dataThrottlingAction) { + mDataThrottlingAction = dataThrottlingAction; + return this; + } + + /** + * Set the completion duration. + * + * @param completionDurationMillis completion duration in millis which represents the time + * over which modem should gradually execute the data thorttling request. This can + * never be a negative number and must be 0 for {@link #DATA_THROTTLING_ACTION_HOLD}. + * Otherwise, an IllegalArgumentException will be thrown. + * + * @return The same instance of the builder. + */ + public @NonNull Builder setCompletionDurationMillis(long completionDurationMillis) { + mCompletionDurationMillis = completionDurationMillis; + return this; + } + + /** + * Build the DataThrottlingRequest. + * + * @return the DataThrottlingRequest object. + */ + public @NonNull DataThrottlingRequest build() { + if (mCompletionDurationMillis < 0) { + throw new IllegalArgumentException("completionDurationMillis cannot be a negative " + + "number"); + } + + if (mDataThrottlingAction == DataThrottlingRequest.DATA_THROTTLING_ACTION_HOLD + && mCompletionDurationMillis != 0) { + throw new IllegalArgumentException("completionDurationMillis must be 0 for " + + "DataThrottlingRequest.DATA_THROTTLING_ACTION_HOLD"); + } + + return new DataThrottlingRequest(mDataThrottlingAction, mCompletionDurationMillis); + } + } + +} diff --git a/telephony/java/android/telephony/DisconnectCause.java b/telephony/java/android/telephony/DisconnectCause.java index be85b30f272b..2704418935d9 100644 --- a/telephony/java/android/telephony/DisconnectCause.java +++ b/telephony/java/android/telephony/DisconnectCause.java @@ -17,16 +17,14 @@ package android.telephony; import android.annotation.NonNull; -import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; /** * Describes the cause of a disconnected call. Those disconnect causes can be converted into a more * generic {@link android.telecom.DisconnectCause} object. * - * @hide + * Used in {@link PhoneStateListener#onCallDisconnectCauseChanged}. */ -@SystemApi public final class DisconnectCause { /** The disconnect cause is not valid (Not received a disconnect cause) */ @@ -337,20 +335,17 @@ public final class DisconnectCause { /** * Indicates that the call is dropped due to RTCP inactivity, primarily due to media path * disruption. - * @hide */ public static final int MEDIA_TIMEOUT = 77; /** * Indicates that an emergency call cannot be placed over WFC because the service is not * available in the current location. - * @hide */ public static final int EMERGENCY_CALL_OVER_WFC_NOT_AVAILABLE = 78; /** * Indicates that WiFi calling service is not available in the current location. - * @hide */ public static final int WFC_SERVICE_NOT_AVAILABLE_IN_THIS_LOCATION = 79; diff --git a/telephony/java/android/telephony/IBootstrapAuthenticationCallback.aidl b/telephony/java/android/telephony/IBootstrapAuthenticationCallback.aidl new file mode 100644 index 000000000000..d39ad0e9a2ef --- /dev/null +++ b/telephony/java/android/telephony/IBootstrapAuthenticationCallback.aidl @@ -0,0 +1,27 @@ +/* + * Copyright 2020 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; + +/** + * Callback to handle the response of bootstrapAuthenticationRequest + * @hide + */ +oneway interface IBootstrapAuthenticationCallback +{ + void onKeysAvailable(int token, in byte[] gbaKey, String btId); + void onAuthenticationFailure(int token, int reason); +} diff --git a/telephony/java/android/telephony/ImsManager.java b/telephony/java/android/telephony/ImsManager.java index 3984bd769edd..42d7707c97d9 100644 --- a/telephony/java/android/telephony/ImsManager.java +++ b/telephony/java/android/telephony/ImsManager.java @@ -19,9 +19,15 @@ package android.telephony.ims; import android.annotation.NonNull; import android.annotation.SdkConstant; import android.annotation.SuppressLint; +import android.annotation.SystemApi; import android.annotation.SystemService; import android.content.Context; +import android.telephony.BinderCacheManager; import android.telephony.SubscriptionManager; +import android.telephony.TelephonyFrameworkInitializer; +import android.telephony.ims.aidl.IImsRcsController; + +import com.android.internal.telephony.ITelephony; /** * Provides access to information about Telephony IMS services on the device. @@ -29,8 +35,6 @@ import android.telephony.SubscriptionManager; @SystemService(Context.TELEPHONY_IMS_SERVICE) public class ImsManager { - private Context mContext; - /** * <p>Broadcast Action: Indicates that a previously allowed IMS operation was rejected by the * network due to the network returning a "forbidden" response. This may be due to a @@ -86,6 +90,14 @@ public class ImsManager { public static final String EXTRA_WFC_REGISTRATION_FAILURE_MESSAGE = "android.telephony.ims.extra.WFC_REGISTRATION_FAILURE_MESSAGE"; + // Cache Telephony Binder interfaces, one cache per process. + private static final BinderCacheManager<ITelephony> sTelephonyCache = + new BinderCacheManager<>(ImsManager::getITelephonyInterface); + private static final BinderCacheManager<IImsRcsController> sRcsCache = + new BinderCacheManager<>(ImsManager::getIImsRcsControllerInterface); + + private final Context mContext; + /** * Use {@link Context#getSystemService(String)} to get an instance of this class. * @hide @@ -107,7 +119,7 @@ public class ImsManager { throw new IllegalArgumentException("Invalid subscription ID: " + subscriptionId); } - return new ImsRcsManager(mContext, subscriptionId); + return new ImsRcsManager(mContext, subscriptionId, sRcsCache); } /** @@ -123,6 +135,44 @@ public class ImsManager { throw new IllegalArgumentException("Invalid subscription ID: " + subscriptionId); } - return new ImsMmTelManager(subscriptionId); + return new ImsMmTelManager(subscriptionId, sTelephonyCache); + } + + /** + * Create an instance of {@link SipDelegateManager} for the subscription id specified. + * <p> + * Allows an IMS application to forward SIP traffic through the device's IMS service, + * which is used for cellular carriers that require the device to share a single IMS + * registration for both MMTEL and RCS features. + * @param subscriptionId The ID of the subscription that this {@link SipDelegateManager} will + * be bound to. + * @throws IllegalArgumentException if the subscription is invalid. + * @return a {@link SipDelegateManager} instance for the specified subscription ID. + * @hide + */ + @SystemApi + @NonNull + public SipDelegateManager getSipDelegateManager(int subscriptionId) { + if (!SubscriptionManager.isValidSubscriptionId(subscriptionId)) { + throw new IllegalArgumentException("Invalid subscription ID: " + subscriptionId); + } + + return new SipDelegateManager(mContext, subscriptionId, sRcsCache); + } + + private static IImsRcsController getIImsRcsControllerInterface() { + return IImsRcsController.Stub.asInterface( + TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getTelephonyImsServiceRegisterer() + .get()); + } + + private static ITelephony getITelephonyInterface() { + return ITelephony.Stub.asInterface( + TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getTelephonyServiceRegisterer() + .get()); } } diff --git a/telephony/java/android/telephony/ImsiEncryptionInfo.java b/telephony/java/android/telephony/ImsiEncryptionInfo.java index 75a79d62d2aa..4978692d3964 100644 --- a/telephony/java/android/telephony/ImsiEncryptionInfo.java +++ b/telephony/java/android/telephony/ImsiEncryptionInfo.java @@ -163,8 +163,8 @@ public final class ImsiEncryptionInfo implements Parcelable { public String toString(){ return "[ImsiEncryptionInfo " + "mcc=" + mcc - + "mnc=" + mnc - + "publicKey=" + publicKey + + " mnc=" + mnc + + " publicKey=" + publicKey + ", keyIdentifier=" + keyIdentifier + ", keyType=" + keyType + ", expirationTime=" + expirationTime diff --git a/telephony/java/android/telephony/LinkCapacityEstimate.aidl b/telephony/java/android/telephony/LinkCapacityEstimate.aidl new file mode 100644 index 000000000000..286f33fc9810 --- /dev/null +++ b/telephony/java/android/telephony/LinkCapacityEstimate.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony; + +parcelable LinkCapacityEstimate;
\ No newline at end of file diff --git a/telephony/java/android/telephony/LinkCapacityEstimate.java b/telephony/java/android/telephony/LinkCapacityEstimate.java new file mode 100644 index 000000000000..deeb80961c3c --- /dev/null +++ b/telephony/java/android/telephony/LinkCapacityEstimate.java @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.os.Parcel; +import android.os.Parcelable; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Objects; + +/** + * Link Capacity Estimate from the modem + * @hide + */ +@SystemApi +public final class LinkCapacityEstimate implements Parcelable { + /** A value indicates that the capacity estimate is not available */ + public static final int INVALID = -1; + + /** + * LCE for the primary network + */ + public static final int LCE_TYPE_PRIMARY = 0; + + /** + * LCE for the secondary network + */ + public static final int LCE_TYPE_SECONDARY = 1; + + /** + * Combined LCE for primary network and secondary network reported by the legacy modem + */ + public static final int LCE_TYPE_COMBINED = 2; + + /** @hide */ + @IntDef(prefix = { "LCE_TYPE_" }, value = { + LCE_TYPE_PRIMARY, + LCE_TYPE_SECONDARY, + LCE_TYPE_COMBINED, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface LceType {} + + private final @LceType int mType; + + /** Downlink capacity estimate in kbps */ + private final int mDownlinkCapacityKbps; + + /** Uplink capacity estimate in kbps */ + private final int mUplinkCapacityKbps; + + /** + * Constructor for link capacity estimate + */ + public LinkCapacityEstimate(@LceType int type, + int downlinkCapacityKbps, int uplinkCapacityKbps) { + mDownlinkCapacityKbps = downlinkCapacityKbps; + mUplinkCapacityKbps = uplinkCapacityKbps; + mType = type; + } + + /** + * @hide + */ + public LinkCapacityEstimate(Parcel in) { + mDownlinkCapacityKbps = in.readInt(); + mUplinkCapacityKbps = in.readInt(); + mType = in.readInt(); + } + + /** + * Retrieves the type of LCE + * @return The type of link capacity estimate + */ + public @LceType int getType() { + return mType; + } + + /** + * Retrieves the downlink bandwidth in Kbps. + * This will be {@link #INVALID} if the network is not connected + * @return The estimated first hop downstream (network to device) bandwidth. + */ + public int getDownlinkCapacityKbps() { + return mDownlinkCapacityKbps; + } + + /** + * Retrieves the uplink bandwidth in Kbps. + * This will be {@link #INVALID} if the network is not connected + * + * @return The estimated first hop upstream (device to network) bandwidth. + */ + public int getUplinkCapacityKbps() { + return mUplinkCapacityKbps; + } + + @Override + public String toString() { + return new StringBuilder() + .append("{mType=") + .append(mType) + .append(", mDownlinkCapacityKbps=") + .append(mDownlinkCapacityKbps) + .append(", mUplinkCapacityKbps=") + .append(mUplinkCapacityKbps) + .append("}") + .toString(); + } + + /** + * {@link Parcelable#describeContents} + */ + public int describeContents() { + return 0; + } + + /** + * {@link Parcelable#writeToParcel} + * @hide + */ + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mDownlinkCapacityKbps); + dest.writeInt(mUplinkCapacityKbps); + dest.writeInt(mType); + } + + @Override + public boolean equals(@Nullable Object o) { + if (o == null || !(o instanceof LinkCapacityEstimate) || hashCode() != o.hashCode()) { + return false; + } + + if (this == o) { + return true; + } + + LinkCapacityEstimate that = (LinkCapacityEstimate) o; + return mDownlinkCapacityKbps == that.mDownlinkCapacityKbps + && mUplinkCapacityKbps == that.mUplinkCapacityKbps + && mType == that.mType; + } + + @Override + public int hashCode() { + return Objects.hash(mDownlinkCapacityKbps, mUplinkCapacityKbps, mType); + } + + public static final + @android.annotation.NonNull Parcelable.Creator<LinkCapacityEstimate> CREATOR = + new Parcelable.Creator() { + public LinkCapacityEstimate createFromParcel(Parcel in) { + return new LinkCapacityEstimate(in); + } + + public LinkCapacityEstimate[] newArray(int size) { + return new LinkCapacityEstimate[size]; + } + }; +} diff --git a/telephony/java/android/telephony/LteVopsSupportInfo.java b/telephony/java/android/telephony/LteVopsSupportInfo.java index 83e41bf3df3b..87761e21350b 100644 --- a/telephony/java/android/telephony/LteVopsSupportInfo.java +++ b/telephony/java/android/telephony/LteVopsSupportInfo.java @@ -21,7 +21,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; import android.os.Parcel; -import android.os.Parcelable; +import android.telephony.AccessNetworkConstants.AccessNetworkType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -32,7 +32,7 @@ import java.util.Objects; * @hide */ @SystemApi -public final class LteVopsSupportInfo implements Parcelable { +public final class LteVopsSupportInfo extends VopsSupportInfo { /**@hide*/ @Retention(RetentionPolicy.SOURCE) @@ -42,7 +42,10 @@ public final class LteVopsSupportInfo implements Parcelable { public @interface LteVopsStatus {} /** * Indicates information not available from modem. + * + * @deprecated as no instance will be created in this case */ + @Deprecated public static final int LTE_STATUS_NOT_AVAILABLE = 1; /** @@ -82,13 +85,38 @@ public final class LteVopsSupportInfo implements Parcelable { return mEmcBearerSupport; } + /** + * Returns whether VoPS is supported by the network + */ + @Override + public boolean isVopsSupported() { + return mVopsSupport == LTE_STATUS_SUPPORTED; + } + + /** + * Returns whether emergency service is supported by the network + */ + @Override + public boolean isEmergencyServiceSupported() { + return mEmcBearerSupport == LTE_STATUS_SUPPORTED; + } + + /** + * Returns whether emergency service fallback is supported by the network + */ + @Override + public boolean isEmergencyServiceFallbackSupported() { + return false; + } + @Override public int describeContents() { return 0; } @Override - public void writeToParcel(Parcel out, int flags) { + public void writeToParcel(@NonNull Parcel out, int flags) { + super.writeToParcel(out, flags, AccessNetworkType.EUTRAN); out.writeInt(mVopsSupport); out.writeInt(mEmcBearerSupport); } @@ -124,6 +152,8 @@ public final class LteVopsSupportInfo implements Parcelable { new Creator<LteVopsSupportInfo>() { @Override public LteVopsSupportInfo createFromParcel(Parcel in) { + // Skip the type info. + in.readInt(); return new LteVopsSupportInfo(in); } @@ -133,6 +163,11 @@ public final class LteVopsSupportInfo implements Parcelable { } }; + /** @hide */ + protected static LteVopsSupportInfo createFromParcelBody(Parcel in) { + return new LteVopsSupportInfo(in); + } + private LteVopsSupportInfo(Parcel in) { mVopsSupport = in.readInt(); mEmcBearerSupport = in.readInt(); diff --git a/telephony/java/android/telephony/MbmsDownloadSession.java b/telephony/java/android/telephony/MbmsDownloadSession.java index 45deea206cfc..18d6f467ba7b 100644 --- a/telephony/java/android/telephony/MbmsDownloadSession.java +++ b/telephony/java/android/telephony/MbmsDownloadSession.java @@ -231,6 +231,8 @@ public class MbmsDownloadSession implements AutoCloseable { private static final String DESTINATION_SANITY_CHECK_FILE_NAME = "destinationSanityCheckFile"; + private static final int MAX_SERVICE_ANNOUNCEMENT_SIZE = 10 * 1024; // 10KB + private static AtomicBoolean sIsInitialized = new AtomicBoolean(false); private final Context mContext; @@ -318,6 +320,16 @@ public class MbmsDownloadSession implements AutoCloseable { return session; } + /** + * Returns the maximum size of the service announcement descriptor that can be provided via + * {@link #addServiceAnnouncement} + * @return The maximum length of the byte array passed as an argument to + * {@link #addServiceAnnouncement}. + */ + public static int getMaximumServiceAnnouncementSize() { + return MAX_SERVICE_ANNOUNCEMENT_SIZE; + } + private int bindAndInitialize() { mServiceConnection = new ServiceConnection() { @Override @@ -424,6 +436,61 @@ public class MbmsDownloadSession implements AutoCloseable { } /** + * Inform the middleware of a service announcement descriptor received from a group + * communication server. + * + * When participating in a group call via the {@link MbmsGroupCallSession} API, applications may + * receive a service announcement descriptor from the group call server that informs them of + * files that may be relevant to users communicating on the group call. + * + * After supplying the service announcement descriptor received from the server to the + * middleware via this API, applications will receive information on the available files via + * {@link MbmsDownloadSessionCallback#onFileServicesUpdated}, and the available files will be + * downloadable via {@link MbmsDownloadSession#download} like other files published via + * {@link MbmsDownloadSessionCallback#onFileServicesUpdated}. + * + * Asynchronous error codes via the {@link MbmsDownloadSessionCallback#onError(int, String)} + * callback may include any of the errors that are not specific to the streaming use-case. + * + * May throw an {@link IllegalStateException} when the middleware has not yet been bound, + * or an {@link IllegalArgumentException} if the byte array is too large, or an + * {@link UnsupportedOperationException} if the middleware has not implemented this method. + * + * @param contents The contents of the service announcement descriptor received from the + * group call server. If the size of this array is greater than the value of + * {@link #getMaximumServiceAnnouncementSize()}, an + * {@link IllegalArgumentException} will be thrown. + */ + public void addServiceAnnouncement(@NonNull byte[] contents) { + IMbmsDownloadService downloadService = mService.get(); + if (downloadService == null) { + throw new IllegalStateException("Middleware not yet bound"); + } + + if (contents.length > MAX_SERVICE_ANNOUNCEMENT_SIZE) { + throw new IllegalArgumentException("File too large"); + } + + try { + int returnCode = downloadService.addServiceAnnouncement( + mSubscriptionId, contents); + if (returnCode == MbmsErrors.UNKNOWN) { + // Unbind and throw an obvious error + close(); + throw new IllegalStateException("Middleware must not return an unknown error code"); + } + if (returnCode != MbmsErrors.SUCCESS) { + sendErrorToApp(returnCode, null); + } + } catch (RemoteException e) { + Log.w(LOG_TAG, "Remote process died"); + mService.set(null); + sIsInitialized.set(false); + sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null); + } + } + + /** * Sets the temp file root for downloads. * All temp files created for the middleware to write to will be contained in the specified * directory. Applications that wish to specify a location only need to call this method once @@ -442,7 +509,7 @@ public class MbmsDownloadSession implements AutoCloseable { * provided directory is the same as what has been previously configured. * * The {@link File} supplied as a root temp file directory must already exist. If not, an - * {@link IllegalArgumentException} will be thrown. In addition, as an additional sanity + * {@link IllegalArgumentException} will be thrown. In addition, as an additional correctness * check, an {@link IllegalArgumentException} will be thrown if you attempt to set the temp * file root directory to one of your data roots (the value of {@link Context#getDataDir()}, * {@link Context#getFilesDir()}, or {@link Context#getCacheDir()}). diff --git a/telephony/java/android/telephony/ModemActivityInfo.java b/telephony/java/android/telephony/ModemActivityInfo.java index 2b72ab7635c3..ec6c25d1b8c6 100644 --- a/telephony/java/android/telephony/ModemActivityInfo.java +++ b/telephony/java/android/telephony/ModemActivityInfo.java @@ -16,8 +16,12 @@ package android.telephony; +import android.annotation.DurationMillisLong; +import android.annotation.ElapsedRealtimeLong; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.annotation.TestApi; import android.os.Parcel; import android.os.Parcelable; import android.os.SystemClock; @@ -25,46 +29,49 @@ import android.util.Range; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import java.util.ArrayList; -import java.util.List; +import java.util.Arrays; +import java.util.Objects; /** - * Reports modem activity information. + * Contains information about the modem's activity. May be useful for power stats reporting. * @hide */ +@SystemApi public final class ModemActivityInfo implements Parcelable { + private static final int TX_POWER_LEVELS = 5; + /** - * Tx(transmit) power level. see power index below - * <ul> - * <li> index 0 = tx_power < 0dBm. </li> - * <li> index 1 = 0dBm < tx_power < 5dBm. </li> - * <li> index 2 = 5dBm < tx_power < 15dBm. </li> - * <li> index 3 = 15dBm < tx_power < 20dBm. </li> - * <li> index 4 = tx_power > 20dBm. </li> - * </ul> - */ - public static final int TX_POWER_LEVELS = 5; - /** - * Tx(transmit) power level 0: tx_power < 0dBm + * Corresponds to transmit power of less than 0dBm. */ public static final int TX_POWER_LEVEL_0 = 0; + /** - * Tx(transmit) power level 1: 0dBm < tx_power < 5dBm + * Corresponds to transmit power between 0dBm and 5dBm. */ public static final int TX_POWER_LEVEL_1 = 1; + /** - * Tx(transmit) power level 2: 5dBm < tx_power < 15dBm + * Corresponds to transmit power between 5dBm and 15dBm. */ public static final int TX_POWER_LEVEL_2 = 2; + /** - * Tx(transmit) power level 3: 15dBm < tx_power < 20dBm. + * Corresponds to transmit power between 15dBm and 20dBm. */ public static final int TX_POWER_LEVEL_3 = 3; + /** - * Tx(transmit) power level 4: tx_power > 20dBm + * Corresponds to transmit power above 20dBm. */ public static final int TX_POWER_LEVEL_4 = 4; + /** + * The number of transmit power levels. Fixed by HAL definition. + */ + public static int getNumTxPowerLevels() { + return TX_POWER_LEVELS; + } + /** @hide */ @IntDef(prefix = {"TX_POWER_LEVEL_"}, value = { TX_POWER_LEVEL_0, @@ -82,34 +89,39 @@ public final class ModemActivityInfo implements Parcelable { new Range<>(5, 15), new Range<>(15, 20), new Range<>(20, Integer.MAX_VALUE) - }; private long mTimestamp; private int mSleepTimeMs; private int mIdleTimeMs; - private List<TransmitPower> mTransmitPowerInfo = new ArrayList<>(TX_POWER_LEVELS); + private int[] mTxTimeMs; private int mRxTimeMs; + /** + * @hide + */ + @TestApi public ModemActivityInfo(long timestamp, int sleepTimeMs, int idleTimeMs, @NonNull int[] txTimeMs, int rxTimeMs) { + Objects.requireNonNull(txTimeMs); + if (txTimeMs.length != TX_POWER_LEVELS) { + throw new IllegalArgumentException("txTimeMs must have length == TX_POWER_LEVELS"); + } mTimestamp = timestamp; mSleepTimeMs = sleepTimeMs; mIdleTimeMs = idleTimeMs; - populateTransmitPowerRange(txTimeMs); + mTxTimeMs = txTimeMs; mRxTimeMs = rxTimeMs; } - /** helper API to populate tx power range for each bucket **/ - private void populateTransmitPowerRange(@NonNull int[] transmitPowerMs) { - int i = 0; - for ( ; i < Math.min(transmitPowerMs.length, TX_POWER_LEVELS); i++) { - mTransmitPowerInfo.add(i, new TransmitPower(TX_POWER_RANGES[i], transmitPowerMs[i])); - } - // Make sure that mTransmitPowerInfo is fully initialized. - for ( ; i < TX_POWER_LEVELS; i++) { - mTransmitPowerInfo.add(i, new TransmitPower(TX_POWER_RANGES[i], 0)); - } + /** + * Provided for convenience in manipulation since the API exposes long values but internal + * representations are ints. + * @hide + */ + public ModemActivityInfo(long timestamp, long sleepTimeMs, long idleTimeMs, + @NonNull int[] txTimeMs, long rxTimeMs) { + this(timestamp, (int) sleepTimeMs, (int) idleTimeMs, txTimeMs, (int) rxTimeMs); } @Override @@ -118,7 +130,7 @@ public final class ModemActivityInfo implements Parcelable { + " mTimestamp=" + mTimestamp + " mSleepTimeMs=" + mSleepTimeMs + " mIdleTimeMs=" + mIdleTimeMs - + " mTransmitPowerInfo[]=" + mTransmitPowerInfo.toString() + + " mTxTimeMs[]=" + Arrays.toString(mTxTimeMs) + " mRxTimeMs=" + mRxTimeMs + "}"; } @@ -129,14 +141,12 @@ public final class ModemActivityInfo implements Parcelable { public static final @android.annotation.NonNull Parcelable.Creator<ModemActivityInfo> CREATOR = new Parcelable.Creator<ModemActivityInfo>() { - public ModemActivityInfo createFromParcel(Parcel in) { + public ModemActivityInfo createFromParcel(@NonNull Parcel in) { long timestamp = in.readLong(); int sleepTimeMs = in.readInt(); int idleTimeMs = in.readInt(); int[] txTimeMs = new int[TX_POWER_LEVELS]; - for (int i = 0; i < TX_POWER_LEVELS; i++) { - txTimeMs[i] = in.readInt(); - } + in.readIntArray(txTimeMs); int rxTimeMs = in.readInt(); return new ModemActivityInfo(timestamp, sleepTimeMs, idleTimeMs, txTimeMs, rxTimeMs); @@ -147,21 +157,25 @@ public final class ModemActivityInfo implements Parcelable { } }; - public void writeToParcel(Parcel dest, int flags) { + /** + * @param dest The Parcel in which the object should be written. + * @param flags Additional flags about how the object should be written. + */ + public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeLong(mTimestamp); dest.writeInt(mSleepTimeMs); dest.writeInt(mIdleTimeMs); - for (int i = 0; i < TX_POWER_LEVELS; i++) { - dest.writeInt(mTransmitPowerInfo.get(i).getTimeInMillis()); - } + dest.writeIntArray(mTxTimeMs); dest.writeInt(mRxTimeMs); } /** - * @return milliseconds since boot, including mTimeInMillis spent in sleep. - * @see SystemClock#elapsedRealtime() + * Gets the timestamp at which this modem activity info was recorded. + * + * @return The timestamp, as returned by {@link SystemClock#elapsedRealtime()}, when this + * {@link ModemActivityInfo} was recorded. */ - public long getTimestamp() { + public @ElapsedRealtimeLong long getTimestampMillis() { return mTimestamp; } @@ -171,35 +185,48 @@ public final class ModemActivityInfo implements Parcelable { } /** - * @return an arrayList of {@link TransmitPower} with each element representing the total time where - * transmitter is awake time (in ms) for a given power range (in dbm). + * Gets the amount of time the modem spent transmitting at a certain power level. * - * @see #TX_POWER_LEVELS + * @param powerLevel The power level to query. + * @return The amount of time, in milliseconds, that the modem spent transmitting at the + * given power level. */ - @NonNull - public List<TransmitPower> getTransmitPowerInfo() { - return mTransmitPowerInfo; + public @DurationMillisLong long getTransmitDurationMillisAtPowerLevel( + @TxPowerLevel int powerLevel) { + return mTxTimeMs[powerLevel]; + } + + /** + * Gets the range of transmit powers corresponding to a certain power level. + * + * @param powerLevel The power level to query + * @return A {@link Range} object representing the range of intensities (in dBm) to which this + * power level corresponds. + */ + public @NonNull Range<Integer> getTransmitPowerRange(@TxPowerLevel int powerLevel) { + return TX_POWER_RANGES[powerLevel]; } /** @hide */ public void setTransmitTimeMillis(int[] txTimeMs) { - populateTransmitPowerRange(txTimeMs); + mTxTimeMs = Arrays.copyOf(txTimeMs, TX_POWER_LEVELS); } - /** @hide */ + /** + * @return The raw array of transmit power durations + * @hide + */ @NonNull public int[] getTransmitTimeMillis() { - int[] transmitTimeMillis = new int[TX_POWER_LEVELS]; - for (int i = 0; i < transmitTimeMillis.length; i++) { - transmitTimeMillis[i] = mTransmitPowerInfo.get(i).getTimeInMillis(); - } - return transmitTimeMillis; + return mTxTimeMs; } /** - * @return total mTimeInMillis (in ms) when modem is in a low power or sleep state. + * Gets the amount of time (in milliseconds) when the modem is in a low power or sleep state. + * + * @return Time in milliseconds. */ - public int getSleepTimeMillis() { + public @DurationMillisLong long getSleepTimeMillis() { return mSleepTimeMs; } @@ -209,10 +236,44 @@ public final class ModemActivityInfo implements Parcelable { } /** - * @return total mTimeInMillis (in ms) when modem is awake but neither the transmitter nor receiver are - * active. + * Provided for convenience, since the API surface needs to return longs but internal + * representations are ints. + * @hide */ - public int getIdleTimeMillis() { + public void setSleepTimeMillis(long sleepTimeMillis) { + mSleepTimeMs = (int) sleepTimeMillis; + } + + /** + * Computes the difference between this instance of {@link ModemActivityInfo} and another + * instance. + * + * This method should be used to compute the amount of activity that has happened between two + * samples of modem activity taken at separate times. The sample passed in as an argument to + * this method should be the one that's taken later in time (and therefore has more activity). + * @param other The other instance of {@link ModemActivityInfo} to diff against. + * @return An instance of {@link ModemActivityInfo} representing the difference in modem + * activity. + */ + public @NonNull ModemActivityInfo getDelta(@NonNull ModemActivityInfo other) { + int[] txTimeMs = new int[ModemActivityInfo.TX_POWER_LEVELS]; + for (int i = 0; i < ModemActivityInfo.TX_POWER_LEVELS; i++) { + txTimeMs[i] = other.mTxTimeMs[i] - mTxTimeMs[i]; + } + return new ModemActivityInfo(other.getTimestampMillis(), + other.getSleepTimeMillis() - getSleepTimeMillis(), + other.getIdleTimeMillis() - getIdleTimeMillis(), + txTimeMs, + other.getReceiveTimeMillis() - getReceiveTimeMillis()); + } + + /** + * Gets the amount of time (in milliseconds) when the modem is awake but neither transmitting + * nor receiving. + * + * @return Time in milliseconds. + */ + public @DurationMillisLong long getIdleTimeMillis() { return mIdleTimeMs; } @@ -222,9 +283,20 @@ public final class ModemActivityInfo implements Parcelable { } /** - * @return rx(receive) mTimeInMillis in ms. + * Provided for convenience, since the API surface needs to return longs but internal + * representations are ints. + * @hide + */ + public void setIdleTimeMillis(long idleTimeMillis) { + mIdleTimeMs = (int) idleTimeMillis; + } + + /** + * Gets the amount of time (in milliseconds) when the modem is awake and receiving data. + * + * @return Time in milliseconds. */ - public int getReceiveTimeMillis() { + public @DurationMillisLong long getReceiveTimeMillis() { return mRxTimeMs; } @@ -234,71 +306,56 @@ public final class ModemActivityInfo implements Parcelable { } /** - * Indicate if the ModemActivityInfo is invalid due to modem's invalid reporting. + * Provided for convenience, since the API surface needs to return longs but internal + * representations are ints. + * @hide + */ + public void setReceiveTimeMillis(long receiveTimeMillis) { + mRxTimeMs = (int) receiveTimeMillis; + } + + /** + * Indicates if the modem has reported valid {@link ModemActivityInfo}. * * @return {@code true} if this {@link ModemActivityInfo} record is valid, * {@code false} otherwise. + * @hide */ + @TestApi public boolean isValid() { - for (TransmitPower powerInfo : getTransmitPowerInfo()) { - if(powerInfo.getTimeInMillis() < 0) { - return false; - } - } + boolean isTxPowerValid = Arrays.stream(mTxTimeMs).allMatch((i) -> i >= 0); - return ((getIdleTimeMillis() >= 0) && (getSleepTimeMillis() >= 0) + return isTxPowerValid && ((getIdleTimeMillis() >= 0) && (getSleepTimeMillis() >= 0) && (getReceiveTimeMillis() >= 0) && !isEmpty()); } - private boolean isEmpty() { - for (TransmitPower txVal : getTransmitPowerInfo()) { - if(txVal.getTimeInMillis() != 0) { - return false; - } - } + /** @hide */ + @TestApi + public boolean isEmpty() { + boolean isTxPowerEmpty = mTxTimeMs == null || mTxTimeMs.length == 0 + || Arrays.stream(mTxTimeMs).allMatch((i) -> i == 0); - return ((getIdleTimeMillis() == 0) && (getSleepTimeMillis() == 0) + return isTxPowerEmpty && ((getIdleTimeMillis() == 0) && (getSleepTimeMillis() == 0) && (getReceiveTimeMillis() == 0)); } - /** - * Transmit power Information, including the power range in dbm and the total time (in ms) where - * the transmitter is active/awake for this power range. - * e.g, range: 0dbm(lower) ~ 5dbm(upper) - * time: 5ms - */ - public class TransmitPower { - private int mTimeInMillis; - private Range<Integer> mPowerRangeInDbm; - /** @hide */ - public TransmitPower(@NonNull Range<Integer> range, int time) { - this.mTimeInMillis = time; - this.mPowerRangeInDbm = range; - } - - /** - * @return the total time in ms where the transmitter is active/wake for this power range - * {@link #getPowerRangeInDbm()}. - */ - public int getTimeInMillis() { - return mTimeInMillis; - } - /** - * @return the power range in dbm. e.g, range: 0dbm(lower) ~ 5dbm(upper) - */ - @NonNull - public Range<Integer> getPowerRangeInDbm() { - return mPowerRangeInDbm; - } + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ModemActivityInfo that = (ModemActivityInfo) o; + return mTimestamp == that.mTimestamp + && mSleepTimeMs == that.mSleepTimeMs + && mIdleTimeMs == that.mIdleTimeMs + && mRxTimeMs == that.mRxTimeMs + && Arrays.equals(mTxTimeMs, that.mTxTimeMs); + } - @Override - public String toString() { - return "TransmitPower{" - + " mTimeInMillis=" + mTimeInMillis - + " mPowerRangeInDbm={" + mPowerRangeInDbm.getLower() - + "," + mPowerRangeInDbm.getUpper() - + "}}"; - } + @Override + public int hashCode() { + int result = Objects.hash(mTimestamp, mSleepTimeMs, mIdleTimeMs, mRxTimeMs); + result = 31 * result + Arrays.hashCode(mTxTimeMs); + return result; } } diff --git a/telephony/java/android/telephony/NetworkRegistrationInfo.java b/telephony/java/android/telephony/NetworkRegistrationInfo.java index 622ef137f494..5fb60d7599ea 100644 --- a/telephony/java/android/telephony/NetworkRegistrationInfo.java +++ b/telephony/java/android/telephony/NetworkRegistrationInfo.java @@ -20,6 +20,7 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; +import android.os.Build; import android.os.Parcel; import android.os.Parcelable; import android.telephony.AccessNetworkConstants.TransportType; @@ -217,6 +218,9 @@ public final class NetworkRegistrationInfo implements Parcelable { @NonNull private String mRplmn; + // Updated based on the accessNetworkTechnology + private boolean mIsUsingCarrierAggregation; + /** * @param domain Network domain. Must be a {@link Domain}. For transport type * {@link AccessNetworkConstants#TRANSPORT_TYPE_WLAN}, this must set to {@link #DOMAIN_PS}. @@ -250,7 +254,7 @@ public final class NetworkRegistrationInfo implements Parcelable { mRegistrationState = registrationState; mRoamingType = (registrationState == REGISTRATION_STATE_ROAMING) ? ServiceState.ROAMING_TYPE_UNKNOWN : ServiceState.ROAMING_TYPE_NOT_ROAMING; - mAccessNetworkTechnology = accessNetworkTechnology; + setAccessNetworkTechnology(accessNetworkTechnology); mRejectCause = rejectCause; mAvailableServices = (availableServices != null) ? new ArrayList<>(availableServices) : new ArrayList<>(); @@ -289,13 +293,12 @@ public final class NetworkRegistrationInfo implements Parcelable { @Nullable CellIdentity cellIdentity, @Nullable String rplmn, int maxDataCalls, boolean isDcNrRestricted, boolean isNrAvailable, boolean isEndcAvailable, - LteVopsSupportInfo lteVopsSupportInfo, - boolean isUsingCarrierAggregation) { + @Nullable VopsSupportInfo vopsSupportInfo) { this(domain, transportType, registrationState, accessNetworkTechnology, rejectCause, emergencyOnly, availableServices, cellIdentity, rplmn); mDataSpecificInfo = new DataSpecificRegistrationInfo( - maxDataCalls, isDcNrRestricted, isNrAvailable, isEndcAvailable, lteVopsSupportInfo, - isUsingCarrierAggregation); + maxDataCalls, isDcNrRestricted, isNrAvailable, + isEndcAvailable, vopsSupportInfo); updateNrState(); } @@ -316,6 +319,7 @@ public final class NetworkRegistrationInfo implements Parcelable { DataSpecificRegistrationInfo.class.getClassLoader()); mNrState = source.readInt(); mRplmn = source.readString(); + mIsUsingCarrierAggregation = source.readBoolean(); } /** @@ -330,6 +334,7 @@ public final class NetworkRegistrationInfo implements Parcelable { mRegistrationState = nri.mRegistrationState; mRoamingType = nri.mRoamingType; mAccessNetworkTechnology = nri.mAccessNetworkTechnology; + mIsUsingCarrierAggregation = nri.mIsUsingCarrierAggregation; mRejectCause = nri.mRejectCause; mEmergencyOnly = nri.mEmergencyOnly; mAvailableServices = new ArrayList<>(nri.mAvailableServices); @@ -340,6 +345,7 @@ public final class NetworkRegistrationInfo implements Parcelable { // TODO: Instead of doing this, we should create a formal way for cloning cell identity. // Cell identity is not an immutable object so we have to deep copy it. mCellIdentity = CellIdentity.CREATOR.createFromParcel(p); + p.recycle(); } if (nri.mVoiceSpecificInfo != null) { @@ -388,7 +394,7 @@ public final class NetworkRegistrationInfo implements Parcelable { } /** - * @return {@code true} if registered on roaming network, {@code false} otherwise. + * @return {@code true} if registered on roaming or home network, {@code false} otherwise. */ public boolean isRegistered() { return mRegistrationState == REGISTRATION_STATE_HOME @@ -396,7 +402,7 @@ public final class NetworkRegistrationInfo implements Parcelable { } /** - * @return {@code true} if registered on roaming network, {@code false} otherwise. + * @return {@code true} if searching for service, {@code false} otherwise. */ public boolean isSearching() { return mRegistrationState == REGISTRATION_STATE_NOT_REGISTERED_SEARCHING; @@ -483,9 +489,7 @@ public final class NetworkRegistrationInfo implements Parcelable { if (tech == TelephonyManager.NETWORK_TYPE_LTE_CA) { // For old device backward compatibility support tech = TelephonyManager.NETWORK_TYPE_LTE; - if (mDataSpecificInfo != null) { - mDataSpecificInfo.setIsUsingCarrierAggregation(true); - } + mIsUsingCarrierAggregation = true; } mAccessNetworkTechnology = tech; } @@ -510,6 +514,27 @@ public final class NetworkRegistrationInfo implements Parcelable { } /** + * Set whether network has configured carrier aggregation or not. + * + * @param isUsingCarrierAggregation set whether or not carrier aggregation is used. + * + * @hide + */ + public void setIsUsingCarrierAggregation(boolean isUsingCarrierAggregation) { + mIsUsingCarrierAggregation = isUsingCarrierAggregation; + } + + /** + * Get whether network has configured carrier aggregation or not. + * + * @return {@code true} if using carrier aggregation. + * @hide + */ + public boolean isUsingCarrierAggregation() { + return mIsUsingCarrierAggregation; + } + + /** * @hide */ @Nullable @@ -571,7 +596,8 @@ public final class NetworkRegistrationInfo implements Parcelable { return "Unknown reg state " + registrationState; } - private static String nrStateToString(@NRState int nrState) { + /** @hide */ + public static String nrStateToString(@NRState int nrState) { switch (nrState) { case NR_STATE_RESTRICTED: return "RESTRICTED"; @@ -613,8 +639,10 @@ public final class NetworkRegistrationInfo implements Parcelable { .append(" cellIdentity=").append(mCellIdentity) .append(" voiceSpecificInfo=").append(mVoiceSpecificInfo) .append(" dataSpecificInfo=").append(mDataSpecificInfo) - .append(" nrState=").append(nrStateToString(mNrState)) + .append(" nrState=").append(Build.IS_DEBUGGABLE + ? nrStateToString(mNrState) : "****") .append(" rRplmn=").append(mRplmn) + .append(" isUsingCarrierAggregation=").append(mIsUsingCarrierAggregation) .append("}").toString(); } @@ -622,7 +650,8 @@ public final class NetworkRegistrationInfo implements Parcelable { public int hashCode() { return Objects.hash(mDomain, mTransportType, mRegistrationState, mRoamingType, mAccessNetworkTechnology, mRejectCause, mEmergencyOnly, mAvailableServices, - mCellIdentity, mVoiceSpecificInfo, mDataSpecificInfo, mNrState, mRplmn); + mCellIdentity, mVoiceSpecificInfo, mDataSpecificInfo, mNrState, mRplmn, + mIsUsingCarrierAggregation); } @Override @@ -642,6 +671,7 @@ public final class NetworkRegistrationInfo implements Parcelable { && mRejectCause == other.mRejectCause && mEmergencyOnly == other.mEmergencyOnly && mAvailableServices.equals(other.mAvailableServices) + && mIsUsingCarrierAggregation == other.mIsUsingCarrierAggregation && Objects.equals(mCellIdentity, other.mCellIdentity) && Objects.equals(mVoiceSpecificInfo, other.mVoiceSpecificInfo) && Objects.equals(mDataSpecificInfo, other.mDataSpecificInfo) @@ -668,6 +698,7 @@ public final class NetworkRegistrationInfo implements Parcelable { dest.writeParcelable(mDataSpecificInfo, 0); dest.writeInt(mNrState); dest.writeString(mRplmn); + dest.writeBoolean(mIsUsingCarrierAggregation); } /** diff --git a/telephony/java/android/telephony/NrVopsSupportInfo.aidl b/telephony/java/android/telephony/NrVopsSupportInfo.aidl new file mode 100644 index 000000000000..460a58971837 --- /dev/null +++ b/telephony/java/android/telephony/NrVopsSupportInfo.aidl @@ -0,0 +1,19 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony; + +parcelable NrVopsSupportInfo; diff --git a/telephony/java/android/telephony/NrVopsSupportInfo.java b/telephony/java/android/telephony/NrVopsSupportInfo.java new file mode 100644 index 000000000000..155ee384b5b0 --- /dev/null +++ b/telephony/java/android/telephony/NrVopsSupportInfo.java @@ -0,0 +1,260 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.os.Parcel; +import android.telephony.AccessNetworkConstants.AccessNetworkType; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Objects; + +/** + * Class stores information related to NR network VoPS support + * @hide + */ +@SystemApi +public final class NrVopsSupportInfo extends VopsSupportInfo { + + /** + * Indicates network does not support vops + */ + public static final int NR_STATUS_VOPS_NOT_SUPPORTED = 0; + + /** + * Indicates network supports vops over 3gpp access. + */ + public static final int NR_STATUS_VOPS_3GPP_SUPPORTED = 1; + + /** + * Indicates network supports vops over non 3gpp access + */ + public static final int NR_STATUS_VOPS_NON_3GPP_SUPPORTED = 2; + + /**@hide*/ + @Retention(RetentionPolicy.SOURCE) + @IntDef( + prefix = {"NR_STATUS_VOPS_"}, + value = { + NR_STATUS_VOPS_NOT_SUPPORTED, + NR_STATUS_VOPS_3GPP_SUPPORTED, + NR_STATUS_VOPS_NON_3GPP_SUPPORTED + }) + public @interface NrVopsStatus {} + + /** + * Indicates network does not support emergency service + */ + public static final int NR_STATUS_EMC_NOT_SUPPORTED = 0; + + /** + * Indicates network supports emergency service in NR connected to 5GCN only + */ + public static final int NR_STATUS_EMC_5GCN_ONLY = 1; + + /** + * Indicates network supports emergency service in E-UTRA connected to 5GCN only + */ + public static final int NR_STATUS_EMC_EUTRA_5GCN_ONLY = 2; + + /** + * Indicates network supports emergency service in NR connected to 5GCN and + * E-UTRA connected to 5GCN + */ + public static final int NR_STATUS_EMC_NR_EUTRA_5GCN = 3; + + /**@hide*/ + @Retention(RetentionPolicy.SOURCE) + @IntDef( + prefix = {"NR_STATUS_EMC_"}, + value = { + NR_STATUS_EMC_NOT_SUPPORTED, + NR_STATUS_EMC_5GCN_ONLY, + NR_STATUS_EMC_EUTRA_5GCN_ONLY, + NR_STATUS_EMC_NR_EUTRA_5GCN + }) + public @interface NrEmcStatus {} + + /** + * Indicates network does not support emergency service + */ + public static final int NR_STATUS_EMF_NOT_SUPPORTED = 0; + + /** + * Indicates network supports emergency service fallback in NR connected to 5GCN only + */ + public static final int NR_STATUS_EMF_5GCN_ONLY = 1; + + /** + * Indicates network supports emergency service fallback in E-UTRA connected to 5GCN only + */ + public static final int NR_STATUS_EMF_EUTRA_5GCN_ONLY = 2; + + /** + * Indicates network supports emergency service fallback in NR connected to 5GCN + * and E-UTRA connected to 5GCN + */ + public static final int NR_STATUS_EMF_NR_EUTRA_5GCN = 3; + + /**@hide*/ + @Retention(RetentionPolicy.SOURCE) + @IntDef( + prefix = {"NR_STATUS_EMF_"}, + value = { + NR_STATUS_EMF_NOT_SUPPORTED, + NR_STATUS_EMF_5GCN_ONLY, + NR_STATUS_EMF_EUTRA_5GCN_ONLY, + NR_STATUS_EMF_NR_EUTRA_5GCN + }) + public @interface NrEmfStatus {} + + @NrVopsStatus + private final int mVopsSupport; + @NrEmcStatus + private final int mEmcSupport; + @NrEmfStatus + private final int mEmfSupport; + + public NrVopsSupportInfo(@NrVopsStatus int vops, @NrEmcStatus int emc, @NrEmcStatus int emf) { + mVopsSupport = vops; + mEmcSupport = emc; + mEmfSupport = emf; + } + + /** + * Provides the NR VoPS support capability as described in: + * 3GPP 24.501 EPS network feature support -> IMS VoPS + */ + public @NrVopsStatus int getVopsSupport() { + return mVopsSupport; + } + + /** + * Provides the NR Emergency bearer support capability as described in: + * 3GPP 24.501 EPS network feature support -> EMC, and + * 38.331 SIB1 : ims-EmergencySupport + */ + public @NrEmcStatus int getEmcSupport() { + return mEmcSupport; + } + + /** + * Provides the NR emergency service fallback support capability as + * described in 3GPP 24.501 EPS network feature support -> EMF + */ + public @NrEmfStatus int getEmfSupport() { + return mEmfSupport; + } + + /** + * Returns whether VoPS is supported by the network + */ + @Override + public boolean isVopsSupported() { + return mVopsSupport != NR_STATUS_VOPS_NOT_SUPPORTED; + } + + /** + * Returns whether emergency service is supported by the network + */ + @Override + public boolean isEmergencyServiceSupported() { + return mEmcSupport != NR_STATUS_EMC_NOT_SUPPORTED; + } + + /** + * Returns whether emergency service fallback is supported by the network + */ + public boolean isEmergencyServiceFallbackSupported() { + return mEmfSupport != NR_STATUS_EMF_NOT_SUPPORTED; + } + + /** + * Implement the Parcelable interface + */ + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel out, int flags) { + super.writeToParcel(out, flags, AccessNetworkType.NGRAN); + out.writeInt(mVopsSupport); + out.writeInt(mEmcSupport); + out.writeInt(mEmfSupport); + } + + @Override + public boolean equals(@Nullable Object o) { + if (o == null || !(o instanceof NrVopsSupportInfo)) { + return false; + } + if (this == o) return true; + NrVopsSupportInfo other = (NrVopsSupportInfo) o; + return mVopsSupport == other.mVopsSupport + && mEmcSupport == other.mEmcSupport + && mEmfSupport == other.mEmfSupport; + } + + @Override + public int hashCode() { + return Objects.hash(mVopsSupport, mEmcSupport, mEmfSupport); + } + + /** + * @return string representation. + */ + @NonNull + @Override + public String toString() { + return ("NrVopsSupportInfo : " + + " mVopsSupport = " + mVopsSupport + + " mEmcSupport = " + mEmcSupport + + " mEmfSupport = " + mEmfSupport); + } + + public static final @android.annotation.NonNull Creator<NrVopsSupportInfo> CREATOR = + new Creator<NrVopsSupportInfo>() { + @Override + public NrVopsSupportInfo createFromParcel(Parcel in) { + // Skip the type info. + in.readInt(); + return new NrVopsSupportInfo(in); + } + + @Override + public NrVopsSupportInfo[] newArray(int size) { + return new NrVopsSupportInfo[size]; + } + }; + + /** @hide */ + protected static NrVopsSupportInfo createFromParcelBody(Parcel in) { + return new NrVopsSupportInfo(in); + } + + private NrVopsSupportInfo(Parcel in) { + mVopsSupport = in.readInt(); + mEmcSupport = in.readInt(); + mEmfSupport = in.readInt(); + } +} diff --git a/telephony/java/android/telephony/OWNERS b/telephony/java/android/telephony/OWNERS new file mode 100644 index 000000000000..6aa399d9ebfb --- /dev/null +++ b/telephony/java/android/telephony/OWNERS @@ -0,0 +1,5 @@ +# Bug component: 20868 + +rgreenwalt@google.com +tgunn@google.com +amitmahajan@google.com diff --git a/telephony/java/android/telephony/PhoneCapability.java b/telephony/java/android/telephony/PhoneCapability.java index b785037e51c1..a3aaf61a6fec 100644 --- a/telephony/java/android/telephony/PhoneCapability.java +++ b/telephony/java/android/telephony/PhoneCapability.java @@ -16,21 +16,27 @@ package android.telephony; +import android.annotation.IntDef; +import android.annotation.IntRange; import android.annotation.NonNull; +import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Objects; /** - * Define capability of a modem group. That is, the capabilities - * are shared between those modems defined by list of modem IDs. - * + * Phone capability which describes the data connection capability of modem. + * It's used to evaluate possible phone config change, for example from single + * SIM device to multi-SIM device. * @hide */ +@SystemApi public final class PhoneCapability implements Parcelable { // Hardcoded default DSDS capability. /** @hide */ @@ -39,6 +45,30 @@ public final class PhoneCapability implements Parcelable { /** @hide */ public static final PhoneCapability DEFAULT_SSSS_CAPABILITY; + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = { "DEVICE_NR_CAPABILITY_" }, value = { + DEVICE_NR_CAPABILITY_NSA, + DEVICE_NR_CAPABILITY_SA, + }) + public @interface DeviceNrCapability {} + + /** + * Indicates DEVICE_NR_CAPABILITY_NSA determine that the device enable the non-standalone + * (NSA) mode of 5G NR. + * @hide + */ + @SystemApi + public static final int DEVICE_NR_CAPABILITY_NSA = 1; + + /** + * Indicates DEVICE_NR_CAPABILITY_SA determine that the device enable the standalone (SA) + * mode of 5G NR. + * @hide + */ + @SystemApi + public static final int DEVICE_NR_CAPABILITY_SA = 2; + static { ModemInfo modemInfo1 = new ModemInfo(0, 0, true, true); ModemInfo modemInfo2 = new ModemInfo(1, 0, true, true); @@ -46,55 +76,91 @@ public final class PhoneCapability implements Parcelable { List<ModemInfo> logicalModemList = new ArrayList<>(); logicalModemList.add(modemInfo1); logicalModemList.add(modemInfo2); - DEFAULT_DSDS_CAPABILITY = new PhoneCapability(1, 1, 0, logicalModemList, false); + int[] deviceNrCapabilities = new int[0]; + + DEFAULT_DSDS_CAPABILITY = new PhoneCapability(1, 1, logicalModemList, false, + deviceNrCapabilities); logicalModemList = new ArrayList<>(); logicalModemList.add(modemInfo1); - DEFAULT_SSSS_CAPABILITY = new PhoneCapability(1, 1, 0, logicalModemList, false); + DEFAULT_SSSS_CAPABILITY = new PhoneCapability(1, 1, logicalModemList, false, + deviceNrCapabilities); } + /** + * mMaxActiveVoiceSubscriptions defines the maximum subscriptions that can support + * simultaneous voice calls. For a dual sim dual standby (DSDS) device it would be one, but + * for a dual sim dual active device it would be 2. + * + * @hide + */ + private final int mMaxActiveVoiceSubscriptions; + + /** + * mMaxActiveDataSubscriptions defines the maximum subscriptions that can support + * simultaneous data connections. + * For example, for L+L device it should be 2. + * + * @hide + */ + private final int mMaxActiveDataSubscriptions; + + /** + * Whether modem supports both internet PDN up so + * that we can do ping test before tearing down the + * other one. + * + * @hide + */ + private final boolean mNetworkValidationBeforeSwitchSupported; + /** @hide */ - public final int maxActiveVoiceCalls; - /** @hide */ - public final int maxActiveData; - /** @hide */ - public final int max5G; - /** @hide */ - public final boolean validationBeforeSwitchSupported; - /** @hide */ - public final List<ModemInfo> logicalModemList; + private final List<ModemInfo> mLogicalModemList; + + /** + * List of logical modem information. + * + * @hide + */ + private final int[] mDeviceNrCapabilities; /** @hide */ - public PhoneCapability(int maxActiveVoiceCalls, int maxActiveData, int max5G, - List<ModemInfo> logicalModemList, boolean validationBeforeSwitchSupported) { - this.maxActiveVoiceCalls = maxActiveVoiceCalls; - this.maxActiveData = maxActiveData; - this.max5G = max5G; + public PhoneCapability(int maxActiveVoiceSubscriptions, int maxActiveDataSubscriptions, + List<ModemInfo> logicalModemList, boolean networkValidationBeforeSwitchSupported, + int[] deviceNrCapabilities) { + this.mMaxActiveVoiceSubscriptions = maxActiveVoiceSubscriptions; + this.mMaxActiveDataSubscriptions = maxActiveDataSubscriptions; // Make sure it's not null. - this.logicalModemList = logicalModemList == null ? new ArrayList<>() : logicalModemList; - this.validationBeforeSwitchSupported = validationBeforeSwitchSupported; + this.mLogicalModemList = logicalModemList == null ? new ArrayList<>() : logicalModemList; + this.mNetworkValidationBeforeSwitchSupported = networkValidationBeforeSwitchSupported; + this.mDeviceNrCapabilities = deviceNrCapabilities; } @Override public String toString() { - return "maxActiveVoiceCalls=" + maxActiveVoiceCalls + " maxActiveData=" + maxActiveData - + " max5G=" + max5G + "logicalModemList:" - + Arrays.toString(logicalModemList.toArray()); + return "mMaxActiveVoiceSubscriptions=" + mMaxActiveVoiceSubscriptions + + " mMaxActiveDataSubscriptions=" + mMaxActiveDataSubscriptions + + " mNetworkValidationBeforeSwitchSupported=" + + mNetworkValidationBeforeSwitchSupported + + " mDeviceNrCapability " + Arrays.toString(mDeviceNrCapabilities); } private PhoneCapability(Parcel in) { - maxActiveVoiceCalls = in.readInt(); - maxActiveData = in.readInt(); - max5G = in.readInt(); - validationBeforeSwitchSupported = in.readBoolean(); - logicalModemList = new ArrayList<>(); - in.readList(logicalModemList, ModemInfo.class.getClassLoader()); + mMaxActiveVoiceSubscriptions = in.readInt(); + mMaxActiveDataSubscriptions = in.readInt(); + mNetworkValidationBeforeSwitchSupported = in.readBoolean(); + mLogicalModemList = new ArrayList<>(); + in.readList(mLogicalModemList, ModemInfo.class.getClassLoader()); + mDeviceNrCapabilities = in.createIntArray(); } @Override public int hashCode() { - return Objects.hash(maxActiveVoiceCalls, maxActiveData, max5G, logicalModemList, - validationBeforeSwitchSupported); + return Objects.hash(mMaxActiveVoiceSubscriptions, + mMaxActiveDataSubscriptions, + mLogicalModemList, + mNetworkValidationBeforeSwitchSupported, + Arrays.hashCode(mDeviceNrCapabilities)); } @Override @@ -109,11 +175,12 @@ public final class PhoneCapability implements Parcelable { PhoneCapability s = (PhoneCapability) o; - return (maxActiveVoiceCalls == s.maxActiveVoiceCalls - && maxActiveData == s.maxActiveData - && max5G == s.max5G - && validationBeforeSwitchSupported == s.validationBeforeSwitchSupported - && logicalModemList.equals(s.logicalModemList)); + return (mMaxActiveVoiceSubscriptions == s.mMaxActiveVoiceSubscriptions + && mMaxActiveDataSubscriptions == s.mMaxActiveDataSubscriptions + && mNetworkValidationBeforeSwitchSupported + == s.mNetworkValidationBeforeSwitchSupported + && mLogicalModemList.equals(s.mLogicalModemList) + && Arrays.equals(mDeviceNrCapabilities, s.mDeviceNrCapabilities)); } /** @@ -127,14 +194,15 @@ public final class PhoneCapability implements Parcelable { * {@link Parcelable#writeToParcel} */ public void writeToParcel(@NonNull Parcel dest, @Parcelable.WriteFlags int flags) { - dest.writeInt(maxActiveVoiceCalls); - dest.writeInt(maxActiveData); - dest.writeInt(max5G); - dest.writeBoolean(validationBeforeSwitchSupported); - dest.writeList(logicalModemList); + dest.writeInt(mMaxActiveVoiceSubscriptions); + dest.writeInt(mMaxActiveDataSubscriptions); + dest.writeBoolean(mNetworkValidationBeforeSwitchSupported); + dest.writeList(mLogicalModemList); + dest.writeIntArray(mDeviceNrCapabilities); } - public static final @android.annotation.NonNull Parcelable.Creator<PhoneCapability> CREATOR = new Parcelable.Creator() { + public static final @android.annotation.NonNull Parcelable.Creator<PhoneCapability> CREATOR = + new Parcelable.Creator() { public PhoneCapability createFromParcel(Parcel in) { return new PhoneCapability(in); } @@ -143,4 +211,57 @@ public final class PhoneCapability implements Parcelable { return new PhoneCapability[size]; } }; + + /** + * @return the maximum subscriptions that can support simultaneous voice calls. For a dual + * sim dual standby (DSDS) device it would be one, but for a dual sim dual active device it + * would be 2. + * @hide + */ + @SystemApi + public @IntRange(from = 1) int getMaxActiveVoiceSubscriptions() { + return mMaxActiveVoiceSubscriptions; + } + + /** + * @return the maximum subscriptions that can support simultaneous data connections. + * For example, for L+L device it should be 2. + * @hide + */ + @SystemApi + public @IntRange(from = 1) int getMaxActiveDataSubscriptions() { + return mMaxActiveDataSubscriptions; + } + + /** + * @return Check whether the Citizens Broadband Radio Service(CBRS) network validation before + * CBRS switch is supported or not. + * + * @hide + */ + public boolean isNetworkValidationBeforeSwitchSupported() { + return mNetworkValidationBeforeSwitchSupported; + } + + /** + * @return The list of logical modem information. + * @hide + */ + public List<ModemInfo> getLogicalModemList() { + return mLogicalModemList; + } + + /** + * Return List of the device's NR capability. If the device doesn't support NR capability, + * then this api return empty array. + * @see DEVICE_NR_CAPABILITY_NSA + * @see DEVICE_NR_CAPABILITY_SA + * + * @return List of the device's NR capability. + * @hide + */ + @SystemApi + public @NonNull @DeviceNrCapability int[] getDeviceNrCapabilities() { + return mDeviceNrCapabilities == null ? (new int[0]) : mDeviceNrCapabilities; + } } diff --git a/telephony/java/android/telephony/PhoneNumberFormattingTextWatcher.java b/telephony/java/android/telephony/PhoneNumberFormattingTextWatcher.java index d4ed860cdafd..24dfbd028d03 100644 --- a/telephony/java/android/telephony/PhoneNumberFormattingTextWatcher.java +++ b/telephony/java/android/telephony/PhoneNumberFormattingTextWatcher.java @@ -17,6 +17,7 @@ package android.telephony; import android.compat.annotation.UnsupportedAppUsage; +import android.os.Build; import android.text.Editable; import android.text.Selection; import android.text.TextWatcher; @@ -50,7 +51,7 @@ public class PhoneNumberFormattingTextWatcher implements TextWatcher { */ private boolean mStopFormatting; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) private AsYouTypeFormatter mFormatter; /** diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java index 58e368bcc444..1273aa3abbc9 100644 --- a/telephony/java/android/telephony/PhoneNumberUtils.java +++ b/telephony/java/android/telephony/PhoneNumberUtils.java @@ -27,6 +27,7 @@ import android.content.Intent; import android.content.res.Resources; import android.database.Cursor; import android.net.Uri; +import android.os.Build; import android.os.PersistableBundle; import android.provider.Contacts; import android.provider.ContactsContract; @@ -477,7 +478,9 @@ public class PhoneNumberUtils { /** * Compare phone numbers a and b, return true if they're identical enough for caller ID purposes. + * @deprecated use {@link #areSamePhoneNumber(String, String, String)} instead */ + @Deprecated public static boolean compare(String a, String b) { // We've used loose comparation at least Eclair, which may change in the future. @@ -488,7 +491,9 @@ public class PhoneNumberUtils { * Compare phone numbers a and b, and return true if they're identical * enough for caller ID purposes. Checks a resource to determine whether * to use a strict or loose comparison algorithm. + * @deprecated use {@link #areSamePhoneNumber(String, String, String)} instead */ + @Deprecated public static boolean compare(Context context, String a, String b) { boolean useStrict = context.getResources().getBoolean( com.android.internal.R.bool.config_use_strict_phone_number_comparation); @@ -1832,7 +1837,7 @@ public class PhoneNumberUtils { * * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @Deprecated public static boolean isPotentialEmergencyNumber(int subId, String number) { // Check against the emergency numbers listed by the RIL / SIM, @@ -2108,7 +2113,7 @@ public class PhoneNumberUtils { * @hide */ @Deprecated - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public static boolean isPotentialLocalEmergencyNumber(Context context, String number) { return isPotentialLocalEmergencyNumber(context, getDefaultVoiceSubId(), number); } @@ -2138,7 +2143,7 @@ public class PhoneNumberUtils { * * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @Deprecated public static boolean isPotentialLocalEmergencyNumber(Context context, int subId, String number) { @@ -3217,7 +3222,7 @@ public class PhoneNumberUtils { } // The conversion map is not defined (this is default). Skip conversion. - if (sConvertToEmergencyMap == null || sConvertToEmergencyMap.length == 0 ) { + if (sConvertToEmergencyMap == null || sConvertToEmergencyMap.length == 0) { return number; } @@ -3253,4 +3258,47 @@ public class PhoneNumberUtils { } return number; } + + /** + * Determines if two phone numbers are the same. + * <p> + * Matching is based on <a href="https://github.com/google/libphonenumber>libphonenumber</a>. + * Unlike {@link #compare(String, String)}, matching takes into account national + * dialing plans rather than simply matching the last 7 digits of the two phone numbers. As a + * result, it is expected that some numbers which would match using the previous method will no + * longer match using this new approach. + * + * @param number1 + * @param number2 + * @param defaultCountryIso The lowercase two letter ISO 3166-1 country code. Used when parsing + * the phone numbers where it is not possible to determine the country + * associated with a phone number based on the number alone. It + * is recommended to pass in + * {@link TelephonyManager#getNetworkCountryIso()}. + * @return True if the two given phone number are same. + */ + public static boolean areSamePhoneNumber(@NonNull String number1, + @NonNull String number2, @NonNull String defaultCountryIso) { + PhoneNumberUtil util = PhoneNumberUtil.getInstance(); + PhoneNumber n1; + PhoneNumber n2; + defaultCountryIso = defaultCountryIso.toUpperCase(); + try { + n1 = util.parseAndKeepRawInput(number1, defaultCountryIso); + n2 = util.parseAndKeepRawInput(number2, defaultCountryIso); + } catch (NumberParseException e) { + return false; + } + + PhoneNumberUtil.MatchType matchType = util.isNumberMatch(n1, n2); + if (matchType == PhoneNumberUtil.MatchType.EXACT_MATCH + || matchType == PhoneNumberUtil.MatchType.NSN_MATCH) { + return true; + } else if (matchType == PhoneNumberUtil.MatchType.SHORT_NSN_MATCH) { + return (n1.getNationalNumber() == n2.getNationalNumber() + && n1.getCountryCode() == n2.getCountryCode()); + } else { + return false; + } + } } diff --git a/telephony/java/android/telephony/PhysicalChannelConfig.java b/telephony/java/android/telephony/PhysicalChannelConfig.java index af62ba4b93a1..8df41fb6b288 100644 --- a/telephony/java/android/telephony/PhysicalChannelConfig.java +++ b/telephony/java/android/telephony/PhysicalChannelConfig.java @@ -17,6 +17,8 @@ package android.telephony; import android.annotation.IntDef; +import android.annotation.IntRange; +import android.annotation.NonNull; import android.os.Parcel; import android.os.Parcelable; import android.telephony.Annotation.NetworkType; @@ -26,12 +28,10 @@ import java.lang.annotation.RetentionPolicy; import java.util.Arrays; import java.util.Objects; -/** - * @hide - */ public final class PhysicalChannelConfig implements Parcelable { // TODO(b/72993578) consolidate these enums in a central location. + /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef({CONNECTION_PRIMARY_SERVING, CONNECTION_SECONDARY_SERVING, CONNECTION_UNKNOWN}) public @interface ConnectionStatus {} @@ -47,7 +47,25 @@ public final class PhysicalChannelConfig implements Parcelable { public static final int CONNECTION_SECONDARY_SERVING = 2; /** Connection status is unknown. */ - public static final int CONNECTION_UNKNOWN = Integer.MAX_VALUE; + public static final int CONNECTION_UNKNOWN = -1; + + /** Channel number is unknown. */ + public static final int CHANNEL_NUMBER_UNKNOWN = Integer.MAX_VALUE; + + /** Physical Cell Id is unknown. */ + public static final int PHYSICAL_CELL_ID_UNKNOWN = -1; + + /** Physical Cell Id's maximum value is 1007. */ + public static final int PHYSICAL_CELL_ID_MAXIMUM_VALUE = 1007; + + /** Cell bandwidth is unknown. */ + public static final int CELL_BANDWIDTH_UNKNOWN = 0; + + /** The frequency is unknown. */ + public static final int FREQUENCY_UNKNOWN = -1; + + /** The band is unknown. */ + public static final int BAND_UNKNOWN = 0; /** * Connection status of the cell. @@ -58,15 +76,20 @@ public final class PhysicalChannelConfig implements Parcelable { private int mCellConnectionStatus; /** - * Cell bandwidth, in kHz. + * Downlink cell bandwidth, in kHz. */ private int mCellBandwidthDownlinkKhz; /** + * Uplink cell bandwidth, in kHz. + */ + private int mCellBandwidthUplinkKhz; + + /** * The radio technology for this physical channel. */ @NetworkType - private int mRat; + private int mNetworkType; /** * The rough frequency range for this physical channel. @@ -75,9 +98,24 @@ public final class PhysicalChannelConfig implements Parcelable { private int mFrequencyRange; /** - * The absolute radio frequency channel number, {@link Integer#MAX_VALUE} if unknown. + * The frequency of Downlink. + */ + private int mDownlinkFrequency; + + /** + * The frequency of Uplink. + */ + private int mUplinkFrequency; + + /** + * Downlink Absolute Radio Frequency Channel Number */ - private int mChannelNumber; + private int mDownlinkChannelNumber; + + /** + * Uplink Absolute Radio Frequency Channel Number + */ + private int mUplinkChannelNumber; /** * A list of data calls mapped to this physical channel. An empty list means the physical @@ -86,51 +124,72 @@ public final class PhysicalChannelConfig implements Parcelable { private int[] mContextIds; /** - * The physical cell identifier for this cell - PCI, PSC, {@link Integer#MAX_VALUE} if known. + * The physical cell identifier for this cell - PCI, PSC, {@link #PHYSICAL_CELL_ID_UNKNOWN} + * if unknown. */ private int mPhysicalCellId; + /** + * This is the band which is being used. + */ + private int mBand; + @Override public int describeContents() { return 0; } @Override - public void writeToParcel(Parcel dest, int flags) { + public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeInt(mCellConnectionStatus); dest.writeInt(mCellBandwidthDownlinkKhz); - dest.writeInt(mRat); - dest.writeInt(mChannelNumber); + dest.writeInt(mCellBandwidthUplinkKhz); + dest.writeInt(mNetworkType); + dest.writeInt(mDownlinkChannelNumber); + dest.writeInt(mUplinkChannelNumber); dest.writeInt(mFrequencyRange); dest.writeIntArray(mContextIds); dest.writeInt(mPhysicalCellId); + dest.writeInt(mBand); } /** - * @return Cell bandwidth, in kHz + * @return Downlink cell bandwidth in kHz, {@link #CELL_BANDWIDTH_UNKNOWN} if unknown. */ - public int getCellBandwidthDownlink() { + @IntRange(from = 1) + public int getCellBandwidthDownlinkKhz() { return mCellBandwidthDownlinkKhz; } /** + * @return Uplink cell bandwidth in kHz, {@link #CELL_BANDWIDTH_UNKNOWN} if unknown. + */ + @IntRange(from = 1) + public int getCellBandwidthUplinkKhz() { + return mCellBandwidthUplinkKhz; + } + + /** * Get the list of data call ids mapped to this physical channel. This list is sorted into * ascending numerical order. Each id in this list must match the id in * {@link com.android.internal.telephony.dataconnection.DataConnection}. An empty list means the * physical channel has no data call mapped to it. * - * @return an integer list indicates the data call ids. + * @return an integer list indicates the data call ids, + * @hide */ public int[] getContextIds() { return mContextIds; } /** - * @return the rough frequency range for this physical channel. + * @return the rough frequency range for this physical channel, + * {@link ServiceState#FREQUENCY_RANGE_UNKNOWN} if unknown. * @see {@link ServiceState#FREQUENCY_RANGE_LOW} * @see {@link ServiceState#FREQUENCY_RANGE_MID} * @see {@link ServiceState#FREQUENCY_RANGE_HIGH} * @see {@link ServiceState#FREQUENCY_RANGE_MMWAVE} + * @hide */ @ServiceState.FrequencyRange public int getFrequencyRange() { @@ -138,11 +197,48 @@ public final class PhysicalChannelConfig implements Parcelable { } /** - * @return the absolute radio frequency channel number for this physical channel, - * {@link Integer#MAX_VALUE} if unknown. + * @return Downlink Absolute Radio Frequency Channel Number, + * {@link #CHANNEL_NUMBER_UNKNOWN} if unknown. + */ + @IntRange(from = 0) + public int getDownlinkChannelNumber() { + return mDownlinkChannelNumber; + } + + /** + * @return Uplink Absolute Radio Frequency Channel Number, + * {@link #CHANNEL_NUMBER_UNKNOWN} if unknown. + */ + @IntRange(from = 0) + public int getUplinkChannelNumber() { + return mUplinkChannelNumber; + } + + /** + * The valid bands are {@link AccessNetworkConstants.GeranBand}, + * {@link AccessNetworkConstants.UtranBand}, {@link AccessNetworkConstants.EutranBand} and + * {@link AccessNetworkConstants.NgranBands}. + * + * @return the frequency band, {@link #BAND_UNKNOWN} if unknown. */ + @IntRange(from = 1, to = 261) + public int getBand() { + return mBand; + } + + /** + * @return The downlink frequency in kHz, {@link #FREQUENCY_UNKNOWN} if unknown. */ - public int getChannelNumber() { - return mChannelNumber; + @IntRange(from = 0) + public int getDownlinkFrequencyKhz() { + return mDownlinkFrequency; + } + + /** + * @return The uplink frequency in kHz, {@link #FREQUENCY_UNKNOWN} if unknown. + */ + @IntRange(from = 0) + public int getUplinkFrequencyKhz() { + return mUplinkFrequency; } /** @@ -152,19 +248,24 @@ public final class PhysicalChannelConfig implements Parcelable { * In EUTRAN, this value is physical layer cell identity. The range is [0, 503]. * Reference: 3GPP TS 36.211 section 6.11. * - * In 5G RAN, this value is physical layer cell identity. The range is [0, 1008]. + * In 5G RAN, this value is physical layer cell identity. The range is [0, 1007]. * Reference: 3GPP TS 38.211 section 7.4.2.1. * - * @return the physical cell identifier for this cell, {@link Integer#MAX_VALUE} if unknown. + * @return the physical cell identifier for this cell, {@link #PHYSICAL_CELL_ID_UNKNOWN} + * if {@link android.telephony.CellInfo#UNAVAILABLE}. */ + @IntRange(from = 0, to = 1007) public int getPhysicalCellId() { return mPhysicalCellId; } - /**The radio technology for this physical channel. */ + /** + * @return The network type for this physical channel, + * {@link TelephonyManager#NETWORK_TYPE_UNKNOWN} if unknown. + */ @NetworkType - public int getRat() { - return mRat; + public int getNetworkType() { + return mNetworkType; } /** @@ -174,14 +275,25 @@ public final class PhysicalChannelConfig implements Parcelable { * @see #CONNECTION_SECONDARY_SERVING * @see #CONNECTION_UNKNOWN * - * @return Connection status of the cell + * @return Connection status of the cell, {@link #CONNECTION_UNKNOWN} if unknown. */ @ConnectionStatus public int getConnectionStatus() { return mCellConnectionStatus; } - /** @return String representation of the connection status */ + /** + * Return a copy of this PhysicalChannelConfig object but redact all the location info. + * @hide + */ + public PhysicalChannelConfig createLocationInfoSanitizedCopy() { + return new Builder(this).setPhysicalCellId(PHYSICAL_CELL_ID_UNKNOWN).build(); + } + + /** + * @return String representation of the connection status + * @hide + */ private String getConnectionStatusString() { switch(mCellConnectionStatus) { case CONNECTION_PRIMARY_SERVING: @@ -195,6 +307,97 @@ public final class PhysicalChannelConfig implements Parcelable { } } + private void setDownlinkFrequency() { + switch (mNetworkType) { + case TelephonyManager.NETWORK_TYPE_NR: + mDownlinkFrequency = AccessNetworkUtils.getFrequencyFromNrArfcn( + mDownlinkChannelNumber); + break; + case TelephonyManager.NETWORK_TYPE_LTE: + mDownlinkFrequency = AccessNetworkUtils.getFrequencyFromEarfcn( + mBand, mDownlinkChannelNumber, false); + break; + case TelephonyManager.NETWORK_TYPE_HSPAP: + case TelephonyManager.NETWORK_TYPE_TD_SCDMA: + case TelephonyManager.NETWORK_TYPE_UMTS: + case TelephonyManager.NETWORK_TYPE_HSDPA: + case TelephonyManager.NETWORK_TYPE_HSUPA: + case TelephonyManager.NETWORK_TYPE_HSPA: + mDownlinkFrequency = AccessNetworkUtils.getFrequencyFromUarfcn( + mBand, mDownlinkChannelNumber, false); + break; + case TelephonyManager.NETWORK_TYPE_GPRS: + case TelephonyManager.NETWORK_TYPE_EDGE: + case TelephonyManager.NETWORK_TYPE_GSM: + mDownlinkFrequency = AccessNetworkUtils.getFrequencyFromArfcn( + mBand, mDownlinkChannelNumber, false); + break; + } + } + + private void setUplinkFrequency() { + switch (mNetworkType){ + case TelephonyManager.NETWORK_TYPE_NR: + mUplinkFrequency = mDownlinkFrequency; + break; + case TelephonyManager.NETWORK_TYPE_LTE: + mUplinkFrequency = AccessNetworkUtils.getFrequencyFromEarfcn( + mBand, mUplinkChannelNumber, true); + break; + case TelephonyManager.NETWORK_TYPE_HSPAP: + case TelephonyManager.NETWORK_TYPE_TD_SCDMA: + case TelephonyManager.NETWORK_TYPE_UMTS: + case TelephonyManager.NETWORK_TYPE_HSDPA: + case TelephonyManager.NETWORK_TYPE_HSUPA: + case TelephonyManager.NETWORK_TYPE_HSPA: + mUplinkFrequency = AccessNetworkUtils.getFrequencyFromUarfcn( + mBand, mUplinkChannelNumber, true); + break; + case TelephonyManager.NETWORK_TYPE_GPRS: + case TelephonyManager.NETWORK_TYPE_EDGE: + case TelephonyManager.NETWORK_TYPE_GSM: + mUplinkFrequency = AccessNetworkUtils.getFrequencyFromArfcn( + mBand, mUplinkChannelNumber, true); + break; + } + } + + private void setFrequencyRange() { + if (mFrequencyRange != ServiceState.FREQUENCY_RANGE_UNKNOWN) { + return; + } + + switch (mNetworkType) { + case TelephonyManager.NETWORK_TYPE_NR: + mFrequencyRange = AccessNetworkUtils.getFrequencyRangeGroupFromNrBand(mBand); + break; + case TelephonyManager.NETWORK_TYPE_LTE: + mFrequencyRange = AccessNetworkUtils.getFrequencyRangeGroupFromEutranBand(mBand); + break; + case TelephonyManager.NETWORK_TYPE_HSPAP: + case TelephonyManager.NETWORK_TYPE_TD_SCDMA: + case TelephonyManager.NETWORK_TYPE_UMTS: + case TelephonyManager.NETWORK_TYPE_HSDPA: + case TelephonyManager.NETWORK_TYPE_HSUPA: + case TelephonyManager.NETWORK_TYPE_HSPA: + mFrequencyRange = AccessNetworkUtils.getFrequencyRangeGroupFromUtranBand(mBand); + break; + case TelephonyManager.NETWORK_TYPE_GPRS: + case TelephonyManager.NETWORK_TYPE_EDGE: + case TelephonyManager.NETWORK_TYPE_GSM: + mFrequencyRange = AccessNetworkUtils.getFrequencyRangeGroupFromGeranBand(mBand); + break; + default: + mFrequencyRange = ServiceState.FREQUENCY_RANGE_UNKNOWN; + break; + } + + if (mFrequencyRange == ServiceState.FREQUENCY_RANGE_UNKNOWN) { + mFrequencyRange = AccessNetworkUtils.getFrequencyRangeFromArfcn( + mDownlinkFrequency); + } + } + @Override public boolean equals(Object o) { if (this == o) { @@ -208,30 +411,37 @@ public final class PhysicalChannelConfig implements Parcelable { PhysicalChannelConfig config = (PhysicalChannelConfig) o; return mCellConnectionStatus == config.mCellConnectionStatus && mCellBandwidthDownlinkKhz == config.mCellBandwidthDownlinkKhz - && mRat == config.mRat + && mCellBandwidthUplinkKhz == config.mCellBandwidthUplinkKhz + && mNetworkType == config.mNetworkType && mFrequencyRange == config.mFrequencyRange - && mChannelNumber == config.mChannelNumber + && mDownlinkChannelNumber == config.mDownlinkChannelNumber + && mUplinkChannelNumber == config.mUplinkChannelNumber && mPhysicalCellId == config.mPhysicalCellId - && Arrays.equals(mContextIds, config.mContextIds); + && Arrays.equals(mContextIds, config.mContextIds) + && mBand == config.mBand + && mDownlinkFrequency == config.mDownlinkFrequency + && mUplinkFrequency == config.mUplinkFrequency; } @Override public int hashCode() { return Objects.hash( - mCellConnectionStatus, mCellBandwidthDownlinkKhz, mRat, mFrequencyRange, - mChannelNumber, mPhysicalCellId, mContextIds); + mCellConnectionStatus, mCellBandwidthDownlinkKhz, mCellBandwidthUplinkKhz, + mNetworkType, mFrequencyRange, mDownlinkChannelNumber, mUplinkChannelNumber, + mContextIds, mPhysicalCellId, mBand, mDownlinkFrequency, mUplinkFrequency); } - public static final @android.annotation.NonNull Parcelable.Creator<PhysicalChannelConfig> CREATOR = - new Parcelable.Creator<PhysicalChannelConfig>() { - public PhysicalChannelConfig createFromParcel(Parcel in) { - return new PhysicalChannelConfig(in); - } + public static final + @android.annotation.NonNull Parcelable.Creator<PhysicalChannelConfig> CREATOR = + new Parcelable.Creator<PhysicalChannelConfig>() { + public PhysicalChannelConfig createFromParcel(Parcel in) { + return new PhysicalChannelConfig(in); + } - public PhysicalChannelConfig[] newArray(int size) { - return new PhysicalChannelConfig[size]; - } - }; + public PhysicalChannelConfig[] newArray(int size) { + return new PhysicalChannelConfig[size]; + } + }; @Override public String toString() { @@ -240,16 +450,26 @@ public final class PhysicalChannelConfig implements Parcelable { .append(getConnectionStatusString()) .append(",mCellBandwidthDownlinkKhz=") .append(mCellBandwidthDownlinkKhz) - .append(",mRat=") - .append(TelephonyManager.getNetworkTypeName(mRat)) + .append(",mCellBandwidthUplinkKhz=") + .append(mCellBandwidthUplinkKhz) + .append(",mNetworkType=") + .append(TelephonyManager.getNetworkTypeName(mNetworkType)) .append(",mFrequencyRange=") .append(ServiceState.frequencyRangeToString(mFrequencyRange)) - .append(",mChannelNumber=") - .append(mChannelNumber) + .append(",mDownlinkChannelNumber=") + .append(mDownlinkChannelNumber) + .append(",mUplinkChannelNumber=") + .append(mUplinkChannelNumber) .append(",mContextIds=") .append(Arrays.toString(mContextIds)) .append(",mPhysicalCellId=") .append(mPhysicalCellId) + .append(",mBand=") + .append(mBand) + .append(",mDownlinkFrequency=") + .append(mDownlinkFrequency) + .append(",mUplinkFrequency=") + .append(mUplinkFrequency) .append("}") .toString(); } @@ -257,89 +477,161 @@ public final class PhysicalChannelConfig implements Parcelable { private PhysicalChannelConfig(Parcel in) { mCellConnectionStatus = in.readInt(); mCellBandwidthDownlinkKhz = in.readInt(); - mRat = in.readInt(); - mChannelNumber = in.readInt(); + mCellBandwidthUplinkKhz = in.readInt(); + mNetworkType = in.readInt(); + mDownlinkChannelNumber = in.readInt(); + mUplinkChannelNumber = in.readInt(); mFrequencyRange = in.readInt(); mContextIds = in.createIntArray(); mPhysicalCellId = in.readInt(); + mBand = in.readInt(); + if (mBand > BAND_UNKNOWN) { + setDownlinkFrequency(); + setUplinkFrequency(); + setFrequencyRange(); + } } private PhysicalChannelConfig(Builder builder) { mCellConnectionStatus = builder.mCellConnectionStatus; mCellBandwidthDownlinkKhz = builder.mCellBandwidthDownlinkKhz; - mRat = builder.mRat; - mChannelNumber = builder.mChannelNumber; + mCellBandwidthUplinkKhz = builder.mCellBandwidthUplinkKhz; + mNetworkType = builder.mNetworkType; + mDownlinkChannelNumber = builder.mDownlinkChannelNumber; + mUplinkChannelNumber = builder.mUplinkChannelNumber; mFrequencyRange = builder.mFrequencyRange; mContextIds = builder.mContextIds; mPhysicalCellId = builder.mPhysicalCellId; + mBand = builder.mBand; + if (mBand > BAND_UNKNOWN) { + setDownlinkFrequency(); + setUplinkFrequency(); + setFrequencyRange(); + } } - /** The builder of {@code PhysicalChannelConfig}. */ + /** + * The builder of {@code PhysicalChannelConfig}. + * @hide + */ public static final class Builder { - private int mRat; + private int mNetworkType; private int mFrequencyRange; - private int mChannelNumber; + private int mDownlinkChannelNumber; + private int mUplinkChannelNumber; private int mCellBandwidthDownlinkKhz; + private int mCellBandwidthUplinkKhz; private int mCellConnectionStatus; private int[] mContextIds; private int mPhysicalCellId; + private int mBand; - /** @hide */ public Builder() { - mRat = ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN; + mNetworkType = TelephonyManager.NETWORK_TYPE_UNKNOWN; mFrequencyRange = ServiceState.FREQUENCY_RANGE_UNKNOWN; - mChannelNumber = Integer.MAX_VALUE; - mCellBandwidthDownlinkKhz = 0; + mDownlinkChannelNumber = CHANNEL_NUMBER_UNKNOWN; + mUplinkChannelNumber = CHANNEL_NUMBER_UNKNOWN; + mCellBandwidthDownlinkKhz = CELL_BANDWIDTH_UNKNOWN; + mCellBandwidthUplinkKhz = CELL_BANDWIDTH_UNKNOWN; mCellConnectionStatus = CONNECTION_UNKNOWN; mContextIds = new int[0]; - mPhysicalCellId = Integer.MAX_VALUE; + mPhysicalCellId = PHYSICAL_CELL_ID_UNKNOWN; + mBand = BAND_UNKNOWN; + } + + /** + * Builder object constructed from existing PhysicalChannelConfig object. + * @hide + */ + public Builder(PhysicalChannelConfig config) { + mNetworkType = config.getNetworkType(); + mFrequencyRange = config.getFrequencyRange(); + mDownlinkChannelNumber = config.getDownlinkChannelNumber(); + mUplinkChannelNumber = config.getUplinkChannelNumber(); + mCellBandwidthDownlinkKhz = config.getCellBandwidthDownlinkKhz(); + mCellBandwidthUplinkKhz = config.getCellBandwidthUplinkKhz(); + mCellConnectionStatus = config.getConnectionStatus(); + mContextIds = Arrays.copyOf(config.getContextIds(), config.getContextIds().length); + mPhysicalCellId = config.getPhysicalCellId(); + mBand = config.getBand(); } - /** @hide */ public PhysicalChannelConfig build() { return new PhysicalChannelConfig(this); } - /** @hide */ - public Builder setRat(int rat) { - this.mRat = rat; + public @NonNull Builder setNetworkType(@NetworkType int networkType) { + if (!TelephonyManager.isNetworkTypeValid(networkType)) { + throw new IllegalArgumentException("Network type: " + networkType + " is invalid."); + } + mNetworkType = networkType; return this; } - /** @hide */ - public Builder setFrequencyRange(int frequencyRange) { - this.mFrequencyRange = frequencyRange; + public @NonNull Builder setFrequencyRange(int frequencyRange) { + if (!ServiceState.isFrequencyRangeValid(frequencyRange) + && frequencyRange != ServiceState.FREQUENCY_RANGE_UNKNOWN) { + throw new IllegalArgumentException("Frequency range: " + frequencyRange + + " is invalid."); + } + mFrequencyRange = frequencyRange; return this; } - /** @hide */ - public Builder setChannelNumber(int channelNumber) { - this.mChannelNumber = channelNumber; + public @NonNull Builder setDownlinkChannelNumber(int downlinkChannelNumber) { + mDownlinkChannelNumber = downlinkChannelNumber; return this; } - /** @hide */ - public Builder setCellBandwidthDownlinkKhz(int cellBandwidthDownlinkKhz) { - this.mCellBandwidthDownlinkKhz = cellBandwidthDownlinkKhz; + public @NonNull Builder setUplinkChannelNumber(int uplinkChannelNumber) { + mUplinkChannelNumber = uplinkChannelNumber; return this; } - /** @hide */ - public Builder setCellConnectionStatus(int connectionStatus) { - this.mCellConnectionStatus = connectionStatus; + public @NonNull Builder setCellBandwidthDownlinkKhz(int cellBandwidthDownlinkKhz) { + if (cellBandwidthDownlinkKhz < CELL_BANDWIDTH_UNKNOWN) { + throw new IllegalArgumentException("Cell downlink bandwidth(kHz): " + + cellBandwidthDownlinkKhz + " is invalid."); + } + mCellBandwidthDownlinkKhz = cellBandwidthDownlinkKhz; return this; } - /** @hide */ - public Builder setContextIds(int[] contextIds) { + public @NonNull Builder setCellBandwidthUplinkKhz(int cellBandwidthUplinkKhz) { + if (cellBandwidthUplinkKhz < CELL_BANDWIDTH_UNKNOWN) { + throw new IllegalArgumentException("Cell uplink bandwidth(kHz): "+ + cellBandwidthUplinkKhz +" is invalid."); + } + mCellBandwidthUplinkKhz = cellBandwidthUplinkKhz; + return this; + } + + public @NonNull Builder setCellConnectionStatus(int connectionStatus) { + mCellConnectionStatus = connectionStatus; + return this; + } + + public @NonNull Builder setContextIds(int[] contextIds) { if (contextIds != null) Arrays.sort(contextIds); - this.mContextIds = contextIds; + mContextIds = contextIds; return this; } - /** @hide */ - public Builder setPhysicalCellId(int physicalCellId) { - this.mPhysicalCellId = physicalCellId; + public @NonNull Builder setPhysicalCellId(int physicalCellId) { + if (physicalCellId > PHYSICAL_CELL_ID_MAXIMUM_VALUE) { + throw new IllegalArgumentException("Physical cell Id: " + physicalCellId + + " is over limit."); + } + mPhysicalCellId = physicalCellId; + return this; + } + + public @NonNull Builder setBand(int band) { + if (band <= BAND_UNKNOWN) { + throw new IllegalArgumentException("Band: " + band + + " is invalid."); + } + mBand = band; return this; } } diff --git a/telephony/java/android/telephony/PinResult.java b/telephony/java/android/telephony/PinResult.java index 98d6448e77ea..b8c1ffe58371 100644 --- a/telephony/java/android/telephony/PinResult.java +++ b/telephony/java/android/telephony/PinResult.java @@ -19,6 +19,7 @@ package android.telephony; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; @@ -27,16 +28,23 @@ import com.android.internal.telephony.PhoneConstants; import java.util.Objects; /** - * Holds the result from a pin attempt. + * Holds the result from a PIN attempt. + * + * @see TelephonyManager#supplyIccLockPin + * @see TelephonyManager#supplyIccLockPuk + * @see TelephonyManager#setIccLockEnabled + * @see TelephonyManager#changeIccLockPin * * @hide */ +@SystemApi public final class PinResult implements Parcelable { /** @hide */ @IntDef({ PIN_RESULT_TYPE_SUCCESS, PIN_RESULT_TYPE_INCORRECT, PIN_RESULT_TYPE_FAILURE, + PIN_RESULT_TYPE_ABORTED, }) public @interface PinResultType {} @@ -55,27 +63,32 @@ public final class PinResult implements Parcelable { */ public static final int PIN_RESULT_TYPE_FAILURE = PhoneConstants.PIN_GENERAL_FAILURE; + /** + * Indicates that the pin attempt was aborted. + */ + public static final int PIN_RESULT_TYPE_ABORTED = PhoneConstants.PIN_OPERATION_ABORTED; + private static final PinResult sFailedResult = new PinResult(PinResult.PIN_RESULT_TYPE_FAILURE, -1); - private final @PinResultType int mType; + private final @PinResultType int mResult; private final int mAttemptsRemaining; /** - * Returns either success, incorrect or failure. + * Returns the result of the PIN attempt. * - * @see #PIN_RESULT_TYPE_SUCCESS - * @see #PIN_RESULT_TYPE_INCORRECT - * @see #PIN_RESULT_TYPE_FAILURE - * @return The result type of the pin attempt. + * @return The result of the PIN attempt. */ - public @PinResultType int getType() { - return mType; + public @PinResultType int getResult() { + return mResult; } /** - * The number of pin attempts remaining. + * Returns the number of PIN attempts remaining. + * This will be set when {@link #getResult} is {@link #PIN_RESULT_TYPE_INCORRECT}. + * Indicates the number of attempts at entering the PIN before the SIM will be locked and + * require a PUK unlock to be performed. * * @return Number of attempts remaining. */ @@ -83,22 +96,32 @@ public final class PinResult implements Parcelable { return mAttemptsRemaining; } + /** + * Used to indicate a failed PIN attempt result. + * + * @return default PinResult for failure. + * + * @hide + */ @NonNull public static PinResult getDefaultFailedResult() { return sFailedResult; } /** - * PinResult constructor + * PinResult constructor. * - * @param type The type of pin result. + * @param result The pin result value. * @see #PIN_RESULT_TYPE_SUCCESS * @see #PIN_RESULT_TYPE_INCORRECT * @see #PIN_RESULT_TYPE_FAILURE + * @see #PIN_RESULT_TYPE_ABORTED * @param attemptsRemaining Number of pin attempts remaining. + * + * @hide */ - public PinResult(@PinResultType int type, int attemptsRemaining) { - mType = type; + public PinResult(@PinResultType int result, int attemptsRemaining) { + mResult = result; mAttemptsRemaining = attemptsRemaining; } @@ -108,7 +131,7 @@ public final class PinResult implements Parcelable { * @hide */ private PinResult(Parcel in) { - mType = in.readInt(); + mResult = in.readInt(); mAttemptsRemaining = in.readInt(); } @@ -118,11 +141,11 @@ public final class PinResult implements Parcelable { @NonNull @Override public String toString() { - return "type: " + getType() + ", attempts remaining: " + getAttemptsRemaining(); + return "result: " + getResult() + ", attempts remaining: " + getAttemptsRemaining(); } /** - * Required to be Parcelable + * Describe the contents of this object. */ @Override public int describeContents() { @@ -130,15 +153,17 @@ public final class PinResult implements Parcelable { } /** - * Required to be Parcelable + * Write this object to a Parcel. */ @Override public void writeToParcel(@NonNull Parcel out, int flags) { - out.writeInt(mType); + out.writeInt(mResult); out.writeInt(mAttemptsRemaining); } - /** Required to be Parcelable */ + /** + * Parcel creator class. + */ public static final @NonNull Parcelable.Creator<PinResult> CREATOR = new Creator<PinResult>() { public PinResult createFromParcel(Parcel in) { return new PinResult(in); @@ -150,7 +175,7 @@ public final class PinResult implements Parcelable { @Override public int hashCode() { - return Objects.hash(mAttemptsRemaining, mType); + return Objects.hash(mAttemptsRemaining, mResult); } @Override @@ -165,7 +190,7 @@ public final class PinResult implements Parcelable { return false; } PinResult other = (PinResult) obj; - return (mType == other.mType + return (mResult == other.mResult && mAttemptsRemaining == other.mAttemptsRemaining); } } diff --git a/telephony/java/android/telephony/PreciseDataConnectionState.java b/telephony/java/android/telephony/PreciseDataConnectionState.java index b682bdd7aee3..ce2f3f924554 100644 --- a/telephony/java/android/telephony/PreciseDataConnectionState.java +++ b/telephony/java/android/telephony/PreciseDataConnectionState.java @@ -28,11 +28,15 @@ import android.net.LinkProperties; import android.os.Build; import android.os.Parcel; import android.os.Parcelable; +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.data.ApnSetting; +import android.telephony.data.DataCallResponse; + +import com.android.internal.telephony.util.TelephonyUtils; import java.util.Objects; @@ -53,14 +57,13 @@ import java.util.Objects; * */ public final class PreciseDataConnectionState implements Parcelable { - - private @DataState int mState = TelephonyManager.DATA_UNKNOWN; - private @NetworkType int mNetworkType = TelephonyManager.NETWORK_TYPE_UNKNOWN; - private @DataFailureCause int mFailCause = DataFailCause.NONE; - private @ApnType int mApnTypes = ApnSetting.TYPE_NONE; - private String mApn = ""; - private LinkProperties mLinkProperties = null; - private ApnSetting mApnSetting = null; + private final @TransportType int mTransportType; + private final int mId; + private final @DataState int mState; + private final @NetworkType int mNetworkType; + private final @DataFailureCause int mFailCause; + private final LinkProperties mLinkProperties; + private final ApnSetting mApnSetting; /** * Constructor @@ -70,67 +73,61 @@ public final class PreciseDataConnectionState implements Parcelable { */ @TestApi @Deprecated - @UnsupportedAppUsage // (maxTargetSdk = Build.VERSION_CODES.Q) + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) // (maxTargetSdk = Build.VERSION_CODES.Q) // FIXME: figure out how to remove the UnsupportedAppUsage and delete this constructor public PreciseDataConnectionState(@DataState int state, @NetworkType int networkType, @ApnType int apnTypes, @NonNull String apn, @Nullable LinkProperties linkProperties, @DataFailureCause int failCause) { - this(state, networkType, apnTypes, apn, linkProperties, failCause, null); + this(AccessNetworkConstants.TRANSPORT_TYPE_INVALID, -1, state, networkType, + linkProperties, failCause, new ApnSetting.Builder() + .setApnTypeBitmask(apnTypes) + .setApnName(apn) + .setEntryName(apn) + .build()); } /** * Constructor of PreciseDataConnectionState * - * @param state the state of the data connection - * @param networkType the access network that is/would carry this data connection - * @param apnTypes the APN types that this data connection carries - * @param apn the APN of this data connection - * @param linkProperties if the data connection is connected, the properties of the connection - * @param failCause in case a procedure related to this data connection fails, a non-zero error + * @param transportType The transport of the data connection + * @param id The id of the data connection + * @param state The state of the data connection + * @param networkType The access network that is/would carry this data connection + * @param linkProperties If the data connection is connected, the properties of the connection + * @param failCause In case a procedure related to this data connection fails, a non-zero error * code indicating the cause of the failure. - * @param apnSetting if there is a valid APN for this Data Connection, then the APN Settings; + * @param apnSetting If there is a valid APN for this Data Connection, then the APN Settings; * if there is no valid APN setting for the specific type, then this will be null - * @hide */ - public PreciseDataConnectionState(@DataState int state, - @NetworkType int networkType, - @ApnType int apnTypes, @NonNull String apn, - @Nullable LinkProperties linkProperties, - @DataFailureCause int failCause, - @Nullable ApnSetting apnSetting) { + private PreciseDataConnectionState(@TransportType int transportType, int id, + @DataState int state, @NetworkType int networkType, + @Nullable LinkProperties linkProperties, @DataFailureCause int failCause, + @Nullable ApnSetting apnSetting) { + mTransportType = transportType; + mId = id; mState = state; mNetworkType = networkType; - mApnTypes = apnTypes; - mApn = apn; mLinkProperties = linkProperties; mFailCause = failCause; mApnSetting = apnSetting; } /** - * Empty Constructor - * - * @hide - */ - public PreciseDataConnectionState() { - } - - /** * Construct a PreciseDataConnectionState object from the given parcel. * * @hide */ private PreciseDataConnectionState(Parcel in) { + mTransportType = in.readInt(); + mId = in.readInt(); mState = in.readInt(); mNetworkType = in.readInt(); - mApnTypes = in.readInt(); - mApn = in.readString(); - mLinkProperties = (LinkProperties) in.readParcelable(null); + mLinkProperties = in.readParcelable(LinkProperties.class.getClassLoader()); mFailCause = in.readInt(); - mApnSetting = (ApnSetting) in.readParcelable(null); + mApnSetting = in.readParcelable(ApnSetting.class.getClassLoader()); } /** @@ -160,29 +157,36 @@ public final class PreciseDataConnectionState implements Parcelable { } /** - * Returns the high-level state of this data connection. + * @return The transport type of this data connection. */ - public @DataState int getState() { - return mState; + public @TransportType int getTransportType() { + return mTransportType; } /** - * Returns the network type associated with this data connection. + * @return The unique id of the data connection + * + * Note this is the id assigned by the data service. + * The id remains the same for data connection handover between + * {@link AccessNetworkConstants#TRANSPORT_TYPE_WLAN} and + * {@link AccessNetworkConstants#TRANSPORT_TYPE_WWAN} * - * @deprecated use {@link getNetworkType()} - * @hide - * @removed Removed from the R preview SDK but was never part of the stable API surface. */ - @Deprecated - @SystemApi - public @NetworkType int getDataConnectionNetworkType() { - return mNetworkType; + public int getId() { + return mId; } /** - * Returns the network type associated with this data connection. + * @return The high-level state of this data connection. + */ + public @DataState int getState() { + return mState; + } + + /** + * Get the network type associated with this data connection. * - * Return the current/latest (radio) bearer technology that carries this data connection. + * @return The current/latest (radio) bearer technology that carries this data connection. * For a variety of reasons, the network type can change during the life of the data * connection, and this information is not reliable unless the physical link is currently * active; (there is currently no mechanism to know whether the physical link is active at @@ -202,7 +206,7 @@ public final class PreciseDataConnectionState implements Parcelable { @Deprecated @SystemApi public @ApnType int getDataConnectionApnTypeBitMask() { - return mApnTypes; + return (mApnSetting != null) ? mApnSetting.getApnTypeBitmask() : ApnSetting.TYPE_NONE; } /** @@ -215,21 +219,7 @@ public final class PreciseDataConnectionState implements Parcelable { @SystemApi @Deprecated public String getDataConnectionApn() { - return mApn; - } - - /** - * Get the properties of the network link {@link LinkProperties}. - * - * @deprecated use {@link #getLinkProperties()} - * @hide - * @removed Removed from the R preview SDK but was never part of the stable API surface. - */ - @Deprecated - @SystemApi - @Nullable - public LinkProperties getDataConnectionLinkProperties() { - return mLinkProperties; + return (mApnSetting != null) ? mApnSetting.getApnName() : ""; } /** @@ -265,7 +255,9 @@ public final class PreciseDataConnectionState implements Parcelable { /** * 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() { return mApnSetting; @@ -278,10 +270,10 @@ public final class PreciseDataConnectionState implements Parcelable { @Override public void writeToParcel(@NonNull Parcel out, int flags) { + out.writeInt(mTransportType); + out.writeInt(mId); out.writeInt(mState); out.writeInt(mNetworkType); - out.writeInt(mApnTypes); - out.writeString(mApn); out.writeParcelable(mLinkProperties, flags); out.writeInt(mFailCause); out.writeParcelable(mApnSetting, flags); @@ -301,24 +293,23 @@ public final class PreciseDataConnectionState implements Parcelable { @Override public int hashCode() { - return Objects.hash(mState, mNetworkType, mApnTypes, mApn, mLinkProperties, - mFailCause, mApnSetting); + return Objects.hash(mTransportType, mId, mState, mNetworkType, mFailCause, + mLinkProperties, mApnSetting); } - @Override - public boolean equals(@Nullable Object obj) { - - if (!(obj instanceof PreciseDataConnectionState)) { - return false; - } - PreciseDataConnectionState other = (PreciseDataConnectionState) obj; - return Objects.equals(mApn, other.mApn) && mApnTypes == other.mApnTypes - && mFailCause == other.mFailCause - && Objects.equals(mLinkProperties, other.mLinkProperties) - && mNetworkType == other.mNetworkType - && mState == other.mState - && Objects.equals(mApnSetting, other.mApnSetting); + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PreciseDataConnectionState that = (PreciseDataConnectionState) o; + return mTransportType == that.mTransportType + && mId == that.mId + && mState == that.mState + && mNetworkType == that.mNetworkType + && mFailCause == that.mFailCause + && Objects.equals(mLinkProperties, that.mLinkProperties) + && Objects.equals(mApnSetting, that.mApnSetting); } @NonNull @@ -326,14 +317,137 @@ public final class PreciseDataConnectionState implements Parcelable { public String toString() { StringBuilder sb = new StringBuilder(); - sb.append("Data Connection state: " + mState); - sb.append(", Network type: " + mNetworkType); - sb.append(", APN types: " + ApnSetting.getApnTypesStringFromBitmask(mApnTypes)); - sb.append(", APN: " + mApn); - sb.append(", Link properties: " + mLinkProperties); - sb.append(", Fail cause: " + DataFailCause.toString(mFailCause)); - sb.append(", Apn Setting: " + mApnSetting); + sb.append(" state: " + TelephonyUtils.dataStateToString(mState)); + sb.append(", transport: " + + AccessNetworkConstants.transportTypeToString(mTransportType)); + sb.append(", id: " + mId); + sb.append(", network type: " + TelephonyManager.getNetworkTypeName(mNetworkType)); + sb.append(", APN Setting: " + mApnSetting); + sb.append(", link properties: " + mLinkProperties); + sb.append(", fail cause: " + DataFailCause.toString(mFailCause)); return sb.toString(); } + + /** + * {@link PreciseDataConnectionState} builder + * + * @hide + */ + public static final class Builder { + /** The transport type of the data connection */ + private @TransportType int mTransportType = AccessNetworkConstants.TRANSPORT_TYPE_INVALID; + + /** + * The unique ID of the data connection. This is the id assigned in + * {@link DataCallResponse)}. + */ + private int mId = -1; + + /** The state of the data connection */ + private @DataState int mState = TelephonyManager.DATA_UNKNOWN; + + /** The network type associated with this data connection */ + private @NetworkType int mNetworkType = TelephonyManager.NETWORK_TYPE_UNKNOWN; + + /** If the data connection is connected, the properties of the connection */ + private @Nullable LinkProperties mLinkProperties = null; + + /** + * In case a procedure related to this data connection fails, a non-zero error code + * indicating the cause of the failure. + */ + private @DataFailureCause int mFailCause = DataFailCause.NONE; + + /** The APN Setting for this data connection */ + private @Nullable ApnSetting mApnSetting = null; + + /** + * Set the transport type of the data connection. + * + * @param transportType The transport type of the data connection + * @return The builder + */ + public @NonNull Builder setTransportType(@TransportType int transportType) { + mTransportType = transportType; + return this; + } + + /** + * Set the id of the data connection. + * + * @param id The id of the data connection + * @return The builder + */ + public @NonNull Builder setId(int id) { + mId = id; + return this; + } + + /** + * Set the state of the data connection. + * + * @param state The state of the data connection + * @return The builder + */ + public @NonNull Builder setState(@DataState int state) { + mState = state; + return this; + } + + /** + * Set the network type associated with this data connection. + * + * @param networkType The network type + * @return The builder + */ + public @NonNull Builder setNetworkType(@NetworkType int networkType) { + mNetworkType = networkType; + return this; + } + + /** + * Set the link properties of the connection. + * + * @param linkProperties Link properties + * @return The builder + */ + public @NonNull Builder setLinkProperties(LinkProperties linkProperties) { + mLinkProperties = linkProperties; + return this; + } + + /** + * Set the fail cause of the data connection. + * + * @param failCause In case a procedure related to this data connection fails, a non-zero + * error code indicating the cause of the failure. + * @return The builder + */ + public @NonNull Builder setFailCause(@DataFailureCause int failCause) { + mFailCause = failCause; + return this; + } + + /** + * Set the APN Setting for this data connection. + * + * @param apnSetting APN setting + * @return This builder + */ + public @NonNull Builder setApnSetting(@NonNull ApnSetting apnSetting) { + mApnSetting = apnSetting; + return this; + } + + /** + * Build the {@link PreciseDataConnectionState} instance. + * + * @return The {@link PreciseDataConnectionState} instance + */ + public PreciseDataConnectionState build() { + return new PreciseDataConnectionState(mTransportType, mId, mState, mNetworkType, + mLinkProperties, mFailCause, mApnSetting); + } + } } diff --git a/telephony/java/android/telephony/PreciseDisconnectCause.java b/telephony/java/android/telephony/PreciseDisconnectCause.java index 250d9e8b212e..3b4cf75e7919 100644 --- a/telephony/java/android/telephony/PreciseDisconnectCause.java +++ b/telephony/java/android/telephony/PreciseDisconnectCause.java @@ -121,7 +121,7 @@ public final class PreciseDisconnectCause { public static final int BEARER_CAPABILITY_NOT_AUTHORIZED = 57; /** The requested bearer capability is not available at this time. */ public static final int BEARER_NOT_AVAIL = 58; - /** The service option is not availble at this time. */ + /** The service option is not available at this time. */ public static final int SERVICE_OPTION_NOT_AVAILABLE = 63; /** The equipment sending this cause does not support the bearer capability requested. */ public static final int BEARER_SERVICE_NOT_IMPLEMENTED = 65; diff --git a/telephony/java/android/telephony/RadioAccessFamily.java b/telephony/java/android/telephony/RadioAccessFamily.java index 90ddf2cd4730..f1e90118a559 100644 --- a/telephony/java/android/telephony/RadioAccessFamily.java +++ b/telephony/java/android/telephony/RadioAccessFamily.java @@ -92,7 +92,7 @@ public class RadioAccessFamily implements Parcelable { * {@link TelephonyManager.NetworkTypeBitMask}. It's a bit mask value to represent the support * type. */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public RadioAccessFamily(int phoneId, int radioAccessFamily) { mPhoneId = phoneId; mRadioAccessFamily = radioAccessFamily; @@ -103,7 +103,7 @@ public class RadioAccessFamily implements Parcelable { * * @return phone ID */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public int getPhoneId() { return mPhoneId; } @@ -113,7 +113,7 @@ public class RadioAccessFamily implements Parcelable { * * @return radio access family */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public @TelephonyManager.NetworkTypeBitMask int getRadioAccessFamily() { return mRadioAccessFamily; } @@ -168,7 +168,7 @@ public class RadioAccessFamily implements Parcelable { } }; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @TelephonyManager.NetworkTypeBitMask public static int getRafFromNetworkType(@PrefNetworkMode int type) { switch (type) { diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java index 9e2ba6875577..2d06062cfa44 100644 --- a/telephony/java/android/telephony/ServiceState.java +++ b/telephony/java/android/telephony/ServiceState.java @@ -564,6 +564,7 @@ public class ServiceState implements Parcelable { * @hide */ @UnsupportedAppUsage + @TestApi public int getDataRegState() { return mDataRegState; } @@ -956,7 +957,7 @@ public class ServiceState implements Parcelable { * * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public static String rilRadioTechnologyToString(int rt) { String rtString; @@ -1102,7 +1103,8 @@ public class ServiceState implements Parcelable { .append(", isUsingCarrierAggregation=").append(isUsingCarrierAggregation()) .append(", mLteEarfcnRsrpBoost=").append(mLteEarfcnRsrpBoost) .append(", mNetworkRegistrationInfos=").append(mNetworkRegistrationInfos) - .append(", mNrFrequencyRange=").append(mNrFrequencyRange) + .append(", mNrFrequencyRange=").append(Build.IS_DEBUGGABLE + ? mNrFrequencyRange : FREQUENCY_RANGE_UNKNOWN) .append(", mOperatorAlphaLongRaw=").append(mOperatorAlphaLongRaw) .append(", mOperatorAlphaShortRaw=").append(mOperatorAlphaShortRaw) .append(", mIsDataRoamingFromRegistration=") @@ -1167,7 +1169,7 @@ public class ServiceState implements Parcelable { } /** @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public void setVoiceRegState(int state) { mVoiceRegState = state; if (DBG) Rlog.d(LOG_TAG, "[ServiceState] setVoiceRegState=" + mVoiceRegState); @@ -1198,7 +1200,7 @@ public class ServiceState implements Parcelable { } /** @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public void setVoiceRoaming(boolean roaming) { setVoiceRoamingType(roaming ? ROAMING_TYPE_UNKNOWN : ROAMING_TYPE_NOT_ROAMING); } @@ -1219,7 +1221,7 @@ public class ServiceState implements Parcelable { } /** @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public void setDataRoaming(boolean dataRoaming) { setDataRoamingType(dataRoaming ? ROAMING_TYPE_UNKNOWN : ROAMING_TYPE_NOT_ROAMING); } @@ -1291,7 +1293,7 @@ public class ServiceState implements Parcelable { * * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public void setOperatorAlphaLong(@Nullable String longName) { mOperatorAlphaLong = longName; } @@ -1412,29 +1414,14 @@ public class ServiceState implements Parcelable { /** @hide */ public boolean isUsingCarrierAggregation() { - boolean isUsingCa = false; - NetworkRegistrationInfo nri = getNetworkRegistrationInfo( - NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN); - if (nri != null) { - DataSpecificRegistrationInfo dsri = nri.getDataSpecificInfo(); - if (dsri != null) { - isUsingCa = dsri.isUsingCarrierAggregation(); - } - } - return isUsingCa || getCellBandwidths().length > 1; - } + if (getCellBandwidths().length > 1) return true; - /** @hide */ - public void setIsUsingCarrierAggregation(boolean ca) { - NetworkRegistrationInfo nri = getNetworkRegistrationInfo( - NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN); - if (nri != null) { - DataSpecificRegistrationInfo dsri = nri.getDataSpecificInfo(); - if (dsri != null) { - dsri.setIsUsingCarrierAggregation(ca); - addNetworkRegistrationInfo(nri); + synchronized (mNetworkRegistrationInfos) { + for (NetworkRegistrationInfo nri : mNetworkRegistrationInfos) { + if (nri.isUsingCarrierAggregation()) return true; } } + return false; } /** @@ -1491,7 +1478,7 @@ public class ServiceState implements Parcelable { } /** @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public int getRilVoiceRadioTechnology() { NetworkRegistrationInfo wwanRegInfo = getNetworkRegistrationInfo( NetworkRegistrationInfo.DOMAIN_CS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN); @@ -1501,7 +1488,7 @@ public class ServiceState implements Parcelable { return RIL_RADIO_TECHNOLOGY_UNKNOWN; } /** @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public int getRilDataRadioTechnology() { return networkTypeToRilRadioTechnology(getDataNetworkType()); } @@ -1778,7 +1765,7 @@ public class ServiceState implements Parcelable { } /** @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public static boolean bitmaskHasTech(int bearerBitmask, int radioTech) { if (bearerBitmask == 0) { return true; @@ -1864,7 +1851,7 @@ public class ServiceState implements Parcelable { * voice SS. The voice SS is only used if it is IN_SERVICE (otherwise the base SS is returned). * @hide * */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public static ServiceState mergeServiceStates(ServiceState baseSs, ServiceState voiceSs) { if (voiceSs.mVoiceRegState != STATE_IN_SERVICE) { return baseSs; @@ -2125,4 +2112,23 @@ public class ServiceState implements Parcelable { } return false; } + + /** + * The frequency range is valid or not. + * + * @param frequencyRange The frequency range {@link FrequencyRange}. + * @return {@code true} if valid, {@code false} otherwise. + * + * @hide + */ + public static boolean isFrequencyRangeValid(int frequencyRange) { + if (frequencyRange == FREQUENCY_RANGE_LOW + || frequencyRange == FREQUENCY_RANGE_MID + || frequencyRange == FREQUENCY_RANGE_HIGH + || frequencyRange == FREQUENCY_RANGE_MMWAVE) { + return true; + } else { + return false; + } + } } diff --git a/telephony/java/android/telephony/SignalStrength.java b/telephony/java/android/telephony/SignalStrength.java index 1376cddbc41f..b317c5557108 100644 --- a/telephony/java/android/telephony/SignalStrength.java +++ b/telephony/java/android/telephony/SignalStrength.java @@ -187,6 +187,21 @@ public class SignalStrength implements Parcelable { new CellSignalStrengthNr(signalStrength.nr)); } + /** + * Constructor for Radio HAL V1.6. + * + * @param signalStrength signal strength reported from modem. + * @hide + */ + public SignalStrength(android.hardware.radio.V1_6.SignalStrength signalStrength) { + this(new CellSignalStrengthCdma(signalStrength.cdma, signalStrength.evdo), + new CellSignalStrengthGsm(signalStrength.gsm), + new CellSignalStrengthWcdma(signalStrength.wcdma), + new CellSignalStrengthTdscdma(signalStrength.tdscdma), + new CellSignalStrengthLte(signalStrength.lte), + new CellSignalStrengthNr(signalStrength.nr)); + } + private CellSignalStrength getPrimary() { // This behavior is intended to replicate the legacy behavior of getLevel() by prioritizing // newer faster RATs for default/for display purposes. @@ -287,11 +302,9 @@ public class SignalStrength implements Parcelable { } /** - * Copy constructors + * This constructor is used to create a copy of an existing SignalStrength object. * * @param s Source SignalStrength - * - * @hide */ public SignalStrength(@NonNull SignalStrength s) { copyFrom(s); diff --git a/telephony/java/android/telephony/SignalStrengthUpdateRequest.aidl b/telephony/java/android/telephony/SignalStrengthUpdateRequest.aidl new file mode 100644 index 000000000000..a45de2e58dd1 --- /dev/null +++ b/telephony/java/android/telephony/SignalStrengthUpdateRequest.aidl @@ -0,0 +1,20 @@ +/* +** +** Copyright (C) 2020 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; + +parcelable SignalStrengthUpdateRequest; diff --git a/telephony/java/android/telephony/SignalStrengthUpdateRequest.java b/telephony/java/android/telephony/SignalStrengthUpdateRequest.java new file mode 100644 index 000000000000..fe7e5976b132 --- /dev/null +++ b/telephony/java/android/telephony/SignalStrengthUpdateRequest.java @@ -0,0 +1,272 @@ +/* + * Copyright (C) 2020 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.NonNull; +import android.os.Binder; +import android.os.IBinder; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashSet; +import java.util.List; +import java.util.Objects; +import java.util.Set; + +/** + * Request used to register {@link SignalThresholdInfo} to be notified when the signal strength + * breach the specified thresholds. + */ +public final class SignalStrengthUpdateRequest implements Parcelable { + /** + * List of SignalThresholdInfo for the request. + */ + private final List<SignalThresholdInfo> mSignalThresholdInfos; + + /** + * Whether the reporting is required for thresholds in the request while device is idle. + */ + private final boolean mIsReportingRequestedWhileIdle; + + /** + * Whether the reporting requested for system thresholds while device is idle. + * + * System signal thresholds are loaded from carrier config items and mainly used for UI + * displaying. By default, they are ignored when device is idle. When setting the value to true, + * modem will continue reporting signal strength changes over the system signal thresholds even + * device is idle. + * + * This should only set to true by the system caller. + */ + private final boolean mIsSystemThresholdReportingRequestedWhileIdle; + + /** + * A IBinder object as a token for server side to check if the request client is still living. + */ + private final IBinder mLiveToken; + + private SignalStrengthUpdateRequest( + @NonNull List<SignalThresholdInfo> signalThresholdInfos, + boolean isReportingRequestedWhileIdle, + boolean isSystemThresholdReportingRequestedWhileIdle) { + validate(signalThresholdInfos); + + mSignalThresholdInfos = signalThresholdInfos; + mIsReportingRequestedWhileIdle = isReportingRequestedWhileIdle; + mIsSystemThresholdReportingRequestedWhileIdle = + isSystemThresholdReportingRequestedWhileIdle; + mLiveToken = new Binder(); + } + + /** + * Builder class to create {@link SignalStrengthUpdateRequest} object. + */ + public static final class Builder { + private List<SignalThresholdInfo> mSignalThresholdInfos = null; + private boolean mIsReportingRequestedWhileIdle = false; + private boolean mIsSystemThresholdReportingRequestedWhileIdle = false; + + /** + * Set the collection of SignalThresholdInfo for the builder object + * + * @param signalThresholdInfos the collection of SignalThresholdInfo + * + * @return the builder to facilitate the chaining + */ + public @NonNull Builder setSignalThresholdInfos( + @NonNull Collection<SignalThresholdInfo> signalThresholdInfos) { + Objects.requireNonNull(signalThresholdInfos, + "SignalThresholdInfo collection must not be null"); + for (SignalThresholdInfo info : signalThresholdInfos) { + Objects.requireNonNull(info, + "SignalThresholdInfo in the collection must not be null"); + } + + mSignalThresholdInfos = new ArrayList<>(signalThresholdInfos); + // Sort the collection with RAN ascending order, make the ordering not matter for equals + mSignalThresholdInfos.sort( + Comparator.comparingInt(SignalThresholdInfo::getRadioAccessNetworkType)); + return this; + } + + /** + * Set the builder object if require reporting on thresholds in this request when device is + * idle. + * + * @param isReportingRequestedWhileIdle true if request reporting when device is idle + * + * @return the builder to facilitate the chaining + */ + public @NonNull Builder setReportingRequestedWhileIdle( + boolean isReportingRequestedWhileIdle) { + mIsReportingRequestedWhileIdle = isReportingRequestedWhileIdle; + return this; + } + + /** + * Set the builder object if require reporting on the system thresholds when device is idle. + * + * This can only used by the system caller. + * + * @param isSystemThresholdReportingRequestedWhileIdle true if request reporting on the + * system thresholds when device is idle + * @return the builder to facilitate the chaining + * @hide + */ + public @NonNull Builder setSystemThresholdReportingRequestedWhileIdle( + boolean isSystemThresholdReportingRequestedWhileIdle) { + mIsSystemThresholdReportingRequestedWhileIdle = + isSystemThresholdReportingRequestedWhileIdle; + return this; + } + + /** + * Build a {@link SignalStrengthUpdateRequest} object. + * + * @return the SignalStrengthUpdateRequest object + * + * @throws IllegalArgumentException if the SignalThresholdInfo collection is empty size, the + * radio access network type in the collection is not unique + */ + public @NonNull SignalStrengthUpdateRequest build() { + return new SignalStrengthUpdateRequest(mSignalThresholdInfos, + mIsReportingRequestedWhileIdle, mIsSystemThresholdReportingRequestedWhileIdle); + } + } + + private SignalStrengthUpdateRequest(Parcel in) { + mSignalThresholdInfos = in.createTypedArrayList(SignalThresholdInfo.CREATOR); + mIsReportingRequestedWhileIdle = in.readBoolean(); + mIsSystemThresholdReportingRequestedWhileIdle = in.readBoolean(); + mLiveToken = in.readStrongBinder(); + } + + /** + * Get the collection of SignalThresholdInfo in the request. + * + * @return the collection of SignalThresholdInfo + */ + @NonNull + public Collection<SignalThresholdInfo> getSignalThresholdInfos() { + return Collections.unmodifiableList(mSignalThresholdInfos); + } + + /** + * Get whether reporting is requested for the threshold in the request while device is idle. + * + * @return true if reporting requested while device is idle + */ + public boolean isReportingRequestedWhileIdle() { + return mIsReportingRequestedWhileIdle; + } + + /** + * @return true if reporting requested for system thresholds while device is idle + * + * @hide + */ + public boolean isSystemThresholdReportingRequestedWhileIdle() { + return mIsSystemThresholdReportingRequestedWhileIdle; + } + + /** + * @return the live token of the request + * + * @hide + */ + public @NonNull IBinder getLiveToken() { + return mLiveToken; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeTypedList(mSignalThresholdInfos); + dest.writeBoolean(mIsReportingRequestedWhileIdle); + dest.writeBoolean(mIsSystemThresholdReportingRequestedWhileIdle); + dest.writeStrongBinder(mLiveToken); + } + + @Override + public boolean equals(Object other) { + if (this == other) return true; + + if (!(other instanceof SignalStrengthUpdateRequest)) { + return false; + } + + SignalStrengthUpdateRequest request = (SignalStrengthUpdateRequest) other; + return mSignalThresholdInfos.equals(request.mSignalThresholdInfos) + && mIsReportingRequestedWhileIdle == request.mIsReportingRequestedWhileIdle + && mIsSystemThresholdReportingRequestedWhileIdle + == request.mIsSystemThresholdReportingRequestedWhileIdle; + } + + @Override + public int hashCode() { + return Objects.hash(mSignalThresholdInfos, mIsReportingRequestedWhileIdle, + mIsSystemThresholdReportingRequestedWhileIdle); + } + + public static final @NonNull Parcelable.Creator<SignalStrengthUpdateRequest> CREATOR = + new Parcelable.Creator<SignalStrengthUpdateRequest>() { + @Override + public SignalStrengthUpdateRequest createFromParcel(Parcel source) { + return new SignalStrengthUpdateRequest(source); + } + + @Override + public SignalStrengthUpdateRequest[] newArray(int size) { + return new SignalStrengthUpdateRequest[size]; + } + }; + + @Override + public String toString() { + return new StringBuilder("SignalStrengthUpdateRequest{") + .append("mSignalThresholdInfos=") + .append(mSignalThresholdInfos) + .append(" mIsReportingRequestedWhileIdle=") + .append(mIsReportingRequestedWhileIdle) + .append(" mIsSystemThresholdReportingRequestedWhileIdle=") + .append(mIsSystemThresholdReportingRequestedWhileIdle) + .append(" mLiveToken") + .append(mLiveToken) + .append("}").toString(); + } + + /** + * Throw IAE when the RAN in the collection is not unique. + */ + private static void validate(Collection<SignalThresholdInfo> infos) { + Set<Integer> uniqueRan = new HashSet<>(infos.size()); + for (SignalThresholdInfo info : infos) { + final int ran = info.getRadioAccessNetworkType(); + if (!uniqueRan.add(ran)) { + throw new IllegalArgumentException("RAN: " + ran + " is not unique"); + } + } + } +} diff --git a/telephony/java/android/telephony/SignalThresholdInfo.java b/telephony/java/android/telephony/SignalThresholdInfo.java index f6f6d75c37c6..ae7d20929d58 100644 --- a/telephony/java/android/telephony/SignalThresholdInfo.java +++ b/telephony/java/android/telephony/SignalThresholdInfo.java @@ -28,101 +28,109 @@ import java.util.Objects; /** * Defines the threshold value of the signal strength. - * @hide */ -public class SignalThresholdInfo implements Parcelable { +public final class SignalThresholdInfo implements Parcelable { + + /** + * Unknown signal measurement type. + */ + public static final int SIGNAL_MEASUREMENT_TYPE_UNKNOWN = 0; + /** * Received Signal Strength Indication. * Range: -113 dBm and -51 dBm - * Used RAN: GERAN, CDMA2000 + * Used RAN: {@link AccessNetworkConstants.AccessNetworkType#GERAN}, + * {@link AccessNetworkConstants.AccessNetworkType#CDMA2000} * Reference: 3GPP TS 27.007 section 8.5. */ - public static final int SIGNAL_RSSI = 1; + public static final int SIGNAL_MEASUREMENT_TYPE_RSSI = 1; /** * Received Signal Code Power. * Range: -120 dBm to -25 dBm; - * Used RAN: UTRAN + * Used RAN: {@link AccessNetworkConstants.AccessNetworkType#UTRAN} * Reference: 3GPP TS 25.123, section 9.1.1.1 */ - public static final int SIGNAL_RSCP = 2; + public static final int SIGNAL_MEASUREMENT_TYPE_RSCP = 2; /** * Reference Signal Received Power. * Range: -140 dBm to -44 dBm; - * Used RAN: EUTRAN + * Used RAN: {@link AccessNetworkConstants.AccessNetworkType#EUTRAN} * Reference: 3GPP TS 36.133 9.1.4 */ - public static final int SIGNAL_RSRP = 3; + public static final int SIGNAL_MEASUREMENT_TYPE_RSRP = 3; /** * Reference Signal Received Quality - * Range: -20 dB to -3 dB; - * Used RAN: EUTRAN + * Range: -34 dB to 3 dB; + * Used RAN: {@link AccessNetworkConstants.AccessNetworkType#EUTRAN} * Reference: 3GPP TS 36.133 9.1.7 */ - public static final int SIGNAL_RSRQ = 4; + public static final int SIGNAL_MEASUREMENT_TYPE_RSRQ = 4; /** * Reference Signal Signal to Noise Ratio * Range: -20 dB to 30 dB; - * Used RAN: EUTRAN + * Used RAN: {@link AccessNetworkConstants.AccessNetworkType#EUTRAN} */ - public static final int SIGNAL_RSSNR = 5; + public static final int SIGNAL_MEASUREMENT_TYPE_RSSNR = 5; /** * 5G SS reference signal received power. * Range: -140 dBm to -44 dBm. - * Used RAN: NGRAN + * Used RAN: {@link AccessNetworkConstants.AccessNetworkType#NGRAN} * Reference: 3GPP TS 38.215. */ - public static final int SIGNAL_SSRSRP = 6; + public static final int SIGNAL_MEASUREMENT_TYPE_SSRSRP = 6; /** * 5G SS reference signal received quality. - * Range: -20 dB to -3 dB. - * Used RAN: NGRAN - * Reference: 3GPP TS 38.215. + * Range: -43 dB to 20 dB. + * Used RAN: {@link AccessNetworkConstants.AccessNetworkType#NGRAN} + * Reference: 3GPP TS 38.133 section 10.1.11.1. */ - public static final int SIGNAL_SSRSRQ = 7; + public static final int SIGNAL_MEASUREMENT_TYPE_SSRSRQ = 7; /** * 5G SS signal-to-noise and interference ratio. * Range: -23 dB to 40 dB - * Used RAN: NGRAN + * Used RAN: {@link AccessNetworkConstants.AccessNetworkType#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; + public static final int SIGNAL_MEASUREMENT_TYPE_SSSINR = 8; /** @hide */ - @IntDef(prefix = { "SIGNAL_" }, value = { - SIGNAL_RSSI, - SIGNAL_RSCP, - SIGNAL_RSRP, - SIGNAL_RSRQ, - SIGNAL_RSSNR, - SIGNAL_SSRSRP, - SIGNAL_SSRSRQ, - SIGNAL_SSSINR + @IntDef(prefix = {"SIGNAL_MEASUREMENT_TYPE_"}, value = { + SIGNAL_MEASUREMENT_TYPE_UNKNOWN, + SIGNAL_MEASUREMENT_TYPE_RSSI, + SIGNAL_MEASUREMENT_TYPE_RSCP, + SIGNAL_MEASUREMENT_TYPE_RSRP, + SIGNAL_MEASUREMENT_TYPE_RSRQ, + SIGNAL_MEASUREMENT_TYPE_RSSNR, + SIGNAL_MEASUREMENT_TYPE_SSRSRP, + SIGNAL_MEASUREMENT_TYPE_SSRSRQ, + SIGNAL_MEASUREMENT_TYPE_SSSINR }) @Retention(RetentionPolicy.SOURCE) - public @interface SignalMeasurementType {} + public @interface SignalMeasurementType { + } @SignalMeasurementType - private int mSignalMeasurement; + private final int mSignalMeasurementType; /** * A hysteresis time in milliseconds to prevent flapping. * A value of 0 disables hysteresis */ - private int mHysteresisMs; + private final 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; + private final int mHysteresisDb; /** * List of threshold values. @@ -130,60 +138,397 @@ public class SignalThresholdInfo implements Parcelable { * The threshold values for which to apply criteria. * A vector size of 0 disables the use of thresholds for reporting. */ - private int[] mThresholds = null; + private final int[] mThresholds; /** * {@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; + private final boolean mIsEnabled; + + /** + * The radio access network type associated with the signal thresholds. + */ + @AccessNetworkConstants.RadioAccessNetworkType + private final int mRan; /** * Indicates the hysteresisMs is disabled. + * + * @hide */ public static final int HYSTERESIS_MS_DISABLED = 0; /** * Indicates the hysteresisDb is disabled. + * + * @hide */ public static final int HYSTERESIS_DB_DISABLED = 0; + + /** + * Minimum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_RSSI}. + * + * @hide + */ + public static final int SIGNAL_RSSI_MIN_VALUE = -113; + + /** + * Maximum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_RSSI}. + * + * @hide + */ + public static final int SIGNAL_RSSI_MAX_VALUE = -51; + + /** + * Minimum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_RSCP}. + * + * @hide + */ + public static final int SIGNAL_RSCP_MIN_VALUE = -120; + + /** + * Maximum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_RSCP}. + * + * @hide + */ + public static final int SIGNAL_RSCP_MAX_VALUE = -25; + + /** + * Minimum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_RSRP}. + * + * @hide + */ + public static final int SIGNAL_RSRP_MIN_VALUE = -140; + + /** + * Maximum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_RSRP}. + * + * @hide + */ + public static final int SIGNAL_RSRP_MAX_VALUE = -44; + + /** + * Minimum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_RSRQ}. + * + * @hide + */ + public static final int SIGNAL_RSRQ_MIN_VALUE = -34; + + /** + * Maximum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_RSRQ}. + * + * @hide + */ + public static final int SIGNAL_RSRQ_MAX_VALUE = 3; + + /** + * Minimum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_RSSNR}. + * + * @hide + */ + public static final int SIGNAL_RSSNR_MIN_VALUE = -20; + + /** + * Maximum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_RSSNR}. + * + * @hide + */ + public static final int SIGNAL_RSSNR_MAX_VALUE = 30; + + /** + * Minimum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_SSRSRP}. + * + * @hide + */ + public static final int SIGNAL_SSRSRP_MIN_VALUE = -140; + + /** + * Maximum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_SSRSRP}. + * + * @hide + */ + public static final int SIGNAL_SSRSRP_MAX_VALUE = -44; + + /** + * Minimum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_SSRSRQ}. + * + * @hide + */ + public static final int SIGNAL_SSRSRQ_MIN_VALUE = -43; + + /** + * Maximum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_SSRSRQ}. + * + * @hide + */ + public static final int SIGNAL_SSRSRQ_MAX_VALUE = 20; + + /** + * Minimum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_SSSINR}. + * + * @hide + */ + public static final int SIGNAL_SSSINR_MIN_VALUE = -23; + + /** + * Maximum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_SSSINR}. + * + * @hide + */ + public static final int SIGNAL_SSSINR_MAX_VALUE = 40; + + /** + * The minimum number of thresholds allowed in each SignalThresholdInfo. + * + * @hide + */ + public static final int MINIMUM_NUMBER_OF_THRESHOLDS_ALLOWED = 1; + + /** + * The maximum number of thresholds allowed in each SignalThresholdInfo. + * + * @hide + */ + public static final int MAXIMUM_NUMBER_OF_THRESHOLDS_ALLOWED = 4; + /** * 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; + * @param ran Radio Access Network type + * @param signalMeasurementType Signal Measurement Type + * @param hysteresisMs hysteresisMs + * @param hysteresisDb hysteresisDb + * @param thresholds threshold value + * @param isEnabled isEnabled + */ + private SignalThresholdInfo(@AccessNetworkConstants.RadioAccessNetworkType int ran, + @SignalMeasurementType int signalMeasurementType, int hysteresisMs, int hysteresisDb, + @NonNull int[] thresholds, boolean isEnabled) { + Objects.requireNonNull(thresholds, "thresholds must not be null"); + validateRanWithMeasurementType(ran, signalMeasurementType); + validateThresholdRange(signalMeasurementType, thresholds); + + mRan = ran; + mSignalMeasurementType = signalMeasurementType; mHysteresisMs = hysteresisMs < 0 ? HYSTERESIS_MS_DISABLED : hysteresisMs; mHysteresisDb = hysteresisDb < 0 ? HYSTERESIS_DB_DISABLED : hysteresisDb; - mThresholds = thresholds == null ? null : thresholds.clone(); + mThresholds = thresholds; mIsEnabled = isEnabled; } - public @SignalMeasurementType int getSignalMeasurement() { - return mSignalMeasurement; + /** + * Builder class to create {@link SignalThresholdInfo} objects. + */ + public static final class Builder { + private int mRan = AccessNetworkConstants.AccessNetworkType.UNKNOWN; + private int mSignalMeasurementType = SIGNAL_MEASUREMENT_TYPE_UNKNOWN; + private int mHysteresisMs = HYSTERESIS_MS_DISABLED; + private int mHysteresisDb = HYSTERESIS_DB_DISABLED; + private int[] mThresholds = null; + private boolean mIsEnabled = false; + + /** + * Set the radio access network type for the builder instance. + * + * @param ran The radio access network type + * @return the builder to facilitate the chaining + */ + public @NonNull Builder setRadioAccessNetworkType( + @AccessNetworkConstants.RadioAccessNetworkType int ran) { + mRan = ran; + return this; + } + + /** + * Set the signal measurement type for the builder instance. + * + * @param signalMeasurementType The signal measurement type + * @return the builder to facilitate the chaining + */ + public @NonNull Builder setSignalMeasurementType( + @SignalMeasurementType int signalMeasurementType) { + mSignalMeasurementType = signalMeasurementType; + return this; + } + + /** + * Set the hysteresis time in milliseconds to prevent flapping. A value of 0 disables + * hysteresis. + * + * @param hysteresisMs the hysteresis time in milliseconds + * @return the builder to facilitate the chaining + * @hide + */ + public @NonNull Builder setHysteresisMs(int hysteresisMs) { + mHysteresisMs = hysteresisMs; + return this; + } + + /** + * Set the interval in dB defining the required magnitude change between reports. A value of + * zero disabled dB-based hysteresis restrictions. + * + * @param hysteresisDb the interval in dB + * @return the builder to facilitate the chaining + * @hide + */ + public @NonNull Builder setHysteresisDb(int hysteresisDb) { + mHysteresisDb = hysteresisDb; + return this; + } + + /** + * Set the signal strength thresholds of the corresponding signal measurement type. + * + * The range and unit must reference specific SignalMeasurementType. The length of the + * thresholds should between the numbers return from + * {@link #getMinimumNumberOfThresholdsAllowed()} and + * {@link #getMaximumNumberOfThresholdsAllowed()}. An IllegalArgumentException will throw + * otherwise. + * + * @param thresholds array of integer as the signal threshold values + * @return the builder to facilitate the chaining + * + * @see #SIGNAL_MEASUREMENT_TYPE_RSSI + * @see #SIGNAL_MEASUREMENT_TYPE_RSCP + * @see #SIGNAL_MEASUREMENT_TYPE_RSRP + * @see #SIGNAL_MEASUREMENT_TYPE_RSRQ + * @see #SIGNAL_MEASUREMENT_TYPE_RSSNR + * @see #SIGNAL_MEASUREMENT_TYPE_SSRSRP + * @see #SIGNAL_MEASUREMENT_TYPE_SSRSRQ + * @see #SIGNAL_MEASUREMENT_TYPE_SSSINR + * @see #getThresholds() for more details on signal strength thresholds + */ + public @NonNull Builder setThresholds(@NonNull int[] thresholds) { + return setThresholds(thresholds, false /*isSystem*/); + } + + /** + * Set the signal strength thresholds for the corresponding signal measurement type. + * + * @param thresholds array of integer as the signal threshold values + * @param isSystem true is the caller is system which does not have restrictions on + * the length of thresholds array. + * @return the builder to facilitate the chaining + * + * @hide + */ + public @NonNull Builder setThresholds(@NonNull int[] thresholds, boolean isSystem) { + Objects.requireNonNull(thresholds, "thresholds must not be null"); + if (!isSystem && (thresholds.length < MINIMUM_NUMBER_OF_THRESHOLDS_ALLOWED + || thresholds.length > MAXIMUM_NUMBER_OF_THRESHOLDS_ALLOWED)) { + throw new IllegalArgumentException( + "thresholds length must between " + MINIMUM_NUMBER_OF_THRESHOLDS_ALLOWED + + " and " + MAXIMUM_NUMBER_OF_THRESHOLDS_ALLOWED); + } + mThresholds = thresholds.clone(); + Arrays.sort(mThresholds); + return this; + } + + + /** + * Set if the modem should trigger the report based on the criteria. + * + * @param isEnabled true if the modem should trigger the report based on the criteria + * @return the builder to facilitate the chaining + * @hide + */ + public @NonNull Builder setIsEnabled(boolean isEnabled) { + mIsEnabled = isEnabled; + return this; + } + + /** + * Build {@link SignalThresholdInfo} object. + * + * @return the SignalThresholdInfo object build out + * + * @throws IllegalArgumentException if the signal measurement type is invalid, any value in + * the thresholds is out of range, or the RAN is not allowed to set with the signal + * measurement type + */ + public @NonNull SignalThresholdInfo build() { + return new SignalThresholdInfo(mRan, mSignalMeasurementType, mHysteresisMs, + mHysteresisDb, mThresholds, mIsEnabled); + } + } + + /** + * Get the radio access network type. + * + * @return radio access network type + */ + public @AccessNetworkConstants.RadioAccessNetworkType int getRadioAccessNetworkType() { + return mRan; + } + + /** + * Get the signal measurement type. + * + * @return the SignalMeasurementType value + */ + public @SignalMeasurementType int getSignalMeasurementType() { + return mSignalMeasurementType; } + /** @hide */ public int getHysteresisMs() { return mHysteresisMs; } + /** @hide */ public int getHysteresisDb() { return mHysteresisDb; } + /** @hide */ public boolean isEnabled() { return mIsEnabled; } - public int[] getThresholds() { - return mThresholds == null ? null : mThresholds.clone(); + /** + * Get the signal strength thresholds. + * + * Signal strength thresholds are a list of integer used for suggesting signal level and signal + * reporting criteria. The range and unit must reference specific SignalMeasurementType. + * + * Please refer to https://source.android.com/devices/tech/connect/signal-strength on how signal + * strength thresholds are used for signal strength reporting. + * + * @return array of integer of the signal thresholds + * + * @see #SIGNAL_MEASUREMENT_TYPE_RSSI + * @see #SIGNAL_MEASUREMENT_TYPE_RSCP + * @see #SIGNAL_MEASUREMENT_TYPE_RSRP + * @see #SIGNAL_MEASUREMENT_TYPE_RSRQ + * @see #SIGNAL_MEASUREMENT_TYPE_RSSNR + * @see #SIGNAL_MEASUREMENT_TYPE_SSRSRP + * @see #SIGNAL_MEASUREMENT_TYPE_SSRSRQ + * @see #SIGNAL_MEASUREMENT_TYPE_SSSINR + */ + public @NonNull int[] getThresholds() { + return mThresholds.clone(); + } + + /** + * Get the minimum number of thresholds allowed in each SignalThresholdInfo. + * + * @return the minimum number of thresholds allowed + */ + public static int getMinimumNumberOfThresholdsAllowed() { + return MINIMUM_NUMBER_OF_THRESHOLDS_ALLOWED; + } + + /** + * Get the maximum number of threshold allowed in each SignalThresholdInfo. + * + * @return the maximum number of thresholds allowed + */ + public static int getMaximumNumberOfThresholdsAllowed() { + return MAXIMUM_NUMBER_OF_THRESHOLDS_ALLOWED; } @Override @@ -192,8 +537,9 @@ public class SignalThresholdInfo implements Parcelable { } @Override - public void writeToParcel(Parcel out, int flags) { - out.writeInt(mSignalMeasurement); + public void writeToParcel(@NonNull Parcel out, int flags) { + out.writeInt(mRan); + out.writeInt(mSignalMeasurementType); out.writeInt(mHysteresisMs); out.writeInt(mHysteresisDb); out.writeIntArray(mThresholds); @@ -201,7 +547,8 @@ public class SignalThresholdInfo implements Parcelable { } private SignalThresholdInfo(Parcel in) { - mSignalMeasurement = in.readInt(); + mRan = in.readInt(); + mSignalMeasurementType = in.readInt(); mHysteresisMs = in.readInt(); mHysteresisDb = in.readInt(); mThresholds = in.createIntArray(); @@ -217,7 +564,8 @@ public class SignalThresholdInfo implements Parcelable { } SignalThresholdInfo other = (SignalThresholdInfo) o; - return mSignalMeasurement == other.mSignalMeasurement + return mRan == other.mRan + && mSignalMeasurementType == other.mSignalMeasurementType && mHysteresisMs == other.mHysteresisMs && mHysteresisDb == other.mHysteresisDb && Arrays.equals(mThresholds, other.mThresholds) @@ -226,8 +574,8 @@ public class SignalThresholdInfo implements Parcelable { @Override public int hashCode() { - return Objects.hash( - mSignalMeasurement, mHysteresisMs, mHysteresisDb, mThresholds, mIsEnabled); + return Objects.hash(mRan, mSignalMeasurementType, mHysteresisMs, mHysteresisDb, mThresholds, + mIsEnabled); } public static final @NonNull Parcelable.Creator<SignalThresholdInfo> CREATOR = @@ -246,11 +594,83 @@ public class SignalThresholdInfo implements Parcelable { @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(); + .append("mRan=").append(mRan) + .append(" mSignalMeasurementType=").append(mSignalMeasurementType) + .append(" mHysteresisMs=").append(mHysteresisMs) + .append(" mHysteresisDb=").append(mHysteresisDb) + .append(" mThresholds=").append(Arrays.toString(mThresholds)) + .append(" mIsEnabled=").append(mIsEnabled) + .append("}").toString(); + } + + /** + * Return true if signal measurement type is valid and the threshold value is in range. + */ + private static boolean isValidThreshold(@SignalMeasurementType int type, int threshold) { + switch (type) { + case SIGNAL_MEASUREMENT_TYPE_RSSI: + return threshold >= SIGNAL_RSSI_MIN_VALUE && threshold <= SIGNAL_RSSI_MAX_VALUE; + case SIGNAL_MEASUREMENT_TYPE_RSCP: + return threshold >= SIGNAL_RSCP_MIN_VALUE && threshold <= SIGNAL_RSCP_MAX_VALUE; + case SIGNAL_MEASUREMENT_TYPE_RSRP: + return threshold >= SIGNAL_RSRP_MIN_VALUE && threshold <= SIGNAL_RSRP_MAX_VALUE; + case SIGNAL_MEASUREMENT_TYPE_RSRQ: + return threshold >= SIGNAL_RSRQ_MIN_VALUE && threshold <= SIGNAL_RSRQ_MAX_VALUE; + case SIGNAL_MEASUREMENT_TYPE_RSSNR: + return threshold >= SIGNAL_RSSNR_MIN_VALUE && threshold <= SIGNAL_RSSNR_MAX_VALUE; + case SIGNAL_MEASUREMENT_TYPE_SSRSRP: + return threshold >= SIGNAL_SSRSRP_MIN_VALUE && threshold <= SIGNAL_SSRSRP_MAX_VALUE; + case SIGNAL_MEASUREMENT_TYPE_SSRSRQ: + return threshold >= SIGNAL_SSRSRQ_MIN_VALUE && threshold <= SIGNAL_SSRSRQ_MAX_VALUE; + case SIGNAL_MEASUREMENT_TYPE_SSSINR: + return threshold >= SIGNAL_SSSINR_MIN_VALUE && threshold <= SIGNAL_SSSINR_MAX_VALUE; + default: + return false; + } + } + + /** + * Return true if the radio access type is allowed to set with the measurement type. + */ + private static boolean isValidRanWithMeasurementType( + @AccessNetworkConstants.RadioAccessNetworkType int ran, + @SignalMeasurementType int type) { + switch (type) { + case SIGNAL_MEASUREMENT_TYPE_RSSI: + return ran == AccessNetworkConstants.AccessNetworkType.GERAN + || ran == AccessNetworkConstants.AccessNetworkType.CDMA2000; + case SIGNAL_MEASUREMENT_TYPE_RSCP: + return ran == AccessNetworkConstants.AccessNetworkType.UTRAN; + case SIGNAL_MEASUREMENT_TYPE_RSRP: + case SIGNAL_MEASUREMENT_TYPE_RSRQ: + case SIGNAL_MEASUREMENT_TYPE_RSSNR: + return ran == AccessNetworkConstants.AccessNetworkType.EUTRAN; + case SIGNAL_MEASUREMENT_TYPE_SSRSRP: + case SIGNAL_MEASUREMENT_TYPE_SSRSRQ: + case SIGNAL_MEASUREMENT_TYPE_SSSINR: + return ran == AccessNetworkConstants.AccessNetworkType.NGRAN; + default: + return false; + } + } + + private void validateRanWithMeasurementType( + @AccessNetworkConstants.RadioAccessNetworkType int ran, + @SignalMeasurementType int signalMeasurement) { + if (!isValidRanWithMeasurementType(ran, signalMeasurement)) { + throw new IllegalArgumentException( + "invalid RAN: " + ran + " with signal measurement type: " + signalMeasurement); + } + } + + private void validateThresholdRange(@SignalMeasurementType int signalMeasurement, + int[] thresholds) { + for (int threshold : thresholds) { + if (!isValidThreshold(signalMeasurement, threshold)) { + throw new IllegalArgumentException( + "invalid signal measurement type: " + signalMeasurement + + " with threshold: " + threshold); + } + } } } diff --git a/telephony/java/android/telephony/SmsCbEtwsInfo.java b/telephony/java/android/telephony/SmsCbEtwsInfo.java index 2a7f7ad81e3b..a98916d715e4 100644 --- a/telephony/java/android/telephony/SmsCbEtwsInfo.java +++ b/telephony/java/android/telephony/SmsCbEtwsInfo.java @@ -27,6 +27,7 @@ import com.android.internal.telephony.uicc.IccUtils; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.time.DateTimeException; import java.time.LocalDateTime; import java.time.ZoneOffset; import java.util.Arrays; @@ -173,7 +174,7 @@ public final class SmsCbEtwsInfo implements Parcelable { /** * Returns the Warning-Security-Information timestamp (GSM primary notifications only). * As of Release 10, 3GPP TS 23.041 states that the UE shall ignore this value if received. - * @return a UTC timestamp in System.currentTimeMillis() format, or 0 if not present + * @return a UTC timestamp in System.currentTimeMillis() format, or 0 if not present or invalid. */ public long getPrimaryNotificationTimestamp() { if (mWarningSecurityInformation == null || mWarningSecurityInformation.length < 7) { @@ -201,18 +202,23 @@ public final class SmsCbEtwsInfo implements Parcelable { // timezoneOffset is in quarter hours. int timeZoneOffsetSeconds = timezoneOffset * 15 * 60; - LocalDateTime localDateTime = LocalDateTime.of( - // We only need to support years above 2000. - year + 2000, - month /* 1-12 */, - day, - hour, - minute, - second); - - long epochSeconds = localDateTime.toEpochSecond(ZoneOffset.UTC) - timeZoneOffsetSeconds; - // Convert to milliseconds, ignore overflow. - return epochSeconds * 1000; + try { + LocalDateTime localDateTime = LocalDateTime.of( + // We only need to support years above 2000. + year + 2000, + month /* 1-12 */, + day, + hour, + minute, + second); + + long epochSeconds = localDateTime.toEpochSecond(ZoneOffset.UTC) - timeZoneOffsetSeconds; + // Convert to milliseconds, ignore overflow. + return epochSeconds * 1000; + } catch (DateTimeException ex) { + // No-op + } + return 0; } /** diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java index b376660f839e..5171cf9dcea7 100644 --- a/telephony/java/android/telephony/SmsManager.java +++ b/telephony/java/android/telephony/SmsManager.java @@ -19,6 +19,7 @@ package android.telephony; import android.Manifest; import android.annotation.CallbackExecutor; import android.annotation.IntDef; +import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; @@ -39,11 +40,14 @@ import android.os.RemoteException; import android.text.TextUtils; import android.util.ArrayMap; import android.util.Log; +import android.util.Pair; +import com.android.internal.annotations.GuardedBy; import com.android.internal.telephony.IIntegerConsumer; import com.android.internal.telephony.ISms; import com.android.internal.telephony.ITelephony; import com.android.internal.telephony.SmsRawData; +import com.android.telephony.Rlog; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -74,11 +78,16 @@ import java.util.concurrent.Executor; public final class SmsManager { private static final String TAG = "SmsManager"; - /** Singleton object constructed during class initialization. */ - private static final SmsManager sInstance = new SmsManager( - SubscriptionManager.DEFAULT_SUBSCRIPTION_ID); private static final Object sLockObject = new Object(); + @GuardedBy("sLockObject") + private static final Map<Pair<Context, Integer>, SmsManager> sSubInstances = + new ArrayMap<>(); + + /** Singleton object constructed during class initialization. */ + private static final SmsManager DEFAULT_INSTANCE = getSmsManagerForContextAndSubscriptionId( + null, SubscriptionManager.DEFAULT_SUBSCRIPTION_ID); + /** SMS record length from TS 51.011 10.5.3 * @hide */ @@ -89,13 +98,16 @@ public final class SmsManager { */ public static final int CDMA_SMS_RECORD_LENGTH = 255; - private static final Map<Integer, SmsManager> sSubInstances = - new ArrayMap<Integer, SmsManager>(); - /** A concrete subscription id, or the pseudo DEFAULT_SUBSCRIPTION_ID */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) private int mSubId; + /** + * Context this SmsManager is for. Can be {@code null} in the case the manager was created via + * legacy APIs + */ + private final @Nullable Context mContext; + /* * Key for the various carrier-dependent configuration values. * Some of the values are used by the system in processing SMS or MMS messages. Others @@ -325,6 +337,34 @@ public final class SmsManager { } /** + * Get {@link Context#getOpPackageName()} if this manager has a context, otherwise a dummy + * value. + * + * @return The package name to be used for app-ops checks + */ + private @Nullable String getOpPackageName() { + if (mContext == null) { + return null; + } else { + return mContext.getOpPackageName(); + } + } + + /** + * Get {@link Context#getAttributionTag()} ()} if this manager has a context, otherwise get the + * default attribution tag. + * + * @return The attribution tag to be used for app-ops checks + */ + private @Nullable String getAttributionTag() { + if (mContext == null) { + return null; + } else { + return mContext.getAttributionTag(); + } + } + + /** * Send a text based SMS. * * <p class="note"><strong>Note:</strong> Using this method requires that your app has the @@ -345,7 +385,6 @@ public final class SmsManager { * where this operation may fail. * </p> * - * * @param destinationAddress the address to send the message to * @param scAddress is the service center address or null to use * the current default SMSC @@ -358,7 +397,6 @@ public final class SmsManager { * <code>RESULT_ERROR_RADIO_OFF</code><br> * <code>RESULT_ERROR_NULL_PDU</code><br> * <code>RESULT_ERROR_NO_SERVICE</code><br> - * <code>RESULT_ERROR_NO_SERVICE</code><br> * <code>RESULT_ERROR_LIMIT_EXCEEDED</code><br> * <code>RESULT_ERROR_FDN_CHECK_FAILURE</code><br> * <code>RESULT_ERROR_SHORT_CODE_NOT_ALLOWED</code><br> @@ -408,12 +446,12 @@ public final class SmsManager { * <code>RESULT_RIL_NO_RESOURCES</code><br> * <code>RESULT_RIL_CANCELLED</code><br> * <code>RESULT_RIL_SIM_ABSENT</code><br> + * <code>RESULT_RIL_SIMULTANEOUS_SMS_AND_CALL_NOT_ALLOWED</code><br> + * <code>RESULT_RIL_ACCESS_BARRED</code><br> + * <code>RESULT_RIL_BLOCKED_DUE_TO_CALL</code><br> * For <code>RESULT_ERROR_GENERIC_FAILURE</code> or any of the RESULT_RIL errors, * the sentIntent may include the extra "errorCode" containing a radio technology specific * value, generally only useful for troubleshooting.<br> - * The per-application based SMS control checks sentIntent. If sentIntent - * is NULL the caller will be checked against all unknown applications, - * which cause smaller number of SMS to be sent in checking period. * @param deliveryIntent if not NULL this <code>PendingIntent</code> is * broadcast when the message is delivered to the recipient. The * raw pdu of the status report is in the extended data ("pdu"). @@ -424,7 +462,8 @@ public final class SmsManager { String destinationAddress, String scAddress, String text, PendingIntent sentIntent, PendingIntent deliveryIntent) { sendTextMessageInternal(destinationAddress, scAddress, text, sentIntent, deliveryIntent, - true /* persistMessage*/, null, null, 0L /* messageId */); + true /* persistMessage*/, getOpPackageName(), getAttributionTag(), + 0L /* messageId */); } @@ -443,7 +482,8 @@ public final class SmsManager { @Nullable PendingIntent sentIntent, @Nullable PendingIntent deliveryIntent, long messageId) { sendTextMessageInternal(destinationAddress, scAddress, text, sentIntent, deliveryIntent, - true /* persistMessage*/, null, null, messageId); + true /* persistMessage*/, getOpPackageName(), getAttributionTag(), + messageId); } /** @@ -470,7 +510,6 @@ public final class SmsManager { * <code>RESULT_ERROR_RADIO_OFF</code><br> * <code>RESULT_ERROR_NULL_PDU</code><br> * <code>RESULT_ERROR_NO_SERVICE</code><br> - * <code>RESULT_ERROR_NO_SERVICE</code><br> * <code>RESULT_ERROR_LIMIT_EXCEEDED</code><br> * <code>RESULT_ERROR_FDN_CHECK_FAILURE</code><br> * <code>RESULT_ERROR_SHORT_CODE_NOT_ALLOWED</code><br> @@ -520,12 +559,12 @@ public final class SmsManager { * <code>RESULT_RIL_NO_RESOURCES</code><br> * <code>RESULT_RIL_CANCELLED</code><br> * <code>RESULT_RIL_SIM_ABSENT</code><br> + * <code>RESULT_RIL_SIMULTANEOUS_SMS_AND_CALL_NOT_ALLOWED</code><br> + * <code>RESULT_RIL_ACCESS_BARRED</code><br> + * <code>RESULT_RIL_BLOCKED_DUE_TO_CALL</code><br> * For <code>RESULT_ERROR_GENERIC_FAILURE</code> or any of the RESULT_RIL errors, * the sentIntent may include the extra "errorCode" containing a radio technology specific * value, generally only useful for troubleshooting.<br> - * The per-application based SMS control checks sentIntent. If sentIntent - * is NULL the caller will be checked against all unknown applications, - * which cause smaller number of SMS to be sent in checking period. * @param deliveryIntent if not NULL this <code>PendingIntent</code> is * broadcast when the message is delivered to the recipient. The * raw pdu of the status report is in the extended data ("pdu"). @@ -550,7 +589,7 @@ public final class SmsManager { * @throws IllegalArgumentException if destinationAddress or text are empty * {@hide} */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public void sendTextMessage( String destinationAddress, String scAddress, String text, PendingIntent sentIntent, PendingIntent deliveryIntent, @@ -591,7 +630,7 @@ public final class SmsManager { persistMessage, messageId); } catch (RemoteException e) { Log.e(TAG, "sendTextMessageInternal: Couldn't send SMS, exception - " - + e.getMessage() + " id: " + messageId); + + e.getMessage() + " " + formatCrossStackMessageId(messageId)); notifySmsError(sentIntent, RESULT_REMOTE_EXCEPTION); } } @@ -611,7 +650,7 @@ public final class SmsManager { persistMessage, messageId); } catch (RemoteException e) { Log.e(TAG, "sendTextMessageInternal (no persist): Couldn't send SMS, exception - " - + e.getMessage() + " id: " + messageId); + + e.getMessage() + " " + formatCrossStackMessageId(messageId)); notifySmsError(sentIntent, RESULT_REMOTE_EXCEPTION); } } @@ -653,8 +692,8 @@ public final class SmsManager { String destinationAddress, String scAddress, String text, PendingIntent sentIntent, PendingIntent deliveryIntent) { sendTextMessageInternal(destinationAddress, scAddress, text, sentIntent, deliveryIntent, - false /* persistMessage */, null, null, - 0L /* messageId */); + false /* persistMessage */, getOpPackageName(), + getAttributionTag(), 0L /* messageId */); } private void sendTextMessageInternal( @@ -670,10 +709,12 @@ public final class SmsManager { } if (priority < 0x00 || priority > 0x03) { + Log.e(TAG, "Invalid Priority " + priority); priority = SMS_MESSAGE_PRIORITY_NOT_SPECIFIED; } if (validityPeriod < 0x05 || validityPeriod > 0x09b0a0) { + Log.e(TAG, "Invalid Validity Period " + validityPeriod); validityPeriod = SMS_MESSAGE_PERIOD_NOT_SPECIFIED; } @@ -854,22 +895,20 @@ public final class SmsManager { * where this operation may fail. * </p> * - * * @param destinationAddress the address to send the message to * @param scAddress is the service center address or null to use - * the current default SMSC + * the current default SMSC * @param parts an <code>ArrayList</code> of strings that, in order, - * comprise the original message + * comprise the original message * @param sentIntents if not null, an <code>ArrayList</code> of - * <code>PendingIntent</code>s (one for each message part) that is - * broadcast when the corresponding message part has been sent. - * The result code will be <code>Activity.RESULT_OK</code> for success, - * or one of these errors:<br> + * <code>PendingIntent</code>s (one for each message part) that is + * broadcast when the corresponding message part has been sent. + * The result code will be <code>Activity.RESULT_OK</code> for success, + * or one of these errors:<br> * <code>RESULT_ERROR_GENERIC_FAILURE</code><br> * <code>RESULT_ERROR_RADIO_OFF</code><br> * <code>RESULT_ERROR_NULL_PDU</code><br> * <code>RESULT_ERROR_NO_SERVICE</code><br> - * <code>RESULT_ERROR_NO_SERVICE</code><br> * <code>RESULT_ERROR_LIMIT_EXCEEDED</code><br> * <code>RESULT_ERROR_FDN_CHECK_FAILURE</code><br> * <code>RESULT_ERROR_SHORT_CODE_NOT_ALLOWED</code><br> @@ -919,17 +958,17 @@ public final class SmsManager { * <code>RESULT_RIL_NO_RESOURCES</code><br> * <code>RESULT_RIL_CANCELLED</code><br> * <code>RESULT_RIL_SIM_ABSENT</code><br> + * <code>RESULT_RIL_SIMULTANEOUS_SMS_AND_CALL_NOT_ALLOWED</code><br> + * <code>RESULT_RIL_ACCESS_BARRED</code><br> + * <code>RESULT_RIL_BLOCKED_DUE_TO_CALL</code><br> * For <code>RESULT_ERROR_GENERIC_FAILURE</code> or any of the RESULT_RIL errors, * the sentIntent may include the extra "errorCode" containing a radio technology specific * value, generally only useful for troubleshooting.<br> - * The per-application based SMS control checks sentIntent. If sentIntent - * is NULL the caller will be checked against all unknown applications, - * which cause smaller number of SMS to be sent in checking period. * @param deliveryIntents if not null, an <code>ArrayList</code> of - * <code>PendingIntent</code>s (one for each message part) that is - * broadcast when the corresponding message part has been delivered - * to the recipient. The raw pdu of the status report is in the - * extended data ("pdu"). + * <code>PendingIntent</code>s (one for each message part) that is + * broadcast when the corresponding message part has been delivered + * to the recipient. The raw pdu of the status report is in the + * extended data ("pdu"). * * @throws IllegalArgumentException if destinationAddress or data are empty */ @@ -937,8 +976,8 @@ public final class SmsManager { String destinationAddress, String scAddress, ArrayList<String> parts, ArrayList<PendingIntent> sentIntents, ArrayList<PendingIntent> deliveryIntents) { sendMultipartTextMessageInternal(destinationAddress, scAddress, parts, sentIntents, - deliveryIntents, true /* persistMessage*/, null, null, - 0L /* messageId */); + deliveryIntents, true /* persistMessage*/, getOpPackageName(), + getAttributionTag(), 0L /* messageId */); } /** @@ -955,8 +994,8 @@ public final class SmsManager { @NonNull List<String> parts, @Nullable List<PendingIntent> sentIntents, @Nullable List<PendingIntent> deliveryIntents, long messageId) { sendMultipartTextMessageInternal(destinationAddress, scAddress, parts, sentIntents, - deliveryIntents, true /* persistMessage*/, null, null, - messageId); + deliveryIntents, true /* persistMessage*/, getOpPackageName(), + getAttributionTag(), messageId); } /** @@ -1020,8 +1059,7 @@ public final class SmsManager { deliveryIntents, persistMessage, messageId); } catch (RemoteException e) { Log.e(TAG, "sendMultipartTextMessageInternal: Couldn't send SMS - " - + e.getMessage() + " id: " - + messageId); + + e.getMessage() + " " + formatCrossStackMessageId(messageId)); notifySmsError(sentIntents, RESULT_REMOTE_EXCEPTION); } } @@ -1042,7 +1080,7 @@ public final class SmsManager { } } catch (RemoteException e) { Log.e(TAG, "sendMultipartTextMessageInternal: Couldn't send SMS - " - + e.getMessage() + " id: " + messageId); + + e.getMessage() + " " + formatCrossStackMessageId(messageId)); notifySmsError(sentIntents, RESULT_REMOTE_EXCEPTION); } } @@ -1086,8 +1124,8 @@ public final class SmsManager { String destinationAddress, String scAddress, List<String> parts, List<PendingIntent> sentIntents, List<PendingIntent> deliveryIntents) { sendMultipartTextMessageInternal(destinationAddress, scAddress, parts, sentIntents, - deliveryIntents, false /* persistMessage*/, null, null, - 0L /* messageId */); + deliveryIntents, false /* persistMessage*/, getOpPackageName(), + getAttributionTag(), 0L /* messageId */); } /** @@ -1112,22 +1150,21 @@ public final class SmsManager { * boolean value {@code true}. See {@link #getDefault()} for more information on the conditions * where this operation may fail. * </p> - + * * @param destinationAddress the address to send the message to * @param scAddress is the service center address or null to use - * the current default SMSC + * the current default SMSC * @param parts an <code>ArrayList</code> of strings that, in order, - * comprise the original message + * comprise the original message * @param sentIntents if not null, an <code>ArrayList</code> of - * <code>PendingIntent</code>s (one for each message part) that is - * broadcast when the corresponding message part has been sent. - * The result code will be <code>Activity.RESULT_OK</code> for success, - * or one of these errors:<br> + * <code>PendingIntent</code>s (one for each message part) that is + * broadcast when the corresponding message part has been sent. + * The result code will be <code>Activity.RESULT_OK</code> for success, + * or one of these errors:<br> * <code>RESULT_ERROR_GENERIC_FAILURE</code><br> * <code>RESULT_ERROR_RADIO_OFF</code><br> * <code>RESULT_ERROR_NULL_PDU</code><br> * <code>RESULT_ERROR_NO_SERVICE</code><br> - * <code>RESULT_ERROR_NO_SERVICE</code><br> * <code>RESULT_ERROR_LIMIT_EXCEEDED</code><br> * <code>RESULT_ERROR_FDN_CHECK_FAILURE</code><br> * <code>RESULT_ERROR_SHORT_CODE_NOT_ALLOWED</code><br> @@ -1177,17 +1214,17 @@ public final class SmsManager { * <code>RESULT_RIL_NO_RESOURCES</code><br> * <code>RESULT_RIL_CANCELLED</code><br> * <code>RESULT_RIL_SIM_ABSENT</code><br> + * <code>RESULT_RIL_SIMULTANEOUS_SMS_AND_CALL_NOT_ALLOWED</code><br> + * <code>RESULT_RIL_ACCESS_BARRED</code><br> + * <code>RESULT_RIL_BLOCKED_DUE_TO_CALL</code><br> * For <code>RESULT_ERROR_GENERIC_FAILURE</code> or any of the RESULT_RIL errors, * the sentIntent may include the extra "errorCode" containing a radio technology specific * value, generally only useful for troubleshooting.<br> - * The per-application based SMS control checks sentIntent. If sentIntent - * is NULL the caller will be checked against all unknown applications, - * which cause smaller number of SMS to be sent in checking period. * @param deliveryIntents if not null, an <code>ArrayList</code> of - * <code>PendingIntent</code>s (one for each message part) that is - * broadcast when the corresponding message part has been delivered - * to the recipient. The raw pdu of the status report is in the - * extended data ("pdu"). + * <code>PendingIntent</code>s (one for each message part) that is + * broadcast when the corresponding message part has been delivered + * to the recipient. The raw pdu of the status report is in the + * extended data ("pdu"). * @param priority Priority level of the message * Refer specification See 3GPP2 C.S0015-B, v2.0, table 4.5.9-1 * --------------------------------- @@ -1231,10 +1268,12 @@ public final class SmsManager { } if (priority < 0x00 || priority > 0x03) { + Log.e(TAG, "Invalid Priority " + priority); priority = SMS_MESSAGE_PRIORITY_NOT_SPECIFIED; } if (validityPeriod < 0x05 || validityPeriod > 0x09b0a0) { + Log.e(TAG, "Invalid Validity Period " + validityPeriod); validityPeriod = SMS_MESSAGE_PERIOD_NOT_SPECIFIED; } @@ -1324,7 +1363,6 @@ public final class SmsManager { * <code>RESULT_ERROR_RADIO_OFF</code><br> * <code>RESULT_ERROR_NULL_PDU</code><br> * <code>RESULT_ERROR_NO_SERVICE</code><br> - * <code>RESULT_ERROR_NO_SERVICE</code><br> * <code>RESULT_ERROR_LIMIT_EXCEEDED</code><br> * <code>RESULT_ERROR_FDN_CHECK_FAILURE</code><br> * <code>RESULT_ERROR_SHORT_CODE_NOT_ALLOWED</code><br> @@ -1374,12 +1412,12 @@ public final class SmsManager { * <code>RESULT_RIL_NO_RESOURCES</code><br> * <code>RESULT_RIL_CANCELLED</code><br> * <code>RESULT_RIL_SIM_ABSENT</code><br> + * <code>RESULT_RIL_SIMULTANEOUS_SMS_AND_CALL_NOT_ALLOWED</code><br> + * <code>RESULT_RIL_ACCESS_BARRED</code><br> + * <code>RESULT_RIL_BLOCKED_DUE_TO_CALL</code><br> * For <code>RESULT_ERROR_GENERIC_FAILURE</code> or any of the RESULT_RIL errors, * the sentIntent may include the extra "errorCode" containing a radio technology specific * value, generally only useful for troubleshooting.<br> - * The per-application based SMS control checks sentIntent. If sentIntent - * is NULL the caller will be checked against all unknown applications, - * which cause smaller number of SMS to be sent in checking period. * @param deliveryIntent if not NULL this <code>PendingIntent</code> is * broadcast when the message is delivered to the recipient. The * raw pdu of the status report is in the extended data ("pdu"). @@ -1447,9 +1485,37 @@ public final class SmsManager { * @return the {@link SmsManager} associated with the default subscription id. * * @see SubscriptionManager#getDefaultSmsSubscriptionId() + * + * @deprecated Use {@link Context#getSystemService Context.getSystemService(SmsManager.class)} + * instead */ + @Deprecated public static SmsManager getDefault() { - return sInstance; + return DEFAULT_INSTANCE; + } + + /** + * Get the instance of the SmsManager associated with a particular context and subscription ID. + * + * @param context The context the manager belongs to + * @param subId an SMS subscription ID, typically accessed using {@link SubscriptionManager} + * + * @return the instance of the SmsManager associated with subscription + * + * @hide + */ + public static @NonNull SmsManager getSmsManagerForContextAndSubscriptionId( + @Nullable Context context, int subId) { + synchronized(sLockObject) { + Pair<Context, Integer> key = new Pair<>(context, subId); + + SmsManager smsManager = sSubInstances.get(key); + if (smsManager == null) { + smsManager = new SmsManager(context, subId); + sSubInstances.put(key, smsManager); + } + return smsManager; + } } /** @@ -1464,19 +1530,33 @@ public final class SmsManager { * * @see SubscriptionManager#getActiveSubscriptionInfoList() * @see SubscriptionManager#getDefaultSmsSubscriptionId() + * @deprecated Use {@link Context#getSystemService Context.getSystemService(SmsManager.class)} + * .{@link #createForSubscriptionId createForSubscriptionId(subId)} instead */ + @Deprecated public static SmsManager getSmsManagerForSubscriptionId(int subId) { - synchronized(sLockObject) { - SmsManager smsManager = sSubInstances.get(subId); - if (smsManager == null) { - smsManager = new SmsManager(subId); - sSubInstances.put(subId, smsManager); - } - return smsManager; - } + return getSmsManagerForContextAndSubscriptionId(null, subId); } - private SmsManager(int subId) { + /** + * Get the instance of the SmsManager associated with a particular subscription ID. + * + * <p class="note"><strong>Note:</strong> Constructing an {@link SmsManager} in this manner will + * never cause an SMS disambiguation dialog to appear, unlike {@link #getDefault()}. + * </p> + * + * @param subId an SMS subscription ID, typically accessed using {@link SubscriptionManager} + * @return the instance of the SmsManager associated with subscription + * + * @see SubscriptionManager#getActiveSubscriptionInfoList() + * @see SubscriptionManager#getDefaultSmsSubscriptionId() + */ + public @NonNull SmsManager createForSubscriptionId(int subId) { + return getSmsManagerForContextAndSubscriptionId(mContext, subId); + } + + private SmsManager(@Nullable Context context, int subId) { + mContext = context; mSubId = subId; } @@ -1711,13 +1791,13 @@ public final class SmsManager { * operation is performed on the correct subscription. * </p> * - * @param messageIndex This is the same index used to access a message - * from {@link #getMessagesFromIcc()}. + * @param messageIndex the message index of the message in the ICC (1-based index). * @return true for success, false if the operation fails. Failure can be due to IPC failure, * RIL/modem error which results in SMS failed to be deleted on SIM * * {@hide} */ + @UnsupportedAppUsage @RequiresPermission(Manifest.permission.ACCESS_MESSAGES_ON_ICC) public boolean deleteMessageFromIcc(int messageIndex) { boolean success = false; @@ -1794,7 +1874,7 @@ public final class SmsManager { * operation is performed on the correct subscription. * </p> * - * @return <code>List</code> of <code>SmsMessage</code> objects + * @return <code>List</code> of <code>SmsMessage</code> objects for valid records only. * * {@hide} */ @@ -1868,7 +1948,6 @@ public final class SmsManager { public boolean enableCellBroadcastRange(int startMessageId, int endMessageId, @android.telephony.SmsCbMessage.MessageFormat int ranType) { boolean success = false; - if (endMessageId < startMessageId) { throw new IllegalArgumentException("endMessageId < startMessageId"); } @@ -1877,10 +1956,14 @@ public final class SmsManager { if (iSms != null) { // If getSubscriptionId() returns INVALID or an inactive subscription, we will use // the default phone internally. - success = iSms.enableCellBroadcastRangeForSubscriber(getSubscriptionId(), + int subId = getSubscriptionId(); + success = iSms.enableCellBroadcastRangeForSubscriber(subId, startMessageId, endMessageId, ranType); + Rlog.d(TAG, "enableCellBroadcastRange: " + (success ? "succeeded" : "failed") + + " at calling enableCellBroadcastRangeForSubscriber. subId = " + subId); } } catch (RemoteException ex) { + Rlog.d(TAG, "enableCellBroadcastRange: " + ex.getStackTrace()); // ignore it } @@ -1934,10 +2017,14 @@ public final class SmsManager { if (iSms != null) { // If getSubscriptionId() returns INVALID or an inactive subscription, we will use // the default phone internally. - success = iSms.disableCellBroadcastRangeForSubscriber(getSubscriptionId(), + int subId = getSubscriptionId(); + success = iSms.disableCellBroadcastRangeForSubscriber(subId, startMessageId, endMessageId, ranType); + Rlog.d(TAG, "disableCellBroadcastRange: " + (success ? "succeeded" : "failed") + + " at calling disableCellBroadcastRangeForSubscriber. subId = " + subId); } } catch (RemoteException ex) { + Rlog.d(TAG, "disableCellBroadcastRange: " + ex.getStackTrace()); // ignore it } @@ -2088,17 +2175,27 @@ public final class SmsManager { } /** - * Gets the total capacity of SMS storage on RUIM and SIM cards - * <p> - * This is the number of 176 byte EF-SMS records which can be stored on the RUIM or SIM card. + * Gets the total capacity of SMS storage on the SIM card. + * * <p> - * See 3GPP TS 31.102 - 4.2.25 - EF-SMS for more information + * This is the number of 176 byte EF-SMS records which can be stored on the SIM card. + * See 3GPP TS 31.102 - 4.2.25 - EF-SMS for more information. + * </p> * - * @return the total number of SMS records which can be stored on the RUIM or SIM cards. - * @hide + * <p class="note"><strong>Note:</strong> This method will never trigger an SMS disambiguation + * dialog. If this method is called on a device that has multiple active subscriptions, this + * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined + * default subscription is defined, the subscription ID associated with this method will be + * INVALID, which will result in the operation being completed on the subscription associated + * with logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the operation + * is performed on the correct subscription. + * </p> + * + * @return the total number of SMS records which can be stored on the SIM card. */ - @SystemApi - @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresPermission(anyOf = {android.Manifest.permission.READ_PHONE_STATE, + android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE}) + @IntRange(from = 0) public int getSmsCapacityOnIcc() { int ret = 0; try { @@ -2107,7 +2204,7 @@ public final class SmsManager { ret = iccISms.getSmsCapacityOnIccForSubscriber(getSubscriptionId()); } } catch (RemoteException ex) { - //ignore it + Log.e(TAG, "getSmsCapacityOnIcc() RemoteException", ex); } return ret; } @@ -2196,7 +2293,10 @@ public final class SmsManager { RESULT_RIL_OPERATION_NOT_ALLOWED, RESULT_RIL_NO_RESOURCES, RESULT_RIL_CANCELLED, - RESULT_RIL_SIM_ABSENT + RESULT_RIL_SIM_ABSENT, + RESULT_RIL_SIMULTANEOUS_SMS_AND_CALL_NOT_ALLOWED, + RESULT_RIL_ACCESS_BARRED, + RESULT_RIL_BLOCKED_DUE_TO_CALL }) @Retention(RetentionPolicy.SOURCE) public @interface Result {} @@ -2461,6 +2561,21 @@ public final class SmsManager { */ public static final int RESULT_RIL_SIM_ABSENT = 120; + /** + * 1X voice and SMS are not allowed simulteneously. + */ + public static final int RESULT_RIL_SIMULTANEOUS_SMS_AND_CALL_NOT_ALLOWED = 121; + + /** + * Access is barred. + */ + public static final int RESULT_RIL_ACCESS_BARRED = 122; + + /** + * SMS is blocked due to call control, e.g., resource unavailable in the SMR entity. + */ + public static final int RESULT_RIL_BLOCKED_DUE_TO_CALL = 123; + // SMS receiving results sent as a "result" extra in {@link Intents.SMS_REJECTED_ACTION} /** @@ -2503,13 +2618,12 @@ public final class SmsManager { /** * Send an MMS message * - * <p class="note"><strong>Note:</strong> This method will never trigger an SMS disambiguation - * dialog. If this method is called on a device that has multiple active subscriptions, this - * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined - * default subscription is defined, the subscription ID associated with this message will be - * INVALID, which will result in the operation being completed on the subscription associated - * with logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the - * operation is performed on the correct subscription. + * <p class="note"><strong>Note:</strong> If {@link #getDefault()} is used to instantiate this + * manager on a multi-SIM device, this operation may fail sending the MMS message because no + * suitable default subscription could be found. In this case, if {@code sentIntent} is + * non-null, then the {@link PendingIntent} will be sent with an error code + * {@code RESULT_NO_DEFAULT_SMS_APP}. See {@link #getDefault()} for more information on the + * conditions where this operation may fail. * </p> * * @param context application context @@ -2523,26 +2637,66 @@ public final class SmsManager { */ public void sendMultimediaMessage(Context context, Uri contentUri, String locationUrl, Bundle configOverrides, PendingIntent sentIntent) { + sendMultimediaMessage(context, contentUri, locationUrl, configOverrides, sentIntent, + 0L /* messageId */); + } + + /** + * Send an MMS message + * + * Same as {@link #sendMultimediaMessage(Context context, Uri contentUri, String locationUrl, + * Bundle configOverrides, PendingIntent sentIntent)}, but adds an optional messageId. + * <p class="note"><strong>Note:</strong> If {@link #getDefault()} is used to instantiate this + * manager on a multi-SIM device, this operation may fail sending the MMS message because no + * suitable default subscription could be found. In this case, if {@code sentIntent} is + * non-null, then the {@link PendingIntent} will be sent with an error code + * {@code RESULT_NO_DEFAULT_SMS_APP}. See {@link #getDefault()} for more information on the + * conditions where this operation may fail. + * </p> + * + * @param context application context + * @param contentUri the content Uri from which the message pdu will be read + * @param locationUrl the optional location url where message should be sent to + * @param configOverrides the carrier-specific messaging configuration values to override for + * sending the message. + * @param sentIntent if not NULL this <code>PendingIntent</code> is + * broadcast when the message is successfully sent, or failed + * @param messageId an id that uniquely identifies the message requested to be sent. + * Used for logging and diagnostics purposes. The id may be 0. + * @throws IllegalArgumentException if contentUri is empty + */ + public void sendMultimediaMessage(@NonNull Context context, @NonNull Uri contentUri, + @Nullable String locationUrl, @Nullable Bundle configOverrides, + @Nullable PendingIntent sentIntent, long messageId) { if (contentUri == null) { throw new IllegalArgumentException("Uri contentUri null"); } MmsManager m = (MmsManager) context.getSystemService(Context.MMS_SERVICE); if (m != null) { - m.sendMultimediaMessage(getSubscriptionId(), contentUri, locationUrl, configOverrides, - sentIntent, 0L /* messageId */); + resolveSubscriptionForOperation(new SubscriptionResolverResult() { + @Override + public void onSuccess(int subId) { + m.sendMultimediaMessage(subId, contentUri, locationUrl, configOverrides, + sentIntent, messageId); + } + + @Override + public void onFailure() { + notifySmsError(sentIntent, RESULT_NO_DEFAULT_SMS_APP); + } + }); } } /** * Download an MMS message from carrier by a given location URL * - * <p class="note"><strong>Note:</strong> This method will never trigger an SMS disambiguation - * dialog. If this method is called on a device that has multiple active subscriptions, this - * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined - * default subscription is defined, the subscription ID associated with this message will be - * INVALID, which will result in the operation being completed on the subscription associated - * with logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the - * operation is performed on the correct subscription. + * <p class="note"><strong>Note:</strong> If {@link #getDefault()} is used to instantiate this + * manager on a multi-SIM device, this operation may fail downloading the MMS message because no + * suitable default subscription could be found. In this case, if {@code downloadedIntent} is + * non-null, then the {@link PendingIntent} will be sent with an error code + * {@code RESULT_NO_DEFAULT_SMS_APP}. See {@link #getDefault()} for more information on the + * conditions where this operation may fail. * </p> * * @param context application context @@ -2557,6 +2711,39 @@ public final class SmsManager { */ public void downloadMultimediaMessage(Context context, String locationUrl, Uri contentUri, Bundle configOverrides, PendingIntent downloadedIntent) { + downloadMultimediaMessage(context, locationUrl, contentUri, configOverrides, + downloadedIntent, 0L /* messageId */); + } + + /** + * Download an MMS message from carrier by a given location URL + * + * Same as {@link #downloadMultimediaMessage(Context context, String locationUrl, + * Uri contentUri, Bundle configOverrides, PendingIntent downloadedIntent)}, + * but adds an optional messageId. + * <p class="note"><strong>Note:</strong> If {@link #getDefault()} is used to instantiate this + * manager on a multi-SIM device, this operation may fail downloading the MMS message because no + * suitable default subscription could be found. In this case, if {@code downloadedIntent} is + * non-null, then the {@link PendingIntent} will be sent with an error code + * {@code RESULT_NO_DEFAULT_SMS_APP}. See {@link #getDefault()} for more information on the + * conditions where this operation may fail. + * </p> + * + * @param context application context + * @param locationUrl the location URL of the MMS message to be downloaded, usually obtained + * from the MMS WAP push notification + * @param contentUri the content uri to which the downloaded pdu will be written + * @param configOverrides the carrier-specific messaging configuration values to override for + * downloading the message. + * @param downloadedIntent if not NULL this <code>PendingIntent</code> is + * broadcast when the message is downloaded, or the download is failed + * @param messageId an id that uniquely identifies the message requested to be downloaded. + * Used for logging and diagnostics purposes. The id may be 0. + * @throws IllegalArgumentException if locationUrl or contentUri is empty + */ + public void downloadMultimediaMessage(@NonNull Context context, @NonNull String locationUrl, + @NonNull Uri contentUri, @Nullable Bundle configOverrides, + @Nullable PendingIntent downloadedIntent, long messageId) { if (TextUtils.isEmpty(locationUrl)) { throw new IllegalArgumentException("Empty MMS location URL"); } @@ -2565,8 +2752,18 @@ public final class SmsManager { } MmsManager m = (MmsManager) context.getSystemService(Context.MMS_SERVICE); if (m != null) { - m.downloadMultimediaMessage(getSubscriptionId(), locationUrl, contentUri, - configOverrides, downloadedIntent, 0L /* messageId */); + resolveSubscriptionForOperation(new SubscriptionResolverResult() { + @Override + public void onSuccess(int subId) { + m.downloadMultimediaMessage(subId, locationUrl, contentUri, configOverrides, + downloadedIntent, messageId); + } + + @Override + public void onFailure() { + notifySmsError(downloadedIntent, RESULT_NO_DEFAULT_SMS_APP); + } + }); } } @@ -2981,26 +3178,24 @@ public final class SmsManager { /** * Reset all cell broadcast ranges. Previously enabled ranges will become invalid after this. - * - * @return {@code true} if succeeded, otherwise {@code false}. - * - * // TODO: Unhide the API in S. * @hide */ - public boolean resetAllCellBroadcastRanges() { - boolean success = false; - + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_CELL_BROADCASTS) + public void resetAllCellBroadcastRanges() { try { ISms iSms = getISmsService(); if (iSms != null) { // If getSubscriptionId() returns INVALID or an inactive subscription, we will use // the default phone internally. - success = iSms.resetAllCellBroadcastRanges(getSubscriptionId()); + iSms.resetAllCellBroadcastRanges(getSubscriptionId()); } } catch (RemoteException ex) { - // ignore it + ex.rethrowFromSystemServer(); } + } - return success; + private static String formatCrossStackMessageId(long id) { + return "{x-message-id:" + id + "}"; } } diff --git a/telephony/java/android/telephony/SmsMessage.java b/telephony/java/android/telephony/SmsMessage.java index e537f666d4c0..5a12865fb2a0 100644 --- a/telephony/java/android/telephony/SmsMessage.java +++ b/telephony/java/android/telephony/SmsMessage.java @@ -29,6 +29,7 @@ import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; import android.content.res.Resources; import android.os.Binder; +import android.os.Build; import android.text.TextUtils; import com.android.internal.telephony.GsmAlphabet; @@ -133,7 +134,7 @@ public class SmsMessage { * * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) private int mSubId = 0; /** set Subscription information @@ -255,28 +256,6 @@ public class SmsMessage { } /** - * TS 27.005 3.4.1 lines[0] and lines[1] are the two lines read from the - * +CMT unsolicited response (PDU mode, of course) - * +CMT: [<alpha>],<length><CR><LF><pdu> - * - * Only public for debugging and for RIL - * - * {@hide} - */ - public static SmsMessage newFromCMT(byte[] pdu) { - // received SMS in 3GPP format - SmsMessageBase wrappedMessage = - com.android.internal.telephony.gsm.SmsMessage.newFromCMT(pdu); - - if (wrappedMessage != null) { - return new SmsMessage(wrappedMessage); - } else { - Rlog.e(LOG_TAG, "newFromCMT(): wrappedMessage is null"); - return null; - } - } - - /** * Creates an SmsMessage from an SMS EF record. * * @param index Index of SMS EF record. @@ -496,7 +475,10 @@ public class SmsMessage { String newMsgBody = null; Resources r = Resources.getSystem(); if (r.getBoolean(com.android.internal.R.bool.config_sms_force_7bit_encoding)) { - newMsgBody = Sms7BitEncodingTranslator.translate(text, isCdma); + // 7-bit ASCII table based translation is required only for CDMA single-part SMS since + // ENCODING_7BIT_ASCII is used for CDMA single-part SMS and ENCODING_GSM_7BIT_ALPHABET + // is used for CDMA multi-part SMS. + newMsgBody = Sms7BitEncodingTranslator.translate(text, isCdma && ted.msgCount == 1); } if (TextUtils.isEmpty(newMsgBody)) { newMsgBody = text; @@ -1059,7 +1041,7 @@ public class SmsMessage { * * @return true if Cdma format should be used for MO SMS, false otherwise. */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) private static boolean useCdmaFormatForMoSms(int subId) { SmsManager smsManager = SmsManager.getSmsManagerForSubscriptionId(subId); if (!smsManager.isImsSmsSupported()) { diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java index 11667c83bc6a..21bb43cb6f32 100644 --- a/telephony/java/android/telephony/SubscriptionInfo.java +++ b/telephony/java/android/telephony/SubscriptionInfo.java @@ -16,6 +16,8 @@ package android.telephony; +import static android.text.TextUtils.formatSimple; + import android.annotation.Nullable; import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; @@ -30,6 +32,7 @@ import android.graphics.PorterDuff; import android.graphics.PorterDuffColorFilter; import android.graphics.Rect; import android.graphics.Typeface; +import android.os.Build; import android.os.Parcel; import android.os.ParcelUuid; import android.os.Parcelable; @@ -148,13 +151,14 @@ public class SubscriptionInfo implements Parcelable { /** * The access rules for this subscription, if it is embedded and defines any. + * This does not include access rules for non-embedded subscriptions. */ @Nullable private UiccAccessRule[] mNativeAccessRules; /** * The carrier certificates for this subscription that are saved in carrier configs. - * The other carrier certificates are embedded on Uicc and stored as part of mNativeAccessRules. + * This does not include access rules from the Uicc, whether embedded or non-embedded. */ @Nullable private UiccAccessRule[] mCarrierConfigAccessRules; @@ -354,7 +358,7 @@ public class SubscriptionInfo implements Parcelable { * Sets the name displayed to the user that identifies this subscription * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public void setDisplayName(CharSequence name) { this.mDisplayName = name; } @@ -379,7 +383,7 @@ public class SubscriptionInfo implements Parcelable { * NAME_SOURCE_USER_INPUT. * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public int getNameSource() { return this.mNameSource; } @@ -423,7 +427,7 @@ public class SubscriptionInfo implements Parcelable { // Set text size scaled by density paint.setTextSize(TEXT_SIZE * metrics.density); // Convert sim slot index to localized string - final String index = String.format("%d", mSimSlotIndex + 1); + final String index = formatSimple("%d", mSimSlotIndex + 1); final Rect textBound = new Rect(); paint.getTextBounds(index, 0, 1, textBound); final float xOffset = (width / 2.f) - textBound.centerX(); @@ -446,7 +450,7 @@ public class SubscriptionInfo implements Parcelable { * Sets the color displayed to the user that identifies this subscription * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public void setIconTint(int iconTint) { this.mIconTint = iconTint; } @@ -568,6 +572,13 @@ public class SubscriptionInfo implements Parcelable { /** * @hide */ + public void clearGroupUuid() { + this.mGroupUUID = null; + } + + /** + * @hide + */ public List<String> getEhplmns() { return mEhplmns == null ? Collections.emptyList() : Arrays.asList(mEhplmns); } @@ -661,7 +672,6 @@ public class SubscriptionInfo implements Parcelable { * is authorized to manage this subscription. * TODO and fix it properly in R / master: either deprecate this and have 3 APIs * native + carrier + all, or have this return all by default. - * @throws UnsupportedOperationException if this subscription is not embedded. * @hide */ @SystemApi diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java index 45070f672483..114c90bbd87a 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -47,6 +47,7 @@ import android.net.NetworkPolicyManager; import android.net.Uri; import android.os.Binder; import android.os.Build; +import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.ParcelUuid; @@ -55,6 +56,7 @@ import android.os.RemoteException; import android.provider.Telephony.SimInfo; import android.telephony.euicc.EuiccManager; import android.telephony.ims.ImsMmTelManager; +import android.util.Base64; import android.util.Log; import android.util.Pair; @@ -66,6 +68,11 @@ import com.android.internal.util.FunctionalUtils; import com.android.internal.util.Preconditions; import com.android.telephony.Rlog; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; @@ -93,10 +100,9 @@ public class SubscriptionManager { /** An invalid subscription identifier */ public static final int INVALID_SUBSCRIPTION_ID = -1; - /** Base value for Dummy SUBSCRIPTION_ID's. */ - /** FIXME: Remove DummySubId's, but for now have them map just below INVALID_SUBSCRIPTION_ID - /** @hide */ - public static final int DUMMY_SUBSCRIPTION_ID_BASE = INVALID_SUBSCRIPTION_ID - 1; + /** Base value for placeholder SUBSCRIPTION_ID's. */ + /** @hide */ + public static final int PLACEHOLDER_SUBSCRIPTION_ID_BASE = INVALID_SUBSCRIPTION_ID - 1; /** An invalid phone identifier */ /** @hide */ @@ -128,7 +134,7 @@ public class SubscriptionManager { public static final int MAX_SUBSCRIPTION_ID_VALUE = DEFAULT_SUBSCRIPTION_ID - 1; /** @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public static final Uri CONTENT_URI = SimInfo.CONTENT_URI; /** @hide */ @@ -151,6 +157,22 @@ public class SubscriptionManager { public static final String CACHE_KEY_SLOT_INDEX_PROPERTY = "cache_key.telephony.get_slot_index"; + /** @hide */ + public static final String GET_SIM_SPECIFIC_SETTINGS_METHOD_NAME = "getSimSpecificSettings"; + + /** @hide */ + public static final String RESTORE_SIM_SPECIFIC_SETTINGS_METHOD_NAME = + "restoreSimSpecificSettings"; + + /** + * Key to the backup & restore data byte array in the Bundle that is returned by {@link + * #getAllSimSpecificSettingsForBackup()} or to be pass in to {@link + * #restoreAllSimSpecificSettings()}. + * + * @hide + */ + public static final String KEY_SIM_SPECIFIC_SETTINGS_DATA = "KEY_SIM_SPECIFIC_SETTINGS_DATA"; + private static final int MAX_CACHE_SIZE = 4; private static class VoidPropertyInvalidatedCache<T> @@ -373,6 +395,48 @@ public class SubscriptionManager { public static final Uri WFC_ROAMING_ENABLED_CONTENT_URI = Uri.withAppendedPath( CONTENT_URI, "wfc_roaming_enabled"); + + /** + * A content {@link uri} used to call the appropriate backup or restore method for sim-specific + * settings + * <p> + * See {@link #GET_SIM_SPECIFIC_SETTINGS_METHOD_NAME} and {@link + * #RESTORE_SIM_SPECIFIC_SETTINGS_METHOD_NAME} for information on what method to call. + * @hide + */ + @NonNull + public static final Uri SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI = Uri.withAppendedPath( + CONTENT_URI, "backup_and_restore"); + + /** + * A content {@link uri} used to notify contentobservers listening to siminfo restore during + * SuW. + * @hide + */ + @NonNull + public static final Uri SIM_INFO_SUW_RESTORE_CONTENT_URI = Uri.withAppendedPath( + SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI, "suw_restore"); + + /** + * A content {@link Uri} used to receive updates on cross sim enabled user setting. + * <p> + * Use this {@link Uri} with a {@link ContentObserver} to be notified of changes to the + * subscription cross sim calling enabled + * {@link ImsMmTelManager#isCrossSimCallingEnabled()} + * while your app is running. You can also use a {@link android.app.job.JobService} + * to ensure your app + * is notified of changes to the {@link Uri} even when it is not running. + * Note, however, that using a {@link android.app.job.JobService} does not guarantee timely + * delivery of updates to the {@link Uri}. + * To be notified of changes to a specific subId, append subId to the URI + * {@link Uri#withAppendedPath(Uri, String)}. + * @hide + */ + @NonNull + @SystemApi + public static final Uri CROSS_SIM_ENABLED_CONTENT_URI = Uri.withAppendedPath(CONTENT_URI, + SimInfo.COLUMN_CROSS_SIM_CALLING_ENABLED); + /** * TelephonyProvider unique key column name is the subscription id. * <P>Type: TEXT (String)</P> @@ -544,6 +608,50 @@ public class SubscriptionManager { public @interface SimDisplayNameSource {} /** + * Device status is not shared to a remote party. + */ + public static final int D2D_SHARING_DISABLED = 0; + + /** + * Device status is shared with all numbers in the user's contacts. + */ + public static final int D2D_SHARING_ALL_CONTACTS = 1; + + /** + * Device status is shared with all selected contacts. + */ + public static final int D2D_SHARING_SELECTED_CONTACTS = 2; + + /** + * Device status is shared whenever possible. + */ + public static final int D2D_SHARING_ALL = 3; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"D2D_SHARING_"}, + value = { + D2D_SHARING_DISABLED, + D2D_SHARING_ALL_CONTACTS, + D2D_SHARING_SELECTED_CONTACTS, + D2D_SHARING_ALL + }) + public @interface DeviceToDeviceStatusSharingPreference {} + + /** + * TelephonyProvider column name for device to device sharing status. + * <P>Type: INTEGER (int)</P> + */ + public static final String D2D_STATUS_SHARING = SimInfo.COLUMN_D2D_STATUS_SHARING; + + /** + * TelephonyProvider column name for contacts information that allow device to device sharing. + * <P>Type: TEXT (String)</P> + */ + public static final String D2D_STATUS_SHARING_SELECTED_CONTACTS = + SimInfo.COLUMN_D2D_STATUS_SHARING_SELECTED_CONTACTS; + + /** * TelephonyProvider column name for the color of a SIM. * <P>Type: INTEGER (int)</P> */ @@ -792,6 +900,13 @@ public class SubscriptionManager { public static final String IMS_RCS_UCE_ENABLED = SimInfo.COLUMN_IMS_RCS_UCE_ENABLED; /** + * Determines if the user has enabled cross SIM calling for this subscription. + * + * @hide + */ + public static final String CROSS_SIM_CALLING_ENABLED = SimInfo.COLUMN_CROSS_SIM_CALLING_ENABLED; + + /** * TelephonyProvider column name for whether a subscription is opportunistic, that is, * whether the network it connects to is limited in functionality or coverage. * For example, CBRS. @@ -826,6 +941,14 @@ public class SubscriptionManager { public static final String PROFILE_CLASS = SimInfo.COLUMN_PROFILE_CLASS; /** + * TelephonyProvider column name for VoIMS opt-in status. + * + * <P>Type: INTEGER (int)</P> + * @hide + */ + public static final String VOIMS_OPT_IN_STATUS = SimInfo.COLUMN_VOIMS_OPT_IN_STATUS; + + /** * Profile class of the subscription * @hide */ @@ -901,7 +1024,8 @@ public class SubscriptionManager { * Indicate which network type is allowed. By default it's enabled. * @hide */ - public static final String ALLOWED_NETWORK_TYPES = SimInfo.COLUMN_ALLOWED_NETWORK_TYPES; + public static final String ALLOWED_NETWORK_TYPES = + SimInfo.COLUMN_ALLOWED_NETWORK_TYPES_FOR_REASONS; /** * Broadcast Action: The user has changed one of the default subs related to @@ -1104,11 +1228,15 @@ public class SubscriptionManager { * individual records themselves. When a change occurs the onSubscriptionsChanged method of * the listener will be invoked immediately if there has been a notification. The * onSubscriptionChanged method will also be triggered once initially when calling this - * function. + * function. The callback will be invoked on the looper specified in the listener's constructor. * * @param listener an instance of {@link OnSubscriptionsChangedListener} with * onSubscriptionsChanged overridden. + * + * @deprecated Will get exception if the parameter listener is not initialized with a Looper. + * Use {@link #addOnSubscriptionsChangedListener(Executor, OnSubscriptionsChangedListener)}. */ + @Deprecated public void addOnSubscriptionsChangedListener(OnSubscriptionsChangedListener listener) { if (listener == null) return; addOnSubscriptionsChangedListener(listener.mExecutor, listener); @@ -1362,6 +1490,7 @@ public class SubscriptionManager { * include those that were inserted before, maybe empty but not null. * @hide */ + @NonNull @UnsupportedAppUsage public List<SubscriptionInfo> getAllSubscriptionInfoList() { if (VDBG) logd("[getAllSubscriptionInfoList]+"); @@ -1379,7 +1508,7 @@ public class SubscriptionManager { } if (result == null) { - result = new ArrayList<>(); + result = Collections.emptyList(); } return result; } @@ -1823,7 +1952,7 @@ public class SubscriptionManager { * @return the number of records updated * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public int setDisplayNumber(String number, int subId) { if (number == null) { logd("[setDisplayNumber]- fail"); @@ -1841,7 +1970,7 @@ public class SubscriptionManager { * @return the number of records updated * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public int setDataRoaming(int roaming, int subId) { if (VDBG) logd("[setDataRoaming]+ roaming:" + roaming + " subId:" + subId); return setSubscriptionPropertyHelper(subId, "setDataRoaming", @@ -1986,13 +2115,13 @@ public class SubscriptionManager { * @return the SubscriptionInfo for the default voice subscription. * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public SubscriptionInfo getDefaultVoiceSubscriptionInfo() { return getActiveSubscriptionInfo(getDefaultVoiceSubscriptionId()); } /** @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public static int getDefaultVoicePhoneId() { return getPhoneId(getDefaultVoiceSubscriptionId()); } @@ -2044,7 +2173,7 @@ public class SubscriptionManager { } /** @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public int getDefaultSmsPhoneId() { return getPhoneId(getDefaultSmsSubscriptionId()); } @@ -2178,7 +2307,7 @@ public class SubscriptionManager { } /** @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public static boolean isValidPhoneId(int phoneId) { return phoneId >= 0 && phoneId < TelephonyManager.getDefault().getActiveModemCount(); } @@ -2197,7 +2326,7 @@ public class SubscriptionManager { } /** @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public static void putPhoneIdAndSubIdExtra(Intent intent, int phoneId, int subId) { if (VDBG) logd("putPhoneIdAndSubIdExtra: phoneId=" + phoneId + " subId=" + subId); intent.putExtra(EXTRA_SLOT_INDEX, phoneId); @@ -2323,6 +2452,57 @@ public class SubscriptionManager { } /** + * Serialize list of contacts uri to string + * @hide + */ + public static String serializeUriLists(List<Uri> uris) { + List<String> contacts = new ArrayList<>(); + for (Uri uri : uris) { + contacts.add(uri.toString()); + } + try { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + ObjectOutputStream oos = new ObjectOutputStream(bos); + oos.writeObject(contacts); + oos.flush(); + return Base64.encodeToString(bos.toByteArray(), Base64.DEFAULT); + } catch (IOException e) { + logd("serializeUriLists IO exception"); + } + return ""; + } + + /** + * Return list of contacts uri corresponding to query result. + * @param subId Subscription Id of Subscription + * @param propKey Column name in SubscriptionInfo database + * @return list of contacts uri to be returned + * @hide + */ + private static List<Uri> getContactsFromSubscriptionProperty(int subId, String propKey, + Context context) { + String result = getSubscriptionProperty(subId, propKey, context); + if (result != null) { + try { + byte[] b = Base64.decode(result, Base64.DEFAULT); + ByteArrayInputStream bis = new ByteArrayInputStream(b); + ObjectInputStream ois = new ObjectInputStream(bis); + List<String> contacts = ArrayList.class.cast(ois.readObject()); + List<Uri> uris = new ArrayList<>(); + for (String contact : contacts) { + uris.add(Uri.parse(contact)); + } + return uris; + } catch (IOException e) { + logd("getContactsFromSubscriptionProperty IO exception"); + } catch (ClassNotFoundException e) { + logd("getContactsFromSubscriptionProperty ClassNotFound exception"); + } + } + return new ArrayList<>(); + } + + /** * Store properties associated with SubscriptionInfo in database * @param subId Subscription Id of Subscription * @param propKey Column name in SubscriptionInfo database @@ -2453,7 +2633,10 @@ public class SubscriptionManager { if (subInfo != null) { overrideConfig.mcc = subInfo.getMcc(); overrideConfig.mnc = subInfo.getMnc(); - if (overrideConfig.mnc == 0) overrideConfig.mnc = Configuration.MNC_ZERO; + if (overrideConfig.mnc == 0) { + overrideConfig.mnc = Configuration.MNC_ZERO; + cacheKey = null; + } } if (useRootLocale) { @@ -2578,14 +2761,46 @@ public class SubscriptionManager { * requested state until explicitly cleared, or the next reboot, * whichever happens first. * @throws SecurityException if the caller doesn't meet the requirements - * outlined above. + * outlined above. */ public void setSubscriptionOverrideUnmetered(int subId, boolean overrideUnmetered, @DurationMillisLong long timeoutMillis) { + setSubscriptionOverrideUnmetered(subId, overrideUnmetered, + TelephonyManager.getAllNetworkTypes(), timeoutMillis); + } + /** + * Temporarily override the billing relationship plan between a carrier and + * a specific subscriber to be considered unmetered. This will be reflected + * to apps via {@link NetworkCapabilities#NET_CAPABILITY_NOT_METERED}. + * <p> + * This method is only accessible to the following narrow set of apps: + * <ul> + * <li>The carrier app for this subscriberId, as determined by + * {@link TelephonyManager#hasCarrierPrivileges()}. + * <li>The carrier app explicitly delegated access through + * {@link CarrierConfigManager#KEY_CONFIG_PLANS_PACKAGE_OVERRIDE_STRING}. + * </ul> + * + * @param subId the subscriber this override applies to. + * @param overrideUnmetered set if the billing relationship should be + * considered unmetered. + * @param networkTypes the network types this override applies to. If no + * network types are specified, override values will be ignored. + * {@see TelephonyManager#getAllNetworkTypes()} + * @param timeoutMillis the timeout after which the requested override will + * be automatically cleared, or {@code 0} to leave in the + * requested state until explicitly cleared, or the next reboot, + * whichever happens first. + * @throws SecurityException if the caller doesn't meet the requirements + * outlined above. + */ + public void setSubscriptionOverrideUnmetered(int subId, boolean overrideUnmetered, + @NonNull @Annotation.NetworkType int[] networkTypes, + @DurationMillisLong long timeoutMillis) { final int overrideValue = overrideUnmetered ? SUBSCRIPTION_OVERRIDE_UNMETERED : 0; getNetworkPolicyManager().setSubscriptionOverride(subId, SUBSCRIPTION_OVERRIDE_UNMETERED, - overrideValue, timeoutMillis, mContext.getOpPackageName()); + overrideValue, networkTypes, timeoutMillis, mContext.getOpPackageName()); } /** @@ -2610,19 +2825,57 @@ public class SubscriptionManager { * requested state until explicitly cleared, or the next reboot, * whichever happens first. * @throws SecurityException if the caller doesn't meet the requirements - * outlined above. + * outlined above. */ public void setSubscriptionOverrideCongested(int subId, boolean overrideCongested, @DurationMillisLong long timeoutMillis) { + setSubscriptionOverrideCongested(subId, overrideCongested, + TelephonyManager.getAllNetworkTypes(), timeoutMillis); + } + + /** + * Temporarily override the billing relationship plan between a carrier and + * a specific subscriber to be considered congested. This will cause the + * device to delay certain network requests when possible, such as developer + * jobs that are willing to run in a flexible time window. + * <p> + * This method is only accessible to the following narrow set of apps: + * <ul> + * <li>The carrier app for this subscriberId, as determined by + * {@link TelephonyManager#hasCarrierPrivileges()}. + * <li>The carrier app explicitly delegated access through + * {@link CarrierConfigManager#KEY_CONFIG_PLANS_PACKAGE_OVERRIDE_STRING}. + * </ul> + * + * @param subId the subscriber this override applies to. + * @param overrideCongested set if the subscription should be considered + * congested. + * @param networkTypes the network types this override applies to. If no + * network types are specified, override values will be ignored. + * {@see TelephonyManager#getAllNetworkTypes()} + * @param timeoutMillis the timeout after which the requested override will + * be automatically cleared, or {@code 0} to leave in the + * requested state until explicitly cleared, or the next reboot, + * whichever happens first. + * @throws SecurityException if the caller doesn't meet the requirements + * outlined above. + */ + public void setSubscriptionOverrideCongested(int subId, boolean overrideCongested, + @NonNull @Annotation.NetworkType int[] networkTypes, + @DurationMillisLong long timeoutMillis) { final int overrideValue = overrideCongested ? SUBSCRIPTION_OVERRIDE_CONGESTED : 0; getNetworkPolicyManager().setSubscriptionOverride(subId, SUBSCRIPTION_OVERRIDE_CONGESTED, - overrideValue, timeoutMillis, mContext.getOpPackageName()); + overrideValue, networkTypes, timeoutMillis, mContext.getOpPackageName()); } /** * Checks whether the app with the given context is authorized to manage the given subscription * according to its metadata. * + * Only supported for embedded subscriptions (if {@link SubscriptionInfo#isEmbedded} returns + * true). To check for permissions for non-embedded subscription as well, + * {@see android.telephony.TelephonyManager#hasCarrierPrivileges}. + * * @param info The subscription to check. * @return whether the app is authorized to manage this subscription per its metadata. */ @@ -2635,6 +2888,10 @@ public class SubscriptionManager { * be authorized if it is included in the {@link android.telephony.UiccAccessRule} of the * {@link android.telephony.SubscriptionInfo} with the access status. * + * Only supported for embedded subscriptions (if {@link SubscriptionInfo#isEmbedded} returns + * true). To check for permissions for non-embedded subscription as well, + * {@see android.telephony.TelephonyManager#hasCarrierPrivileges}. + * * @param info The subscription to check. * @param packageName Package name of the app to check. * @return whether the app is authorized to manage this subscription per its access rules. @@ -3145,7 +3402,10 @@ public class SubscriptionManager { * Set uicc applications being enabled or disabled. * The value will be remembered on the subscription and will be applied whenever it's present. * If the subscription in currently present, it will also apply the setting to modem - * immediately. + * immediately (the setting in the modem will not change until the modem receives and responds + * to the request, but typically this should only take a few seconds. The user visible setting + * available from SubscriptionInfo.areUiccApplicationsEnabled() will be updated + * immediately.) * * Permissions android.Manifest.permission.MODIFY_PHONE_STATE is required * @@ -3231,6 +3491,72 @@ public class SubscriptionManager { } /** + * Set the device to device status sharing user preference for a subscription ID. The setting + * app uses this method to indicate with whom they wish to share device to device status + * information. + * @param sharing the status sharing preference + * @param subscriptionId the unique Subscription ID in database + */ + @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) + public void setDeviceToDeviceStatusSharingPreference(int subscriptionId, + @DeviceToDeviceStatusSharingPreference int sharing) { + if (VDBG) { + logd("[setDeviceToDeviceStatusSharing] + sharing: " + sharing + " subId: " + + subscriptionId); + } + setSubscriptionPropertyHelper(subscriptionId, "setDeviceToDeviceSharingStatus", + (iSub)->iSub.setDeviceToDeviceStatusSharing(sharing, subscriptionId)); + } + + /** + * Returns the user-chosen device to device status sharing preference + * @param subscriptionId Subscription id of subscription + * @return The device to device status sharing preference + */ + public @DeviceToDeviceStatusSharingPreference int getDeviceToDeviceStatusSharingPreference( + int subscriptionId) { + if (VDBG) { + logd("[getDeviceToDeviceStatusSharing] + subId: " + subscriptionId); + } + return getIntegerSubscriptionProperty(subscriptionId, D2D_STATUS_SHARING, + D2D_SHARING_DISABLED, mContext); + } + + /** + * Set the list of contacts that allow device to device status sharing for a subscription ID. + * The setting app uses this method to indicate with whom they wish to share device to device + * status information. + * @param contacts The list of contacts that allow device to device status sharing + * @param subscriptionId The unique Subscription ID in database + */ + @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) + public void setDeviceToDeviceStatusSharingContacts(int subscriptionId, + @NonNull List<Uri> contacts) { + String contactString = serializeUriLists(contacts); + if (VDBG) { + logd("[setDeviceToDeviceStatusSharingContacts] + contacts: " + contactString + + " subId: " + subscriptionId); + } + setSubscriptionPropertyHelper(subscriptionId, "setDeviceToDeviceSharingStatus", + (iSub)->iSub.setDeviceToDeviceStatusSharingContacts(serializeUriLists(contacts), + subscriptionId)); + } + + /** + * Returns the list of contacts that allow device to device status sharing. + * @param subscriptionId Subscription id of subscription + * @return The list of contacts that allow device to device status sharing + */ + public @NonNull List<Uri> getDeviceToDeviceStatusSharingContacts( + int subscriptionId) { + if (VDBG) { + logd("[getDeviceToDeviceStatusSharingContacts] + subId: " + subscriptionId); + } + return getContactsFromSubscriptionProperty(subscriptionId, + D2D_STATUS_SHARING_SELECTED_CONTACTS, mContext); + } + + /** * DO NOT USE. * This API is designed for features that are not finished at this point. Do not call this API. * @hide @@ -3359,4 +3685,71 @@ public class SubscriptionManager { sSlotIndexCache.clear(); sPhoneIdCache.clear(); } + + /** + * Called to retrieve SIM-specific settings data to be backed up. + * + * @return data in byte[] to be backed up. + * + * @hide + */ + @NonNull + @SystemApi + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public byte[] getAllSimSpecificSettingsForBackup() { + Bundle bundle = mContext.getContentResolver().call( + SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI, + GET_SIM_SPECIFIC_SETTINGS_METHOD_NAME, null, null); + return bundle.getByteArray(SubscriptionManager.KEY_SIM_SPECIFIC_SETTINGS_DATA); + } + + /** + * Called to attempt to restore the backed up sim-specific configs to device for specific sim. + * This will try to restore the data that was stored internally when {@link + * #restoreAllSimSpecificSettingsFromBackup(byte[] data)} was called during setup wizard. + * End result is SimInfoDB is modified to match any backed up configs for the requested + * inserted sim. + * + * <p> + * The {@link Uri} {@link #SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI} is notified if any SimInfoDB + * entry is updated as the result of this method call. + * + * @param iccId of the sim that a restore is requested for. + * + * @hide + */ + @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) + public void restoreSimSpecificSettingsForIccIdFromBackup(@NonNull String iccId) { + mContext.getContentResolver().call( + SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI, + RESTORE_SIM_SPECIFIC_SETTINGS_METHOD_NAME, + iccId, null); + } + + /** + * Called during setup wizard restore flow to attempt to restore the backed up sim-specific + * configs to device for all existing SIMs in SimInfoDB. Internally, it will store the backup + * data in an internal file. This file will persist on device for device's lifetime and will be + * used later on when a SIM is inserted to restore that specific SIM's settings by calling + * {@link #restoreSimSpecificSettingsForIccIdFromBackup(String iccId)}. End result is + * SimInfoDB is modified to match any backed up configs for the appropriate inserted SIMs. + * + * <p> + * The {@link Uri} {@link #SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI} is notified if any SimInfoDB + * entry is updated as the result of this method call. + * + * @param data with the sim specific configs to be backed up. + * + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) + public void restoreAllSimSpecificSettingsFromBackup(@NonNull byte[] data) { + Bundle bundle = new Bundle(); + bundle.putByteArray(KEY_SIM_SPECIFIC_SETTINGS_DATA, data); + mContext.getContentResolver().call( + SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI, + RESTORE_SIM_SPECIFIC_SETTINGS_METHOD_NAME, + null, bundle); + } } diff --git a/telephony/java/android/telephony/TelephonyDisplayInfo.java b/telephony/java/android/telephony/TelephonyDisplayInfo.java index 3d5c6aad1042..88c66acdca6f 100644 --- a/telephony/java/android/telephony/TelephonyDisplayInfo.java +++ b/telephony/java/android/telephony/TelephonyDisplayInfo.java @@ -29,6 +29,10 @@ import java.util.Objects; * information is provided in accordance with carrier policy and branding preferences; it is not * necessarily a precise or accurate representation of the current state and should be treated * accordingly. + * To be notified of changes in TelephonyDisplayInfo, use + * {@link TelephonyManager#registerTelephonyCallback} with a {@link TelephonyCallback} + * that implements {@link TelephonyCallback.DisplayInfoListener}. + * Override the onDisplayInfoChanged() method to handle the broadcast. */ public final class TelephonyDisplayInfo implements Parcelable { /** @@ -62,9 +66,26 @@ public final class TelephonyDisplayInfo implements Parcelable { * {@link TelephonyManager#NETWORK_TYPE_LTE} network and has E-UTRA-NR Dual Connectivity(EN-DC) * capability or is currently connected to the secondary * {@link TelephonyManager#NETWORK_TYPE_NR} cellular network on millimeter wave bands. + * @deprecated Use{@link #OVERRIDE_NETWORK_TYPE_NR_ADVANCED} instead. */ + @Deprecated public static final int OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE = 4; + /** + * Override network type when the device is connected NR cellular network and the data rate is + * higher than the generic 5G date rate. + * Including but not limited to + * <ul> + * <li>The device is connected to the NR cellular network on millimeter wave bands. </li> + * <li>The device is connected to the specific network which the carrier is using + * proprietary means to provide a faster overall data connection than would be otherwise + * possible. This may include using other bands unique to the carrier, or carrier + * aggregation, for example.</li> + * </ul> + * One of the use case is that UX can show a different icon, for example, "5G+" + */ + public static final int OVERRIDE_NETWORK_TYPE_NR_ADVANCED = 5; + @NetworkType private final int mNetworkType; @@ -166,6 +187,7 @@ public final class TelephonyDisplayInfo implements Parcelable { case OVERRIDE_NETWORK_TYPE_LTE_ADVANCED_PRO: return "LTE_ADV_PRO"; case OVERRIDE_NETWORK_TYPE_NR_NSA: return "NR_NSA"; case OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE: return "NR_NSA_MMWAVE"; + case OVERRIDE_NETWORK_TYPE_NR_ADVANCED: return "NR_ADVANCED"; default: return "UNKNOWN"; } } diff --git a/telephony/java/android/telephony/TelephonyFrameworkInitializer.java b/telephony/java/android/telephony/TelephonyFrameworkInitializer.java index c9540fb5f467..8308821b59ff 100644 --- a/telephony/java/android/telephony/TelephonyFrameworkInitializer.java +++ b/telephony/java/android/telephony/TelephonyFrameworkInitializer.java @@ -92,6 +92,12 @@ public class TelephonyFrameworkInitializer { ImsManager.class, context -> new ImsManager(context) ); + SystemServiceRegistry.registerContextAwareService( + Context.SMS_SERVICE, + SmsManager.class, + context -> SmsManager.getSmsManagerForContextAndSubscriptionId(context, + SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) + ); } /** @hide */ diff --git a/telephony/java/android/telephony/TelephonyLocalConnection.java b/telephony/java/android/telephony/TelephonyLocalConnection.java new file mode 100644 index 000000000000..1cab267cc817 --- /dev/null +++ b/telephony/java/android/telephony/TelephonyLocalConnection.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony; + +import java.util.UUID; + +/** + * Shim used for code in frameworks/opt/telephony to be able to call code in + * packages/services/Telephony. A singleton instance of this class is set when the phone process + * is brought up. + * @hide + */ +public class TelephonyLocalConnection { + public interface ConnectionImpl { + String getCallComposerServerUrlForHandle(int subscriptionId, UUID uuid); + } + private static ConnectionImpl sInstance; + + public static String getCallComposerServerUrlForHandle(int subscriptionId, UUID uuid) { + checkInstance(); + return sInstance.getCallComposerServerUrlForHandle(subscriptionId, uuid); + } + + private static void checkInstance() { + if (sInstance == null) { + throw new IllegalStateException("Connection impl is null!"); + } + } + + public static void setInstance(ConnectionImpl impl) { + sInstance = impl; + } +} diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 975fe41cdeaa..a262954e6547 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -23,14 +23,17 @@ import static android.provider.Telephony.Carriers.INVALID_APN_ID; import static com.android.internal.util.Preconditions.checkNotNull; import android.Manifest; +import android.annotation.BytesLong; import android.annotation.CallbackExecutor; import android.annotation.IntDef; import android.annotation.LongDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.RequiresFeature; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; +import android.annotation.StringDef; import android.annotation.SuppressAutoDoc; import android.annotation.SuppressLint; import android.annotation.SystemApi; @@ -55,9 +58,12 @@ import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; +import android.os.Looper; +import android.os.OutcomeReceiver; import android.os.ParcelFileDescriptor; +import android.os.ParcelUuid; +import android.os.Parcelable; import android.os.PersistableBundle; -import android.os.Process; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.SystemProperties; @@ -76,17 +82,19 @@ import android.telephony.Annotation.CarrierPrivilegeStatus; import android.telephony.Annotation.NetworkType; import android.telephony.Annotation.RadioPowerState; import android.telephony.Annotation.SimActivationState; +import android.telephony.Annotation.ThermalMitigationResult; import android.telephony.Annotation.UiccAppType; +import android.telephony.Annotation.UiccAppTypeExt; import android.telephony.CallForwardingInfo.CallForwardingReason; import android.telephony.VisualVoicemailService.VisualVoicemailTask; import android.telephony.data.ApnSetting; import android.telephony.data.ApnSetting.MvnoType; +import android.telephony.data.NetworkSlicingConfig; import android.telephony.emergency.EmergencyNumber; import android.telephony.emergency.EmergencyNumber.EmergencyServiceCategories; +import android.telephony.gba.UaSecurityProtocolIdentifier; import android.telephony.ims.ImsMmTelManager; import android.telephony.ims.aidl.IImsConfig; -import android.telephony.ims.aidl.IImsMmTelFeature; -import android.telephony.ims.aidl.IImsRcsFeature; import android.telephony.ims.aidl.IImsRegistration; import android.telephony.ims.feature.MmTelFeature; import android.telephony.ims.stub.ImsRegistrationImplBase; @@ -94,11 +102,13 @@ import android.text.TextUtils; import android.util.Log; import android.util.Pair; -import com.android.ims.internal.IImsServiceFeatureCallback; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.os.BackgroundThread; import com.android.internal.telephony.CellNetworkScanResult; import com.android.internal.telephony.IBooleanConsumer; +import com.android.internal.telephony.ICallForwardingInfoCallback; +import com.android.internal.telephony.IIntegerConsumer; import com.android.internal.telephony.INumberVerificationCallback; import com.android.internal.telephony.IOns; import com.android.internal.telephony.IPhoneSubInfo; @@ -113,10 +123,13 @@ import com.android.internal.telephony.RILConstants; import com.android.internal.telephony.SmsApplication; import com.android.telephony.Rlog; -import java.io.FileInputStream; import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -126,9 +139,10 @@ import java.util.Map; import java.util.Objects; import java.util.UUID; import java.util.concurrent.Executor; +import java.util.concurrent.RejectedExecutionException; import java.util.function.Consumer; -import java.util.regex.Matcher; -import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.IntStream; /** * Provides access to information about the telephony services on @@ -157,6 +171,7 @@ import java.util.regex.Pattern; public class TelephonyManager { private static final String TAG = "TelephonyManager"; + private TelephonyRegistryManager mTelephonyRegistryMgr; /** * To expand the error codes for {@link TelephonyManager#updateAvailableNetworks} and * {@link TelephonyManager#setPreferredOpportunisticDataSubscription}. @@ -172,6 +187,9 @@ public class TelephonyManager { */ public static final String MODEM_ACTIVITY_RESULT_KEY = "controller_activity"; + /** @hide */ + public static final String EXCEPTION_RESULT_KEY = "exception"; + /** * The process name of the Phone app as well as many other apps that use this process name, such * as settings and vendor components. @@ -310,6 +328,8 @@ public class TelephonyManager { private static boolean sServiceHandleCacheEnabled = true; @GuardedBy("sCacheLock") + private static ITelephony sITelephony; + @GuardedBy("sCacheLock") private static IPhoneSubInfo sIPhoneSubInfo; @GuardedBy("sCacheLock") private static ISub sISub; @@ -407,8 +427,25 @@ public class TelephonyManager { return null; } - private boolean isSystemProcess() { - return Process.myUid() == Process.SYSTEM_UID; + /** + * Post a runnable to the BackgroundThread. + * + * Used to invoke user callbacks without calling into the caller's executor from the caller's + * calling thread context, for example to provide asynchronous error information that is + * generated locally (not over a binder thread). + * + * <p>This is not necessary unless you are invoking caller's code asynchronously from within + * the caller's thread context. + * + * @param r a runnable. + */ + private static void runOnBackgroundThread(@NonNull Runnable r) { + try { + BackgroundThread.getExecutor().execute(r); + } catch (RejectedExecutionException e) { + throw new IllegalStateException( + "Failed to post a callback from the caller's thread context.", e); + } } /** @@ -544,6 +581,22 @@ public class TelephonyManager { return getPhoneCount() > 1; } + private static final int MAXIMUM_CALL_COMPOSER_PICTURE_SIZE = 80000; + + /** + * Indicates the maximum size of the call composure picture. + * + * Pictures sent via + * {@link #uploadCallComposerPicture(InputStream, String, Executor, OutcomeReceiver)} + * or {@link #uploadCallComposerPicture(Path, String, Executor, OutcomeReceiver)} must not + * exceed this size, or an error will be returned via the callback in those methods. + * + * @return Maximum file size in bytes. + */ + public static @BytesLong long getMaximumCallComposerPictureSize() { + return MAXIMUM_CALL_COMPOSER_PICTURE_SIZE; + } + // // Broadcast Intent actions // @@ -1494,6 +1547,16 @@ public class TelephonyManager { public static final int EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_ALL = 4; /** + * Used as an int value for {@link #EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE} + * to indicate that default subscription for data/sms/voice is now determined, that + * it should dismiss any dialog or pop-ups that is asking user to select default sub. + * This is used when, for example, opportunistic subscription is configured. At that + * time the primary becomes default sub there's no need to ask user to select anymore. + * @hide + */ + public static final int EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_DISMISS = 5; + + /** * Integer intent extra to be used with {@link #ACTION_PRIMARY_SUBSCRIPTION_LIST_CHANGED} * to indicate if the SIM combination in DSDS has limitation or compatible issue. * e.g. two CDMA SIMs may disrupt each other's voice call in certain scenarios. @@ -1572,177 +1635,157 @@ public class TelephonyManager { "android.telephony.extra.PHONE_IN_ECM_STATE"; /** - * <p>Broadcast Action: when data connections get redirected with validation failure. - * intended for sim/account status checks and only sent to the specified carrier app - * The intent will have the following extra values:</p> + * Broadcast action sent when a data connection is redirected with validation failure. + * + * This action is intended for sim/account status checks and only sent to the carrier apps + * specified in the carrier config for the subscription ID that's attached to this intent. + * + * The intent will have the following extra values: * <ul> - * <li>{@link #EXTRA_APN_TYPE}</li><dd>A string with the apn type.</dd> - * <li>{@link #EXTRA_APN_TYPE_INT}</li><dd>A integer with the apn type.</dd> - * <li>{@link #EXTRA_REDIRECTION_URL}</li><dd>redirection url string</dd> - * <li>subId</li><dd>Sub Id which associated the data connection failure.</dd> + * <li>{@link #EXTRA_APN_TYPE}</li><dd>An integer indicating the apn type.</dd> + * <li>{@link #EXTRA_REDIRECTION_URL}</li><dd>A string indicating the redirection url</dd> + * <li>{@link SubscriptionManager#EXTRA_SUBSCRIPTION_INDEX}</li> + * <dd>The subscription ID on which the validation failure happened.</dd> * </ul> * <p class="note">This is a protected intent that can only be sent by the system.</p> - * @hide */ - @SuppressLint("ActionValue") + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_CARRIER_SIGNAL_REDIRECTED = - "com.android.internal.telephony.CARRIER_SIGNAL_REDIRECTED"; + "android.telephony.action.CARRIER_SIGNAL_REDIRECTED"; /** - * <p>Broadcast Action: when data connections setup fails. - * intended for sim/account status checks and only sent to the specified carrier app - * The intent will have the following extra values:</p> + * Broadcast action sent when a data connection setup fails. + * + * This action is intended for sim/account status checks and only sent to the carrier apps + * specified in the carrier config for the subscription ID that's attached to this intent. + * + * The intent will have the following extra values: * <ul> - * <li>{@link #EXTRA_APN_TYPE}</li><dd>A string with the apn type.</dd> - * <li>{@link #EXTRA_APN_TYPE_INT}</li><dd>A integer with the apn type.</dd> - * <li>{@link #EXTRA_ERROR_CODE}</li><dd>A integer with dataFailCause.</dd> - * <li>subId</li><dd>Sub Id which associated the data connection failure.</dd> + * <li>{@link #EXTRA_APN_TYPE}</li><dd>An integer indicating the apn type.</dd> + * <li>{@link #EXTRA_DATA_FAIL_CAUSE}</li><dd>A integer indicating the data fail cause.</dd> + * <li>{@link SubscriptionManager#EXTRA_SUBSCRIPTION_INDEX}</li> + * <dd>The subscription ID on which the data setup failure happened.</dd> * </ul> * <p class="note">This is a protected intent that can only be sent by the system. </p> - * @hide */ - @SuppressLint("ActionValue") + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED = - "com.android.internal.telephony.CARRIER_SIGNAL_REQUEST_NETWORK_FAILED"; + "android.telephony.action.CARRIER_SIGNAL_REQUEST_NETWORK_FAILED"; /** - * <p>Broadcast Action: when pco value is available. - * intended for sim/account status checks and only sent to the specified carrier app + * Broadcast action sent when a PCO value becomes available from the modem. + * + * This action is intended for sim/account status checks and only sent to the carrier apps + * specified in the carrier config for the subscription ID that's attached to this intent. + * * The intent will have the following extra values:</p> * <ul> - * <li>{@link #EXTRA_APN_TYPE}</li><dd>A string with the apn type.</dd> - * <li>{@link #EXTRA_APN_TYPE_INT}</li><dd>A integer with the apn type.</dd> - * <li>{@link #EXTRA_APN_PROTOCOL}</li><dd>A string with the protocol of the apn connection - * (IP,IPV6, IPV4V6)</dd> - * <li>{@link #EXTRA_APN_PROTOCOL_INT}</li><dd>A integer with the protocol of the apn - * connection (IP,IPV6, IPV4V6)</dd> - * <li>{@link #EXTRA_PCO_ID}</li><dd>An integer indicating the pco id for the data.</dd> - * <li>{@link #EXTRA_PCO_VALUE}</li><dd>A byte array of pco data read from modem.</dd> - * <li>subId</li><dd>Sub Id which associated the data connection.</dd> + * <li>{@link #EXTRA_APN_TYPE}</li><dd>An integer indicating the apn type.</dd> + * <li>{@link #EXTRA_APN_PROTOCOL}</li><dd>An integer indicating the protocol of the apn + * connection</dd> + * <li>{@link #EXTRA_PCO_ID}</li><dd>An integer indicating the PCO id for the data.</dd> + * <li>{@link #EXTRA_PCO_VALUE}</li><dd>A byte array of PCO data read from modem.</dd> + * <li>{@link SubscriptionManager#EXTRA_SUBSCRIPTION_INDEX}</li> + * <dd>The subscription ID for which the PCO info was received.</dd> * </ul> * <p class="note">This is a protected intent that can only be sent by the system. </p> - * @hide */ - @SuppressLint("ActionValue") + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_CARRIER_SIGNAL_PCO_VALUE = - "com.android.internal.telephony.CARRIER_SIGNAL_PCO_VALUE"; + "android.telephony.action.CARRIER_SIGNAL_PCO_VALUE"; /** - * <p>Broadcast Action: when system default network available/unavailable with - * carrier-disabled mobile data. Intended for carrier apps to set/reset carrier actions when - * other network becomes system default network, Wi-Fi for example. + * Broadcast action sent when the availability of the system default network changes. + * + * @see ConnectivityManager#registerDefaultNetworkCallback(ConnectivityManager.NetworkCallback) + * + * This action is intended for carrier apps to set/reset carrier actions. It is only sent to the + * carrier apps specified in the carrier config for the subscription ID attached to this intent. + * * The intent will have the following extra values:</p> * <ul> * <li>{@link #EXTRA_DEFAULT_NETWORK_AVAILABLE}</li> - * <dd>A boolean indicates default network available.</dd> - * <li>subId</li><dd>Sub Id which associated the default data.</dd> + * <dd>{@code true} if the default network is now available, {@code false} otherwise.</dd> + * <li>{@link SubscriptionManager#EXTRA_SUBSCRIPTION_INDEX}</li> + * <dd>The subscription ID on which the default network availability changed.</dd> * </ul> * <p class="note">This is a protected intent that can only be sent by the system. </p> - * @hide */ - @SuppressLint("ActionValue") + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE = - "com.android.internal.telephony.CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE"; + "android.telephony.action.CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE"; /** - * <p>Broadcast Action: when framework reset all carrier actions on sim load or absent. - * intended for carrier apps clean up (clear UI e.g.) and only sent to the specified carrier app + * Broadcast action sent when carrier apps should reset their internal state. + * + * Sent when certain events such as turning on/off mobile data, removing the SIM, etc. require + * carrier apps to reset their state. + * + * This action is intended to signal carrier apps to perform cleanup operations. It is only sent + * to the carrier apps specified in the carrier config for the subscription ID attached to + * this intent. + * * The intent will have the following extra values:</p> * <ul> - * <li>subId</li><dd>Sub Id which associated the data connection failure.</dd> + * <li>{@link SubscriptionManager#EXTRA_SUBSCRIPTION_INDEX}</li> + * <dd>The subscription ID for which state should be reset.</dd> * </ul> * <p class="note">This is a protected intent that can only be sent by the system.</p> - * @hide */ - @SuppressLint("ActionValue") + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_CARRIER_SIGNAL_RESET = - "com.android.internal.telephony.CARRIER_SIGNAL_RESET"; + "android.telephony.action.CARRIER_SIGNAL_RESET"; - // CARRIER_SIGNAL_ACTION extra keys /** - * An string extra of redirected url upon {@link #ACTION_CARRIER_SIGNAL_REDIRECTED}. - * @hide + * String extra containing the redirection URL sent with + * {@link #ACTION_CARRIER_SIGNAL_REDIRECTED}. */ - @SuppressLint("ActionValue") - public static final String EXTRA_REDIRECTION_URL = "redirectionUrl"; + public static final String EXTRA_REDIRECTION_URL = "android.telephony.extra.REDIRECTION_URL"; /** - * An integer extra of error code upon {@link #ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED}. - * Check {@link DataFailCause} for all possible values. - * @hide - */ - @SuppressLint("ActionValue") - public static final String EXTRA_ERROR_CODE = "errorCode"; - - /** - * An string extra of corresponding apn type upon - * {@link #ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED}, - * {@link #ACTION_CARRIER_SIGNAL_REDIRECTED} and - * {@link #ACTION_CARRIER_SIGNAL_PCO_VALUE} broadcasts. - * @deprecated This is kept for backward compatibility reason. Use {@link #EXTRA_APN_TYPE_INT} - * instead. + * An integer extra containing the data fail cause. * - * @hide - */ - @Deprecated - @SuppressLint("ActionValue") - public static final String EXTRA_APN_TYPE = "apnType"; - - /** - * An string integer of corresponding apn type upon - * {@link #ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED}, - * {@link #ACTION_CARRIER_SIGNAL_REDIRECTED} and - * {@link #ACTION_CARRIER_SIGNAL_PCO_VALUE} broadcasts. - * Check {@link ApnSetting} TYPE_* for its values. - * @hide + * Sent with {@link #ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED}. See {@link DataFailCause} + * for a list of possible values. */ - @SuppressLint("ActionValue") - public static final String EXTRA_APN_TYPE_INT = "apnTypeInt"; + public static final String EXTRA_DATA_FAIL_CAUSE = "android.telephony.extra.DATA_FAIL_CAUSE"; /** - * An string extra with the protocol of the apn connection (IP,IPV6, IPV4V6) upon - * {@link #ACTION_CARRIER_SIGNAL_PCO_VALUE} broadcasts. - * @deprecated This is kept for backward compatibility reason. - * Use {@link #EXTRA_APN_PROTOCOL_INT} instead. + * An integer extra containing the APN type. * - * @hide + * Sent with the {@link #ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED}, + * {@link #ACTION_CARRIER_SIGNAL_REDIRECTED}, and {@link #ACTION_CARRIER_SIGNAL_PCO_VALUE} + * broadcasts. + * See the {@code TYPE_} constants in {@link ApnSetting} for a list of possible values. */ - @Deprecated - @SuppressLint("ActionValue") - public static final String EXTRA_APN_PROTOCOL = "apnProto"; + public static final String EXTRA_APN_TYPE = "android.telephony.extra.APN_TYPE"; /** - * An integer extra with the protocol of the apn connection (IP,IPV6, IPV4V6) upon - * {@link #ACTION_CARRIER_SIGNAL_PCO_VALUE} broadcasts. - * Check {@link ApnSetting} PROTOCOL_* for its values. - * @hide + * An integer extra containing the protocol of the apn connection. + * + * Sent with the {@link #ACTION_CARRIER_SIGNAL_PCO_VALUE} broadcast. + * See the {@code PROTOCOL_*} constants in {@link ApnSetting} for a list of possible values. */ - @SuppressLint("ActionValue") - public static final String EXTRA_APN_PROTOCOL_INT = "apnProtoInt"; + public static final String EXTRA_APN_PROTOCOL = "android.telephony.extra.APN_PROTOCOL"; /** - * An integer extra indicating the pco id for the data upon - * {@link #ACTION_CARRIER_SIGNAL_PCO_VALUE} broadcasts. - * @hide + * An integer extra indicating the ID for the PCO data. + * Sent with the {@link #ACTION_CARRIER_SIGNAL_PCO_VALUE} broadcast. */ - @SuppressLint("ActionValue") - public static final String EXTRA_PCO_ID = "pcoId"; + public static final String EXTRA_PCO_ID = "android.telephony.extra.PCO_ID"; /** - * An extra of byte array of pco data read from modem upon - * {@link #ACTION_CARRIER_SIGNAL_PCO_VALUE} broadcasts. - * @hide + * A byte array extra containing PCO data read from the modem. + * Sent with the {@link #ACTION_CARRIER_SIGNAL_PCO_VALUE} broadcast. */ - @SuppressLint("ActionValue") - public static final String EXTRA_PCO_VALUE = "pcoValue"; + public static final String EXTRA_PCO_VALUE = "android.telephony.extra.PCO_VALUE"; /** - * An boolean extra indicating default network available upon - * {@link #ACTION_CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE} broadcasts. - * @hide + * A boolean extra indicating the availability of the default network. + * Sent with the {@link #ACTION_CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE} broadcast. */ - @SuppressLint("ActionValue") - public static final String EXTRA_DEFAULT_NETWORK_AVAILABLE = "defaultNetworkAvailable"; + public static final String EXTRA_DEFAULT_NETWORK_AVAILABLE = + "android.telephony.extra.DEFAULT_NETWORK_AVAILABLE"; /** * <p>Broadcast Action: The emergency call state is changed. @@ -1859,14 +1902,14 @@ public class TelephonyManager { * the IMEI/SV for GSM phones. Return null if the software version is * not available. * <p> - * Requires Permission: READ_PRIVILEGED_PHONE_STATE. + * Requires Permission: READ_PHONE_STATE. * * @param slotIndex of which deviceID is returned * * @hide */ @SystemApi - @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) @Nullable public String getDeviceSoftwareVersion(int slotIndex) { ITelephony telephony = getITelephony(); @@ -1893,11 +1936,9 @@ public class TelephonyManager { * <ul> * <li>If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this * is a privileged permission that can only be granted to apps preloaded on the device. - * <li>If the calling app is the device or profile owner and has been granted the - * {@link Manifest.permission#READ_PHONE_STATE} permission. The profile owner is an app that - * owns a managed profile on the device; for more details see <a - * href="https://developer.android.com/work/managed-profiles">Work profiles</a>. - * Profile owner access is deprecated and will be removed in a future release. + * <li>If the calling app is the device owner of a fully-managed device, a profile + * owner of an organization-owned device, or their delegates (see {@link + * android.app.admin.DevicePolicyManager#getEnrollmentSpecificId()}). * <li>If the calling app has carrier privileges (see {@link #hasCarrierPrivileges}) on any * active subscription. * <li>If the calling app is the default SMS role holder (see {@link @@ -1946,11 +1987,9 @@ public class TelephonyManager { * <ul> * <li>If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this * is a privileged permission that can only be granted to apps preloaded on the device. - * <li>If the calling app is the device or profile owner and has been granted the - * {@link Manifest.permission#READ_PHONE_STATE} permission. The profile owner is an app that - * owns a managed profile on the device; for more details see <a - * href="https://developer.android.com/work/managed-profiles">Work profiles</a>. - * Profile owner access is deprecated and will be removed in a future release. + * <li>If the calling app is the device owner of a fully-managed device, a profile + * owner of an organization-owned device, or their delegates (see {@link + * android.app.admin.DevicePolicyManager#getEnrollmentSpecificId()}). * <li>If the calling app has carrier privileges (see {@link #hasCarrierPrivileges}) on any * active subscription. * <li>If the calling app is the default SMS role holder (see {@link @@ -2015,15 +2054,15 @@ public class TelephonyManager { * <ul> * <li>If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this * is a privileged permission that can only be granted to apps preloaded on the device. - * <li>If the calling app is the device or profile owner and has been granted the - * {@link Manifest.permission#READ_PHONE_STATE} permission. The profile owner is an app that - * owns a managed profile on the device; for more details see <a - * href="https://developer.android.com/work/managed-profiles">Work profiles</a>. - * Profile owner access is deprecated and will be removed in a future release. + * <li>If the calling app is the device owner of a fully-managed device, a profile + * owner of an organization-owned device, or their delegates (see {@link + * android.app.admin.DevicePolicyManager#getEnrollmentSpecificId()}). * <li>If the calling app has carrier privileges (see {@link #hasCarrierPrivileges}) on any * active subscription. * <li>If the calling app is the default SMS role holder (see {@link * RoleManager#isRoleHeld(String)}). + * <li>If the calling app has been granted the + * {@link Manifest.permission#USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER} permission. * </ul> * * <p>If the calling app does not meet one of these requirements then this method will behave @@ -2093,11 +2132,9 @@ public class TelephonyManager { * <ul> * <li>If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this * is a privileged permission that can only be granted to apps preloaded on the device. - * <li>If the calling app is the device or profile owner and has been granted the - * {@link Manifest.permission#READ_PHONE_STATE} permission. The profile owner is an app that - * owns a managed profile on the device; for more details see <a - * href="https://developer.android.com/work/managed-profiles">Work profiles</a>. - * Profile owner access is deprecated and will be removed in a future release. + * <li>If the calling app is the device owner of a fully-managed device, a profile + * owner of an organization-owned device, or their delegates (see {@link + * android.app.admin.DevicePolicyManager#getEnrollmentSpecificId()}). * <li>If the calling app has carrier privileges (see {@link #hasCarrierPrivileges}) on any * active subscription. * <li>If the calling app is the default SMS role holder (see {@link @@ -2131,11 +2168,9 @@ public class TelephonyManager { * <ul> * <li>If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this * is a privileged permission that can only be granted to apps preloaded on the device. - * <li>If the calling app is the device or profile owner and has been granted the - * {@link Manifest.permission#READ_PHONE_STATE} permission. The profile owner is an app that - * owns a managed profile on the device; for more details see <a - * href="https://developer.android.com/work/managed-profiles">Work profiles</a>. - * Profile owner access is deprecated and will be removed in a future release. + * <li>If the calling app is the device owner of a fully-managed device, a profile + * owner of an organization-owned device, or their delegates (see {@link + * android.app.admin.DevicePolicyManager#getEnrollmentSpecificId()}). * <li>If the calling app has carrier privileges (see {@link #hasCarrierPrivileges}) on any * active subscription. * <li>If the calling app is the default SMS role holder (see {@link @@ -2215,11 +2250,9 @@ public class TelephonyManager { * <ul> * <li>If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this * is a privileged permission that can only be granted to apps preloaded on the device. - * <li>If the calling app is the device or profile owner and has been granted the - * {@link Manifest.permission#READ_PHONE_STATE} permission. The profile owner is an app that - * owns a managed profile on the device; for more details see <a - * href="https://developer.android.com/work/managed-profiles">Work profiles</a>. - * Profile owner access is deprecated and will be removed in a future release. + * <li>If the calling app is the device owner of a fully-managed device, a profile + * owner of an organization-owned device, or their delegates (see {@link + * android.app.admin.DevicePolicyManager#getEnrollmentSpecificId()}). * <li>If the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). * <li>If the calling app is the default SMS role holder (see {@link * RoleManager#isRoleHeld(String)}). @@ -2252,11 +2285,9 @@ public class TelephonyManager { * <ul> * <li>If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this * is a privileged permission that can only be granted to apps preloaded on the device. - * <li>If the calling app is the device or profile owner and has been granted the - * {@link Manifest.permission#READ_PHONE_STATE} permission. The profile owner is an app that - * owns a managed profile on the device; for more details see <a - * href="https://developer.android.com/work/managed-profiles">Work profiles</a>. - * Profile owner access is deprecated and will be removed in a future release. + * <li>If the calling app is the device owner of a fully-managed device, a profile + * owner of an organization-owned device, or their delegates (see {@link + * android.app.admin.DevicePolicyManager#getEnrollmentSpecificId()}). * <li>If the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). * <li>If the calling app is the default SMS role holder (see {@link * RoleManager#isRoleHeld(String)}). @@ -2341,58 +2372,6 @@ public class TelephonyManager { } /** - * Enables location update notifications. {@link PhoneStateListener#onCellLocationChanged - * PhoneStateListener.onCellLocationChanged} will be called on location updates. - * - * @hide - */ - @RequiresPermission(android.Manifest.permission.CONTROL_LOCATION_UPDATES) - public void enableLocationUpdates() { - enableLocationUpdates(getSubId()); - } - - /** - * Enables location update notifications for a subscription. - * {@link PhoneStateListener#onCellLocationChanged - * PhoneStateListener.onCellLocationChanged} will be called on location updates. - * - * @param subId for which the location updates are enabled - * @hide - */ - @RequiresPermission(android.Manifest.permission.CONTROL_LOCATION_UPDATES) - public void enableLocationUpdates(int subId) { - try { - ITelephony telephony = getITelephony(); - if (telephony != null) - telephony.enableLocationUpdatesForSubscriber(subId); - } catch (RemoteException ex) { - } catch (NullPointerException ex) { - } - } - - /** - * Disables location update notifications. {@link PhoneStateListener#onCellLocationChanged - * PhoneStateListener.onCellLocationChanged} will be called on location updates. - * - * @hide - */ - @RequiresPermission(android.Manifest.permission.CONTROL_LOCATION_UPDATES) - public void disableLocationUpdates() { - disableLocationUpdates(getSubId()); - } - - /** @hide */ - public void disableLocationUpdates(int subId) { - try { - ITelephony telephony = getITelephony(); - if (telephony != null) - telephony.disableLocationUpdatesForSubscriber(subId); - } catch (RemoteException ex) { - } catch (NullPointerException ex) { - } - } - - /** * Returns the neighboring cell information of the device. * * @return List of NeighboringCellInfo or null if info unavailable. @@ -2592,7 +2571,8 @@ public class TelephonyManager { return PhoneConstants.PHONE_TYPE_CDMA; case RILConstants.NETWORK_MODE_LTE_ONLY: - if (getLteOnCdmaModeStatic() == PhoneConstants.LTE_ON_CDMA_TRUE) { + if (TelephonyProperties.lte_on_cdma_device().orElse( + PhoneConstants.LTE_ON_CDMA_FALSE) == PhoneConstants.LTE_ON_CDMA_TRUE) { return PhoneConstants.PHONE_TYPE_CDMA; } else { return PhoneConstants.PHONE_TYPE_GSM; @@ -2603,35 +2583,6 @@ public class TelephonyManager { } /** - * The contents of the /proc/cmdline file - */ - @UnsupportedAppUsage - private static String getProcCmdLine() - { - String cmdline = ""; - FileInputStream is = null; - try { - is = new FileInputStream("/proc/cmdline"); - byte [] buffer = new byte[2048]; - int count = is.read(buffer); - if (count > 0) { - cmdline = new String(buffer, 0, count); - } - } catch (IOException e) { - Rlog.d(TAG, "No /proc/cmdline exception=" + e); - } finally { - if (is != null) { - try { - is.close(); - } catch (IOException e) { - } - } - } - Rlog.d(TAG, "/proc/cmdline=" + cmdline); - return cmdline; - } - - /** * @return The max value for the timeout passed in {@link #requestNumberVerification}. * @hide */ @@ -2640,56 +2591,6 @@ public class TelephonyManager { return MAX_NUMBER_VERIFICATION_TIMEOUT_MILLIS; } - /** Kernel command line */ - private static final String sKernelCmdLine = getProcCmdLine(); - - /** Pattern for selecting the product type from the kernel command line */ - private static final Pattern sProductTypePattern = - Pattern.compile("\\sproduct_type\\s*=\\s*(\\w+)"); - - /** The ProductType used for LTE on CDMA devices */ - private static final String sLteOnCdmaProductType = - TelephonyProperties.lte_on_cdma_product_type().orElse(""); - - /** - * Return if the current radio is LTE on CDMA. This - * is a tri-state return value as for a period of time - * the mode may be unknown. - * - * @return {@link PhoneConstants#LTE_ON_CDMA_UNKNOWN}, {@link PhoneConstants#LTE_ON_CDMA_FALSE} - * or {@link PhoneConstants#LTE_ON_CDMA_TRUE} - * - * @hide - */ - @UnsupportedAppUsage - public static int getLteOnCdmaModeStatic() { - int retVal; - int curVal; - String productType = ""; - - curVal = TelephonyProperties.lte_on_cdma_device().orElse( - PhoneConstants.LTE_ON_CDMA_UNKNOWN); - retVal = curVal; - if (retVal == PhoneConstants.LTE_ON_CDMA_UNKNOWN) { - Matcher matcher = sProductTypePattern.matcher(sKernelCmdLine); - if (matcher.find()) { - productType = matcher.group(1); - if (sLteOnCdmaProductType.equals(productType)) { - retVal = PhoneConstants.LTE_ON_CDMA_TRUE; - } else { - retVal = PhoneConstants.LTE_ON_CDMA_FALSE; - } - } else { - retVal = PhoneConstants.LTE_ON_CDMA_FALSE; - } - } - - Rlog.d(TAG, "getLteOnCdmaMode=" + retVal + " curVal=" + curVal + - " product_type='" + productType + - "' lteOnCdmaProductType='" + sLteOnCdmaProductType + "'"); - return retVal; - } - // // // Current Network @@ -2935,9 +2836,13 @@ public class TelephonyManager { /** Current network is IWLAN */ public static final int NETWORK_TYPE_IWLAN = TelephonyProtoEnums.NETWORK_TYPE_IWLAN; // = 18. /** Current network is LTE_CA {@hide} */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public static final int NETWORK_TYPE_LTE_CA = TelephonyProtoEnums.NETWORK_TYPE_LTE_CA; // = 19. - /** Current network is NR(New Radio) 5G. */ + /** + * Current network is NR (New Radio) 5G. + * This will only be returned for 5G SA. + * For 5G NSA, the network type will be {@link #NETWORK_TYPE_LTE}. + */ public static final int NETWORK_TYPE_NR = TelephonyProtoEnums.NETWORK_TYPE_NR; // 20. private static final @NetworkType int[] NETWORK_TYPES = { @@ -2964,13 +2869,15 @@ public class TelephonyManager { }; /** - * Return a collection of all network types - * @return network types + * Returns an array of all valid network types. + * + * @return An integer array containing all valid network types in no particular order. * * @hide */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) public static @NonNull @NetworkType int[] getAllNetworkTypes() { - return NETWORK_TYPES; + return NETWORK_TYPES.clone(); } /** @@ -3247,6 +3154,10 @@ public class TelephonyManager { return NETWORK_TYPE_BITMASK_LTE_CA; case NETWORK_TYPE_NR: return NETWORK_TYPE_BITMASK_NR; + case NETWORK_TYPE_IWLAN: + return NETWORK_TYPE_BITMASK_IWLAN; + case NETWORK_TYPE_IDEN: + return (1 << (NETWORK_TYPE_IDEN - 1)); default: return NETWORK_TYPE_BITMASK_UNKNOWN; } @@ -3858,11 +3769,9 @@ public class TelephonyManager { * <ul> * <li>If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this * is a privileged permission that can only be granted to apps preloaded on the device. - * <li>If the calling app is the device or profile owner and has been granted the - * {@link Manifest.permission#READ_PHONE_STATE} permission. The profile owner is an app that - * owns a managed profile on the device; for more details see <a - * href="https://developer.android.com/work/managed-profiles">Work profiles</a>. - * Profile owner access is deprecated and will be removed in a future release. + * <li>If the calling app is the device owner of a fully-managed device, a profile + * owner of an organization-owned device, or their delegates (see {@link + * android.app.admin.DevicePolicyManager#getEnrollmentSpecificId()}). * <li>If the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). * <li>If the calling app is the default SMS role holder (see {@link * RoleManager#isRoleHeld(String)}). @@ -3896,11 +3805,9 @@ public class TelephonyManager { * <ul> * <li>If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this * is a privileged permission that can only be granted to apps preloaded on the device. - * <li>If the calling app is the device or profile owner and has been granted the - * {@link Manifest.permission#READ_PHONE_STATE} permission. The profile owner is an app that - * owns a managed profile on the device; for more details see <a - * href="https://developer.android.com/work/managed-profiles">Work profiles</a>. - * Profile owner access is deprecated and will be removed in a future release. + * <li>If the calling app is the device owner of a fully-managed device, a profile + * owner of an organization-owned device, or their delegates (see {@link + * android.app.admin.DevicePolicyManager#getEnrollmentSpecificId()}). * <li>If the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). * <li>If the calling app is the default SMS role holder (see {@link * RoleManager#isRoleHeld(String)}). @@ -4150,14 +4057,14 @@ public class TelephonyManager { * <ul> * <li>If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this * is a privileged permission that can only be granted to apps preloaded on the device. - * <li>If the calling app is the device or profile owner and has been granted the - * {@link Manifest.permission#READ_PHONE_STATE} permission. The profile owner is an app that - * owns a managed profile on the device; for more details see <a - * href="https://developer.android.com/work/managed-profiles">Work profiles</a>. - * Profile owner access is deprecated and will be removed in a future release. + * <li>If the calling app is the device owner of a fully-managed device, a profile + * owner of an organization-owned device, or their delegates (see {@link + * android.app.admin.DevicePolicyManager#getEnrollmentSpecificId()}). * <li>If the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). * <li>If the calling app is the default SMS role holder (see {@link * RoleManager#isRoleHeld(String)}). + * <li>If the calling app has been granted the + * {@link Manifest.permission#USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER} permission. * </ul> * * <p>If the calling app does not meet one of these requirements then this method will behave @@ -4182,33 +4089,8 @@ public class TelephonyManager { * for a subscription. * Return null if it is unavailable. * - * <p>Starting with API level 29, persistent device identifiers are guarded behind additional - * restrictions, and apps are recommended to use resettable identifiers (see <a - * href="/training/articles/user-data-ids">Best practices for unique identifiers</a>). This - * method can be invoked if one of the following requirements is met: - * <ul> - * <li>If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this - * is a privileged permission that can only be granted to apps preloaded on the device. - * <li>If the calling app is the device or profile owner and has been granted the - * {@link Manifest.permission#READ_PHONE_STATE} permission. The profile owner is an app that - * owns a managed profile on the device; for more details see <a - * href="https://developer.android.com/work/managed-profiles">Work profiles</a>. - * Profile owner access is deprecated and will be removed in a future release. - * <li>If the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). - * <li>If the calling app is the default SMS role holder (see {@link - * RoleManager#isRoleHeld(String)}). - * </ul> - * - * <p>If the calling app does not meet one of these requirements then this method will behave - * as follows: - * - * <ul> - * <li>If the calling app's target SDK is API level 28 or lower and the app has the - * READ_PHONE_STATE permission then null is returned.</li> - * <li>If the calling app's target SDK is API level 28 or lower and the app does not have - * the READ_PHONE_STATE permission, or if the calling app is targeting API level 29 or - * higher, then a SecurityException is thrown.</li> - * </ul> + * See {@link #getSubscriberId()} for details on the required permissions and behavior + * when the caller does not hold sufficient permissions. * * @param subId whose subscriber id is returned * @hide @@ -4298,23 +4180,16 @@ public class TelephonyManager { try { IPhoneSubInfo info = getSubscriberInfoService(); if (info == null) { - Rlog.e(TAG, "IMSI error: Subscriber Info is null"); - if (!isSystemProcess()) { - throw new RuntimeException("IMSI error: Subscriber Info is null"); - } - return; + throw new RuntimeException("IMSI error: Subscriber Info is null"); } int subId = getSubId(SubscriptionManager.getDefaultDataSubscriptionId()); info.resetCarrierKeysForImsiEncryption(subId, mContext.getOpPackageName()); } catch (RemoteException ex) { - Rlog.e(TAG, "getCarrierInfoForImsiEncryption RemoteException" + ex); - if (!isSystemProcess()) { - ex.rethrowAsRuntimeException(); - } + Rlog.e(TAG, "Telephony#getCarrierInfoForImsiEncryption RemoteException" + ex); } } - /** + /** * @param keyAvailability bitmask that defines the availabilty of keys for a type. * @param keyType the key type which is being checked. (WLAN, EPDG) * @return true if the digit at position keyType is 1, else false. @@ -4375,6 +4250,363 @@ public class TelephonyManager { } /** + * Exception that may be supplied to the callback in {@link #uploadCallComposerPicture} if + * something goes awry. + */ + public static class CallComposerException extends Exception { + /** + * Used internally only, signals success of the upload to the carrier. + * @hide + */ + public static final int SUCCESS = -1; + /** + * Indicates that an unknown error was encountered when uploading the call composer picture. + * + * Clients that encounter this error should retry the upload. + */ + public static final int ERROR_UNKNOWN = 0; + + /** + * Indicates that the phone process died or otherwise became unavailable while uploading the + * call composer picture. + * + * Clients that encounter this error should retry the upload. + */ + public static final int ERROR_REMOTE_END_CLOSED = 1; + + /** + * Indicates that the file or stream supplied exceeds the size limit defined in + * {@link #getMaximumCallComposerPictureSize()}. + * + * Clients that encounter this error should retry the upload after reducing the size of the + * picture. + */ + public static final int ERROR_FILE_TOO_LARGE = 2; + + /** + * Indicates that the device failed to authenticate with the carrier when uploading the + * picture. + * + * Clients that encounter this error should not retry the upload unless a reboot or radio + * reset has been performed in the interim. + */ + public static final int ERROR_AUTHENTICATION_FAILED = 3; + + /** + * Indicates that the {@link InputStream} passed to {@link #uploadCallComposerPicture} + * was closed. + * + * The caller should retry if this error is encountered, and be sure to not close the stream + * before the callback is called this time. + */ + public static final int ERROR_INPUT_CLOSED = 4; + + /** + * Indicates that an {@link IOException} was encountered while reading the picture. + * + * The offending {@link IOException} will be available via {@link #getIOException()}. + * Clients should use the contents of the exception to determine whether a retry is + * warranted. + */ + public static final int ERROR_IO_EXCEPTION = 5; + + /** + * Indicates that the device is currently not connected to a network that's capable of + * reaching a carrier's RCS servers. + * + * Clients should prompt the user to remedy the issue by moving to an area with better + * signal, by connecting to a different network, or to retry at another time. + */ + public static final int ERROR_NETWORK_UNAVAILABLE = 6; + + /** @hide */ + @IntDef(prefix = {"ERROR_"}, value = { + ERROR_UNKNOWN, + ERROR_REMOTE_END_CLOSED, + ERROR_FILE_TOO_LARGE, + ERROR_AUTHENTICATION_FAILED, + ERROR_INPUT_CLOSED, + ERROR_IO_EXCEPTION, + ERROR_NETWORK_UNAVAILABLE, + }) + + @Retention(RetentionPolicy.SOURCE) + public @interface CallComposerError {} + + private final int mErrorCode; + private final IOException mIOException; + + public CallComposerException(@CallComposerError int errorCode, + @Nullable IOException ioException) { + mErrorCode = errorCode; + mIOException = ioException; + } + + /** + * Fetches the error code associated with this exception. + * @return An error code. + */ + public @CallComposerError int getErrorCode() { + return mErrorCode; + } + + /** + * Fetches the {@link IOException} that caused the error. + */ + // Follows the naming of IOException + @SuppressLint("AcronymName") + public @Nullable IOException getIOException() { + return mIOException; + } + } + + /** @hide */ + public static final String KEY_CALL_COMPOSER_PICTURE_HANDLE = "call_composer_picture_handle"; + + /** + * Uploads a picture to the carrier network for use with call composer. + * + * @see #uploadCallComposerPicture(InputStream, String, Executor, OutcomeReceiver) + * @param pictureToUpload Path to a local file containing the picture to upload. + * @param contentType The MIME type of the picture you're uploading (e.g. image/jpeg) + * @param executor The {@link Executor} on which the {@code pictureToUpload} file will be read + * from disk, as well as on which {@code callback} will be called. + * @param callback A callback called when the upload operation terminates, either in success + * or in error. + */ + public void uploadCallComposerPicture(@NonNull Path pictureToUpload, + @NonNull String contentType, + @CallbackExecutor @NonNull Executor executor, + @NonNull OutcomeReceiver<ParcelUuid, CallComposerException> callback) { + Objects.requireNonNull(pictureToUpload); + Objects.requireNonNull(executor); + Objects.requireNonNull(callback); + + // Do the role check now so that we can quit early if needed -- there's an additional + // permission check on the other side of the binder call as well. + RoleManager rm = mContext.getSystemService(RoleManager.class); + if (!rm.isRoleHeld(RoleManager.ROLE_DIALER)) { + throw new SecurityException("You must hold RoleManager.ROLE_DIALER to do this"); + } + + executor.execute(() -> { + try { + if (Looper.getMainLooper().isCurrentThread()) { + Log.w(TAG, "Uploading call composer picture on main thread!" + + " hic sunt dracones!"); + } + long size = Files.size(pictureToUpload); + if (size > getMaximumCallComposerPictureSize()) { + callback.onError(new CallComposerException( + CallComposerException.ERROR_FILE_TOO_LARGE, null)); + return; + } + InputStream fileStream = Files.newInputStream(pictureToUpload); + try { + uploadCallComposerPicture(fileStream, contentType, executor, + new OutcomeReceiver<ParcelUuid, CallComposerException>() { + @Override + public void onResult(ParcelUuid result) { + try { + fileStream.close(); + } catch (IOException e) { + // ignore + Log.e(TAG, "Error closing file input stream when" + + " uploading call composer pic"); + } + callback.onResult(result); + } + + @Override + public void onError(CallComposerException error) { + try { + fileStream.close(); + } catch (IOException e) { + // ignore + Log.e(TAG, "Error closing file input stream when" + + " uploading call composer pic"); + } + callback.onError(error); + } + }); + } catch (Exception e) { + Log.e(TAG, "Got exception calling into stream-version of" + + " uploadCallComposerPicture: " + e); + try { + fileStream.close(); + } catch (IOException e1) { + // ignore + Log.e(TAG, "Error closing file input stream when uploading" + + " call composer pic"); + } + } + } catch (IOException e) { + Log.e(TAG, "IOException when uploading call composer pic:" + e); + callback.onError( + new CallComposerException(CallComposerException.ERROR_IO_EXCEPTION, e)); + } + }); + + } + + /** + * Uploads a picture to the carrier network for use with call composer. + * + * This method allows a dialer app to upload a picture to the carrier network that can then + * later be attached to an outgoing call. In order to attach the picture to a call, use the + * {@link ParcelUuid} returned from {@code callback} upon successful upload as the value to + * {@link TelecomManager#EXTRA_OUTGOING_PICTURE}. + * + * This functionality is only available to the app filling the {@link RoleManager#ROLE_DIALER} + * role on the device. + * + * This functionality is only available when + * {@link CarrierConfigManager#KEY_SUPPORTS_CALL_COMPOSER_BOOL} is set to {@code true} in the + * bundle returned from {@link #getCarrierConfig()}. + * + * @param pictureToUpload An {@link InputStream} that supplies the bytes representing the + * picture to upload. The client bears responsibility for closing this + * stream after {@code callback} is called with success or failure. + * + * Additionally, if the stream supplies more bytes than the return value + * of {@link #getMaximumCallComposerPictureSize()}, the upload will be + * aborted and the callback will be called with an exception containing + * {@link CallComposerException#ERROR_FILE_TOO_LARGE}. + * @param contentType The MIME type of the picture you're uploading (e.g. image/jpeg). The list + * of acceptable content types can be found at 3GPP TS 26.141 sections + * 4.2 and 4.3. + * @param executor The {@link Executor} on which the {@code pictureToUpload} stream will be + * read, as well as on which the callback will be called. + * @param callback A callback called when the upload operation terminates, either in success + * or in error. + */ + public void uploadCallComposerPicture(@NonNull InputStream pictureToUpload, + @NonNull String contentType, + @CallbackExecutor @NonNull Executor executor, + @NonNull OutcomeReceiver<ParcelUuid, CallComposerException> callback) { + Objects.requireNonNull(pictureToUpload); + Objects.requireNonNull(executor); + Objects.requireNonNull(callback); + + ITelephony telephony = getITelephony(); + if (telephony == null) { + throw new IllegalStateException("Telephony service not available."); + } + + ParcelFileDescriptor writeFd; + ParcelFileDescriptor readFd; + try { + ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createReliablePipe(); + writeFd = pipe[1]; + readFd = pipe[0]; + } catch (IOException e) { + executor.execute(() -> callback.onError( + new CallComposerException(CallComposerException.ERROR_IO_EXCEPTION, e))); + return; + } + + OutputStream output = new ParcelFileDescriptor.AutoCloseOutputStream(writeFd); + + try { + telephony.uploadCallComposerPicture(getSubId(), mContext.getOpPackageName(), + contentType, readFd, new ResultReceiver(null) { + @Override + protected void onReceiveResult(int resultCode, Bundle result) { + if (resultCode != CallComposerException.SUCCESS) { + executor.execute(() -> callback.onError( + new CallComposerException(resultCode, null))); + return; + } + ParcelUuid resultUuid = + result.getParcelable(KEY_CALL_COMPOSER_PICTURE_HANDLE); + if (resultUuid == null) { + Log.e(TAG, "Got null uuid without an error" + + " while uploading call composer pic"); + executor.execute(() -> callback.onError( + new CallComposerException( + CallComposerException.ERROR_UNKNOWN, null))); + return; + } + executor.execute(() -> callback.onResult(resultUuid)); + } + }); + } catch (RemoteException e) { + Log.e(TAG, "Remote exception uploading call composer pic:" + e); + e.rethrowAsRuntimeException(); + } + + executor.execute(() -> { + if (Looper.getMainLooper().isCurrentThread()) { + Log.w(TAG, "Uploading call composer picture on main thread!" + + " hic sunt dracones!"); + } + + int totalBytesRead = 0; + byte[] buffer = new byte[16 * 1024]; + try { + while (true) { + int numRead; + try { + numRead = pictureToUpload.read(buffer); + } catch (IOException e) { + Log.e(TAG, "IOException reading from input while uploading pic: " + e); + // Most likely, this was because the stream was closed. We have no way to + // tell though. + callback.onError(new CallComposerException( + CallComposerException.ERROR_INPUT_CLOSED, e)); + try { + writeFd.closeWithError("input closed"); + } catch (IOException e1) { + // log and ignore + Log.e(TAG, "Error closing fd pipe: " + e1); + } + break; + } + + if (numRead < 0) { + break; + } + + totalBytesRead += numRead; + if (totalBytesRead > getMaximumCallComposerPictureSize()) { + Log.e(TAG, "Read too many bytes from call composer pic stream: " + + totalBytesRead); + try { + callback.onError(new CallComposerException( + CallComposerException.ERROR_FILE_TOO_LARGE, null)); + writeFd.closeWithError("too large"); + } catch (IOException e1) { + // log and ignore + Log.e(TAG, "Error closing fd pipe: " + e1); + } + break; + } + + try { + output.write(buffer, 0, numRead); + } catch (IOException e) { + callback.onError(new CallComposerException( + CallComposerException.ERROR_REMOTE_END_CLOSED, e)); + try { + writeFd.closeWithError("remote end closed"); + } catch (IOException e1) { + // log and ignore + Log.e(TAG, "Error closing fd pipe: " + e1); + } + break; + } + } + } finally { + try { + output.close(); + } catch (IOException e) { + // Ignore -- we might've already closed it. + } + } + }); + } + + /** * Returns the Group Identifier Level1 for a GSM phone. * Return null if it is unavailable. * @@ -4619,11 +4851,11 @@ public class TelephonyManager { /** * Return the set of IMSIs that should be considered "merged together" for data usage - * purposes. Unlike {@link #getMergedSubscriberIds()} this API merge IMSIs based on - * subscription grouping: IMSI of those in the same group will all be returned. - * Return the current IMSI if there is no subscription group. - * - * <p>Requires the calling app to have READ_PRIVILEGED_PHONE_STATE permission. + * purposes. This API merges IMSIs based on subscription grouping: IMSI of those in the same + * group will all be returned. + * Return the current IMSI if there is no subscription group, see + * {@link SubscriptionManager#createSubscriptionGroup(List)} for the definition of a group, + * otherwise return an empty array if there is a failure. * * @hide */ @@ -4636,7 +4868,6 @@ public class TelephonyManager { return telephony.getMergedImsisFromGroup(getSubId(), getOpPackageName()); } } catch (RemoteException ex) { - } catch (NullPointerException ex) { } return new String[0]; } @@ -4783,7 +5014,7 @@ public class TelephonyManager { * be implemented instead. */ @SystemApi - @SuppressLint("Doclava125") + @SuppressLint("RequiresPermission") public void setVisualVoicemailEnabled(PhoneAccountHandle phoneAccountHandle, boolean enabled){ } @@ -4798,7 +5029,7 @@ public class TelephonyManager { */ @SystemApi @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) - @SuppressLint("Doclava125") + @SuppressLint("RequiresPermission") public boolean isVisualVoicemailEnabled(PhoneAccountHandle phoneAccountHandle){ return false; } @@ -4817,7 +5048,7 @@ public class TelephonyManager { * @hide */ @SystemApi - @SuppressLint("Doclava125") + @SuppressLint("RequiresPermission") @Nullable public Bundle getVisualVoicemailSettings(){ try { @@ -5349,17 +5580,11 @@ public class TelephonyManager { try { final ITelephony telephony = getITelephony(); if (telephony == null) { - if (!isSystemProcess()) { - throw new RuntimeException("Telephony service unavailable"); - } return; } telephony.sendDialerSpecialCode(mContext.getOpPackageName(), inputCode); } catch (RemoteException ex) { - // This could happen if binder process crashes. - if (!isSystemProcess()) { - ex.rethrowAsRuntimeException(); - } + Rlog.e(TAG, "Telephony#sendDialerSpecialCode RemoteException" + ex); } } @@ -5461,9 +5686,20 @@ public class TelephonyManager { * Note: The call state returned via this method may differ from what is reported by * {@link PhoneStateListener#onCallStateChanged(int, String)}, as that callback only considers * Telephony (mobile) calls. + * <p> + * Requires Permission: + * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} for applications + * targeting API level 31+. * * @return the current call state. + * @deprecated Use {@link #getCallStateForSubscription} to retrieve the call state for a + * specific telephony subscription (which allows carrier privileged apps), + * {@link TelephonyCallback.CallStateListener} for real-time call state updates, or + * {@link TelecomManager#isInCall()}, which supplies an aggregate "in call" state for the entire + * device. */ + @RequiresPermission(value = android.Manifest.permission.READ_PHONE_STATE, conditional = true) + @Deprecated public @CallState int getCallState() { if (mContext != null) { TelecomManager telecomManager = mContext.getSystemService(TelecomManager.class); @@ -5475,19 +5711,48 @@ public class TelephonyManager { } /** + * Retrieve the call state for a specific subscription that was specified when this + * TelephonyManager instance was created. + * <p>Requires Permission: + * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} or that the calling + * application has carrier privileges (see {@link #hasCarrierPrivileges}). + * @see TelephonyManager#createForSubscriptionId(int) + * @see TelephonyManager#createForPhoneAccountHandle(PhoneAccountHandle) + * @return The call state of the subscription associated with this TelephonyManager instance. + */ + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + public @CallState int getCallStateForSubscription() { + return getCallState(getSubId()); + } + + /** * Returns the Telephony call state for calls on a specific subscription. * <p> * Note: This method considers ONLY telephony/mobile calls, where {@link #getCallState()} * considers the state of calls from other {@link android.telecom.ConnectionService} * implementations. + * <p> + * Requires Permission: + * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} for applications + * targeting API level 31+ or that the calling application has carrier privileges + * (see {@link #hasCarrierPrivileges()}). * * @param subId the subscription to check call state for. * @hide */ @UnsupportedAppUsage + @RequiresPermission(value = android.Manifest.permission.READ_PHONE_STATE, conditional = true) public @CallState int getCallState(int subId) { - int phoneId = SubscriptionManager.getPhoneId(subId); - return getCallStateForSlot(phoneId); + ITelephony telephony = getITelephony(); + if (telephony == null) { + return CALL_STATE_IDLE; + } + try { + return telephony.getCallStateForSubscription(subId, mContext.getPackageName(), + mContext.getAttributionTag()); + } catch (RemoteException e) { + return CALL_STATE_IDLE; + } } /** @@ -5504,22 +5769,28 @@ public class TelephonyManager { * Note: This method considers ONLY telephony/mobile calls, where {@link #getCallState()} * considers the state of calls from other {@link android.telecom.ConnectionService} * implementations. + * <p> + * Requires Permission: + * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} for applications + * targeting API level 31+ or that the calling application has carrier privileges + * (see {@link #hasCarrierPrivileges()}). * * @param slotIndex the SIM slot index to check call state for. * @hide */ + @RequiresPermission(value = android.Manifest.permission.READ_PHONE_STATE, conditional = true) public @CallState int getCallStateForSlot(int slotIndex) { try { + int[] subId = SubscriptionManager.getSubId(slotIndex); ITelephony telephony = getITelephony(); - if (telephony == null) + if (telephony == null || subId == null || subId.length == 0) { return CALL_STATE_IDLE; - return telephony.getCallStateForSlot(slotIndex); - } catch (RemoteException ex) { + } + return telephony.getCallStateForSubscription(subId[0], mContext.getPackageName(), + mContext.getAttributionTag()); + } catch (RemoteException | NullPointerException ex) { // the phone process is restarting. return CALL_STATE_IDLE; - } catch (NullPointerException ex) { - // the phone process is restarting. - return CALL_STATE_IDLE; } } @@ -5634,29 +5905,38 @@ public class TelephonyManager { } /** - * Convert data state to string - * - * @return The data state in string format. * @hide */ - public static String dataStateToString(@DataState int state) { - switch (state) { - case DATA_DISCONNECTED: return "DISCONNECTED"; - case DATA_CONNECTING: return "CONNECTING"; - case DATA_CONNECTED: return "CONNECTED"; - case DATA_SUSPENDED: return "SUSPENDED"; - case DATA_DISCONNECTING: return "DISCONNECTING"; - } - return "UNKNOWN(" + state + ")"; - } - - /** - * @hide - */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) private ITelephony getITelephony() { - return ITelephony.Stub.asInterface(TelephonyFrameworkInitializer - .getTelephonyServiceManager().getTelephonyServiceRegisterer().get()); + // Keeps cache disabled until test fixes are checked into AOSP. + if (!sServiceHandleCacheEnabled) { + return ITelephony.Stub.asInterface( + TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getTelephonyServiceRegisterer() + .get()); + } + + if (sITelephony == null) { + ITelephony temp = ITelephony.Stub.asInterface( + TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getTelephonyServiceRegisterer() + .get()); + synchronized (sCacheLock) { + if (sITelephony == null && temp != null) { + try { + sITelephony = temp; + sITelephony.asBinder().linkToDeath(sServiceDeath, 0); + } catch (Exception e) { + // something has gone horribly wrong + sITelephony = null; + } + } + } + } + return sITelephony; } private IOns getIOns() { @@ -5706,7 +5986,9 @@ public class TelephonyManager { * @param events The telephony state(s) of interest to the listener, * as a bitwise-OR combination of {@link PhoneStateListener} * LISTEN_ flags. + * @deprecated Use {@link #registerTelephonyCallback(Executor, TelephonyCallback)}. */ + @Deprecated public void listen(PhoneStateListener listener, int events) { if (mContext == null) return; boolean notifyNow = (getITelephony() != null); @@ -5714,34 +5996,85 @@ public class TelephonyManager { (TelephonyRegistryManager) mContext.getSystemService(Context.TELEPHONY_REGISTRY_SERVICE); if (telephonyRegistry != null) { - telephonyRegistry.listenForSubscriber(mSubId, getOpPackageName(), getAttributionTag(), - listener, events, notifyNow); + telephonyRegistry.listenFromListener(mSubId, getOpPackageName(), + getAttributionTag(), listener, events, notifyNow); } else { Rlog.w(TAG, "telephony registry not ready."); } } + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"ERI_"}, value = { + ERI_ON, + ERI_OFF, + ERI_FLASH + }) + public @interface EriIconIndex {} + + /** + * ERI (Enhanced Roaming Indicator) is ON i.e value 0 defined by + * 3GPP2 C.R1001-H v1.0 Table 8.1-1. + */ + public static final int ERI_ON = 0; + + /** + * ERI (Enhanced Roaming Indicator) is OFF i.e value 1 defined by + * 3GPP2 C.R1001-H v1.0 Table 8.1-1. + */ + public static final int ERI_OFF = 1; + + /** + * ERI (Enhanced Roaming Indicator) is FLASH i.e value 2 defined by + * 3GPP2 C.R1001-H v1.0 Table 8.1-1. + */ + public static final int ERI_FLASH = 2; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"ERI_ICON_MODE_"}, value = { + ERI_ICON_MODE_NORMAL, + ERI_ICON_MODE_FLASH + }) + public @interface EriIconMode {} + /** - * Get the CDMA ERI (Enhanced Roaming Indicator) information + * ERI (Enhanced Roaming Indicator) icon mode is normal. This constant represents that + * the ERI icon should be displayed normally. * - * Returns {@link android.telephony#CdmaEriInformation} + * Note: ERI is defined 3GPP2 C.R1001-H Table 8.1-1 + * @hide + */ + public static final int ERI_ICON_MODE_NORMAL = 0; + + /** + * ERI (Enhanced Roaming Indicator) icon mode flash. This constant represents that + * the ERI icon should be flashing. * + * Note: ERI is defined 3GPP2 C.R1001-H Table 8.1-1 + * @hide + */ + public static final int ERI_ICON_MODE_FLASH = 1; + + /** + * Returns the CDMA ERI icon display number. The number is assigned by + * 3GPP2 C.R1001-H v1.0 Table 8.1-1. Additionally carriers define their own ERI display numbers. + * Defined values are {@link #ERI_ON}, {@link #ERI_OFF}, and {@link #ERI_FLASH}. * @hide */ + @SystemApi @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) - @NonNull - public CdmaEriInformation getCdmaEriInformation() { - return new CdmaEriInformation( - getCdmaEriIconIndex(getSubId()), getCdmaEriIconMode(getSubId())); + public @EriIconIndex int getCdmaEnhancedRoamingIndicatorDisplayNumber() { + return getCdmaEriIconIndex(getSubId()); } /** - * Returns the CDMA ERI icon index to display for a subscription + * Returns the CDMA ERI icon index to display for a subscription. * @hide */ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @UnsupportedAppUsage - public int getCdmaEriIconIndex(int subId) { + public @EriIconIndex int getCdmaEriIconIndex(int subId) { try { ITelephony telephony = getITelephony(); if (telephony == null) @@ -5765,7 +6098,7 @@ public class TelephonyManager { */ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @UnsupportedAppUsage - public int getCdmaEriIconMode(int subId) { + public @EriIconMode int getCdmaEriIconMode(int subId) { try { ITelephony telephony = getITelephony(); if (telephony == null) @@ -5937,7 +6270,7 @@ public class TelephonyManager { /** * Error response to - * {@link android.telephony.TelephonyManager#requestCellInfoUpdate requestCellInfoUpdate()}. + * {@link TelephonyManager#requestCellInfoUpdate requestCellInfoUpdate()}. * * Invoked when an error condition prevents updated {@link CellInfo} from being fetched * and returned from the modem. Callers of requestCellInfoUpdate() should override this @@ -5955,6 +6288,20 @@ public class TelephonyManager { }; /** + * Used for checking if the target SDK version for the current process is S or above. + * + * <p> Applies to the following methods: + * {@link #requestCellInfoUpdate}, + * {@link #setPreferredOpportunisticDataSubscription}, + * {@link #updateAvailableNetworks}, + * requestNumberVerification(), + * setSimPowerStateForSlot(), + */ + @ChangeId + @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.R) + private static final long NULL_TELEPHONY_THROW_NO_CB = 182185642L; + + /** * Requests all available cell information from the current subscription for observed * camped/registered, serving, and neighboring cells. * @@ -5974,7 +6321,14 @@ public class TelephonyManager { @NonNull @CallbackExecutor Executor executor, @NonNull CellInfoCallback callback) { try { ITelephony telephony = getITelephony(); - if (telephony == null) return; + if (telephony == null) { + if (Compatibility.isChangeEnabled(NULL_TELEPHONY_THROW_NO_CB)) { + throw new IllegalStateException("Telephony is null"); + } else { + return; + } + } + telephony.requestCellInfoUpdate( getSubId(), new ICellInfoCallback.Stub() { @@ -6001,6 +6355,8 @@ public class TelephonyManager { } }, getOpPackageName(), getAttributionTag()); } catch (RemoteException ex) { + runOnBackgroundThread(() -> executor.execute( + () -> callback.onError(CellInfoCallback.ERROR_MODEM_ERROR, ex))); } } @@ -6028,7 +6384,14 @@ public class TelephonyManager { @NonNull @CallbackExecutor Executor executor, @NonNull CellInfoCallback callback) { try { ITelephony telephony = getITelephony(); - if (telephony == null) return; + if (telephony == null) { + if (Compatibility.isChangeEnabled(NULL_TELEPHONY_THROW_NO_CB)) { + throw new IllegalStateException("Telephony is null"); + } else { + return; + } + } + telephony.requestCellInfoUpdateWithWorkSource( getSubId(), new ICellInfoCallback.Stub() { @@ -6056,6 +6419,8 @@ public class TelephonyManager { } }, getOpPackageName(), getAttributionTag(), workSource); } catch (RemoteException ex) { + runOnBackgroundThread(() -> executor.execute( + () -> callback.onError(CellInfoCallback.ERROR_MODEM_ERROR, ex))); } } @@ -6793,7 +7158,7 @@ public class TelephonyManager { * * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public boolean nvResetConfig(int resetType) { try { ITelephony telephony = getITelephony(); @@ -7022,14 +7387,21 @@ public class TelephonyManager { try { ITelephony telephony = getITelephony(); - if (telephony != null) { - telephony.requestNumberVerification(range, timeoutMillis, internalCallback, - getOpPackageName()); + if (telephony == null) { + if (Compatibility.isChangeEnabled(NULL_TELEPHONY_THROW_NO_CB)) { + throw new IllegalStateException("Telephony is null"); + } else { + return; + } } + + telephony.requestNumberVerification(range, timeoutMillis, internalCallback, + getOpPackageName()); } catch (RemoteException ex) { Rlog.e(TAG, "requestNumberVerification RemoteException", ex); - executor.execute(() -> - callback.onVerificationFailed(NumberVerificationCallback.REASON_UNSPECIFIED)); + runOnBackgroundThread(() -> executor.execute( + () -> callback.onVerificationFailed( + NumberVerificationCallback.REASON_UNSPECIFIED))); } } @@ -7062,7 +7434,7 @@ public class TelephonyManager { * @return The value at the given index of settings. * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public static int getIntAtIndex(android.content.ContentResolver cr, String name, int index) throws android.provider.Settings.SettingNotFoundException { @@ -7095,7 +7467,7 @@ public class TelephonyManager { * @return true if the value was set, false on database errors * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public static boolean putIntAtIndex(android.content.ContentResolver cr, String name, int index, int value) { String data = ""; @@ -7167,7 +7539,7 @@ public class TelephonyManager { * * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public static String getTelephonyProperty(String property, String defaultVal) { String propVal = SystemProperties.get(property); return TextUtils.isEmpty(propVal) ? defaultVal : propVal; @@ -7229,6 +7601,8 @@ public class TelephonyManager { } } + /** UICC application type is unknown or not specified */ + public static final int APPTYPE_UNKNOWN = PhoneConstants.APPTYPE_UNKNOWN; /** UICC application type is SIM */ public static final int APPTYPE_SIM = PhoneConstants.APPTYPE_SIM; /** UICC application type is USIM */ @@ -7251,8 +7625,13 @@ public class TelephonyManager { * Returns the response of authentication for the default subscription. * Returns null if the authentication hasn't been successful * - * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE or that the calling - * app has carrier privileges (see {@link #hasCarrierPrivileges}). + * <p>Requires one of the following permissions: + * <ul> + * <li>READ_PRIVILEGED_PHONE_STATE + * <li>the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). + * <li>the calling app has been granted the + * {@link Manifest.permission#USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER} permission. + * </ul> * * @param appType the icc application type, like {@link #APPTYPE_USIM} * @param authType the authentication type, {@link #AUTHTYPE_EAP_AKA} or @@ -7277,7 +7656,8 @@ public class TelephonyManager { * Returns the response of USIM Authentication for specified subId. * Returns null if the authentication hasn't been successful * - * <p>Requires that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). + * <p>See {@link #getIccAuthentication(int, int, String)} for details on the required + * permissions. * * @param subId subscription ID used for authentication * @param appType the icc application type, like {@link #APPTYPE_USIM} @@ -7300,7 +7680,8 @@ public class TelephonyManager { IPhoneSubInfo info = getSubscriberInfoService(); if (info == null) return null; - return info.getIccSimChallengeResponse(subId, appType, authType, data); + return info.getIccSimChallengeResponse(subId, appType, authType, data, + getOpPackageName(), getAttributionTag()); } catch (RemoteException ex) { return null; } catch (NullPointerException ex) { @@ -7460,87 +7841,13 @@ public class TelephonyManager { } /** - * Returns the {@link IImsMmTelFeature} that corresponds to the given slot Id and MMTel - * feature or {@link null} if the service is not available. If an MMTelFeature is available, the - * {@link IImsServiceFeatureCallback} callback is registered as a listener for feature updates. - * @param slotIndex The SIM slot that we are requesting the {@link IImsMmTelFeature} for. - * @param callback Listener that will send updates to ImsManager when there are updates to - * ImsServiceController. - * @return {@link IImsMmTelFeature} interface for the feature specified or {@code null} if - * it is unavailable. - * @hide - */ - public @Nullable IImsMmTelFeature getImsMmTelFeatureAndListen(int slotIndex, - IImsServiceFeatureCallback callback) { - try { - ITelephony telephony = getITelephony(); - if (telephony != null) { - return telephony.getMmTelFeatureAndListen(slotIndex, callback); - } - } catch (RemoteException e) { - Rlog.e(TAG, "getImsMmTelFeatureAndListen, RemoteException: " - + e.getMessage()); - } - return null; - } - - /** - * Returns the {@link IImsRcsFeature} that corresponds to the given slot Id and RCS - * feature for emergency calling or {@link null} if the service is not available. If an - * RcsFeature is available, the {@link IImsServiceFeatureCallback} callback is registered as a - * listener for feature updates. - * @param slotIndex The SIM slot that we are requesting the {@link IImsRcsFeature} for. - * @param callback Listener that will send updates to ImsManager when there are updates to - * ImsServiceController. - * @return {@link IImsRcsFeature} interface for the feature specified or {@code null} if - * it is unavailable. - * @hide - */ - public @Nullable IImsRcsFeature getImsRcsFeatureAndListen(int slotIndex, - IImsServiceFeatureCallback callback) { - try { - ITelephony telephony = getITelephony(); - if (telephony != null) { - return telephony.getRcsFeatureAndListen(slotIndex, callback); - } - } catch (RemoteException e) { - Rlog.e(TAG, "getImsRcsFeatureAndListen, RemoteException: " - + e.getMessage()); - } - return null; - } - - /** - * Unregister a IImsServiceFeatureCallback previously associated with an ImsFeature through - * {@link #getImsMmTelFeatureAndListen(int, IImsServiceFeatureCallback)} or - * {@link #getImsRcsFeatureAndListen(int, IImsServiceFeatureCallback)}. - * @param slotIndex The SIM slot associated with the callback. - * @param featureType The {@link android.telephony.ims.feature.ImsFeature.FeatureType} - * associated with the callback. - * @param callback The callback to be unregistered. - * @hide - */ - public void unregisterImsFeatureCallback(int slotIndex, int featureType, - IImsServiceFeatureCallback callback) { - try { - ITelephony telephony = getITelephony(); - if (telephony != null) { - telephony.unregisterImsFeatureCallback(slotIndex, featureType, callback); - } - } catch (RemoteException e) { - Rlog.e(TAG, "unregisterImsFeatureCallback, RemoteException: " - + e.getMessage()); - } - } - - /** * @return the {@IImsRegistration} interface that corresponds with the slot index and feature. * @param slotIndex The SIM slot corresponding to the ImsService ImsRegistration is active for. * @param feature An integer indicating the feature that we wish to get the ImsRegistration for. * Corresponds to features defined in ImsFeature. * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public @Nullable IImsRegistration getImsRegistration(int slotIndex, int feature) { try { ITelephony telephony = getITelephony(); @@ -7560,7 +7867,7 @@ public class TelephonyManager { * Corresponds to features defined in ImsFeature. * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public @Nullable IImsConfig getImsConfig(int slotIndex, int feature) { try { ITelephony telephony = getITelephony(); @@ -7574,18 +7881,23 @@ public class TelephonyManager { } /** - * Set IMS registration state + * Set IMS registration state on all active subscriptions. + * <p/> + * Use {@link android.telephony.ims.stub.ImsRegistrationImplBase#onRegistered} and + * {@link android.telephony.ims.stub.ImsRegistrationImplBase#onDeregistered} to set Ims + * registration state instead. + * + * @param registered whether ims is registered * - * @param Registration state * @hide */ - @UnsupportedAppUsage - public void setImsRegistrationState(boolean registered) { + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + public void setImsRegistrationState(final boolean registered) { try { - ITelephony telephony = getITelephony(); + final ITelephony telephony = getITelephony(); if (telephony != null) telephony.setImsRegistrationState(registered); - } catch (RemoteException e) { + } catch (final RemoteException e) { } } @@ -7867,21 +8179,13 @@ public class TelephonyManager { * * @return the preferred network type. * @hide - * @deprecated Use {@link #getPreferredNetworkTypeBitmask} instead. + * @deprecated Use {@link #getAllowedNetworkTypesBitmask} instead. */ @Deprecated @RequiresPermission((android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)) @UnsupportedAppUsage public @PrefNetworkMode int getPreferredNetworkType(int subId) { - try { - ITelephony telephony = getITelephony(); - if (telephony != null) { - return telephony.getPreferredNetworkType(subId); - } - } catch (RemoteException ex) { - Rlog.e(TAG, "getPreferredNetworkType RemoteException", ex); - } - return -1; + return RadioAccessFamily.getNetworkTypeFromRaf((int) getAllowedNetworkTypesBitmask()); } /** @@ -7897,24 +8201,47 @@ public class TelephonyManager { * @return The bitmask of preferred network types. * * @hide + * @deprecated Use {@link #getAllowedNetworkTypesBitmask} instead. */ + @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @SystemApi public @NetworkTypeBitMask long getPreferredNetworkTypeBitmask() { + return getAllowedNetworkTypesBitmask(); + } + + /** + * Get the allowed network type bitmask. + * Note that the device can only register on the network of {@link NetworkTypeBitmask} + * (except for emergency call cases). + * + * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the + * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()} + * + * <p>Requires Permission: + * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE} + * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). + * + * @return The bitmask of allowed network types. + * + * @hide + */ + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @SystemApi + public @NetworkTypeBitMask long getAllowedNetworkTypesBitmask() { try { ITelephony telephony = getITelephony(); if (telephony != null) { - return (long) RadioAccessFamily.getRafFromNetworkType( - telephony.getPreferredNetworkType(getSubId())); + return (long) telephony.getAllowedNetworkTypesBitmask(getSubId()); } } catch (RemoteException ex) { - Rlog.e(TAG, "getPreferredNetworkTypeBitmask RemoteException", ex); + Rlog.e(TAG, "getAllowedNetworkTypesBitmask RemoteException", ex); } return 0; } /** - * Get the allowed network types. + * Get the allowed network types by carriers. * * <p>Requires Permission: * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE} @@ -7922,14 +8249,17 @@ public class TelephonyManager { * * @return the allowed network type bitmask * @hide + * @deprecated Use {@link #getAllowedNetworkTypesForReason} instead. */ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @SystemApi + @Deprecated public @NetworkTypeBitMask long getAllowedNetworkTypes() { try { ITelephony telephony = getITelephony(); if (telephony != null) { - return telephony.getAllowedNetworkTypes(getSubId()); + return telephony.getAllowedNetworkTypesForReason(getSubId(), + TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_CARRIER); } } catch (RemoteException ex) { Rlog.e(TAG, "getAllowedNetworkTypes RemoteException", ex); @@ -8151,7 +8481,7 @@ public class TelephonyManager { return false; } - /** + /** * Get the network selection mode. * * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the @@ -8182,13 +8512,13 @@ public class TelephonyManager { /** * Get the PLMN chosen for Manual Network Selection if active. - * Return empty string if in automatic selection. + * Return null string if in automatic selection. * * <p>Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE * READ_PRECISE_PHONE_STATE} or that the calling app has carrier privileges * (see {@link #hasCarrierPrivileges}) * - * @return manually selected network info on success or empty string on failure + * @return manually selected network info on success or null string on failure */ @SuppressAutoDoc // No support carrier privileges (b/72967236). @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) @@ -8201,7 +8531,7 @@ public class TelephonyManager { } catch (RemoteException ex) { Rlog.e(TAG, "getManualNetworkSelectionPlmn RemoteException", ex); } - return ""; + return null; } /** @@ -8219,6 +8549,7 @@ public class TelephonyManager { * * @hide */ + @SystemApi @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isInEmergencySmsMode() { try { @@ -8238,20 +8569,27 @@ public class TelephonyManager { * <p>Requires Permission: * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling * app has carrier privileges (see {@link #hasCarrierPrivileges}). + * <p> + * If {@link android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported} + * ({@link TelephonyManager#CAPABILITY_ALLOWED_NETWORK_TYPES_USED}) returns true, then + * setAllowedNetworkTypesBitmap is used on the radio interface. Otherwise, + * setPreferredNetworkTypesBitmap is used instead. * * @param subId the id of the subscription to set the preferred network type for. * @param networkType the preferred network type * @return true on success; false on any failure. * @hide - * @deprecated Use {@link #setPreferredNetworkTypeBitmask} instead. + * @deprecated Use {@link #setAllowedNetworkTypesForReason} instead. */ @Deprecated - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public boolean setPreferredNetworkType(int subId, @PrefNetworkMode int networkType) { try { ITelephony telephony = getITelephony(); if (telephony != null) { - return telephony.setPreferredNetworkType(subId, networkType); + return telephony.setAllowedNetworkTypesForReason(subId, + TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER, + RadioAccessFamily.getRafFromNetworkType(networkType)); } } catch (RemoteException ex) { Rlog.e(TAG, "setPreferredNetworkType RemoteException", ex); @@ -8269,20 +8607,26 @@ public class TelephonyManager { * <p>Requires Permission: * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling * app has carrier privileges (see {@link #hasCarrierPrivileges}). + * <p> + * If {@link android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported} + * ({@link TelephonyManager#CAPABILITY_ALLOWED_NETWORK_TYPES_USED}) returns true, then + * setAllowedNetworkTypesBitmap is used on the radio interface. Otherwise, + * setPreferredNetworkTypesBitmap is used instead. * * @param networkTypeBitmask The bitmask of preferred network types. * @return true on success; false on any failure. * @hide + * @deprecated Use {@link #setAllowedNetworkTypesForReason} instead. */ + @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @SystemApi public boolean setPreferredNetworkTypeBitmask(@NetworkTypeBitMask long networkTypeBitmask) { try { ITelephony telephony = getITelephony(); if (telephony != null) { - return telephony.setPreferredNetworkType( - getSubId(), RadioAccessFamily.getNetworkTypeFromRaf( - (int) networkTypeBitmask)); + return telephony.setAllowedNetworkTypesForReason(getSubId(), + TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER, networkTypeBitmask); } } catch (RemoteException ex) { Rlog.e(TAG, "setPreferredNetworkTypeBitmask RemoteException", ex); @@ -8294,18 +8638,29 @@ public class TelephonyManager { * Set the allowed network types of the device. This is for carrier or privileged apps to * enable/disable certain network types on the device. The user preferred network types should * be set through {@link #setPreferredNetworkTypeBitmask}. + * <p> + * If {@link android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported} + * ({@link TelephonyManager#CAPABILITY_ALLOWED_NETWORK_TYPES_USED}) returns true, then + * setAllowedNetworkTypesBitmap is used on the radio interface. Otherwise, + * setPreferredNetworkTypesBitmap is used instead. * * @param allowedNetworkTypes The bitmask of allowed network types. * @return true on success; false on any failure. * @hide + * @deprecated Use {@link #setAllowedNetworkTypesForReason} instead. */ + @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature( + enforcement = "android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported", + value = TelephonyManager.CAPABILITY_ALLOWED_NETWORK_TYPES_USED) @SystemApi public boolean setAllowedNetworkTypes(@NetworkTypeBitMask long allowedNetworkTypes) { try { ITelephony telephony = getITelephony(); if (telephony != null) { - return telephony.setAllowedNetworkTypes(getSubId(), allowedNetworkTypes); + return telephony.setAllowedNetworkTypesForReason(getSubId(), + TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_CARRIER, allowedNetworkTypes); } } catch (RemoteException ex) { Rlog.e(TAG, "setAllowedNetworkTypes RemoteException", ex); @@ -8315,35 +8670,77 @@ public class TelephonyManager { /** @hide */ @IntDef({ - ALLOWED_NETWORK_TYPES_REASON_POWER + ALLOWED_NETWORK_TYPES_REASON_USER, + ALLOWED_NETWORK_TYPES_REASON_POWER, + ALLOWED_NETWORK_TYPES_REASON_CARRIER, + ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G }) @Retention(RetentionPolicy.SOURCE) - public @interface AllowedNetworkTypesReason{} + public @interface AllowedNetworkTypesReason { + } + + /** + * To indicate allowed network type change is requested by user. + * + * @hide + */ + @SystemApi + public static final int ALLOWED_NETWORK_TYPES_REASON_USER = 0; /** * To indicate allowed network type change is requested by power manager. * Power Manger configuration won't affect the settings configured through - * {@link setAllowedNetworkTypes} and will result in allowing network types that are in both + * other reasons and will result in allowing network types that are in both + * configurations (i.e intersection of both sets). + * + * @hide + */ + @SystemApi + public static final int ALLOWED_NETWORK_TYPES_REASON_POWER = 1; + + /** + * To indicate allowed network type change is requested by carrier. + * Carrier configuration won't affect the settings configured through + * other reasons and will result in allowing network types that are in both * configurations (i.e intersection of both sets). + * + * @hide + */ + @SystemApi + public static final int ALLOWED_NETWORK_TYPES_REASON_CARRIER = 2; + + /** + * To indicate allowed network type change is requested by the user via the 2G toggle. + * * @hide */ - public static final int ALLOWED_NETWORK_TYPES_REASON_POWER = 0; + @SystemApi + public static final int ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G = 3; /** - * Set the allowed network types of the device and - * provide the reason triggering the allowed network change. + * Set the allowed network types of the device and provide the reason triggering the allowed + * network change. * This can be called for following reasons * <ol> + * <li>Allowed network types control by USER {@link #ALLOWED_NETWORK_TYPES_REASON_USER} * <li>Allowed network types control by power manager * {@link #ALLOWED_NETWORK_TYPES_REASON_POWER} + * <li>Allowed network types control by carrier {@link #ALLOWED_NETWORK_TYPES_REASON_CARRIER} + * <li>Allowed network types control by the user-controlled "Allow 2G" toggle + * {@link #ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G} * </ol> * This API will result in allowing an intersection of allowed network types for all reasons, - * including the configuration done through {@link setAllowedNetworkTypes}. - * While this API and {@link setAllowedNetworkTypes} is controlling allowed network types - * on device, user preference will still be set through {@link #setPreferredNetworkTypeBitmask}. - * Thus resultant network type configured on modem will be an intersection of the network types - * from setAllowedNetworkTypesForReason, {@link setAllowedNetworkTypes} - * and {@link #setPreferredNetworkTypeBitmask}. + * including the configuration done through other reasons. + * + * The functionality of this API with the parameter + * {@link #ALLOWED_NETWORK_TYPES_REASON_CARRIER} is the same as the API + * {@link TelephonyManager#setAllowedNetworkTypes}. Use this API instead of + * {@link TelephonyManager#setAllowedNetworkTypes}. + * <p> + * If {@link android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported} + * ({@link TelephonyManager#CAPABILITY_ALLOWED_NETWORK_TYPES_USED}) returns true, then + * setAllowedNetworkTypesBitmap is used on the radio interface. Otherwise, + * setPreferredNetworkTypesBitmap is used instead. * * @param reason the reason the allowed network type change is taking place * @param allowedNetworkTypes The bitmask of allowed network types. @@ -8351,12 +8748,17 @@ public class TelephonyManager { * @throws IllegalArgumentException if invalid AllowedNetworkTypesReason is passed. * @hide */ + @SystemApi @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature( + enforcement = "android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported", + value = TelephonyManager.CAPABILITY_ALLOWED_NETWORK_TYPES_USED) public void setAllowedNetworkTypesForReason(@AllowedNetworkTypesReason int reason, @NetworkTypeBitMask long allowedNetworkTypes) { - if (reason != ALLOWED_NETWORK_TYPES_REASON_POWER) { + if (!isValidAllowedNetworkTypesReason(reason)) { throw new IllegalArgumentException("invalid AllowedNetworkTypesReason."); } + try { ITelephony telephony = getITelephony(); if (telephony != null) { @@ -8375,25 +8777,25 @@ public class TelephonyManager { * Get the allowed network types for certain reason. * * {@link #getAllowedNetworkTypesForReason} returns allowed network type for a - * specific reason. For effective allowed network types configured on device, - * query {@link getEffectiveAllowedNetworkTypes} + * specific reason. * - * <p>Requires Permission: - * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE} - * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). - *s * @param reason the reason the allowed network type change is taking place * @return the allowed network type bitmask - * @throws IllegalStateException if the Telephony process is not currently available. + * @throws IllegalStateException if the Telephony process is not currently available. * @throws IllegalArgumentException if invalid AllowedNetworkTypesReason is passed. * @hide */ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresFeature( + enforcement = "android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported", + value = TelephonyManager.CAPABILITY_ALLOWED_NETWORK_TYPES_USED) + @SystemApi public @NetworkTypeBitMask long getAllowedNetworkTypesForReason( @AllowedNetworkTypesReason int reason) { - if (reason != ALLOWED_NETWORK_TYPES_REASON_POWER) { + if (!isValidAllowedNetworkTypesReason(reason)) { throw new IllegalArgumentException("invalid AllowedNetworkTypesReason."); } + try { ITelephony telephony = getITelephony(); if (telephony != null) { @@ -8407,7 +8809,20 @@ public class TelephonyManager { } return -1; } - + /** + * Verifies that the reason provided is valid. + * @hide + */ + public static boolean isValidAllowedNetworkTypesReason(@AllowedNetworkTypesReason int reason) { + switch (reason) { + case TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER: + case TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_POWER: + case TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_CARRIER: + case TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G: + return true; + } + return false; + } /** * Get bit mask of all network types. * @@ -8419,32 +8834,22 @@ public class TelephonyManager { } /** - * Get the allowed network types configured on the device. - * This API will return an intersection of allowed network types for all reasons, - * including the configuration done through setAllowedNetworkTypes + * Returns a string representation of the allowed network types{@link NetworkTypeBitMask}. * - * <p>Requires Permission: - * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE} - * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). - * - * @return the allowed network type bitmask - * @throws IllegalStateException if the Telephony process is not currently available. + * @param networkTypeBitmask The bitmask of allowed network types. + * @return the name of the allowed network types * @hide */ - @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) - public @NetworkTypeBitMask long getEffectiveAllowedNetworkTypes() { - try { - ITelephony telephony = getITelephony(); - if (telephony != null) { - return telephony.getEffectiveAllowedNetworkTypes(getSubId()); - } else { - throw new IllegalStateException("telephony service is null."); - } - } catch (RemoteException ex) { - Rlog.e(TAG, "getEffectiveAllowedNetworkTypes RemoteException", ex); - ex.rethrowFromSystemServer(); - } - return -1; + public static String convertNetworkTypeBitmaskToString( + @NetworkTypeBitMask long networkTypeBitmask) { + String networkTypeName = IntStream.rangeClosed(NETWORK_TYPE_GPRS, NETWORK_TYPE_NR) + .filter(x -> { + return (networkTypeBitmask & getBitMaskForNetworkType(x)) + == getBitMaskForNetworkType(x); + }) + .mapToObj(x -> getNetworkTypeName(x)) + .collect(Collectors.joining("|")); + return TextUtils.isEmpty(networkTypeName) ? "UNKNOWN" : networkTypeName; } /** @@ -8524,6 +8929,9 @@ public class TelephonyManager { * call will return true. This access is granted by the owner of the UICC * card and does not depend on the registered carrier. * + * Note that this API applies to both physical and embedded subscriptions and + * is a superset of the checks done in SubscriptionManager#canManageSubscription. + * * @return true if the app has carrier privileges. */ public boolean hasCarrierPrivileges() { @@ -8537,6 +8945,9 @@ public class TelephonyManager { * call will return true. This access is granted by the owner of the UICC * card and does not depend on the registered carrier. * + * Note that this API applies to both physical and embedded subscriptions and + * is a superset of the checks done in SubscriptionManager#canManageSubscription. + * * @param subId The subscription to use. * @return true if the app has carrier privileges. * @hide @@ -8620,7 +9031,7 @@ public class TelephonyManager { * * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public boolean setRoamingOverride(List<String> gsmRoamingList, List<String> gsmNonRoamingList, List<String> cdmaRoamingList, List<String> cdmaNonRoamingList) { @@ -8716,7 +9127,7 @@ public class TelephonyManager { /** @hide */ @SystemApi - @SuppressLint("Doclava125") + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int checkCarrierPrivilegesForPackage(String pkgName) { try { ITelephony telephony = getITelephony(); @@ -8732,7 +9143,7 @@ public class TelephonyManager { /** @hide */ @SystemApi - @SuppressLint("Doclava125") + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int checkCarrierPrivilegesForPackageAnyPhone(String pkgName) { try { ITelephony telephony = getITelephony(); @@ -8754,6 +9165,7 @@ public class TelephonyManager { /** @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public List<String> getCarrierPackageNamesForIntentAndPhone(Intent intent, int phoneId) { try { ITelephony telephony = getITelephony(); @@ -8768,6 +9180,7 @@ public class TelephonyManager { } /** @hide */ + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public List<String> getPackagesWithCarrierPrivileges() { try { ITelephony telephony = getITelephony(); @@ -8805,10 +9218,80 @@ public class TelephonyManager { return Collections.EMPTY_LIST; } + /** + * Call composer status OFF from user setting. + */ + public static final int CALL_COMPOSER_STATUS_OFF = 0; + + /** + * Call composer status ON from user setting. + */ + public static final int CALL_COMPOSER_STATUS_ON = 1; + + /** @hide */ + @IntDef(prefix = {"CALL_COMPOSER_STATUS_"}, + value = { + CALL_COMPOSER_STATUS_ON, + CALL_COMPOSER_STATUS_OFF, + }) + public @interface CallComposerStatus {} + + /** + * Set the user-set status for enriched calling with call composer. + * + * @param status user-set status for enriched calling with call composer. + * + * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the + * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()} + * + * @throws IllegalArgumentException if requested state is invalid. + * @throws SecurityException if the caller does not have the permission. + */ + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + public void setCallComposerStatus(@CallComposerStatus int status) { + if (status > CALL_COMPOSER_STATUS_ON + || status < CALL_COMPOSER_STATUS_OFF) { + throw new IllegalArgumentException("requested status is invalid"); + } + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + telephony.setCallComposerStatus(getSubId(), status); + } + } catch (RemoteException ex) { + Log.e(TAG, "Error calling ITelephony#setCallComposerStatus", ex); + ex.rethrowFromSystemServer(); + } + } + + /** + * Get the user-set status for enriched calling with call composer. + * + * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the + * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()} + * + * @throws SecurityException if the caller does not have the permission. + * + * @return the user-set status for enriched calling with call composer, either of + * {@link #CALL_COMPOSER_STATUS_ON} or {@link #CALL_COMPOSER_STATUS_OFF}. + */ + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public @CallComposerStatus int getCallComposerStatus() { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + return telephony.getCallComposerStatus(getSubId()); + } + } catch (RemoteException ex) { + Log.e(TAG, "Error calling ITelephony#getCallComposerStatus", ex); + ex.rethrowFromSystemServer(); + } + return CALL_COMPOSER_STATUS_OFF; + } /** @hide */ @SystemApi - @SuppressLint("Doclava125") + @SuppressLint("RequiresPermission") public void dial(String number) { try { ITelephony telephony = getITelephony(); @@ -8867,7 +9350,7 @@ public class TelephonyManager { */ @Deprecated @SystemApi - @SuppressLint("Doclava125") + @SuppressLint("RequiresPermission") public void silenceRinger() { // No-op } @@ -8966,9 +9449,13 @@ public class TelephonyManager { return false; } - /** @hide */ + /** + * @deprecated use {@link #supplyIccLockPin(String)} instead. + * @hide + */ @SystemApi @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @Deprecated public int[] supplyPinReportResult(String pin) { try { ITelephony telephony = getITelephony(); @@ -8980,65 +9467,91 @@ public class TelephonyManager { return new int[0]; } - /** @hide */ + /** + * @deprecated use {@link #supplyIccLockPuk(String, String)} instead. + * @hide + */ @SystemApi @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @Deprecated public int[] supplyPukReportResult(String puk, String pin) { try { ITelephony telephony = getITelephony(); if (telephony != null) return telephony.supplyPukReportResultForSubscriber(getSubId(), puk, pin); } catch (RemoteException e) { - Log.e(TAG, "Error calling ITelephony#]", e); + Log.e(TAG, "Error calling ITelephony#supplyPukReportResultForSubscriber", e); } return new int[0]; } /** - * Used when the user attempts to enter their pin. + * Supplies a PIN to unlock the ICC and returns the corresponding {@link PinResult}. + * Used when the user enters their ICC unlock PIN to attempt an unlock. * - * @param pin The user entered pin. - * @return The result of the pin. + * @param pin The user entered PIN. + * @return The result of the PIN. + * @throws SecurityException if the caller doesn't have the permission. + * @throws IllegalStateException if the Telephony process is not currently available. + * + * <p>Requires Permission: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling + * app has carrier privileges (see {@link #hasCarrierPrivileges}). * * @hide */ - @Nullable + @SystemApi + @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) - public PinResult supplyPinReportPinResult(@NonNull String pin) { + public PinResult supplyIccLockPin(@NonNull String pin) { try { ITelephony telephony = getITelephony(); if (telephony != null) { int[] result = telephony.supplyPinReportResultForSubscriber(getSubId(), pin); return new PinResult(result[0], result[1]); + } else { + throw new IllegalStateException("telephony service is null."); } } catch (RemoteException e) { - Log.e(TAG, "Error calling ITelephony#supplyPinReportResultForSubscriber", e); + Log.e(TAG, "Error calling ITelephony#supplyIccLockPin", e); + e.rethrowFromSystemServer(); } - return null; + return PinResult.getDefaultFailedResult(); } /** - * Used when the user attempts to enter the puk or their pin. + * Supplies a PUK and PIN to unlock the ICC and returns the corresponding {@link PinResult}. + * Used when the user enters their ICC unlock PUK and PIN to attempt an unlock. + * + * @param puk The product unlocking key. + * @param pin The user entered PIN. + * @return The result of the PUK and PIN. + * @throws SecurityException if the caller doesn't have the permission. + * @throws IllegalStateException if the Telephony process is not currently available. * - * @param puk The product unblocking key. - * @param pin The user entered pin. - * @return The result of the pin. + * <p>Requires Permission: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling + * app has carrier privileges (see {@link #hasCarrierPrivileges}). * * @hide */ - @Nullable + @SystemApi + @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) - public PinResult supplyPukReportPinResult(@NonNull String puk, @NonNull String pin) { + public PinResult supplyIccLockPuk(@NonNull String puk, @NonNull String pin) { try { ITelephony telephony = getITelephony(); if (telephony != null) { int[] result = telephony.supplyPukReportResultForSubscriber(getSubId(), puk, pin); return new PinResult(result[0], result[1]); + } else { + throw new IllegalStateException("telephony service is null."); } } catch (RemoteException e) { - Log.e(TAG, "Error calling ITelephony#]", e); + Log.e(TAG, "Error calling ITelephony#supplyIccLockPuk", e); + e.rethrowFromSystemServer(); } - return null; + return PinResult.getDefaultFailedResult(); } /** @@ -9305,17 +9818,14 @@ public class TelephonyManager { return RADIO_POWER_UNAVAILABLE; } - /** @hide */ + /** + * This method should not be used due to privacy and stability concerns. + * + * @hide + */ @SystemApi - @SuppressLint("Doclava125") public void updateServiceLocation() { - try { - ITelephony telephony = getITelephony(); - if (telephony != null) - telephony.updateServiceLocation(); - } catch (RemoteException e) { - Log.e(TAG, "Error calling ITelephony#updateServiceLocation", e); - } + Log.e(TAG, "Do not call TelephonyManager#updateServiceLocation()"); } /** @hide */ @@ -9374,6 +9884,33 @@ public class TelephonyManager { } /** + * Get the mobile provisioning url that is used to launch a browser to allow users to manage + * their mobile plan. + * + * <p>Requires Permission: + * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE}. + * + * TODO: The legacy design only supports single sim design. Ideally, this should support + * multi-sim design in current world. + * + * {@hide} + */ + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public @Nullable String getMobileProvisioningUrl() { + try { + final ITelephony service = getITelephony(); + if (service != null) { + return service.getMobileProvisioningUrl(); + } else { + throw new IllegalStateException("telephony service is null."); + } + } catch (RemoteException ex) { + Rlog.e(TAG, "Telephony#getMobileProvisioningUrl RemoteException" + ex); + } + return null; + } + + /** * Turns mobile data on or off. * If this object has been created with {@link #createForSubscriptionId}, applies to the given * subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()} @@ -9383,8 +9920,10 @@ public class TelephonyManager { * app has carrier privileges (see {@link #hasCarrierPrivileges}). * * @param enable Whether to enable mobile data. + * @deprecated use setDataEnabledForReason with reason DATA_ENABLED_REASON_USER instead. * */ + @Deprecated @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataEnabled(boolean enable) { @@ -9393,19 +9932,16 @@ public class TelephonyManager { /** * @hide - * @deprecated use {@link #setDataEnabled(boolean)} instead. + * @deprecated use {@link #setDataEnabledForReason(int, boolean)} instead. */ @SystemApi @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataEnabled(int subId, boolean enable) { try { - Log.d(TAG, "setDataEnabled: enabled=" + enable); - ITelephony telephony = getITelephony(); - if (telephony != null) - telephony.setUserDataEnabled(subId, enable); - } catch (RemoteException e) { - Log.e(TAG, "Error calling ITelephony#setUserDataEnabled", e); + setDataEnabledForReason(subId, DATA_ENABLED_REASON_USER, enable); + } catch (RuntimeException e) { + Log.e(TAG, "Error calling setDataEnabledForReason e:" + e); } } @@ -9438,9 +9974,16 @@ public class TelephonyManager { * @return true if mobile data is enabled. */ @RequiresPermission(anyOf = {android.Manifest.permission.ACCESS_NETWORK_STATE, - android.Manifest.permission.MODIFY_PHONE_STATE}) + android.Manifest.permission.MODIFY_PHONE_STATE, + android.Manifest.permission.READ_PHONE_STATE}) public boolean isDataEnabled() { - return getDataEnabled(getSubId(SubscriptionManager.getDefaultDataSubscriptionId())); + try { + return isDataEnabledForReason(DATA_ENABLED_REASON_USER); + } catch (IllegalStateException ise) { + // TODO(b/176163590): Remove this catch once TelephonyManager is booting safely. + Log.e(TAG, "Error calling #isDataEnabled, returning default (false).", ise); + return false; + } } /** @@ -9479,11 +10022,22 @@ public class TelephonyManager { * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()} * - * @return one of {@link #CDMA_ROAMING_MODE_RADIO_DEFAULT}, {@link #CDMA_ROAMING_MODE_HOME}, - * {@link #CDMA_ROAMING_MODE_AFFILIATED}, {@link #CDMA_ROAMING_MODE_ANY}. + * @return the CDMA roaming mode. + * @throws SecurityException if the caller does not have the permission. + * @throws IllegalStateException if the Telephony process is not currently available. + * + * @see #CDMA_ROAMING_MODE_RADIO_DEFAULT + * @see #CDMA_ROAMING_MODE_HOME + * @see #CDMA_ROAMING_MODE_AFFILIATED + * @see #CDMA_ROAMING_MODE_ANY + * + * <p>Requires permission: + * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE} + * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). * * @hide */ + @SystemApi @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public @CdmaRoamingMode int getCdmaRoamingMode() { int mode = CDMA_ROAMING_MODE_RADIO_DEFAULT; @@ -9491,42 +10045,61 @@ public class TelephonyManager { ITelephony telephony = getITelephony(); if (telephony != null) { mode = telephony.getCdmaRoamingMode(getSubId()); + } else { + throw new IllegalStateException("telephony service is null."); } } catch (RemoteException ex) { Log.e(TAG, "Error calling ITelephony#getCdmaRoamingMode", ex); + ex.rethrowFromSystemServer(); } return mode; } /** - * Sets the roaming mode for CDMA phone to the given mode {@code mode}. + * Sets the roaming mode for CDMA phone to the given mode {@code mode}. If the phone is not + * CDMA capable, this method throws an IllegalStateException. * * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()} * - * @param mode should be one of {@link #CDMA_ROAMING_MODE_RADIO_DEFAULT}, - * {@link #CDMA_ROAMING_MODE_HOME}, {@link #CDMA_ROAMING_MODE_AFFILIATED}, - * {@link #CDMA_ROAMING_MODE_ANY}. + * @param mode CDMA roaming mode. + * @throws SecurityException if the caller does not have the permission. + * @throws IllegalStateException if the Telephony process or radio is not currently available, + * the device is not CDMA capable, or the request fails. + * + * @see #CDMA_ROAMING_MODE_RADIO_DEFAULT + * @see #CDMA_ROAMING_MODE_HOME + * @see #CDMA_ROAMING_MODE_AFFILIATED + * @see #CDMA_ROAMING_MODE_ANY * - * @return {@code true} if successed. + * <p>Requires Permission: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling + * app has carrier privileges (see {@link #hasCarrierPrivileges}). * * @hide */ + @SystemApi @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) - public boolean setCdmaRoamingMode(@CdmaRoamingMode int mode) { + public void setCdmaRoamingMode(@CdmaRoamingMode int mode) { + if (getPhoneType() != PHONE_TYPE_CDMA) { + throw new IllegalStateException("Phone does not support CDMA."); + } try { ITelephony telephony = getITelephony(); if (telephony != null) { - return telephony.setCdmaRoamingMode(getSubId(), mode); + boolean result = telephony.setCdmaRoamingMode(getSubId(), mode); + if (!result) throw new IllegalStateException("radio is unavailable."); + } else { + throw new IllegalStateException("telephony service is null."); } } catch (RemoteException ex) { Log.e(TAG, "Error calling ITelephony#setCdmaRoamingMode", ex); + ex.rethrowFromSystemServer(); } - return false; } /** @hide */ - @IntDef(flag = true, prefix = { "CDMA_SUBSCRIPTION_" }, value = { + @IntDef(prefix = { "CDMA_SUBSCRIPTION_" }, value = { CDMA_SUBSCRIPTION_UNKNOWN, CDMA_SUBSCRIPTION_RUIM_SIM, CDMA_SUBSCRIPTION_NV @@ -9534,48 +10107,99 @@ public class TelephonyManager { @Retention(RetentionPolicy.SOURCE) public @interface CdmaSubscription{} - /** Used for CDMA subscription mode, it'll be UNKNOWN if there is no Subscription source. + /** + * Used for CDMA subscription mode, it'll be UNKNOWN if there is no Subscription source. * @hide */ + @SystemApi public static final int CDMA_SUBSCRIPTION_UNKNOWN = -1; - /** Used for CDMA subscription mode: RUIM/SIM (default) + /** + * Used for CDMA subscription mode: RUIM/SIM (default) * @hide */ + @SystemApi public static final int CDMA_SUBSCRIPTION_RUIM_SIM = 0; - /** Used for CDMA subscription mode: NV -> non-volatile memory + /** + * Used for CDMA subscription mode: NV -> non-volatile memory * @hide */ + @SystemApi public static final int CDMA_SUBSCRIPTION_NV = 1; - /** @hide */ - public static final int PREFERRED_CDMA_SUBSCRIPTION = CDMA_SUBSCRIPTION_RUIM_SIM; - /** - * Sets the subscription mode for CDMA phone to the given mode {@code mode}. + * Gets the subscription mode for CDMA phone. + * + * @return the CDMA subscription mode. + * @throws SecurityException if the caller does not have the permission. + * @throws IllegalStateException if the Telephony process or radio is not currently available. + * + * @see #CDMA_SUBSCRIPTION_UNKNOWN + * @see #CDMA_SUBSCRIPTION_RUIM_SIM + * @see #CDMA_SUBSCRIPTION_NV * - * @param mode CDMA subscription mode + * <p>Requires Permission: + * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE} + * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). + * + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public @CdmaSubscription int getCdmaSubscriptionMode() { + int mode = CDMA_SUBSCRIPTION_RUIM_SIM; + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + mode = telephony.getCdmaSubscriptionMode(getSubId()); + } else { + throw new IllegalStateException("telephony service is null."); + } + } catch (RemoteException ex) { + Log.e(TAG, "Error calling ITelephony#getCdmaSubscriptionMode", ex); + ex.rethrowFromSystemServer(); + } + return mode; + } + + /** + * Sets the subscription mode for CDMA phone to the given mode {@code mode}. If the phone is not + * CDMA capable, this method throws an IllegalStateException. * - * @return {@code true} if successed. + * @param mode CDMA subscription mode. + * @throws SecurityException if the caller does not have the permission. + * @throws IllegalStateException if the Telephony process or radio is not currently available, + * the device is not CDMA capable, or the request fails. * * @see #CDMA_SUBSCRIPTION_UNKNOWN * @see #CDMA_SUBSCRIPTION_RUIM_SIM * @see #CDMA_SUBSCRIPTION_NV * + * <p>Requires Permission: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling + * app has carrier privileges (see {@link #hasCarrierPrivileges}). + * * @hide */ + @SystemApi @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) - public boolean setCdmaSubscriptionMode(@CdmaSubscription int mode) { + public void setCdmaSubscriptionMode(@CdmaSubscription int mode) { + if (getPhoneType() != PHONE_TYPE_CDMA) { + throw new IllegalStateException("Phone does not support CDMA."); + } try { ITelephony telephony = getITelephony(); if (telephony != null) { - return telephony.setCdmaSubscriptionMode(getSubId(), mode); + boolean result = telephony.setCdmaSubscriptionMode(getSubId(), mode); + if (!result) throw new IllegalStateException("radio is unavailable."); + } else { + throw new IllegalStateException("telephony service is null."); } } catch (RemoteException ex) { Log.e(TAG, "Error calling ITelephony#setCdmaSubscriptionMode", ex); + ex.rethrowFromSystemServer(); } - return false; } /** @@ -9613,15 +10237,12 @@ public class TelephonyManager { @Deprecated @SystemApi public boolean getDataEnabled(int subId) { - boolean retVal = false; try { - ITelephony telephony = getITelephony(); - if (telephony != null) - retVal = telephony.isUserDataEnabled(subId); - } catch (RemoteException | NullPointerException e) { - Log.e(TAG, "Error calling ITelephony#isUserDataEnabled", e); + return isDataEnabledForReason(subId, DATA_ENABLED_REASON_USER); + } catch (RuntimeException e) { + Log.e(TAG, "Error calling isDataEnabledForReason e:" + e); } - return retVal; + return false; } /** @@ -9889,6 +10510,8 @@ public class TelephonyManager { * Valid return results are: * - {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE} for LTE registration, * - {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN} for IWLAN registration, or + * - {@link ImsRegistrationImplBase#REGISTRATION_TECH_CROSS_SIM} for registration over + * other sim's internet, or * - {@link ImsRegistrationImplBase#REGISTRATION_TECH_NONE} if we are not registered or the * result is unavailable. * Use {@link ImsMmTelManager.RegistrationCallback} instead. @@ -9917,7 +10540,7 @@ public class TelephonyManager { * * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public void setSimOperatorNumericForPhone(int phoneId, String numeric) { if (SubscriptionManager.isValidPhoneId(phoneId)) { List<String> newList = updateTelephonyProperty( @@ -10027,6 +10650,16 @@ public class TelephonyManager { */ public static final int CARD_POWER_UP_PASS_THROUGH = 2; + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"CARD_POWER"}, + value = { + CARD_POWER_DOWN, + CARD_POWER_UP, + CARD_POWER_UP_PASS_THROUGH, + }) + public @interface SimPowerState {} + /** * Set SIM card power state. * @@ -10037,12 +10670,17 @@ public class TelephonyManager { * Callers should monitor for {@link TelephonyIntents#ACTION_SIM_STATE_CHANGED} * broadcasts to determine success or failure and timeout if needed. * + * @deprecated prefer {@link setSimPowerState(int, Executor, Consumer<Integer>)}. + * There is no guarantee that SIM power changes will trigger ACTION_SIM_STATE_CHANGED on new + * devices. + * * <p>Requires Permission: * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} * * {@hide} **/ @SystemApi + @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setSimPowerState(int state) { setSimPowerStateForSlot(getSlotIndex(), state); @@ -10059,12 +10697,16 @@ public class TelephonyManager { * Callers should monitor for {@link TelephonyIntents#ACTION_SIM_STATE_CHANGED} * broadcasts to determine success or failure and timeout if needed. * + * @deprecated prefer {@link setSimPowerStateForSlot(int, int, Executor, Consumer<Integer>)}. + * changes will trigger ACTION_SIM_STATE_CHANGED on new devices. + * * <p>Requires Permission: * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} * * {@hide} **/ @SystemApi + @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setSimPowerStateForSlot(int slotIndex, int state) { try { @@ -10080,6 +10722,94 @@ public class TelephonyManager { } /** + * Set SIM card power state. + * + * @param state State of SIM (power down, power up, pass through) + * @see #CARD_POWER_DOWN + * @see #CARD_POWER_UP + * @see #CARD_POWER_UP_PASS_THROUGH + * @param executor The executor of where the callback will execute. + * @param callback Callback will be triggered once it succeeds or failed. + * @see #SET_SIM_POWER_STATE_SUCCESS + * @see #SET_SIM_POWER_STATE_ALREADY_IN_STATE + * @see #SET_SIM_POWER_STATE_MODEM_ERROR + * @see #SET_SIM_POWER_STATE_SIM_ERROR + * @see #SET_SIM_POWER_STATE_NOT_SUPPORTED + * @throws IllegalArgumentException if requested SIM state is invalid + * + * <p>Requires Permission: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} + * + * {@hide} + **/ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + public void setSimPowerState(@SimPowerState int state, @NonNull Executor executor, + @NonNull @SetSimPowerStateResult Consumer<Integer> callback) { + setSimPowerStateForSlot(getSlotIndex(), state, executor, callback); + } + + /** + * Set SIM card power state. + * + * @param slotIndex SIM slot id + * @param state State of SIM (power down, power up, pass through) + * @see #CARD_POWER_DOWN + * @see #CARD_POWER_UP + * @see #CARD_POWER_UP_PASS_THROUGH + * @param executor The executor of where the callback will execute. + * @param callback Callback will be triggered once it succeeds or failed. + * @see #SET_SIM_POWER_STATE_SUCCESS + * @see #SET_SIM_POWER_STATE_ALREADY_IN_STATE + * @see #SET_SIM_POWER_STATE_MODEM_ERROR + * @see #SET_SIM_POWER_STATE_SIM_ERROR + * @see #SET_SIM_POWER_STATE_NOT_SUPPORTED + * @throws IllegalArgumentException if requested SIM state is invalid + * + * <p>Requires Permission: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} + * + * {@hide} + **/ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + public void setSimPowerStateForSlot(int slotIndex, @SimPowerState int state, + @NonNull Executor executor, + @NonNull @SetSimPowerStateResult Consumer<Integer> callback) { + if (state != CARD_POWER_DOWN && state != CARD_POWER_UP + && state != CARD_POWER_UP_PASS_THROUGH) { + throw new IllegalArgumentException("requested SIM state is invalid"); + } + try { + ITelephony telephony = getITelephony(); + if (telephony == null) throw new IllegalStateException("Telephony is null."); + + IIntegerConsumer internalCallback = new IIntegerConsumer.Stub() { + @Override + public void accept(int result) { + executor.execute(() -> + Binder.withCleanCallingIdentity(() -> callback.accept(result))); + } + }; + if (telephony == null) { + if (Compatibility.isChangeEnabled(NULL_TELEPHONY_THROW_NO_CB)) { + throw new IllegalStateException("Telephony is null"); + } else { + return; + } + } + telephony.setSimPowerStateForSlotWithCallback(slotIndex, state, internalCallback); + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#setSimPowerStateForSlot", e); + runOnBackgroundThread(() -> executor.execute( + () -> callback.accept(SET_SIM_POWER_STATE_MODEM_ERROR))); + } catch (SecurityException e) { + Log.e(TAG, "Permission error calling ITelephony#setSimPowerStateForSlot", + e); + } + } + + /** * Set baseband version for the default phone. * * @param version baseband version @@ -10289,7 +11019,7 @@ public class TelephonyManager { * @param name the alphabetic name of current registered operator. * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public void setNetworkOperatorNameForPhone(int phoneId, String name) { if (SubscriptionManager.isValidPhoneId(phoneId)) { List<String> newList = updateTelephonyProperty( @@ -10314,7 +11044,7 @@ public class TelephonyManager { * @param operator the numeric name (MCC+MNC) of current registered operator * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public void setNetworkOperatorNumericForPhone(int phoneId, String numeric) { if (SubscriptionManager.isValidPhoneId(phoneId)) { List<String> newList = updateTelephonyProperty( @@ -10339,7 +11069,7 @@ public class TelephonyManager { * @param isRoaming is network in romaing state or not * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public void setNetworkRoamingForPhone(int phoneId, boolean isRoaming) { if (SubscriptionManager.isValidPhoneId(phoneId)) { List<Boolean> newList = updateTelephonyProperty( @@ -10368,7 +11098,7 @@ public class TelephonyManager { * @param type the network type currently in use on the device for data transmission * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public void setDataNetworkTypeForPhone(int phoneId, int type) { if (SubscriptionManager.isValidPhoneId(phoneId)) { List<String> newList = updateTelephonyProperty( @@ -10397,6 +11127,25 @@ public class TelephonyManager { } /** + * Determines the {@link PhoneAccountHandle} associated with this TelephonyManager. + * + * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the + * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()} + * + * <p>Requires Permission android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE or that the + * calling app has carrier privileges (see {@link #hasCarrierPrivileges}) + * + * @return The {@link PhoneAccountHandle} associated with the TelphonyManager, or {@code null} + * if there is no associated {@link PhoneAccountHandle}; this can happen if the subscription is + * data-only or an opportunistic subscription. + */ + @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public @Nullable PhoneAccountHandle getPhoneAccountHandle() { + return getPhoneAccountHandleForSubscriptionId(getSubId()); + } + + /** * Determines the {@link PhoneAccountHandle} associated with a subscription Id. * * @param subscriptionId The subscription Id to check. @@ -10522,26 +11271,149 @@ public class TelephonyManager { return null; } + /** + * Exception that may be supplied to the callback provided in {@link #requestModemActivityInfo}. + * @hide + */ + @SystemApi + public static class ModemActivityInfoException extends Exception { + /** Indicates that an unknown error occurred */ + public static final int ERROR_UNKNOWN = 0; + + /** + * Indicates that the modem or phone processes are not available (such as when the device + * is in airplane mode). + */ + public static final int ERROR_PHONE_NOT_AVAILABLE = 1; + + /** + * Indicates that the modem supplied an invalid instance of {@link ModemActivityInfo} + */ + public static final int ERROR_INVALID_INFO_RECEIVED = 2; + + /** + * Indicates that the modem encountered an internal failure when processing the request + * for activity info. + */ + public static final int ERROR_MODEM_RESPONSE_ERROR = 3; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"ERROR_"}, + value = { + ERROR_UNKNOWN, + ERROR_PHONE_NOT_AVAILABLE, + ERROR_INVALID_INFO_RECEIVED, + ERROR_MODEM_RESPONSE_ERROR, + }) + public @interface ModemActivityInfoError {} + + private final int mErrorCode; + + /** @hide */ + public ModemActivityInfoException(@ModemActivityInfoError int errorCode) { + mErrorCode = errorCode; + } + + public @ModemActivityInfoError int getErrorCode() { + return mErrorCode; + } + + @Override + public String toString() { + switch (mErrorCode) { + case ERROR_UNKNOWN: return "ERROR_UNKNOWN"; + case ERROR_PHONE_NOT_AVAILABLE: return "ERROR_PHONE_NOT_AVAILABLE"; + case ERROR_INVALID_INFO_RECEIVED: return "ERROR_INVALID_INFO_RECEIVED"; + case ERROR_MODEM_RESPONSE_ERROR: return "ERROR_MODEM_RESPONSE_ERROR"; + default: return "UNDEFINED"; + } + } + } /** - * Requests the modem activity info. The recipient will place the result - * in `result`. - * @param result The object on which the recipient will send the resulting - * {@link android.telephony.ModemActivityInfo} object with key of - * {@link #MODEM_ACTIVITY_RESULT_KEY}. + * Requests the current modem activity info. + * + * The provided instance of {@link ModemActivityInfo} represents the cumulative activity since + * the last restart of the phone process. + * + * @param callback A callback object to which the result will be delivered. If there was an + * error processing the request, {@link OutcomeReceiver#onError} will be called + * with more details about the error. * @hide */ - public void requestModemActivityInfo(@NonNull ResultReceiver result) { + @SystemApi + @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) + public void requestModemActivityInfo(@NonNull @CallbackExecutor Executor executor, + @NonNull OutcomeReceiver<ModemActivityInfo, ModemActivityInfoException> callback) { + Objects.requireNonNull(executor); + Objects.requireNonNull(callback); + + // Pass no handler into the receiver, since we're going to be trampolining the call to the + // listener onto the provided executor. + ResultReceiver wrapperResultReceiver = new ResultReceiver(null) { + @Override + protected void onReceiveResult(int resultCode, Bundle data) { + if (data == null) { + Log.w(TAG, "requestModemActivityInfo: received null bundle"); + sendErrorToListener(ModemActivityInfoException.ERROR_UNKNOWN); + return; + } + data.setDefusable(true); + if (data.containsKey(EXCEPTION_RESULT_KEY)) { + int receivedErrorCode = data.getInt(EXCEPTION_RESULT_KEY); + sendErrorToListener(receivedErrorCode); + return; + } + + if (!data.containsKey(MODEM_ACTIVITY_RESULT_KEY)) { + Log.w(TAG, "requestModemActivityInfo: Bundle did not contain expected key"); + sendErrorToListener(ModemActivityInfoException.ERROR_UNKNOWN); + return; + } + Parcelable receivedResult = data.getParcelable(MODEM_ACTIVITY_RESULT_KEY); + if (!(receivedResult instanceof ModemActivityInfo)) { + Log.w(TAG, "requestModemActivityInfo: Bundle contained something that wasn't " + + "a ModemActivityInfo."); + sendErrorToListener(ModemActivityInfoException.ERROR_UNKNOWN); + return; + } + ModemActivityInfo modemActivityInfo = (ModemActivityInfo) receivedResult; + if (!modemActivityInfo.isValid()) { + Log.w(TAG, "requestModemActivityInfo: Received an invalid ModemActivityInfo"); + sendErrorToListener(ModemActivityInfoException.ERROR_INVALID_INFO_RECEIVED); + return; + } + Log.d(TAG, "requestModemActivityInfo: Sending result to app: " + modemActivityInfo); + sendResultToListener(modemActivityInfo); + } + + private void sendResultToListener(ModemActivityInfo info) { + Binder.withCleanCallingIdentity(() -> + executor.execute(() -> + callback.onResult(info))); + } + + private void sendErrorToListener(int code) { + ModemActivityInfoException e = new ModemActivityInfoException(code); + Binder.withCleanCallingIdentity(() -> + executor.execute(() -> + callback.onError(e))); + } + }; + try { ITelephony service = getITelephony(); if (service != null) { - service.requestModemActivityInfo(result); + service.requestModemActivityInfo(wrapperResultReceiver); return; } } catch (RemoteException e) { Log.e(TAG, "Error calling ITelephony#getModemActivityInfo", e); } - result.send(0, null); + executor.execute(() -> callback.onError( + new ModemActivityInfoException( + ModemActivityInfoException.ERROR_PHONE_NOT_AVAILABLE))); } /** @@ -10550,22 +11422,31 @@ public class TelephonyManager { * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()} * + * If you want continuous updates of service state info, register a {@link PhoneStateListener} + * via {@link #listen} with the {@link PhoneStateListener#LISTEN_SERVICE_STATE} event. + * * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}) * and {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}. + * + * May return {@code null} when the subscription is inactive or when there was an error + * communicating with the phone process. */ @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges @RequiresPermission(allOf = { Manifest.permission.READ_PHONE_STATE, Manifest.permission.ACCESS_COARSE_LOCATION }) - public ServiceState getServiceState() { + public @Nullable ServiceState getServiceState() { return getServiceStateForSubscriber(getSubId()); } /** * Returns the service state information on specified subscription. Callers require * either READ_PRIVILEGED_PHONE_STATE or READ_PHONE_STATE to retrieve the information. + * + * May return {@code null} when the subscription is inactive or when there was an error + * communicating with the phone process. * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) @@ -10592,9 +11473,9 @@ public class TelephonyManager { * @param accountHandle The handle for the {@link PhoneAccount} for which to retrieve the * voicemail ringtone. * @return The URI for the ringtone to play when receiving a voicemail from a specific - * PhoneAccount. + * PhoneAccount. May be {@code null} if no ringtone is set. */ - public Uri getVoicemailRingtoneUri(PhoneAccountHandle accountHandle) { + public @Nullable Uri getVoicemailRingtoneUri(PhoneAccountHandle accountHandle) { try { ITelephony service = getITelephony(); if (service != null) { @@ -10847,26 +11728,26 @@ public class TelephonyManager { } /** - * Return a list of certs in hex string from loaded carrier privileges access rules. - * - * @return a list of certificate in hex string. return {@code null} if there is no certs - * or privilege rules are not loaded yet. + * Return a list of certs as hex strings from loaded carrier privileges access rules. * - * <p>Requires Permission: - * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE} + * @return a list of certificates as hex strings, or an empty list if there are no certs or + * privilege rules are not loaded yet. * @hide */ + @TestApi @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @NonNull public List<String> getCertsFromCarrierPrivilegeAccessRules() { + List<String> certs = null; try { ITelephony service = getITelephony(); if (service != null) { - return service.getCertsFromCarrierPrivilegeAccessRules(getSubId()); + certs = service.getCertsFromCarrierPrivilegeAccessRules(getSubId()); } } catch (RemoteException ex) { // This could happen if binder process crashes. } - return null; + return certs == null ? Collections.emptyList() : certs; } /** @@ -11066,6 +11947,55 @@ public class TelephonyManager { public @interface SetCarrierRestrictionResult {} /** + * The SIM power state was successfully set. + * @hide + */ + @SystemApi + public static final int SET_SIM_POWER_STATE_SUCCESS = 0; + + /** + * The SIM is already in the requested power state. + * @hide + */ + @SystemApi + public static final int SET_SIM_POWER_STATE_ALREADY_IN_STATE = 1; + + /** + * Failed to connect to the modem to make the power state request. This may happen if the + * modem has an error. The user may want to make the request again later. + * @hide + */ + @SystemApi + public static final int SET_SIM_POWER_STATE_MODEM_ERROR = 2; + + /** + * Failed to connect to the SIM to make the power state request. This may happen if the + * SIM has been removed. The user may want to make the request again later. + * @hide + */ + @SystemApi + public static final int SET_SIM_POWER_STATE_SIM_ERROR = 3; + + /** + * The modem version does not support synchronous power. + * @hide + */ + @SystemApi + public static final int SET_SIM_POWER_STATE_NOT_SUPPORTED = 4; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"SET_SIM_POWER_STATE_"}, + value = { + SET_SIM_POWER_STATE_SUCCESS, + SET_SIM_POWER_STATE_ALREADY_IN_STATE, + SET_SIM_POWER_STATE_MODEM_ERROR, + SET_SIM_POWER_STATE_SIM_ERROR, + SET_SIM_POWER_STATE_NOT_SUPPORTED + }) + public @interface SetSimPowerStateResult {} + + /** * Set the allowed carrier list and the excluded carrier list indicating the priority between * the two lists. * Requires system privileges. @@ -11170,19 +12100,18 @@ public class TelephonyManager { * * @param enabled control enable or disable carrier data. * @see #resetAllCarrierActions() + * @deprecated use {@link #setDataEnabledForReason(int, boolean) with + * reason {@link #DATA_ENABLED_REASON_CARRIER}} instead. * @hide */ + @Deprecated @SystemApi @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setCarrierDataEnabled(boolean enabled) { try { - ITelephony service = getITelephony(); - if (service != null) { - service.carrierActionSetMeteredApnsEnabled( - getSubId(SubscriptionManager.getDefaultDataSubscriptionId()), enabled); - } - } catch (RemoteException e) { - Log.e(TAG, "Error calling ITelephony#setCarrierDataEnabled", e); + setDataEnabledForReason(DATA_ENABLED_REASON_CARRIER, enabled); + } catch (RuntimeException e) { + Log.e(TAG, "Error calling setDataEnabledForReason e:" + e); } } @@ -11268,18 +12197,142 @@ public class TelephonyManager { /** * Policy control of data connection. Usually used when data limit is passed. * @param enabled True if enabling the data, otherwise disabling. + * @deprecated use {@link #setDataEnabledForReason(int, boolean) with + * reason {@link #DATA_ENABLED_REASON_POLICY}} instead. * @hide */ + @Deprecated @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) public void setPolicyDataEnabled(boolean enabled) { try { + setDataEnabledForReason(DATA_ENABLED_REASON_POLICY, enabled); + } catch (RuntimeException e) { + Log.e(TAG, "Error calling setDataEnabledForReason e:" + e); + } + } + + /** @hide */ + @IntDef({ + DATA_ENABLED_REASON_USER, + DATA_ENABLED_REASON_POLICY, + DATA_ENABLED_REASON_CARRIER, + DATA_ENABLED_REASON_THERMAL + }) + @Retention(RetentionPolicy.SOURCE) + public @interface DataEnabledReason{} + + /** + * To indicate that user enabled or disabled data. + */ + public static final int DATA_ENABLED_REASON_USER = 0; + + /** + * To indicate that data control due to policy. Usually used when data limit is passed. + * Policy data on/off won't affect user settings but will bypass the + * settings and turns off data internally if set to {@code false}. + */ + public static final int DATA_ENABLED_REASON_POLICY = 1; + + /** + * To indicate enable or disable carrier data by the system based on carrier signalling or + * carrier privileged apps. Carrier data on/off won't affect user settings but will bypass the + * settings and turns off data internally if set to {@code false}. + */ + public static final int DATA_ENABLED_REASON_CARRIER = 2; + + /** + * To indicate enable or disable data by thermal service. + * Thermal data on/off won't affect user settings but will bypass the + * settings and turns off data internally if set to {@code false}. + */ + public static final int DATA_ENABLED_REASON_THERMAL = 3; + + /** + * Control of data connection and provide the reason triggering the data connection control. + * This can be called for following reasons + * <ol> + * <li>data limit is passed {@link #DATA_ENABLED_REASON_POLICY} + * <li>data disabled by carrier {@link #DATA_ENABLED_REASON_CARRIER} + * <li>data disabled by user {@link #DATA_ENABLED_REASON_USER} + * <li>data disabled due to thermal {@link #DATA_ENABLED_REASON_THERMAL} + * </ol> + * If any of the reason is off, then it will result in + * bypassing user preference and result in data to be turned off. + * + * <p>If this object has been created with {@link #createForSubscriptionId}, applies + * to the given subId. Otherwise, applies to + * {@link SubscriptionManager#getDefaultDataSubscriptionId()} + * + * + * @param reason the reason the data enable change is taking place + * @param enabled True if enabling the data, otherwise disabling. + * + * <p>Requires Permission: + * The calling app has carrier privileges (see {@link #hasCarrierPrivileges}) if the reason is + * {@link #DATA_ENABLED_REASON_USER} or {@link #DATA_ENABLED_REASON_CARRIER} or the call app + * has {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} irrespective of + * the reason. + * @throws IllegalStateException if the Telephony process is not currently available. + */ + @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) + public void setDataEnabledForReason(@DataEnabledReason int reason, boolean enabled) { + setDataEnabledForReason(getSubId(), reason, enabled); + } + + private void setDataEnabledForReason(int subId, @DataEnabledReason int reason, + boolean enabled) { + try { ITelephony service = getITelephony(); if (service != null) { - service.setPolicyDataEnabled(enabled, getSubId()); + service.setDataEnabledForReason(subId, reason, enabled); + } else { + throw new IllegalStateException("telephony service is null."); } - } catch (RemoteException e) { - Log.e(TAG, "Error calling ITelephony#setPolicyDataEnabled", e); + } catch (RemoteException ex) { + Log.e(TAG, "Telephony#setDataEnabledForReason RemoteException", ex); + ex.rethrowFromSystemServer(); + } + } + + /** + * Return whether data is enabled for certain reason . + * + * If {@link #isDataEnabledForReason} returns false, it means in data enablement for a + * specific reason is turned off. If any of the reason is off, then it will result in + * bypassing user preference and result in data to be turned off. Call + * {@link #isDataConnectionAllowed} in order to know whether + * data connection is allowed on the device. + * + * <p>If this object has been created with {@link #createForSubscriptionId}, applies + * to the given subId. Otherwise, applies to + * {@link SubscriptionManager#getDefaultDataSubscriptionId()} + * + * @param reason the reason the data enable change is taking place + * @return whether data is enabled for a reason. + * <p>Requires Permission: + * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} or + * {@link android.Manifest.permission#ACCESS_NETWORK_STATE} + * @throws IllegalStateException if the Telephony process is not currently available. + */ + @RequiresPermission(anyOf = {android.Manifest.permission.ACCESS_NETWORK_STATE, + android.Manifest.permission.READ_PHONE_STATE}) + public boolean isDataEnabledForReason(@DataEnabledReason int reason) { + return isDataEnabledForReason(getSubId(), reason); + } + + private boolean isDataEnabledForReason(int subId, @DataEnabledReason int reason) { + try { + ITelephony service = getITelephony(); + if (service != null) { + return service.isDataEnabledForReason(subId, reason); + } else { + throw new IllegalStateException("telephony service is null."); + } + } catch (RemoteException ex) { + Log.e(TAG, "Telephony#isDataEnabledForReason RemoteException", ex); + ex.rethrowFromSystemServer(); } + return false; } /** @@ -11417,10 +12470,14 @@ public class TelephonyManager { * <LI>And possibly others.</LI> * </UL> * @return {@code true} if the overall data connection is allowed; {@code false} if not. - * @hide + * <p>Requires Permission: + * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} or + * {@link android.Manifest.permission#ACCESS_NETWORK_STATE} or + * android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE */ - @SystemApi - @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresPermission(anyOf = {android.Manifest.permission.ACCESS_NETWORK_STATE, + android.Manifest.permission.READ_PHONE_STATE, + android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE}) public boolean isDataConnectionAllowed() { boolean retVal = false; try { @@ -11440,12 +12497,6 @@ public class TelephonyManager { * "Data capable" means that this device supports packet-switched * data connections over the telephony network. * <p> - * Note: the meaning of this flag is subtly different from the - * PackageManager.FEATURE_TELEPHONY system feature, which is available - * on any device with a telephony radio, even if the device is - * voice-only. - * - * @hide */ public boolean isDataCapable() { if (mContext == null) return true; @@ -11454,18 +12505,6 @@ public class TelephonyManager { } /** - * In this mode, modem will not send specified indications when screen is off. - * @hide - */ - public static final int INDICATION_UPDATE_MODE_NORMAL = 1; - - /** - * In this mode, modem will still send specified indications when screen is off. - * @hide - */ - public static final int INDICATION_UPDATE_MODE_IGNORE_SCREEN_OFF = 2; - - /** * The indication for signal strength update. * @hide */ @@ -11672,6 +12711,7 @@ public class TelephonyManager { NETWORK_TYPE_BITMASK_LTE, NETWORK_TYPE_BITMASK_LTE_CA, NETWORK_TYPE_BITMASK_NR, + NETWORK_TYPE_BITMASK_IWLAN }) public @interface NetworkTypeBitMask {} @@ -11858,12 +12898,13 @@ public class TelephonyManager { * @hide */ @SystemApi + @TestApi @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public @NetworkTypeBitMask long getSupportedRadioAccessFamily() { try { ITelephony telephony = getITelephony(); if (telephony != null) { - return (long) telephony.getRadioAccessFamily(getSlotIndex(), getOpPackageName()); + return telephony.getRadioAccessFamily(getSlotIndex(), getOpPackageName()); } else { // This can happen when the ITelephony interface is not up yet. return NETWORK_TYPE_BITMASK_UNKNOWN; @@ -11971,6 +13012,7 @@ public class TelephonyManager { * @hide */ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @SuppressWarnings("AndroidFrameworkClientSidePermissionCheck") @SystemApi public boolean isEmergencyAssistanceEnabled() { mContext.enforceCallingOrSelfPermission( @@ -12060,23 +13102,15 @@ public class TelephonyManager { @NonNull public Map<Integer, List<EmergencyNumber>> getEmergencyNumberList( @EmergencyServiceCategories int categories) { - Map<Integer, List<EmergencyNumber>> emergencyNumberList = new HashMap<>(); + Map<Integer, List<EmergencyNumber>> emergencyNumberListForCategories = new HashMap<>(); try { ITelephony telephony = getITelephony(); if (telephony != null) { - emergencyNumberList = telephony.getEmergencyNumberList( - mContext.getOpPackageName(), mContext.getAttributionTag()); - if (emergencyNumberList != null) { - for (Integer subscriptionId : emergencyNumberList.keySet()) { - List<EmergencyNumber> numberList = emergencyNumberList.get(subscriptionId); - for (EmergencyNumber number : numberList) { - if (!number.isInEmergencyServiceCategories(categories)) { - numberList.remove(number); - } - } - } - } - return emergencyNumberList; + Map<Integer, List<EmergencyNumber>> emergencyNumberList = + telephony.getEmergencyNumberList(mContext.getOpPackageName(), + mContext.getAttributionTag()); + emergencyNumberListForCategories = + filterEmergencyNumbersByCategories(emergencyNumberList, categories); } else { throw new IllegalStateException("telephony service is null."); } @@ -12084,7 +13118,34 @@ public class TelephonyManager { Log.e(TAG, "getEmergencyNumberList with Categories RemoteException", ex); ex.rethrowAsRuntimeException(); } - return emergencyNumberList; + return emergencyNumberListForCategories; + } + + /** + * Filter emergency numbers with categories. + * + * @hide + */ + @VisibleForTesting + public Map<Integer, List<EmergencyNumber>> filterEmergencyNumbersByCategories( + Map<Integer, List<EmergencyNumber>> emergencyNumberList, + @EmergencyServiceCategories int categories) { + Map<Integer, List<EmergencyNumber>> emergencyNumberListForCategories = new HashMap<>(); + if (emergencyNumberList != null) { + for (Integer subscriptionId : emergencyNumberList.keySet()) { + List<EmergencyNumber> allNumbersForSub = emergencyNumberList.get( + subscriptionId); + List<EmergencyNumber> numbersForCategoriesPerSub = new ArrayList<>(); + for (EmergencyNumber number : allNumbersForSub) { + if (number.isInEmergencyServiceCategories(categories)) { + numbersForCategoriesPerSub.add(number); + } + } + emergencyNumberListForCategories.put( + subscriptionId, numbersForCategoriesPerSub); + } + } + return emergencyNumberListForCategories; } /** @@ -12314,22 +13375,12 @@ public class TelephonyManager { try { IOns iOpportunisticNetworkService = getIOns(); if (iOpportunisticNetworkService == null) { - if (executor == null || callback == null) { - return; - } - final long identity = Binder.clearCallingIdentity(); - try { - executor.execute(() -> { - if (Compatibility.isChangeEnabled(CALLBACK_ON_MORE_ERROR_CODE_CHANGE)) { - callback.accept(SET_OPPORTUNISTIC_SUB_REMOTE_SERVICE_EXCEPTION); - } else { - callback.accept(SET_OPPORTUNISTIC_SUB_INACTIVE_SUBSCRIPTION); - } - }); - } finally { - Binder.restoreCallingIdentity(identity); + if (Compatibility.isChangeEnabled(NULL_TELEPHONY_THROW_NO_CB)) { + throw new IllegalStateException("Opportunistic Network Service is null"); + } else { + // Let the general remote exception handling catch this. + throw new RemoteException("Null Opportunistic Network Service!"); } - return; } ISetOpportunisticDataCallback callbackStub = new ISetOpportunisticDataCallback.Stub() { @Override @@ -12352,9 +13403,18 @@ public class TelephonyManager { .setPreferredDataSubscriptionId(subId, needValidation, callbackStub, pkgForDebug); } catch (RemoteException ex) { - Rlog.e(TAG, "setPreferredDataSubscriptionId RemoteException", ex); + Rlog.e(TAG, "setPreferredOpportunisticDataSubscription RemoteException", ex); + if (executor == null || callback == null) { + return; + } + runOnBackgroundThread(() -> executor.execute(() -> { + if (Compatibility.isChangeEnabled(CALLBACK_ON_MORE_ERROR_CODE_CHANGE)) { + callback.accept(SET_OPPORTUNISTIC_SUB_REMOTE_SERVICE_EXCEPTION); + } else { + callback.accept(SET_OPPORTUNISTIC_SUB_INACTIVE_SUBSCRIPTION); + } + })); } - return; } /** @@ -12411,37 +13471,18 @@ public class TelephonyManager { @Nullable @CallbackExecutor Executor executor, @UpdateAvailableNetworksResult @Nullable Consumer<Integer> callback) { String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>"; + Objects.requireNonNull(availableNetworks, "availableNetworks must not be null."); try { IOns iOpportunisticNetworkService = getIOns(); - if (iOpportunisticNetworkService == null || availableNetworks == null) { - if (executor == null || callback == null) { - return; - } - if (iOpportunisticNetworkService == null) { - final long identity = Binder.clearCallingIdentity(); - try { - executor.execute(() -> { - if (Compatibility.isChangeEnabled(CALLBACK_ON_MORE_ERROR_CODE_CHANGE)) { - callback.accept(UPDATE_AVAILABLE_NETWORKS_REMOTE_SERVICE_EXCEPTION); - } else { - callback.accept(UPDATE_AVAILABLE_NETWORKS_UNKNOWN_FAILURE); - } - }); - } finally { - Binder.restoreCallingIdentity(identity); - } + if (iOpportunisticNetworkService == null) { + if (Compatibility.isChangeEnabled(NULL_TELEPHONY_THROW_NO_CB)) { + throw new IllegalStateException("Opportunistic Network Service is null"); } else { - final long identity = Binder.clearCallingIdentity(); - try { - executor.execute(() -> { - callback.accept(UPDATE_AVAILABLE_NETWORKS_INVALID_ARGUMENTS); - }); - } finally { - Binder.restoreCallingIdentity(identity); - } + // Let the general remote exception handling catch this. + throw new RemoteException("Null Opportunistic Network Service!"); } - return; } + IUpdateAvailableNetworksCallback callbackStub = new IUpdateAvailableNetworksCallback.Stub() { @Override @@ -12449,20 +13490,25 @@ public class TelephonyManager { if (executor == null || callback == null) { return; } - final long identity = Binder.clearCallingIdentity(); - try { - executor.execute(() -> { - callback.accept(result); - }); - } finally { - Binder.restoreCallingIdentity(identity); - } + Binder.withCleanCallingIdentity(() -> { + executor.execute(() -> callback.accept(result)); + }); } }; - iOpportunisticNetworkService.updateAvailableNetworks(availableNetworks, callbackStub, - pkgForDebug); + iOpportunisticNetworkService + .updateAvailableNetworks(availableNetworks, callbackStub, pkgForDebug); } catch (RemoteException ex) { Rlog.e(TAG, "updateAvailableNetworks RemoteException", ex); + if (executor == null || callback == null) { + return; + } + runOnBackgroundThread(() -> executor.execute(() -> { + if (Compatibility.isChangeEnabled(CALLBACK_ON_MORE_ERROR_CODE_CHANGE)) { + callback.accept(UPDATE_AVAILABLE_NETWORKS_REMOTE_SERVICE_EXCEPTION); + } else { + callback.accept(UPDATE_AVAILABLE_NETWORKS_UNKNOWN_FAILURE); + } + })); } } @@ -12832,7 +13878,7 @@ public class TelephonyManager { * 1) User data is turned on, or * 2) APN is un-metered for this subscription, or * 3) APN type is whitelisted. E.g. MMS is whitelisted if - * {@link #setAlwaysAllowMmsData(boolean)} is turned on. + * {@link #MOBILE_DATA_POLICY_MMS_ALWAYS_ALLOWED} is enabled. * * @param apnType Value indicating the apn type. Apn types are defined in {@link ApnSetting}. * @return whether data is enabled for a apn type. @@ -12849,9 +13895,7 @@ public class TelephonyManager { return service.isDataEnabledForApn(apnType, getSubId(), pkgForDebug); } } catch (RemoteException ex) { - if (!isSystemProcess()) { - ex.rethrowAsRuntimeException(); - } + Rlog.e(TAG, "Telephony#isDataEnabledForApn RemoteException" + ex); } return false; } @@ -12871,9 +13915,7 @@ public class TelephonyManager { return service.isApnMetered(apnType, getSubId()); } } catch (RemoteException ex) { - if (!isSystemProcess()) { - ex.rethrowAsRuntimeException(); - } + Rlog.e(TAG, "Telephony#isApnMetered RemoteException" + ex); } return true; } @@ -12933,10 +13975,37 @@ public class TelephonyManager { service.setSystemSelectionChannels(specifiers, getSubId(), aidlConsumer); } } catch (RemoteException ex) { - if (!isSystemProcess()) { - ex.rethrowAsRuntimeException(); + Rlog.e(TAG, "Telephony#setSystemSelectionChannels RemoteException" + ex); + } + } + + /** + * Get which bands the modem's background scan is acting on, specified by + * {@link #setSystemSelectionChannels}. + * + * <p>Requires Permission: + * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE} + * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). + * + * @return a list of {@link RadioAccessSpecifier}, or an empty list if no bands are specified. + * @throws IllegalStateException if the Telephony process is not currently available. + * + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public @NonNull List<RadioAccessSpecifier> getSystemSelectionChannels() { + try { + ITelephony service = getITelephony(); + if (service != null) { + return service.getSystemSelectionChannels(getSubId()); + } else { + throw new IllegalStateException("telephony service is null."); } + } catch (RemoteException ex) { + Rlog.e(TAG, "Telephony#getSystemSelectionChannels RemoteException" + ex); } + return new ArrayList<>(); } /** @@ -12963,302 +14032,449 @@ public class TelephonyManager { return service.isMvnoMatched(getSubId(), mvnoType, mvnoMatchData); } } catch (RemoteException ex) { - if (!isSystemProcess()) { - ex.rethrowAsRuntimeException(); - } + Rlog.e(TAG, "Telephony#matchesCurrentSimOperator RemoteException" + ex); } return false; } /** - * Gets the voice call forwarding info {@link CallForwardingInfo}, given the call forward - * reason. + * Callback to be used with {@link #getCallForwarding} + * @hide + */ + @SystemApi + public interface CallForwardingInfoCallback { + /** + * Indicates that the operation was successful. + */ + int RESULT_SUCCESS = 0; + + /** + * Indicates that setting or retrieving the call forwarding info failed with an unknown + * error. + */ + int RESULT_ERROR_UNKNOWN = 1; + + /** + * Indicates that call forwarding is not enabled because the recipient is not on a + * Fixed Dialing Number (FDN) list. + */ + int RESULT_ERROR_FDN_CHECK_FAILURE = 2; + + /** + * Indicates that call forwarding is not supported on the network at this time. + */ + int RESULT_ERROR_NOT_SUPPORTED = 3; + + /** + * Call forwarding errors + * @hide + */ + @IntDef(prefix = { "RESULT_ERROR_" }, value = { + RESULT_ERROR_UNKNOWN, + RESULT_ERROR_NOT_SUPPORTED, + RESULT_ERROR_FDN_CHECK_FAILURE + }) + @Retention(RetentionPolicy.SOURCE) + @interface CallForwardingError{ + } + /** + * Called when the call forwarding info is successfully retrieved from the network. + * @param info information about how calls are forwarded + */ + void onCallForwardingInfoAvailable(@NonNull CallForwardingInfo info); + + /** + * Called when there was an error retrieving the call forwarding information. + * @param error + */ + void onError(@CallForwardingError int error); + } + + /** + * Gets the voice call forwarding info for a given call forwarding reason. * - * @param callForwardingReason the call forwarding reasons + * This method queries the network for the currently set call forwarding configuration for the + * provided call forwarding reason. When the network has provided its response, the result will + * be supplied via the provided {@link Executor} on the provided + * {@link CallForwardingInfoCallback}. * - * @throws IllegalArgumentException if callForwardingReason is not any of - * {@link CallForwardingInfo.REASON_UNCONDITIONAL}, {@link CallForwardingInfo.REASON_BUSY}, - * {@link CallForwardingInfo.REASON_NO_REPLY}, {@link CallForwardingInfo.REASON_NOT_REACHABLE}, - * {@link CallForwardingInfo.REASON_ALL}, {@link CallForwardingInfo.REASON_ALL_CONDITIONAL} + * @param callForwardingReason the call forwarding reason to query. + * @param executor The executor on which to execute the callback once the result is ready. + * @param callback The callback the results should be delivered on. * - * @return {@link CallForwardingInfo} with the status {@link CallForwardingInfo#STATUS_ACTIVE} - * or {@link CallForwardingInfo#STATUS_INACTIVE} and the target phone number to forward calls - * to, if it's available. Otherwise, it will return a {@link CallForwardingInfo} with status - * {@link CallForwardingInfo#STATUS_UNKNOWN_ERROR}, - * {@link CallForwardingInfo#STATUS_NOT_SUPPORTED}, - * or {@link CallForwardingInfo#STATUS_FDN_CHECK_FAILURE} depending on the situation. + * @throws IllegalArgumentException if callForwardingReason is not any of + * {@link CallForwardingInfo#REASON_UNCONDITIONAL}, {@link CallForwardingInfo#REASON_BUSY}, + * {@link CallForwardingInfo#REASON_NO_REPLY}, {@link CallForwardingInfo#REASON_NOT_REACHABLE}, + * {@link CallForwardingInfo#REASON_ALL}, or {@link CallForwardingInfo#REASON_ALL_CONDITIONAL} * * @hide */ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) - @NonNull - public CallForwardingInfo getCallForwarding(@CallForwardingReason int callForwardingReason) { + @SystemApi + public void getCallForwarding(@CallForwardingReason int callForwardingReason, + @NonNull Executor executor, @NonNull CallForwardingInfoCallback callback) { if (callForwardingReason < CallForwardingInfo.REASON_UNCONDITIONAL || callForwardingReason > CallForwardingInfo.REASON_ALL_CONDITIONAL) { throw new IllegalArgumentException("callForwardingReason is out of range"); } + + ICallForwardingInfoCallback internalCallback = new ICallForwardingInfoCallback.Stub() { + @Override + public void onCallForwardingInfoAvailable(CallForwardingInfo info) { + executor.execute(() -> + Binder.withCleanCallingIdentity(() -> + callback.onCallForwardingInfoAvailable(info))); + } + + @Override + public void onError(int error) { + executor.execute(() -> + Binder.withCleanCallingIdentity(() -> + callback.onError(error))); + } + }; + try { ITelephony telephony = getITelephony(); if (telephony != null) { - return telephony.getCallForwarding(getSubId(), callForwardingReason); + telephony.getCallForwarding(getSubId(), callForwardingReason, internalCallback); } } catch (RemoteException ex) { Rlog.e(TAG, "getCallForwarding RemoteException", ex); - } catch (NullPointerException ex) { - Rlog.e(TAG, "getCallForwarding NPE", ex); + ex.rethrowAsRuntimeException(); } - return new CallForwardingInfo( - CallForwardingInfo.STATUS_UNKNOWN_ERROR, 0 /* reason */, null /* number */, - 0 /* timeout */); } /** - * Sets the voice call forwarding info including status (enable/disable), call forwarding - * reason, the number to forward, and the timeout before the forwarding is attempted. + * Sets voice call forwarding behavior as described by the provided {@link CallForwardingInfo}. * - * @param callForwardingInfo {@link CallForwardingInfo} to setup the call forwarding. - * Enabling if {@link CallForwardingInfo#getStatus()} returns - * {@link CallForwardingInfo#STATUS_ACTIVE}; Disabling if - * {@link CallForwardingInfo#getStatus()} returns {@link CallForwardingInfo#STATUS_INACTIVE}. + * This method will enable call forwarding if the provided {@link CallForwardingInfo} returns + * {@code true} from its {@link CallForwardingInfo#isEnabled()} method, and disables call + * forwarding otherwise. * - * @throws IllegalArgumentException if any of the following for parameter callForwardingInfo: - * 0) it is {@code null}. - * 1) {@link CallForwardingInfo#getStatus()} returns neither - * {@link CallForwardingInfo#STATUS_ACTIVE} nor {@link CallForwardingInfo#STATUS_INACTIVE}. - * 2) {@link CallForwardingInfo#getReason()} is not any of - * {@link CallForwardingInfo.REASON_UNCONDITIONAL}, {@link CallForwardingInfo.REASON_BUSY}, - * {@link CallForwardingInfo.REASON_NO_REPLY}, {@link CallForwardingInfo.REASON_NOT_REACHABLE}, - * {@link CallForwardingInfo.REASON_ALL}, {@link CallForwardingInfo.REASON_ALL_CONDITIONAL} - * 3) {@link CallForwardingInfo#getNumber()} returns {@code null}. - * 4) {@link CallForwardingInfo#getTimeoutSeconds()} doesn't return a positive value. + * If you wish to be notified about the results of this operation, provide an {@link Executor} + * and {@link Consumer<Integer>} to be notified asynchronously when the operation completes. * - * @return {@code true} to indicate it was set successfully; {@code false} otherwise. + * @param callForwardingInfo Info about whether calls should be forwarded and where they + * should be forwarded to. + * @param executor The executor on which the listener will be called. Must be non-null if + * {@code listener} is non-null. + * @param resultListener Asynchronous listener that'll be called when the operation completes. + * Called with {@link CallForwardingInfoCallback#RESULT_SUCCESS} if the + * operation succeeded and an error code from + * {@link CallForwardingInfoCallback} it failed. * + * @throws IllegalArgumentException if any of the following are true for the parameter + * callForwardingInfo: + * <ul> + * <li>it is {@code null}.</li> + * <li>{@link CallForwardingInfo#getReason()} is not any of: + * <ul> + * <li>{@link CallForwardingInfo#REASON_UNCONDITIONAL}</li> + * <li>{@link CallForwardingInfo#REASON_BUSY}</li> + * <li>{@link CallForwardingInfo#REASON_NO_REPLY}</li> + * <li>{@link CallForwardingInfo#REASON_NOT_REACHABLE}</li> + * <li>{@link CallForwardingInfo#REASON_ALL}</li> + * <li>{@link CallForwardingInfo#REASON_ALL_CONDITIONAL}</li> + * </ul> + * <li>{@link CallForwardingInfo#getNumber()} returns {@code null} when enabling call + * forwarding</li> + * <li>{@link CallForwardingInfo#getTimeoutSeconds()} returns a non-positive value when + * enabling call forwarding</li> + * </ul> * @hide */ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) - public boolean setCallForwarding(@NonNull CallForwardingInfo callForwardingInfo) { + @SystemApi + public void setCallForwarding(@NonNull CallForwardingInfo callForwardingInfo, + @Nullable @CallbackExecutor Executor executor, + @Nullable @CallForwardingInfoCallback.CallForwardingError + Consumer<Integer> resultListener) { if (callForwardingInfo == null) { throw new IllegalArgumentException("callForwardingInfo is null"); } - int callForwardingStatus = callForwardingInfo.getStatus(); - if (callForwardingStatus != CallForwardingInfo.STATUS_ACTIVE - && callForwardingStatus != CallForwardingInfo.STATUS_INACTIVE) { - throw new IllegalArgumentException( - "callForwardingStatus is neither active nor inactive"); - } int callForwardingReason = callForwardingInfo.getReason(); if (callForwardingReason < CallForwardingInfo.REASON_UNCONDITIONAL || callForwardingReason > CallForwardingInfo.REASON_ALL_CONDITIONAL) { throw new IllegalArgumentException("callForwardingReason is out of range"); } - if (callForwardingInfo.getNumber() == null) { - throw new IllegalArgumentException("callForwarding number is null"); + if (callForwardingInfo.isEnabled()) { + if (callForwardingInfo.getNumber() == null) { + throw new IllegalArgumentException("callForwarding number is null"); + } + if (callForwardingInfo.getTimeoutSeconds() <= 0) { + throw new IllegalArgumentException("callForwarding timeout isn't positive"); + } } - if (callForwardingInfo.getTimeoutSeconds() <= 0) { - throw new IllegalArgumentException("callForwarding timeout isn't positive"); + if (resultListener != null) { + Objects.requireNonNull(executor); } + + IIntegerConsumer internalCallback = new IIntegerConsumer.Stub() { + @Override + public void accept(int result) { + executor.execute(() -> + Binder.withCleanCallingIdentity(() -> resultListener.accept(result))); + } + }; + try { ITelephony telephony = getITelephony(); if (telephony != null) { - return telephony.setCallForwarding(getSubId(), callForwardingInfo); + telephony.setCallForwarding(getSubId(), callForwardingInfo, internalCallback); } } catch (RemoteException ex) { Rlog.e(TAG, "setCallForwarding RemoteException", ex); + ex.rethrowAsRuntimeException(); } catch (NullPointerException ex) { Rlog.e(TAG, "setCallForwarding NPE", ex); + throw ex; } - return false; } /** - * Indicates the call waiting status is active. + * Indicates that call waiting is enabled. * * @hide */ - public static final int CALL_WAITING_STATUS_ACTIVE = 1; + @SystemApi + public static final int CALL_WAITING_STATUS_ENABLED = 1; /** - * Indicates the call waiting status is inactive. + * Indicates that call waiting is disabled. * * @hide */ - public static final int CALL_WAITING_STATUS_INACTIVE = 2; + @SystemApi + public static final int CALL_WAITING_STATUS_DISABLED = 2; /** - * Indicates the call waiting status is with an unknown error. + * Indicates there was an unknown error retrieving the call waiting status. * * @hide */ + @SystemApi public static final int CALL_WAITING_STATUS_UNKNOWN_ERROR = 3; /** - * Indicates the call waiting is not supported (e.g. called via CDMA). + * Indicates the call waiting is not supported on the current network. * * @hide */ + @SystemApi public static final int CALL_WAITING_STATUS_NOT_SUPPORTED = 4; /** - * Call waiting function status - * * @hide */ @IntDef(prefix = { "CALL_WAITING_STATUS_" }, value = { - CALL_WAITING_STATUS_ACTIVE, - CALL_WAITING_STATUS_INACTIVE, - CALL_WAITING_STATUS_NOT_SUPPORTED, - CALL_WAITING_STATUS_UNKNOWN_ERROR + CALL_WAITING_STATUS_ENABLED, + CALL_WAITING_STATUS_DISABLED, + CALL_WAITING_STATUS_UNKNOWN_ERROR, + CALL_WAITING_STATUS_NOT_SUPPORTED, }) @Retention(RetentionPolicy.SOURCE) public @interface CallWaitingStatus { } /** - * Gets the status of voice call waiting function. Call waiting function enables the waiting - * for the incoming call when it reaches the user who is busy to make another call and allows - * users to decide whether to switch to the incoming call. + * Retrieves the call waiting status of this device from the network. * - * @return the status of call waiting function. + * When call waiting is enabled, an incoming call that arrives when the user is already on + * an active call will be held in a waiting state while the user is notified instead of being + * rejected with a busy signal. + * + * @param executor The executor on which the result listener will be called. + * @param resultListener A {@link Consumer} that will be called with the result fetched + * from the network. The result will be one of: + * <ul> + * <li>{@link #CALL_WAITING_STATUS_ENABLED}}</li> + * <li>{@link #CALL_WAITING_STATUS_DISABLED}}</li> + * <li>{@link #CALL_WAITING_STATUS_UNKNOWN_ERROR}}</li> + * <li>{@link #CALL_WAITING_STATUS_NOT_SUPPORTED}}</li> + * </ul> * @hide */ + @SystemApi @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) - public @CallWaitingStatus int getCallWaitingStatus() { + public void getCallWaitingStatus(@NonNull Executor executor, + @NonNull @CallWaitingStatus Consumer<Integer> resultListener) { + Objects.requireNonNull(executor); + Objects.requireNonNull(resultListener); + + IIntegerConsumer internalCallback = new IIntegerConsumer.Stub() { + @Override + public void accept(int result) { + executor.execute(() -> Binder.withCleanCallingIdentity( + () -> resultListener.accept(result))); + } + }; + try { ITelephony telephony = getITelephony(); if (telephony != null) { - return telephony.getCallWaitingStatus(getSubId()); + telephony.getCallWaitingStatus(getSubId(), internalCallback); } } catch (RemoteException ex) { Rlog.e(TAG, "getCallWaitingStatus RemoteException", ex); + ex.rethrowAsRuntimeException(); } catch (NullPointerException ex) { Rlog.e(TAG, "getCallWaitingStatus NPE", ex); + throw ex; } - return CALL_WAITING_STATUS_UNKNOWN_ERROR; } /** - * Sets the status for voice call waiting function. Call waiting function enables the waiting - * for the incoming call when it reaches the user who is busy to make another call and allows - * users to decide whether to switch to the incoming call. + * Sets the call waiting status of this device with the network. + * + * If you wish to be notified about the results of this operation, provide an {@link Executor} + * and {@link Consumer<Integer>} to be notified asynchronously when the operation completes. * - * @param isEnable {@code true} to enable; {@code false} to disable. - * @return {@code true} to indicate it was set successfully; {@code false} otherwise. + * @see #getCallWaitingStatus for a description of the call waiting functionality. * + * @param enabled {@code true} to enable; {@code false} to disable. + * @param executor The executor on which the listener will be called. Must be non-null if + * {@code listener} is non-null. + * @param resultListener Asynchronous listener that'll be called when the operation completes. + * Called with the new call waiting status (either + * {@link #CALL_WAITING_STATUS_ENABLED} or + * {@link #CALL_WAITING_STATUS_DISABLED} if the operation succeeded and + * {@link #CALL_WAITING_STATUS_NOT_SUPPORTED} or + * {@link #CALL_WAITING_STATUS_UNKNOWN_ERROR} if it failed. * @hide */ + @SystemApi @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) - public boolean setCallWaitingStatus(boolean isEnable) { + public void setCallWaitingEnabled(boolean enabled, @Nullable Executor executor, + @Nullable Consumer<Integer> resultListener) { + if (resultListener != null) { + Objects.requireNonNull(executor); + } + + IIntegerConsumer internalCallback = new IIntegerConsumer.Stub() { + @Override + public void accept(int result) { + executor.execute(() -> + Binder.withCleanCallingIdentity(() -> resultListener.accept(result))); + } + }; + try { ITelephony telephony = getITelephony(); if (telephony != null) { - return telephony.setCallWaitingStatus(getSubId(), isEnable); + telephony.setCallWaitingStatus(getSubId(), enabled, internalCallback); } } catch (RemoteException ex) { Rlog.e(TAG, "setCallWaitingStatus RemoteException", ex); + ex.rethrowAsRuntimeException(); } catch (NullPointerException ex) { Rlog.e(TAG, "setCallWaitingStatus NPE", ex); + throw ex; } - return false; } /** - * Set allowing mobile data during voice call. This is used for allowing data on the non-default - * data SIM. When a voice call is placed on the non-default data SIM on DSDS devices, users will - * not be able to use mobile data. By calling this API, data will be temporarily enabled on the - * non-default data SIM during the life cycle of the voice call. + * Controls whether mobile data on the non-default SIM is allowed during a voice call. * - * @param allow {@code true} if allowing using data during voice call, {@code false} if - * disallowed. + * This is used for allowing data on the non-default data SIM when a voice call is placed on + * the non-default data SIM on DSDS devices. If this policy is disabled, users will not be able + * to use mobile data via the non-default data SIM during the call, which may mean no mobile + * data at all since some modem implementations disallow mobile data via the default data SIM + * during voice calls. + * If this policy is enabled, data will be temporarily enabled on the non-default data SIM + * during any voice calls. * - * @return {@code true} if operation is successful. otherwise {@code false}. + * This policy can be enabled and disabled via {@link #setMobileDataPolicyEnabled}. + * @hide + */ + @SystemApi + public static final int MOBILE_DATA_POLICY_DATA_ON_NON_DEFAULT_DURING_VOICE_CALL = 1; + + /** + * Controls whether MMS messages bypass the user-specified "mobile data" toggle. * - * @throws SecurityException if the caller doesn't have the permission. + * When enabled, requests for connections to the MMS APN will be accepted by telephony even if + * the user has turned "mobile data" off on this specific sim card. {@link #isDataEnabledForApn} + * will also return true for {@link ApnSetting#TYPE_MMS}. + * When disabled, the MMS APN will be governed by the same rules as all other APNs. * + * This policy can be enabled and disabled via {@link #setMobileDataPolicyEnabled}. * @hide */ - @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) - public boolean setDataAllowedDuringVoiceCall(boolean allow) { - try { - ITelephony service = getITelephony(); - if (service != null) { - return service.setDataAllowedDuringVoiceCall(getSubId(), allow); - } - } catch (RemoteException ex) { - // This could happen if binder process crashes. - if (!isSystemProcess()) { - ex.rethrowAsRuntimeException(); - } - } - return false; - } + @SystemApi + public static final int MOBILE_DATA_POLICY_MMS_ALWAYS_ALLOWED = 2; /** - * Check whether data is allowed during voice call. This is used for allowing data on the - * non-default data SIM. When a voice call is placed on the non-default data SIM on DSDS - * devices, users will not be able to use mobile data. By calling this API, data will be - * temporarily enabled on the non-default data SIM during the life cycle of the voice call. - * - * @return {@code true} if data is allowed during voice call. + * @hide + */ + @IntDef(prefix = { "MOBILE_DATA_POLICY_" }, value = { + MOBILE_DATA_POLICY_DATA_ON_NON_DEFAULT_DURING_VOICE_CALL, + MOBILE_DATA_POLICY_MMS_ALWAYS_ALLOWED, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface MobileDataPolicy { } + + /** + * Enables or disables a piece of mobile data policy. * - * @throws SecurityException if the caller doesn't have the permission. + * Enables or disables the mobile data policy specified in {@code policy}. See the detailed + * description of each policy constant for what they do. * + * @param policy The data policy to enable. + * @param enabled Whether to enable or disable the policy. * @hide */ - @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) - public boolean isDataAllowedInVoiceCall() { + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + public void setMobileDataPolicyEnabled(@MobileDataPolicy int policy, boolean enabled) { try { ITelephony service = getITelephony(); if (service != null) { - return service.isDataAllowedInVoiceCall(getSubId()); + service.setMobileDataPolicyEnabled(getSubId(), policy, enabled); } } catch (RemoteException ex) { - // This could happen if binder process crashes. - if (!isSystemProcess()) { - ex.rethrowAsRuntimeException(); - } + Rlog.e(TAG, "Telephony#setMobileDataPolicyEnabled RemoteException" + ex); } - return false; } /** - * Set whether the specific sim card always allows MMS connection. If true, MMS network - * request will be accepted by telephony even if user turns "mobile data" off - * on this specific sim card. - * - * @param alwaysAllow whether Mms data is always allowed. - * @return whether operation is successful. + * Fetches the status of a piece of mobile data policy. * + * @param policy The data policy that you want the status for. + * @return {@code true} if enabled, {@code false} otherwise. * @hide */ - @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) - public boolean setAlwaysAllowMmsData(boolean alwaysAllow) { + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public boolean isMobileDataPolicyEnabled(@MobileDataPolicy int policy) { try { ITelephony service = getITelephony(); if (service != null) { - return service.setAlwaysAllowMmsData(getSubId(), alwaysAllow); + return service.isMobileDataPolicyEnabled(getSubId(), policy); } } catch (RemoteException ex) { - if (!isSystemProcess()) { - ex.rethrowAsRuntimeException(); - } + Rlog.e(TAG, "Telephony#isMobileDataPolicyEnabled RemoteException" + ex); } return false; } /** - * The IccLock state or password was changed successfully. + * Indicates that the ICC PIN lock state or PIN was changed successfully. * @hide */ public static final int CHANGE_ICC_LOCK_SUCCESS = Integer.MAX_VALUE; /** - * Check whether ICC pin lock is enabled. - * This is a sync call which returns the cached pin enabled state. - * - * @return {@code true} if ICC lock enabled, {@code false} if ICC lock disabled. + * Check whether ICC PIN lock is enabled. + * This is a sync call which returns the cached PIN enabled state. * + * @return {@code true} if ICC PIN lock enabled, {@code false} if disabled. * @throws SecurityException if the caller doesn't have the permission. + * @throws IllegalStateException if the Telephony process is not currently available. + * + * <p>Requires Permission: + * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE} + * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). * * @hide */ @@ -13270,81 +14486,97 @@ public class TelephonyManager { ITelephony telephony = getITelephony(); if (telephony != null) { return telephony.isIccLockEnabled(getSubId()); + } else { + throw new IllegalStateException("telephony service is null."); } } catch (RemoteException e) { Log.e(TAG, "isIccLockEnabled RemoteException", e); + e.rethrowFromSystemServer(); } return false; } /** - * Set the ICC pin lock enabled or disabled. - * - * If enable/disable ICC pin lock successfully, a value of {@link Integer#MAX_VALUE} is - * returned. - * If an incorrect old password is specified, the return value will indicate how many more - * attempts the user can make to change the password before the SIM is locked. - * Using PUK code to unlock SIM if enter the incorrect old password 3 times. - * - * @param enabled "true" for locked, "false" for unlocked. - * @param password needed to change the ICC pin state, aka. Pin1 - * @return an integer representing the status of IccLock enabled or disabled in the following - * three cases: - * - {@link TelephonyManager#CHANGE_ICC_LOCK_SUCCESS} if enabled or disabled IccLock - * successfully. - * - Positive number and zero for remaining password attempts. - * - Negative number for other failure cases (such like enabling/disabling PIN failed). + * Enable or disable the ICC PIN lock. * + * @param enabled "true" for locked, "false" for unlocked. + * @param pin needed to change the ICC PIN lock, aka. Pin1. + * @return the result of enabling or disabling the ICC PIN lock. * @throws SecurityException if the caller doesn't have the permission. + * @throws IllegalStateException if the Telephony process is not currently available. + * + * <p>Requires Permission: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling + * app has carrier privileges (see {@link #hasCarrierPrivileges}). * * @hide */ + @SystemApi + @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) - public int setIccLockEnabled(boolean enabled, @NonNull String password) { - checkNotNull(password, "setIccLockEnabled password can't be null."); + public PinResult setIccLockEnabled(boolean enabled, @NonNull String pin) { + checkNotNull(pin, "setIccLockEnabled pin can't be null."); try { ITelephony telephony = getITelephony(); if (telephony != null) { - return telephony.setIccLockEnabled(getSubId(), enabled, password); + int result = telephony.setIccLockEnabled(getSubId(), enabled, pin); + if (result == CHANGE_ICC_LOCK_SUCCESS) { + return new PinResult(PinResult.PIN_RESULT_TYPE_SUCCESS, 0); + } else if (result < 0) { + return PinResult.getDefaultFailedResult(); + } else { + return new PinResult(PinResult.PIN_RESULT_TYPE_INCORRECT, result); + } + } else { + throw new IllegalStateException("telephony service is null."); } } catch (RemoteException e) { Log.e(TAG, "setIccLockEnabled RemoteException", e); + e.rethrowFromSystemServer(); } - return 0; + return PinResult.getDefaultFailedResult(); } /** - * Change the ICC password used in ICC pin lock. - * - * If the password was changed successfully, a value of {@link Integer#MAX_VALUE} is returned. - * If an incorrect old password is specified, the return value will indicate how many more - * attempts the user can make to change the password before the SIM is locked. - * Using PUK code to unlock SIM if enter the incorrect old password 3 times. - * - * @param oldPassword is the old password - * @param newPassword is the new password - * @return an integer representing the status of IccLock changed in the following three cases: - * - {@link TelephonyManager#CHANGE_ICC_LOCK_SUCCESS} if changed IccLock successfully. - * - Positive number and zero for remaining password attempts. - * - Negative number for other failure cases (such like enabling/disabling PIN failed). + * Change the ICC lock PIN. * + * @param oldPin is the old PIN + * @param newPin is the new PIN + * @return The result of changing the ICC lock PIN. * @throws SecurityException if the caller doesn't have the permission. + * @throws IllegalStateException if the Telephony process is not currently available. + * + * <p>Requires Permission: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling + * app has carrier privileges (see {@link #hasCarrierPrivileges}). * * @hide */ + @SystemApi + @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) - public int changeIccLockPassword(@NonNull String oldPassword, @NonNull String newPassword) { - checkNotNull(oldPassword, "changeIccLockPassword oldPassword can't be null."); - checkNotNull(newPassword, "changeIccLockPassword newPassword can't be null."); + public PinResult changeIccLockPin(@NonNull String oldPin, @NonNull String newPin) { + checkNotNull(oldPin, "changeIccLockPin oldPin can't be null."); + checkNotNull(newPin, "changeIccLockPin newPin can't be null."); try { ITelephony telephony = getITelephony(); if (telephony != null) { - return telephony.changeIccLockPassword(getSubId(), oldPassword, newPassword); + int result = telephony.changeIccLockPassword(getSubId(), oldPin, newPin); + if (result == CHANGE_ICC_LOCK_SUCCESS) { + return new PinResult(PinResult.PIN_RESULT_TYPE_SUCCESS, 0); + } else if (result < 0) { + return PinResult.getDefaultFailedResult(); + } else { + return new PinResult(PinResult.PIN_RESULT_TYPE_INCORRECT, result); + } + } else { + throw new IllegalStateException("telephony service is null."); } } catch (RemoteException e) { - Log.e(TAG, "changeIccLockPassword RemoteException", e); + Log.e(TAG, "changeIccLockPin RemoteException", e); + e.rethrowFromSystemServer(); } - return 0; + return PinResult.getDefaultFailedResult(); } /** @@ -13366,6 +14598,161 @@ public class TelephonyManager { } } + /** + * No error. Operation succeeded. + * @hide + */ + @SystemApi + public static final int ENABLE_NR_DUAL_CONNECTIVITY_SUCCESS = 0; + + /** + * NR Dual connectivity enablement is not supported. + * @hide + */ + @SystemApi + public static final int ENABLE_NR_DUAL_CONNECTIVITY_NOT_SUPPORTED = 1; + + /** + * Radio is not available. + * @hide + */ + @SystemApi + public static final int ENABLE_NR_DUAL_CONNECTIVITY_RADIO_NOT_AVAILABLE = 2; + + /** + * Internal Radio error. + * @hide + */ + @SystemApi + public static final int ENABLE_NR_DUAL_CONNECTIVITY_RADIO_ERROR = 3; + + /** + * Currently in invalid state. Not able to process the request. + * @hide + */ + @SystemApi + public static final int ENABLE_NR_DUAL_CONNECTIVITY_INVALID_STATE = 4; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"ENABLE_NR_DUAL_CONNECTIVITY"}, value = { + ENABLE_NR_DUAL_CONNECTIVITY_SUCCESS, + ENABLE_NR_DUAL_CONNECTIVITY_NOT_SUPPORTED, + ENABLE_NR_DUAL_CONNECTIVITY_INVALID_STATE, + ENABLE_NR_DUAL_CONNECTIVITY_RADIO_NOT_AVAILABLE, + ENABLE_NR_DUAL_CONNECTIVITY_RADIO_ERROR}) + public @interface EnableNrDualConnectivityResult {} + + /** + * Enable NR dual connectivity. Enabled state does not mean dual connectivity + * is active. It means device is allowed to connect to both primary and secondary. + * + * @hide + */ + @SystemApi + public static final int NR_DUAL_CONNECTIVITY_ENABLE = 1; + + /** + * Disable NR dual connectivity. Disabled state does not mean the secondary cell is released. + * Modem will release it only if the current bearer is released to avoid radio link failure. + * @hide + */ + @SystemApi + public static final int NR_DUAL_CONNECTIVITY_DISABLE = 2; + + /** + * Disable NR dual connectivity and force the secondary cell to be released if dual connectivity + * was active. This will result in radio link failure. + * @hide + */ + @SystemApi + public static final int NR_DUAL_CONNECTIVITY_DISABLE_IMMEDIATE = 3; + + /** + * @hide + */ + @IntDef(prefix = { "NR_DUAL_CONNECTIVITY_" }, value = { + NR_DUAL_CONNECTIVITY_ENABLE, + NR_DUAL_CONNECTIVITY_DISABLE, + NR_DUAL_CONNECTIVITY_DISABLE_IMMEDIATE, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface NrDualConnectivityState { + } + + /** + * Enable/Disable E-UTRA-NR Dual Connectivity. + * + * This api is supported only if + * {@link android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported} + * ({@link TelephonyManager#CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE}) + * returns true. + * @param nrDualConnectivityState expected NR dual connectivity state + * This can be passed following states + * <ol> + * <li>Enable NR dual connectivity {@link #NR_DUAL_CONNECTIVITY_ENABLE} + * <li>Disable NR dual connectivity {@link #NR_DUAL_CONNECTIVITY_DISABLE} + * <li>Disable NR dual connectivity and force secondary cell to be released + * {@link #NR_DUAL_CONNECTIVITY_DISABLE_IMMEDIATE} + * </ol> + * @return operation result. + * @throws IllegalStateException if the Telephony process is not currently available. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature( + enforcement = "android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported", + value = TelephonyManager.CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE) + public @EnableNrDualConnectivityResult int setNrDualConnectivityState( + @NrDualConnectivityState int nrDualConnectivityState) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + return telephony.setNrDualConnectivityState(getSubId(), nrDualConnectivityState); + } else { + throw new IllegalStateException("telephony service is null."); + } + } catch (RemoteException ex) { + Rlog.e(TAG, "setNrDualConnectivityState RemoteException", ex); + ex.rethrowFromSystemServer(); + } + + return ENABLE_NR_DUAL_CONNECTIVITY_INVALID_STATE; + } + + /** + * Is E-UTRA-NR Dual Connectivity enabled. + * This api is supported only if + * {@link android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported} + * ({@link TelephonyManager#CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE}) + * returns true. + * @return true if dual connectivity is enabled else false. Enabled state does not mean dual + * connectivity is active. It means the device is allowed to connect to both primary and + * secondary cell. + * @throws IllegalStateException if the Telephony process is not currently available. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresFeature( + enforcement = "android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported", + value = TelephonyManager.CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE) + public boolean isNrDualConnectivityEnabled() { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + return telephony.isNrDualConnectivityEnabled(getSubId()); + } else { + throw new IllegalStateException("telephony service is null."); + } + } catch (RemoteException ex) { + Rlog.e(TAG, "isNRDualConnectivityEnabled RemoteException", ex); + ex.rethrowFromSystemServer(); + } + return false; + } + private static class DeathRecipient implements IBinder.DeathRecipient { @Override public void binderDied() { @@ -13380,6 +14767,10 @@ public class TelephonyManager { */ private static void resetServiceCache() { synchronized (sCacheLock) { + if (sITelephony != null) { + sITelephony.asBinder().unlinkToDeath(sServiceDeath, 0); + sITelephony = null; + } if (sISub != null) { sISub.asBinder().unlinkToDeath(sServiceDeath, 0); sISub = null; @@ -13396,9 +14787,9 @@ public class TelephonyManager { } } - /** - * @hide - */ + /** + * @hide + */ static IPhoneSubInfo getSubscriberInfoService() { // Keeps cache disabled until test fixes are checked into AOSP. if (!sServiceHandleCacheEnabled) { @@ -13517,6 +14908,15 @@ public class TelephonyManager { } /** + * Setup sITelephony for testing. + * @hide + */ + @VisibleForTesting + public static void setupITelephonyForTest(ITelephony telephony) { + sITelephony = telephony; + } + + /** * Whether device can connect to 5G network when two SIMs are active. * @hide * TODO b/153669716: remove or make system API. @@ -13532,4 +14932,837 @@ public class TelephonyManager { return true; } } + + /** + * Returns a list of the equivalent home PLMNs (EF_EHPLMN) from the USIM app. + * + * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} + * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). + * + * @return A list of equivalent home PLMNs. Returns an empty list if EF_EHPLMN is empty or + * does not exist on the SIM card. + * + * @throws IllegalStateException if the Telephony process is not currently available. + * @throws SecurityException if the caller doesn't have the permission. + * + */ + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + public @NonNull List<String> getEquivalentHomePlmns() { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + return telephony.getEquivalentHomePlmns(getSubId(), mContext.getOpPackageName(), + getAttributionTag()); + } else { + throw new IllegalStateException("telephony service is null."); + } + } catch (RemoteException ex) { + Rlog.e(TAG, "Telephony#getEquivalentHomePlmns RemoteException" + ex); + } + + return Collections.emptyList(); + } + + /** + * Indicates whether {@link CarrierBandwidth#getSecondaryDownlinkCapacityKbps()} and + * {@link CarrierBandwidth#getSecondaryUplinkCapacityKbps()} are visible. See comments + * on respective methods for more information. + * + * @hide + */ + @SystemApi + public static final String CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE = + "CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE"; + + /** + * Indicates whether {@link #setPreferredNetworkType}, {@link + * #setPreferredNetworkTypeBitmask}, {@link #setAllowedNetworkTypes} and + * {@link #setAllowedNetworkTypesForReason} rely on + * setAllowedNetworkTypesBitmap instead of setPreferredNetworkTypesBitmap on the radio + * interface. + * + * @hide + */ + @SystemApi + public static final String CAPABILITY_ALLOWED_NETWORK_TYPES_USED = + "CAPABILITY_ALLOWED_NETWORK_TYPES_USED"; + + /** + * Indicates whether {@link #setNrDualConnectivityState()} and + * {@link #isNrDualConnectivityEnabled()} ()} are available. See comments + * on respective methods for more information. + * + * @hide + */ + @SystemApi + public static final String CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE = + "CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE"; + + /** + * Indicates whether a data throttling request sent with {@link #sendThermalMitigationRequest} + * is supported. See comments on {@link #sendThermalMitigationRequest} for more information. + * + * @hide + */ + @SystemApi + public static final String CAPABILITY_THERMAL_MITIGATION_DATA_THROTTLING = + "CAPABILITY_THERMAL_MITIGATION_DATA_THROTTLING"; + + /** + * Indicates whether {@link #getNetworkSlicingConfiguration} is supported. See comments on + * respective methods for more information. + */ + public static final String CAPABILITY_SLICING_CONFIG_SUPPORTED = + "CAPABILITY_SLICING_CONFIG_SUPPORTED"; + + /** + * Indicates whether PHYSICAL_CHANNEL_CONFIG HAL1.6 is supported. See comments on + * respective methods for more information. + * + * @hide + */ + public static final String CAPABILITY_PHYSICAL_CHANNEL_CONFIG_1_6_SUPPORTED = + "CAPABILITY_PHYSICAL_CHANNEL_CONFIG_1_6_SUPPORTED"; + + /** + * Indicates whether modem supports handling parsed SIM phonebook records through the RIL, + * both batched reads and individual writes. + * + * @hide + */ + public static final String CAPABILITY_SIM_PHONEBOOK_IN_MODEM = + "CAPABILITY_SIM_PHONEBOOK_IN_MODEM"; + + /** + * A list of the radio interface capability values with public valid constants. + * + * Here is a related list for the systemapi-only valid constants: + * CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE + * CAPABILITY_ALLOWED_NETWORK_TYPES_USED + * CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE + * CAPABILITY_THERMAL_MITIGATION_DATA_THROTTLING + * + * @hide + * TODO(b/185508047): Doc generation for mixed public/systemapi StringDefs formats badly. + */ + @Retention(RetentionPolicy.SOURCE) + @StringDef(prefix = "CAPABILITY_", value = { + CAPABILITY_SLICING_CONFIG_SUPPORTED, + CAPABILITY_SIM_PHONEBOOK_IN_MODEM, + }) + public @interface RadioInterfaceCapability {} + + /** + * Whether the device supports a given capability on the radio interface. + * + * If the capability is not in the set of radio interface capabilities, false is returned. + * + * @param capability the name of the capability to check for + * @return the availability of the capability + */ + public boolean isRadioInterfaceCapabilitySupported( + @NonNull @RadioInterfaceCapability String capability) { + try { + if (capability == null) return false; + + ITelephony telephony = getITelephony(); + if (telephony != null) { + return telephony.isRadioInterfaceCapabilitySupported(capability); + } else { + throw new IllegalStateException("telephony service is null."); + } + } catch (RemoteException ex) { + Rlog.e(TAG, "Telephony#isRadioInterfaceCapabilitySupported RemoteException" + ex); + } + return false; + } + + /** + * Indicates that the thermal mitigation request was completed successfully. + * + * @hide + */ + @SystemApi + public static final int THERMAL_MITIGATION_RESULT_SUCCESS = 0; + + /** + * Indicates that the thermal mitigation request was not completed because of a modem error. + * + * @hide + */ + @SystemApi + public static final int THERMAL_MITIGATION_RESULT_MODEM_ERROR = 1; + + /** + * Indicates that the thermal mitigation request was not completed because the modem is not + * available. + * + * @hide + */ + @SystemApi + public static final int THERMAL_MITIGATION_RESULT_MODEM_NOT_AVAILABLE = 2; + + /** + * Indicates that the thermal mitigation request could not power off the radio due to the device + * either being in an active voice call, device pending an emergency call, or any other state + * that would dissallow powering off of radio. + * + * @hide + */ + @SystemApi + public static final int THERMAL_MITIGATION_RESULT_INVALID_STATE = 3; + + /** + * Indicates that the thermal mitigation request resulted an unknown error. + * + * @hide + */ + @SystemApi + public static final int THERMAL_MITIGATION_RESULT_UNKNOWN_ERROR = 4; + + /** + * Thermal mitigation request to control functionalities at modem. Thermal mitigation is done + * per-subscription. Caller must be sure to bind the TelephonyManager instance to subId by + * calling {@link #createForSubscriptionId(int)} if they want thermal mitigation on a specific + * subscription Id. Otherwise, TelephonyManager will use the default subscription. + * + * Calling this does not guarantee that the thermal mitigation action requested was done to + * completion. A thermal module should actively monitor the temperature levels and request an + * appropriate thermal mitigation action. Every action is assumed to be done 'on top of' the + * previous action, where the order of actions from least thermal mitigation to most is as + * follows: + * <ol> + * <li>{@link ThermalMitigationRequest#THERMAL_MITIGATION_ACTION_DATA_THROTTLING}</li> + * <ol> + * <li>{@link DataThrottlingRequest#DATA_THROTTLING_ACTION_NO_DATA_THROTTLING}</li> + * <li>{@link DataThrottlingRequest#DATA_THROTTLING_ACTION_THROTTLE_SECONDARY_CARRIER}</li> + * <li>{@link DataThrottlingRequest#DATA_THROTTLING_ACTION_THROTTLE_PRIMARY_CARRIER}</li> + * </ol> + * <li>{@link ThermalMitigationRequest#THERMAL_MITIGATION_ACTION_VOICE_ONLY}</li> + * <li>{@link ThermalMitigationRequest#THERMAL_MITIGATION_ACTION_RADIO_OFF}</li> + * </ol> + * + * So, for example, requesting {@link + * DataThrottlingRequest#DATA_THROTTLING_ACTION_THROTTLE_PRIMARY_CARRIER} will ensure that the + * data on secondary carrier has been disabled before throttling on primary carrier. {@link + * ThermalMitigationRequest#THERMAL_MITIGATION_ACTION_VOICE_ONLY} will ensure that data on both + * primary and secondary have been disabled. {@link + * ThermalMitigationRequest#THERMAL_MITIGATION_ACTION_RADIO_OFF} will ensure that voice is + * disabled and that data on both primary and secondary carriers are disabled before turning + * radio off. {@link DataThrottlingRequest#DATA_THROTTLING_ACTION_HOLD} is not part of the order + * and can be used at any time during data throttling to hold onto the current level of data + * throttling. + * + * <p> If {@link android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported}({@link + * #CAPABILITY_THERMAL_MITIGATION_DATA_THROTTLING}) returns false, then sending a {@link + * DataThrottlingRequest#DATA_THROTTLING_ACTION_HOLD}, {@link + * DataThrottlingRequest#DATA_THROTTLING_ACTION_THROTTLE_SECONDARY_CARRIER}, or {@link + * DataThrottlingRequest#DATA_THROTTLING_ACTION_THROTTLE_PRIMARY_CARRIER} will result in {@link + * IllegalArgumentException} being thrown. However, on devices that do not + * support data throttling, {@link + * DataThrottlingRequest#DATA_THROTTLING_ACTION_NO_DATA_THROTTLING} can still be requested in + * order to undo the mitigations above it (i.e {@link + * ThermalMitigationRequest#THERMAL_MITIGATION_ACTION_VOICE_ONLY} and/or {@link + * ThermalMitigationRequest#THERMAL_MITIGATION_ACTION_RADIO_OFF}). </p> + * + * <p> In addition to the {@link Manifest.permission#MODIFY_PHONE_STATE} permission, callers of + * this API must also be listed in the device configuration as an authorized app in + * {@code packages/services/Telephony/res/values/config.xml} under the + * {@code thermal_mitigation_allowlisted_packages} key. </p> + * + * @param thermalMitigationRequest Thermal mitigation request. See {@link + * ThermalMitigationRequest} for details. + * + * @throws IllegalStateException if the Telephony process is not currently available. + * @throws IllegalArgumentException if the thermalMitigationRequest had invalid parameters or + * if the device's modem does not support data throttling. + * + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @ThermalMitigationResult + public int sendThermalMitigationRequest( + @NonNull ThermalMitigationRequest thermalMitigationRequest) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + return telephony.sendThermalMitigationRequest(getSubId(), thermalMitigationRequest, + getOpPackageName()); + } + throw new IllegalStateException("telephony service is null."); + } catch (RemoteException ex) { + Log.e(TAG, "Telephony#thermalMitigationRequest RemoteException", ex); + ex.rethrowFromSystemServer(); + } + return THERMAL_MITIGATION_RESULT_UNKNOWN_ERROR; + } + + /** + * Registers a callback object to receive notification of changes in specified telephony states. + * <p> + * To register a callback, pass a {@link TelephonyCallback} which implements + * interfaces of events. For example, + * FakeServiceStateCallback extends {@link TelephonyCallback} implements + * {@link TelephonyCallback.ServiceStateListener}. + * + * At registration, and when a specified telephony state changes, the telephony manager invokes + * the appropriate callback method on the callback object and passes the current (updated) + * values. + * <p> + * + * If this TelephonyManager object has been created with {@link #createForSubscriptionId}, + * applies to the given subId. Otherwise, applies to + * {@link SubscriptionManager#getDefaultSubscriptionId()}. To register events for multiple + * subIds, pass a separate callback object to each TelephonyManager object created with + * {@link #createForSubscriptionId}. + * + * Note: if you call this method while in the middle of a binder transaction, you <b>must</b> + * call {@link android.os.Binder#clearCallingIdentity()} before calling this method. A + * {@link SecurityException} will be thrown otherwise. + * + * This API should be used sparingly -- large numbers of callbacks will cause system + * instability. If a process has registered too many callbacks without unregistering them, it + * may encounter an {@link IllegalStateException} when trying to register more callbacks. + * + * @param executor The executor of where the callback will execute. + * @param callback The {@link TelephonyCallback} object to register. + */ + public void registerTelephonyCallback(@NonNull @CallbackExecutor Executor executor, + @NonNull TelephonyCallback callback) { + + if (mContext == null) { + throw new IllegalStateException("telephony service is null."); + } + + if (executor == null || callback == null) { + throw new IllegalArgumentException("TelephonyCallback and executor must be non-null"); + } + mTelephonyRegistryMgr = (TelephonyRegistryManager) + mContext.getSystemService(Context.TELEPHONY_REGISTRY_SERVICE); + if (mTelephonyRegistryMgr != null) { + mTelephonyRegistryMgr.registerTelephonyCallback(executor, mSubId, getOpPackageName(), + getAttributionTag(), callback, getITelephony() != null); + } else { + throw new IllegalStateException("telephony service is null."); + } + } + + /** + * Unregister an existing {@link TelephonyCallback}. + * + * @param callback The {@link TelephonyCallback} object to unregister. + */ + public void unregisterTelephonyCallback(@NonNull TelephonyCallback callback) { + + if (mContext == null) { + throw new IllegalStateException("telephony service is null."); + } + + if (callback.callback == null) { + return; + } + + mTelephonyRegistryMgr = mContext.getSystemService(TelephonyRegistryManager.class); + if (mTelephonyRegistryMgr != null) { + mTelephonyRegistryMgr.unregisterTelephonyCallback(mSubId, getOpPackageName(), + getAttributionTag(), callback, getITelephony() != null); + } else { + throw new IllegalStateException("telephony service is null."); + } + } + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"GBA_FAILURE_REASON_"}, value = { + GBA_FAILURE_REASON_UNKNOWN, + GBA_FAILURE_REASON_FEATURE_NOT_SUPPORTED, + GBA_FAILURE_REASON_FEATURE_NOT_READY, + GBA_FAILURE_REASON_NETWORK_FAILURE, + GBA_FAILURE_REASON_INCORRECT_NAF_ID, + GBA_FAILURE_REASON_SECURITY_PROTOCOL_NOT_SUPPORTED}) + public @interface AuthenticationFailureReason {} + + /** + * GBA Authentication has failed for an unknown reason. + * + * <p>The caller should retry a message that failed with this response. + * @hide + */ + @SystemApi + public static final int GBA_FAILURE_REASON_UNKNOWN = 0; + + /** + * GBA Authentication is not supported by the carrier, SIM or android. + * + * <p>Application should use other authentication mechanisms if possible. + * @hide + */ + @SystemApi + public static final int GBA_FAILURE_REASON_FEATURE_NOT_SUPPORTED = 1; + + /** + * GBA Authentication service is not ready for use. + * + * <p>Application could try again at a later time. + * @hide + */ + @SystemApi + public static final int GBA_FAILURE_REASON_FEATURE_NOT_READY = 2; + + /** + * GBA Authentication has been failed by the network. + * @hide + */ + @SystemApi + public static final int GBA_FAILURE_REASON_NETWORK_FAILURE = 3; + + /** + * GBA Authentication has failed due to incorrect NAF URL. + * @hide + */ + @SystemApi + public static final int GBA_FAILURE_REASON_INCORRECT_NAF_ID = 4; + + /** + * GBA Authentication has failed due to unsupported security protocol + * @hide + */ + @SystemApi + public static final int GBA_FAILURE_REASON_SECURITY_PROTOCOL_NOT_SUPPORTED = 5; + + /** + * The callback associated with a {@link #bootstrapAuthenticationRequest()}. + * @hide + */ + @SystemApi + public static class BootstrapAuthenticationCallback { + + /** + * Invoked when the previously requested GBA keys are available (@see + * bootstrapAuthenticationRequest()). + * @param gbaKey Ks_NAF/Ks_ext_NAF Response + * @param transactionId Bootstrapping Transaction Identifier + */ + public void onKeysAvailable(@NonNull byte[] gbaKey, @NonNull String transactionId) {} + + /** + * @param reason The reason for the authentication failure. + */ + public void onAuthenticationFailure(@AuthenticationFailureReason int reason) {} + } + + /** + * Used to get the Generic Bootstrapping Architecture authentication keys + * KsNAF/Ks_ext_NAF for a particular NAF as defined in 3GPP spec TS 33.220 for + * the specified sub id. + * + * <p>Application must be prepared to wait for receiving the Gba keys through the + * registered callback and not invoke the API on the main application thread. + * Application also must call the api to get the fresh key every time instead + * of caching the key. + * + * Following steps may be invoked on the API call depending on the state of the + * underlying GBA implementation: + * <ol> + * <li>Resolve and bind to a Gba implementation.</li> + * <li>Run bootstrapping if no valid keys are available or bootstrapping is forced.</li> + * <li>Generate the ks_NAF/ ks_Ext_NAF to be returned via the callback.</li> + * </ol> + * + * <p> Requires Permission: + * <ul> + * <li>{@link android.Manifest.permission#MODIFY_PHONE_STATE},</li> + * <li>{@link android.Manifest.permission#PERFORM_IMS_SINGLE_REGISTRATION},</li> + * <li>or that the caller has carrier privileges (see + * {@link TelephonyManager#hasCarrierPrivileges()}).</li> + * </ul> + * @param appType icc application type, like {@link #APPTYPE_USIM} or {@link + * #APPTYPE_ISIM} or {@link#APPTYPE_UNKNOWN} + * @param nafId A URI to specify Network Application Function(NAF) fully qualified domain + * name (FQDN) and the selected GBA mode. The authority of the URI must contain two parts + * delimited by "@" sign. The first part is the constant string "3GPP-bootstrapping" (GBA_ME), + * "3GPP-bootstrapping-uicc" (GBA_ U), or "3GPP-bootstrapping-digest" (GBA_Digest). + * The second part shall be the FQDN of the NAF. The scheme of the URI is not actually used + * for the authentication, which may be set the same as the resource that the application is + * going to access. For example, the nafId can be + * "https://3GPP-bootstrapping@naf1.operator.com", + * "https://3GPP-bootstrapping-uicc@naf1.operator.com", + * "https://3GPP-bootstrapping-digest@naf1.operator.com", + * "ftps://3GPP-bootstrapping-digest@naf1.operator.com". + * @param securityProtocol Security protocol identifier between UE and NAF. See + * 3GPP TS 33.220 Annex H. Application can use + * {@link UaSecurityProtocolIdentifier#createDefaultUaSpId}, + * {@link UaSecurityProtocolIdentifier#create3GppUaSpId}, + * to create the ua security protocol identifier as needed + * @param forceBootStrapping true=force bootstrapping, false=do not force + * bootstrapping. Bootstrapping shouldn't be forced unless the application sees + * authentication errors from the server. + * @param e The {@link Executor} that will be used to call the Gba callback. + * @param callback A callback called on the supplied {@link Executor} that will + * contain the GBA Ks_NAF/Ks_ext_NAF when available. If the NAF keys are + * available and valid at the time of call and bootstrapping is not requested, + * then the callback shall be invoked with the available keys. + * @hide + */ + @SystemApi + @WorkerThread + @RequiresPermission(anyOf = {android.Manifest.permission.MODIFY_PHONE_STATE, + Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION}) + public void bootstrapAuthenticationRequest( + @UiccAppTypeExt int appType, @NonNull Uri nafId, + @NonNull UaSecurityProtocolIdentifier securityProtocol, + boolean forceBootStrapping, @NonNull Executor e, + @NonNull BootstrapAuthenticationCallback callback) { + try { + ITelephony service = getITelephony(); + if (service == null) { + e.execute(() -> callback.onAuthenticationFailure( + GBA_FAILURE_REASON_FEATURE_NOT_READY)); + return; + } + service.bootstrapAuthenticationRequest( + getSubId(), appType, nafId, securityProtocol, forceBootStrapping, + new IBootstrapAuthenticationCallback.Stub() { + @Override + public void onKeysAvailable(int token, byte[] gbaKey, + String transactionId) { + final long identity = Binder.clearCallingIdentity(); + try { + e.execute(() -> callback.onKeysAvailable(gbaKey, transactionId)); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + @Override + public void onAuthenticationFailure(int token, int reason) { + final long identity = Binder.clearCallingIdentity(); + try { + e.execute(() -> callback.onAuthenticationFailure(reason)); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + }); + } catch (RemoteException exception) { + Log.e(TAG, "Error calling ITelephony#bootstrapAuthenticationRequest", exception); + e.execute(() -> callback.onAuthenticationFailure(GBA_FAILURE_REASON_FEATURE_NOT_READY)); + } + } + + /** + * The network type is valid or not. + * + * @param networkType The network type {@link NetworkType}. + * @return {@code true} if valid, {@code false} otherwise. + * + * @hide + */ + public static boolean isNetworkTypeValid(@NetworkType int networkType) { + return networkType >= TelephonyManager.NETWORK_TYPE_UNKNOWN && + networkType <= TelephonyManager.NETWORK_TYPE_NR; + } + + /** + * Set a {@link SignalStrengthUpdateRequest} to receive notification when signal quality + * measurements breach the specified thresholds. + * + * To be notified, set the signal strength update request and then register + * {@link TelephonyManager#listen(PhoneStateListener, int)} with + * {@link PhoneStateListener#LISTEN_SIGNAL_STRENGTHS}. The notification will arrive through + * {@link PhoneStateListener#onSignalStrengthsChanged(SignalStrength)}. + * + * To stop receiving the notification over the specified thresholds, pass the same + * {@link SignalStrengthUpdateRequest} object to + * {@link #clearSignalStrengthUpdateRequest(SignalStrengthUpdateRequest)}. + * + * System will clean up the {@link SignalStrengthUpdateRequest} if the caller process died + * without calling {@link #clearSignalStrengthUpdateRequest(SignalStrengthUpdateRequest)}. + * + * If this TelephonyManager object has been created with {@link #createForSubscriptionId}, + * applies to the given subId. Otherwise, applies to + * {@link SubscriptionManager#getDefaultSubscriptionId()}. To request for multiple subIds, + * pass a request object to each TelephonyManager object created with + * {@link #createForSubscriptionId}. + * + * <p>Requires Permission: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} + * or that the calling app has carrier privileges (see + * {@link TelephonyManager#hasCarrierPrivileges}). + * + * Note that the thresholds in the request will be used on a best-effort basis; the system may + * modify requests to multiplex various request sources or to optimize power consumption. The + * caller should not expect to be notified with the exactly the same thresholds. + * + * @see #clearSignalStrengthUpdateRequest(SignalStrengthUpdateRequest) + * + * @param request the SignalStrengthUpdateRequest to be set into the System + * + * @throws IllegalStateException if a new request is set with same subId from the same caller + */ + @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + public void setSignalStrengthUpdateRequest(@NonNull SignalStrengthUpdateRequest request) { + Objects.requireNonNull(request, "request must not be null"); + + try { + ITelephony service = getITelephony(); + if (service != null) { + service.setSignalStrengthUpdateRequest(getSubId(), request, getOpPackageName()); + } + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#setSignalStrengthUpdateRequest", e); + } + } + + /** + * Clear a {@link SignalStrengthUpdateRequest} from the system. + * + * <p>Requires Permission: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} + * or that the calling app has carrier privileges (see + * {@link TelephonyManager#hasCarrierPrivileges}). + * + * <p>If the given request was not set before, this operation is a no-op. + * + * @see #setSignalStrengthUpdateRequest(SignalStrengthUpdateRequest) + * + * @param request the SignalStrengthUpdateRequest to be cleared from the System + */ + @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + public void clearSignalStrengthUpdateRequest(@NonNull SignalStrengthUpdateRequest request) { + Objects.requireNonNull(request, "request must not be null"); + + try { + ITelephony service = getITelephony(); + if (service != null) { + service.clearSignalStrengthUpdateRequest(getSubId(), request, getOpPackageName()); + } + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#clearSignalStrengthUpdateRequest", e); + } + } + + /** + * Gets the current phone capability. + * + * @return the PhoneCapability which describes the data connection capability of modem. + * It's used to evaluate possible phone config change, for example from single + * SIM device to multi-SIM device. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public @NonNull PhoneCapability getPhoneCapability() { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + return telephony.getPhoneCapability(); + } else { + throw new IllegalStateException("telephony service is null."); + } + } catch (RemoteException ex) { + ex.rethrowAsRuntimeException(); + } + if (getActiveModemCount() > 1) { + return PhoneCapability.DEFAULT_DSDS_CAPABILITY; + } else { + return PhoneCapability.DEFAULT_SSSS_CAPABILITY; + } + } + + /** + * The unattended reboot was prepared successfully. + * @hide + */ + @SystemApi + public static final int PREPARE_UNATTENDED_REBOOT_SUCCESS = 0; + + /** + * The unattended reboot was prepared, but the user will need to manually + * enter the PIN code of at least one SIM card present in the device. + * @hide + */ + @SystemApi + public static final int PREPARE_UNATTENDED_REBOOT_PIN_REQUIRED = 1; + + /** + * The unattended reboot was not prepared due to a non-recoverable error. After this error, + * the client that manages the unattended reboot should not try to invoke the API again + * until the next power cycle. + * @hide + */ + @SystemApi + public static final int PREPARE_UNATTENDED_REBOOT_ERROR = 2; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"PREPARE_UNATTENDED_REBOOT_"}, + value = { + PREPARE_UNATTENDED_REBOOT_SUCCESS, + PREPARE_UNATTENDED_REBOOT_PIN_REQUIRED, + PREPARE_UNATTENDED_REBOOT_ERROR + }) + public @interface PrepareUnattendedRebootResult {} + + /** + * Prepare TelephonyManager for an unattended reboot. The reboot is required to be done + * shortly (e.g. within 15 seconds) after the API is invoked. + * + * <p>Requires Permission: + * {@link android.Manifest.permission#REBOOT} + * + * @return {@link #PREPARE_UNATTENDED_REBOOT_SUCCESS} in case of success. + * {@link #PREPARE_UNATTENDED_REBOOT_PIN_REQUIRED} if the device contains + * at least one SIM card for which the user needs to manually enter the PIN + * code after the reboot. {@link #PREPARE_UNATTENDED_REBOOT_ERROR} in case + * of error. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.REBOOT) + @PrepareUnattendedRebootResult + public int prepareForUnattendedReboot() { + try { + ITelephony service = getITelephony(); + if (service != null) { + return service.prepareForUnattendedReboot(); + } + } catch (RemoteException e) { + Log.e(TAG, "Telephony#prepareForUnattendedReboot RemoteException", e); + e.rethrowFromSystemServer(); + } + return PREPARE_UNATTENDED_REBOOT_ERROR; + } + + /** + * Exception that may be supplied to the callback in {@link #getNetworkSlicingConfiguration} if + * something goes awry. + */ + public static class NetworkSlicingException extends Exception { + /** + * Getting the current slicing configuration successfully. Used internally only. + * @hide + */ + public static final int SUCCESS = 0; + + /** + * The system timed out waiting for a response from the Radio. + * @hide + */ + public static final int ERROR_TIMEOUT = 1; + + /** + * The modem returned a failure. + * @hide + */ + public static final int ERROR_MODEM_ERROR = 2; + + /** @hide */ + @IntDef(prefix = {"ERROR_"}, value = { + ERROR_TIMEOUT, + ERROR_MODEM_ERROR, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface NetworkSlicingError {} + + private final int mErrorCode; + + /** @hide */ + public NetworkSlicingException(@NetworkSlicingError int errorCode) { + mErrorCode = errorCode; + } + + @Override + public String toString() { + switch (mErrorCode) { + case ERROR_TIMEOUT: return "ERROR_TIMEOUT"; + case ERROR_MODEM_ERROR: return "ERROR_MODEM_ERROR"; + default: return "UNDEFINED"; + } + } + } + + /** + * Exception that is supplied to the callback in {@link #getNetworkSlicingConfiguration} if the + * system timed out waiting for a response from the Radio. + */ + public class TimeoutException extends NetworkSlicingException { + /** @hide */ + public TimeoutException(int errorCode) { + super(errorCode); + } + } + + /** + * Exception that is supplied to the callback in {@link #getNetworkSlicingConfiguration} if the + * modem returned a failure. + */ + public class ModemErrorException extends NetworkSlicingException { + /** @hide */ + public ModemErrorException(int errorCode) { + super(errorCode); + } + } + + /** @hide */ + public static final String KEY_SLICING_CONFIG_HANDLE = "slicing_config_handle"; + + /** + * Request to get the current slicing configuration including URSP rules and + * NSSAIs (configured, allowed and rejected). + * + * This method can be invoked if one of the following requirements is met: + * <ul> + * <li>If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this + * is a privileged permission that can only be granted to apps preloaded on the device. + * <li>If the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). + * </ul> + * + * This will be invalid if the device does not support + * android.telephony.TelephonyManager#CAPABILITY_SLICING_CONFIG_SUPPORTED. + * + * @param executor the executor on which callback will be invoked. + * @param callback a callback to receive the current slicing configuration. + */ + @RequiresFeature( + enforcement = "android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported", + value = TelephonyManager.CAPABILITY_SLICING_CONFIG_SUPPORTED) + @SuppressAutoDoc // No support for carrier privileges (b/72967236). + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public void getNetworkSlicingConfiguration( + @NonNull @CallbackExecutor Executor executor, + @NonNull OutcomeReceiver<NetworkSlicingConfig, NetworkSlicingException> callback) { + Objects.requireNonNull(executor); + Objects.requireNonNull(callback); + + try { + ITelephony telephony = getITelephony(); + if (telephony == null) { + throw new IllegalStateException("telephony service is null."); + } + telephony.getSlicingConfig(new ResultReceiver(null) { + @Override + protected void onReceiveResult(int resultCode, Bundle result) { + if (resultCode == NetworkSlicingException.ERROR_TIMEOUT) { + executor.execute(() -> callback.onError( + new TimeoutException(resultCode))); + return; + } else if (resultCode == NetworkSlicingException.ERROR_MODEM_ERROR) { + executor.execute(() -> callback.onError( + new ModemErrorException(resultCode))); + return; + } + + NetworkSlicingConfig slicingConfig = + result.getParcelable(KEY_SLICING_CONFIG_HANDLE); + executor.execute(() -> callback.onResult(slicingConfig)); + } + }); + } catch (RemoteException ex) { + ex.rethrowAsRuntimeException(); + } + } } diff --git a/telephony/java/android/telephony/TelephonyScanManager.java b/telephony/java/android/telephony/TelephonyScanManager.java index cdff651c9c3d..e890acb36b48 100644 --- a/telephony/java/android/telephony/TelephonyScanManager.java +++ b/telephony/java/android/telephony/TelephonyScanManager.java @@ -30,6 +30,7 @@ import android.os.Parcelable; import android.os.RemoteException; import android.util.SparseArray; +import com.android.internal.annotations.GuardedBy; import com.android.internal.telephony.ITelephony; import com.android.telephony.Rlog; @@ -55,6 +56,8 @@ public final class TelephonyScanManager { public static final int CALLBACK_SCAN_COMPLETE = 3; /** @hide */ public static final int CALLBACK_RESTRICTED_SCAN_RESULTS = 4; + /** @hide */ + public static final int CALLBACK_TELEPHONY_DIED = 5; /** @hide */ public static final int INVALID_SCAN_ID = -1; @@ -103,17 +106,44 @@ public final class TelephonyScanManager { } private final Looper mLooper; + private final Handler mHandler; private final Messenger mMessenger; private final SparseArray<NetworkScanInfo> mScanInfo = new SparseArray<NetworkScanInfo>(); + private final Binder.DeathRecipient mDeathRecipient; public TelephonyScanManager() { HandlerThread thread = new HandlerThread(TAG); thread.start(); mLooper = thread.getLooper(); - mMessenger = new Messenger(new Handler(mLooper) { + mHandler = new Handler(mLooper) { @Override public void handleMessage(Message message) { checkNotNull(message, "message cannot be null"); + if (message.what == CALLBACK_TELEPHONY_DIED) { + // If there are no objects in mScanInfo then binder death will simply return. + synchronized (mScanInfo) { + for (int i = 0; i < mScanInfo.size(); i++) { + NetworkScanInfo nsi = mScanInfo.valueAt(i); + // At this point we go into panic mode and ignore errors that would + // normally stop the show in order to try and clean up as gracefully + // as possible. + if (nsi == null) continue; // shouldn't be possible + Executor e = nsi.mExecutor; + NetworkScanCallback cb = nsi.mCallback; + if (e == null || cb == null) continue; + try { + e.execute( + () -> cb.onError(NetworkScan.ERROR_MODEM_UNAVAILABLE)); + } catch (java.util.concurrent.RejectedExecutionException ignore) { + // ignore so that we can continue + } + } + + mScanInfo.clear(); + } + return; + } + NetworkScanInfo nsi; synchronized (mScanInfo) { nsi = mScanInfo.get(message.arg2); @@ -158,6 +188,9 @@ public final class TelephonyScanManager { Rlog.d(TAG, "onError: " + errorCode); callback.onError(errorCode); }); + synchronized (mScanInfo) { + mScanInfo.remove(message.arg2); + } } catch (Exception e) { Rlog.e(TAG, "Exception in networkscan callback onError", e); } @@ -168,7 +201,9 @@ public final class TelephonyScanManager { Rlog.d(TAG, "onComplete"); callback.onComplete(); }); - mScanInfo.remove(message.arg2); + synchronized (mScanInfo) { + mScanInfo.remove(message.arg2); + } } catch (Exception e) { Rlog.e(TAG, "Exception in networkscan callback onComplete", e); } @@ -178,7 +213,14 @@ public final class TelephonyScanManager { break; } } - }); + }; + mMessenger = new Messenger(mHandler); + mDeathRecipient = new Binder.DeathRecipient() { + @Override + public void binderDied() { + mHandler.obtainMessage(CALLBACK_TELEPHONY_DIED).sendToTarget(); + } + }; } /** @@ -189,7 +231,7 @@ public final class TelephonyScanManager { * * <p> * Requires Permission: - * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION} and + * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} and * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} * Or the calling app has carrier privileges. @see #hasCarrierPrivileges * @@ -204,19 +246,26 @@ public final class TelephonyScanManager { NetworkScanRequest request, Executor executor, NetworkScanCallback callback, String callingPackage, @Nullable String callingFeatureId) { try { - ITelephony telephony = getITelephony(); - if (telephony != null) { - synchronized (mScanInfo) { - int scanId = telephony.requestNetworkScan( - subId, request, mMessenger, new Binder(), callingPackage, - callingFeatureId); - if (scanId == INVALID_SCAN_ID) { - Rlog.e(TAG, "Failed to initiate network scan"); - return null; - } - saveScanInfo(scanId, request, executor, callback); - return new NetworkScan(scanId, subId); - } + final ITelephony telephony = getITelephony(); + if (telephony == null) return null; + + int scanId = telephony.requestNetworkScan( + subId, request, mMessenger, new Binder(), callingPackage, + callingFeatureId); + if (scanId == INVALID_SCAN_ID) { + Rlog.e(TAG, "Failed to initiate network scan"); + return null; + } + synchronized (mScanInfo) { + // We link to death whenever a scan is started to ensure that we are linked + // at the point that phone process death might matter. + // We never unlink because: + // - Duplicate links to death with the same callback do not result in + // extraneous callbacks (the tracking de-dupes). + // - Receiving binderDeath() when no scans are active is a no-op. + telephony.asBinder().linkToDeath(mDeathRecipient, 0); + saveScanInfo(scanId, request, executor, callback); + return new NetworkScan(scanId, subId); } } catch (RemoteException ex) { Rlog.e(TAG, "requestNetworkScan RemoteException", ex); @@ -226,6 +275,7 @@ public final class TelephonyScanManager { return null; } + @GuardedBy("mScanInfo") private void saveScanInfo( int id, NetworkScanRequest request, Executor executor, NetworkScanCallback callback) { mScanInfo.put(id, new NetworkScanInfo(request, executor, callback)); diff --git a/telephony/java/android/telephony/ThermalMitigationRequest.aidl b/telephony/java/android/telephony/ThermalMitigationRequest.aidl new file mode 100644 index 000000000000..a912f77a1de8 --- /dev/null +++ b/telephony/java/android/telephony/ThermalMitigationRequest.aidl @@ -0,0 +1,19 @@ +/* + * Copyright 2020 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; + +parcelable ThermalMitigationRequest;
\ No newline at end of file diff --git a/telephony/java/android/telephony/ThermalMitigationRequest.java b/telephony/java/android/telephony/ThermalMitigationRequest.java new file mode 100644 index 000000000000..91ad9c3e1f51 --- /dev/null +++ b/telephony/java/android/telephony/ThermalMitigationRequest.java @@ -0,0 +1,248 @@ +/* + * Copyright (C) 2020 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.annotation.Nullable; +import android.annotation.SystemApi; +import android.os.Parcel; +import android.os.Parcelable; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + + +/** + * Class stores information related to the type of data throttling request to be sent to {@link + * TelephonyManager#sendThermalMitigationRequest(ThermalMitigationResult)}. + * @hide + */ +@SystemApi +public final class ThermalMitigationRequest implements Parcelable { + /** + * Sent as a thermal mititgation action to {@link + * TelephonyManager#sendThermalMitigationRequest(ThermalMitigationResult)} to start data + * throttling. {@link TelephonyManager#InvalidThermalMitigationRequestException} will be thrown + * if dataThrottlingRequest is {@code null} or if completion duration is < 0. + * + * @hide + */ + @SystemApi + public static final int THERMAL_MITIGATION_ACTION_DATA_THROTTLING = 0; + + /** + * Sent as a thermal mititgation action to {@link + * TelephonyManager#sendThermalMitigationRequest(ThermalMitigationResult)} to allow only voice + * calls and internet data will not be available. This attempts to enable radio if currently + * disabled for thermal mitigation with no guarantee of it actually turning on. + * dataThrottlingRequest must be {@code null} or {@link + * TelephonyManager#InvalidThermalMitigationRequestException} will be thrown. + * + * @hide + */ + @SystemApi + public static final int THERMAL_MITIGATION_ACTION_VOICE_ONLY = 1; + + /** + * Sent as a thermal mititgation action to {@link' + * TelephonyManager#sendThermalMitigationRequest(ThermalMitigationResult)} to turn radio off. If + * radio is not able to be powered off because of an ongoing voice call, pending emergency call, + * or any other state that wouldn't allow radio off, {@link + * TelephonyManager#THERMAL_MITIGATION_RESULT_INVALID_STATE}. + * dataThrottlingRequest must be {@code null} or + * {@link TelephonyManager#InvalidThermalMitigationRequestException} will be returned. + * + * @hide + */ + @SystemApi + public static final int THERMAL_MITIGATION_ACTION_RADIO_OFF = 2; + + /** + * Type of thermal mitigation action. + * @hide + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = { "THERMAL_MITIGATION_ACTION_" }, value = { + THERMAL_MITIGATION_ACTION_DATA_THROTTLING, + THERMAL_MITIGATION_ACTION_VOICE_ONLY, + THERMAL_MITIGATION_ACTION_RADIO_OFF}) + public @interface ThermalMitigationAction {} + + private @ThermalMitigationAction int mThermalMitigationAction; + private DataThrottlingRequest mDataThrottlingRequest; + + /** + * @param thermalMitigationAction thermal mitigation action. + * @param dataThrottlingRequest is the parameters for more fine-controlled data throttling. This + * is only applicable if thermalMitigationAction is + * {@link #THERMAL_MITIGATION_ACTION_DATA_THROTTLING}. Otherwise, it must be set to + * {@code null}. See {@link DataThrottlingRequest} for more details. + */ + private ThermalMitigationRequest(@ThermalMitigationAction int thermalMitigationAction, + @Nullable DataThrottlingRequest dataThrottlingRequest) { + mThermalMitigationAction = thermalMitigationAction; + mDataThrottlingRequest = dataThrottlingRequest; + } + + private ThermalMitigationRequest(Parcel in) { + mThermalMitigationAction = in.readInt(); + mDataThrottlingRequest = in.readParcelable(DataThrottlingRequest.class.getClassLoader()); + } + + /** + * Implement the Parcelable interface + */ + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mThermalMitigationAction); + dest.writeParcelable(mDataThrottlingRequest, 0); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public String toString() { + return "[ThermalMitigationRequest " + + ", thermalMitigationAction=" + mThermalMitigationAction + + ", dataThrottlingRequest=" + mDataThrottlingRequest + + "]"; + } + + /** + * @return the thermal mitigation action. + */ + public @ThermalMitigationAction int getThermalMitigationAction() { + return mThermalMitigationAction; + } + + /** + * @return the data throttling request. + */ + @Nullable + public DataThrottlingRequest getDataThrottlingRequest() { + return mDataThrottlingRequest; + } + + public static final @NonNull Parcelable.Creator<ThermalMitigationRequest> CREATOR = + new Parcelable.Creator<ThermalMitigationRequest>() { + + @Override + public ThermalMitigationRequest createFromParcel(Parcel in) { + return new ThermalMitigationRequest(in); + } + + @Override + public ThermalMitigationRequest[] newArray(int size) { + return new ThermalMitigationRequest[size]; + } + }; + + /** + * Provides a convenient way to set the fields of a {@link ThermalMitigationRequest} when + * creating a new instance. + * + * <p>The example below shows how you might create a new {@code ThermalMitigationRequest}: + * + * <pre><code> + * + * ThermalMitigationRequest dp = new ThermalMitigationRequest.Builder() + * .setThermalMitigationAction( + * ThermalMitigationRequest.THERMAL_MITIGATION_ACTION_DATA_THROTTLING) + * .setDataThrottlingRequest(new DataThrottlingRequest.Builder() + * .setDataThrottlingAction( + * DataThrottlingRequest.DATA_THROTTLING_ACTION_THROTTLE_SECONDARY_CARRIER) + * .setCompletionDurationMillis(10000L) + * .build()) + * .build(); + * </code></pre> + * + * @hide + */ + @SystemApi + public static final class Builder { + private @ThermalMitigationAction int mThermalMitigationAction = -1; + private DataThrottlingRequest mDataThrottlingRequest; + + /** + * Default constructor for Builder. + */ + public Builder() {} + + /** + * Set the thermal mitigation action. + * + * @param thermalMitigationAction thermal mitigation action. See {@link + * #THERMAL_MITIGATION_ACTION_DATA_THROTTLING}, {@link + * #THERMAL_MITIGATION_ACTION_VOICE_ONLY}, and {@link + * #THERMAL_MITIGATION_ACTION_RADIO_OFF} for more details. + * + * @return The same instance of the builder. + */ + public @NonNull Builder setThermalMitigationAction( + @ThermalMitigationAction int thermalMitigationAction) { + mThermalMitigationAction = thermalMitigationAction; + return this; + } + + /** + * Set the data throttling request. + * + * @param dataThrottlingRequest is the parameters for more fine-controlled data throttling. + * This is only applicable if thermalMitigationAction is {@link + * #THERMAL_MITIGATION_ACTION_DATA_THROTTLING}. Otherwise, it should not be set and + * will throw an IllegalArgumentException if it is. See {@link DataThrottlingRequest} + * for more details. + * + * @return The same instance of the builder. + */ + public @NonNull Builder setDataThrottlingRequest( + @NonNull DataThrottlingRequest dataThrottlingRequest) { + mDataThrottlingRequest = dataThrottlingRequest; + return this; + } + + /** + * Build the ThermalMitigationRequest. + * + * @return the ThermalMitigationRequest object. + */ + public @NonNull ThermalMitigationRequest build() { + if (mThermalMitigationAction < 0) { + throw new IllegalArgumentException("thermalMitigationAction was " + + " not set"); + } + + if (mThermalMitigationAction == THERMAL_MITIGATION_ACTION_DATA_THROTTLING) { + if (mDataThrottlingRequest == null) { + throw new IllegalArgumentException("dataThrottlingRequest cannot be null for " + + "THERMAL_MITIGATION_ACTION_DATA_THROTTLING"); + } + + + } else if (mDataThrottlingRequest != null) { + throw new IllegalArgumentException("dataThrottlingRequest must be null for " + + "THERMAL_MITIGATION_ACTION_VOICE_ONLY and " + + "THERMAL_MITIGATION_ACTION_RADIO_OFF"); + } + + return new ThermalMitigationRequest(mThermalMitigationAction, mDataThrottlingRequest); + } + } +} diff --git a/telephony/java/android/telephony/UiccAccessRule.java b/telephony/java/android/telephony/UiccAccessRule.java index 12bb36647f8f..38b551bff9a7 100644 --- a/telephony/java/android/telephony/UiccAccessRule.java +++ b/telephony/java/android/telephony/UiccAccessRule.java @@ -35,6 +35,7 @@ import java.io.DataOutputStream; import java.io.IOException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -52,6 +53,16 @@ public final class UiccAccessRule implements Parcelable { private static final int ENCODING_VERSION = 1; + /** + * Delimiter used to decode {@link CarrierConfigManager#KEY_CARRIER_CERTIFICATE_STRING_ARRAY}. + */ + private static final String DELIMITER_CERTIFICATE_HASH_PACKAGE_NAMES = ":"; + + /** + * Delimiter used to decode {@link CarrierConfigManager#KEY_CARRIER_CERTIFICATE_STRING_ARRAY}. + */ + private static final String DELIMITER_INDIVIDUAL_PACKAGE_NAMES = ","; + public static final @android.annotation.NonNull Creator<UiccAccessRule> CREATOR = new Creator<UiccAccessRule>() { @Override public UiccAccessRule createFromParcel(Parcel in) { @@ -98,6 +109,36 @@ public final class UiccAccessRule implements Parcelable { } /** + * Decodes {@link CarrierConfigManager#KEY_CARRIER_CERTIFICATE_STRING_ARRAY} values. + * @hide + */ + @Nullable + public static UiccAccessRule[] decodeRulesFromCarrierConfig(@Nullable String[] certs) { + if (certs == null) { + return null; + } + List<UiccAccessRule> carrierConfigAccessRulesArray = new ArrayList(); + for (String cert : certs) { + String[] splitStr = cert.split(DELIMITER_CERTIFICATE_HASH_PACKAGE_NAMES); + byte[] certificateHash = IccUtils.hexStringToBytes(splitStr[0]); + if (splitStr.length == 1) { + // The value is a certificate hash, without any package name + carrierConfigAccessRulesArray.add(new UiccAccessRule(certificateHash, null, 0)); + } else { + // The value is composed of the certificate hash followed by at least one + // package name + String[] packageNames = splitStr[1].split(DELIMITER_INDIVIDUAL_PACKAGE_NAMES); + for (String packageName : packageNames) { + carrierConfigAccessRulesArray.add( + new UiccAccessRule(certificateHash, packageName, 0)); + } + } + } + return carrierConfigAccessRulesArray.toArray( + new UiccAccessRule[carrierConfigAccessRulesArray.size()]); + } + + /** * Decodes a byte array generated with {@link #encodeRules}. * @hide */ @@ -204,16 +245,32 @@ public final class UiccAccessRule implements Parcelable { * {@link TelephonyManager#CARRIER_PRIVILEGE_STATUS_NO_ACCESS}. */ public int getCarrierPrivilegeStatus(Signature signature, String packageName) { - // SHA-1 is for backward compatible support only, strongly discouraged for new use. - byte[] certHash = getCertHash(signature, "SHA-1"); byte[] certHash256 = getCertHash(signature, "SHA-256"); - if (matches(certHash, packageName) || matches(certHash256, packageName)) { + // Check SHA-256 first as it's the new standard. + if (matches(certHash256, packageName)) { return TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS; } + // Then check SHA-1 for backward compatibility. This should be removed + // in the near future when GPD_SPE_068 fully replaces GPD_SPE_013. + if (this.mCertificateHash.length == 20) { + byte[] certHash = getCertHash(signature, "SHA-1"); + if (matches(certHash, packageName)) { + return TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS; + } + } + return TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS; } + /** + * Returns true if the given certificate and package name match this rule's values. + * @hide + */ + public boolean matches(@Nullable String certHash, @Nullable String packageName) { + return matches(IccUtils.hexStringToBytes(certHash), packageName); + } + private boolean matches(byte[] certHash, String packageName) { return certHash != null && Arrays.equals(this.mCertificateHash, certHash) && (TextUtils.isEmpty(this.mPackageName) || this.mPackageName.equals(packageName)); diff --git a/telephony/java/android/telephony/VisualVoicemailSmsFilterSettings.java b/telephony/java/android/telephony/VisualVoicemailSmsFilterSettings.java index e8f3f1ebb6cf..eadb726bf63b 100644 --- a/telephony/java/android/telephony/VisualVoicemailSmsFilterSettings.java +++ b/telephony/java/android/telephony/VisualVoicemailSmsFilterSettings.java @@ -92,8 +92,8 @@ public final class VisualVoicemailSmsFilterSettings implements Parcelable { } /** - * Sets the originating number whitelist for the visual voicemail SMS filter. If the list is - * not null only the SMS messages from a number in the list can be considered as a visual + * Sets the originating number allow list for the visual voicemail SMS filter. If the list + * is not null only the SMS messages from a number in the list can be considered as a visual * voicemail SMS. Otherwise, messages from any address will be considered. */ public Builder setOriginatingNumbers(List<String> originatingNumbers) { @@ -133,7 +133,7 @@ public final class VisualVoicemailSmsFilterSettings implements Parcelable { public final String clientPrefix; /** - * The originating number whitelist for the visual voicemail SMS filter of a phone account. If + * The originating number allow list for the visual voicemail SMS filter of a phone account. If * the list is not null only the SMS messages from a number in the list can be considered as a * visual voicemail SMS. Otherwise, messages from any address will be considered. */ diff --git a/telephony/java/android/telephony/VopsSupportInfo.aidl b/telephony/java/android/telephony/VopsSupportInfo.aidl new file mode 100644 index 000000000000..31c608fe9546 --- /dev/null +++ b/telephony/java/android/telephony/VopsSupportInfo.aidl @@ -0,0 +1,19 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony; + +parcelable VopsSupportInfo; diff --git a/telephony/java/android/telephony/VopsSupportInfo.java b/telephony/java/android/telephony/VopsSupportInfo.java new file mode 100644 index 000000000000..f89bfa9b60b4 --- /dev/null +++ b/telephony/java/android/telephony/VopsSupportInfo.java @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony; + +import android.annotation.NonNull; +import android.annotation.SuppressLint; +import android.annotation.SystemApi; +import android.os.Parcel; +import android.os.Parcelable; +import android.telephony.AccessNetworkConstants.AccessNetworkType; + +/** + * Abstract base class for the information related to network VoPS support. + * This is the base class for XxVopsSupportInfo which represent VoPS support + * information for specific network access techonology. + * @hide + */ +@SuppressLint("ParcelNotFinal") +@SystemApi +public abstract class VopsSupportInfo implements Parcelable { + + /** + * @hide + */ + public VopsSupportInfo() {} + + /** + * Returns whether VoPS is supported by the network + */ + public abstract boolean isVopsSupported(); + + /** + * Returns whether emergency service is supported by the network + */ + public abstract boolean isEmergencyServiceSupported(); + + /** + * Returns whether emergency service fallback is supported by the network + */ + public abstract boolean isEmergencyServiceFallbackSupported(); + + /** + * Implement the Parcelable interface + */ + @Override + public int describeContents() { + return 0; + } + + /** Implement the Parcelable interface */ + @Override + public abstract void writeToParcel(@NonNull Parcel dest, int flags); + + /** + * Used by child classes for parceling. + * + * @hide + */ + protected void writeToParcel(@NonNull Parcel dest, int flags, int type) { + dest.writeInt(type); + } + + /** Implement the Parcelable interface */ + public static final @android.annotation.NonNull Creator<VopsSupportInfo> CREATOR = + new Creator<VopsSupportInfo>() { + @Override + public VopsSupportInfo createFromParcel(Parcel in) { + int type = in.readInt(); + switch (type) { + case AccessNetworkType.EUTRAN: + return LteVopsSupportInfo.createFromParcelBody(in); + case AccessNetworkType.NGRAN: + return NrVopsSupportInfo.createFromParcelBody(in); + default: throw new RuntimeException("Bad VopsSupportInfo Parcel"); + } + } + + @Override + public VopsSupportInfo[] newArray(int size) { + return new VopsSupportInfo[size]; + } + }; + + @Override + public abstract int hashCode(); + + @Override + public abstract boolean equals(Object o); +} diff --git a/telephony/java/android/telephony/cdma/CdmaCellLocation.java b/telephony/java/android/telephony/cdma/CdmaCellLocation.java index 9bc39a0c6ced..d808cabaaa92 100644 --- a/telephony/java/android/telephony/cdma/CdmaCellLocation.java +++ b/telephony/java/android/telephony/cdma/CdmaCellLocation.java @@ -23,7 +23,10 @@ import android.telephony.CellLocation; /** * Represents the cell location on a CDMA phone. + * + * @deprecated use {@link android.telephony.CellIdentity CellIdentity}. */ +@Deprecated public class CdmaCellLocation extends CellLocation { @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) private int mBaseStationId = -1; diff --git a/telephony/java/android/telephony/cdma/OWNERS b/telephony/java/android/telephony/cdma/OWNERS new file mode 100644 index 000000000000..6aa399d9ebfb --- /dev/null +++ b/telephony/java/android/telephony/cdma/OWNERS @@ -0,0 +1,5 @@ +# Bug component: 20868 + +rgreenwalt@google.com +tgunn@google.com +amitmahajan@google.com diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java index bfb54b008cd8..8b6f2b5c8f08 100644 --- a/telephony/java/android/telephony/data/ApnSetting.java +++ b/telephony/java/android/telephony/data/ApnSetting.java @@ -18,6 +18,8 @@ package android.telephony.data; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.StringDef; +import android.annotation.SystemApi; import android.content.ContentValues; import android.database.Cursor; import android.hardware.radio.V1_5.ApnTypes; @@ -26,7 +28,6 @@ import android.os.Parcel; import android.os.Parcelable; import android.provider.Telephony; import android.provider.Telephony.Carriers; -import android.telephony.Annotation.ApnType; import android.telephony.Annotation.NetworkType; import android.telephony.ServiceState; import android.telephony.TelephonyManager; @@ -113,6 +114,37 @@ public class ApnSetting implements Parcelable { public static final int TYPE_MCX = ApnTypes.MCX; /** APN type for XCAP. */ public static final int TYPE_XCAP = ApnTypes.XCAP; + /** APN type for VSIM. */ + public static final int TYPE_VSIM = 1 << 12; // TODO: Refer to ApnTypes.VSIM + /** APN type for BIP. */ + public static final int TYPE_BIP = 1 << 13; // TODO: Refer to ApnTypes.BIP + /** + * APN type for ENTERPRISE. + * @hide + */ + public static final int TYPE_ENTERPRISE = TYPE_BIP << 1; + + /** @hide */ + @IntDef(flag = true, prefix = {"TYPE_"}, value = { + TYPE_DEFAULT, + TYPE_MMS, + TYPE_SUPL, + TYPE_DUN, + TYPE_HIPRI, + TYPE_FOTA, + TYPE_IMS, + TYPE_CBS, + TYPE_IA, + TYPE_EMERGENCY, + TYPE_MCX, + TYPE_XCAP, + TYPE_BIP, + TYPE_VSIM, + TYPE_ENTERPRISE, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ApnType { + } // Possible values for authentication types. /** No authentication type. */ @@ -133,6 +165,28 @@ public class ApnSetting implements Parcelable { @Retention(RetentionPolicy.SOURCE) public @interface Skip464XlatStatus {} + /** @hide */ + @StringDef(value = { + TYPE_ALL_STRING, + TYPE_CBS_STRING, + TYPE_DEFAULT_STRING, + TYPE_DUN_STRING, + TYPE_EMERGENCY_STRING, + TYPE_FOTA_STRING, + TYPE_HIPRI_STRING, + TYPE_IA_STRING, + TYPE_IMS_STRING, + TYPE_MCX_STRING, + TYPE_MMS_STRING, + TYPE_SUPL_STRING, + TYPE_XCAP_STRING, + TYPE_VSIM_STRING, + TYPE_BIP_STRING, + TYPE_ENTERPRISE_STRING, + }, prefix = "TYPE_", suffix = "_STRING") + @Retention(RetentionPolicy.SOURCE) + public @interface ApnTypeString {} + /** * APN types for data connections. These are usage categories for an APN * entry. One APN entry may support multiple APN types, eg, a single APN @@ -140,102 +194,167 @@ public class ApnSetting implements Parcelable { * connections.<br/> * APN_TYPE_ALL is a special type to indicate that this APN entry can * service all data connections. - * <p> - * Note: The goal is to deprecate this. Due to the Carrier Table being used - * directly, this isn't feasible right now. * * @hide */ + @SystemApi public static final String TYPE_ALL_STRING = "*"; /** * APN type for default data traffic * + * Note: String representations of APN types are intended for system apps to communicate with + * modem components or carriers. Non-system apps should use the integer variants instead. * @hide */ + @SystemApi public static final String TYPE_DEFAULT_STRING = "default"; /** - * APN type for MMS traffic + * APN type for MMS (Multimedia Messaging Service) traffic. * + * Note: String representations of APN types are intended for system apps to communicate with + * modem components or carriers. Non-system apps should use the integer variants instead. * @hide */ + @SystemApi public static final String TYPE_MMS_STRING = "mms"; /** - * APN type for SUPL assisted GPS + * APN type for SUPL (Secure User Plane Location) assisted GPS. * + * Note: String representations of APN types are intended for system apps to communicate with + * modem components or carriers. Non-system apps should use the integer variants instead. * @hide */ + @SystemApi public static final String TYPE_SUPL_STRING = "supl"; /** - * APN type for DUN traffic + * APN type for DUN (Dial-up networking) traffic * + * Note: String representations of APN types are intended for system apps to communicate with + * modem components or carriers. Non-system apps should use the integer variants instead. * @hide */ + @SystemApi public static final String TYPE_DUN_STRING = "dun"; /** - * APN type for HiPri traffic + * APN type for high-priority traffic * + * Note: String representations of APN types are intended for system apps to communicate with + * modem components or carriers. Non-system apps should use the integer variants instead. * @hide */ + @SystemApi public static final String TYPE_HIPRI_STRING = "hipri"; /** - * APN type for FOTA + * APN type for FOTA (Firmware over-the-air) traffic. * + * Note: String representations of APN types are intended for system apps to communicate with + * modem components or carriers. Non-system apps should use the integer variants instead. * @hide */ + @SystemApi public static final String TYPE_FOTA_STRING = "fota"; /** - * APN type for IMS + * APN type for IMS (IP Multimedia Subsystem) traffic. * + * Note: String representations of APN types are intended for system apps to communicate with + * modem components or carriers. Non-system apps should use the integer variants instead. * @hide */ + @SystemApi public static final String TYPE_IMS_STRING = "ims"; /** - * APN type for CBS + * APN type for CBS (Carrier Branded Services) traffic. * + * Note: String representations of APN types are intended for system apps to communicate with + * modem components or carriers. Non-system apps should use the integer variants instead. * @hide */ + @SystemApi public static final String TYPE_CBS_STRING = "cbs"; /** - * APN type for IA Initial Attach APN + * APN type for the IA (Initial Attach) APN * + * Note: String representations of APN types are intended for system apps to communicate with + * modem components or carriers. Non-system apps should use the integer variants instead. * @hide */ + @SystemApi public static final String TYPE_IA_STRING = "ia"; /** * APN type for Emergency PDN. This is not an IA apn, but is used * for access to carrier services in an emergency call situation. * + * Note: String representations of APN types are intended for system apps to communicate with + * modem components or carriers. Non-system apps should use the integer variants instead. * @hide */ + @SystemApi public static final String TYPE_EMERGENCY_STRING = "emergency"; /** - * APN type for Mission Critical Services + * APN type for Mission Critical Services. * + * Note: String representations of APN types are intended for system apps to communicate with + * modem components or carriers. Non-system apps should use the integer variants instead. * @hide */ + @SystemApi public static final String TYPE_MCX_STRING = "mcx"; /** - * APN type for XCAP + * APN type for XCAP (XML Configuration Access Protocol) traffic. * + * Note: String representations of APN types are intended for system apps to communicate with + * modem components or carriers. Non-system apps should use the integer variants instead. * @hide */ + @SystemApi public static final String TYPE_XCAP_STRING = "xcap"; + + /** + * APN type for Virtual SIM service. + * + * Note: String representations of APN types are intended for system apps to communicate with + * modem components or carriers. Non-system apps should use the integer variants instead. + * @hide + */ + @SystemApi + public static final String TYPE_VSIM_STRING = "vsim"; + + /** + * APN type for Bearer Independent Protocol. + * + * Note: String representations of APN types are intended for system apps to communicate with + * modem components or carriers. Non-system apps should use the integer variants instead. + * @hide + */ + @SystemApi + public static final String TYPE_BIP_STRING = "bip"; + + /** + * APN type for ENTERPRISE traffic. + * + * Note: String representations of APN types are intended for system apps to communicate with + * modem components or carriers. Non-system apps should use the integer variants instead. + * @hide + */ + public static final String TYPE_ENTERPRISE_STRING = "enterprise"; + + /** @hide */ @IntDef(prefix = { "AUTH_TYPE_" }, value = { AUTH_TYPE_NONE, @@ -314,6 +433,9 @@ public class ApnSetting implements Parcelable { APN_TYPE_STRING_MAP.put(TYPE_EMERGENCY_STRING, TYPE_EMERGENCY); APN_TYPE_STRING_MAP.put(TYPE_MCX_STRING, TYPE_MCX); APN_TYPE_STRING_MAP.put(TYPE_XCAP_STRING, TYPE_XCAP); + APN_TYPE_STRING_MAP.put(TYPE_ENTERPRISE_STRING, TYPE_ENTERPRISE); + APN_TYPE_STRING_MAP.put(TYPE_VSIM_STRING, TYPE_VSIM); + APN_TYPE_STRING_MAP.put(TYPE_BIP_STRING, TYPE_BIP); APN_TYPE_INT_MAP = new ArrayMap<>(); APN_TYPE_INT_MAP.put(TYPE_DEFAULT, TYPE_DEFAULT_STRING); @@ -328,6 +450,9 @@ public class ApnSetting implements Parcelable { APN_TYPE_INT_MAP.put(TYPE_EMERGENCY, TYPE_EMERGENCY_STRING); APN_TYPE_INT_MAP.put(TYPE_MCX, TYPE_MCX_STRING); APN_TYPE_INT_MAP.put(TYPE_XCAP, TYPE_XCAP_STRING); + APN_TYPE_INT_MAP.put(TYPE_ENTERPRISE, TYPE_ENTERPRISE_STRING); + APN_TYPE_INT_MAP.put(TYPE_VSIM, TYPE_VSIM_STRING); + APN_TYPE_INT_MAP.put(TYPE_BIP, TYPE_BIP_STRING); PROTOCOL_STRING_MAP = new ArrayMap<>(); PROTOCOL_STRING_MAP.put("IP", PROTOCOL_IP); @@ -1214,12 +1339,16 @@ public class ApnSetting implements Parcelable { return false; } - // TODO - if we have this function we should also have hashCode. - // Also should handle changes in type order and perhaps case-insensitivity. + @Override + public int hashCode() { + return Objects.hash(mApnName, mProxyAddress, mProxyPort, mMmsc, mMmsProxyAddress, + mMmsProxyPort, mUser, mPassword, mAuthType, mApnTypeBitmask, mId, mOperatorNumeric, + mProtocol, mRoamingProtocol, mMtu, mCarrierEnabled, mNetworkTypeBitmask, mProfileId, + mPersistent, mMaxConns, mWaitTime, mMaxConnsTime, mMvnoType, mMvnoMatchData, + mApnSetId, mCarrierId, mSkip464Xlat); + } - /** - * @hide - */ + @Override public boolean equals(Object o) { if (o instanceof ApnSetting == false) { return false; @@ -1422,16 +1551,33 @@ public class ApnSetting implements Parcelable { } /** - * @param apnType APN type - * @return APN type in string format + * Converts the integer representation of APN type to its string representation. + * + * @param apnType APN type as an integer + * @return String representation of the APN type, or an empty string if the provided integer is + * not a valid APN type. * @hide */ - public static String getApnTypeString(int apnType) { + @SystemApi + public static @NonNull @ApnTypeString String getApnTypeString(@ApnType int apnType) { if (apnType == TYPE_ALL) { return "*"; } String apnTypeString = APN_TYPE_INT_MAP.get(apnType); - return apnTypeString == null ? "Unknown" : apnTypeString; + return apnTypeString == null ? "" : apnTypeString; + } + + /** + * Converts the string representation of an APN type to its integer representation. + * + * @param apnType APN type as a string + * @return Integer representation of the APN type, or 0 if the provided string is not a valid + * APN type. + * @hide + */ + @SystemApi + public static @ApnType int getApnTypeInt(@NonNull @ApnTypeString String apnType) { + return APN_TYPE_STRING_MAP.getOrDefault(apnType.toLowerCase(), 0); } /** @@ -1643,7 +1789,7 @@ public class ApnSetting implements Parcelable { * * <pre><code> * // Create an MMS proxy address with a hostname. A network might not be - * // available, so supply a dummy (0.0.0.0) IPv4 address to avoid DNS lookup. + * // available, so supply a placeholder (0.0.0.0) IPv4 address to avoid DNS lookup. * String host = "mms.example.com"; * byte[] ipAddress = new byte[4]; * InetAddress mmsProxy; @@ -1828,7 +1974,8 @@ public class ApnSetting implements Parcelable { * {@link java.net.InetAddress#getAllByName getAllByName()} require DNS for hostname * resolution. To avoid this requirement when setting a hostname, call * {@link java.net.InetAddress#getByAddress(java.lang.String, byte[])} with both the - * hostname and a dummy IP address. See {@link ApnSetting.Builder above} for an example. + * hostname and a placeholder IP address. See {@link ApnSetting.Builder above} for an + * example. * * @param proxy the proxy address to set for the APN * @deprecated use {@link #setProxyAddress(String)} instead. @@ -1882,7 +2029,8 @@ public class ApnSetting implements Parcelable { * {@link java.net.InetAddress#getAllByName getAllByName()} require DNS for hostname * resolution. To avoid this requirement when setting a hostname, call * {@link java.net.InetAddress#getByAddress(java.lang.String, byte[])} with both the - * hostname and a dummy IP address. See {@link ApnSetting.Builder above} for an example. + * hostname and a placeholder IP address. See {@link ApnSetting.Builder above} for an + * example. * * @param mmsProxy the MMS proxy address to set for the APN * @deprecated use {@link #setMmsProxyAddress(String)} instead. @@ -2073,7 +2221,7 @@ public class ApnSetting implements Parcelable { public ApnSetting build() { if ((mApnTypeBitmask & (TYPE_DEFAULT | TYPE_MMS | TYPE_SUPL | TYPE_DUN | TYPE_HIPRI | TYPE_FOTA | TYPE_IMS | TYPE_CBS | TYPE_IA | TYPE_EMERGENCY | TYPE_MCX - | TYPE_XCAP)) == 0 + | TYPE_XCAP | TYPE_VSIM | TYPE_BIP | TYPE_ENTERPRISE)) == 0 || TextUtils.isEmpty(mApnName) || TextUtils.isEmpty(mEntryName)) { return null; } diff --git a/telephony/java/android/telephony/data/DataCallResponse.java b/telephony/java/android/telephony/data/DataCallResponse.java index 242c2e979571..ef02589abaf8 100644 --- a/telephony/java/android/telephony/data/DataCallResponse.java +++ b/telephony/java/android/telephony/data/DataCallResponse.java @@ -18,6 +18,7 @@ package android.telephony.data; import android.annotation.IntDef; +import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; @@ -29,6 +30,7 @@ import android.telephony.DataFailCause; import android.telephony.data.ApnSetting.ProtocolType; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.Preconditions; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -67,8 +69,61 @@ public final class DataCallResponse implements Parcelable { /** Indicates the data connection is active with physical link up. */ public static final int LINK_STATUS_ACTIVE = 2; + /** {@hide} */ + @IntDef(prefix = "HANDOVER_FAILURE_MODE_", value = { + HANDOVER_FAILURE_MODE_UNKNOWN, + HANDOVER_FAILURE_MODE_LEGACY, + HANDOVER_FAILURE_MODE_DO_FALLBACK, + HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_HANDOVER, + HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_SETUP_NORMAL + }) + @Retention(RetentionPolicy.SOURCE) + public @interface HandoverFailureMode {} + + /** + * Data handover failure mode is unknown. + */ + public static final int HANDOVER_FAILURE_MODE_UNKNOWN = -1; + + /** + * Perform fallback to the source data transport on data handover failure using + * the legacy logic, which is fallback if the fail cause is + * {@link DataFailCause#HANDOFF_PREFERENCE_CHANGED}. + */ + public static final int HANDOVER_FAILURE_MODE_LEGACY = 0; + + /** + * Perform fallback to the source data transport on data handover failure. + */ + public static final int HANDOVER_FAILURE_MODE_DO_FALLBACK = 1; + + /** + * Do not perform fallback to the source data transport on data handover failure. + * Framework will retry setting up a new data connection by sending + * {@link DataService#REQUEST_REASON_HANDOVER} request to the underlying {@link DataService}. + */ + public static final int HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_HANDOVER = 2; + + /** + * Do not perform fallback to the source transport on data handover failure. + * Framework will retry setting up a new data connection by sending + * {@link DataService#REQUEST_REASON_NORMAL} request to the underlying {@link DataService}. + */ + public static final int HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_SETUP_NORMAL = 3; + + /** + * Indicates that data retry duration is not specified. Platform can determine when to + * perform data setup appropriately. + */ + public static final int RETRY_DURATION_UNDEFINED = -1; + + /** + * Indicates that the pdu session id is not set. + */ + public static final int PDU_SESSION_ID_NOT_SET = 0; + private final @DataFailureCause int mCause; - private final int mSuggestedRetryTime; + private final long mSuggestedRetryTime; private final int mId; private final @LinkStatus int mLinkStatus; private final @ProtocolType int mProtocolType; @@ -80,6 +135,12 @@ public final class DataCallResponse implements Parcelable { private final int mMtu; private final int mMtuV4; private final int mMtuV6; + private final @HandoverFailureMode int mHandoverFailureMode; + private final int mPduSessionId; + private final Qos mDefaultQos; + private final List<QosBearerSession> mQosBearerSessions; + private final NetworkSliceInfo mSliceInfo; + private final List<TrafficDescriptor> mTrafficDescriptors; /** * @param cause Data call fail cause. {@link DataFailCause#NONE} indicates no error. @@ -126,14 +187,23 @@ public final class DataCallResponse implements Parcelable { mPcscfAddresses = (pcscfAddresses == null) ? new ArrayList<>() : new ArrayList<>(pcscfAddresses); mMtu = mMtuV4 = mMtuV6 = mtu; + mHandoverFailureMode = HANDOVER_FAILURE_MODE_LEGACY; + mPduSessionId = PDU_SESSION_ID_NOT_SET; + mDefaultQos = null; + mQosBearerSessions = new ArrayList<>(); + mSliceInfo = null; + mTrafficDescriptors = new ArrayList<>(); } - /** @hide */ - private DataCallResponse(@DataFailureCause int cause, int suggestedRetryTime, int id, + private DataCallResponse(@DataFailureCause int cause, long suggestedRetryTime, int id, @LinkStatus int linkStatus, @ProtocolType int protocolType, @Nullable String interfaceName, @Nullable List<LinkAddress> addresses, @Nullable List<InetAddress> dnsAddresses, @Nullable List<InetAddress> gatewayAddresses, - @Nullable List<InetAddress> pcscfAddresses, int mtu, int mtuV4, int mtuV6) { + @Nullable List<InetAddress> pcscfAddresses, int mtu, int mtuV4, int mtuV6, + @HandoverFailureMode int handoverFailureMode, int pduSessionId, + @Nullable Qos defaultQos, @Nullable List<QosBearerSession> qosBearerSessions, + @Nullable NetworkSliceInfo sliceInfo, + @Nullable List<TrafficDescriptor> trafficDescriptors) { mCause = cause; mSuggestedRetryTime = suggestedRetryTime; mId = id; @@ -151,13 +221,21 @@ public final class DataCallResponse implements Parcelable { mMtu = mtu; mMtuV4 = mtuV4; mMtuV6 = mtuV6; + mHandoverFailureMode = handoverFailureMode; + mPduSessionId = pduSessionId; + mDefaultQos = defaultQos; + mQosBearerSessions = (qosBearerSessions == null) + ? new ArrayList<>() : new ArrayList<>(qosBearerSessions); + mSliceInfo = sliceInfo; + mTrafficDescriptors = (trafficDescriptors == null) + ? new ArrayList<>() : new ArrayList<>(trafficDescriptors); } /** @hide */ @VisibleForTesting public DataCallResponse(Parcel source) { mCause = source.readInt(); - mSuggestedRetryTime = source.readInt(); + mSuggestedRetryTime = source.readLong(); mId = source.readInt(); mLinkStatus = source.readInt(); mProtocolType = source.readInt(); @@ -173,6 +251,14 @@ public final class DataCallResponse implements Parcelable { mMtu = source.readInt(); mMtuV4 = source.readInt(); mMtuV6 = source.readInt(); + mHandoverFailureMode = source.readInt(); + mPduSessionId = source.readInt(); + mDefaultQos = source.readParcelable(Qos.class.getClassLoader()); + mQosBearerSessions = new ArrayList<>(); + source.readList(mQosBearerSessions, QosBearerSession.class.getClassLoader()); + mSliceInfo = source.readParcelable(NetworkSliceInfo.class.getClassLoader()); + mTrafficDescriptors = new ArrayList<>(); + source.readList(mTrafficDescriptors, TrafficDescriptor.class.getClassLoader()); } /** @@ -182,9 +268,33 @@ public final class DataCallResponse implements Parcelable { public int getCause() { return mCause; } /** - * @return The suggested data retry time in milliseconds. + * @return The suggested data retry time in milliseconds. 0 when network does not + * suggest a retry time (Note this is different from the replacement + * {@link #getRetryDurationMillis()}). + * + * @deprecated Use {@link #getRetryDurationMillis()} instead. + */ + @Deprecated + public int getSuggestedRetryTime() { + // To match the pre-deprecated getSuggestedRetryTime() behavior. + if (mSuggestedRetryTime == RETRY_DURATION_UNDEFINED) { + return 0; + } else if (mSuggestedRetryTime > Integer.MAX_VALUE) { + return Integer.MAX_VALUE; + } + return (int) mSuggestedRetryTime; + } + + /** + * @return The network suggested data retry duration in milliseconds as specified in + * 3GPP TS 24.302 section 8.2.9.1. The APN associated to this data call will be throttled for + * the specified duration unless {@link DataServiceCallback#onApnUnthrottled} is called. + * {@code Long.MAX_VALUE} indicates data retry should not occur. + * {@link #RETRY_DURATION_UNDEFINED} indicates network did not suggest any retry duration. */ - public int getSuggestedRetryTime() { return mSuggestedRetryTime; } + public long getRetryDurationMillis() { + return mSuggestedRetryTime; + } /** * @return The unique id of the data connection. @@ -262,6 +372,57 @@ public final class DataCallResponse implements Parcelable { return mMtuV6; } + /** + * @return The data handover failure mode. + */ + public @HandoverFailureMode int getHandoverFailureMode() { + return mHandoverFailureMode; + } + + /** + * @return The pdu session id + */ + public int getPduSessionId() { + return mPduSessionId; + } + + /** + * @return default QOS of the data connection received from the network + * + * @hide + */ + @Nullable + public Qos getDefaultQos() { + return mDefaultQos; + } + + /** + * @return All the dedicated bearer QOS sessions of the data connection received from the + * network. + * + * @hide + */ + @NonNull + public List<QosBearerSession> getQosBearerSessions() { + return mQosBearerSessions; + } + + /** + * @return The slice info related to this data connection. + */ + @Nullable + public NetworkSliceInfo getSliceInfo() { + return mSliceInfo; + } + + /** + * @return The traffic descriptors related to this data connection. + */ + @NonNull + public List<TrafficDescriptor> getTrafficDescriptors() { + return mTrafficDescriptors; + } + @NonNull @Override public String toString() { @@ -280,6 +441,12 @@ public final class DataCallResponse implements Parcelable { .append(" mtu=").append(getMtu()) .append(" mtuV4=").append(getMtuV4()) .append(" mtuV6=").append(getMtuV6()) + .append(" handoverFailureMode=").append(failureModeToString(mHandoverFailureMode)) + .append(" pduSessionId=").append(getPduSessionId()) + .append(" defaultQos=").append(mDefaultQos) + .append(" qosBearerSessions=").append(mQosBearerSessions) + .append(" sliceInfo=").append(mSliceInfo) + .append(" trafficDescriptors=").append(mTrafficDescriptors) .append("}"); return sb.toString(); } @@ -293,12 +460,29 @@ public final class DataCallResponse implements Parcelable { } DataCallResponse other = (DataCallResponse) o; - return this.mCause == other.mCause - && this.mSuggestedRetryTime == other.mSuggestedRetryTime - && this.mId == other.mId - && this.mLinkStatus == other.mLinkStatus - && this.mProtocolType == other.mProtocolType - && this.mInterfaceName.equals(other.mInterfaceName) + + final boolean isQosSame = (mDefaultQos == null || other.mDefaultQos == null) + ? mDefaultQos == other.mDefaultQos + : mDefaultQos.equals(other.mDefaultQos); + + final boolean isQosBearerSessionsSame = + (mQosBearerSessions == null || other.mQosBearerSessions == null) + ? mQosBearerSessions == other.mQosBearerSessions + : mQosBearerSessions.size() == other.mQosBearerSessions.size() + && mQosBearerSessions.containsAll(other.mQosBearerSessions); + + final boolean isTrafficDescriptorsSame = + (mTrafficDescriptors == null || other.mTrafficDescriptors == null) + ? mTrafficDescriptors == other.mTrafficDescriptors + : mTrafficDescriptors.size() == other.mTrafficDescriptors.size() + && mTrafficDescriptors.containsAll(other.mTrafficDescriptors); + + return mCause == other.mCause + && mSuggestedRetryTime == other.mSuggestedRetryTime + && mId == other.mId + && mLinkStatus == other.mLinkStatus + && mProtocolType == other.mProtocolType + && mInterfaceName.equals(other.mInterfaceName) && mAddresses.size() == other.mAddresses.size() && mAddresses.containsAll(other.mAddresses) && mDnsAddresses.size() == other.mDnsAddresses.size() @@ -309,14 +493,21 @@ public final class DataCallResponse implements Parcelable { && mPcscfAddresses.containsAll(other.mPcscfAddresses) && mMtu == other.mMtu && mMtuV4 == other.mMtuV4 - && mMtuV6 == other.mMtuV6; + && mMtuV6 == other.mMtuV6 + && mHandoverFailureMode == other.mHandoverFailureMode + && mPduSessionId == other.mPduSessionId + && isQosSame + && isQosBearerSessionsSame + && Objects.equals(mSliceInfo, other.mSliceInfo) + && isTrafficDescriptorsSame; } @Override public int hashCode() { return Objects.hash(mCause, mSuggestedRetryTime, mId, mLinkStatus, mProtocolType, mInterfaceName, mAddresses, mDnsAddresses, mGatewayAddresses, mPcscfAddresses, - mMtu, mMtuV4, mMtuV6); + mMtu, mMtuV4, mMtuV6, mHandoverFailureMode, mPduSessionId, mDefaultQos, + mQosBearerSessions, mSliceInfo, mTrafficDescriptors); } @Override @@ -327,7 +518,7 @@ public final class DataCallResponse implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(mCause); - dest.writeInt(mSuggestedRetryTime); + dest.writeLong(mSuggestedRetryTime); dest.writeInt(mId); dest.writeInt(mLinkStatus); dest.writeInt(mProtocolType); @@ -339,6 +530,20 @@ public final class DataCallResponse implements Parcelable { dest.writeInt(mMtu); dest.writeInt(mMtuV4); dest.writeInt(mMtuV6); + dest.writeInt(mHandoverFailureMode); + dest.writeInt(mPduSessionId); + if (mDefaultQos != null) { + if (mDefaultQos.getType() == Qos.QOS_TYPE_EPS) { + dest.writeParcelable((EpsQos) mDefaultQos, flags); + } else { + dest.writeParcelable((NrQos) mDefaultQos, flags); + } + } else { + dest.writeParcelable(null, flags); + } + dest.writeList(mQosBearerSessions); + dest.writeParcelable(mSliceInfo, flags); + dest.writeList(mTrafficDescriptors); } public static final @android.annotation.NonNull Parcelable.Creator<DataCallResponse> CREATOR = @@ -355,6 +560,25 @@ public final class DataCallResponse implements Parcelable { }; /** + * Convert handover failure mode to string. + * + * @param handoverFailureMode Handover failure mode + * @return Handover failure mode in string + * + * @hide + */ + public static String failureModeToString(@HandoverFailureMode int handoverFailureMode) { + switch (handoverFailureMode) { + case HANDOVER_FAILURE_MODE_UNKNOWN: return "unknown"; + case HANDOVER_FAILURE_MODE_LEGACY: return "legacy"; + case HANDOVER_FAILURE_MODE_DO_FALLBACK: return "fallback"; + case HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_HANDOVER: return "retry handover"; + case HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_SETUP_NORMAL: return "retry setup new one"; + default: return Integer.toString(handoverFailureMode); + } + } + + /** * Provides a convenient way to set the fields of a {@link DataCallResponse} when creating a new * instance. * @@ -371,7 +595,7 @@ public final class DataCallResponse implements Parcelable { public static final class Builder { private @DataFailureCause int mCause; - private int mSuggestedRetryTime; + private long mSuggestedRetryTime = RETRY_DURATION_UNDEFINED; private int mId; @@ -395,6 +619,18 @@ public final class DataCallResponse implements Parcelable { private int mMtuV6; + private @HandoverFailureMode int mHandoverFailureMode = HANDOVER_FAILURE_MODE_LEGACY; + + private int mPduSessionId = PDU_SESSION_ID_NOT_SET; + + private Qos mDefaultQos; + + private List<QosBearerSession> mQosBearerSessions = new ArrayList<>(); + + private NetworkSliceInfo mSliceInfo; + + private List<TrafficDescriptor> mTrafficDescriptors = new ArrayList<>(); + /** * Default constructor for Builder. */ @@ -417,9 +653,23 @@ public final class DataCallResponse implements Parcelable { * * @param suggestedRetryTime The suggested data retry time in milliseconds. * @return The same instance of the builder. + * + * @deprecated Use {@link #setRetryDurationMillis(long)} instead. */ + @Deprecated public @NonNull Builder setSuggestedRetryTime(int suggestedRetryTime) { - mSuggestedRetryTime = suggestedRetryTime; + mSuggestedRetryTime = (long) suggestedRetryTime; + return this; + } + + /** + * Set the network suggested data retry duration. + * + * @param retryDurationMillis The suggested data retry duration in milliseconds. + * @return The same instance of the builder. + */ + public @NonNull Builder setRetryDurationMillis(long retryDurationMillis) { + mSuggestedRetryTime = retryDurationMillis; return this; } @@ -553,6 +803,99 @@ public final class DataCallResponse implements Parcelable { } /** + * Set data handover failure mode for the data call response. + * + * @param failureMode Handover failure mode. + * @return The same instance of the builder. + */ + public @NonNull Builder setHandoverFailureMode(@HandoverFailureMode int failureMode) { + mHandoverFailureMode = failureMode; + return this; + } + + /** + * Set pdu session id. + * <p/> + * The id must be between 1 and 15 when linked to a pdu session. If no pdu session + * exists for the current data call, the id must be set to {@link PDU_SESSION_ID_NOT_SET}. + * + * @param pduSessionId Pdu Session Id of the data call. + * @return The same instance of the builder. + */ + public @NonNull Builder setPduSessionId( + @IntRange(from = PDU_SESSION_ID_NOT_SET, to = 15) int pduSessionId) { + Preconditions.checkArgument(pduSessionId >= PDU_SESSION_ID_NOT_SET, + "pduSessionId must be greater than or equal to" + PDU_SESSION_ID_NOT_SET); + Preconditions.checkArgument(pduSessionId <= 15, + "pduSessionId must be less than or equal to 15."); + mPduSessionId = pduSessionId; + return this; + } + + /** + * Set the default QOS for this data connection. + * + * @param defaultQos QOS (Quality Of Service) received from network. + * + * @return The same instance of the builder. + * + * @hide + */ + public @NonNull Builder setDefaultQos(@Nullable Qos defaultQos) { + mDefaultQos = defaultQos; + return this; + } + + /** + * Set the dedicated bearer QOS sessions for this data connection. + * + * @param qosBearerSessions Dedicated bearer QOS (Quality Of Service) sessions received + * from network. + * + * @return The same instance of the builder. + * + * @hide + */ + public @NonNull Builder setQosBearerSessions( + @NonNull List<QosBearerSession> qosBearerSessions) { + mQosBearerSessions = qosBearerSessions; + return this; + } + + /** + * The Slice used for this data connection. + * <p/> + * If a handover occurs from EPDG to 5G, + * this is the {@link NetworkSliceInfo} used in {@link DataService#setupDataCall}. + * + * @param sliceInfo the slice info for the data call + * + * @return The same instance of the builder. + */ + public @NonNull Builder setSliceInfo(@Nullable NetworkSliceInfo sliceInfo) { + mSliceInfo = sliceInfo; + return this; + } + + /** + * The traffic descriptors for this data connection, as defined in 3GPP TS 24.526 + * Section 5.2. They are used for URSP traffic matching as described in 3GPP TS 24.526 + * Section 4.2.2. They includes an optional DNN, which, if present, must be used for traffic + * matching; it does not specify the end point to be used for the data call. The end point + * is specified by {@link DataProfile}, which must be used as the end point if one is not + * specified through URSP rules. + * + * @param trafficDescriptors the traffic descriptors for the data call. + * + * @return The same instance of the builder. + */ + public @NonNull Builder setTrafficDescriptors( + @NonNull List<TrafficDescriptor> trafficDescriptors) { + mTrafficDescriptors = trafficDescriptors; + return this; + } + + /** * Build the DataCallResponse. * * @return the DataCallResponse object. @@ -560,7 +903,8 @@ public final class DataCallResponse implements Parcelable { public @NonNull DataCallResponse build() { return new DataCallResponse(mCause, mSuggestedRetryTime, mId, mLinkStatus, mProtocolType, mInterfaceName, mAddresses, mDnsAddresses, mGatewayAddresses, - mPcscfAddresses, mMtu, mMtuV4, mMtuV6); + mPcscfAddresses, mMtu, mMtuV4, mMtuV6, mHandoverFailureMode, mPduSessionId, + mDefaultQos, mQosBearerSessions, mSliceInfo, mTrafficDescriptors); } } } diff --git a/telephony/java/android/telephony/data/DataService.java b/telephony/java/android/telephony/data/DataService.java index f56bbe13051c..2f034752ae5f 100644 --- a/telephony/java/android/telephony/data/DataService.java +++ b/telephony/java/android/telephony/data/DataService.java @@ -17,6 +17,7 @@ package android.telephony.data; import android.annotation.IntDef; +import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SdkConstant; @@ -30,7 +31,7 @@ import android.os.IBinder; import android.os.Looper; import android.os.Message; import android.os.RemoteException; -import android.telephony.AccessNetworkConstants; +import android.util.Log; import android.util.SparseArray; import com.android.internal.annotations.VisibleForTesting; @@ -40,6 +41,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.List; +import java.util.Objects; /** * Base class of data service. Services that extend DataService must register the service in @@ -104,6 +106,11 @@ public abstract class DataService extends Service { private static final int DATA_SERVICE_REQUEST_REGISTER_DATA_CALL_LIST_CHANGED = 9; private static final int DATA_SERVICE_REQUEST_UNREGISTER_DATA_CALL_LIST_CHANGED = 10; private static final int DATA_SERVICE_INDICATION_DATA_CALL_LIST_CHANGED = 11; + private static final int DATA_SERVICE_REQUEST_START_HANDOVER = 12; + private static final int DATA_SERVICE_REQUEST_CANCEL_HANDOVER = 13; + private static final int DATA_SERVICE_REQUEST_REGISTER_APN_UNTHROTTLED = 14; + private static final int DATA_SERVICE_REQUEST_UNREGISTER_APN_UNTHROTTLED = 15; + private static final int DATA_SERVICE_INDICATION_APN_UNTHROTTLED = 16; private final HandlerThread mHandlerThread; @@ -126,6 +133,8 @@ public abstract class DataService extends Service { private final List<IDataServiceCallback> mDataCallListChangedCallbacks = new ArrayList<>(); + private final List<IDataServiceCallback> mApnUnthrottledCallbacks = new ArrayList<>(); + /** * Constructor * @param slotIndex SIM slot index the data service provider associated with. @@ -147,7 +156,7 @@ public abstract class DataService extends Service { * the provided callback to notify the platform. * * @param accessNetworkType Access network type that the data call will be established on. - * Must be one of {@link AccessNetworkConstants.AccessNetworkType}. + * Must be one of {@link android.telephony.AccessNetworkConstants.AccessNetworkType}. * @param dataProfile Data profile used for data call setup. See {@link DataProfile} * @param isRoaming True if the device is data roaming. * @param allowRoaming True if data roaming is allowed by the user. @@ -158,10 +167,9 @@ public abstract class DataService extends Service { * @param callback The result callback for this request. */ public void setupDataCall(int accessNetworkType, @NonNull DataProfile dataProfile, - boolean isRoaming, boolean allowRoaming, - @SetupDataReason int reason, - @Nullable LinkProperties linkProperties, - @NonNull DataServiceCallback callback) { + boolean isRoaming, boolean allowRoaming, + @SetupDataReason int reason, @Nullable LinkProperties linkProperties, + @NonNull DataServiceCallback callback) { // The default implementation is to return unsupported. if (callback != null) { callback.onSetupDataCallComplete(DataServiceCallback.RESULT_ERROR_UNSUPPORTED, @@ -170,6 +178,55 @@ public abstract class DataService extends Service { } /** + * Setup a data connection. The data service provider must implement this method to support + * establishing a packet data connection. When completed or error, the service must invoke + * the provided callback to notify the platform. + * + * @param accessNetworkType Access network type that the data call will be established on. + * Must be one of {@link android.telephony.AccessNetworkConstants.AccessNetworkType}. + * @param dataProfile Data profile used for data call setup. See {@link DataProfile} + * @param isRoaming True if the device is data roaming. + * @param allowRoaming True if data roaming is allowed by the user. + * @param reason The reason for data setup. Must be {@link #REQUEST_REASON_NORMAL} or + * {@link #REQUEST_REASON_HANDOVER}. + * @param linkProperties If {@code reason} is {@link #REQUEST_REASON_HANDOVER}, this is the + * link properties of the existing data connection, otherwise null. + * @param pduSessionId The pdu session id to be used for this data call. + * The standard range of values are 1-15 while 0 means no pdu session id + * was attached to this call. Reference: 3GPP TS 24.007 section + * 11.2.3.1b. + * @param sliceInfo used within the data connection when a handover occurs from EPDG to 5G. + * The value is null unless the access network is + * {@link android.telephony.AccessNetworkConstants.AccessNetworkType#NGRAN} and a + * handover is occurring from EPDG to 5G. If the slice passed is rejected, then + * {@link DataCallResponse#getCause()} is + * {@link android.telephony.DataFailCause#SLICE_REJECTED}. + * @param trafficDescriptor {@link TrafficDescriptor} for which data connection needs to be + * established. It is used for URSP traffic matching as described in 3GPP TS 24.526 + * Section 4.2.2. It includes an optional DNN which, if present, must be used for + * traffic matching; it does not specify the end point to be used for the data call. + * @param matchAllRuleAllowed Indicates if using default match-all URSP rule for this + * request is allowed. If false, this request must not use the match-all URSP rule + * and if a non-match-all rule is not found (or if URSP rules are not available) then + * {@link DataCallResponse#getCause()} is + * {@link android.telephony.DataFailCause#MATCH_ALL_RULE_NOT_ALLOWED}. This is needed + * as some requests need to have a hard failure if the intention cannot be met, + * for example, a zero-rating slice. + * @param callback The result callback for this request. + */ + public void setupDataCall(int accessNetworkType, @NonNull DataProfile dataProfile, + boolean isRoaming, boolean allowRoaming, + @SetupDataReason int reason, + @Nullable LinkProperties linkProperties, + @IntRange(from = 0, to = 15) int pduSessionId, @Nullable NetworkSliceInfo sliceInfo, + @Nullable TrafficDescriptor trafficDescriptor, boolean matchAllRuleAllowed, + @NonNull DataServiceCallback callback) { + /* Call the old version since the new version isn't supported */ + setupDataCall(accessNetworkType, dataProfile, isRoaming, allowRoaming, reason, + linkProperties, callback); + } + + /** * Deactivate a data connection. The data service provider must implement this method to * support data connection tear down. When completed or error, the service must invoke the * provided callback to notify the platform. @@ -224,6 +281,60 @@ public abstract class DataService extends Service { } /** + * Indicates that a handover has begun. This is called on the source transport. + * + * Any resources being transferred cannot be released while a + * handover is underway. + * <p/> + * If a handover was unsuccessful, then the framework calls + * {@link DataService#cancelHandover}. The target transport retains ownership over any of + * the resources being transferred. + * <p/> + * If a handover was successful, the framework calls {@link DataService#deactivateDataCall} + * with reason {@link DataService.REQUEST_REASON_HANDOVER}. The target transport now owns + * the transferred resources and is responsible for releasing them. + * + * @param cid The identifier of the data call which is provided in {@link DataCallResponse} + * @param callback The result callback for this request. + * + * @hide + */ + public void startHandover(int cid, @NonNull DataServiceCallback callback) { + Objects.requireNonNull(callback, "callback cannot be null"); + // The default implementation is to return unsupported. + Log.d(TAG, "startHandover: " + cid); + callback.onHandoverStarted(DataServiceCallback.RESULT_ERROR_UNSUPPORTED); + } + + /** + * Indicates that a handover was cancelled after a call to + * {@link DataService#startHandover}. This is called on the source transport. + * <p/> + * Since the handover was unsuccessful, the source transport retains ownership over any of + * the resources being transferred and is still responsible for releasing them. + * <p/> + * The handover can be cancelled up until either: + * <ul><li> + * The handover was successful after receiving a successful response from + * {@link DataService#setupDataCall} on the target transport. + * </li><li> + * The data call on the source transport was lost. + * </li> + * </ul> + * + * @param cid The identifier of the data call which is provided in {@link DataCallResponse} + * @param callback The result callback for this request. + * + * @hide + */ + public void cancelHandover(int cid, @NonNull DataServiceCallback callback) { + Objects.requireNonNull(callback, "callback cannot be null"); + // The default implementation is to return unsupported. + Log.d(TAG, "cancelHandover: " + cid); + callback.onHandoverCancelled(DataServiceCallback.RESULT_ERROR_UNSUPPORTED); + } + + /** * Get the active data call list. * * @param callback The result callback for this request. @@ -246,6 +357,19 @@ public abstract class DataService extends Service { } } + private void registerForApnUnthrottled(IDataServiceCallback callback) { + synchronized (mApnUnthrottledCallbacks) { + mApnUnthrottledCallbacks.add(callback); + } + } + + private void unregisterForApnUnthrottled(IDataServiceCallback callback) { + synchronized (mApnUnthrottledCallbacks) { + mApnUnthrottledCallbacks.remove(callback); + } + } + + /** * Notify the system that current data call list changed. Data service must invoke this * method whenever there is any data call status changed. @@ -263,6 +387,21 @@ public abstract class DataService extends Service { } /** + * Notify the system that a given APN was unthrottled. + * + * @param apn Access Point Name defined by the carrier. + */ + public final void notifyApnUnthrottled(@NonNull String apn) { + synchronized (mApnUnthrottledCallbacks) { + for (IDataServiceCallback callback : mApnUnthrottledCallbacks) { + mHandler.obtainMessage(DATA_SERVICE_INDICATION_APN_UNTHROTTLED, + mSlotIndex, 0, new ApnUnthrottledIndication(apn, + callback)).sendToTarget(); + } + } + } + + /** * Called when the instance of data service is destroyed (e.g. got unbind or binder died) * or when the data service provider is removed. The extended class should implement this * method to perform cleanup works. @@ -278,16 +417,25 @@ public abstract class DataService extends Service { public final boolean allowRoaming; public final int reason; public final LinkProperties linkProperties; + public final int pduSessionId; + public final NetworkSliceInfo sliceInfo; + public final TrafficDescriptor trafficDescriptor; + public final boolean matchAllRuleAllowed; public final IDataServiceCallback callback; SetupDataCallRequest(int accessNetworkType, DataProfile dataProfile, boolean isRoaming, - boolean allowRoaming, int reason, LinkProperties linkProperties, - IDataServiceCallback callback) { + boolean allowRoaming, int reason, LinkProperties linkProperties, int pduSessionId, + NetworkSliceInfo sliceInfo, TrafficDescriptor trafficDescriptor, + boolean matchAllRuleAllowed, IDataServiceCallback callback) { this.accessNetworkType = accessNetworkType; this.dataProfile = dataProfile; this.isRoaming = isRoaming; this.allowRoaming = allowRoaming; this.linkProperties = linkProperties; this.reason = reason; + this.pduSessionId = pduSessionId; + this.sliceInfo = sliceInfo; + this.trafficDescriptor = trafficDescriptor; + this.matchAllRuleAllowed = matchAllRuleAllowed; this.callback = callback; } } @@ -327,6 +475,16 @@ public abstract class DataService extends Service { } } + private static final class BeginCancelHandoverRequest { + public final int cid; + public final IDataServiceCallback callback; + BeginCancelHandoverRequest(int cid, + IDataServiceCallback callback) { + this.cid = cid; + this.callback = callback; + } + } + private static final class DataCallListChangedIndication { public final List<DataCallResponse> dataCallList; public final IDataServiceCallback callback; @@ -337,6 +495,16 @@ public abstract class DataService extends Service { } } + private static final class ApnUnthrottledIndication { + public final String apn; + public final IDataServiceCallback callback; + ApnUnthrottledIndication(String apn, + IDataServiceCallback callback) { + this.apn = apn; + this.callback = callback; + } + } + private class DataServiceHandler extends Handler { DataServiceHandler(Looper looper) { @@ -377,7 +545,9 @@ public abstract class DataService extends Service { serviceProvider.setupDataCall(setupDataCallRequest.accessNetworkType, setupDataCallRequest.dataProfile, setupDataCallRequest.isRoaming, setupDataCallRequest.allowRoaming, setupDataCallRequest.reason, - setupDataCallRequest.linkProperties, + setupDataCallRequest.linkProperties, setupDataCallRequest.pduSessionId, + setupDataCallRequest.sliceInfo, setupDataCallRequest.trafficDescriptor, + setupDataCallRequest.matchAllRuleAllowed, (setupDataCallRequest.callback != null) ? new DataServiceCallback(setupDataCallRequest.callback) : null); @@ -438,6 +608,40 @@ public abstract class DataService extends Service { loge("Failed to call onDataCallListChanged. " + e); } break; + case DATA_SERVICE_REQUEST_START_HANDOVER: + if (serviceProvider == null) break; + BeginCancelHandoverRequest bReq = (BeginCancelHandoverRequest) message.obj; + serviceProvider.startHandover(bReq.cid, + (bReq.callback != null) + ? new DataServiceCallback(bReq.callback) : null); + break; + case DATA_SERVICE_REQUEST_CANCEL_HANDOVER: + if (serviceProvider == null) break; + BeginCancelHandoverRequest cReq = (BeginCancelHandoverRequest) message.obj; + serviceProvider.cancelHandover(cReq.cid, + (cReq.callback != null) + ? new DataServiceCallback(cReq.callback) : null); + break; + case DATA_SERVICE_REQUEST_REGISTER_APN_UNTHROTTLED: + if (serviceProvider == null) break; + serviceProvider.registerForApnUnthrottled((IDataServiceCallback) message.obj); + break; + case DATA_SERVICE_REQUEST_UNREGISTER_APN_UNTHROTTLED: + if (serviceProvider == null) break; + callback = (IDataServiceCallback) message.obj; + serviceProvider.unregisterForApnUnthrottled(callback); + break; + case DATA_SERVICE_INDICATION_APN_UNTHROTTLED: + if (serviceProvider == null) break; + ApnUnthrottledIndication apnUnthrottledIndication = + (ApnUnthrottledIndication) message.obj; + try { + apnUnthrottledIndication.callback + .onApnUnthrottled(apnUnthrottledIndication.apn); + } catch (RemoteException e) { + loge("Failed to call onApnUnthrottled. " + e); + } + break; } } } @@ -506,11 +710,14 @@ public abstract class DataService extends Service { @Override public void setupDataCall(int slotIndex, int accessNetworkType, DataProfile dataProfile, - boolean isRoaming, boolean allowRoaming, int reason, - LinkProperties linkProperties, IDataServiceCallback callback) { + boolean isRoaming, boolean allowRoaming, int reason, + LinkProperties linkProperties, int pduSessionId, NetworkSliceInfo sliceInfo, + TrafficDescriptor trafficDescriptor, boolean matchAllRuleAllowed, + IDataServiceCallback callback) { mHandler.obtainMessage(DATA_SERVICE_REQUEST_SETUP_DATA_CALL, slotIndex, 0, new SetupDataCallRequest(accessNetworkType, dataProfile, isRoaming, - allowRoaming, reason, linkProperties, callback)) + allowRoaming, reason, linkProperties, pduSessionId, sliceInfo, + trafficDescriptor, matchAllRuleAllowed, callback)) .sendToTarget(); } @@ -566,6 +773,49 @@ public abstract class DataService extends Service { mHandler.obtainMessage(DATA_SERVICE_REQUEST_UNREGISTER_DATA_CALL_LIST_CHANGED, slotIndex, 0, callback).sendToTarget(); } + + @Override + public void startHandover(int slotIndex, int cid, IDataServiceCallback callback) { + if (callback == null) { + loge("startHandover: callback is null"); + return; + } + BeginCancelHandoverRequest req = new BeginCancelHandoverRequest(cid, callback); + mHandler.obtainMessage(DATA_SERVICE_REQUEST_START_HANDOVER, + slotIndex, 0, req) + .sendToTarget(); + } + + @Override + public void cancelHandover(int slotIndex, int cid, IDataServiceCallback callback) { + if (callback == null) { + loge("cancelHandover: callback is null"); + return; + } + BeginCancelHandoverRequest req = new BeginCancelHandoverRequest(cid, callback); + mHandler.obtainMessage(DATA_SERVICE_REQUEST_CANCEL_HANDOVER, + slotIndex, 0, req).sendToTarget(); + } + + @Override + public void registerForUnthrottleApn(int slotIndex, IDataServiceCallback callback) { + if (callback == null) { + loge("registerForUnthrottleApn: callback is null"); + return; + } + mHandler.obtainMessage(DATA_SERVICE_REQUEST_REGISTER_APN_UNTHROTTLED, slotIndex, + 0, callback).sendToTarget(); + } + + @Override + public void unregisterForUnthrottleApn(int slotIndex, IDataServiceCallback callback) { + if (callback == null) { + loge("uregisterForUnthrottleApn: callback is null"); + return; + } + mHandler.obtainMessage(DATA_SERVICE_REQUEST_UNREGISTER_APN_UNTHROTTLED, + slotIndex, 0, callback).sendToTarget(); + } } private void log(String s) { diff --git a/telephony/java/android/telephony/data/DataServiceCallback.java b/telephony/java/android/telephony/data/DataServiceCallback.java index 72b68e4645c9..363e47a6d242 100644 --- a/telephony/java/android/telephony/data/DataServiceCallback.java +++ b/telephony/java/android/telephony/data/DataServiceCallback.java @@ -79,7 +79,7 @@ public class DataServiceCallback { * @param response Setup data call response. */ public void onSetupDataCallComplete(@ResultCode int result, - @Nullable DataCallResponse response) { + @Nullable DataCallResponse response) { if (mCallback != null) { try { if (DBG) Rlog.d(TAG, "onSetupDataCallComplete"); @@ -156,7 +156,7 @@ public class DataServiceCallback { * set it to an empty list. */ public void onRequestDataCallListComplete(@ResultCode int result, - @NonNull List<DataCallResponse> dataCallList) { + @NonNull List<DataCallResponse> dataCallList) { if (mCallback != null) { try { mCallback.onRequestDataCallListComplete(result, dataCallList); @@ -186,4 +186,92 @@ public class DataServiceCallback { Rlog.e(TAG, "onDataCallListChanged: callback is null!"); } } + + /** + * Called to indicate result for the request {@link DataService#startHandover}. + * + * @param result The result code. Must be one of the {@link ResultCode} + * + * @hide + */ + public void onHandoverStarted(@ResultCode int result) { + if (mCallback != null) { + try { + if (DBG) Rlog.d(TAG, "onHandoverStarted"); + mCallback.onHandoverStarted(result); + } catch (RemoteException e) { + Rlog.e(TAG, "Failed to onHandoverStarted on the remote"); + } + } else { + Rlog.e(TAG, "onHandoverStarted: callback is null!"); + } + } + + /** + * Called to indicate result for the request {@link DataService#cancelHandover}. + * + * @param result The result code. Must be one of the {@link ResultCode} + * + * @hide + */ + public void onHandoverCancelled(@ResultCode int result) { + if (mCallback != null) { + try { + if (DBG) Rlog.d(TAG, "onHandoverCancelled"); + mCallback.onHandoverCancelled(result); + } catch (RemoteException e) { + Rlog.e(TAG, "Failed to onHandoverCancelled on the remote"); + } + } else { + Rlog.e(TAG, "onHandoverCancelled: callback is null!"); + } + } + + /** + * Get the result code as a string + * + * @param resultCode The result code. Must be one of the {@link ResultCode} + * @return the string representation + * + * @hide + */ + @NonNull + public static String resultCodeToString(@DataServiceCallback.ResultCode int resultCode) { + switch (resultCode) { + case RESULT_SUCCESS: + return "RESULT_SUCCESS"; + case RESULT_ERROR_UNSUPPORTED: + return "RESULT_ERROR_UNSUPPORTED"; + case RESULT_ERROR_INVALID_ARG: + return "RESULT_ERROR_INVALID_ARG"; + case RESULT_ERROR_BUSY: + return "RESULT_ERROR_BUSY"; + case RESULT_ERROR_ILLEGAL_STATE: + return "RESULT_ERROR_ILLEGAL_STATE"; + default: + return "Missing case for result code=" + resultCode; + } + } + + /** + * Unthrottles the APN on the current transport. There is no matching "APN throttle" method. + * Instead, the APN is throttled for the time specified in + * {@link DataCallResponse#getRetryDurationMillis}. + * <p/> + * see: {@link DataCallResponse#getRetryDurationMillis} + * + * @param apn Access Point Name defined by the carrier. + */ + public void onApnUnthrottled(final @NonNull String apn) { + if (mCallback != null) { + try { + if (DBG) Rlog.d(TAG, "onApnUnthrottled"); + mCallback.onApnUnthrottled(apn); + } catch (RemoteException e) { + Rlog.e(TAG, "onApnUnthrottled: remote exception", e); + } + } else { + Rlog.e(TAG, "onApnUnthrottled: callback is null!"); + } + } } diff --git a/telephony/java/android/telephony/data/EpsBearerQosSessionAttributes.aidl b/telephony/java/android/telephony/data/EpsBearerQosSessionAttributes.aidl new file mode 100644 index 000000000000..da31f9864cf1 --- /dev/null +++ b/telephony/java/android/telephony/data/EpsBearerQosSessionAttributes.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2020 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.data; + + parcelable EpsBearerQosSessionAttributes;
\ No newline at end of file diff --git a/telephony/java/android/telephony/data/EpsBearerQosSessionAttributes.java b/telephony/java/android/telephony/data/EpsBearerQosSessionAttributes.java new file mode 100644 index 000000000000..9bc7a5c6cf96 --- /dev/null +++ b/telephony/java/android/telephony/data/EpsBearerQosSessionAttributes.java @@ -0,0 +1,224 @@ +/* + * Copyright (C) 2020 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.data; + +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.net.QosSessionAttributes; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.Log; + +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +/** + * Provides Qos attributes of an EPS bearer. + * + * {@hide} + */ +@SystemApi +public final class EpsBearerQosSessionAttributes implements Parcelable, QosSessionAttributes { + private static final String TAG = EpsBearerQosSessionAttributes.class.getSimpleName(); + private final int mQci; + private final long mMaxUplinkBitRate; + private final long mMaxDownlinkBitRate; + private final long mGuaranteedUplinkBitRate; + private final long mGuaranteedDownlinkBitRate; + @NonNull private final List<InetSocketAddress> mRemoteAddresses; + + /** + * Quality of Service Class Identifier (QCI), see 3GPP TS 23.203 and 29.212. + * The allowed values are standard values(1-9, 65-68, 69-70, 75, 79-80, 82-85) + * defined in the spec and operator specific values in the range 128-254. + * + * @return the qci of the session + */ + public int getQosIdentifier() { + return mQci; + } + + /** + * Minimum bit rate in kbps that is guaranteed to be provided by the network on the uplink. + * + * see 3GPP TS 23.107 section 6.4.3.1 + * + * Note: The Qos Session may be shared with OTHER applications besides yours. + * + * @return the guaranteed bit rate in kbps + */ + public long getGuaranteedUplinkBitRateKbps() { + return mGuaranteedUplinkBitRate; + } + + /** + * Minimum bit rate in kbps that is guaranteed to be provided by the network on the downlink. + * + * see 3GPP TS 23.107 section 6.4.3.1 + * + * Note: The Qos Session may be shared with OTHER applications besides yours. + * + * @return the guaranteed bit rate in kbps + */ + public long getGuaranteedDownlinkBitRateKbps() { + return mGuaranteedDownlinkBitRate; + } + + /** + * The maximum kbps that the network will accept. + * + * see 3GPP TS 23.107 section 6.4.3.1 + * + * Note: The Qos Session may be shared with OTHER applications besides yours. + * + * @return the max uplink bit rate in kbps + */ + public long getMaxUplinkBitRateKbps() { + return mMaxUplinkBitRate; + } + + /** + * The maximum kbps that the network can provide. + * + * see 3GPP TS 23.107 section 6.4.3.1 + * + * Note: The Qos Session may be shared with OTHER applications besides yours. + * + * @return the max downlink bit rate in kbps + */ + public long getMaxDownlinkBitRateKbps() { + return mMaxDownlinkBitRate; + } + + /** + * List of remote addresses associated with the Qos Session. The given uplink bit rates apply + * to this given list of remote addresses. + * + * Note: In the event that the list is empty, it is assumed that the uplink bit rates apply to + * all remote addresses that are not contained in a different set of attributes. + * + * @return list of remote socket addresses that the attributes apply to + */ + @NonNull + public List<InetSocketAddress> getRemoteAddresses() { + return mRemoteAddresses; + } + + /** + * ..ctor for attributes + * + * @param qci quality class indicator + * @param maxDownlinkBitRate the max downlink bit rate in kbps + * @param maxUplinkBitRate the max uplink bit rate in kbps + * @param guaranteedDownlinkBitRate the guaranteed downlink bit rate in kbps + * @param guaranteedUplinkBitRate the guaranteed uplink bit rate in kbps + * @param remoteAddresses the remote addresses that the uplink bit rates apply to + * + * @hide + */ + public EpsBearerQosSessionAttributes(final int qci, + final long maxDownlinkBitRate, final long maxUplinkBitRate, + final long guaranteedDownlinkBitRate, final long guaranteedUplinkBitRate, + @NonNull final List<InetSocketAddress> remoteAddresses) { + Objects.requireNonNull(remoteAddresses, "remoteAddress must be non-null"); + mQci = qci; + mMaxDownlinkBitRate = maxDownlinkBitRate; + mMaxUplinkBitRate = maxUplinkBitRate; + mGuaranteedDownlinkBitRate = guaranteedDownlinkBitRate; + mGuaranteedUplinkBitRate = guaranteedUplinkBitRate; + + final List<InetSocketAddress> remoteAddressesTemp = copySocketAddresses(remoteAddresses); + mRemoteAddresses = Collections.unmodifiableList(remoteAddressesTemp); + } + + private static List<InetSocketAddress> copySocketAddresses( + @NonNull final List<InetSocketAddress> remoteAddresses) { + final List<InetSocketAddress> remoteAddressesTemp = new ArrayList<>(); + for (final InetSocketAddress socketAddress : remoteAddresses) { + if (socketAddress != null && socketAddress.getAddress() != null) { + remoteAddressesTemp.add(socketAddress); + } + } + return remoteAddressesTemp; + } + + private EpsBearerQosSessionAttributes(@NonNull final Parcel in) { + mQci = in.readInt(); + mMaxDownlinkBitRate = in.readLong(); + mMaxUplinkBitRate = in.readLong(); + mGuaranteedDownlinkBitRate = in.readLong(); + mGuaranteedUplinkBitRate = in.readLong(); + + final int size = in.readInt(); + final List<InetSocketAddress> remoteAddresses = new ArrayList<>(size); + for (int i = 0; i < size; i++) { + final byte[] addressBytes = in.createByteArray(); + final int port = in.readInt(); + try { + remoteAddresses.add( + new InetSocketAddress(InetAddress.getByAddress(addressBytes), port)); + } catch (final UnknownHostException e) { + // Impossible case since we filter out null values in the ..ctor + Log.e(TAG, "unable to unparcel remote address at index: " + i, e); + } + } + mRemoteAddresses = Collections.unmodifiableList(remoteAddresses); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull final Parcel dest, final int flags) { + dest.writeInt(mQci); + dest.writeLong(mMaxDownlinkBitRate); + dest.writeLong(mMaxUplinkBitRate); + dest.writeLong(mGuaranteedDownlinkBitRate); + dest.writeLong(mGuaranteedUplinkBitRate); + + final int size = mRemoteAddresses.size(); + dest.writeInt(size); + for (int i = 0; i < size; i++) { + final InetSocketAddress address = mRemoteAddresses.get(i); + dest.writeByteArray(address.getAddress().getAddress()); + dest.writeInt(address.getPort()); + } + } + + @NonNull + public static final Creator<EpsBearerQosSessionAttributes> CREATOR = + new Creator<EpsBearerQosSessionAttributes>() { + @NonNull + @Override + public EpsBearerQosSessionAttributes createFromParcel(@NonNull final Parcel in) { + return new EpsBearerQosSessionAttributes(in); + } + + @NonNull + @Override + public EpsBearerQosSessionAttributes[] newArray(final int size) { + return new EpsBearerQosSessionAttributes[size]; + } + }; +} diff --git a/telephony/java/android/telephony/data/EpsQos.java b/telephony/java/android/telephony/data/EpsQos.java new file mode 100644 index 000000000000..22c8b0a0a74f --- /dev/null +++ b/telephony/java/android/telephony/data/EpsQos.java @@ -0,0 +1,109 @@ +/** + * Copyright 2020 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.data; + +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.Objects; + + +/** + * Class that stores information specific to NR QOS. + * + * @hide + */ +public final class EpsQos extends Qos implements Parcelable { + + int qosClassId; + + public EpsQos() { + super(Qos.QOS_TYPE_EPS, + new android.hardware.radio.V1_6.QosBandwidth(), + new android.hardware.radio.V1_6.QosBandwidth()); + } + + public EpsQos(@NonNull android.hardware.radio.V1_6.EpsQos qos) { + super(Qos.QOS_TYPE_EPS, qos.downlink, qos.uplink); + qosClassId = qos.qci; + } + + private EpsQos(Parcel source) { + super(source); + qosClassId = source.readInt(); + } + + public int getQci() { + return qosClassId; + } + + public static @NonNull EpsQos createFromParcelBody(@NonNull Parcel in) { + return new EpsQos(in); + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + super.writeToParcel(Qos.QOS_TYPE_EPS, dest, flags); + dest.writeInt(qosClassId); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), qosClassId); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + + if (o == null || !(o instanceof EpsQos)) { + return false; + } + + EpsQos other = (EpsQos) o; + + return this.qosClassId == other.qosClassId + && super.equals(other); + } + + @Override + public String toString() { + return "EpsQos {" + + " qosClassId=" + qosClassId + + " downlink=" + downlink + + " uplink=" + uplink + "}"; + } + + public static final @NonNull Parcelable.Creator<EpsQos> CREATOR = + new Parcelable.Creator<EpsQos>() { + @Override + public EpsQos createFromParcel(Parcel source) { + return new EpsQos(source); + } + + @Override + public EpsQos[] newArray(int size) { + return new EpsQos[size]; + } + }; +} diff --git a/telephony/java/android/telephony/data/IDataService.aidl b/telephony/java/android/telephony/data/IDataService.aidl index 9c74dcccec54..134694694a0e 100644 --- a/telephony/java/android/telephony/data/IDataService.aidl +++ b/telephony/java/android/telephony/data/IDataService.aidl @@ -19,6 +19,8 @@ package android.telephony.data; import android.net.LinkProperties; import android.telephony.data.DataProfile; import android.telephony.data.IDataServiceCallback; +import android.telephony.data.NetworkSliceInfo; +import android.telephony.data.TrafficDescriptor; /** * {@hide} @@ -29,6 +31,8 @@ oneway interface IDataService void removeDataServiceProvider(int slotId); void setupDataCall(int slotId, int accessNetwork, in DataProfile dataProfile, boolean isRoaming, boolean allowRoaming, int reason, in LinkProperties linkProperties, + int pduSessionId, in NetworkSliceInfo sliceInfo, + in TrafficDescriptor trafficDescriptor, boolean matchAllRuleAllowed, IDataServiceCallback callback); void deactivateDataCall(int slotId, int cid, int reason, IDataServiceCallback callback); void setInitialAttachApn(int slotId, in DataProfile dataProfile, boolean isRoaming, @@ -38,4 +42,8 @@ oneway interface IDataService void requestDataCallList(int slotId, IDataServiceCallback callback); void registerForDataCallListChanged(int slotId, IDataServiceCallback callback); void unregisterForDataCallListChanged(int slotId, IDataServiceCallback callback); + void startHandover(int slotId, int cid, IDataServiceCallback callback); + void cancelHandover(int slotId, int cid, IDataServiceCallback callback); + void registerForUnthrottleApn(int slotIndex, IDataServiceCallback callback); + void unregisterForUnthrottleApn(int slotIndex, IDataServiceCallback callback); } diff --git a/telephony/java/android/telephony/data/IDataServiceCallback.aidl b/telephony/java/android/telephony/data/IDataServiceCallback.aidl index cec757d23988..9cc2feac331a 100644 --- a/telephony/java/android/telephony/data/IDataServiceCallback.aidl +++ b/telephony/java/android/telephony/data/IDataServiceCallback.aidl @@ -30,4 +30,7 @@ oneway interface IDataServiceCallback void onSetDataProfileComplete(int result); void onRequestDataCallListComplete(int result, in List<DataCallResponse> dataCallList); void onDataCallListChanged(in List<DataCallResponse> dataCallList); + void onHandoverStarted(int result); + void onHandoverCancelled(int result); + void onApnUnthrottled(in String apn); } diff --git a/telephony/java/android/telephony/data/IQualifiedNetworksService.aidl b/telephony/java/android/telephony/data/IQualifiedNetworksService.aidl index 3bf09bc19788..ba2b62d14bec 100644 --- a/telephony/java/android/telephony/data/IQualifiedNetworksService.aidl +++ b/telephony/java/android/telephony/data/IQualifiedNetworksService.aidl @@ -17,6 +17,7 @@ package android.telephony.data; import android.telephony.data.IQualifiedNetworksServiceCallback; +import android.telephony.data.ThrottleStatus; /** * {@hide} @@ -25,4 +26,5 @@ interface IQualifiedNetworksService { oneway void createNetworkAvailabilityProvider(int slotId, IQualifiedNetworksServiceCallback callback); oneway void removeNetworkAvailabilityProvider(int slotId); + oneway void reportThrottleStatusChanged(int slotId, in List<ThrottleStatus> statuses); } diff --git a/telephony/java/android/telephony/data/NetworkSliceInfo.aidl b/telephony/java/android/telephony/data/NetworkSliceInfo.aidl new file mode 100644 index 000000000000..e1a11f2b87b8 --- /dev/null +++ b/telephony/java/android/telephony/data/NetworkSliceInfo.aidl @@ -0,0 +1,20 @@ +/* + * Copyright 2020 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. + */ + +/** @hide */ +package android.telephony.data; + +parcelable NetworkSliceInfo; diff --git a/telephony/java/android/telephony/data/NetworkSliceInfo.java b/telephony/java/android/telephony/data/NetworkSliceInfo.java new file mode 100644 index 000000000000..232a93012f4b --- /dev/null +++ b/telephony/java/android/telephony/data/NetworkSliceInfo.java @@ -0,0 +1,452 @@ +/* + * Copyright (C) 2020 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.data; + +import android.annotation.IntDef; +import android.annotation.IntRange; +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.Objects; + +/** + * Represents a S-NSSAI as defined in 3GPP TS 24.501, which represents a network slice. + * + * There are 2 main fields that define a slice, SliceServiceType and SliceDifferentiator. + * SliceServiceType defines the type of service provided by the slice, and SliceDifferentiator is + * used to differentiate between multiple slices of the same type. If the devices is not on HPLMN, + * the mappedHplmn versions of these 2 fields indicate the corresponding values in HPLMN. + */ +public final class NetworkSliceInfo implements Parcelable { + /** + * When set on a Slice Differentiator, this value indicates that there is no corresponding + * Slice. + */ + public static final int SLICE_DIFFERENTIATOR_NO_SLICE = -1; + + /** + * Indicates that the service type is not present. + */ + public static final int SLICE_SERVICE_TYPE_NONE = 0; + + /** + * Slice suitable for the handling of 5G enhanced Mobile Broadband. + */ + public static final int SLICE_SERVICE_TYPE_EMBB = 1; + + /** + * Slice suitable for the handling of ultra-reliable low latency communications. + */ + public static final int SLICE_SERVICE_TYPE_URLLC = 2; + + /** + * Slice suitable for the handling of massive IoT. + */ + public static final int SLICE_SERVICE_TYPE_MIOT = 3; + + /** + * The min acceptable value for a Slice Differentiator + * @hide + */ + public static final int MIN_SLICE_DIFFERENTIATOR = -1; + + /** + * The max acceptable value for a Slice Differentiator + * @hide + */ + public static final int MAX_SLICE_DIFFERENTIATOR = 0xFFFFFE; + + /** @hide */ + @IntDef(prefix = { "SLICE_SERVICE_TYPE_" }, value = { + SLICE_SERVICE_TYPE_NONE, + SLICE_SERVICE_TYPE_EMBB, + SLICE_SERVICE_TYPE_URLLC, + SLICE_SERVICE_TYPE_MIOT, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface SliceServiceType {} + + /** + * The slice status is unknown. This can happen during IWLAN->cellular handover when the + * NetworkSliceInfo is received over IWLAN. + */ + public static final int SLICE_STATUS_UNKNOWN = 0; + + /** + * The slice is configured but not allowed or rejected yet. + */ + public static final int SLICE_STATUS_CONFIGURED = 1; + + /** + * The slice is allowed to be used. + */ + public static final int SLICE_STATUS_ALLOWED = 2; + + /** + * The slice is rejected because not available in PLMN. + */ + public static final int SLICE_STATUS_REJECTED_NOT_AVAILABLE_IN_PLMN = 3; + + /** + * The slice is rejected because not available in registered area. + */ + public static final int SLICE_STATUS_REJECTED_NOT_AVAILABLE_IN_REGISTERED_AREA = 4; + + /** + * The slice is configured by home operator(HPLMN) in default and is used if configured/allowed + * slices are not available for the serving PLMN. + */ + public static final int SLICE_STATUS_DEFAULT_CONFIGURED = 5; + + /** + * The min acceptable value for a slice status. + * @hide + */ + public static final int MIN_SLICE_STATUS = SLICE_STATUS_UNKNOWN; + + /** + * The max acceptable value for a slice status. + * @hide + */ + public static final int MAX_SLICE_STATUS = SLICE_STATUS_DEFAULT_CONFIGURED; + + /** @hide */ + @IntDef(prefix = { "SLICE_STATUS_" }, value = { + SLICE_STATUS_UNKNOWN, + SLICE_STATUS_CONFIGURED, + SLICE_STATUS_ALLOWED, + SLICE_STATUS_REJECTED_NOT_AVAILABLE_IN_PLMN, + SLICE_STATUS_REJECTED_NOT_AVAILABLE_IN_REGISTERED_AREA, + SLICE_STATUS_DEFAULT_CONFIGURED, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface SliceStatus {} + + + @SliceServiceType + private final int mSliceServiceType; + @IntRange(from = MIN_SLICE_DIFFERENTIATOR, to = MAX_SLICE_DIFFERENTIATOR) + private final int mSliceDifferentiator; + @SliceServiceType + private final int mMappedHplmnSliceServiceType; + @IntRange(from = MIN_SLICE_DIFFERENTIATOR, to = MAX_SLICE_DIFFERENTIATOR) + private final int mMappedHplmnSliceDifferentiator; + @SliceStatus + @IntRange(from = MIN_SLICE_STATUS, to = MAX_SLICE_STATUS) + private final int mStatus; + + private NetworkSliceInfo(@SliceServiceType int sliceServiceType, + int sliceDifferentiator, int mappedHplmnSliceServiceType, + int mappedHplmnSliceDifferentiator, int status) { + mSliceServiceType = sliceServiceType; + mSliceDifferentiator = sliceDifferentiator; + mMappedHplmnSliceDifferentiator = mappedHplmnSliceDifferentiator; + mMappedHplmnSliceServiceType = mappedHplmnSliceServiceType; + mStatus = status; + } + + /** + * The type of service provided by the slice. + * <p/> + * see: 3GPP TS 24.501 Section 9.11.2.8. + */ + @SliceServiceType + public int getSliceServiceType() { + return mSliceServiceType; + } + + /** + * Identifies the slice from others with the same Slice Service Type. + * <p/> + * Returns {@link #SLICE_DIFFERENTIATOR_NO_SLICE} if {@link #getSliceServiceType} returns + * {@link #SLICE_SERVICE_TYPE_NONE}. + * <p/> + * see: 3GPP TS 24.501 Section 9.11.2.8. + */ + @IntRange(from = MIN_SLICE_DIFFERENTIATOR, to = MAX_SLICE_DIFFERENTIATOR) + public int getSliceDifferentiator() { + return mSliceDifferentiator; + } + + /** + * Corresponds to a Slice Info (S-NSSAI) of the HPLMN. + * <p/> + * see: 3GPP TS 24.501 Section 9.11.2.8. + */ + @SliceServiceType + public int getMappedHplmnSliceServiceType() { + return mMappedHplmnSliceServiceType; + } + + /** + * This Slice Differentiator corresponds to a {@link NetworkSliceInfo} (S-NSSAI) of the HPLMN; + * {@link #getSliceDifferentiator()} is mapped to this value. + * <p/> + * Returns {@link #SLICE_DIFFERENTIATOR_NO_SLICE} if either of the following are true: + * <ul> + * <li>{@link #getSliceDifferentiator()} returns {@link #SLICE_DIFFERENTIATOR_NO_SLICE}</li> + * <li>{@link #getMappedHplmnSliceServiceType()} returns {@link #SLICE_SERVICE_TYPE_NONE}</li> + * </ul> + * <p/> + * see: 3GPP TS 24.501 Section 9.11.2.8. + */ + @IntRange(from = MIN_SLICE_DIFFERENTIATOR, to = MAX_SLICE_DIFFERENTIATOR) + public int getMappedHplmnSliceDifferentiator() { + return mMappedHplmnSliceDifferentiator; + } + + /** + * Field to indicate the current status of the slice. + * @return the current status for this slice info. + */ + @SliceStatus + public int getStatus() { + return mStatus; + } + + private NetworkSliceInfo(@NonNull Parcel in) { + mSliceServiceType = in.readInt(); + mSliceDifferentiator = in.readInt(); + mMappedHplmnSliceServiceType = in.readInt(); + mMappedHplmnSliceDifferentiator = in.readInt(); + mStatus = in.readInt(); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mSliceServiceType); + dest.writeInt(mSliceDifferentiator); + dest.writeInt(mMappedHplmnSliceServiceType); + dest.writeInt(mMappedHplmnSliceDifferentiator); + dest.writeInt(mStatus); + } + + public static final @android.annotation.NonNull Parcelable.Creator<NetworkSliceInfo> CREATOR = + new Parcelable.Creator<NetworkSliceInfo>() { + @Override + @NonNull + public NetworkSliceInfo createFromParcel(@NonNull Parcel source) { + return new NetworkSliceInfo(source); + } + + @Override + @NonNull + public NetworkSliceInfo[] newArray(int size) { + return new NetworkSliceInfo[size]; + } + }; + + @Override + public String toString() { + return "SliceInfo{" + + "mSliceServiceType=" + sliceServiceTypeToString(mSliceServiceType) + + ", mSliceDifferentiator=" + mSliceDifferentiator + + ", mMappedHplmnSliceServiceType=" + + sliceServiceTypeToString(mMappedHplmnSliceServiceType) + + ", mMappedHplmnSliceDifferentiator=" + mMappedHplmnSliceDifferentiator + + ", mStatus=" + sliceStatusToString(mStatus) + + '}'; + } + + private static String sliceServiceTypeToString(@SliceServiceType int sliceServiceType) { + switch(sliceServiceType) { + case SLICE_SERVICE_TYPE_NONE: + return "NONE"; + case SLICE_SERVICE_TYPE_EMBB: + return "EMBB"; + case SLICE_SERVICE_TYPE_URLLC: + return "URLLC"; + case SLICE_SERVICE_TYPE_MIOT: + return "MIOT"; + default: + return Integer.toString(sliceServiceType); + } + } + + private static String sliceStatusToString(@SliceStatus int sliceStatus) { + switch(sliceStatus) { + case SLICE_STATUS_UNKNOWN: + return "UNKNOWN"; + case SLICE_STATUS_CONFIGURED: + return "CONFIGURED"; + case SLICE_STATUS_ALLOWED: + return "ALLOWED"; + case SLICE_STATUS_REJECTED_NOT_AVAILABLE_IN_PLMN: + return "REJECTED_NOT_AVAILABLE_IN_PLMN"; + case SLICE_STATUS_REJECTED_NOT_AVAILABLE_IN_REGISTERED_AREA: + return "REJECTED_NOT_AVAILABLE_IN_REGISTERED_AREA"; + case SLICE_STATUS_DEFAULT_CONFIGURED: + return "DEFAULT_CONFIGURED"; + default: + return Integer.toString(sliceStatus); + } + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + NetworkSliceInfo sliceInfo = (NetworkSliceInfo) o; + return mSliceServiceType == sliceInfo.mSliceServiceType + && mSliceDifferentiator == sliceInfo.mSliceDifferentiator + && mMappedHplmnSliceServiceType == sliceInfo.mMappedHplmnSliceServiceType + && mMappedHplmnSliceDifferentiator == sliceInfo.mMappedHplmnSliceDifferentiator + && mStatus == sliceInfo.mStatus; + } + + @Override + public int hashCode() { + return Objects.hash(mSliceServiceType, mSliceDifferentiator, mMappedHplmnSliceServiceType, + mMappedHplmnSliceDifferentiator, mStatus); + } + + /** + * Provides a convenient way to set the fields of a {@link NetworkSliceInfo} when creating a + * new instance. + * + * <p>The example below shows how you might create a new {@code SliceInfo}: + * + * <pre><code> + * + * SliceInfo response = new SliceInfo.Builder() + * .setSliceServiceType(SLICE_SERVICE_TYPE_URLLC) + * .build(); + * </code></pre> + */ + public static final class Builder { + @SliceServiceType + private int mSliceServiceType = SLICE_SERVICE_TYPE_NONE; + @IntRange(from = MIN_SLICE_DIFFERENTIATOR, to = MAX_SLICE_DIFFERENTIATOR) + private int mSliceDifferentiator = SLICE_DIFFERENTIATOR_NO_SLICE; + @SliceServiceType + private int mMappedHplmnSliceServiceType = SLICE_SERVICE_TYPE_NONE; + @IntRange(from = MIN_SLICE_DIFFERENTIATOR, to = MAX_SLICE_DIFFERENTIATOR) + private int mMappedHplmnSliceDifferentiator = SLICE_DIFFERENTIATOR_NO_SLICE; + @SliceStatus + @IntRange(from = MIN_SLICE_STATUS, to = MAX_SLICE_STATUS) + private int mStatus = SLICE_STATUS_UNKNOWN; + + /** + * Default constructor for Builder. + */ + public Builder() { + } + + /** + * Set the Slice Service Type. + * + * @return The same instance of the builder. + */ + @NonNull + public Builder setSliceServiceType(@SliceServiceType int mSliceServiceType) { + this.mSliceServiceType = mSliceServiceType; + return this; + } + + /** + * Set the Slice Differentiator. + * <p/> + * A value of {@link #SLICE_DIFFERENTIATOR_NO_SLICE} indicates that there is no + * corresponding Slice. + * + * @throws IllegalArgumentException if the parameter is not in the expected range. + * + * @return The same instance of the builder. + */ + @NonNull + public Builder setSliceDifferentiator( + @IntRange(from = MIN_SLICE_DIFFERENTIATOR, to = MAX_SLICE_DIFFERENTIATOR) + int sliceDifferentiator) { + if (sliceDifferentiator < MIN_SLICE_DIFFERENTIATOR + || sliceDifferentiator > MAX_SLICE_DIFFERENTIATOR) { + throw new IllegalArgumentException("The slice diffentiator value is out of range"); + } + this.mSliceDifferentiator = sliceDifferentiator; + return this; + } + + /** + * Set the HPLMN Slice Service Type. + * + * @return The same instance of the builder. + */ + @NonNull + public Builder setMappedHplmnSliceServiceType( + @SliceServiceType int mappedHplmnSliceServiceType) { + this.mMappedHplmnSliceServiceType = mappedHplmnSliceServiceType; + return this; + } + + /** + * Set the HPLMN Slice Differentiator. + * <p/> + * A value of {@link #SLICE_DIFFERENTIATOR_NO_SLICE} indicates that there is no + * corresponding Slice of the HPLMN. + * + * @throws IllegalArgumentException if the parameter is not in the expected range. + * + * @return The same instance of the builder. + */ + @NonNull + public Builder setMappedHplmnSliceDifferentiator( + @IntRange(from = MIN_SLICE_DIFFERENTIATOR, to = MAX_SLICE_DIFFERENTIATOR) + int mappedHplmnSliceDifferentiator) { + if (mappedHplmnSliceDifferentiator < MIN_SLICE_DIFFERENTIATOR + || mappedHplmnSliceDifferentiator > MAX_SLICE_DIFFERENTIATOR) { + throw new IllegalArgumentException("The slice diffentiator value is out of range"); + } + this.mMappedHplmnSliceDifferentiator = mappedHplmnSliceDifferentiator; + return this; + } + + /** + * Set the slice status. + * + * @throws IllegalArgumentException if the status is invalid. + * + * @return The same instance of the builder. + */ + @NonNull + public Builder setStatus(@SliceStatus int status) { + if (status < MIN_SLICE_STATUS || status > MAX_SLICE_STATUS) { + throw new IllegalArgumentException("The slice status is not valid"); + } + this.mStatus = status; + return this; + } + + /** + * Build the {@link NetworkSliceInfo}. + * + * @return the {@link NetworkSliceInfo} object. + */ + @NonNull + public NetworkSliceInfo build() { + return new NetworkSliceInfo(this.mSliceServiceType, this.mSliceDifferentiator, + this.mMappedHplmnSliceServiceType, this.mMappedHplmnSliceDifferentiator, + this.mStatus); + } + } +} diff --git a/telephony/java/android/telephony/data/NetworkSlicingConfig.aidl b/telephony/java/android/telephony/data/NetworkSlicingConfig.aidl new file mode 100644 index 000000000000..cd4a8f1e45be --- /dev/null +++ b/telephony/java/android/telephony/data/NetworkSlicingConfig.aidl @@ -0,0 +1,19 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.data; + +parcelable NetworkSlicingConfig; diff --git a/telephony/java/android/telephony/data/NetworkSlicingConfig.java b/telephony/java/android/telephony/data/NetworkSlicingConfig.java new file mode 100644 index 000000000000..dec787f40ff2 --- /dev/null +++ b/telephony/java/android/telephony/data/NetworkSlicingConfig.java @@ -0,0 +1,137 @@ +/** + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.data; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * Represents a slicing configuration + */ +public final class NetworkSlicingConfig implements Parcelable { + private final List<UrspRule> mUrspRules; + private final List<NetworkSliceInfo> mSliceInfo; + + public NetworkSlicingConfig() { + mUrspRules = new ArrayList<UrspRule>(); + mSliceInfo = new ArrayList<NetworkSliceInfo>(); + } + + /** @hide */ + public NetworkSlicingConfig(android.hardware.radio.V1_6.SlicingConfig sc) { + this(sc.urspRules, sc.sliceInfo); + } + + /** @hide */ + public NetworkSlicingConfig(List<android.hardware.radio.V1_6.UrspRule> urspRules, + List<android.hardware.radio.V1_6.SliceInfo> sliceInfo) { + mUrspRules = new ArrayList<UrspRule>(); + for (android.hardware.radio.V1_6.UrspRule ur : urspRules) { + mUrspRules.add(new UrspRule(ur.precedence, ur.trafficDescriptors, + ur.routeSelectionDescriptor)); + } + mSliceInfo = new ArrayList<NetworkSliceInfo>(); + for (android.hardware.radio.V1_6.SliceInfo si : sliceInfo) { + mSliceInfo.add(sliceInfoBuilder(si)); + } + } + + private NetworkSliceInfo sliceInfoBuilder(android.hardware.radio.V1_6.SliceInfo si) { + NetworkSliceInfo.Builder builder = new NetworkSliceInfo.Builder() + .setSliceServiceType(si.sst) + .setMappedHplmnSliceServiceType(si.mappedHplmnSst); + if (si.sliceDifferentiator != NetworkSliceInfo.SLICE_DIFFERENTIATOR_NO_SLICE) { + builder + .setSliceDifferentiator(si.sliceDifferentiator) + .setMappedHplmnSliceDifferentiator(si.mappedHplmnSD); + } + return builder.build(); + } + + /** @hide */ + public NetworkSlicingConfig(Parcel p) { + mUrspRules = p.createTypedArrayList(UrspRule.CREATOR); + mSliceInfo = p.createTypedArrayList(NetworkSliceInfo.CREATOR); + } + + /** + * This list contains the current URSP rules. Empty list represents that no rules are + * configured. + * @return the current URSP rules for this slicing configuration. + */ + public @NonNull List<UrspRule> getUrspRules() { + return mUrspRules; + } + + /** + * @return the list of all slices for this slicing configuration. + */ + public @NonNull List<NetworkSliceInfo> getSliceInfo() { + return mSliceInfo; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeTypedList(mUrspRules, flags); + dest.writeTypedList(mSliceInfo, flags); + } + + public static final @NonNull Parcelable.Creator<NetworkSlicingConfig> CREATOR = + new Parcelable.Creator<NetworkSlicingConfig>() { + @Override + public NetworkSlicingConfig createFromParcel(Parcel source) { + return new NetworkSlicingConfig(source); + } + + @Override + public NetworkSlicingConfig[] newArray(int size) { + return new NetworkSlicingConfig[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public boolean equals(@Nullable Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + NetworkSlicingConfig that = (NetworkSlicingConfig) o; + return mUrspRules.size() == that.mUrspRules.size() + && mUrspRules.containsAll(that.mUrspRules) + && mSliceInfo.size() == that.mSliceInfo.size() + && mSliceInfo.containsAll(that.mSliceInfo); + } + + @Override + public int hashCode() { + return Objects.hash(mUrspRules, mSliceInfo); + } + + @Override + public String toString() { + return "{.urspRules = " + mUrspRules + ", .sliceInfo = " + mSliceInfo + "}"; + } +} diff --git a/telephony/java/android/telephony/data/NrQos.java b/telephony/java/android/telephony/data/NrQos.java new file mode 100644 index 000000000000..fe124ac15393 --- /dev/null +++ b/telephony/java/android/telephony/data/NrQos.java @@ -0,0 +1,124 @@ +/** + * Copyright 2020 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.data; + +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.Objects; + +/** + * Class that stores information specific to NR QOS. + * + * @hide + */ +public final class NrQos extends Qos implements Parcelable { + int qosFlowId; + int fiveQi; + int averagingWindowMs; + + public NrQos(@NonNull android.hardware.radio.V1_6.NrQos qos) { + super(Qos.QOS_TYPE_NR, qos.downlink, qos.uplink); + fiveQi = qos.fiveQi; + qosFlowId = qos.qfi; + averagingWindowMs = qos.averagingWindowMs; + } + + private NrQos(Parcel source) { + super(source); + this.qosFlowId = source.readInt(); + this.fiveQi = source.readInt(); + this.averagingWindowMs = source.readInt(); + } + + public static @NonNull NrQos createFromParcelBody(@NonNull Parcel in) { + return new NrQos(in); + } + + public int get5Qi() { + return fiveQi; + } + + public int getQfi() { + return qosFlowId; + } + + public int getAveragingWindow() { + return averagingWindowMs; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + super.writeToParcel(Qos.QOS_TYPE_NR, dest, flags); + dest.writeInt(qosFlowId); + dest.writeInt(fiveQi); + dest.writeInt(averagingWindowMs); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), qosFlowId, fiveQi, averagingWindowMs); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + + if (o == null || !(o instanceof NrQos)) { + return false; + } + + NrQos other = (NrQos) o; + + if (!super.equals(other)) { + return false; + } + + return this.qosFlowId == other.qosFlowId + && this.fiveQi == other.fiveQi + && this.averagingWindowMs == other.averagingWindowMs; + } + + @Override + public String toString() { + return "NrQos {" + + " fiveQi=" + fiveQi + + " downlink=" + downlink + + " uplink=" + uplink + + " qosFlowId=" + qosFlowId + + " averagingWindowMs=" + averagingWindowMs + "}"; + } + + public static final @NonNull Parcelable.Creator<NrQos> CREATOR = + new Parcelable.Creator<NrQos>() { + @Override + public NrQos createFromParcel(Parcel source) { + return new NrQos(source); + } + + @Override + public NrQos[] newArray(int size) { + return new NrQos[size]; + } + }; +} diff --git a/telephony/java/android/telephony/data/NrQosSessionAttributes.aidl b/telephony/java/android/telephony/data/NrQosSessionAttributes.aidl new file mode 100644 index 000000000000..fd3bbb0865cb --- /dev/null +++ b/telephony/java/android/telephony/data/NrQosSessionAttributes.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + package android.telephony.data; + + parcelable NrQosSessionAttributes; diff --git a/telephony/java/android/telephony/data/NrQosSessionAttributes.java b/telephony/java/android/telephony/data/NrQosSessionAttributes.java new file mode 100644 index 000000000000..4c37687910a1 --- /dev/null +++ b/telephony/java/android/telephony/data/NrQosSessionAttributes.java @@ -0,0 +1,259 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.data; + +import android.annotation.IntRange; +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.net.QosSessionAttributes; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.Log; + +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.UnknownHostException; +import java.time.Duration; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +/** + * Provides Qos attributes of an NR bearer. + * + * {@hide} + */ +@SystemApi +public final class NrQosSessionAttributes implements Parcelable, QosSessionAttributes { + private static final String TAG = NrQosSessionAttributes.class.getSimpleName(); + private final int m5Qi; + private final @IntRange(from=1, to=63) int mQfi; + private final long mMaxUplinkBitRate; + private final long mMaxDownlinkBitRate; + private final long mGuaranteedUplinkBitRate; + private final long mGuaranteedDownlinkBitRate; + private final long mAveragingWindow; + @NonNull private final List<InetSocketAddress> mRemoteAddresses; + + /** + * 5G QOS Identifier (5QI), see 3GPP TS 24.501 and 23.501. + * The allowed values are standard values(1-9, 65-68, 69-70, 75, 79-80, 82-85) + * defined in the spec and operator specific values in the range 128-254. + * + * @return the 5QI of the QOS flow + */ + public int getQosIdentifier() { + return m5Qi; + } + + /** + * QOS flow identifier of the QOS flow description in the + * range of 1 to 63. see 3GPP TS 24.501 and 23.501. + * + * @return the QOS flow identifier of the session + */ + public @IntRange(from=1, to=63) int getQosFlowIdentifier() { + return mQfi; + } + + /** + * Minimum bit rate in kbps that is guaranteed to be provided by the network on the uplink. + * + * see 3GPP TS 24.501 section 6.2.5 + * + * Note: The Qos Session may be shared with OTHER applications besides yours. + * + * @return the guaranteed bit rate in kbps + */ + public long getGuaranteedUplinkBitRateKbps() { + return mGuaranteedUplinkBitRate; + } + + /** + * Minimum bit rate in kbps that is guaranteed to be provided by the network on the downlink. + * + * see 3GPP TS 24.501 section 6.2.5 + * + * Note: The Qos Session may be shared with OTHER applications besides yours. + * + * @return the guaranteed bit rate in kbps + */ + public long getGuaranteedDownlinkBitRateKbps() { + return mGuaranteedDownlinkBitRate; + } + + /** + * The maximum uplink kbps that the network will accept. + * + * see 3GPP TS 24.501 section 6.2.5 + * + * Note: The Qos Session may be shared with OTHER applications besides yours. + * + * @return the max uplink bit rate in kbps + */ + public long getMaxUplinkBitRateKbps() { + return mMaxUplinkBitRate; + } + + /** + * The maximum downlink kbps that the network can provide. + * + * see 3GPP TS 24.501 section 6.2.5 + * + * Note: The Qos Session may be shared with OTHER applications besides yours. + * + * @return the max downlink bit rate in kbps + */ + public long getMaxDownlinkBitRateKbps() { + return mMaxDownlinkBitRate; + } + + /** + * The duration in milliseconds over which the maximum bit rates and guaranteed bit rates + * are calculated + * + * see 3GPP TS 24.501 section 6.2.5 + * + * @return the averaging window duration in milliseconds + */ + @NonNull + public Duration getBitRateWindowDuration() { + return Duration.ofMillis(mAveragingWindow); + } + + /** + * List of remote addresses associated with the Qos Session. The given uplink bit rates apply + * to this given list of remote addresses. + * + * Note: In the event that the list is empty, it is assumed that the uplink bit rates apply to + * all remote addresses that are not contained in a different set of attributes. + * + * @return list of remote socket addresses that the attributes apply to + */ + @NonNull + public List<InetSocketAddress> getRemoteAddresses() { + return mRemoteAddresses; + } + + /** + * ..ctor for attributes + * + * @param fiveQi 5G quality class indicator + * @param qfi QOS flow identifier + * @param maxDownlinkBitRate the max downlink bit rate in kbps + * @param maxUplinkBitRate the max uplink bit rate in kbps + * @param guaranteedDownlinkBitRate the guaranteed downlink bit rate in kbps + * @param guaranteedUplinkBitRate the guaranteed uplink bit rate in kbps + * @param averagingWindow the averaging window duration in milliseconds + * @param remoteAddresses the remote addresses that the uplink bit rates apply to + * + * @hide + */ + public NrQosSessionAttributes(final int fiveQi, final int qfi, + final long maxDownlinkBitRate, final long maxUplinkBitRate, + final long guaranteedDownlinkBitRate, final long guaranteedUplinkBitRate, + final long averagingWindow, @NonNull final List<InetSocketAddress> remoteAddresses) { + Objects.requireNonNull(remoteAddresses, "remoteAddress must be non-null"); + m5Qi = fiveQi; + mQfi = qfi; + mMaxDownlinkBitRate = maxDownlinkBitRate; + mMaxUplinkBitRate = maxUplinkBitRate; + mGuaranteedDownlinkBitRate = guaranteedDownlinkBitRate; + mGuaranteedUplinkBitRate = guaranteedUplinkBitRate; + mAveragingWindow = averagingWindow; + + final List<InetSocketAddress> remoteAddressesTemp = copySocketAddresses(remoteAddresses); + mRemoteAddresses = Collections.unmodifiableList(remoteAddressesTemp); + } + + private static List<InetSocketAddress> copySocketAddresses( + @NonNull final List<InetSocketAddress> remoteAddresses) { + final List<InetSocketAddress> remoteAddressesTemp = new ArrayList<>(); + for (final InetSocketAddress socketAddress : remoteAddresses) { + if (socketAddress != null && socketAddress.getAddress() != null) { + remoteAddressesTemp.add(socketAddress); + } + } + return remoteAddressesTemp; + } + + private NrQosSessionAttributes(@NonNull final Parcel in) { + m5Qi = in.readInt(); + mQfi = in.readInt(); + mMaxDownlinkBitRate = in.readLong(); + mMaxUplinkBitRate = in.readLong(); + mGuaranteedDownlinkBitRate = in.readLong(); + mGuaranteedUplinkBitRate = in.readLong(); + mAveragingWindow = in.readLong(); + + final int size = in.readInt(); + final List<InetSocketAddress> remoteAddresses = new ArrayList<>(size); + for (int i = 0; i < size; i++) { + final byte[] addressBytes = in.createByteArray(); + final int port = in.readInt(); + try { + remoteAddresses.add( + new InetSocketAddress(InetAddress.getByAddress(addressBytes), port)); + } catch (final UnknownHostException e) { + // Impossible case since its filtered out the null values in the ..ctor + Log.e(TAG, "unable to unparcel remote address at index: " + i, e); + } + } + mRemoteAddresses = Collections.unmodifiableList(remoteAddresses); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull final Parcel dest, final int flags) { + dest.writeInt(m5Qi); + dest.writeInt(mQfi); + dest.writeLong(mMaxDownlinkBitRate); + dest.writeLong(mMaxUplinkBitRate); + dest.writeLong(mGuaranteedDownlinkBitRate); + dest.writeLong(mGuaranteedUplinkBitRate); + dest.writeLong(mAveragingWindow); + + final int size = mRemoteAddresses.size(); + dest.writeInt(size); + for (int i = 0; i < size; i++) { + final InetSocketAddress address = mRemoteAddresses.get(i); + dest.writeByteArray(address.getAddress().getAddress()); + dest.writeInt(address.getPort()); + } + } + + @NonNull + public static final Creator<NrQosSessionAttributes> CREATOR = + new Creator<NrQosSessionAttributes>() { + @NonNull + @Override + public NrQosSessionAttributes createFromParcel(@NonNull final Parcel in) { + return new NrQosSessionAttributes(in); + } + + @NonNull + @Override + public NrQosSessionAttributes[] newArray(final int size) { + return new NrQosSessionAttributes[size]; + } + }; +} diff --git a/telephony/java/android/telephony/data/OWNERS b/telephony/java/android/telephony/data/OWNERS new file mode 100644 index 000000000000..932b35cbdff7 --- /dev/null +++ b/telephony/java/android/telephony/data/OWNERS @@ -0,0 +1,5 @@ +# Bug component: 20868 + +rgreenwalt@google.com +tgunn@google.com +jackyu@google.com diff --git a/telephony/java/android/telephony/data/Qos.java b/telephony/java/android/telephony/data/Qos.java new file mode 100644 index 000000000000..c286c6217450 --- /dev/null +++ b/telephony/java/android/telephony/data/Qos.java @@ -0,0 +1,191 @@ +/** + * Copyright 2020 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.data; + +import android.annotation.CallSuper; +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.Objects; + +/** + * Class that stores information specific to QOS. + * + * @hide + */ +public abstract class Qos { + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = "QOS_TYPE_", + value = {QOS_TYPE_EPS, QOS_TYPE_NR}) + public @interface QosType {} + + @QosType + final int type; + + static final int QOS_TYPE_EPS = 1; + static final int QOS_TYPE_NR = 2; + + final QosBandwidth downlink; + final QosBandwidth uplink; + + Qos(int type, + @NonNull android.hardware.radio.V1_6.QosBandwidth downlink, + @NonNull android.hardware.radio.V1_6.QosBandwidth uplink) { + this.type = type; + this.downlink = new QosBandwidth(downlink.maxBitrateKbps, downlink.guaranteedBitrateKbps); + this.uplink = new QosBandwidth(uplink.maxBitrateKbps, uplink.guaranteedBitrateKbps); + } + + public QosBandwidth getDownlinkBandwidth() { + return downlink; + } + + public QosBandwidth getUplinkBandwidth() { + return uplink; + } + + public static class QosBandwidth implements Parcelable { + int maxBitrateKbps; + int guaranteedBitrateKbps; + + QosBandwidth() { + } + + QosBandwidth(int maxBitrateKbps, int guaranteedBitrateKbps) { + this.maxBitrateKbps = maxBitrateKbps; + this.guaranteedBitrateKbps = guaranteedBitrateKbps; + } + + private QosBandwidth(Parcel source) { + maxBitrateKbps = source.readInt(); + guaranteedBitrateKbps = source.readInt(); + } + + public int getMaxBitrateKbps() { + return maxBitrateKbps; + } + + public int getGuaranteedBitrateKbps() { + return guaranteedBitrateKbps; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(maxBitrateKbps); + dest.writeInt(guaranteedBitrateKbps); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public int hashCode() { + return Objects.hash(maxBitrateKbps, guaranteedBitrateKbps); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + + if (o == null || !(o instanceof QosBandwidth)) { + return false; + } + + QosBandwidth other = (QosBandwidth) o; + return maxBitrateKbps == other.maxBitrateKbps + && guaranteedBitrateKbps == other.guaranteedBitrateKbps; + } + + @Override + public String toString() { + return "Bandwidth {" + + " maxBitrateKbps=" + maxBitrateKbps + + " guaranteedBitrateKbps=" + guaranteedBitrateKbps + "}"; + } + + public static final @NonNull Parcelable.Creator<QosBandwidth> CREATOR = + new Parcelable.Creator<QosBandwidth>() { + @Override + public QosBandwidth createFromParcel(Parcel source) { + return new QosBandwidth(source); + } + + @Override + public QosBandwidth[] newArray(int size) { + return new QosBandwidth[size]; + } + }; + }; + + protected Qos(@NonNull Parcel source) { + type = source.readInt(); + downlink = source.readParcelable(QosBandwidth.class.getClassLoader()); + uplink = source.readParcelable(QosBandwidth.class.getClassLoader()); + } + + /** + * Used by child classes for parceling. + * + * @hide + */ + @CallSuper + public void writeToParcel(@QosType int type, Parcel dest, int flags) { + dest.writeInt(type); + dest.writeParcelable(downlink, flags); + dest.writeParcelable(uplink, flags); + } + + /** @hide */ + public static @NonNull Qos create(@NonNull android.hardware.radio.V1_6.Qos qos) { + switch (qos.getDiscriminator()) { + case android.hardware.radio.V1_6.Qos.hidl_discriminator.eps: + return new EpsQos(qos.eps()); + case android.hardware.radio.V1_6.Qos.hidl_discriminator.nr: + return new NrQos(qos.nr()); + default: + return null; + } + } + + /** @hide */ + public @QosType int getType() { + return type; + } + + @Override + public int hashCode() { + return Objects.hash(downlink, uplink); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + + Qos other = (Qos) o; + return type == other.type + && downlink.equals(other.downlink) + && uplink.equals(other.uplink); + } +} diff --git a/telephony/java/android/telephony/data/QosBearerFilter.java b/telephony/java/android/telephony/data/QosBearerFilter.java new file mode 100644 index 000000000000..5642549d7313 --- /dev/null +++ b/telephony/java/android/telephony/data/QosBearerFilter.java @@ -0,0 +1,400 @@ +/** + * Copyright 2020 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.data; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.net.InetAddresses; +import android.net.LinkAddress; +import android.os.Parcel; +import android.os.Parcelable; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.net.InetAddress; +import java.net.Inet4Address; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * Class that stores QOS filter parameters as defined in + * 3gpp 24.008 10.5.6.12 and 3gpp 24.501 9.11.4.13. + * + * @hide + */ +public final class QosBearerFilter implements Parcelable { + + private List<LinkAddress> localAddresses; + private List<LinkAddress> remoteAddresses; + private PortRange localPort; + private PortRange remotePort; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = "QOS_PROTOCOL_", + value = {QOS_PROTOCOL_UNSPECIFIED, QOS_PROTOCOL_TCP, QOS_PROTOCOL_UDP, + QOS_PROTOCOL_ESP, QOS_PROTOCOL_AH}) + public @interface QosProtocol {} + + public static final int QOS_PROTOCOL_UNSPECIFIED = + android.hardware.radio.V1_6.QosProtocol.UNSPECIFIED; + public static final int QOS_PROTOCOL_TCP = android.hardware.radio.V1_6.QosProtocol.TCP; + public static final int QOS_PROTOCOL_UDP = android.hardware.radio.V1_6.QosProtocol.UDP; + public static final int QOS_PROTOCOL_ESP = android.hardware.radio.V1_6.QosProtocol.ESP; + public static final int QOS_PROTOCOL_AH = android.hardware.radio.V1_6.QosProtocol.AH; + + @QosProtocol + private int protocol; + + private int typeOfServiceMask; + + private long flowLabel; + + /** IPSec security parameter index */ + private long securityParameterIndex; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = "QOS_FILTER_DIRECTION_", + value = {QOS_FILTER_DIRECTION_DOWNLINK, QOS_FILTER_DIRECTION_UPLINK, + QOS_FILTER_DIRECTION_BIDIRECTIONAL}) + public @interface QosBearerFilterDirection {} + + public static final int QOS_FILTER_DIRECTION_DOWNLINK = + android.hardware.radio.V1_6.QosFilterDirection.DOWNLINK; + public static final int QOS_FILTER_DIRECTION_UPLINK = + android.hardware.radio.V1_6.QosFilterDirection.UPLINK; + public static final int QOS_FILTER_DIRECTION_BIDIRECTIONAL = + android.hardware.radio.V1_6.QosFilterDirection.BIDIRECTIONAL; + + @QosBearerFilterDirection + private int filterDirection; + + /** + * Specified the order in which the filter needs to be matched. + * A Lower numerical value has a higher precedence. + */ + private int precedence; + + QosBearerFilter() { + localAddresses = new ArrayList<>(); + remoteAddresses = new ArrayList<>(); + localPort = new PortRange(); + remotePort = new PortRange(); + protocol = QOS_PROTOCOL_UNSPECIFIED; + filterDirection = QOS_FILTER_DIRECTION_BIDIRECTIONAL; + } + + public QosBearerFilter(List<LinkAddress> localAddresses, List<LinkAddress> remoteAddresses, + PortRange localPort, PortRange remotePort, int protocol, int tos, + long flowLabel, long spi, int direction, int precedence) { + this.localAddresses = localAddresses; + this.remoteAddresses = remoteAddresses; + this.localPort = localPort; + this.remotePort = remotePort; + this.protocol = protocol; + this.typeOfServiceMask = tos; + this.flowLabel = flowLabel; + this.securityParameterIndex = spi; + this.filterDirection = direction; + this.precedence = precedence; + } + + public List<LinkAddress> getLocalAddresses() { + return localAddresses; + } + + public List<LinkAddress> getRemoteAddresses() { + return remoteAddresses; + } + + public PortRange getLocalPortRange() { + return localPort; + } + + public PortRange getRemotePortRange() { + return remotePort; + } + + public int getPrecedence() { + return precedence; + } + + /** @hide */ + public static @NonNull QosBearerFilter create( + @NonNull android.hardware.radio.V1_6.QosFilter qosFilter) { + QosBearerFilter ret = new QosBearerFilter(); + + String[] localAddresses = qosFilter.localAddresses.stream().toArray(String[]::new); + if (localAddresses != null) { + for (String address : localAddresses) { + ret.localAddresses.add(createLinkAddressFromString(address)); + } + } + + String[] remoteAddresses = qosFilter.remoteAddresses.stream().toArray(String[]::new); + if (remoteAddresses != null) { + for (String address : remoteAddresses) { + ret.remoteAddresses.add(createLinkAddressFromString(address)); + } + } + + if (qosFilter.localPort != null) { + if (qosFilter.localPort.getDiscriminator() + == android.hardware.radio.V1_6.MaybePort.hidl_discriminator.range) { + final android.hardware.radio.V1_6.PortRange portRange = qosFilter.localPort.range(); + ret.localPort.start = portRange.start; + ret.localPort.end = portRange.end; + } + } + + if (qosFilter.remotePort != null) { + if (qosFilter.remotePort.getDiscriminator() + == android.hardware.radio.V1_6.MaybePort.hidl_discriminator.range) { + final android.hardware.radio.V1_6.PortRange portRange + = qosFilter.remotePort.range(); + ret.remotePort.start = portRange.start; + ret.remotePort.end = portRange.end; + } + } + + ret.protocol = qosFilter.protocol; + + if (qosFilter.tos != null) { + if (qosFilter.tos.getDiscriminator() + == android.hardware.radio.V1_6.QosFilter.TypeOfService.hidl_discriminator.value) { + ret.typeOfServiceMask = qosFilter.tos.value(); + } + } + + if (qosFilter.flowLabel != null) { + if (qosFilter.flowLabel.getDiscriminator() + == android.hardware.radio.V1_6.QosFilter.Ipv6FlowLabel.hidl_discriminator.value) { + ret.flowLabel = qosFilter.flowLabel.value(); + } + } + + if (qosFilter.spi != null) { + if (qosFilter.spi.getDiscriminator() + == android.hardware.radio.V1_6.QosFilter.IpsecSpi.hidl_discriminator.value) { + ret.securityParameterIndex = qosFilter.spi.value(); + } + } + + ret.filterDirection = qosFilter.direction; + ret.precedence = qosFilter.precedence; + + return ret; + } + + public static class PortRange implements Parcelable { + int start; + int end; + + PortRange() { + start = -1; + end = -1; + } + + private PortRange(Parcel source) { + start = source.readInt(); + end = source.readInt(); + } + + public PortRange(int start, int end) { + this.start = start; + this.end = end; + } + + public int getStart() { + return start; + } + + public int getEnd() { + return end; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(start); + dest.writeInt(end); + } + + @Override + public int describeContents() { + return 0; + } + + public static final @NonNull Parcelable.Creator<PortRange> CREATOR = + new Parcelable.Creator<PortRange>() { + @Override + public PortRange createFromParcel(Parcel source) { + return new PortRange(source); + } + + @Override + public PortRange[] newArray(int size) { + return new PortRange[size]; + } + }; + + @Override + public String toString() { + return "PortRange {" + + " start=" + start + + " end=" + end + "}"; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + + if (o == null || !(o instanceof PortRange)) { + return false; + } + + PortRange other = (PortRange) o; + return start == other.start + && end == other.end; + } + + @Override + public int hashCode() { + return Objects.hash(start, end); + } + }; + + @Override + public String toString() { + return "QosBearerFilter {" + + " localAddresses=" + localAddresses + + " remoteAddresses=" + remoteAddresses + + " localPort=" + localPort + + " remotePort=" + remotePort + + " protocol=" + protocol + + " typeOfServiceMask=" + typeOfServiceMask + + " flowLabel=" + flowLabel + + " securityParameterIndex=" + securityParameterIndex + + " filterDirection=" + filterDirection + + " precedence=" + precedence + "}"; + } + + @Override + public int hashCode() { + return Objects.hash(localAddresses, remoteAddresses, localPort, + remotePort, protocol, typeOfServiceMask, flowLabel, + securityParameterIndex, filterDirection, precedence); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + + if (o == null || !(o instanceof QosBearerFilter)) { + return false; + } + + QosBearerFilter other = (QosBearerFilter) o; + + return localAddresses.size() == other.localAddresses.size() + && localAddresses.containsAll(other.localAddresses) + && remoteAddresses.size() == other.remoteAddresses.size() + && remoteAddresses.containsAll(other.remoteAddresses) + && localPort.equals(other.localPort) + && remotePort.equals(other.remotePort) + && protocol == other.protocol + && typeOfServiceMask == other.typeOfServiceMask + && flowLabel == other.flowLabel + && securityParameterIndex == other.securityParameterIndex + && filterDirection == other.filterDirection + && precedence == other.precedence; + } + + private static LinkAddress createLinkAddressFromString(String addressString) { + addressString = addressString.trim(); + InetAddress address = null; + int prefixLength = -1; + try { + String[] pieces = addressString.split("/", 2); + address = InetAddresses.parseNumericAddress(pieces[0]); + if (pieces.length == 1) { + prefixLength = (address instanceof Inet4Address) ? 32 : 128; + } else if (pieces.length == 2) { + prefixLength = Integer.parseInt(pieces[1]); + } + } catch (NullPointerException e) { // Null string. + } catch (ArrayIndexOutOfBoundsException e) { // No prefix length. + } catch (NumberFormatException e) { // Non-numeric prefix. + } catch (IllegalArgumentException e) { // Invalid IP address. + } + + if (address == null || prefixLength == -1) { + throw new IllegalArgumentException("Invalid link address " + addressString); + } + + return new LinkAddress(address, prefixLength, 0, 0, + LinkAddress.LIFETIME_UNKNOWN, LinkAddress.LIFETIME_UNKNOWN); + } + + private QosBearerFilter(Parcel source) { + localAddresses = new ArrayList<>(); + source.readList(localAddresses, LinkAddress.class.getClassLoader()); + remoteAddresses = new ArrayList<>(); + source.readList(remoteAddresses, LinkAddress.class.getClassLoader()); + localPort = source.readParcelable(PortRange.class.getClassLoader()); + remotePort = source.readParcelable(PortRange.class.getClassLoader()); + protocol = source.readInt(); + typeOfServiceMask = source.readInt(); + flowLabel = source.readLong(); + securityParameterIndex = source.readLong(); + filterDirection = source.readInt(); + precedence = source.readInt(); + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeList(localAddresses); + dest.writeList(remoteAddresses); + dest.writeParcelable(localPort, flags); + dest.writeParcelable(remotePort, flags); + dest.writeInt(protocol); + dest.writeInt(typeOfServiceMask); + dest.writeLong(flowLabel); + dest.writeLong(securityParameterIndex); + dest.writeInt(filterDirection); + dest.writeInt(precedence); + } + + @Override + public int describeContents() { + return 0; + } + + public static final @NonNull Parcelable.Creator<QosBearerFilter> CREATOR = + new Parcelable.Creator<QosBearerFilter>() { + @Override + public QosBearerFilter createFromParcel(Parcel source) { + return new QosBearerFilter(source); + } + + @Override + public QosBearerFilter[] newArray(int size) { + return new QosBearerFilter[size]; + } + }; +} diff --git a/telephony/java/android/telephony/data/QosBearerSession.java b/telephony/java/android/telephony/data/QosBearerSession.java new file mode 100644 index 000000000000..30effc9273d7 --- /dev/null +++ b/telephony/java/android/telephony/data/QosBearerSession.java @@ -0,0 +1,137 @@ +/** + * Copyright 2020 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.data; + +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + + +/** + * Class that stores information specific to QOS session. + * + * @hide + */ +public final class QosBearerSession implements Parcelable{ + + final int qosBearerSessionId; + final Qos qos; + final List<QosBearerFilter> qosBearerFilterList; + + public QosBearerSession(int qosBearerSessionId, @NonNull Qos qos, @NonNull List<QosBearerFilter> qosBearerFilterList) { + this.qosBearerSessionId = qosBearerSessionId; + this.qos = qos; + this.qosBearerFilterList = qosBearerFilterList; + } + + private QosBearerSession(Parcel source) { + qosBearerSessionId = source.readInt(); + qos = source.readParcelable(Qos.class.getClassLoader()); + qosBearerFilterList = new ArrayList<>(); + source.readList(qosBearerFilterList, QosBearerFilter.class.getClassLoader()); + } + + public int getQosBearerSessionId() { + return qosBearerSessionId; + } + + public Qos getQos() { + return qos; + } + + public List<QosBearerFilter> getQosBearerFilterList() { + return qosBearerFilterList; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(qosBearerSessionId); + if (qos.getType() == Qos.QOS_TYPE_EPS) { + dest.writeParcelable((EpsQos)qos, flags); + } else { + dest.writeParcelable((NrQos)qos, flags); + } + dest.writeList(qosBearerFilterList); + } + + public static @NonNull QosBearerSession create( + @NonNull android.hardware.radio.V1_6.QosSession qosSession) { + List<QosBearerFilter> qosBearerFilters = new ArrayList<>(); + + if (qosSession.qosFilters != null) { + for (android.hardware.radio.V1_6.QosFilter filter : qosSession.qosFilters) { + qosBearerFilters.add(QosBearerFilter.create(filter)); + } + } + + return new QosBearerSession( + qosSession.qosSessionId, + Qos.create(qosSession.qos), + qosBearerFilters); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public String toString() { + return "QosBearerSession {" + + " qosBearerSessionId=" + qosBearerSessionId + + " qos=" + qos + + " qosBearerFilterList=" + qosBearerFilterList + "}"; + } + + @Override + public int hashCode() { + return Objects.hash(qosBearerSessionId, qos, qosBearerFilterList); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + + if (o == null || !(o instanceof QosBearerSession)) { + return false; + } + + QosBearerSession other = (QosBearerSession) o; + return this.qosBearerSessionId == other.qosBearerSessionId + && this.qos.equals(other.qos) + && this.qosBearerFilterList.size() == other.qosBearerFilterList.size() + && this.qosBearerFilterList.containsAll(other.qosBearerFilterList); + } + + + public static final @NonNull Parcelable.Creator<QosBearerSession> CREATOR = + new Parcelable.Creator<QosBearerSession>() { + @Override + public QosBearerSession createFromParcel(Parcel source) { + return new QosBearerSession(source); + } + + @Override + public QosBearerSession[] newArray(int size) { + return new QosBearerSession[size]; + } + }; +} diff --git a/telephony/java/android/telephony/data/QualifiedNetworksService.java b/telephony/java/android/telephony/data/QualifiedNetworksService.java index 05971c4d2e70..4e85d8926f11 100644 --- a/telephony/java/android/telephony/data/QualifiedNetworksService.java +++ b/telephony/java/android/telephony/data/QualifiedNetworksService.java @@ -28,6 +28,7 @@ import android.os.Message; import android.os.RemoteException; import android.telephony.AccessNetworkConstants.AccessNetworkType; import android.telephony.Annotation.ApnType; +import android.util.Log; import android.util.SparseArray; import com.android.internal.annotations.VisibleForTesting; @@ -65,6 +66,7 @@ public abstract class QualifiedNetworksService extends Service { private static final int QNS_REMOVE_NETWORK_AVAILABILITY_PROVIDER = 2; private static final int QNS_REMOVE_ALL_NETWORK_AVAILABILITY_PROVIDERS = 3; private static final int QNS_UPDATE_QUALIFIED_NETWORKS = 4; + private static final int QNS_APN_THROTTLE_STATUS_CHANGED = 5; private final HandlerThread mHandlerThread; @@ -160,6 +162,17 @@ public abstract class QualifiedNetworksService extends Service { } /** + * The framework calls this method when the throttle status of an APN changes. + * + * This method is meant to be overridden. + * + * @param statuses the statuses that have changed + */ + public void reportThrottleStatusChanged(@NonNull List<ThrottleStatus> statuses) { + Log.d(TAG, "reportThrottleStatusChanged: statuses size=" + statuses.size()); + } + + /** * Called when the qualified networks provider is removed. The extended class should * implement this method to perform cleanup works. */ @@ -197,6 +210,12 @@ public abstract class QualifiedNetworksService extends Service { + slotIndex); } break; + case QNS_APN_THROTTLE_STATUS_CHANGED: + if (provider != null) { + List<ThrottleStatus> statuses = (List<ThrottleStatus>) message.obj; + provider.reportThrottleStatusChanged(statuses); + } + break; case QNS_REMOVE_NETWORK_AVAILABILITY_PROVIDER: if (provider != null) { @@ -286,6 +305,13 @@ public abstract class QualifiedNetworksService extends Service { mHandler.obtainMessage(QNS_REMOVE_NETWORK_AVAILABILITY_PROVIDER, slotIndex, 0) .sendToTarget(); } + + @Override + public void reportThrottleStatusChanged(int slotIndex, + List<ThrottleStatus> statuses) { + mHandler.obtainMessage(QNS_APN_THROTTLE_STATUS_CHANGED, slotIndex, 0, statuses) + .sendToTarget(); + } } private void log(String s) { diff --git a/telephony/java/android/telephony/data/RouteSelectionDescriptor.aidl b/telephony/java/android/telephony/data/RouteSelectionDescriptor.aidl new file mode 100644 index 000000000000..563a00e5d177 --- /dev/null +++ b/telephony/java/android/telephony/data/RouteSelectionDescriptor.aidl @@ -0,0 +1,19 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.data; + +parcelable RouteSelectionDescriptor; diff --git a/telephony/java/android/telephony/data/RouteSelectionDescriptor.java b/telephony/java/android/telephony/data/RouteSelectionDescriptor.java new file mode 100644 index 000000000000..c2457f21f5ef --- /dev/null +++ b/telephony/java/android/telephony/data/RouteSelectionDescriptor.java @@ -0,0 +1,263 @@ +/** + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.data; + +import android.annotation.IntDef; +import android.annotation.IntRange; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Parcel; +import android.os.Parcelable; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * Represents a single route selection descriptor as defined in + * 3GPP TS 24.526. + */ +public final class RouteSelectionDescriptor implements Parcelable { + /** + * The min acceptable value for the precedence of a route selection descriptor. + * @hide + */ + public static final int MIN_ROUTE_PRECEDENCE = 0; + + /** + * The max acceptable value for the precedence of a route selection descriptor. + * @hide + */ + public static final int MAX_ROUTE_PRECEDENCE = 255; + + /** + * The route selection descriptor is for the session with IPV4 type. + */ + public static final int SESSION_TYPE_IPV4 = 0; + + /** + * The route selection descriptor is for the session with IPV6 type. + */ + public static final int SESSION_TYPE_IPV6 = 1; + + /** + * The route selection descriptor is for the session with both IP and IPV6 types. + */ + public static final int SESSION_TYPE_IPV4V6 = 2; + + /** @hide */ + @IntDef(prefix = { "SESSION_TYPE_" }, value = { + SESSION_TYPE_IPV4, + SESSION_TYPE_IPV6, + SESSION_TYPE_IPV4V6, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface RouteSessionType {} + + /** + * The route selection descriptor is using SSC mode 1. The session will provide continual + * support when UE's location is updated. + */ + public static final int ROUTE_SSC_MODE_1 = 1; + + /** + * The route selection descriptor is using SSC mode 2. The new session for the same network + * will be established after releasing the old session when UE's location is updated. + */ + public static final int ROUTE_SSC_MODE_2 = 2; + + /** + * The route selection descriptor is using SSC mode 3. The new session for the same network is + * allowed to be established before releasing the old session when UE's location is updated. + */ + public static final int ROUTE_SSC_MODE_3 = 3; + + /** + * The min acceptable value for the SSC mode of a route selection descriptor. + * @hide + */ + public static final int MIN_ROUTE_SSC_MODE = ROUTE_SSC_MODE_1; + + /** + * The max acceptable value for the SSC mode of a route selection descriptor. + * @hide + */ + public static final int MAX_ROUTE_SSC_MODE = ROUTE_SSC_MODE_3; + + /** @hide */ + @IntDef(prefix = { "ROUTE_SSC_MODE_" }, value = { + ROUTE_SSC_MODE_1, + ROUTE_SSC_MODE_2, + ROUTE_SSC_MODE_3, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface RouteSscMode {} + + @IntRange(from = MIN_ROUTE_PRECEDENCE, to = MAX_ROUTE_PRECEDENCE) + private final int mPrecedence; + @RouteSessionType + private final int mSessionType; + @RouteSscMode + @IntRange(from = MIN_ROUTE_SSC_MODE, to = MAX_ROUTE_SSC_MODE) + private final int mSscMode; + private final List<NetworkSliceInfo> mSliceInfo; + private final List<String> mDnn; + + /** @hide */ + RouteSelectionDescriptor(android.hardware.radio.V1_6.RouteSelectionDescriptor rsd) { + this(rsd.precedence, rsd.sessionType.value(), rsd.sscMode.value(), rsd.sliceInfo, + rsd.dnn); + } + + /** @hide */ + public RouteSelectionDescriptor(int precedence, int sessionType, int sscMode, + List<android.hardware.radio.V1_6.SliceInfo> sliceInfo, List<String> dnn) { + mPrecedence = precedence; + mSessionType = sessionType; + mSscMode = sscMode; + mSliceInfo = new ArrayList<NetworkSliceInfo>(); + for (android.hardware.radio.V1_6.SliceInfo si : sliceInfo) { + mSliceInfo.add(sliceInfoBuilder(si)); + } + mDnn = new ArrayList<String>(); + mDnn.addAll(dnn); + } + + private NetworkSliceInfo sliceInfoBuilder(android.hardware.radio.V1_6.SliceInfo si) { + NetworkSliceInfo.Builder builder = new NetworkSliceInfo.Builder() + .setSliceServiceType(si.sst) + .setMappedHplmnSliceServiceType(si.mappedHplmnSst); + if (si.sliceDifferentiator != NetworkSliceInfo.SLICE_DIFFERENTIATOR_NO_SLICE) { + builder + .setSliceDifferentiator(si.sliceDifferentiator) + .setMappedHplmnSliceDifferentiator(si.mappedHplmnSD); + } + return builder.build(); + } + + private RouteSelectionDescriptor(Parcel p) { + mPrecedence = p.readInt(); + mSessionType = p.readInt(); + mSscMode = p.readInt(); + mSliceInfo = p.createTypedArrayList(NetworkSliceInfo.CREATOR); + mDnn = new ArrayList<String>(); + p.readStringList(mDnn); + } + + /** + * Precedence value in the range of 0 to 255. Higher value has lower precedence. + * @return the precedence value for this route selection descriptor. + */ + @IntRange(from = MIN_ROUTE_PRECEDENCE, to = MAX_ROUTE_PRECEDENCE) + public int getPrecedence() { + return mPrecedence; + } + + /** + * This is used for checking which session type defined in 3GPP TS 23.501 is allowed for the + * route in a route selection descriptor. + * @return the session type for this route selection descriptor. + */ + @RouteSessionType + public int getSessionType() { + return mSessionType; + } + + /** + * SSC mode stands for Session and Service Continuity mode (which specifies the IP continuity + * mode) as defined in 3GPP TS 23.501. + * @return the SSC mode for this route selection descriptor. + */ + @RouteSscMode + public int getSscMode() { + return mSscMode; + } + + /** + * This is the list of all the slices available in the route selection descriptor as indicated + * by the network. These are the slices that can be used by the device if this route selection + * descriptor is used based the traffic (see 3GPP TS 23.501 for details). + * @return the list of all the slices available in the route selection descriptor. + */ + public @NonNull List<NetworkSliceInfo> getSliceInfo() { + return mSliceInfo; + } + + /** + * DNN stands for Data Network Name and represents an APN as defined in 3GPP TS 23.003. There + * can be 0 or more DNNs specified in a route selection descriptor. + * @return the list of DNN for this route selection descriptor. + */ + public @NonNull List<String> getDataNetworkName() { + return mDnn; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mPrecedence); + dest.writeInt(mSessionType); + dest.writeInt(mSscMode); + dest.writeTypedList(mSliceInfo, flags); + dest.writeStringList(mDnn); + } + + public static final @NonNull Parcelable.Creator<RouteSelectionDescriptor> CREATOR = + new Parcelable.Creator<RouteSelectionDescriptor>() { + @Override + public RouteSelectionDescriptor createFromParcel(Parcel source) { + return new RouteSelectionDescriptor(source); + } + + @Override + public RouteSelectionDescriptor[] newArray(int size) { + return new RouteSelectionDescriptor[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public boolean equals(@Nullable Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + RouteSelectionDescriptor that = (RouteSelectionDescriptor) o; + return mPrecedence == that.mPrecedence + && mSessionType == that.mSessionType + && mSscMode == that.mSscMode + && mSliceInfo.size() == that.mSliceInfo.size() + && mSliceInfo.containsAll(that.mSliceInfo) + && mDnn.size() == that.mDnn.size() + && mDnn.containsAll(that.mDnn); + } + + @Override + public int hashCode() { + return Objects.hash(mPrecedence, mSessionType, mSscMode, mSliceInfo, mDnn); + } + + @Override + public String toString() { + return "{.precedence = " + mPrecedence + ", .sessionType = " + mSessionType + + ", .sscMode = " + mSscMode + ", .sliceInfo = " + mSliceInfo + + ", .dnn = " + mDnn + "}"; + } +} diff --git a/telephony/java/android/telephony/data/ThrottleStatus.aidl b/telephony/java/android/telephony/data/ThrottleStatus.aidl new file mode 100644 index 000000000000..f75f35709816 --- /dev/null +++ b/telephony/java/android/telephony/data/ThrottleStatus.aidl @@ -0,0 +1,20 @@ +/* + * Copyright 2020 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. + */ + +/** @hide */ +package android.telephony.data; + +parcelable ThrottleStatus; diff --git a/telephony/java/android/telephony/data/ThrottleStatus.java b/telephony/java/android/telephony/data/ThrottleStatus.java new file mode 100644 index 000000000000..0335c6868340 --- /dev/null +++ b/telephony/java/android/telephony/data/ThrottleStatus.java @@ -0,0 +1,387 @@ +/* + * Copyright (C) 2020 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.data; + +import android.annotation.ElapsedRealtimeLong; +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.SuppressLint; +import android.annotation.SystemApi; +import android.os.Parcel; +import android.os.Parcelable; +import android.os.SystemClock; +import android.telephony.AccessNetworkConstants; +import android.telephony.Annotation; + +import java.util.Objects; + +/** + * Status information regarding the throttle status of an APN type. + * + * @hide + */ +@SystemApi +public final class ThrottleStatus implements Parcelable { + /** + * The APN type is not throttled. + */ + public static final int THROTTLE_TYPE_NONE = 1; + + /** + * The APN type is throttled until {@link android.os.SystemClock#elapsedRealtime()} + * has reached {@link ThrottleStatus#getThrottleExpiryTimeMillis} + */ + public static final int THROTTLE_TYPE_ELAPSED_TIME = 2; + + /** {@hide} */ + @IntDef(flag = true, prefix = {"THROTTLE_TYPE_"}, value = { + ThrottleStatus.THROTTLE_TYPE_NONE, + ThrottleStatus.THROTTLE_TYPE_ELAPSED_TIME, + }) + public @interface ThrottleType { + } + + /** + * The framework will not retry the APN type. + */ + public static final int RETRY_TYPE_NONE = 1; + + /** + * The next time the framework retries, it will attempt to establish a new connection. + */ + public static final int RETRY_TYPE_NEW_CONNECTION = 2; + + /** + * The next time the framework retires, it will retry to handover. + */ + public static final int RETRY_TYPE_HANDOVER = 3; + + /** {@hide} */ + @IntDef(flag = true, prefix = {"RETRY_TYPE_"}, value = { + ThrottleStatus.RETRY_TYPE_NONE, + ThrottleStatus.RETRY_TYPE_NEW_CONNECTION, + ThrottleStatus.RETRY_TYPE_HANDOVER, + }) + public @interface RetryType { + } + + private final int mSlotIndex; + private final @AccessNetworkConstants.TransportType int mTransportType; + private final @Annotation.ApnType int mApnType; + private final long mThrottleExpiryTimeMillis; + private final @RetryType int mRetryType; + private final @ThrottleType int mThrottleType; + + /** + * The slot index that the status applies to. + * + * @return the slot index + */ + public int getSlotIndex() { + return mSlotIndex; + } + + /** + * The type of transport that the status applies to. + * + * @return the transport type + */ + @AccessNetworkConstants.TransportType + public int getTransportType() { + return mTransportType; + } + + /** + * The APN type that the status applies to. + * + * @return the apn type + */ + @Annotation.ApnType + public int getApnType() { + return mApnType; + } + + /** + * The type of throttle applied to the APN type. + * + * @return the throttle type + */ + @ThrottleType + public int getThrottleType() { + return mThrottleType; + } + + /** + * Indicates the type of request that the framework will make the next time it retries + * to call {@link IDataService#setupDataCall}. + * + * @return the retry type + */ + @RetryType + public int getRetryType() { + return mRetryType; + } + + /** + * Gets the time at which the throttle expires. The value is based off of + * {@link SystemClock#elapsedRealtime}. + * + * This value only applies when the throttle type is set to + * {@link ThrottleStatus#THROTTLE_TYPE_ELAPSED_TIME}. + * + * A value of {@link Long#MAX_VALUE} implies that the APN type is throttled indefinitely. + * + * @return the time at which the throttle expires + */ + @ElapsedRealtimeLong + public long getThrottleExpiryTimeMillis() { + return mThrottleExpiryTimeMillis; + } + + private ThrottleStatus(int slotIndex, + @AccessNetworkConstants.TransportType int transportType, + @Annotation.ApnType int apnTypes, + @ThrottleType int throttleType, + long throttleExpiryTimeMillis, + @RetryType int retryType) { + mSlotIndex = slotIndex; + mTransportType = transportType; + mApnType = apnTypes; + mThrottleType = throttleType; + mThrottleExpiryTimeMillis = throttleExpiryTimeMillis; + mRetryType = retryType; + } + + private ThrottleStatus(@NonNull Parcel source) { + mSlotIndex = source.readInt(); + mTransportType = source.readInt(); + mApnType = source.readInt(); + mThrottleExpiryTimeMillis = source.readLong(); + mRetryType = source.readInt(); + mThrottleType = source.readInt(); + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mSlotIndex); + dest.writeInt(mTransportType); + dest.writeInt(mApnType); + dest.writeLong(mThrottleExpiryTimeMillis); + dest.writeInt(mRetryType); + dest.writeInt(mThrottleType); + } + + public static final @NonNull Parcelable.Creator<ThrottleStatus> CREATOR = + new Parcelable.Creator<ThrottleStatus>() { + @Override + public ThrottleStatus createFromParcel(@NonNull Parcel source) { + return new ThrottleStatus(source); + } + + @Override + public ThrottleStatus[] newArray(int size) { + return new ThrottleStatus[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public int hashCode() { + return Objects.hash(mSlotIndex, mApnType, mRetryType, mThrottleType, + mThrottleExpiryTimeMillis, mTransportType); + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } else if (obj instanceof ThrottleStatus) { + ThrottleStatus other = (ThrottleStatus) obj; + return this.mSlotIndex == other.mSlotIndex + && this.mApnType == other.mApnType + && this.mRetryType == other.mRetryType + && this.mThrottleType == other.mThrottleType + && this.mThrottleExpiryTimeMillis == other.mThrottleExpiryTimeMillis + && this.mTransportType == other.mTransportType; + } else { + return false; + } + } + + @Override + public String toString() { + return "ThrottleStatus{" + + "mSlotIndex=" + mSlotIndex + + ", mTransportType=" + mTransportType + + ", mApnType=" + ApnSetting.getApnTypeString(mApnType) + + ", mThrottleExpiryTimeMillis=" + mThrottleExpiryTimeMillis + + ", mRetryType=" + mRetryType + + ", mThrottleType=" + mThrottleType + + '}'; + } + + /** + * Provides a convenient way to set the fields of an {@link ThrottleStatus} when creating a + * new instance. + * + * <p>The example below shows how you might create a new {@code ThrottleStatus}: + * + * <pre><code> + * + * ThrottleStatus = new ThrottleStatus.Builder() + * .setSlotIndex(1) + * .setApnType({@link ApnSetting#TYPE_EMERGENCY}) + * .setNoThrottle() + * .setRetryType({@link ThrottleStatus#RETRY_TYPE_NEW_CONNECTION}) + * .build(); + * </code></pre> + */ + public static final class Builder { + private int mSlotIndex; + private @AccessNetworkConstants.TransportType int mTransportType; + private @Annotation.ApnType int mApnType; + private long mThrottleExpiryTimeMillis; + private @RetryType int mRetryType; + private @ThrottleType int mThrottleType; + + /** + * @hide + */ + public static final long NO_THROTTLE_EXPIRY_TIME = + DataCallResponse.RETRY_DURATION_UNDEFINED; + + /** + * Default constructor for the Builder. + */ + public Builder() { + } + + /** + * Set the slot index. + * + * @param slotIndex the slot index. + * @return The same instance of the builder. + */ + @NonNull + public Builder setSlotIndex(int slotIndex) { + this.mSlotIndex = slotIndex; + return this; + } + + /** + * Set the transport type. + * + * @param transportType the transport type. + * @return The same instance of the builder. + */ + @NonNull + public Builder setTransportType(@AccessNetworkConstants.TransportType + int transportType) { + this.mTransportType = transportType; + return this; + } + + /** + * Set the APN type. + * + * @param apnType the APN type. + * @return The same instance of the builder. + */ + @NonNull + public Builder setApnType(@Annotation.ApnType int apnType) { + this.mApnType = apnType; + return this; + } + + /** + * Sets the time at which the throttle will expire. The value is based off of + * {@link SystemClock#elapsedRealtime}. + * + * When setting this value, the throttle type is set to + * {@link ThrottleStatus#THROTTLE_TYPE_ELAPSED_TIME}. + * + * A value of {@link Long#MAX_VALUE} implies that the APN type is throttled indefinitely. + * + * @param throttleExpiryTimeMillis The elapsed time at which the throttle expires. + * Throws {@link IllegalArgumentException} for values less + * than 0. + * @return The same instance of the builder. + */ + @NonNull + public Builder setThrottleExpiryTimeMillis( + @ElapsedRealtimeLong long throttleExpiryTimeMillis) { + if (throttleExpiryTimeMillis >= 0) { + this.mThrottleExpiryTimeMillis = throttleExpiryTimeMillis; + this.mThrottleType = THROTTLE_TYPE_ELAPSED_TIME; + } else { + throw new IllegalArgumentException("throttleExpiryTimeMillis must be greater than " + + "or equal to 0"); + } + return this; + } + + /** + * Sets the status of the APN type as not being throttled. + * + * When setting this value, the throttle type is set to + * {@link ThrottleStatus#THROTTLE_TYPE_NONE} and the expiry time is set to + * {@link Builder#NO_THROTTLE_EXPIRY_TIME}. + * + * @return The same instance of the builder. + */ + @SuppressLint("MissingGetterMatchingBuilder") + @NonNull + public Builder setNoThrottle() { + mThrottleType = THROTTLE_TYPE_NONE; + mThrottleExpiryTimeMillis = NO_THROTTLE_EXPIRY_TIME; + return this; + } + + /** + * Set the type of request that the framework will make the next time it retries + * to call {@link IDataService#setupDataCall}. + * + * @param retryType the type of request + * @return The same instance of the builder. + */ + @NonNull + public Builder setRetryType(@RetryType int retryType) { + this.mRetryType = retryType; + return this; + } + + /** + * Build the {@link ThrottleStatus} + * + * @return the {@link ThrottleStatus} object + */ + @NonNull + public ThrottleStatus build() { + return new ThrottleStatus( + mSlotIndex, + mTransportType, + mApnType, + mThrottleType, + mThrottleExpiryTimeMillis, + mRetryType); + } + } +} diff --git a/telephony/java/android/telephony/data/TrafficDescriptor.aidl b/telephony/java/android/telephony/data/TrafficDescriptor.aidl new file mode 100644 index 000000000000..a9c7604a91b6 --- /dev/null +++ b/telephony/java/android/telephony/data/TrafficDescriptor.aidl @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** @hide */ +package android.telephony.data; + +parcelable TrafficDescriptor; diff --git a/telephony/java/android/telephony/data/TrafficDescriptor.java b/telephony/java/android/telephony/data/TrafficDescriptor.java new file mode 100644 index 000000000000..2178fc1717b9 --- /dev/null +++ b/telephony/java/android/telephony/data/TrafficDescriptor.java @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.data; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.Arrays; +import java.util.Objects; + +/** + * A traffic descriptor, as defined in 3GPP TS 24.526 Section 5.2. It is used for UE Route Selection + * Policy(URSP) traffic matching as described in 3GPP TS 24.526 Section 4.2.2. It includes an + * optional Data Network Name(DNN), which, if present, must be used for traffic matching; it does + * not specify the end point to be used for the data call. + */ +public final class TrafficDescriptor implements Parcelable { + private final String mDnn; + private final byte[] mOsAppId; + + private TrafficDescriptor(@NonNull Parcel in) { + mDnn = in.readString(); + mOsAppId = in.createByteArray(); + } + + /** + * Create a traffic descriptor, as defined in 3GPP TS 24.526 Section 5.2 + * @param dnn optional DNN, which must be used for traffic matching, if present + * @param osAppId OsId + osAppId of the traffic descriptor + * + * @hide + */ + public TrafficDescriptor(String dnn, byte[] osAppId) { + mDnn = dnn; + mOsAppId = osAppId; + } + + /** + * DNN stands for Data Network Name and represents an APN as defined in 3GPP TS 23.003. + * @return the DNN of this traffic descriptor if one is included by the network, null + * otherwise. + */ + public @Nullable String getDataNetworkName() { + return mDnn; + } + + /** + * OsAppId is the app id as defined in 3GPP TS 24.526 Section 5.2, and it identifies a traffic + * category. It includes the OS Id component of the field as defined in the specs. + * @return the OS App ID of this traffic descriptor if one is included by the network, null + * otherwise. + */ + public @Nullable byte[] getOsAppId() { + return mOsAppId; + } + + @Override + public int describeContents() { + return 0; + } + + @NonNull @Override + public String toString() { + return "TrafficDescriptor={mDnn=" + mDnn + ", mOsAppId=" + mOsAppId + "}"; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeString(mDnn); + dest.writeByteArray(mOsAppId); + } + + public static final @NonNull Parcelable.Creator<TrafficDescriptor> CREATOR = + new Parcelable.Creator<TrafficDescriptor>() { + @Override + public @NonNull TrafficDescriptor createFromParcel(@NonNull Parcel source) { + return new TrafficDescriptor(source); + } + + @Override + public @NonNull TrafficDescriptor[] newArray(int size) { + return new TrafficDescriptor[size]; + } + }; + + @Override + public boolean equals(@Nullable Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + TrafficDescriptor that = (TrafficDescriptor) o; + return Objects.equals(mDnn, that.mDnn) && Arrays.equals(mOsAppId, that.mOsAppId); + } + + @Override + public int hashCode() { + return Objects.hash(mDnn, mOsAppId); + } + + /** + * Provides a convenient way to set the fields of a {@link TrafficDescriptor} when creating a + * new instance. + * + * <p>The example below shows how you might create a new {@code TrafficDescriptor}: + * + * <pre><code> + * + * TrafficDescriptor response = new TrafficDescriptor.Builder() + * .setDnn("") + * .build(); + * </code></pre> + * + */ + public static final class Builder { + private String mDnn = null; + private byte[] mOsAppId = null; + + /** + * Default constructor for Builder. + */ + public Builder() { + } + + /** + * Set the Data Network Name(DNN). + * + * @return The same instance of the builder. + */ + @NonNull + public Builder setDataNetworkName(@NonNull String dnn) { + this.mDnn = dnn; + return this; + } + + /** + * Set the OS App ID (including OS Id as defind in the specs). + * + * @return The same instance of the builder. + */ + @NonNull + public Builder setOsAppId(@NonNull byte[] osAppId) { + this.mOsAppId = osAppId; + return this; + } + + /** + * Build the {@link TrafficDescriptor}. + * + * @throws IllegalArgumentException if DNN and OS App ID are null. + * + * @return the {@link TrafficDescriptor} object. + */ + @NonNull + public TrafficDescriptor build() { + if (this.mDnn == null && this.mOsAppId == null) { + throw new IllegalArgumentException("DNN and OS App ID are null"); + } + return new TrafficDescriptor(this.mDnn, this.mOsAppId); + } + } +} diff --git a/telephony/java/android/telephony/data/UrspRule.aidl b/telephony/java/android/telephony/data/UrspRule.aidl new file mode 100644 index 000000000000..2bed583c750e --- /dev/null +++ b/telephony/java/android/telephony/data/UrspRule.aidl @@ -0,0 +1,19 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.data; + +parcelable UrspRule; diff --git a/telephony/java/android/telephony/data/UrspRule.java b/telephony/java/android/telephony/data/UrspRule.java new file mode 100644 index 000000000000..fbe199975b00 --- /dev/null +++ b/telephony/java/android/telephony/data/UrspRule.java @@ -0,0 +1,178 @@ +/** + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.data; + +import android.annotation.IntRange; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.hardware.radio.V1_6.OptionalDnn; +import android.hardware.radio.V1_6.OptionalOsAppId; +import android.os.Parcel; +import android.os.Parcelable; + + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * Represents a single URSP rule as defined in 3GPP TS 24.526. URSP stands for UE Route Selection + * Policy. In 5G, network can provide URSP information to devices which provides information on + * what connection parameters should be used for what traffic. + */ +public final class UrspRule implements Parcelable { + /** + * The min acceptable value for the precedence of a URSP rule. + * @hide + */ + public static final int MIN_URSP_PRECEDENCE = 0; + + /** + * The max acceptable value for the precedence of a URSP rule. + * @hide + */ + public static final int MAX_URSP_PRECEDENCE = 255; + + @IntRange(from = MIN_URSP_PRECEDENCE, to = MAX_URSP_PRECEDENCE) + private final int mPrecedence; + private final List<TrafficDescriptor> mTrafficDescriptors; + private final List<RouteSelectionDescriptor> mRouteSelectionDescriptor; + + UrspRule(android.hardware.radio.V1_6.UrspRule ur) { + this(ur.precedence, ur.trafficDescriptors, ur.routeSelectionDescriptor); + } + + /** @hide */ + public UrspRule(int precedence, + List<android.hardware.radio.V1_6.TrafficDescriptor> trafficDescriptors, + List<android.hardware.radio.V1_6.RouteSelectionDescriptor> routeSelectionDescriptor) { + mPrecedence = precedence; + mTrafficDescriptors = new ArrayList<TrafficDescriptor>(); + for (android.hardware.radio.V1_6.TrafficDescriptor td : trafficDescriptors) { + mTrafficDescriptors.add(convertToTrafficDescriptor(td)); + } + mRouteSelectionDescriptor = new ArrayList<RouteSelectionDescriptor>(); + for (android.hardware.radio.V1_6.RouteSelectionDescriptor rsd : routeSelectionDescriptor) { + mRouteSelectionDescriptor.add(new RouteSelectionDescriptor(rsd)); + } + } + + /** Convert an ArrayList of Bytes to an exactly-sized primitive array */ + private byte[] arrayListToPrimitiveArray(ArrayList<Byte> bytes) { + byte[] ret = new byte[bytes.size()]; + for (int i = 0; i < ret.length; i++) { + ret[i] = bytes.get(i); + } + return ret; + } + + private TrafficDescriptor convertToTrafficDescriptor( + android.hardware.radio.V1_6.TrafficDescriptor td) { + String dnn = td.dnn.getDiscriminator() == OptionalDnn.hidl_discriminator.noinit + ? null : td.dnn.value(); + byte[] osAppId = td.osAppId.getDiscriminator() == OptionalOsAppId.hidl_discriminator.noinit + ? null : arrayListToPrimitiveArray(td.osAppId.value().osAppId); + TrafficDescriptor.Builder builder = new TrafficDescriptor.Builder(); + if (dnn != null) { + builder.setDataNetworkName(dnn); + } + if (osAppId != null) { + builder.setOsAppId(osAppId); + } + return builder.build(); + } + + private UrspRule(Parcel p) { + mPrecedence = p.readInt(); + mTrafficDescriptors = p.createTypedArrayList(TrafficDescriptor.CREATOR); + mRouteSelectionDescriptor = p.createTypedArrayList(RouteSelectionDescriptor.CREATOR); + } + + /** + * Precedence value in the range of 0 to 255. Higher value has lower precedence. + * @return the precedence value for this URSP rule. + */ + @IntRange(from = MIN_URSP_PRECEDENCE, to = MAX_URSP_PRECEDENCE) + public int getPrecedence() { + return mPrecedence; + } + + /** + * These traffic descriptors are used as a matcher for network requests. + * @return the traffic descriptors which are associated to this URSP rule. + */ + public @NonNull List<TrafficDescriptor> getTrafficDescriptors() { + return mTrafficDescriptors; + } + + /** + * List of routes (connection parameters) that must be used by the device for requests matching + * a traffic descriptor. + * @return the route selection descriptors which are associated to this URSP rule. + */ + public @NonNull List<RouteSelectionDescriptor> getRouteSelectionDescriptor() { + return mRouteSelectionDescriptor; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mPrecedence); + dest.writeTypedList(mTrafficDescriptors, flags); + dest.writeTypedList(mRouteSelectionDescriptor, flags); + } + + public static final @NonNull Parcelable.Creator<UrspRule> CREATOR = + new Parcelable.Creator<UrspRule>() { + @Override + public UrspRule createFromParcel(Parcel source) { + return new UrspRule(source); + } + + @Override + public UrspRule[] newArray(int size) { + return new UrspRule[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public boolean equals(@Nullable Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + UrspRule that = (UrspRule) o; + return mPrecedence == that.mPrecedence + && mTrafficDescriptors.size() == that.mTrafficDescriptors.size() + && mTrafficDescriptors.containsAll(that.mTrafficDescriptors) + && mRouteSelectionDescriptor.size() == that.mRouteSelectionDescriptor.size() + && mRouteSelectionDescriptor.containsAll(that.mRouteSelectionDescriptor); + } + + @Override + public int hashCode() { + return Objects.hash(mPrecedence, mTrafficDescriptors, mRouteSelectionDescriptor); + } + + @Override + public String toString() { + return "{.precedence = " + mPrecedence + ", .trafficDescriptors = " + mTrafficDescriptors + + ", .routeSelectionDescriptor = " + mRouteSelectionDescriptor + "}"; + } +} diff --git a/telephony/java/android/telephony/emergency/OWNERS b/telephony/java/android/telephony/emergency/OWNERS new file mode 100644 index 000000000000..fa07dce5f615 --- /dev/null +++ b/telephony/java/android/telephony/emergency/OWNERS @@ -0,0 +1,5 @@ +# Bug component: 20868 + +rgreenwalt@google.com +tgunn@google.com +shuoq@google.com diff --git a/telephony/java/android/telephony/euicc/DownloadableSubscription.java b/telephony/java/android/telephony/euicc/DownloadableSubscription.java index 23d46ba09599..a5150b010f57 100644 --- a/telephony/java/android/telephony/euicc/DownloadableSubscription.java +++ b/telephony/java/android/telephony/euicc/DownloadableSubscription.java @@ -15,10 +15,12 @@ */ package android.telephony.euicc; +import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; import android.app.PendingIntent; import android.compat.annotation.UnsupportedAppUsage; +import android.os.Build; import android.os.Parcel; import android.os.Parcelable; import android.telephony.UiccAccessRule; @@ -61,7 +63,7 @@ public final class DownloadableSubscription implements Parcelable { */ @Nullable @Deprecated - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public final String encodedActivationCode; @Nullable private String confirmationCode; @@ -101,44 +103,81 @@ public final class DownloadableSubscription implements Parcelable { this.accessRules = accessRules; } - /** @hide */ - @SystemApi public static final class Builder { @Nullable private String encodedActivationCode; @Nullable private String confirmationCode; @Nullable private String carrierName; List<UiccAccessRule> accessRules; + /** @hide */ + @SystemApi public Builder() {} - public Builder(DownloadableSubscription baseSubscription) { + public Builder(@NonNull DownloadableSubscription baseSubscription) { encodedActivationCode = baseSubscription.getEncodedActivationCode(); confirmationCode = baseSubscription.getConfirmationCode(); carrierName = baseSubscription.getCarrierName(); accessRules = baseSubscription.getAccessRules(); } + public Builder(@NonNull String encodedActivationCode) { + this.encodedActivationCode = encodedActivationCode; + } + + /** + * Builds a {@link DownloadableSubscription} object. + * @return a non-null {@link DownloadableSubscription} object. + */ + @NonNull public DownloadableSubscription build() { return new DownloadableSubscription(encodedActivationCode, confirmationCode, carrierName, accessRules); } - public Builder setEncodedActivationCode(String value) { + /** + * Sets the encoded activation code. + * @param value the activation code to use. An activation code can be parsed from a user + * scanned QR code. The format of activation code is defined in SGP.22. For + * example, "1$SMDP.GSMA.COM$04386-AGYFT-A74Y8-3F815$1.3.6.1.4.1.31746". For + * detail, see {@code com.android.euicc.data.ActivationCode}. Must not be null. + */ + @NonNull + public Builder setEncodedActivationCode(@NonNull String value) { encodedActivationCode = value; return this; } - public Builder setConfirmationCode(String value) { + /** + * Sets the confirmation code. + * @param value the confirmation code to use to authenticate the carrier server got + * subscription download. + */ + @NonNull + public Builder setConfirmationCode(@NonNull String value) { confirmationCode = value; return this; } - public Builder setCarrierName(String value) { + /** + * Sets the user-visible carrier name. + * @param value carrier name. + * @hide + */ + @NonNull + @SystemApi + public Builder setCarrierName(@NonNull String value) { carrierName = value; return this; } - public Builder setAccessRules(List<UiccAccessRule> value) { + /** + * Sets the {@link UiccAccessRule}s dictating access to this subscription. + * @param value A list of {@link UiccAccessRule}s. + * @hide + */ + @NonNull + @SystemApi + public Builder setAccessRules(@NonNull List<UiccAccessRule> value) { accessRules = value; return this; } @@ -191,7 +230,7 @@ public final class DownloadableSubscription implements Parcelable { * @deprecated - Do not use. */ @Deprecated - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public void setCarrierName(String carrierName) { this.carrierName = carrierName; } @@ -238,7 +277,7 @@ public final class DownloadableSubscription implements Parcelable { * @deprecated - Do not use. */ @Deprecated - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public void setAccessRules(UiccAccessRule[] accessRules) { this.accessRules = Arrays.asList(accessRules); } diff --git a/telephony/java/android/telephony/euicc/EuiccInfo.java b/telephony/java/android/telephony/euicc/EuiccInfo.java index 467d2689cd3c..08de2051975e 100644 --- a/telephony/java/android/telephony/euicc/EuiccInfo.java +++ b/telephony/java/android/telephony/euicc/EuiccInfo.java @@ -17,6 +17,7 @@ package android.telephony.euicc; import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; +import android.os.Build; import android.os.Parcel; import android.os.Parcelable; @@ -44,7 +45,7 @@ public final class EuiccInfo implements Parcelable { }; @Nullable - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) private final String osVersion; /** diff --git a/telephony/java/android/telephony/euicc/OWNERS b/telephony/java/android/telephony/euicc/OWNERS new file mode 100644 index 000000000000..9e51a4b30516 --- /dev/null +++ b/telephony/java/android/telephony/euicc/OWNERS @@ -0,0 +1,6 @@ +# Bug component: 20868 + +rgreenwalt@google.com +tgunn@google.com +refuhoo@google.com +amitmahajan@google.com diff --git a/telephony/java/android/telephony/gba/GbaAuthRequest.aidl b/telephony/java/android/telephony/gba/GbaAuthRequest.aidl new file mode 100644 index 000000000000..ba243a27b4be --- /dev/null +++ b/telephony/java/android/telephony/gba/GbaAuthRequest.aidl @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2020 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.gba; + +parcelable GbaAuthRequest; diff --git a/telephony/java/android/telephony/gba/GbaAuthRequest.java b/telephony/java/android/telephony/gba/GbaAuthRequest.java new file mode 100644 index 000000000000..5366e9af3147 --- /dev/null +++ b/telephony/java/android/telephony/gba/GbaAuthRequest.java @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2020 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.gba; + +import android.annotation.NonNull; +import android.net.Uri; +import android.os.Parcel; +import android.os.Parcelable; +import android.telephony.IBootstrapAuthenticationCallback; + +import com.android.internal.telephony.uicc.IccUtils; + +import java.util.concurrent.atomic.AtomicInteger; + +/** + * GBA authentication request + * {@hide} + */ +public final class GbaAuthRequest implements Parcelable { + private int mToken; + private int mSubId; + private int mAppType; + private Uri mNafUrl; + private byte[] mSecurityProtocol; + private boolean mForceBootStrapping; + private IBootstrapAuthenticationCallback mCallback; + + private static AtomicInteger sUniqueToken = new AtomicInteger(0); + + public GbaAuthRequest(int subId, int appType, Uri nafUrl, byte[] securityProtocol, + boolean forceBootStrapping, IBootstrapAuthenticationCallback callback) { + this(nextUniqueToken(), subId, appType, nafUrl, + securityProtocol, forceBootStrapping, callback); + } + + public GbaAuthRequest(GbaAuthRequest request) { + this(request.mToken, request.mSubId, request.mAppType, request.mNafUrl, + request.mSecurityProtocol, request.mForceBootStrapping, request.mCallback); + } + + public GbaAuthRequest(int token, int subId, int appType, Uri nafUrl, byte[] securityProtocol, + boolean forceBootStrapping, IBootstrapAuthenticationCallback callback) { + mToken = token; + mSubId = subId; + mAppType = appType; + mNafUrl = nafUrl; + mSecurityProtocol = securityProtocol; + mCallback = callback; + mForceBootStrapping = forceBootStrapping; + } + + public int getToken() { + return mToken; + } + + public int getSubId() { + return mSubId; + } + + public int getAppType() { + return mAppType; + } + + public Uri getNafUrl() { + return mNafUrl; + } + + public byte[] getSecurityProtocol() { + return mSecurityProtocol; + } + + public boolean isForceBootStrapping() { + return mForceBootStrapping; + } + + public void setCallback(IBootstrapAuthenticationCallback cb) { + mCallback = cb; + } + + public IBootstrapAuthenticationCallback getCallback() { + return mCallback; + } + + /** + * {@link Parcelable#writeToParcel} + */ + public void writeToParcel(@NonNull Parcel out, int flags) { + out.writeInt(mToken); + out.writeInt(mSubId); + out.writeInt(mAppType); + out.writeParcelable(mNafUrl, 0); + out.writeInt(mSecurityProtocol.length); + out.writeByteArray(mSecurityProtocol); + out.writeBoolean(mForceBootStrapping); + out.writeStrongInterface(mCallback); + } + + /** + * {@link Parcelable.Creator} + * + */ + public static final @android.annotation.NonNull Parcelable.Creator< + GbaAuthRequest> CREATOR = new Creator<GbaAuthRequest>() { + @Override + public GbaAuthRequest createFromParcel(Parcel in) { + int token = in.readInt(); + int subId = in.readInt(); + int appType = in.readInt(); + Uri nafUrl = in.readParcelable(GbaAuthRequest.class.getClassLoader()); + int len = in.readInt(); + byte[] protocol = new byte[len]; + in.readByteArray(protocol); + boolean forceBootStrapping = in.readBoolean(); + IBootstrapAuthenticationCallback callback = + IBootstrapAuthenticationCallback.Stub + .asInterface(in.readStrongBinder()); + return new GbaAuthRequest(token, subId, appType, nafUrl, protocol, + forceBootStrapping, callback); + } + + @Override + public GbaAuthRequest[] newArray(int size) { + return new GbaAuthRequest[size]; + } + }; + + /** + * {@link Parcelable#describeContents} + */ + public int describeContents() { + return 0; + } + + private static int nextUniqueToken() { + return sUniqueToken.getAndIncrement() << 16 | (0xFFFF & (int) System.currentTimeMillis()); + } + + @Override + public String toString() { + String str = "Token: " + mToken + "SubId:" + mSubId + ", AppType:" + + mAppType + ", NafUrl:" + mNafUrl + ", SecurityProtocol:" + + IccUtils.bytesToHexString(mSecurityProtocol) + + ", ForceBootStrapping:" + mForceBootStrapping + + ", CallBack:" + mCallback; + return str; + } +} diff --git a/telephony/java/android/telephony/gba/GbaService.java b/telephony/java/android/telephony/gba/GbaService.java new file mode 100644 index 000000000000..3962aff9df4f --- /dev/null +++ b/telephony/java/android/telephony/gba/GbaService.java @@ -0,0 +1,226 @@ +/* + * Copyright (C) 2020 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.gba; + +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.app.Service; +import android.content.Intent; +import android.net.Uri; +import android.os.Build; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.RemoteException; +import android.telephony.Annotation.UiccAppTypeExt; +import android.telephony.IBootstrapAuthenticationCallback; +import android.telephony.TelephonyManager; +import android.telephony.TelephonyManager.AuthenticationFailureReason; +import android.util.Log; +import android.util.SparseArray; + +/** + * Base class for GBA Service. Any implementation which wants to provide + * GBA service must extend this class. + * + * <p>Note that the application to implement the service must declare to use + * the permission {@link android.Manifest.permission#BIND_GBA_SERVICE}, + * and filter the intent of {@link #SERVICE_INTERFACE}. + * The manifest of the service must follow the format below: + * + * <p>... + * <service + * android:name=".EgGbaService" + * android:directBootAware="true" + * android:permission="android.permission.BIND_GBA_SERVICE" > + * ... + * <intent-filter> + * <action android:name="android.telephony.gba.GbaService"/> + * </intent-filter> + * </service> + * ... + * + * <p>The service should also be file-based encryption (FBE) aware. + * {@hide} + */ +@SystemApi +public class GbaService extends Service { + private static final boolean DBG = Build.IS_DEBUGGABLE; + private static final String TAG = "GbaService"; + + /** + * The intent must be defined as an intent-filter in the + * AndroidManifest of the GbaService. + */ + public static final String SERVICE_INTERFACE = "android.telephony.gba.GbaService"; + + private static final int EVENT_GBA_AUTH_REQUEST = 1; + + private final HandlerThread mHandlerThread; + private final GbaServiceHandler mHandler; + + private final SparseArray<IBootstrapAuthenticationCallback> mCallbacks = new SparseArray<>(); + private final IGbaServiceWrapper mBinder = new IGbaServiceWrapper(); + + /** + * Default constructor. + */ + public GbaService() { + mHandlerThread = new HandlerThread(TAG); + mHandlerThread.start(); + + mHandler = new GbaServiceHandler(mHandlerThread.getLooper()); + Log.d(TAG, "GBA service created"); + } + + private class GbaServiceHandler extends Handler { + + GbaServiceHandler(Looper looper) { + super(looper); + } + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case EVENT_GBA_AUTH_REQUEST: + GbaAuthRequest req = (GbaAuthRequest) msg.obj; + synchronized (mCallbacks) { + mCallbacks.put(req.getToken(), req.getCallback()); + } + onAuthenticationRequest(req.getSubId(), req.getToken(), req.getAppType(), + req.getNafUrl(), req.getSecurityProtocol(), req.isForceBootStrapping()); + break; + default: + break; + } + } + } + + /** + * Called by the platform when a GBA authentication request is received from + * {@link TelephonyManager#bootstrapAuthenticationRequest} to get the KsNAF for + * a particular NAF. + * + * @param subscriptionId the ICC card to be used for the bootstrapping authentication. + * @param token the identification of the authentication request. + * @param appType icc application type, like {@link #APPTYPE_USIM} or {@link + * #APPTYPE_ISIM} or {@link#APPTYPE_UNKNOWN} + * @param nafUrl Network Application Function(NAF) fully qualified domain name and + * the selected GBA mode. It shall contain two parts delimited by "@" sign. The first + * part is the constant string "3GPP-bootstrapping" (GBA_ME), + * "3GPP-bootstrapping-uicc" (GBA_ U), or "3GPP-bootstrapping-digest" (GBA_Digest), + * and the latter part shall be the FQDN of the NAF (e.g. + * "3GPP-bootstrapping@naf1.operator.com" or "3GPP-bootstrapping-uicc@naf1.operator.com", + * or "3GPP-bootstrapping-digest@naf1.operator.com"). + * @param securityProtocol Security protocol identifier between UE and NAF. See + * 3GPP TS 33.220 Annex H. Application can use + * {@link UaSecurityProtocolIdentifier#createDefaultUaSpId}, + * {@link UaSecurityProtocolIdentifier#create3GppUaSpId}, + * to create the ua security protocol identifier as needed + * @param forceBootStrapping true=force bootstrapping, false=do not force + * bootstrapping. Bootstrapping shouldn't be forced unless the application sees + * authentication errors from the server. + * Response is returned via {@link TelephonyManager#BootstrapAuthenticationCallback} + * along with the token to identify the request. + * + * <p>Note that this is not called in the main thread. + */ + public void onAuthenticationRequest(int subscriptionId, int token, @UiccAppTypeExt int appType, + @NonNull Uri nafUrl, @NonNull byte[] securityProtocol, boolean forceBootStrapping) { + //Default implementation should be overridden by vendor Gba Service. Vendor Gba Service + //should handle the gba bootstrap authentication request, and call reportKeysAvailable or + //reportAuthenticationFailure to notify the caller accordingly. + reportAuthenticationFailure( + token, TelephonyManager.GBA_FAILURE_REASON_FEATURE_NOT_SUPPORTED); + } + + /** + * Called by {@link GbaService} when the previously requested GBA keys are available + * (@see onAuthenticationRequest()) + * + * @param token unique identifier of the request. + * @param gbaKey KsNaf Response. + * @param transactionId Bootstrapping Transaction ID. + * @throws RuntimeException when there is remote failure of callback. + */ + public final void reportKeysAvailable(int token, @NonNull byte[] gbaKey, + @NonNull String transactionId) throws RuntimeException { + IBootstrapAuthenticationCallback cb = null; + synchronized (mCallbacks) { + cb = mCallbacks.get(token); + mCallbacks.remove(token); + } + if (cb != null) { + try { + cb.onKeysAvailable(token, gbaKey, transactionId); + } catch (RemoteException exception) { + throw exception.rethrowAsRuntimeException(); + } + } + } + + /** + * Invoked when the previously requested GBA key authentication failed + * (@see onAuthenticationRequest()) + * + * @param token unique identifier of the request. + * @param reason The reason for the authentication failure. + * @throws RuntimeException when there is remote failure of callback. + */ + public final void reportAuthenticationFailure(int token, + @AuthenticationFailureReason int reason) throws RuntimeException { + IBootstrapAuthenticationCallback cb = null; + synchronized (mCallbacks) { + cb = mCallbacks.get(token); + mCallbacks.remove(token); + } + if (cb != null) { + try { + cb.onAuthenticationFailure(token, reason); + } catch (RemoteException exception) { + throw exception.rethrowAsRuntimeException(); + } + } + } + + /** @hide */ + @Override + public IBinder onBind(Intent intent) { + if (SERVICE_INTERFACE.equals(intent.getAction())) { + Log.d(TAG, "GbaService Bound."); + return mBinder; + } + return null; + } + + /** @hide */ + @Override + public void onDestroy() { + mHandlerThread.quit(); + super.onDestroy(); + } + + private class IGbaServiceWrapper extends IGbaService.Stub { + @Override + public void authenticationRequest(GbaAuthRequest request) { + if (DBG) Log.d(TAG, "receive request: " + request); + mHandler.obtainMessage(EVENT_GBA_AUTH_REQUEST, request).sendToTarget(); + } + } +} diff --git a/telephony/java/android/telephony/gba/IGbaService.aidl b/telephony/java/android/telephony/gba/IGbaService.aidl new file mode 100644 index 000000000000..b7ba5a4441ec --- /dev/null +++ b/telephony/java/android/telephony/gba/IGbaService.aidl @@ -0,0 +1,27 @@ +/* + * Copyright 2020 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.gba; + +import android.net.Uri; +import android.telephony.gba.GbaAuthRequest; + +/** + * @hide + */ +interface IGbaService +{ + oneway void authenticationRequest(in GbaAuthRequest request); +} diff --git a/telephony/java/android/telephony/gba/TlsParams.java b/telephony/java/android/telephony/gba/TlsParams.java new file mode 100644 index 000000000000..922f4bbb4dbf --- /dev/null +++ b/telephony/java/android/telephony/gba/TlsParams.java @@ -0,0 +1,281 @@ +/* + * Copyright 2020 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.gba; + +import android.annotation.IntDef; +import android.annotation.SystemApi; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Arrays; + +/** + * Defines the TLS parameters for GBA as per IANA and TS 33.210, which are used + * by some UA security protocol identifiers defined in 3GPP TS 33.220 Annex H, + * and 3GPP TS 33.222. + * + * @hide + */ +@SystemApi +public class TlsParams { + + private TlsParams() {} + + /** + * TLS protocol version supported by GBA + */ + public static final int PROTOCOL_VERSION_TLS_1_2 = 0x0303; + public static final int PROTOCOL_VERSION_TLS_1_3 = 0x0304; + + /** + * TLS cipher suites are used to create {@link UaSecurityProtocolIdentifier} + * by {@link UaSecurityProtocolIdentifier#create3GppUaSpId} + * + * @hide + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef( + prefix = {"TLS_"}, + value = { + TLS_NULL_WITH_NULL_NULL, + TLS_RSA_WITH_NULL_MD5, + TLS_RSA_WITH_NULL_SHA, + TLS_RSA_WITH_RC4_128_MD5, + TLS_RSA_WITH_RC4_128_SHA, + TLS_RSA_WITH_3DES_EDE_CBC_SHA, + TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA, + TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA, + TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA, + TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, + TLS_DH_ANON_WITH_RC4_128_MD5, + TLS_DH_ANON_WITH_3DES_EDE_CBC_SHA, + TLS_RSA_WITH_AES_128_CBC_SHA, + TLS_DH_DSS_WITH_AES_128_CBC_SHA, + TLS_DH_RSA_WITH_AES_128_CBC_SHA, + TLS_DHE_DSS_WITH_AES_128_CBC_SHA, + TLS_DHE_RSA_WITH_AES_128_CBC_SHA, + TLS_DH_ANON_WITH_AES_128_CBC_SHA, + TLS_RSA_WITH_AES_256_CBC_SHA, + TLS_DH_DSS_WITH_AES_256_CBC_SHA, + TLS_DH_RSA_WITH_AES_256_CBC_SHA, + TLS_DHE_DSS_WITH_AES_256_CBC_SHA, + TLS_DHE_RSA_WITH_AES_256_CBC_SHA, + TLS_DH_ANON_WITH_AES_256_CBC_SHA, + TLS_RSA_WITH_NULL_SHA256, + TLS_RSA_WITH_AES_128_CBC_SHA256, + TLS_RSA_WITH_AES_256_CBC_SHA256, + TLS_DH_DSS_WITH_AES_128_CBC_SHA256, + TLS_DH_RSA_WITH_AES_128_CBC_SHA256, + TLS_DHE_DSS_WITH_AES_128_CBC_SHA256, + TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, + TLS_DH_DSS_WITH_AES_256_CBC_SHA256, + TLS_DH_RSA_WITH_AES_256_CBC_SHA256, + TLS_DHE_DSS_WITH_AES_256_CBC_SHA256, + TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, + TLS_DH_ANON_WITH_AES_128_CBC_SHA256, + TLS_DH_ANON_WITH_AES_256_CBC_SHA256, + TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, + TLS_DHE_RSA_WITH_AES_256_GCM_SHA384, + TLS_DHE_PSK_WITH_AES_128_GCM_SHA256, + TLS_DHE_PSK_WITH_AES_256_GCM_SHA384, + TLS_AES_128_GCM_SHA256, + TLS_AES_256_GCM_SHA384, + TLS_CHACHA20_POLY1305_SHA256, + TLS_AES_128_CCM_SHA256, + TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + TLS_DHE_RSA_WITH_AES_128_CCM, + TLS_DHE_RSA_WITH_AES_256_CCM, + TLS_DHE_PSK_WITH_AES_128_CCM, + TLS_DHE_PSK_WITH_AES_256_CCM, + TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, + TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256, + TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256, + TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256, + TLS_ECDHE_PSK_WITH_AES_256_GCM_SHA384, + TLS_ECDHE_PSK_WITH_AES_128_CCM_SHA256 + }) + public @interface TlsCipherSuite {} + + // Cipher suites for TLS v1.2 per RFC5246 + public static final int TLS_NULL_WITH_NULL_NULL = 0x0000; + public static final int TLS_RSA_WITH_NULL_MD5 = 0x0001; + public static final int TLS_RSA_WITH_NULL_SHA = 0x0002; + public static final int TLS_RSA_WITH_RC4_128_MD5 = 0x0004; + public static final int TLS_RSA_WITH_RC4_128_SHA = 0x0005; + public static final int TLS_RSA_WITH_3DES_EDE_CBC_SHA = 0x000A; + public static final int TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA = 0x000D; + public static final int TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA = 0x0010; + public static final int TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA = 0x0013; + public static final int TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA = 0x0016; + public static final int TLS_DH_ANON_WITH_RC4_128_MD5 = 0x0018; + public static final int TLS_DH_ANON_WITH_3DES_EDE_CBC_SHA = 0x001B; + public static final int TLS_RSA_WITH_AES_128_CBC_SHA = 0x002F; + public static final int TLS_DH_DSS_WITH_AES_128_CBC_SHA = 0x0030; + public static final int TLS_DH_RSA_WITH_AES_128_CBC_SHA = 0x0031; + public static final int TLS_DHE_DSS_WITH_AES_128_CBC_SHA = 0x0032; + public static final int TLS_DHE_RSA_WITH_AES_128_CBC_SHA = 0x0033; + public static final int TLS_DH_ANON_WITH_AES_128_CBC_SHA = 0x0034; + public static final int TLS_RSA_WITH_AES_256_CBC_SHA = 0x0035; + public static final int TLS_DH_DSS_WITH_AES_256_CBC_SHA = 0x0036; + public static final int TLS_DH_RSA_WITH_AES_256_CBC_SHA = 0x0037; + public static final int TLS_DHE_DSS_WITH_AES_256_CBC_SHA = 0x0038; + public static final int TLS_DHE_RSA_WITH_AES_256_CBC_SHA = 0x0039; + public static final int TLS_DH_ANON_WITH_AES_256_CBC_SHA = 0x003A; + public static final int TLS_RSA_WITH_NULL_SHA256 = 0x003B; + public static final int TLS_RSA_WITH_AES_128_CBC_SHA256 = 0x003C; + public static final int TLS_RSA_WITH_AES_256_CBC_SHA256 = 0x003D; + public static final int TLS_DH_DSS_WITH_AES_128_CBC_SHA256 = 0x003E; + public static final int TLS_DH_RSA_WITH_AES_128_CBC_SHA256 = 0x003F; + public static final int TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 = 0x0040; + public static final int TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 = 0x0067; + public static final int TLS_DH_DSS_WITH_AES_256_CBC_SHA256 = 0x0068; + public static final int TLS_DH_RSA_WITH_AES_256_CBC_SHA256 = 0x0069; + public static final int TLS_DHE_DSS_WITH_AES_256_CBC_SHA256 = 0x006A; + public static final int TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 = 0x006B; + public static final int TLS_DH_ANON_WITH_AES_128_CBC_SHA256 = 0x006C; + public static final int TLS_DH_ANON_WITH_AES_256_CBC_SHA256 = 0x006D; + + // Cipher suites for TLS v1.3 per RFC8446 and recommended by IANA + public static final int TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 = 0x009E; + public static final int TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 = 0x009F; + public static final int TLS_DHE_PSK_WITH_AES_128_GCM_SHA256 = 0x00AA; + public static final int TLS_DHE_PSK_WITH_AES_256_GCM_SHA384 = 0x00AB; + public static final int TLS_AES_128_GCM_SHA256 = 0x1301; + public static final int TLS_AES_256_GCM_SHA384 = 0x1302; + public static final int TLS_CHACHA20_POLY1305_SHA256 = 0x1303; + public static final int TLS_AES_128_CCM_SHA256 = 0x1304; + public static final int TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 = 0xC02B; + public static final int TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 = 0xC02C; + public static final int TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 = 0xC02F; + public static final int TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 = 0xC030; + public static final int TLS_DHE_RSA_WITH_AES_128_CCM = 0xC09E; + public static final int TLS_DHE_RSA_WITH_AES_256_CCM = 0xC09F; + public static final int TLS_DHE_PSK_WITH_AES_128_CCM = 0xC0A6; + public static final int TLS_DHE_PSK_WITH_AES_256_CCM = 0xC0A7; + public static final int TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 = 0xCCA8; + public static final int TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 = 0xCCA9; + public static final int TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 = 0xCCAA; + public static final int TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256 = 0xCCAC; + public static final int TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256 = 0xCCAD; + public static final int TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256 = 0xD001; + public static final int TLS_ECDHE_PSK_WITH_AES_256_GCM_SHA384 = 0xD002; + public static final int TLS_ECDHE_PSK_WITH_AES_128_CCM_SHA256 = 0xD005; + + private static final int[] CS_EXPECTED = { + TLS_NULL_WITH_NULL_NULL, + TLS_RSA_WITH_NULL_MD5, + TLS_RSA_WITH_NULL_SHA, + TLS_RSA_WITH_RC4_128_MD5, + TLS_RSA_WITH_RC4_128_SHA, + TLS_RSA_WITH_3DES_EDE_CBC_SHA, + TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA, + TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA, + TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA, + TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, + TLS_DH_ANON_WITH_RC4_128_MD5, + TLS_DH_ANON_WITH_3DES_EDE_CBC_SHA, + TLS_RSA_WITH_AES_128_CBC_SHA, + TLS_DH_DSS_WITH_AES_128_CBC_SHA, + TLS_DH_RSA_WITH_AES_128_CBC_SHA, + TLS_DHE_DSS_WITH_AES_128_CBC_SHA, + TLS_DHE_RSA_WITH_AES_128_CBC_SHA, + TLS_DH_ANON_WITH_AES_128_CBC_SHA, + TLS_RSA_WITH_AES_256_CBC_SHA, + TLS_DH_DSS_WITH_AES_256_CBC_SHA, + TLS_DH_RSA_WITH_AES_256_CBC_SHA, + TLS_DHE_DSS_WITH_AES_256_CBC_SHA, + TLS_DHE_RSA_WITH_AES_256_CBC_SHA, + TLS_DH_ANON_WITH_AES_256_CBC_SHA, + TLS_RSA_WITH_NULL_SHA256, + TLS_RSA_WITH_AES_128_CBC_SHA256, + TLS_RSA_WITH_AES_256_CBC_SHA256, + TLS_DH_DSS_WITH_AES_128_CBC_SHA256, + TLS_DH_RSA_WITH_AES_128_CBC_SHA256, + TLS_DHE_DSS_WITH_AES_128_CBC_SHA256, + TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, + TLS_DH_DSS_WITH_AES_256_CBC_SHA256, + TLS_DH_RSA_WITH_AES_256_CBC_SHA256, + TLS_DHE_DSS_WITH_AES_256_CBC_SHA256, + TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, + TLS_DH_ANON_WITH_AES_128_CBC_SHA256, + TLS_DH_ANON_WITH_AES_256_CBC_SHA256, + TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, + TLS_DHE_RSA_WITH_AES_256_GCM_SHA384, + TLS_DHE_PSK_WITH_AES_128_GCM_SHA256, + TLS_DHE_PSK_WITH_AES_256_GCM_SHA384, + TLS_AES_128_GCM_SHA256, + TLS_AES_256_GCM_SHA384, + TLS_CHACHA20_POLY1305_SHA256, + TLS_AES_128_CCM_SHA256, + TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + TLS_DHE_RSA_WITH_AES_128_CCM, + TLS_DHE_RSA_WITH_AES_256_CCM, + TLS_DHE_PSK_WITH_AES_128_CCM, + TLS_DHE_PSK_WITH_AES_256_CCM, + TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, + TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256, + TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256, + TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256, + TLS_ECDHE_PSK_WITH_AES_256_GCM_SHA384, + TLS_ECDHE_PSK_WITH_AES_128_CCM_SHA256 + }; + + /** + * TLS supported groups required by TS 33.210 + */ + public static final int GROUP_SECP256R1 = 23; + public static final int GROUP_SECP384R1 = 24; + public static final int GROUP_X25519 = 29; + public static final int GROUP_X448 = 30; + + /** + * Signature algorithms shall be supported as per TS 33.210 + */ + public static final int SIG_RSA_PKCS1_SHA1 = 0X0201; + public static final int SIG_ECDSA_SHA1 = 0X0203; + public static final int SIG_RSA_PKCS1_SHA256 = 0X0401; + public static final int SIG_ECDSA_SECP256R1_SHA256 = 0X0403; + public static final int SIG_RSA_PKCS1_SHA256_LEGACY = 0X0420; + public static final int SIG_RSA_PKCS1_SHA384 = 0X0501; + public static final int SIG_ECDSA_SECP384R1_SHA384 = 0X0503; + public static final int SIG_RSA_PKCS1_SHA384_LEGACY = 0X0520; + public static final int SIG_RSA_PKCS1_SHA512 = 0X0601; + public static final int SIG_ECDSA_SECP521R1_SHA512 = 0X0603; + public static final int SIG_RSA_PKCS1_SHA512_LEGACY = 0X0620; + public static final int SIG_RSA_PSS_RSAE_SHA256 = 0X0804; + public static final int SIG_RSA_PSS_RSAE_SHA384 = 0X0805; + public static final int SIG_RSA_PSS_RSAE_SHA512 = 0X0806; + public static final int SIG_ECDSA_BRAINPOOLP256R1TLS13_SHA256 = 0X081A; + public static final int SIG_ECDSA_BRAINPOOLP384R1TLS13_SHA384 = 0X081B; + public static final int SIG_ECDSA_BRAINPOOLP512R1TLS13_SHA512 = 0X081C; + + /** + * Returns whether the TLS cipher suite id is supported + */ + public static boolean isTlsCipherSuiteSupported(int csId) { + return Arrays.binarySearch(CS_EXPECTED, csId) >= 0; + } +} diff --git a/telephony/java/android/telephony/gba/UaSecurityProtocolIdentifier.aidl b/telephony/java/android/telephony/gba/UaSecurityProtocolIdentifier.aidl new file mode 100644 index 000000000000..a71e860fd54c --- /dev/null +++ b/telephony/java/android/telephony/gba/UaSecurityProtocolIdentifier.aidl @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2020 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.gba; + +parcelable UaSecurityProtocolIdentifier; diff --git a/telephony/java/android/telephony/gba/UaSecurityProtocolIdentifier.java b/telephony/java/android/telephony/gba/UaSecurityProtocolIdentifier.java new file mode 100644 index 000000000000..c1418758f64f --- /dev/null +++ b/telephony/java/android/telephony/gba/UaSecurityProtocolIdentifier.java @@ -0,0 +1,436 @@ +/* + * Copyright (C) 2020 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.gba; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.os.Parcel; +import android.os.Parcelable; +import android.telephony.gba.TlsParams.TlsCipherSuite; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.nio.ByteBuffer; +import java.util.Objects; + +/** + * Description of ua security protocol identifier defined in 3GPP TS 33.220 H.2 + * @hide + */ +@SystemApi +public final class UaSecurityProtocolIdentifier implements Parcelable { + + /** + * Organization code defined in 3GPP TS 33.220 H.3 + * + * @hide + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"ORG_"}, value = { + ORG_NONE, + ORG_3GPP, + ORG_3GPP2, + ORG_OMA, + ORG_GSMA, + ORG_LOCAL}) + public @interface OrganizationCode {} + + /** + * Organization octet value for default ua security protocol + */ + public static final int ORG_NONE = 0; + /** + * Organization octet value for 3GPP ua security protocol + */ + public static final int ORG_3GPP = 0x01; + /** + * Organization octet value for 3GPP2 ua security protocol + */ + public static final int ORG_3GPP2 = 0x02; + /** + * Organization octet value for OMA ua security protocol + */ + public static final int ORG_OMA = 0x03; + /** + * Organization octet value for GSMA ua security protocol + */ + public static final int ORG_GSMA = 0x04; + /** + * Internal organization octet value for local/experimental protocols + */ + public static final int ORG_LOCAL = 0xFF; + + /** + * 3GPP UA Security Protocol ID defined in 3GPP TS 33.220 H.3 + * + * @hide + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"UA_SECURITY_PROTOCOL_3GPP_"}, value = { + UA_SECURITY_PROTOCOL_3GPP_SUBSCRIBER_CERTIFICATE, + UA_SECURITY_PROTOCOL_3GPP_MBMS, + UA_SECURITY_PROTOCOL_3GPP_HTTP_DIGEST_AUTHENTICATION, + UA_SECURITY_PROTOCOL_3GPP_HTTP_BASED_MBMS, + UA_SECURITY_PROTOCOL_3GPP_SIP_BASED_MBMS, + UA_SECURITY_PROTOCOL_3GPP_GENERIC_PUSH_LAYER, + UA_SECURITY_PROTOCOL_3GPP_IMS_MEDIA_PLANE, + UA_SECURITY_PROTOCOL_3GPP_GENERATION_TMPI, + UA_SECURITY_PROTOCOL_3GPP_TLS_DEFAULT, + UA_SECURITY_PROTOCOL_3GPP_TLS_BROWSER}) + public @interface UaSecurityProtocol3gpp {} + + /** + * Security protocol param according to TS 33.221 as described in TS + * 33.220 Annex H. Mapped to byte stream "0x01,0x00,0x00,0x00,0x00". + */ + public static final int UA_SECURITY_PROTOCOL_3GPP_SUBSCRIBER_CERTIFICATE = 0; + + /** + * Security protocol param according to TS 33.246 for Multimedia + * broadcast/Multimedia services (MBMS) as described in TS + * 33.220 Annex H. Mapped to byte stream "0x01,0x00,0x00,0x00,0x01". + */ + public static final int UA_SECURITY_PROTOCOL_3GPP_MBMS = 1; + + /** + * Security protocol param based on HTTP digest authentication + * according to TS 24.109 as described in TS 33.220 Annex H. Mapped to + * byte stream "0x01,0x00,0x00,0x00,0x02". + */ + public static final int UA_SECURITY_PROTOCOL_3GPP_HTTP_DIGEST_AUTHENTICATION = 2; + + /** + * Security protocol param used with HTTP-based security procedures for + * Multimedia broadcast/Multimedia services (MBMS) user services + * according to TS 26.237 as described in TS 33.220 Annex H. + * Mapped to byte stream "0x01,0x00,0x00,0x00,0x03". + */ + public static final int UA_SECURITY_PROTOCOL_3GPP_HTTP_BASED_MBMS = 3; + + /** + * Security protocol param used with SIP-based security procedures for + * Multimedia broadcast/Multimedia services (MBMS) user services + * according to TS 26.237 as described in TS 33.220 Annex H. + * Mapped to byte stream "0x01,0x00,0x00,0x00,0x04". + */ + public static final int UA_SECURITY_PROTOCOL_3GPP_SIP_BASED_MBMS = 4; + + /** + * Security protocol param used with Generic Push Layer according to TS + * 33.224 as described in TS 33.220 Annex H. Mapped to byte stream + * "0x01,0x00,0x00,0x00,0x05". + */ + public static final int UA_SECURITY_PROTOCOL_3GPP_GENERIC_PUSH_LAYER = 5; + + /** + * Security protocol param used for IMS UE to KMS http based message + * exchanges according to "IMS media plane security", TS 33.328 as + * described in TS 33.220 Annex H. Mapped to byte stream + * "0x01,0x00,0x00,0x00,0x06". + */ + public static final int UA_SECURITY_PROTOCOL_3GPP_IMS_MEDIA_PLANE = 6; + + /** + * Security protocol param used for Generation of Temporary IP + * Multimedia Private Identity (TMPI) according to TS 33.220 Annex B.4 + * Mapped to byte stream "0x01,0x00,0x00,0x01,0x00". + */ + public static final int UA_SECURITY_PROTOCOL_3GPP_GENERATION_TMPI = 0x0100; + + /** + * Security protocol param used for Shared key-based UE authentication with + * certificate-based NAF authentication, according to TS 33.222 section 5.3, + * or Shared key-based mutual authentication between UE and NAF, according to + * TS 33.222 section 5.4. Mapped to byte stream "0x01,0x00,0x01,yy,zz". + * "yy, zz" is the TLS CipherSuite code. + */ + public static final int UA_SECURITY_PROTOCOL_3GPP_TLS_DEFAULT = 0x010000; + + /** + * Security protocol param used for Shared key-based UE authentication with + * certificate-based NAF authentication, according to TS 33.222 Annex D. + * Mapped to byte stream "0x01,0x00,0x02,yy,zz". + * "yy, zz" is the TLS CipherSuite code. + */ + public static final int UA_SECURITY_PROTOCOL_3GPP_TLS_BROWSER = 0x020000; + + private static final int PROTOCOL_SIZE = 5; + private static final int[] sUaSp3gppIds = new int[] { + UA_SECURITY_PROTOCOL_3GPP_SUBSCRIBER_CERTIFICATE, + UA_SECURITY_PROTOCOL_3GPP_MBMS, + UA_SECURITY_PROTOCOL_3GPP_HTTP_DIGEST_AUTHENTICATION, + UA_SECURITY_PROTOCOL_3GPP_HTTP_BASED_MBMS, + UA_SECURITY_PROTOCOL_3GPP_SIP_BASED_MBMS, + UA_SECURITY_PROTOCOL_3GPP_GENERIC_PUSH_LAYER, + UA_SECURITY_PROTOCOL_3GPP_IMS_MEDIA_PLANE, + UA_SECURITY_PROTOCOL_3GPP_GENERATION_TMPI, + UA_SECURITY_PROTOCOL_3GPP_TLS_DEFAULT, + UA_SECURITY_PROTOCOL_3GPP_TLS_BROWSER}; + + private int mOrg; + private int mProtocol; + private int mTlsCipherSuite; + + private UaSecurityProtocolIdentifier() {} + + private UaSecurityProtocolIdentifier(UaSecurityProtocolIdentifier sp) { + mOrg = sp.mOrg; + mProtocol = sp.mProtocol; + mTlsCipherSuite = sp.mTlsCipherSuite; + } + + /** + * Returns the byte array representing the ua security protocol + */ + @NonNull + public byte[] toByteArray() { + byte[] data = new byte[PROTOCOL_SIZE]; + ByteBuffer buf = ByteBuffer.wrap(data); + buf.put((byte) mOrg); + buf.putInt(mProtocol | mTlsCipherSuite); + return data; + } + + /** + * Returns the organization code + */ + public @OrganizationCode int getOrg() { + return mOrg; + } + + /** + * Returns the security procotol id + * + * <p>Note that only 3GPP UA Security Protocols are supported for now + */ + public @UaSecurityProtocol3gpp int getProtocol() { + return mProtocol; + } + + /** + * Returns the TLS cipher suite + */ + public @TlsCipherSuite int getTlsCipherSuite() { + return mTlsCipherSuite; + } + + /** + * {@link Parcelable#writeToParcel} + */ + @Override + public void writeToParcel(@NonNull Parcel out, int flags) { + out.writeInt(mOrg); + out.writeInt(mProtocol); + out.writeInt(mTlsCipherSuite); + } + + /** + * {@link Parcelable.Creator} + * + */ + public static final @NonNull Parcelable.Creator< + UaSecurityProtocolIdentifier> CREATOR = new Creator<UaSecurityProtocolIdentifier>() { + @Nullable + @Override + public UaSecurityProtocolIdentifier createFromParcel(Parcel in) { + int org = in.readInt(); + int protocol = in.readInt(); + int cs = in.readInt(); + if (org < 0 || protocol < 0 || cs < 0) { + return null; + } + Builder builder = new Builder(); + try { + if (org > 0) { + builder.setOrg(org); + } + if (protocol > 0) { + builder.setProtocol(protocol); + } + if (cs > 0) { + builder.setTlsCipherSuite(cs); + } + } catch (IllegalArgumentException e) { + return null; + } + return builder.build(); + } + + @NonNull + @Override + public UaSecurityProtocolIdentifier[] newArray(int size) { + return new UaSecurityProtocolIdentifier[size]; + } + }; + + /** + * {@link Parcelable#describeContents} + */ + @Override + public int describeContents() { + return 0; + } + + @Override + public String toString() { + return "UaSecurityProtocolIdentifier[" + mOrg + " , " + (mProtocol | mTlsCipherSuite) + "]"; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof UaSecurityProtocolIdentifier)) { + return false; + } + + UaSecurityProtocolIdentifier other = (UaSecurityProtocolIdentifier) obj; + + return mOrg == other.mOrg && mProtocol == other.mProtocol + && mTlsCipherSuite == other.mTlsCipherSuite; + } + + @Override + public int hashCode() { + return Objects.hash(mOrg, mProtocol, mTlsCipherSuite); + } + + private boolean isTlsSupported() { + //TODO May update to support non 3gpp protocol in the future + if (mOrg == ORG_3GPP && (mProtocol == UA_SECURITY_PROTOCOL_3GPP_TLS_DEFAULT + || mProtocol == UA_SECURITY_PROTOCOL_3GPP_TLS_BROWSER)) { + return true; + } + + return false; + } + + /** + * Builder class for UaSecurityProtocolIdentifier + */ + public static final class Builder { + private final UaSecurityProtocolIdentifier mSp; + + /** + * Creates a Builder with default UaSecurityProtocolIdentifier, a.k.a 0x00 00 00 00 00 + */ + public Builder() { + mSp = new UaSecurityProtocolIdentifier(); + } + + /** + * Creates a Builder from a UaSecurityProtocolIdentifier + */ + public Builder(@NonNull final UaSecurityProtocolIdentifier sp) { + Objects.requireNonNull(sp); + mSp = new UaSecurityProtocolIdentifier(sp); + } + + /** + * Sets the organization code + * + * @param orgCode the organization code with the following value + * <ol> + * <li>{@link #ORG_NONE} </li> + * <li>{@link #ORG_3GPP} </li> + * <li>{@link #ORG_3GPP2} </li> + * <li>{@link #ORG_OMA} </li> + * <li>{@link #ORG_GSMA} </li> + * <li>{@link #ORG_LOCAL} </li> + * </ol> + * @throws IllegalArgumentException if it is not one of the value above. + * + * <p>Note that this method will reset the security protocol and TLS cipher suite + * if they have been set. + */ + @NonNull + public Builder setOrg(@OrganizationCode int orgCode) { + if (orgCode < ORG_NONE || orgCode > ORG_LOCAL) { + throw new IllegalArgumentException("illegal organization code"); + } + mSp.mOrg = orgCode; + mSp.mProtocol = 0; + mSp.mTlsCipherSuite = 0; + return this; + } + + /** + * Sets the UA security protocol for 3GPP + * + * @param protocol only 3GPP ua security protocol ID is supported for now, which + * is one of the following value + * <ol> + * <li>{@link #UA_SECURITY_PROTOCOL_3GPP_SUBSCRIBER_CERTIFICATE} </li> + * <li>{@link #UA_SECURITY_PROTOCOL_3GPP_MBMS} </li> + * <li>{@link #UA_SECURITY_PROTOCOL_3GPP_HTTP_DIGEST_AUTHENTICATION} </li> + * <li>{@link #UA_SECURITY_PROTOCOL_3GPP_HTTP_BASED_MBMS} </li> + * <li>{@link #UA_SECURITY_PROTOCOL_3GPP_SIP_BASED_MBMS} </li> + * <li>{@link #UA_SECURITY_PROTOCOL_3GPP_GENERIC_PUSH_LAYER} </li> + * <li>{@link #UA_SECURITY_PROTOCOL_3GPP_IMS_MEDIA_PLANE} </li> + * <li>{@link #UA_SECURITY_PROTOCOL_3GPP_GENERATION_TMPI} </li> + * <li>{@link #UA_SECURITY_PROTOCOL_3GPP_TLS_DEFAULT} </li> + * <li>{@link #UA_SECURITY_PROTOCOL_3GPP_TLS_BROWSER} </li> + * </ol> + * @throws IllegalArgumentException if the protocol is not one of the value above. + * + * <p>Note that this method will reset TLS cipher suite if it has been set. + */ + @NonNull + public Builder setProtocol(@UaSecurityProtocol3gpp int protocol) { + //TODO May update to support non 3gpp protocol in the future + if (protocol < UA_SECURITY_PROTOCOL_3GPP_SUBSCRIBER_CERTIFICATE + || (protocol > UA_SECURITY_PROTOCOL_3GPP_IMS_MEDIA_PLANE + && protocol != UA_SECURITY_PROTOCOL_3GPP_GENERATION_TMPI + && protocol != UA_SECURITY_PROTOCOL_3GPP_TLS_DEFAULT + && protocol != UA_SECURITY_PROTOCOL_3GPP_TLS_BROWSER) + || mSp.mOrg != ORG_3GPP) { + throw new IllegalArgumentException("illegal protocol code"); + } + mSp.mProtocol = protocol; + mSp.mTlsCipherSuite = 0; + return this; + } + + /** + * Sets the UA security protocol for 3GPP + * + * @param cs TLS cipher suite value defined by {@link TlsParams#TlsCipherSuite} + * @throws IllegalArgumentException if it is not a 3GPP ua security protocol, + * the protocol does not support TLS, or does not support the cipher suite. + */ + @NonNull + public Builder setTlsCipherSuite(@TlsCipherSuite int cs) { + if (!mSp.isTlsSupported()) { + throw new IllegalArgumentException("The protocol does not support TLS"); + } + if (!TlsParams.isTlsCipherSuiteSupported(cs)) { + throw new IllegalArgumentException("TLS cipher suite is not supported"); + } + mSp.mTlsCipherSuite = cs; + return this; + } + + /** + * Builds the instance of UaSecurityProtocolIdentifier + * + * @return the built instance of UaSecurityProtocolIdentifier + */ + @NonNull + public UaSecurityProtocolIdentifier build() { + return new UaSecurityProtocolIdentifier(mSp); + } + } +} diff --git a/telephony/java/android/telephony/gsm/GsmCellLocation.java b/telephony/java/android/telephony/gsm/GsmCellLocation.java index 30cea0e6dd59..2eee4ce371a0 100644 --- a/telephony/java/android/telephony/gsm/GsmCellLocation.java +++ b/telephony/java/android/telephony/gsm/GsmCellLocation.java @@ -23,7 +23,10 @@ import android.telephony.CellLocation; /** * Represents the cell location on a GSM phone. + * + * @deprecated use {@link android.telephony.CellIdentity CellIdentity}. */ +@Deprecated public class GsmCellLocation extends CellLocation { private int mLac; private int mCid; @@ -55,7 +58,7 @@ public class GsmCellLocation extends CellLocation { } /** - * @return gsm cell id, -1 if unknown, 0xffff max legal value + * @return gsm cell id, -1 if unknown or invalid, 0xffff max legal value */ public int getCid() { return mCid; diff --git a/telephony/java/android/telephony/gsm/OWNERS b/telephony/java/android/telephony/gsm/OWNERS new file mode 100644 index 000000000000..6aa399d9ebfb --- /dev/null +++ b/telephony/java/android/telephony/gsm/OWNERS @@ -0,0 +1,5 @@ +# Bug component: 20868 + +rgreenwalt@google.com +tgunn@google.com +amitmahajan@google.com diff --git a/telephony/java/android/telephony/ims/AudioCodecAttributes.aidl b/telephony/java/android/telephony/ims/AudioCodecAttributes.aidl new file mode 100644 index 000000000000..bbab548bcd37 --- /dev/null +++ b/telephony/java/android/telephony/ims/AudioCodecAttributes.aidl @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2020 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.ims; + +parcelable AudioCodecAttributes; diff --git a/telephony/java/android/telephony/ims/AudioCodecAttributes.java b/telephony/java/android/telephony/ims/AudioCodecAttributes.java new file mode 100644 index 000000000000..7b6ab00b93e4 --- /dev/null +++ b/telephony/java/android/telephony/ims/AudioCodecAttributes.java @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2020 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.ims; + +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.Range; + +/** + * Parcelable object to handle audio codec attributes. + * It provides the audio codec bitrate, bandwidth and their upper/lower bound. + * + * @hide + */ +@SystemApi +public final class AudioCodecAttributes implements Parcelable { + // The audio codec bitrate in kbps. + private float mBitrateKbps; + // The range of the audio codec bitrate in kbps. + private Range<Float> mBitrateRangeKbps; + // The audio codec bandwidth in kHz. + private float mBandwidthKhz; + // The range of the audio codec bandwidth in kHz. + private Range<Float> mBandwidthRangeKhz; + + + /** + * Constructor. + * + * @param bitrateKbps The audio codec bitrate in kbps. + * @param bitrateRangeKbps The range of the audio codec bitrate in kbps. + * @param bandwidthKhz The audio codec bandwidth in kHz. + * @param bandwidthRangeKhz The range of the audio codec bandwidth in kHz. + */ + + public AudioCodecAttributes(float bitrateKbps, @NonNull Range<Float> bitrateRangeKbps, + float bandwidthKhz, @NonNull Range<Float> bandwidthRangeKhz) { + mBitrateKbps = bitrateKbps; + mBitrateRangeKbps = bitrateRangeKbps; + mBandwidthKhz = bandwidthKhz; + mBandwidthRangeKhz = bandwidthRangeKhz; + } + + private AudioCodecAttributes(Parcel in) { + mBitrateKbps = in.readFloat(); + mBitrateRangeKbps = new Range<>(in.readFloat(), in.readFloat()); + mBandwidthKhz = in.readFloat(); + mBandwidthRangeKhz = new Range<>(in.readFloat(), in.readFloat()); + } + + @Override + public void writeToParcel(@NonNull Parcel out, int flags) { + out.writeFloat(mBitrateKbps); + out.writeFloat(mBitrateRangeKbps.getLower()); + out.writeFloat(mBitrateRangeKbps.getUpper()); + out.writeFloat(mBandwidthKhz); + out.writeFloat(mBandwidthRangeKhz.getLower()); + out.writeFloat(mBandwidthRangeKhz.getUpper()); + } + + @Override + public int describeContents() { + return 0; + } + + public static final @NonNull Creator<AudioCodecAttributes> CREATOR = + new Creator<AudioCodecAttributes>() { + @Override + public AudioCodecAttributes createFromParcel(Parcel in) { + return new AudioCodecAttributes(in); + } + + @Override + public AudioCodecAttributes[] newArray(int size) { + return new AudioCodecAttributes[size]; + } + }; + + /** + * @return the exact value of the audio codec bitrate in kbps. + */ + public float getBitrateKbps() { + return mBitrateKbps; + } + + /** + * @return the range of the audio codec bitrate in kbps + */ + public @NonNull Range<Float> getBitrateRangeKbps() { + return mBitrateRangeKbps; + } + + /** + * @return the exact value of the audio codec bandwidth in kHz. + */ + public float getBandwidthKhz() { + return mBandwidthKhz; + } + + /** + * @return the range of the audio codec bandwidth in kHz. + */ + public @NonNull Range<Float> getBandwidthRangeKhz() { + return mBandwidthRangeKhz; + } + + @NonNull + @Override + public String toString() { + return "{ bitrateKbps=" + mBitrateKbps + + ", bitrateRangeKbps=" + mBitrateRangeKbps + + ", bandwidthKhz=" + mBandwidthKhz + + ", bandwidthRangeKhz=" + mBandwidthRangeKhz + " }"; + } +} diff --git a/telephony/java/android/telephony/ims/DelegateMessageCallback.java b/telephony/java/android/telephony/ims/DelegateMessageCallback.java new file mode 100644 index 000000000000..a008cfdbc535 --- /dev/null +++ b/telephony/java/android/telephony/ims/DelegateMessageCallback.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2020 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.ims; + +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.telephony.ims.stub.SipDelegate; + +/** + * Callback interface provided to the SipTransport implementation to notify a remote application of + * the following: + * <ul> + * <li>A new incoming SIP message associated with the feature tags the SipDelegate registered + * with has been received or an in-dialog request to this SipDelegate has been received.</li> + * <li>Acknowledge that an outgoing SIP message from the RCS application has been sent + * successfully or notify the application of the reason why it was not sent</li> + * </ul> + * @hide + */ +@SystemApi +public interface DelegateMessageCallback { + + /** + * Sends a new incoming SIP message to the remote application for processing. + */ + void onMessageReceived(@NonNull SipMessage message); + + /** + * Notifies the remote application that a previous request to send a SIP message using + * {@link SipDelegate#sendMessage} has succeeded. + * + * @param viaTransactionId The transaction ID found in the via header field of the + * previously sent {@link SipMessage}. + */ + void onMessageSent(@NonNull String viaTransactionId); + + /** + * Notifies the remote application that a previous request to send a SIP message using + * {@link SipDelegate#sendMessage} has failed. + * + * @param viaTransactionId The Transaction ID found in the via header field of the previously + * sent {@link SipMessage}. + * @param reason The reason for the failure. + */ + void onMessageSendFailure(@NonNull String viaTransactionId, + @SipDelegateManager.MessageFailureReason int reason); +} diff --git a/telephony/java/android/telephony/ims/DelegateRegistrationState.aidl b/telephony/java/android/telephony/ims/DelegateRegistrationState.aidl new file mode 100644 index 000000000000..756ea920d06a --- /dev/null +++ b/telephony/java/android/telephony/ims/DelegateRegistrationState.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2020 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.ims; + +parcelable DelegateRegistrationState; diff --git a/telephony/java/android/telephony/ims/DelegateRegistrationState.java b/telephony/java/android/telephony/ims/DelegateRegistrationState.java new file mode 100644 index 000000000000..c00c741a0d60 --- /dev/null +++ b/telephony/java/android/telephony/ims/DelegateRegistrationState.java @@ -0,0 +1,330 @@ +/* + * Copyright (C) 2020 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.ims; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.SuppressLint; +import android.annotation.SystemApi; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.ArraySet; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Objects; +import java.util.Set; + +/** + * Contains the full state of the IMS feature tags associated with a SipDelegate and managed by the + * ImsService. + * @hide + */ +@SystemApi +public final class DelegateRegistrationState implements Parcelable { + + /** + * This feature tag has been deregistered for an unknown reason. Outgoing out-of-dialog SIP + * messages associated with feature tags that are not registered will fail. + */ + public static final int DEREGISTERED_REASON_UNKNOWN = 0; + + /** + * This feature tag has been deregistered because it is not provisioned to be used on this radio + * access technology or PDN. Outgoing out-of-dialog SIP messages associated with feature tags + * that are not registered will fail. + * <p> + * There may be new incoming SIP dialog requests on a feature that that is not provisioned. It + * is still expected that the SipDelegateConnection responds to the request. + */ + public static final int DEREGISTERED_REASON_NOT_PROVISIONED = 1; + + /** + * This feature tag has been deregistered because IMS has been deregistered. All outgoing SIP + * messages will fail until IMS registration occurs. + */ + public static final int DEREGISTERED_REASON_NOT_REGISTERED = 2; + + /** + * This feature tag is being deregistered because the PDN that the IMS registration is on is + *changing. + * All open SIP dialogs need to be closed before the PDN change can proceed using + * {@link SipDelegateConnection#cleanupSession(String)}. + */ + public static final int DEREGISTERING_REASON_PDN_CHANGE = 3; + + /** + * This feature tag is being deregistered due to a provisioning change. This can be triggered by + * many things, such as a provisioning change triggered by the carrier network, a radio access + * technology change by the modem causing a different set of feature tags to be provisioned, or + * a user triggered hange, such as data being enabled/disabled. + * <p> + * All open SIP dialogs associated with the new deprovisioned feature tag need to be closed + * using {@link SipDelegateConnection#cleanupSession(String)} before the IMS registration + * modification can proceed. + */ + public static final int DEREGISTERING_REASON_PROVISIONING_CHANGE = 4; + + /** + * This feature tag is deregistering because the SipDelegate associated with this feature tag + * needs to change its supported feature set. + * <p> + * All open SIP Dialogs associated with this feature tag must be closed + * using {@link SipDelegateConnection#cleanupSession(String)} before this operation can proceed. + */ + public static final int DEREGISTERING_REASON_FEATURE_TAGS_CHANGING = 5; + + /** + * This feature tag is deregistering because the SipDelegate is in the process of being + * destroyed. + * <p> + * All open SIP Dialogs associated with this feature tag must be closed + * using {@link SipDelegateConnection#cleanupSession(String)} before this operation can proceed. + */ + public static final int DEREGISTERING_REASON_DESTROY_PENDING = 6; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = "DEREGISTERED_REASON_", value = { + DEREGISTERED_REASON_UNKNOWN, + DEREGISTERED_REASON_NOT_PROVISIONED, + DEREGISTERED_REASON_NOT_REGISTERED + }) + public @interface DeregisteredReason {} + + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = "DEREGISTERING_REASON_", value = { + DEREGISTERING_REASON_PDN_CHANGE, + DEREGISTERING_REASON_PROVISIONING_CHANGE, + DEREGISTERING_REASON_FEATURE_TAGS_CHANGING, + DEREGISTERING_REASON_DESTROY_PENDING + }) + public @interface DeregisteringReason {} + + private ArraySet<String> mRegisteredTags = new ArraySet<>(); + private final ArraySet<FeatureTagState> mDeregisteringTags = new ArraySet<>(); + private final ArraySet<FeatureTagState> mDeregisteredTags = new ArraySet<>(); + + /** + * Builder used to create new instances of {@link DelegateRegistrationState}. + */ + public static final class Builder { + + private final DelegateRegistrationState mState; + + /* Create a new instance of {@link Builder} */ + public Builder() { + mState = new DelegateRegistrationState(); + } + + /** + * Add a feature tag that is currently included in the current network IMS Registration. + * @param featureTag The IMS media feature tag included in the current IMS registration. + * @return The in-progress Builder instance for RegistrationState. + */ + public @NonNull Builder addRegisteredFeatureTag(@NonNull String featureTag) { + mState.mRegisteredTags.add(featureTag); + return this; + } + + /** + * Add a list of feature tags that are currently included in the current network IMS + * Registration. + * @param featureTags The IMS media feature tags included in the current IMS registration. + * @return The in-progress Builder instance for RegistrationState. + */ + @SuppressLint("MissingGetterMatchingBuilder") + public @NonNull Builder addRegisteredFeatureTags(@NonNull Set<String> featureTags) { + mState.mRegisteredTags.addAll(featureTags); + return this; + } + + /** + * Add a feature tag that is in the current network IMS Registration, but is in the progress + * of being deregistered and requires action from the RCS application before the IMS + * registration can be modified. + * + * See {@link DeregisteringReason} for more information regarding what is required by the + * RCS application to proceed. + * + * @param featureTag The media feature tag that has limited or no availability due to its + * current deregistering state. + * @param reason The reason why the media feature tag has moved to the deregistering state. + * The availability of the feature tag depends on the {@link DeregisteringReason}. + * @return The in-progress Builder instance for RegistrationState. + */ + public @NonNull Builder addDeregisteringFeatureTag(@NonNull String featureTag, + @DeregisteringReason int reason) { + mState.mDeregisteringTags.add(new FeatureTagState(featureTag, reason)); + return this; + } + + /** + * Add a feature tag that is currently not included in the network RCS registration. See + * {@link DeregisteredReason} for more information regarding the reason for why the feature + * tag is not registered. + * @param featureTag The media feature tag that is not registered. + * @param reason The reason why the media feature tag has been deregistered. + * @return The in-progress Builder instance for RegistrationState. + */ + public @NonNull Builder addDeregisteredFeatureTag(@NonNull String featureTag, + @DeregisteredReason int reason) { + mState.mDeregisteredTags.add(new FeatureTagState(featureTag, reason)); + return this; + } + + /** + * @return the finalized instance. + */ + public @NonNull DelegateRegistrationState build() { + return mState; + } + } + + /** + * The builder should be used to construct a new instance of this class. + */ + private DelegateRegistrationState() {} + + /** + * Used for unparcelling only. + */ + private DelegateRegistrationState(Parcel source) { + mRegisteredTags = (ArraySet<String>) source.readArraySet(null); + readStateFromParcel(source, mDeregisteringTags); + readStateFromParcel(source, mDeregisteredTags); + } + + /** + * Get the feature tags that this SipDelegate is associated with that are currently part of the + * network IMS registration. SIP Messages both in and out of a SIP Dialog may be sent and + * received using these feature tags. + * @return A Set of feature tags that the SipDelegate has associated with that are included in + * the network IMS registration. + */ + public @NonNull Set<String> getRegisteredFeatureTags() { + return new ArraySet<>(mRegisteredTags); + } + + /** + * Get the feature tags that this SipDelegate is associated with that are currently part of the + * network IMS registration but are in the process of being deregistered. + * <p> + * Any incoming SIP messages associated with a feature tag included in this list will still be + * delivered. Outgoing SIP messages that are still in-dialog will be delivered to the + * SipDelegate, but outgoing out-of-dialog SIP messages with a feature tag that is included in + * this list will fail. + * <p> + * The SipDelegate will stay in this state for a limited period of time while it waits for the + * RCS application to perform a specific action. More details on the actions that can cause this + * state as well as the expected response are included in the reason codes and can be found in + * {@link DeregisteringReason}. + * @return A Set of feature tags that the SipDelegate has associated with that are included in + * the network IMS registration but are in the process of deregistering. + */ + public @NonNull Set<FeatureTagState> getDeregisteringFeatureTags() { + return new ArraySet<>(mDeregisteringTags); + } + + /** + * Get the list of feature tags that are associated with this SipDelegate but are not currently + * included in the network IMS registration. + * <p> + * See {@link DeregisteredReason} codes for more information related to the reasons why this may + * occur. + * <p> + * Due to network race conditions, there may still be onditions where an incoming out-of-dialog + * SIP message is delivered for a feature tag that is considered deregistered. Due to this + * condition, in-dialog outgoing SIP messages for deregistered feature tags will still be + * allowed as long as they are in response to a dialog started by a remote party. Any outgoing + * out-of-dialog SIP messages associated with feature tags included in this list will fail to be + * sent. + * @return A list of feature tags that the SipDelegate has associated with that not included in + * the network IMS registration. + */ + public @NonNull Set<FeatureTagState> getDeregisteredFeatureTags() { + return new ArraySet<>(mDeregisteredTags); + } + + + public static final @NonNull Creator<DelegateRegistrationState> CREATOR = + new Creator<DelegateRegistrationState>() { + @Override + public DelegateRegistrationState createFromParcel(Parcel source) { + return new DelegateRegistrationState(source); + } + + @Override + public DelegateRegistrationState[] newArray(int size) { + return new DelegateRegistrationState[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeArraySet(mRegisteredTags); + writeStateToParcel(dest, mDeregisteringTags); + writeStateToParcel(dest, mDeregisteredTags); + } + + private void writeStateToParcel(Parcel dest, Set<FeatureTagState> state) { + dest.writeInt(state.size()); + for (FeatureTagState s : state) { + dest.writeString(s.getFeatureTag()); + dest.writeInt(s.getState()); + } + } + + private void readStateFromParcel(Parcel source, Set<FeatureTagState> emptyState) { + int len = source.readInt(); + for (int i = 0; i < len; i++) { + String ft = source.readString(); + int reason = source.readInt(); + + emptyState.add(new FeatureTagState(ft, reason)); + } + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + DelegateRegistrationState that = (DelegateRegistrationState) o; + return mRegisteredTags.equals(that.mRegisteredTags) + && mDeregisteringTags.equals(that.mDeregisteringTags) + && mDeregisteredTags.equals(that.mDeregisteredTags); + } + + @Override + public int hashCode() { + return Objects.hash(mRegisteredTags, mDeregisteringTags, mDeregisteredTags); + } + + @Override + public String toString() { + return "DelegateRegistrationState{ registered={" + mRegisteredTags + + "}, deregistering={" + mDeregisteringTags + "}, deregistered={" + + mDeregisteredTags + "}}"; + } +} diff --git a/telephony/java/android/telephony/ims/DelegateRequest.aidl b/telephony/java/android/telephony/ims/DelegateRequest.aidl new file mode 100644 index 000000000000..60c990f8258f --- /dev/null +++ b/telephony/java/android/telephony/ims/DelegateRequest.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2020 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.ims; + +parcelable DelegateRequest; diff --git a/telephony/java/android/telephony/ims/DelegateRequest.java b/telephony/java/android/telephony/ims/DelegateRequest.java new file mode 100644 index 000000000000..c322d924182a --- /dev/null +++ b/telephony/java/android/telephony/ims/DelegateRequest.java @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2020 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.ims; + +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.os.Parcel; +import android.os.Parcelable; +import android.telephony.ims.stub.SipDelegate; +import android.util.ArraySet; + +import java.util.ArrayList; +import java.util.Objects; +import java.util.Set; + +/** + * Contains information required for the creation of a {@link SipDelegate} and the associated + * SipDelegateConnection given back to the requesting application. + * @hide + */ +@SystemApi +public final class DelegateRequest implements Parcelable { + + private final ArrayList<String> mFeatureTags; + + /** + * Create a new DelegateRequest, which will be used to create a SipDelegate by the ImsService. + * @param featureTags The list of IMS feature tags that will be associated with the SipDelegate + * created using this DelegateRequest. All feature tags are expected to be in + * the format defined in RCC.07 section 2.6.1.3. + */ + public DelegateRequest(@NonNull Set<String> featureTags) { + if (featureTags == null) { + throw new IllegalStateException("Invalid arguments, featureTags List can not be null"); + } + mFeatureTags = new ArrayList<>(featureTags); + } + + /** + * @return the list of IMS feature tag associated with this DelegateRequest in the format + * defined in RCC.07 section 2.6.1.3. + */ + public @NonNull Set<String> getFeatureTags() { + return new ArraySet<>(mFeatureTags); + } + + /** + * Internal constructor used only for unparcelling. + */ + private DelegateRequest(Parcel in) { + mFeatureTags = new ArrayList<>(); + in.readList(mFeatureTags, null /*classLoader*/); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeList(mFeatureTags); + } + + public static final @NonNull Creator<DelegateRequest> CREATOR = new Creator<DelegateRequest>() { + @Override + public DelegateRequest createFromParcel(Parcel source) { + return new DelegateRequest(source); + } + + @Override + public DelegateRequest[] newArray(int size) { + return new DelegateRequest[size]; + } + }; + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + DelegateRequest that = (DelegateRequest) o; + return mFeatureTags.equals(that.mFeatureTags); + } + + @Override + public int hashCode() { + return Objects.hash(mFeatureTags); + } + + @Override + public String toString() { + return "DelegateRequest{mFeatureTags=" + mFeatureTags + '}'; + } +} diff --git a/telephony/java/android/telephony/ims/DelegateStateCallback.java b/telephony/java/android/telephony/ims/DelegateStateCallback.java new file mode 100644 index 000000000000..2b4fb7d5cbf3 --- /dev/null +++ b/telephony/java/android/telephony/ims/DelegateStateCallback.java @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2020 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.ims; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SuppressLint; +import android.annotation.SystemApi; +import android.telephony.ims.stub.SipDelegate; +import android.telephony.ims.stub.SipTransportImplBase; + +import java.util.Set; + +/** + * Callback interface to notify a remote application of the following: + * <ul> + * <li>the {@link SipDelegate} associated with this callback has been created or destroyed in + * response to a creation or destruction request from the framework</li> + * <li>the SIP IMS configuration associated with this {@link SipDelegate} has changed</li> + * <li>the IMS registration of the feature tags associated with this {@link SipDelegate} have + * changed.</li> + * </ul> + * @hide + */ +@SystemApi +public interface DelegateStateCallback { + + /** + * This must be called by the ImsService after {@link SipTransportImplBase#createSipDelegate} is + * called by the framework to notify the framework and remote application that the + * {@link SipDelegate} has been successfully created. + * @param delegate The SipDelegate created to service the DelegateRequest. + * @param deniedTags A Set of {@link FeatureTagState}s, which contain the feature tags + * associated with this {@link SipDelegate} that have no access to send/receive SIP messages + * as well as a reason for why the feature tag is denied. For more information on the reason + * why the feature tag was denied access, see the + * {@link SipDelegateManager.DeniedReason} reasons. This is considered a permanent denial due + * to this {@link SipDelegate} not supporting a feature or this ImsService already + * implementing this feature elsewhere. If all features of this {@link SipDelegate} are + * denied, this method should still be called. + */ + void onCreated(@NonNull SipDelegate delegate, + @SuppressLint("NullableCollection") // TODO(b/154763999): Mark deniedTags @Nonnull + @Nullable Set<FeatureTagState> deniedTags); + + /** + * This must be called by the ImsService after the framework calls + * {@link SipTransportImplBase#destroySipDelegate} to notify the framework and remote + * application that the procedure to destroy the {@link SipDelegate} has been completed. + * @param reasonCode The reason for closing this delegate. + */ + void onDestroyed(@SipDelegateManager.SipDelegateDestroyReason int reasonCode); + + /** + * Call to notify the remote application of a configuration change associated with this + * {@link SipDelegate}. + * <p> + * The remote application will not be able to proceed sending SIP messages until after this + * configuration is sent the first time, so this configuration should be sent as soon as the + * {@link SipDelegate} has access to these configuration parameters. + * <p> + * Incoming SIP messages should not be routed to the remote application until AFTER this + * configuration change is sent to ensure that the remote application can respond correctly. + * Similarly, if there is an event that triggers the IMS configuration to change, incoming SIP + * messages routing should be delayed until the {@link SipDelegate} sends the IMS configuration + * change event to reduce conditions where the remote application is using a stale IMS + * configuration. + * @deprecated This is being removed from API surface, Use + * {@link #onConfigurationChanged(SipDelegateConfiguration)} instead. + */ + @Deprecated + void onImsConfigurationChanged(@NonNull SipDelegateImsConfiguration config); + + /** + * Call to notify the remote application of a configuration change associated with this + * {@link SipDelegate}. + * <p> + * The remote application will not be able to proceed sending SIP messages until after this + * configuration is sent the first time, so this configuration should be sent as soon as the + * {@link SipDelegate} has access to these configuration parameters. + * <p> + * Incoming SIP messages should not be routed to the remote application until AFTER this + * configuration change is sent to ensure that the remote application can respond correctly. + * Similarly, if there is an event that triggers the IMS configuration to change, incoming SIP + * messages routing should be delayed until the {@link SipDelegate} sends the IMS configuration + * change event to reduce conditions where the remote application is using a stale IMS + * configuration. + */ + void onConfigurationChanged(@NonNull SipDelegateConfiguration config); + + /** + * Call to notify the remote application that the {@link SipDelegate} has modified the IMS + * registration state of the RCS feature tags that were requested as part of the initial + * {@link DelegateRequest}. + * <p> + * See {@link DelegateRegistrationState} for more information about how IMS Registration state + * should be communicated the associated SipDelegateConnection in cases such as + * IMS deregistration, handover, PDN change, provisioning changes, etc… + * <p> + * Note: Even after the status of the feature tags are updated here to deregistered, the + * SipDelegate must still be able to handle these messages and call + * {@link DelegateMessageCallback#onMessageSendFailure} to notify the RCS application that the + * message was not sent. + * + * @param registrationState The current network IMS registration state for all feature tags + * associated with this SipDelegate. + */ + void onFeatureTagRegistrationChanged(@NonNull DelegateRegistrationState registrationState); +} diff --git a/telephony/java/android/telephony/ims/FeatureTagState.aidl b/telephony/java/android/telephony/ims/FeatureTagState.aidl new file mode 100644 index 000000000000..bce55742461c --- /dev/null +++ b/telephony/java/android/telephony/ims/FeatureTagState.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2020 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.ims; + +parcelable FeatureTagState; diff --git a/telephony/java/android/telephony/ims/FeatureTagState.java b/telephony/java/android/telephony/ims/FeatureTagState.java new file mode 100644 index 000000000000..3622065c5fe8 --- /dev/null +++ b/telephony/java/android/telephony/ims/FeatureTagState.java @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2020 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.ims; + +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.os.Parcel; +import android.os.Parcelable; +import android.telephony.ims.stub.DelegateConnectionStateCallback; +import android.telephony.ims.stub.SipDelegate; + +import java.util.List; +import java.util.Objects; + +/** + * Maps an IMS media feature tag 3gpp universal resource name (URN) previously mapped to a + * {@link SipDelegate} in the associated {@link DelegateRequest} to its current availability + * state as set by the ImsService managing the related IMS registration. + * + * This class is only used to report more information about a IMS feature tag that is not fully + * available at this time. + * <p> + * Please see {@link DelegateRegistrationState}, {@link DelegateStateCallback}, and + * {@link DelegateConnectionStateCallback} for more information about how this class is used to + * convey the state of IMS feature tags that were requested by {@link DelegateRequest} but are not + * currently available. + * @hide + */ +@SystemApi +public final class FeatureTagState implements Parcelable { + + private final String mFeatureTag; + private final int mState; + + /** + * Associate an IMS feature tag with its current state. See {@link DelegateRegistrationState} + * and {@link DelegateConnectionStateCallback#onFeatureTagStatusChanged( + * DelegateRegistrationState, List)} and + * {@link DelegateStateCallback#onCreated(SipDelegate, java.util.Set)} for examples on how and + * when this is used. + * + * @param featureTag The IMS feature tag that is deregistered, in the process of + * deregistering, or denied. + * @param state The {@link DelegateRegistrationState.DeregisteredReason}, + * {@link DelegateRegistrationState.DeregisteringReason}, or + * {@link SipDelegateManager.DeniedReason} associated with this feature tag. + */ + public FeatureTagState(@NonNull String featureTag, int state) { + mFeatureTag = featureTag; + mState = state; + } + + /** + * Used for constructing instances during un-parcelling. + */ + private FeatureTagState(Parcel source) { + mFeatureTag = source.readString(); + mState = source.readInt(); + } + + /** + * @return The IMS feature tag string that is in the process of deregistering, + * deregistered, or denied. + */ + public @NonNull String getFeatureTag() { + return mFeatureTag; + } + + /** + * @return The reason for why the feature tag is currently in the process of deregistering, + * has been deregistered, or has been denied. See {@link DelegateRegistrationState} and + * {@link DelegateConnectionStateCallback} for more information. + */ + public int getState() { + return mState; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeString(mFeatureTag); + dest.writeInt(mState); + } + + public static final @NonNull Creator<FeatureTagState> CREATOR = new Creator<FeatureTagState>() { + @Override + public FeatureTagState createFromParcel(Parcel source) { + return new FeatureTagState(source); + } + + @Override + public FeatureTagState[] newArray(int size) { + return new FeatureTagState[size]; + } + }; + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + FeatureTagState that = (FeatureTagState) o; + return mState == that.mState + && mFeatureTag.equals(that.mFeatureTag); + } + + @Override + public int hashCode() { + return Objects.hash(mFeatureTag, mState); + } + + @Override + public String toString() { + return "FeatureTagState{" + "mFeatureTag='" + mFeatureTag + ", mState=" + mState + '}'; + } +} diff --git a/telephony/java/android/telephony/ims/ImsCallForwardInfo.java b/telephony/java/android/telephony/ims/ImsCallForwardInfo.java index 3f9c8d26ca91..1c2cac27eb37 100644 --- a/telephony/java/android/telephony/ims/ImsCallForwardInfo.java +++ b/telephony/java/android/telephony/ims/ImsCallForwardInfo.java @@ -20,6 +20,7 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; +import android.os.Build; import android.os.Parcel; import android.os.Parcelable; @@ -128,26 +129,26 @@ public final class ImsCallForwardInfo implements Parcelable { public @interface TypeOfAddress{} /**@hide*/ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public @CallForwardReasons int mCondition; /** @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public @CallForwardStatus int mStatus; /** @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public @TypeOfAddress int mToA; /** @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public @ImsSsData.ServiceClassFlags int mServiceClass; /** @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public String mNumber; /** @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public int mTimeSeconds; /** @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public ImsCallForwardInfo() { } diff --git a/telephony/java/android/telephony/ims/ImsCallProfile.java b/telephony/java/android/telephony/ims/ImsCallProfile.java index 47a0ab61f970..486f74632ca2 100644 --- a/telephony/java/android/telephony/ims/ImsCallProfile.java +++ b/telephony/java/android/telephony/ims/ImsCallProfile.java @@ -18,9 +18,11 @@ package android.telephony.ims; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.SystemApi; import android.annotation.TestApi; import android.compat.annotation.UnsupportedAppUsage; +import android.os.Build; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; @@ -28,6 +30,8 @@ import android.telecom.VideoProfile; import android.telephony.emergency.EmergencyNumber; import android.telephony.emergency.EmergencyNumber.EmergencyCallRouting; import android.telephony.emergency.EmergencyNumber.EmergencyServiceCategories; +import android.telephony.ims.feature.MmTelFeature; +import android.util.ArraySet; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; @@ -37,7 +41,10 @@ import com.android.internal.telephony.util.TelephonyUtils; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; /** * A Parcelable object to handle the IMS call profile, which provides the service, call type, and @@ -126,10 +133,28 @@ public final class ImsCallProfile implements Parcelable { * the video during voice call. * conference_avail : Indicates if the session can be extended to the conference. */ + + /** + * Indicates if the session is for a conference call or not. If not defined, should be + * considered {@code false}. + * Boolean extra properties - {@code true} / {@code false}. + * + * This extra is set on an instance of {@link ImsCallProfile} via {@link #setCallExtraBoolean}. + * @hide + */ + @SystemApi + public static final String EXTRA_CONFERENCE = "android.telephony.ims.extra.CONFERENCE"; + /** + * The previous string of EXTRA_CONFERENCE. Use EXTRA_CONFERENCE whenever possible. + * For external app or vendor code backward compatibility, we should always set value for both + * EXTRA_CONFERENCE_DEPRECATED and EXTRA_CONFERENCE. + * + * @deprecated Remove when not needed anymore. + * * @hide */ - public static final String EXTRA_CONFERENCE = "conference"; + public static final String EXTRA_CONFERENCE_DEPRECATED = "conference"; /** * Boolean extra property set on an {@link ImsCallProfile} to indicate that this call is an @@ -146,7 +171,27 @@ public final class ImsCallProfile implements Parcelable { * @hide */ public static final String EXTRA_CALL_MODE_CHANGEABLE = "call_mode_changeable"; + + /** + * Indicates if the session can be extended to a conference call. If not defined, should be + * considered {@code false}. + * Boolean extra properties - {@code true} / {@code false}. + * + * This extra is set on an instance of {@link ImsCallProfile} via {@link #setCallExtraBoolean}. + * @hide + */ + @SystemApi + public static final String EXTRA_EXTENDING_TO_CONFERENCE_SUPPORTED = + "android.telephony.ims.extra.EXTENDING_TO_CONFERENCE_SUPPORTED"; + /** + * The previous string of EXTRA_EXTENDING_TO_CONFERENCE_SUPPORTED. + * Use EXTRA_EXTENDING_TO_CONFERENCE_SUPPORTED whenever possible. + * For backward compatibility, we should always set value for both + * EXTRA_EXTENDING_TO_CONFERENCE_SUPPORTED and EXTRA_CONFERENCE_AVAIL. + * + * @deprecated Remove when not needed anymore. + * * @hide */ public static final String EXTRA_CONFERENCE_AVAIL = "conference_avail"; @@ -201,6 +246,55 @@ public final class ImsCallProfile implements Parcelable { "android.telephony.ims.extra.RETRY_CALL_FAIL_NETWORKTYPE"; /** + * Extra for the call composer call priority, either {@link ImsCallProfile#PRIORITY_NORMAL} or + * {@link ImsCallProfile#PRIORITY_URGENT}. It can be set via + * {@link #setCallExtraInt(String, int)}. + * + * Reference: RCC.20 Section 2.4.4.2 + */ + public static final String EXTRA_PRIORITY = "android.telephony.ims.extra.PRIORITY"; + + // TODO(hallliu) remove the reference to the maximum length and update it later. + /** + * Extra for the call composer call subject, a string of maximum length 60 characters. + * It can be set via {@link #setCallExtra(String, String)}. + * + * Reference: RCC.20 Section 2.4.3.2 + */ + public static final String EXTRA_CALL_SUBJECT = "android.telephony.ims.extra.CALL_SUBJECT"; + + /** + * Extra for the call composer call location, an {@Link android.location.Location} parcelable + * class to represent the geolocation as a latitude and longitude pair. It can be set via + * {@link #setCallExtraParcelable(String, Parcelable)}. + * + * Reference: RCC.20 Section 2.4.3.2 + */ + public static final String EXTRA_LOCATION = "android.telephony.ims.extra.LOCATION"; + + /** + * Extra for the call composer picture URL, a String that indicates the URL on the carrier’s + * server infrastructure to get the picture. It can be set via + * {@link #setCallExtra(String, String)}. + * + * Note that this URL is not intended to be parsed by the IMS stack -- it should be sent + * directly to the network for consumption by the called party or forwarded directly from the + * network to the platform for caching and download. + * + * Reference: RCC.20 Section 2.4.3.2 + */ + public static final String EXTRA_PICTURE_URL = "android.telephony.ims.extra.PICTURE_URL"; + + /** + * Boolean extra indicating whether the call is a business call. + * + * This extra will be set to {@code true} if and only if the SIP INVITE headers contain the + * "Organization" header. + */ + public static final String EXTRA_IS_BUSINESS_CALL = + "android.telephony.ims.extra.IS_BUSINESS_CALL"; + + /** * Values for EXTRA_OIR / EXTRA_CNAP */ /** @@ -238,6 +332,21 @@ public final class ImsCallProfile implements Parcelable { */ public static final int DIALSTRING_USSD = 2; + // Values for EXTRA_PRIORITY + /** + * Indicates the call composer call priority is normal. + * + * Reference: RCC.20 Section 2.4.4.2 + */ + public static final int PRIORITY_NORMAL = 0; + + /** + * Indicates the call composer call priority is urgent. + * + * Reference: RCC.20 Section 2.4.4.2 + */ + public static final int PRIORITY_URGENT = 1; + /** * Call is not restricted on peer side and High Definition media is supported */ @@ -353,13 +462,22 @@ public final class ImsCallProfile implements Parcelable { public static final String EXTRA_FORWARDED_NUMBER = "android.telephony.ims.extra.FORWARDED_NUMBER"; + /** + * Extra key with an {@code boolean} value which can be set in + * {@link #setCallExtraBoolean(String, boolean)} to indicate whether call is a cross sim call. + * <p> + * Valid values are true if call is cross sim call else false. + */ + public static final String EXTRA_IS_CROSS_SIM_CALL = + "android.telephony.ims.extra.IS_CROSS_SIM_CALL"; + /** @hide */ public int mServiceType; /** @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public int mCallType; /** @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public @CallRestrictCause int mRestrictCause = CALL_RESTRICT_CAUSE_NONE; /** @@ -444,6 +562,8 @@ public final class ImsCallProfile implements Parcelable { /** Indicates if we have known the intent of the user for the call is emergency */ private boolean mHasKnownUserIntentEmergency = false; + private Set<RtpHeaderExtensionType> mAcceptedRtpHeaderExtensionTypes = new ArraySet<>(); + /** * Extras associated with this {@link ImsCallProfile}. * <p> @@ -467,10 +587,10 @@ public final class ImsCallProfile implements Parcelable { * a {@link android.os.Binder}. */ /** @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public Bundle mCallExtras; /** @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public ImsStreamMediaProfile mMediaProfile; /** @hide */ @@ -580,6 +700,19 @@ public final class ImsCallProfile implements Parcelable { return mCallExtras.getInt(name, defaultValue); } + /** + * Get the call extras (Parcelable), given the extra name. + * @param name call extra name + * @return the corresponding call extra Parcelable or null if not applicable + */ + @Nullable + public <T extends Parcelable> T getCallExtraParcelable(@Nullable String name) { + if (mCallExtras != null) { + return mCallExtras.getParcelable(name); + } + return null; + } + public void setCallExtra(String name, String value) { if (mCallExtras != null) { mCallExtras.putString(name, value); @@ -599,6 +732,21 @@ public final class ImsCallProfile implements Parcelable { } /** + * Set the call extra value (Parcelable), given the call extra name. + * + * Note that the {@link Parcelable} provided must be a class defined in the Android API surface, + * as opposed to a class defined by your app. + * + * @param name call extra name + * @param parcelable call extra value + */ + public void setCallExtraParcelable(@NonNull String name, @NonNull Parcelable parcelable) { + if (mCallExtras != null) { + mCallExtras.putParcelable(name, parcelable); + } + } + + /** * Set the call restrict cause, which provides the reason why a call has been restricted from * using High Definition media. */ @@ -660,7 +808,9 @@ public final class ImsCallProfile implements Parcelable { + ", emergencyCallTesting=" + mEmergencyCallTesting + ", hasKnownUserIntentEmergency=" + mHasKnownUserIntentEmergency + ", mRestrictCause=" + mRestrictCause - + ", mCallerNumberVerstat= " + mCallerNumberVerificationStatus + " }"; + + ", mCallerNumberVerstat= " + mCallerNumberVerificationStatus + + ", mAcceptedRtpHeaderExtensions= " + mAcceptedRtpHeaderExtensionTypes + + " }"; } @Override @@ -682,6 +832,7 @@ public final class ImsCallProfile implements Parcelable { out.writeBoolean(mHasKnownUserIntentEmergency); out.writeInt(mRestrictCause); out.writeInt(mCallerNumberVerificationStatus); + out.writeArray(mAcceptedRtpHeaderExtensionTypes.toArray()); } private void readFromParcel(Parcel in) { @@ -696,9 +847,13 @@ public final class ImsCallProfile implements Parcelable { mHasKnownUserIntentEmergency = in.readBoolean(); mRestrictCause = in.readInt(); mCallerNumberVerificationStatus = in.readInt(); + Object[] accepted = in.readArray(RtpHeaderExtensionType.class.getClassLoader()); + mAcceptedRtpHeaderExtensionTypes = Arrays.stream(accepted) + .map(o -> (RtpHeaderExtensionType) o).collect(Collectors.toSet()); } - public static final @android.annotation.NonNull Creator<ImsCallProfile> CREATOR = new Creator<ImsCallProfile>() { + public static final @android.annotation.NonNull Creator<ImsCallProfile> CREATOR = + new Creator<ImsCallProfile>() { @Override public ImsCallProfile createFromParcel(Parcel in) { return new ImsCallProfile(in); @@ -823,7 +978,7 @@ public final class ImsCallProfile implements Parcelable { * See {@link #presentationToOir(int)}. * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public static int presentationToOIR(int presentation) { switch (presentation) { case PhoneConstants.PRESENTATION_RESTRICTED: @@ -1085,4 +1240,33 @@ public final class ImsCallProfile implements Parcelable { public boolean hasKnownUserIntentEmergency() { return mHasKnownUserIntentEmergency; } + + /** + * Gets the {@link RtpHeaderExtensionType}s which have been accepted by both ends of the call. + * <p> + * According to RFC8285, RTP header extensions available to a call are determined using the + * offer/accept phase of the SDP protocol (see RFC4566). + * <p> + * The offered header extension types supported by the framework and exposed to the + * {@link ImsService} via {@link MmTelFeature#changeOfferedRtpHeaderExtensionTypes(Set)}. + * + * @return the {@link RtpHeaderExtensionType}s which were accepted by the other end of the call. + */ + public @NonNull Set<RtpHeaderExtensionType> getAcceptedRtpHeaderExtensionTypes() { + return mAcceptedRtpHeaderExtensionTypes; + } + + /** + * Sets the accepted {@link RtpHeaderExtensionType}s for this call. + * <p> + * According to RFC8285, RTP header extensions available to a call are determined using the + * offer/accept phase of the SDP protocol (see RFC4566). + * + * @param rtpHeaderExtensions + */ + public void setAcceptedRtpHeaderExtensionTypes(@NonNull Set<RtpHeaderExtensionType> + rtpHeaderExtensions) { + mAcceptedRtpHeaderExtensionTypes.clear(); + mAcceptedRtpHeaderExtensionTypes.addAll(rtpHeaderExtensions); + } } diff --git a/telephony/java/android/telephony/ims/ImsCallSession.java b/telephony/java/android/telephony/ims/ImsCallSession.java index 80c38cbfc39a..0aff99709a52 100755 --- a/telephony/java/android/telephony/ims/ImsCallSession.java +++ b/telephony/java/android/telephony/ims/ImsCallSession.java @@ -22,11 +22,16 @@ import android.os.Message; import android.os.RemoteException; import android.telephony.CallQuality; import android.telephony.ims.aidl.IImsCallSessionListener; +import android.util.ArraySet; import android.util.Log; import com.android.ims.internal.IImsCallSession; import com.android.ims.internal.IImsVideoCallProvider; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + /** * Provides the call initiation/termination, and media exchange between two IMS endpoints. * It directly communicates with IMS service which implements the IMS protocol behavior. @@ -96,10 +101,29 @@ public class ImsCallSession { */ public static class Listener { /** - * Called when a request is sent out to initiate a new session - * and 1xx response is received from the network. + * Called when the session is initiating. * - * @param session the session object that carries out the IMS session + * see: {@link ImsCallSessionListener#callSessionInitiating(ImsCallProfile)} + */ + public void callSessionInitiating(ImsCallSession session, + ImsCallProfile profile) { + // no-op + } + + /** + * Called when the session failed before initiating was called. + * + * see: {@link ImsCallSessionListener#callSessionInitiatingFailed(ImsReasonInfo)} + */ + public void callSessionInitiatingFailed(ImsCallSession session, + ImsReasonInfo reasonInfo) { + // no-op + } + + /** + * Called when the session is progressing. + * + * see: {@link ImsCallSessionListener#callSessionProgressing(ImsStreamMediaProfile)} */ public void callSessionProgressing(ImsCallSession session, ImsStreamMediaProfile profile) { @@ -468,11 +492,31 @@ public class ImsCallSession { } /** + * Informs the framework of a DTMF digit which was received from the network. + * <p> + * According to <a href="http://tools.ietf.org/html/rfc2833">RFC 2833 sec 3.10</a>, + * event 0 ~ 9 maps to decimal value 0 ~ 9, '*' to 10, '#' to 11, event 'A' ~ 'D' to + * 12 ~ 15. + * @param digit the DTMF digit + */ + public void callSessionDtmfReceived(char digit) { + // no-op + } + + /** * Called when the IMS service reports a change to the call quality. */ public void callQualityChanged(CallQuality callQuality) { // no-op } + + /** + * Called when the IMS service reports incoming RTP header extension data. + */ + public void callSessionRtpHeaderExtensionsReceived( + @NonNull Set<RtpHeaderExtension> extensions) { + // no-op + } } private final IImsCallSession miSession; @@ -815,7 +859,7 @@ public class ImsCallSession { * Transfers an ongoing call. * * @param number number to be transferred to. - * @param isConfirmationRequired indicates blind or assured transfer. + * @param isConfirmationRequired indicates whether confirmation of the transfer is required. */ public void transfer(@NonNull String number, boolean isConfirmationRequired) { if (mClosed) { @@ -1119,6 +1163,31 @@ public class ImsCallSession { } /** + * Requests that {@code rtpHeaderExtensions} are sent as a header extension with the next + * RTP packet sent by the IMS stack. + * <p> + * The {@link RtpHeaderExtensionType}s negotiated during SDP (Session Description Protocol) + * signalling determine the {@link RtpHeaderExtension}s which can be sent using this method. + * See RFC8285 for more information. + * <p> + * By specification, the RTP header extension is an unacknowledged transmission and there is no + * guarantee that the header extension will be delivered by the network to the other end of the + * call. + * @param rtpHeaderExtensions The header extensions to be included in the next RTP header. + */ + public void sendRtpHeaderExtensions(@NonNull Set<RtpHeaderExtension> rtpHeaderExtensions) { + if (mClosed) { + return; + } + + try { + miSession.sendRtpHeaderExtensions( + new ArrayList<RtpHeaderExtension>(rtpHeaderExtensions)); + } catch (RemoteException e) { + } + } + + /** * A listener type for receiving notification on IMS call session events. * When an event is generated for an {@link IImsCallSession}, * the application is notified by having one of the methods called on @@ -1129,6 +1198,13 @@ public class ImsCallSession { * Notifies the result of the basic session operation (setup / terminate). */ @Override + public void callSessionInitiating(ImsCallProfile profile) { + if (mListener != null) { + mListener.callSessionInitiating(ImsCallSession.this, profile); + } + } + + @Override public void callSessionProgressing(ImsStreamMediaProfile profile) { if (mListener != null) { mListener.callSessionProgressing(ImsCallSession.this, profile); @@ -1143,6 +1219,13 @@ public class ImsCallSession { } @Override + public void callSessionInitiatingFailed(ImsReasonInfo reasonInfo) { + if (mListener != null) { + mListener.callSessionStartFailed(ImsCallSession.this, reasonInfo); + } + } + + @Override public void callSessionInitiatedFailed(ImsReasonInfo reasonInfo) { if (mListener != null) { mListener.callSessionStartFailed(ImsCallSession.this, reasonInfo); @@ -1477,6 +1560,17 @@ public class ImsCallSession { } /** + * DTMF digit received. + * @param dtmf The DTMF digit. + */ + @Override + public void callSessionDtmfReceived(char dtmf) { + if (mListener != null) { + mListener.callSessionDtmfReceived(dtmf); + } + } + + /** * Call quality updated */ @Override @@ -1485,6 +1579,19 @@ public class ImsCallSession { mListener.callQualityChanged(callQuality); } } + + /** + * RTP header extension data received. + * @param extensions The header extension data. + */ + @Override + public void callSessionRtpHeaderExtensionsReceived( + @NonNull List<RtpHeaderExtension> extensions) { + if (mListener != null) { + mListener.callSessionRtpHeaderExtensionsReceived( + new ArraySet<RtpHeaderExtension>(extensions)); + } + } } /** diff --git a/telephony/java/android/telephony/ims/ImsCallSessionListener.java b/telephony/java/android/telephony/ims/ImsCallSessionListener.java index b3907ff7c066..db99acfd9a35 100644 --- a/telephony/java/android/telephony/ims/ImsCallSessionListener.java +++ b/telephony/java/android/telephony/ims/ImsCallSessionListener.java @@ -28,6 +28,10 @@ import android.telephony.ims.stub.ImsCallSessionImplBase; import com.android.ims.internal.IImsCallSession; +import java.util.ArrayList; +import java.util.Objects; +import java.util.Set; + /** * Listener interface for notifying the Framework's {@link ImsCallSession} for updates to an ongoing * IMS call. @@ -49,8 +53,45 @@ public class ImsCallSessionListener { } /** - * A request has been sent out to initiate a new IMS call session and a 1xx response has been - * received from the network. + * Called when the network first begins to establish the call session and is now connecting + * to the remote party. This must be called once after {@link ImsCallSessionImplBase#start} and + * before any other method on this listener. After this is called, + * {@link #callSessionProgressing(ImsStreamMediaProfile)} must be called to communicate any + * further updates. + * <p/> + * Once this is called, {@link #callSessionTerminated} must be called + * to end the call session. In the event that the session failed before the remote party + * was contacted, {@link #callSessionInitiatingFailed} must be called. + * + * @param profile the associated {@link ImsCallProfile}. + */ + public void callSessionInitiating(@NonNull ImsCallProfile profile) { + try { + mListener.callSessionInitiating(profile); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + + /** + * The IMS call session establishment has failed while initiating. + * + * @param reasonInfo {@link ImsReasonInfo} detailing the reason of the IMS call session + * establishment failure. + */ + public void callSessionInitiatingFailed(@NonNull ImsReasonInfo reasonInfo) { + try { + mListener.callSessionInitiatingFailed(reasonInfo); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + + /** + * Called after the network has contacted the remote party and the call state should move to + * ALERTING. + * + * @param profile the associated {@link ImsCallProfile}. */ public void callSessionProgressing(ImsStreamMediaProfile profile) { try { @@ -61,7 +102,8 @@ public class ImsCallSessionListener { } /** - * The IMS call session has been initiated. + * Called once the outgoing IMS call session has been begun between the local and remote party. + * The call state should move to ACTIVE. * * @param profile the associated {@link ImsCallProfile}. */ @@ -78,7 +120,12 @@ public class ImsCallSessionListener { * * @param reasonInfo {@link ImsReasonInfo} detailing the reason of the IMS call session * establishment failure. + * @deprecated {@link #callSessionInitiated(ImsCallProfile)} is called immediately after + * the session is first started which meant that there was no time in which a call to this + * method was technically valid. This method is replaced starting Android S in favor of + * {@link #callSessionInitiatingFailed(ImsReasonInfo)}. */ + @Deprecated public void callSessionInitiatedFailed(ImsReasonInfo reasonInfo) { try { mListener.callSessionInitiatedFailed(reasonInfo); @@ -681,5 +728,85 @@ public class ImsCallSessionListener { e.rethrowFromSystemServer(); } } + + /** + * The {@link ImsService} calls this method to inform the framework of a DTMF digit which was + * received from the network. + * <p> + * According to <a href="http://tools.ietf.org/html/rfc2833">RFC 2833 sec 3.10</a>, + * event 0 ~ 9 maps to decimal value 0 ~ 9, '*' to 10, '#' to 11, event 'A' ~ 'D' to 12 ~ 15. + * <p> + * <em>Note:</em> Alpha DTMF digits are converted from lower-case to upper-case. + * + * @param dtmf The DTMF digit received, '0'-'9', *, #, A, B, C, or D. + * @throws IllegalArgumentException If an invalid DTMF character is provided. + */ + public void callSessionDtmfReceived(char dtmf) { + if (!(dtmf >= '0' && dtmf <= '9' + || dtmf >= 'A' && dtmf <= 'D' + || dtmf >= 'a' && dtmf <= 'd' + || dtmf == '*' + || dtmf == '#')) { + throw new IllegalArgumentException("DTMF digit must be 0-9, *, #, A, B, C, D"); + } + try { + mListener.callSessionDtmfReceived(Character.toUpperCase(dtmf)); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + + /** + * The {@link ImsService} calls this method to inform the framework of RTP header extension data + * which was received from the network. + * <p> + * The set of {@link RtpHeaderExtension} data are identified by local identifiers which were + * negotiated during SDP signalling. See RFC8285, + * {@link ImsCallProfile#getAcceptedRtpHeaderExtensionTypes()} and + * {@link RtpHeaderExtensionType} for more information. + * <p> + * By specification, the RTP header extension is an unacknowledged transmission and there is no + * guarantee that the header extension will be delivered by the network to the other end of the + * call. + * + * @param extensions The RTP header extension data received. + */ + public void callSessionRtpHeaderExtensionsReceived( + @NonNull Set<RtpHeaderExtension> extensions) { + Objects.requireNonNull(extensions, "extensions are required."); + try { + mListener.callSessionRtpHeaderExtensionsReceived( + new ArrayList<RtpHeaderExtension>(extensions)); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + + /** + * Notifies the result of transfer request. + * @hide + */ + public void callSessionTransferred() { + try { + mListener.callSessionTransferred(); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + + /** + * Notifies the result of transfer request. + * + * @param reasonInfo {@link ImsReasonInfo} containing a reason for the + * session transfer failure + * @hide + */ + public void callSessionTransferFailed(ImsReasonInfo reasonInfo) { + try { + mListener.callSessionTransferFailed(reasonInfo); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } } diff --git a/telephony/java/android/telephony/ims/ImsConferenceState.java b/telephony/java/android/telephony/ims/ImsConferenceState.java index 13f1a79c1040..1fa5f52968e5 100644 --- a/telephony/java/android/telephony/ims/ImsConferenceState.java +++ b/telephony/java/android/telephony/ims/ImsConferenceState.java @@ -201,10 +201,10 @@ public final class ImsConferenceState implements Parcelable { for (String key : participantData.keySet()) { sb.append(key); sb.append("="); - if (ENDPOINT.equals(key) || USER.equals(key)) { - sb.append(Rlog.pii(TAG, participantData.get(key))); - } else { + if (STATUS.equals(key)) { sb.append(participantData.get(key)); + } else { + sb.append(Rlog.pii(TAG, participantData.get(key))); } sb.append(", "); } diff --git a/telephony/java/android/telephony/ims/ImsExternalCallState.java b/telephony/java/android/telephony/ims/ImsExternalCallState.java index fdf636c323b6..c663e393fe06 100644 --- a/telephony/java/android/telephony/ims/ImsExternalCallState.java +++ b/telephony/java/android/telephony/ims/ImsExternalCallState.java @@ -49,8 +49,7 @@ public final class ImsExternalCallState implements Parcelable { public static final int CALL_STATE_TERMINATED = 2; /**@hide*/ - @IntDef(flag = true, - value = { + @IntDef(value = { CALL_STATE_CONFIRMED, CALL_STATE_TERMINATED }, @@ -59,8 +58,7 @@ public final class ImsExternalCallState implements Parcelable { public @interface ExternalCallState {} /**@hide*/ - @IntDef(flag = true, - value = { + @IntDef(value = { ImsCallProfile.CALL_TYPE_VOICE, ImsCallProfile.CALL_TYPE_VT_TX, ImsCallProfile.CALL_TYPE_VT_RX, diff --git a/telephony/java/android/telephony/ims/ImsMmTelManager.java b/telephony/java/android/telephony/ims/ImsMmTelManager.java index 9300a1cbc55c..7ba6f36d28e2 100644 --- a/telephony/java/android/telephony/ims/ImsMmTelManager.java +++ b/telephony/java/android/telephony/ims/ImsMmTelManager.java @@ -29,6 +29,7 @@ import android.os.Binder; import android.os.RemoteException; import android.os.ServiceSpecificException; import android.telephony.AccessNetworkConstants; +import android.telephony.BinderCacheManager; import android.telephony.CarrierConfigManager; import android.telephony.SubscriptionManager; import android.telephony.TelephonyFrameworkInitializer; @@ -58,6 +59,7 @@ import java.util.function.Consumer; * manager. */ public class ImsMmTelManager implements RegistrationManager { + private static final String TAG = "ImsMmTelManager"; /** * @hide @@ -159,7 +161,7 @@ public class ImsMmTelManager implements RegistrationManager { public void onCapabilitiesStatusChanged(int config) { if (mLocalCallback == null) return; - long callingIdentity = Binder.clearCallingIdentity(); + final long callingIdentity = Binder.clearCallingIdentity(); try { mExecutor.execute(() -> mLocalCallback.onCapabilitiesStatusChanged( new MmTelFeature.MmTelCapabilities(config))); @@ -212,6 +214,7 @@ public class ImsMmTelManager implements RegistrationManager { } private final int mSubId; + private final BinderCacheManager<ITelephony> mBinderCache; /** * Create an instance of {@link ImsMmTelManager} for the subscription id specified. @@ -241,7 +244,8 @@ public class ImsMmTelManager implements RegistrationManager { throw new IllegalArgumentException("Invalid subscription ID"); } - return new ImsMmTelManager(subId); + return new ImsMmTelManager(subId, new BinderCacheManager<>( + ImsMmTelManager::getITelephonyInterface)); } /** @@ -249,8 +253,9 @@ public class ImsMmTelManager implements RegistrationManager { * @hide */ @VisibleForTesting - public ImsMmTelManager(int subId) { + public ImsMmTelManager(int subId, BinderCacheManager<ITelephony> binderCache) { mSubId = subId; + mBinderCache = binderCache; } /** @@ -709,14 +714,8 @@ public class ImsMmTelManager implements RegistrationManager { * @see android.telephony.CarrierConfigManager#KEY_CARRIER_IMS_GBA_REQUIRED_BOOL * @see #isAvailable(int, int) * - * @param imsRegTech The IMS registration technology, can be one of the following: - * {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE}, - * {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN} - * @param capability The IMS MmTel capability to query, can be one of the following: - * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VOICE}, - * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VIDEO}, - * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_UT}, - * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_SMS} + * @param imsRegTech The IMS registration technology. + * @param capability The IMS MmTel capability to query. * @return {@code true} if the MmTel IMS capability is capable for this subscription, false * otherwise. * @hide @@ -743,14 +742,8 @@ public class ImsMmTelManager implements RegistrationManager { * * @see #isCapable(int, int) * - * @param imsRegTech The IMS registration technology, can be one of the following: - * {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE}, - * {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN} - * @param capability The IMS MmTel capability to query, can be one of the following: - * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VOICE}, - * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VIDEO}, - * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_UT}, - * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_SMS} + * @param imsRegTech The IMS registration technology. + * @param capability The IMS MmTel capability to query. * @return {@code true} if the MmTel IMS capability is available for this subscription, false * otherwise. * @hide @@ -807,7 +800,7 @@ public class ImsMmTelManager implements RegistrationManager { } try { - getITelephony().isMmTelCapabilitySupported(mSubId, new IIntegerConsumer.Stub() { + iTelephony.isMmTelCapabilitySupported(mSubId, new IIntegerConsumer.Stub() { @Override public void accept(int result) { executor.execute(() -> callback.accept(result == 1)); @@ -974,6 +967,105 @@ public class ImsMmTelManager implements RegistrationManager { } /** + * This configuration is meaningful only on dual sim device. + * If enabled, this will result in the device setting up IMS of all other + * active subscriptions over the INTERNET APN of the primary default data subscription + * when any of those subscriptions are roaming or out of service and if wifi is not available + * for VoWifi. This feature will be disabled if + * {@link CarrierConfigManager#KEY_CARRIER_CROSS_SIM_IMS_AVAILABLE_BOOL} is set to false. + * <p>Following are the conditions in which system will try to register IMS over + * cross sim + * <ul> + * <li>Wifi is not available, one SIM is roaming and the default data + * SIM is in home network. Then roaming SIM IMS will be registered over INTERNET APN of the + * default data subscription </li> + * <li>Wifi is not available, one SIM is out of service and the default data + * SIM is in home network. Then out of service SIM IMS will be registered over INTERNET + * APN of the default data subscription </li> + * </ul> + * <p>This API requires one of the following: + * <ul> + * <li>The caller holds the READ_PRECISE_PHONE_STATE permission.</li> + * <li>If the caller is the device or profile owner, the caller holds the + * {@link Manifest.permission#READ_PRECISE_PHONE_STATE} permission.</li> + * <li>The caller has carrier privileges (see + * {@link android.telephony.TelephonyManager#hasCarrierPrivileges}) on any + * active subscription.</li> + * </ul> + * <p>The profile owner is an app that owns a managed profile on the device; for more details + * see <a href="https://developer.android.com/work/managed-profiles">Work profiles</a>. + * Access by profile owners is deprecated and will be removed in a future release. + * + * @throws ImsException if the IMS service associated with this subscription is not available or + * the IMS service is not available. + * @return true if the user's setting for Voice over Cross SIM is enabled and false if it is not + */ + @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236). + @RequiresPermission(anyOf = { + android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, + android.Manifest.permission.READ_PRECISE_PHONE_STATE}) + public boolean isCrossSimCallingEnabled() throws ImsException { + ITelephony iTelephony = getITelephony(); + if (iTelephony == null) { + throw new ImsException("Could not find Telephony Service.", + ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); + } + + try { + return iTelephony.isCrossSimCallingEnabledByUser(mSubId); + } catch (ServiceSpecificException sse) { + throw new ImsException(sse.getMessage(), sse.errorCode); + } catch (RemoteException e) { + e.rethrowAsRuntimeException(); + } + // Not reachable. Adding return to make compiler happy. + return false; + } + + /** + * Sets the user's setting for whether or not Voice over Cross SIM is enabled. + * If enabled, this will result in the device setting up IMS of all other + * active subscriptions over the INTERNET APN of the primary default data subscription + * when any of those subscriptions are roaming or out of service and if wifi is not available + * for VoWifi. This feature will be disabled if + * {@link CarrierConfigManager#KEY_CARRIER_CROSS_SIM_IMS_AVAILABLE_BOOL} is set to false. + * + * <p>Following are the conditions in which system will try to register IMS over + * cross sim + * <ul> + * <li>Wifi is not available, one SIM is roaming and the default data + * SIM is in home network. Then roaming SIM IMS will be registered over INTERNET APN of the + * default data subscription </li> + * <li>Wifi is not available, one SIM is out of service and the default data + * SIM is in home network. Then out of service SIM IMS will be registered over INTERNET + * APN of the default data subscription </li> + * </ul> + * @throws ImsException if the IMS service associated with this subscription is not available or + * the IMS service is not available. + * @param isEnabled true if the user's setting for Voice over Cross SIM is enabled, + * false otherwise + * @see #isCrossSimCallingEnabled() + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) + public void setCrossSimCallingEnabled(boolean isEnabled) throws ImsException { + ITelephony iTelephony = getITelephony(); + if (iTelephony == null) { + throw new ImsException("Could not find Telephony Service.", + ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); + } + + try { + iTelephony.setCrossSimCallingEnabled(mSubId, isEnabled); + } catch (ServiceSpecificException sse) { + throw new ImsException(sse.getMessage(), sse.errorCode); + } catch (RemoteException e) { + e.rethrowAsRuntimeException(); + } + } + + /** * Returns the user's voice over WiFi roaming setting associated with the current subscription. * * <p>This API requires one of the following: @@ -1366,7 +1458,11 @@ public class ImsMmTelManager implements RegistrationManager { } } - private static ITelephony getITelephony() { + private ITelephony getITelephony() { + return mBinderCache.getBinder(); + } + + private static ITelephony getITelephonyInterface() { ITelephony binder = ITelephony.Stub.asInterface( TelephonyFrameworkInitializer .getTelephonyServiceManager() diff --git a/telephony/java/android/telephony/ims/ImsRcsManager.java b/telephony/java/android/telephony/ims/ImsRcsManager.java index 94407f1dcd3a..814ce18c51e3 100644 --- a/telephony/java/android/telephony/ims/ImsRcsManager.java +++ b/telephony/java/android/telephony/ims/ImsRcsManager.java @@ -21,25 +21,27 @@ import android.annotation.CallbackExecutor; import android.annotation.NonNull; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; +import android.annotation.SystemApi; import android.content.Context; import android.content.Intent; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; +import android.os.ServiceSpecificException; import android.provider.Settings; import android.telephony.AccessNetworkConstants; -import android.telephony.CarrierConfigManager; -import android.telephony.SubscriptionManager; +import android.telephony.BinderCacheManager; import android.telephony.TelephonyFrameworkInitializer; import android.telephony.ims.aidl.IImsCapabilityCallback; import android.telephony.ims.aidl.IImsRcsController; import android.telephony.ims.feature.ImsFeature; -import android.telephony.ims.feature.RcsFeature; import android.telephony.ims.stub.ImsRegistrationImplBase; import android.util.Log; import com.android.internal.telephony.IIntegerConsumer; +import java.util.HashMap; +import java.util.Map; import java.util.concurrent.Executor; import java.util.function.Consumer; @@ -62,9 +64,10 @@ public class ImsRcsManager { * been enabled by the user can be queried using {@link RcsUceAdapter#isUceSettingEnabled()}. * <p> * This intent will always be handled by the system, however the application should only send - * this Intent if the carrier supports RCS contact discovery, which can be queried using the key - * {@link CarrierConfigManager#KEY_USE_RCS_PRESENCE_BOOL}. Otherwise, the RCS contact discovery - * opt-in dialog will not be shown. + * this Intent if the carrier supports bulk RCS contact exchange, which will be true if either + * key {@link android.telephony.CarrierConfigManager.Ims#KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL} + * or {@link android.telephony.CarrierConfigManager#KEY_USE_RCS_PRESENCE_BOOL} is set to true. + * Otherwise, the RCS contact discovery opt-in dialog will not be shown. * <p> * Input: A mandatory {@link Settings#EXTRA_SUB_ID} extra containing the subscription that the * setting will be be shown for. @@ -77,32 +80,49 @@ public class ImsRcsManager { "android.telephony.ims.action.SHOW_CAPABILITY_DISCOVERY_OPT_IN"; /** - * Receives RCS availability status updates from the ImsService. - * - * @see #isAvailable(int) - * @see #registerRcsAvailabilityCallback(Executor, AvailabilityCallback) - * @see #unregisterRcsAvailabilityCallback(AvailabilityCallback) + * An application can use {@link #addOnAvailabilityChangedListener} to register a + * {@link OnAvailabilityChangedListener}, which will notify the user when the RCS feature + * availability status updates from the ImsService. * @hide */ - public static class AvailabilityCallback { + @SystemApi + public interface OnAvailabilityChangedListener { + /** + * The availability of the feature's capabilities has changed to either available or + * unavailable. + * <p> + * If unavailable, the feature does not support the capability at the current time. This may + * be due to network or subscription provisioning changes, such as the IMS registration + * being lost, network type changing, or OMA-DM provisioning updates. + * + * @param capabilities The new availability of the capabilities. + */ + void onAvailabilityChanged(@RcsUceAdapter.RcsImsCapabilityFlag int capabilities); + } - private static class CapabilityBinder extends IImsCapabilityCallback.Stub { + /** + * Receive the availability status changed from the ImsService and pass the status change to + * the associated {@link OnAvailabilityChangedListener} + */ + private static class AvailabilityCallbackAdapter { - private final AvailabilityCallback mLocalCallback; - private Executor mExecutor; + private static class CapabilityBinder extends IImsCapabilityCallback.Stub { + private final OnAvailabilityChangedListener mOnAvailabilityChangedListener; + private final Executor mExecutor; - CapabilityBinder(AvailabilityCallback c) { - mLocalCallback = c; + CapabilityBinder(OnAvailabilityChangedListener listener, Executor executor) { + mExecutor = executor; + mOnAvailabilityChangedListener = listener; } @Override public void onCapabilitiesStatusChanged(int config) { - if (mLocalCallback == null) return; + if (mOnAvailabilityChangedListener == null) return; - long callingIdentity = Binder.clearCallingIdentity(); + final long callingIdentity = Binder.clearCallingIdentity(); try { - mExecutor.execute(() -> mLocalCallback.onAvailabilityChanged( - new RcsFeature.RcsImsCapabilities(config))); + mExecutor.execute(() -> + mOnAvailabilityChangedListener.onAvailabilityChanged(config)); } finally { restoreCallingIdentity(callingIdentity); } @@ -111,55 +131,45 @@ public class ImsRcsManager { @Override public void onQueryCapabilityConfiguration(int capability, int radioTech, boolean isEnabled) { - // This is not used for public interfaces. + // This is not used. } @Override public void onChangeCapabilityConfigurationError(int capability, int radioTech, @ImsFeature.ImsCapabilityError int reason) { - // This is not used for public interfaces - } - - private void setExecutor(Executor executor) { - mExecutor = executor; + // This is not used. } } - private final CapabilityBinder mBinder = new CapabilityBinder(this); + private final CapabilityBinder mBinder; - /** - * The availability of the feature's capabilities has changed to either available or - * unavailable. - * <p> - * If unavailable, the feature does not support the capability at the current time. This may - * be due to network or subscription provisioning changes, such as the IMS registration - * being lost, network type changing, or OMA-DM provisioning updates. - * - * @param capabilities The new availability of the capabilities. - */ - public void onAvailabilityChanged(@NonNull RcsFeature.RcsImsCapabilities capabilities) { + AvailabilityCallbackAdapter(@NonNull Executor executor, + @NonNull OnAvailabilityChangedListener listener) { + mBinder = new CapabilityBinder(listener, executor); } /**@hide*/ public final IImsCapabilityCallback getBinder() { return mBinder; } - - private void setExecutor(Executor executor) { - mBinder.setExecutor(executor); - } } private final int mSubId; private final Context mContext; + private final BinderCacheManager<IImsRcsController> mBinderCache; + private final Map<OnAvailabilityChangedListener, AvailabilityCallbackAdapter> + mAvailabilityChangedCallbacks; /** * Use {@link ImsManager#getImsRcsManager(int)} to create an instance of this class. * @hide */ - public ImsRcsManager(Context context, int subId) { + public ImsRcsManager(Context context, int subId, + BinderCacheManager<IImsRcsController> binderCache) { mSubId = subId; mContext = context; + mBinderCache = binderCache; + mAvailabilityChangedCallbacks = new HashMap<>(); } /** @@ -172,10 +182,23 @@ public class ImsRcsManager { } /** - * @hide + * Registers a {@link RegistrationManager.RegistrationCallback} with the system. When the + * callback is registered, it will initiate the callback c to be called with the current + * registration state. + * + * Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE + * READ_PRECISE_PHONE_STATE} or that the calling app has carrier privileges + * (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}). + * + * @param executor The executor the callback events should be run on. + * @param c The {@link RegistrationManager.RegistrationCallback} to be added. + * @see #unregisterImsRegistrationCallback(RegistrationManager.RegistrationCallback) + * @throws ImsException if the subscription associated with this callback is valid, but + * the {@link ImsService} associated with the subscription is not available. This can happen if + * the service crashed, for example. See {@link ImsException#getCode()} for a more detailed + * reason. */ - // @Override add back to RegistrationManager interface once public. - @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE) public void registerImsRegistrationCallback( @NonNull @CallbackExecutor Executor executor, @NonNull RegistrationManager.RegistrationCallback c) @@ -189,7 +212,7 @@ public class ImsRcsManager { IImsRcsController imsRcsController = getIImsRcsController(); if (imsRcsController == null) { - Log.e(TAG, "Register registration callback: IImsRcsController is null"); + Log.w(TAG, "Register registration callback: IImsRcsController is null"); throw new ImsException("Cannot find remote IMS service", ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); } @@ -197,16 +220,29 @@ public class ImsRcsManager { c.setExecutor(executor); try { imsRcsController.registerImsRegistrationCallback(mSubId, c.getBinder()); + } catch (ServiceSpecificException e) { + throw new ImsException(e.toString(), e.errorCode); } catch (RemoteException | IllegalStateException e) { throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); } } /** - * @hide + * Removes an existing {@link RegistrationManager.RegistrationCallback}. + * + * When the subscription associated with this callback is removed (SIM removed, ESIM swap, + * etc...), this callback will automatically be removed. If this method is called for an + * inactive subscription, it will result in a no-op. + * + * Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE + * READ_PRECISE_PHONE_STATE} or that the calling app has carrier privileges + * (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}). + * + * @param c The {@link RegistrationManager.RegistrationCallback} to be removed. + * @see android.telephony.SubscriptionManager.OnSubscriptionsChangedListener + * @see #registerImsRegistrationCallback(Executor, RegistrationCallback) */ - // @Override add back to RegistrationManager interface once public. - @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE) public void unregisterImsRegistrationCallback( @NonNull RegistrationManager.RegistrationCallback c) { if (c == null) { @@ -215,7 +251,7 @@ public class ImsRcsManager { IImsRcsController imsRcsController = getIImsRcsController(); if (imsRcsController == null) { - Log.e(TAG, "Unregister registration callback: IImsRcsController is null"); + Log.w(TAG, "Unregister registration callback: IImsRcsController is null"); throw new IllegalStateException("Cannot find remote IMS service"); } @@ -227,10 +263,21 @@ public class ImsRcsManager { } /** - * @hide + * Gets the registration state of the IMS service. + * + * Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE + * READ_PRECISE_PHONE_STATE} or that the calling app has carrier privileges + * (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}). + * + * @param executor The {@link Executor} that will be used to call the IMS registration state + * callback. + * @param stateCallback A callback called on the supplied {@link Executor} that will contain the + * registration state of the IMS service, which will be one of the + * following: {@link RegistrationManager#REGISTRATION_STATE_NOT_REGISTERED}, + * {@link RegistrationManager#REGISTRATION_STATE_REGISTERING}, or + * {@link RegistrationManager#REGISTRATION_STATE_REGISTERED}. */ - // @Override add back to RegistrationManager interface once public. - @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE) public void getRegistrationState(@NonNull @CallbackExecutor Executor executor, @NonNull @RegistrationManager.ImsRegistrationState Consumer<Integer> stateCallback) { if (stateCallback == null) { @@ -242,7 +289,7 @@ public class ImsRcsManager { IImsRcsController imsRcsController = getIImsRcsController(); if (imsRcsController == null) { - Log.e(TAG, "Get registration state error: IImsRcsController is null"); + Log.w(TAG, "Get registration state error: IImsRcsController is null"); throw new IllegalStateException("Cannot find remote IMS service"); } @@ -259,9 +306,20 @@ public class ImsRcsManager { } /** - * @hide + * Gets the Transport Type associated with the current IMS registration. + * + * Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE + * READ_PRECISE_PHONE_STATE} or that the calling app has carrier privileges + * (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}). + * + * @param executor The {@link Executor} that will be used to call the transportTypeCallback. + * @param transportTypeCallback The transport type associated with the current IMS registration, + * which will be one of following: + * {@see AccessNetworkConstants#TRANSPORT_TYPE_WWAN}, + * {@see AccessNetworkConstants#TRANSPORT_TYPE_WLAN}, or + * {@see AccessNetworkConstants#TRANSPORT_TYPE_INVALID}. */ - @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE) public void getRegistrationTransportType(@NonNull @CallbackExecutor Executor executor, @NonNull @AccessNetworkConstants.TransportType Consumer<Integer> transportTypeCallback) { @@ -274,7 +332,7 @@ public class ImsRcsManager { IImsRcsController imsRcsController = getIImsRcsController(); if (imsRcsController == null) { - Log.e(TAG, "Get registration transport type error: IImsRcsController is null"); + Log.w(TAG, "Get registration transport type error: IImsRcsController is null"); throw new IllegalStateException("Cannot find remote IMS service"); } @@ -292,31 +350,33 @@ public class ImsRcsManager { } /** - * Registers an {@link AvailabilityCallback} with the system, which will provide RCS + * Add an {@link OnAvailabilityChangedListener} with the system, which will provide RCS * availability updates for the subscription specified. * * Use {@link SubscriptionManager.OnSubscriptionsChangedListener} to listen to * subscription changed events and call - * {@link #unregisterRcsAvailabilityCallback(AvailabilityCallback)} to clean up after a - * subscription is removed. + * {@link #removeOnAvailabilityChangedListener(OnAvailabilityChangedListener)} to clean up + * after a subscription is removed. * <p> - * When the callback is registered, it will initiate the callback c to be called with the - * current capabilities. + * When the listener is registered, it will initiate the callback listener to be called with + * the current capabilities. * * @param executor The executor the callback events should be run on. - * @param c The RCS {@link AvailabilityCallback} to be registered. - * @see #unregisterRcsAvailabilityCallback(AvailabilityCallback) + * @param listener The RCS {@link OnAvailabilityChangedListener} to be registered. + * @see #removeOnAvailabilityChangedListener(OnAvailabilityChangedListener) * @throws ImsException if the subscription associated with this instance of * {@link ImsRcsManager} is valid, but the ImsService associated with the subscription is not * available. This can happen if the ImsService has crashed, for example, or if the subscription * becomes inactive. See {@link ImsException#getCode()} for more information on the error codes. * @hide */ + @SystemApi @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) - public void registerRcsAvailabilityCallback(@NonNull @CallbackExecutor Executor executor, - @NonNull AvailabilityCallback c) throws ImsException { - if (c == null) { - throw new IllegalArgumentException("Must include a non-null AvailabilityCallback."); + public void addOnAvailabilityChangedListener(@NonNull @CallbackExecutor Executor executor, + @NonNull OnAvailabilityChangedListener listener) throws ImsException { + if (listener == null) { + throw new IllegalArgumentException("Must include a non-null" + + "OnAvailabilityChangedListener."); } if (executor == null) { throw new IllegalArgumentException("Must include a non-null Executor."); @@ -324,53 +384,61 @@ public class ImsRcsManager { IImsRcsController imsRcsController = getIImsRcsController(); if (imsRcsController == null) { - Log.e(TAG, "Register availability callback: IImsRcsController is null"); + Log.w(TAG, "Add availability changed listener: IImsRcsController is null"); throw new ImsException("Cannot find remote IMS service", ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); } - c.setExecutor(executor); + AvailabilityCallbackAdapter adapter = + addAvailabilityChangedListenerToCollection(executor, listener); try { - imsRcsController.registerRcsAvailabilityCallback(mSubId, c.getBinder()); + imsRcsController.registerRcsAvailabilityCallback(mSubId, adapter.getBinder()); + } catch (ServiceSpecificException e) { + throw new ImsException(e.toString(), e.errorCode); } catch (RemoteException e) { - Log.e(TAG, "Error calling IImsRcsController#registerRcsAvailabilityCallback", e); + Log.w(TAG, "Error calling IImsRcsController#registerRcsAvailabilityCallback", e); throw new ImsException("Remote IMS Service is not available", ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); } } - /** - * Removes an existing RCS {@link AvailabilityCallback}. + /** + * Removes an existing RCS {@link OnAvailabilityChangedListener}. * <p> * When the subscription associated with this callback is removed (SIM removed, ESIM swap, * etc...), this callback will automatically be unregistered. If this method is called for an * inactive subscription, it will result in a no-op. - * @param c The RCS {@link AvailabilityCallback} to be removed. - * @see #registerRcsAvailabilityCallback(Executor, AvailabilityCallback) + * @param listener The RCS {@link OnAvailabilityChangedListener} to be removed. + * @see #addOnAvailabilityChangedListener(Executor, OnAvailabilityChangedListener) * @throws ImsException if the IMS service is not available when calling this method. * See {@link ImsException#getCode()} for more information on the error codes. * @hide */ + @SystemApi @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) - public void unregisterRcsAvailabilityCallback(@NonNull AvailabilityCallback c) - throws ImsException { - if (c == null) { - throw new IllegalArgumentException("Must include a non-null AvailabilityCallback."); + public void removeOnAvailabilityChangedListener( + @NonNull OnAvailabilityChangedListener listener) { + if (listener == null) { + throw new IllegalArgumentException("Must include a non-null" + + "OnAvailabilityChangedListener."); } IImsRcsController imsRcsController = getIImsRcsController(); if (imsRcsController == null) { - Log.e(TAG, "Unregister availability callback: IImsRcsController is null"); - throw new ImsException("Cannot find remote IMS service", - ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); + Log.w(TAG, "Remove availability changed listener: IImsRcsController is null"); + return; + } + + AvailabilityCallbackAdapter callback = + removeAvailabilityChangedListenerFromCollection(listener); + if (callback == null) { + return; } try { - imsRcsController.unregisterRcsAvailabilityCallback(mSubId, c.getBinder()); + imsRcsController.unregisterRcsAvailabilityCallback(mSubId, callback.getBinder()); } catch (RemoteException e) { - Log.e(TAG, "Error calling IImsRcsController#unregisterRcsAvailabilityCallback", e); - throw new ImsException("Remote IMS Service is not available", - ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); + Log.w(TAG, "Error calling IImsRcsController#unregisterRcsAvailabilityCallback", e); } } @@ -381,24 +449,24 @@ public class ImsRcsManager { * RCS capabilities provided over-the-top by applications. * * @param capability The RCS capability to query. - * @param radioTech The radio tech that this capability failed for, defined as - * {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE} or - * {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN}. + * @param radioTech The radio technology type that we are querying. * @return true if the RCS capability is capable for this subscription, false otherwise. This * does not necessarily mean that we are registered for IMS and the capability is available, but * rather the subscription is capable of this service over IMS. - * @see #isAvailable(int) + * @see #isAvailable(int, int) * @see android.telephony.CarrierConfigManager#KEY_USE_RCS_PRESENCE_BOOL + * @see android.telephony.CarrierConfigManager.Ims#KEY_ENABLE_PRESENCE_CAPABILITY_EXCHANGE_BOOL * @throws ImsException if the IMS service is not available when calling this method. * See {@link ImsException#getCode()} for more information on the error codes. * @hide */ + @SystemApi @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) - public boolean isCapable(@RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability, + public boolean isCapable(@RcsUceAdapter.RcsImsCapabilityFlag int capability, @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) throws ImsException { IImsRcsController imsRcsController = getIImsRcsController(); if (imsRcsController == null) { - Log.e(TAG, "isCapable: IImsRcsController is null"); + Log.w(TAG, "isCapable: IImsRcsController is null"); throw new ImsException("Cannot find remote IMS service", ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); } @@ -406,7 +474,7 @@ public class ImsRcsManager { try { return imsRcsController.isCapable(mSubId, capability, radioTech); } catch (RemoteException e) { - Log.e(TAG, "Error calling IImsRcsController#isCapable", e); + Log.w(TAG, "Error calling IImsRcsController#isCapable", e); throw new ImsException("Remote IMS Service is not available", ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); } @@ -419,6 +487,7 @@ public class ImsRcsManager { * RCS capabilities provided by over-the-top by applications. * * @param capability the RCS capability to query. + * @param radioTech The radio technology type that we are querying. * @return true if the RCS capability is currently available for the associated subscription, * false otherwise. If the capability is available, IMS is registered and the service is * currently available over IMS. @@ -427,25 +496,57 @@ public class ImsRcsManager { * See {@link ImsException#getCode()} for more information on the error codes. * @hide */ + @SystemApi @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) - public boolean isAvailable(@RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability) + public boolean isAvailable(@RcsUceAdapter.RcsImsCapabilityFlag int capability, + @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) throws ImsException { IImsRcsController imsRcsController = getIImsRcsController(); if (imsRcsController == null) { - Log.e(TAG, "isAvailable: IImsRcsController is null"); + Log.w(TAG, "isAvailable: IImsRcsController is null"); throw new ImsException("Cannot find remote IMS service", ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); } try { - return imsRcsController.isAvailable(mSubId, capability); + return imsRcsController.isAvailable(mSubId, capability, radioTech); } catch (RemoteException e) { - Log.e(TAG, "Error calling IImsRcsController#isAvailable", e); + Log.w(TAG, "Error calling IImsRcsController#isAvailable", e); throw new ImsException("Remote IMS Service is not available", ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); } } + /** + * Add the {@link OnAvailabilityChangedListener} to collection for tracking. + * @param executor The executor that will be used when the publish state is changed and the + * {@link OnAvailabilityChangedListener} is called. + * @param listener The {@link OnAvailabilityChangedListener} to call the publish state changed. + * @return The {@link AvailabilityCallbackAdapter} to wrapper the + * {@link OnAvailabilityChangedListener} + */ + private AvailabilityCallbackAdapter addAvailabilityChangedListenerToCollection( + @NonNull Executor executor, @NonNull OnAvailabilityChangedListener listener) { + AvailabilityCallbackAdapter adapter = new AvailabilityCallbackAdapter(executor, listener); + synchronized (mAvailabilityChangedCallbacks) { + mAvailabilityChangedCallbacks.put(listener, adapter); + } + return adapter; + } + + /** + * Remove the existing {@link OnAvailabilityChangedListener} from the collection. + * @param listener The {@link OnAvailabilityChangedListener} to remove from the collection. + * @return The wrapper class {@link AvailabilityCallbackAdapter} associated with the + * {@link OnAvailabilityChangedListener}. + */ + private AvailabilityCallbackAdapter removeAvailabilityChangedListenerFromCollection( + @NonNull OnAvailabilityChangedListener listener) { + synchronized (mAvailabilityChangedCallbacks) { + return mAvailabilityChangedCallbacks.remove(listener); + } + } + private IImsRcsController getIImsRcsController() { IBinder binder = TelephonyFrameworkInitializer .getTelephonyServiceManager() diff --git a/telephony/java/android/telephony/ims/ImsReasonInfo.java b/telephony/java/android/telephony/ims/ImsReasonInfo.java index 0d6b31d349e1..dda021e6172f 100644 --- a/telephony/java/android/telephony/ims/ImsReasonInfo.java +++ b/telephony/java/android/telephony/ims/ImsReasonInfo.java @@ -27,7 +27,8 @@ import android.os.Parcelable; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; - +import java.util.HashMap; +import java.util.Map; /** * Provides details on why an IMS call failed. Applications can use the methods in this class to * get local or network fault behind an IMS services failure. For example, if the code is @@ -890,6 +891,12 @@ public final class ImsReasonInfo implements Parcelable { public static final int CODE_WFC_SERVICE_NOT_AVAILABLE_IN_THIS_LOCATION = 1623; /** + * Call failed because of network congestion, resource is not available, + * or no circuit or channel available, etc. + */ + public static final int CODE_NETWORK_CONGESTION = 1624; + + /** * The dialed RTT call should be retried without RTT * @hide */ @@ -1075,6 +1082,7 @@ public final class ImsReasonInfo implements Parcelable { CODE_REJECT_VT_AVPF_NOT_ALLOWED, CODE_REJECT_ONGOING_ENCRYPTED_CALL, CODE_REJECT_ONGOING_CS_CALL, + CODE_NETWORK_CONGESTION, CODE_RETRY_ON_IMS_WITHOUT_RTT, CODE_OEM_CAUSE_1, CODE_OEM_CAUSE_2, @@ -1095,6 +1103,197 @@ public final class ImsReasonInfo implements Parcelable { @Retention(RetentionPolicy.SOURCE) public @interface ImsCode {} + + private static final Map<Integer, String> sImsCodeMap; + static { + sImsCodeMap = new HashMap<>(); + sImsCodeMap.put(CODE_UNSPECIFIED, "CODE_UNSPECIFIED"); + sImsCodeMap.put(CODE_LOCAL_ILLEGAL_ARGUMENT, "CODE_LOCAL_ILLEGAL_ARGUMENT"); + sImsCodeMap.put(CODE_LOCAL_ILLEGAL_STATE, "CODE_LOCAL_ILLEGAL_STATE"); + sImsCodeMap.put(CODE_LOCAL_INTERNAL_ERROR, "CODE_LOCAL_INTERNAL_ERROR"); + sImsCodeMap.put(CODE_LOCAL_IMS_SERVICE_DOWN, "CODE_LOCAL_IMS_SERVICE_DOWN"); + sImsCodeMap.put(CODE_LOCAL_NO_PENDING_CALL, "CODE_LOCAL_NO_PENDING_CALL"); + sImsCodeMap.put(CODE_LOCAL_ENDED_BY_CONFERENCE_MERGE, + "CODE_LOCAL_ENDED_BY_CONFERENCE_MERGE"); + sImsCodeMap.put(CODE_LOCAL_POWER_OFF, "CODE_LOCAL_POWER_OFF"); + sImsCodeMap.put(CODE_LOCAL_LOW_BATTERY, "CODE_LOCAL_LOW_BATTERY"); + sImsCodeMap.put(CODE_LOCAL_NETWORK_NO_SERVICE, "CODE_LOCAL_NETWORK_NO_SERVICE"); + sImsCodeMap.put(CODE_LOCAL_NETWORK_NO_LTE_COVERAGE, "CODE_LOCAL_NETWORK_NO_LTE_COVERAGE"); + sImsCodeMap.put(CODE_LOCAL_NETWORK_ROAMING, "CODE_LOCAL_NETWORK_ROAMING"); + sImsCodeMap.put(CODE_LOCAL_NETWORK_IP_CHANGED, "CODE_LOCAL_NETWORK_IP_CHANGED"); + sImsCodeMap.put(CODE_LOCAL_SERVICE_UNAVAILABLE, "CODE_LOCAL_SERVICE_UNAVAILABLE"); + sImsCodeMap.put(CODE_LOCAL_NOT_REGISTERED, "CODE_LOCAL_NOT_REGISTERED"); + sImsCodeMap.put(CODE_LOCAL_CALL_EXCEEDED, "CODE_LOCAL_CALL_EXCEEDED"); + sImsCodeMap.put(CODE_LOCAL_CALL_BUSY, "CODE_LOCAL_CALL_BUSY"); + sImsCodeMap.put(CODE_LOCAL_CALL_DECLINE, "CODE_LOCAL_CALL_DECLINE"); + sImsCodeMap.put(CODE_LOCAL_CALL_VCC_ON_PROGRESSING, "CODE_LOCAL_CALL_VCC_ON_PROGRESSING"); + sImsCodeMap.put(CODE_LOCAL_CALL_RESOURCE_RESERVATION_FAILED, + "CODE_LOCAL_CALL_RESOURCE_RESERVATION_FAILED"); + sImsCodeMap.put(CODE_LOCAL_CALL_CS_RETRY_REQUIRED, "CODE_LOCAL_CALL_CS_RETRY_REQUIRED"); + sImsCodeMap.put(CODE_LOCAL_CALL_VOLTE_RETRY_REQUIRED, + "CODE_LOCAL_CALL_VOLTE_RETRY_REQUIRED"); + sImsCodeMap.put(CODE_LOCAL_CALL_TERMINATED, "CODE_LOCAL_CALL_TERMINATED"); + sImsCodeMap.put(CODE_LOCAL_HO_NOT_FEASIBLE, "CODE_LOCAL_HO_NOT_FEASIBLE"); + sImsCodeMap.put(CODE_TIMEOUT_1XX_WAITING, "CODE_TIMEOUT_1XX_WAITING"); + sImsCodeMap.put(CODE_TIMEOUT_NO_ANSWER, "CODE_TIMEOUT_NO_ANSWER"); + sImsCodeMap.put(CODE_TIMEOUT_NO_ANSWER_CALL_UPDATE, "CODE_TIMEOUT_NO_ANSWER_CALL_UPDATE"); + sImsCodeMap.put(CODE_CALL_BARRED, "CODE_CALL_BARRED"); + sImsCodeMap.put(CODE_FDN_BLOCKED, "CODE_FDN_BLOCKED"); + sImsCodeMap.put(CODE_IMEI_NOT_ACCEPTED, "CODE_IMEI_NOT_ACCEPTED"); + sImsCodeMap.put(CODE_DIAL_MODIFIED_TO_USSD, "CODE_DIAL_MODIFIED_TO_USSD"); + sImsCodeMap.put(CODE_DIAL_MODIFIED_TO_SS, "CODE_DIAL_MODIFIED_TO_SS"); + sImsCodeMap.put(CODE_DIAL_MODIFIED_TO_DIAL, "CODE_DIAL_MODIFIED_TO_DIAL"); + sImsCodeMap.put(CODE_DIAL_MODIFIED_TO_DIAL_VIDEO, "CODE_DIAL_MODIFIED_TO_DIAL_VIDEO"); + sImsCodeMap.put(CODE_DIAL_VIDEO_MODIFIED_TO_DIAL, "CODE_DIAL_VIDEO_MODIFIED_TO_DIAL"); + sImsCodeMap.put(CODE_DIAL_VIDEO_MODIFIED_TO_DIAL_VIDEO, + "CODE_DIAL_VIDEO_MODIFIED_TO_DIAL_VIDEO"); + sImsCodeMap.put(CODE_DIAL_VIDEO_MODIFIED_TO_SS, "CODE_DIAL_VIDEO_MODIFIED_TO_SS"); + sImsCodeMap.put(CODE_DIAL_VIDEO_MODIFIED_TO_USSD, "CODE_DIAL_VIDEO_MODIFIED_TO_USSD"); + sImsCodeMap.put(CODE_SIP_REDIRECTED, "CODE_SIP_REDIRECTED"); + sImsCodeMap.put(CODE_SIP_BAD_REQUEST, "CODE_SIP_BAD_REQUEST"); + sImsCodeMap.put(CODE_SIP_FORBIDDEN, "CODE_SIP_FORBIDDEN"); + sImsCodeMap.put(CODE_SIP_NOT_FOUND, "CODE_SIP_NOT_FOUND"); + sImsCodeMap.put(CODE_SIP_NOT_SUPPORTED, "CODE_SIP_NOT_SUPPORTED"); + sImsCodeMap.put(CODE_SIP_REQUEST_TIMEOUT, "CODE_SIP_REQUEST_TIMEOUT"); + sImsCodeMap.put(CODE_SIP_TEMPRARILY_UNAVAILABLE, "CODE_SIP_TEMPRARILY_UNAVAILABLE"); + sImsCodeMap.put(CODE_SIP_BAD_ADDRESS, "CODE_SIP_BAD_ADDRESS"); + sImsCodeMap.put(CODE_SIP_BUSY, "CODE_SIP_BUSY"); + sImsCodeMap.put(CODE_SIP_REQUEST_CANCELLED, "CODE_SIP_REQUEST_CANCELLED"); + sImsCodeMap.put(CODE_SIP_NOT_ACCEPTABLE, "CODE_SIP_NOT_ACCEPTABLE"); + sImsCodeMap.put(CODE_SIP_NOT_REACHABLE, "CODE_SIP_NOT_REACHABLE"); + sImsCodeMap.put(CODE_SIP_CLIENT_ERROR, "CODE_SIP_CLIENT_ERROR"); + sImsCodeMap.put(CODE_SIP_TRANSACTION_DOES_NOT_EXIST, "CODE_SIP_TRANSACTION_DOES_NOT_EXIST"); + sImsCodeMap.put(CODE_SIP_SERVER_INTERNAL_ERROR, "CODE_SIP_SERVER_INTERNAL_ERROR"); + sImsCodeMap.put(CODE_SIP_SERVICE_UNAVAILABLE, "CODE_SIP_SERVICE_UNAVAILABLE"); + sImsCodeMap.put(CODE_SIP_SERVER_TIMEOUT, "CODE_SIP_SERVER_TIMEOUT"); + sImsCodeMap.put(CODE_SIP_SERVER_ERROR, "CODE_SIP_SERVER_ERROR"); + sImsCodeMap.put(CODE_SIP_USER_REJECTED, "CODE_SIP_USER_REJECTED"); + sImsCodeMap.put(CODE_SIP_GLOBAL_ERROR, "CODE_SIP_GLOBAL_ERROR"); + sImsCodeMap.put(CODE_EMERGENCY_TEMP_FAILURE, "CODE_EMERGENCY_TEMP_FAILURE"); + sImsCodeMap.put(CODE_EMERGENCY_PERM_FAILURE, "CODE_EMERGENCY_PERM_FAILURE"); + sImsCodeMap.put(CODE_SIP_USER_MARKED_UNWANTED, "CODE_SIP_USER_MARKED_UNWANTED"); + sImsCodeMap.put(CODE_SIP_METHOD_NOT_ALLOWED, "CODE_SIP_METHOD_NOT_ALLOWED"); + sImsCodeMap.put(CODE_SIP_PROXY_AUTHENTICATION_REQUIRED, + "CODE_SIP_PROXY_AUTHENTICATION_REQUIRED"); + sImsCodeMap.put(CODE_SIP_REQUEST_ENTITY_TOO_LARGE, "CODE_SIP_REQUEST_ENTITY_TOO_LARGE"); + sImsCodeMap.put(CODE_SIP_REQUEST_URI_TOO_LARGE, "CODE_SIP_REQUEST_URI_TOO_LARGE"); + sImsCodeMap.put(CODE_SIP_EXTENSION_REQUIRED, "CODE_SIP_EXTENSION_REQUIRED"); + sImsCodeMap.put(CODE_SIP_INTERVAL_TOO_BRIEF, "CODE_SIP_INTERVAL_TOO_BRIEF"); + sImsCodeMap.put(CODE_SIP_CALL_OR_TRANS_DOES_NOT_EXIST, + "CODE_SIP_CALL_OR_TRANS_DOES_NOT_EXIST"); + sImsCodeMap.put(CODE_SIP_LOOP_DETECTED, "CODE_SIP_LOOP_DETECTED"); + sImsCodeMap.put(CODE_SIP_TOO_MANY_HOPS, "CODE_SIP_TOO_MANY_HOPS"); + sImsCodeMap.put(CODE_SIP_AMBIGUOUS, "CODE_SIP_AMBIGUOUS"); + sImsCodeMap.put(CODE_SIP_REQUEST_PENDING, "CODE_SIP_REQUEST_PENDING"); + sImsCodeMap.put(CODE_SIP_UNDECIPHERABLE, "CODE_SIP_UNDECIPHERABLE"); + sImsCodeMap.put(CODE_MEDIA_INIT_FAILED, "CODE_MEDIA_INIT_FAILED"); + sImsCodeMap.put(CODE_MEDIA_NO_DATA, "CODE_MEDIA_NO_DATA"); + sImsCodeMap.put(CODE_MEDIA_NOT_ACCEPTABLE, "CODE_MEDIA_NOT_ACCEPTABLE"); + sImsCodeMap.put(CODE_MEDIA_UNSPECIFIED, "CODE_MEDIA_UNSPECIFIED"); + sImsCodeMap.put(CODE_USER_TERMINATED, "CODE_USER_TERMINATED"); + sImsCodeMap.put(CODE_USER_NOANSWER, "CODE_USER_NOANSWER"); + sImsCodeMap.put(CODE_USER_IGNORE, "CODE_USER_IGNORE"); + sImsCodeMap.put(CODE_USER_DECLINE, "CODE_USER_DECLINE"); + sImsCodeMap.put(CODE_LOW_BATTERY, "CODE_LOW_BATTERY"); + sImsCodeMap.put(CODE_BLACKLISTED_CALL_ID, "CODE_BLACKLISTED_CALL_ID"); + sImsCodeMap.put(CODE_USER_TERMINATED_BY_REMOTE, "CODE_USER_TERMINATED_BY_REMOTE"); + sImsCodeMap.put(CODE_USER_REJECTED_SESSION_MODIFICATION, + "CODE_USER_REJECTED_SESSION_MODIFICATION"); + sImsCodeMap.put(CODE_USER_CANCELLED_SESSION_MODIFICATION, + "CODE_USER_CANCELLED_SESSION_MODIFICATION"); + sImsCodeMap.put(CODE_SESSION_MODIFICATION_FAILED, "CODE_SESSION_MODIFICATION_FAILED"); + sImsCodeMap.put(CODE_UT_NOT_SUPPORTED, "CODE_UT_NOT_SUPPORTED"); + sImsCodeMap.put(CODE_UT_SERVICE_UNAVAILABLE, "CODE_UT_SERVICE_UNAVAILABLE"); + sImsCodeMap.put(CODE_UT_OPERATION_NOT_ALLOWED, "CODE_UT_OPERATION_NOT_ALLOWED"); + sImsCodeMap.put(CODE_UT_NETWORK_ERROR, "CODE_UT_NETWORK_ERROR"); + sImsCodeMap.put(CODE_UT_CB_PASSWORD_MISMATCH, "CODE_UT_CB_PASSWORD_MISMATCH"); + sImsCodeMap.put(CODE_UT_SS_MODIFIED_TO_DIAL, "CODE_UT_SS_MODIFIED_TO_DIAL"); + sImsCodeMap.put(CODE_UT_SS_MODIFIED_TO_USSD, "CODE_UT_SS_MODIFIED_TO_USSD"); + sImsCodeMap.put(CODE_UT_SS_MODIFIED_TO_SS, "CODE_UT_SS_MODIFIED_TO_SS"); + sImsCodeMap.put(CODE_UT_SS_MODIFIED_TO_DIAL_VIDEO, "CODE_UT_SS_MODIFIED_TO_DIAL_VIDEO"); + sImsCodeMap.put(CODE_ECBM_NOT_SUPPORTED, "CODE_ECBM_NOT_SUPPORTED"); + sImsCodeMap.put(CODE_MULTIENDPOINT_NOT_SUPPORTED, "CODE_MULTIENDPOINT_NOT_SUPPORTED"); + sImsCodeMap.put(CODE_REGISTRATION_ERROR, "CODE_REGISTRATION_ERROR"); + sImsCodeMap.put(CODE_ANSWERED_ELSEWHERE, "CODE_ANSWERED_ELSEWHERE"); + sImsCodeMap.put(CODE_CALL_PULL_OUT_OF_SYNC, "CODE_CALL_PULL_OUT_OF_SYNC"); + sImsCodeMap.put(CODE_CALL_END_CAUSE_CALL_PULL, "CODE_CALL_END_CAUSE_CALL_PULL"); + sImsCodeMap.put(CODE_CALL_DROP_IWLAN_TO_LTE_UNAVAILABLE, + "CODE_CALL_DROP_IWLAN_TO_LTE_UNAVAILABLE"); + sImsCodeMap.put(CODE_REJECTED_ELSEWHERE, "CODE_REJECTED_ELSEWHERE"); + sImsCodeMap.put(CODE_SUPP_SVC_FAILED, "CODE_SUPP_SVC_FAILED"); + sImsCodeMap.put(CODE_SUPP_SVC_CANCELLED, "CODE_SUPP_SVC_CANCELLED"); + sImsCodeMap.put(CODE_SUPP_SVC_REINVITE_COLLISION, "CODE_SUPP_SVC_REINVITE_COLLISION"); + sImsCodeMap.put(CODE_IWLAN_DPD_FAILURE, "CODE_IWLAN_DPD_FAILURE"); + sImsCodeMap.put(CODE_EPDG_TUNNEL_ESTABLISH_FAILURE, "CODE_EPDG_TUNNEL_ESTABLISH_FAILURE"); + sImsCodeMap.put(CODE_EPDG_TUNNEL_REKEY_FAILURE, "CODE_EPDG_TUNNEL_REKEY_FAILURE"); + sImsCodeMap.put(CODE_EPDG_TUNNEL_LOST_CONNECTION, "CODE_EPDG_TUNNEL_LOST_CONNECTION"); + sImsCodeMap.put(CODE_MAXIMUM_NUMBER_OF_CALLS_REACHED, + "CODE_MAXIMUM_NUMBER_OF_CALLS_REACHED"); + sImsCodeMap.put(CODE_REMOTE_CALL_DECLINE, "CODE_REMOTE_CALL_DECLINE"); + sImsCodeMap.put(CODE_DATA_LIMIT_REACHED, "CODE_DATA_LIMIT_REACHED"); + sImsCodeMap.put(CODE_DATA_DISABLED, "CODE_DATA_DISABLED"); + sImsCodeMap.put(CODE_WIFI_LOST, "CODE_WIFI_LOST"); + sImsCodeMap.put(CODE_IKEV2_AUTH_FAILURE, "CODE_IKEV2_AUTH_FAILURE"); + sImsCodeMap.put(CODE_RADIO_OFF, "CODE_RADIO_OFF"); + sImsCodeMap.put(CODE_NO_VALID_SIM, "CODE_NO_VALID_SIM"); + sImsCodeMap.put(CODE_RADIO_INTERNAL_ERROR, "CODE_RADIO_INTERNAL_ERROR"); + sImsCodeMap.put(CODE_NETWORK_RESP_TIMEOUT, "CODE_NETWORK_RESP_TIMEOUT"); + sImsCodeMap.put(CODE_NETWORK_REJECT, "CODE_NETWORK_REJECT"); + sImsCodeMap.put(CODE_RADIO_ACCESS_FAILURE, "CODE_RADIO_ACCESS_FAILURE"); + sImsCodeMap.put(CODE_RADIO_LINK_FAILURE, "CODE_RADIO_LINK_FAILURE"); + sImsCodeMap.put(CODE_RADIO_LINK_LOST, "CODE_RADIO_LINK_LOST"); + sImsCodeMap.put(CODE_RADIO_UPLINK_FAILURE, "CODE_RADIO_UPLINK_FAILURE"); + sImsCodeMap.put(CODE_RADIO_SETUP_FAILURE, "CODE_RADIO_SETUP_FAILURE"); + sImsCodeMap.put(CODE_RADIO_RELEASE_NORMAL, "CODE_RADIO_RELEASE_NORMAL"); + sImsCodeMap.put(CODE_RADIO_RELEASE_ABNORMAL, "CODE_RADIO_RELEASE_ABNORMAL"); + sImsCodeMap.put(CODE_ACCESS_CLASS_BLOCKED, "CODE_ACCESS_CLASS_BLOCKED"); + sImsCodeMap.put(CODE_NETWORK_DETACH, "CODE_NETWORK_DETACH"); + sImsCodeMap.put(CODE_SIP_ALTERNATE_EMERGENCY_CALL, "CODE_SIP_ALTERNATE_EMERGENCY_CALL"); + sImsCodeMap.put(CODE_UNOBTAINABLE_NUMBER, "CODE_UNOBTAINABLE_NUMBER"); + sImsCodeMap.put(CODE_NO_CSFB_IN_CS_ROAM, "CODE_NO_CSFB_IN_CS_ROAM"); + sImsCodeMap.put(CODE_REJECT_UNKNOWN, "CODE_REJECT_UNKNOWN"); + sImsCodeMap.put(CODE_REJECT_ONGOING_CALL_WAITING_DISABLED, + "CODE_REJECT_ONGOING_CALL_WAITING_DISABLED"); + sImsCodeMap.put(CODE_REJECT_CALL_ON_OTHER_SUB, "CODE_REJECT_CALL_ON_OTHER_SUB"); + sImsCodeMap.put(CODE_REJECT_1X_COLLISION, "CODE_REJECT_1X_COLLISION"); + sImsCodeMap.put(CODE_REJECT_SERVICE_NOT_REGISTERED, "CODE_REJECT_SERVICE_NOT_REGISTERED"); + sImsCodeMap.put(CODE_REJECT_CALL_TYPE_NOT_ALLOWED, "CODE_REJECT_CALL_TYPE_NOT_ALLOWED"); + sImsCodeMap.put(CODE_REJECT_ONGOING_E911_CALL, "CODE_REJECT_ONGOING_E911_CALL"); + sImsCodeMap.put(CODE_REJECT_ONGOING_CALL_SETUP, "CODE_REJECT_ONGOING_CALL_SETUP"); + sImsCodeMap.put(CODE_REJECT_MAX_CALL_LIMIT_REACHED, "CODE_REJECT_MAX_CALL_LIMIT_REACHED"); + sImsCodeMap.put(CODE_REJECT_UNSUPPORTED_SIP_HEADERS, "CODE_REJECT_UNSUPPORTED_SIP_HEADERS"); + sImsCodeMap.put(CODE_REJECT_UNSUPPORTED_SDP_HEADERS, "CODE_REJECT_UNSUPPORTED_SDP_HEADERS"); + sImsCodeMap.put(CODE_REJECT_ONGOING_CALL_TRANSFER, "CODE_REJECT_ONGOING_CALL_TRANSFER"); + sImsCodeMap.put(CODE_REJECT_INTERNAL_ERROR, "CODE_REJECT_INTERNAL_ERROR"); + sImsCodeMap.put(CODE_REJECT_QOS_FAILURE, "CODE_REJECT_QOS_FAILURE"); + sImsCodeMap.put(CODE_REJECT_ONGOING_HANDOVER, "CODE_REJECT_ONGOING_HANDOVER"); + sImsCodeMap.put(CODE_REJECT_VT_TTY_NOT_ALLOWED, "CODE_REJECT_VT_TTY_NOT_ALLOWED"); + sImsCodeMap.put(CODE_REJECT_ONGOING_CALL_UPGRADE, "CODE_REJECT_ONGOING_CALL_UPGRADE"); + sImsCodeMap.put(CODE_REJECT_CONFERENCE_TTY_NOT_ALLOWED, + "CODE_REJECT_CONFERENCE_TTY_NOT_ALLOWED"); + sImsCodeMap.put(CODE_REJECT_ONGOING_CONFERENCE_CALL, "CODE_REJECT_ONGOING_CONFERENCE_CALL"); + sImsCodeMap.put(CODE_REJECT_VT_AVPF_NOT_ALLOWED, "CODE_REJECT_VT_AVPF_NOT_ALLOWED"); + sImsCodeMap.put(CODE_REJECT_ONGOING_ENCRYPTED_CALL, "CODE_REJECT_ONGOING_ENCRYPTED_CALL"); + sImsCodeMap.put(CODE_REJECT_ONGOING_CS_CALL, "CODE_REJECT_ONGOING_CS_CALL"); + sImsCodeMap.put(CODE_NETWORK_CONGESTION, "CODE_NETWORK_CONGESTION"); + sImsCodeMap.put(CODE_RETRY_ON_IMS_WITHOUT_RTT, "CODE_RETRY_ON_IMS_WITHOUT_RTT"); + sImsCodeMap.put(CODE_OEM_CAUSE_1, "CODE_OEM_CAUSE_1"); + sImsCodeMap.put(CODE_OEM_CAUSE_2, "CODE_OEM_CAUSE_2"); + sImsCodeMap.put(CODE_OEM_CAUSE_3, "CODE_OEM_CAUSE_3"); + sImsCodeMap.put(CODE_OEM_CAUSE_4, "CODE_OEM_CAUSE_4"); + sImsCodeMap.put(CODE_OEM_CAUSE_5, "CODE_OEM_CAUSE_5"); + sImsCodeMap.put(CODE_OEM_CAUSE_6, "CODE_OEM_CAUSE_6"); + sImsCodeMap.put(CODE_OEM_CAUSE_7, "CODE_OEM_CAUSE_7"); + sImsCodeMap.put(CODE_OEM_CAUSE_8, "CODE_OEM_CAUSE_8"); + sImsCodeMap.put(CODE_OEM_CAUSE_9, "CODE_OEM_CAUSE_9"); + sImsCodeMap.put(CODE_OEM_CAUSE_10, "CODE_OEM_CAUSE_10"); + sImsCodeMap.put(CODE_OEM_CAUSE_11, "CODE_OEM_CAUSE_11"); + sImsCodeMap.put(CODE_OEM_CAUSE_12, "CODE_OEM_CAUSE_12"); + sImsCodeMap.put(CODE_OEM_CAUSE_13, "CODE_OEM_CAUSE_13"); + sImsCodeMap.put(CODE_OEM_CAUSE_14, "CODE_OEM_CAUSE_14"); + sImsCodeMap.put(CODE_OEM_CAUSE_15, "CODE_OEM_CAUSE_15"); + } + /** * Network string error messages. * mExtraMessage may have these values. @@ -1131,6 +1330,13 @@ public final class ImsReasonInfo implements Parcelable { */ public static final int EXTRA_CODE_CALL_RETRY_BY_SETTINGS = 3; + /** + * An extra that may be populated when the {@link #CODE_LOCAL_CALL_CS_RETRY_REQUIRED} result has + * been returned. + * <p> + * Try to connect the call using CS as emergency + */ + public static final int EXTRA_CODE_CALL_RETRY_EMERGENCY = 4; // For main reason code /** @hide */ @@ -1162,7 +1368,7 @@ public final class ImsReasonInfo implements Parcelable { } /** @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public ImsReasonInfo(int code, int extraCode) { mCode = code; mExtraCode = extraCode; @@ -1203,7 +1409,9 @@ public final class ImsReasonInfo implements Parcelable { @NonNull @Override public String toString() { - return "ImsReasonInfo :: {" + mCode + ", " + mExtraCode + ", " + mExtraMessage + "}"; + String imsCode = (sImsCodeMap.containsKey(mCode)) ? sImsCodeMap.get(mCode) : "UNKNOWN_CODE"; + return "ImsReasonInfo :: {" + mCode + " : " + imsCode + ", " + + mExtraCode + ", " + mExtraMessage + "}"; } @Override diff --git a/telephony/java/android/telephony/ims/ImsRegistrationAttributes.aidl b/telephony/java/android/telephony/ims/ImsRegistrationAttributes.aidl new file mode 100644 index 000000000000..0830ff2ff050 --- /dev/null +++ b/telephony/java/android/telephony/ims/ImsRegistrationAttributes.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.ims; + +parcelable ImsRegistrationAttributes; diff --git a/telephony/java/android/telephony/ims/ImsRegistrationAttributes.java b/telephony/java/android/telephony/ims/ImsRegistrationAttributes.java new file mode 100644 index 000000000000..ccb3231526dd --- /dev/null +++ b/telephony/java/android/telephony/ims/ImsRegistrationAttributes.java @@ -0,0 +1,252 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.ims; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.os.Parcel; +import android.os.Parcelable; +import android.telephony.AccessNetworkConstants; +import android.telephony.ims.stub.ImsRegistrationImplBase; +import android.util.ArraySet; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Objects; +import java.util.Set; + +/** + * Contains the attributes associated with the current IMS registration. + */ +public final class ImsRegistrationAttributes implements Parcelable { + + /** + * Attribute to specify if an EPDG tunnel is setup over the cellular internet APN. + * <p> + * If IMS is registered through an EPDG tunnel is setup over the cellular internet APN then this + * bit will be set. If IMS is registered through the IMS APN, then this bit will not be set. + * + */ + public static final int ATTR_EPDG_OVER_CELL_INTERNET = 1 << 0; + + /** @hide */ + // Defines the underlying radio technology type that we have registered for IMS over. + @IntDef(prefix = "ATTR_", + value = { + ATTR_EPDG_OVER_CELL_INTERNET, + }, + flag = true) + @Retention(RetentionPolicy.SOURCE) + public @interface ImsAttributeFlag {} + + /** + * Builder for creating {@link ImsRegistrationAttributes} instances. + * @hide + */ + @SystemApi + public static final class Builder { + private final int mRegistrationTech; + private Set<String> mFeatureTags = Collections.emptySet(); + + /** + * Build a new instance of {@link ImsRegistrationAttributes}. + * + * @param registrationTech The Radio Access Technology that IMS is registered on. + */ + public Builder(@ImsRegistrationImplBase.ImsRegistrationTech int registrationTech) { + mRegistrationTech = registrationTech; + } + + /** + * Optional IMS feature tags included in this IMS registration. + * @param tags A set of Strings containing the MMTEL and RCS feature tags associated with + * the IMS registration. This information is used for services such as the UCE + * service to ascertain the complete IMS registration state to ensure the SIP + * PUBLISH is accurate. The format of the set of feature tags must be one feature + * tag key and value per entry. Each feature tag will contain the feature tag name + * and string value (if applicable), even if they have the same feature tag name. + * For example, + * {@code +g.3gpp.icsi-ref="urn%3Aurn-7%3A3gpp-service.ims.icsi.oma.cpm.msg, + * urn%3Aurn-7%3A3gpp-service.ims.icsi.oma.cpm.session", +g.gsma.callcomposer} must + * be split into three feature tag entries: + * {@code {+g.3gpp.icsi-ref="urn%3Aurn-7%3A3gpp-service.ims.icsi.oma.cpm.msg", + * +g.3gpp.icsi-ref="urn%3Aurn-7%3A3gpp-service.ims.icsi.oma.cpm.session", + * +g.gsma.callcomposer}}. + */ + public @NonNull Builder setFeatureTags(@NonNull Set<String> tags) { + if (tags == null) { + throw new IllegalArgumentException("feature tag set must not be null"); + } + mFeatureTags = new ArraySet<>(tags); + return this; + } + + /** + * @return A new instance created from this builder. + */ + public @NonNull ImsRegistrationAttributes build() { + return new ImsRegistrationAttributes(mRegistrationTech, + RegistrationManager.getAccessType(mRegistrationTech), + getAttributeFlags(mRegistrationTech), + mFeatureTags); + } + + /** + * @return attribute flags from the registration technology. + */ + private static int getAttributeFlags(int imsRadioTech) { + int attributes = 0; + if (imsRadioTech == ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM) { + attributes |= ATTR_EPDG_OVER_CELL_INTERNET; + } + return attributes; + } + } + + private final int mRegistrationTech; + private final int mTransportType; + private final int mImsAttributeFlags; + private final ArrayList<String> mFeatureTags; + + /** + * Create a new {@link ImsRegistrationAttributes} instance. + * + * @param registrationTech The technology that IMS has been registered on. + * @param transportType The transport type that IMS has been registered on. + * @param imsAttributeFlags The attributes associated with the IMS registration. + * @param featureTags The feature tags included in the IMS registration. + * @see Builder + * @hide + */ + public ImsRegistrationAttributes( + @ImsRegistrationImplBase.ImsRegistrationTech int registrationTech, + @AccessNetworkConstants.TransportType int transportType, + @ImsAttributeFlag int imsAttributeFlags, + @Nullable Set<String> featureTags) { + mRegistrationTech = registrationTech; + mTransportType = transportType; + mImsAttributeFlags = imsAttributeFlags; + mFeatureTags = new ArrayList<>(featureTags); + } + + /**@hide*/ + public ImsRegistrationAttributes(Parcel source) { + mRegistrationTech = source.readInt(); + mTransportType = source.readInt(); + mImsAttributeFlags = source.readInt(); + mFeatureTags = new ArrayList<>(); + source.readList(mFeatureTags, null /*classloader*/); + } + + /** + * @return The Radio Access Technology that the IMS registration has been registered over. + * @hide + */ + @SystemApi + public @ImsRegistrationImplBase.ImsRegistrationTech int getRegistrationTechnology() { + return mRegistrationTech; + } + + /** + * @return The access network transport type that IMS has been registered over. + */ + public @AccessNetworkConstants.TransportType int getTransportType() { + return mTransportType; + } + + /** + * @return A bit-mask containing attributes associated with the IMS registration. + */ + public @ImsAttributeFlag int getAttributeFlags() { + return mImsAttributeFlags; + } + + /** + * Gets the Set of feature tags associated with the current IMS registration, if the IMS + * service supports supplying this information. + * <p> + * The format of the set of feature tags will be one feature tag key and value per entry and + * will potentially contain MMTEL and RCS feature tags, depending the configuration of the IMS + * service associated with the registration indications. Each feature tag will contain the + * feature tag name and string value (if applicable), even if they have the same feature tag + * name. For example, {@code +g.3gpp.icsi-ref="urn%3Aurn-7%3A3gpp-service.ims.icsi.oma.cpm.msg, + * urn%3Aurn-7%3A3gpp-service.ims.icsi.oma.cpm.session", +g.gsma.callcomposer} will be split + * into three feature tag entries: + * {@code {+g.3gpp.icsi-ref="urn%3Aurn-7%3A3gpp-service.ims.icsi.oma.cpm.msg", + * +g.3gpp.icsi-ref="urn%3Aurn-7%3A3gpp-service.ims.icsi.oma.cpm.session", + * +g.gsma.callcomposer}}. + * @return The Set of feature tags associated with the current IMS registration. + */ + public @NonNull Set<String> getFeatureTags() { + if (mFeatureTags == null) { + return Collections.emptySet(); + } + return new ArraySet<>(mFeatureTags); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mRegistrationTech); + dest.writeInt(mTransportType); + dest.writeInt(mImsAttributeFlags); + dest.writeList(mFeatureTags); + } + + public static final @NonNull Creator<ImsRegistrationAttributes> CREATOR = + new Creator<ImsRegistrationAttributes>() { + @Override + public ImsRegistrationAttributes createFromParcel(Parcel source) { + return new ImsRegistrationAttributes(source); + } + + @Override + public ImsRegistrationAttributes[] newArray(int size) { + return new ImsRegistrationAttributes[size]; + } + }; + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ImsRegistrationAttributes that = (ImsRegistrationAttributes) o; + return mRegistrationTech == that.mRegistrationTech + && mTransportType == that.mTransportType + && mImsAttributeFlags == that.mImsAttributeFlags + && Objects.equals(mFeatureTags, that.mFeatureTags); + } + + @Override + public int hashCode() { + return Objects.hash(mRegistrationTech, mTransportType, mImsAttributeFlags, mFeatureTags); + } + + @Override + public String toString() { + return "ImsRegistrationAttributes { transportType= " + mTransportType + ", attributeFlags=" + + mImsAttributeFlags + ", featureTags=[" + mFeatureTags + "]}"; + } +} diff --git a/telephony/java/android/telephony/ims/ImsService.java b/telephony/java/android/telephony/ims/ImsService.java index 20736ee3287f..9ab5aeb9c34c 100644 --- a/telephony/java/android/telephony/ims/ImsService.java +++ b/telephony/java/android/telephony/ims/ImsService.java @@ -16,6 +16,9 @@ package android.telephony.ims; +import android.annotation.LongDef; +import android.annotation.Nullable; +import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.app.Service; import android.content.Intent; @@ -28,18 +31,25 @@ import android.telephony.ims.aidl.IImsRcsFeature; import android.telephony.ims.aidl.IImsRegistration; import android.telephony.ims.aidl.IImsServiceController; import android.telephony.ims.aidl.IImsServiceControllerListener; +import android.telephony.ims.aidl.ISipTransport; import android.telephony.ims.feature.ImsFeature; import android.telephony.ims.feature.MmTelFeature; import android.telephony.ims.feature.RcsFeature; import android.telephony.ims.stub.ImsConfigImplBase; import android.telephony.ims.stub.ImsFeatureConfiguration; import android.telephony.ims.stub.ImsRegistrationImplBase; +import android.telephony.ims.stub.SipTransportImplBase; import android.util.Log; import android.util.SparseArray; import com.android.ims.internal.IImsFeatureStatusCallback; import com.android.internal.annotations.VisibleForTesting; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.HashMap; +import java.util.Map; + /** * Main ImsService implementation, which binds via the Telephony ImsResolver. Services that extend * ImsService must register the service in their AndroidManifest to be detected by the framework. @@ -96,6 +106,61 @@ public class ImsService extends Service { private static final String LOG_TAG = "ImsService"; /** + * This ImsService supports the capability to place emergency calls over MMTEL. + * <p> + * Note: This should never be set by {@link #getImsServiceCapabilities()}, as whether it is + * there or not depends on whether or not {@link ImsFeature#FEATURE_EMERGENCY_MMTEL} is defined + * for this ImsService. If it is set, it will be removed during sanitization before the final + * capabilities bitfield is sent back to the framework. + * @hide This is encoded into the {@link ImsFeature#FEATURE_EMERGENCY_MMTEL}, but we will be + * adding other capabilities in a central location, so track this capability here as well. + */ + public static final long CAPABILITY_EMERGENCY_OVER_MMTEL = 1 << 0; + + /** + * This ImsService supports the capability to create SIP delegates for other IMS applications + * to use to proxy SIP messaging traffic through it. + * <p> + * In order for the framework to report SipDelegate creation as being available for this + * ImsService implementation, this ImsService must report this capability flag in + * {@link #getImsServiceCapabilities()}, {@link #getSipTransport(int)} must not return null, and + * this ImsService MUST report the ability to create both {@link ImsFeature#FEATURE_MMTEL} and + * {@link ImsFeature#FEATURE_RCS} features. + */ + public static final long CAPABILITY_SIP_DELEGATE_CREATION = 1 << 1; + + /** + * Used for internal correctness checks of capabilities set by the ImsService implementation and + * tracks the index of the largest defined flag in the capabilities long. + * @hide + */ + public static final long CAPABILITY_MAX_INDEX = + Long.numberOfTrailingZeros(CAPABILITY_SIP_DELEGATE_CREATION); + + /** + * @hide + */ + @LongDef(flag = true, + prefix = "CAPABILITY_", + value = { + // CAPABILITY_EMERGENCY_OVER_MMTEL is not included here because it is managed by + // whether or not ImsFeature.FEATURE_EMERGENCY_MMTEL feature is set and should + // not be set by users of ImsService. + CAPABILITY_SIP_DELEGATE_CREATION + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ImsServiceCapability {} + + /** + * Used for logging purposes, see {@link #getCapabilitiesString(long)} + * @hide + */ + private static final Map<Long, String> CAPABILITIES_LOG_MAP = new HashMap<Long, String>() {{ + put(CAPABILITY_EMERGENCY_OVER_MMTEL, "EMERGENCY_OVER_MMTEL"); + put(CAPABILITY_SIP_DELEGATE_CREATION, "SIP_DELEGATE_CREATION"); + }}; + + /** * The intent that must be defined as an intent-filter in the AndroidManifest of the ImsService. * @hide */ @@ -135,18 +200,30 @@ public class ImsService extends Service { } @Override - public IImsMmTelFeature createMmTelFeature(int slotId, IImsFeatureStatusCallback c) { - return createMmTelFeatureInternal(slotId, c); + public IImsMmTelFeature createMmTelFeature(int slotId) { + return createMmTelFeatureInternal(slotId); + } + + @Override + public IImsRcsFeature createRcsFeature(int slotId) { + return createRcsFeatureInternal(slotId); + } + + @Override + public void addFeatureStatusCallback(int slotId, int featureType, + IImsFeatureStatusCallback c) { + ImsService.this.addImsFeatureStatusCallback(slotId, featureType, c); } @Override - public IImsRcsFeature createRcsFeature(int slotId, IImsFeatureStatusCallback c) { - return createRcsFeatureInternal(slotId, c); + public void removeFeatureStatusCallback(int slotId, int featureType, + IImsFeatureStatusCallback c) { + ImsService.this.removeImsFeatureStatusCallback(slotId, featureType, c); } @Override - public void removeImsFeature(int slotId, int featureType, IImsFeatureStatusCallback c) { - ImsService.this.removeImsFeature(slotId, featureType, c); + public void removeImsFeature(int slotId, int featureType) { + ImsService.this.removeImsFeature(slotId, featureType); } @Override @@ -155,6 +232,17 @@ public class ImsService extends Service { } @Override + public long getImsServiceCapabilities() { + long caps = ImsService.this.getImsServiceCapabilities(); + long sanitizedCaps = sanitizeCapabilities(caps); + if (caps != sanitizedCaps) { + Log.w(LOG_TAG, "removing invalid bits from field: 0x" + + Long.toHexString(caps ^ sanitizedCaps)); + } + return sanitizedCaps; + } + + @Override public void notifyImsServiceReadyForFeatureCreation() { ImsService.this.readyForFeatureCreation(); } @@ -172,6 +260,12 @@ public class ImsService extends Service { } @Override + public ISipTransport getSipTransport(int slotId) { + SipTransportImplBase s = ImsService.this.getSipTransport(slotId); + return s != null ? s.getBinder() : null; + } + + @Override public void enableIms(int slotId) { ImsService.this.enableIms(slotId); } @@ -202,11 +296,10 @@ public class ImsService extends Service { return mFeaturesBySlot.get(slotId); } - private IImsMmTelFeature createMmTelFeatureInternal(int slotId, - IImsFeatureStatusCallback c) { + private IImsMmTelFeature createMmTelFeatureInternal(int slotId) { MmTelFeature f = createMmTelFeature(slotId); if (f != null) { - setupFeature(f, slotId, ImsFeature.FEATURE_MMTEL, c); + setupFeature(f, slotId, ImsFeature.FEATURE_MMTEL); return f.getBinder(); } else { Log.e(LOG_TAG, "createMmTelFeatureInternal: null feature returned."); @@ -214,11 +307,10 @@ public class ImsService extends Service { } } - private IImsRcsFeature createRcsFeatureInternal(int slotId, - IImsFeatureStatusCallback c) { + private IImsRcsFeature createRcsFeatureInternal(int slotId) { RcsFeature f = createRcsFeature(slotId); if (f != null) { - setupFeature(f, slotId, ImsFeature.FEATURE_RCS, c); + setupFeature(f, slotId, ImsFeature.FEATURE_RCS); return f.getBinder(); } else { Log.e(LOG_TAG, "createRcsFeatureInternal: null feature returned."); @@ -226,13 +318,45 @@ public class ImsService extends Service { } } - private void setupFeature(ImsFeature f, int slotId, int featureType, - IImsFeatureStatusCallback c) { + private void setupFeature(ImsFeature f, int slotId, int featureType) { f.initialize(this, slotId); - f.addImsFeatureStatusCallback(c); addImsFeature(slotId, featureType, f); } + private void addImsFeatureStatusCallback(int slotId, int featureType, + IImsFeatureStatusCallback c) { + synchronized (mFeaturesBySlot) { + // get ImsFeature associated with the slot/feature + SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId); + if (features == null) { + Log.w(LOG_TAG, "Can not add ImsFeatureStatusCallback - no features on slot " + + slotId); + return; + } + ImsFeature f = features.get(featureType); + if (f != null) { + f.addImsFeatureStatusCallback(c); + } + } + } + + private void removeImsFeatureStatusCallback(int slotId, int featureType, + IImsFeatureStatusCallback c) { + synchronized (mFeaturesBySlot) { + // get ImsFeature associated with the slot/feature + SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId); + if (features == null) { + Log.w(LOG_TAG, "Can not remove ImsFeatureStatusCallback - no features on slot " + + slotId); + return; + } + ImsFeature f = features.get(featureType); + if (f != null) { + f.removeImsFeatureStatusCallback(c); + } + } + } + private void addImsFeature(int slotId, int featureType, ImsFeature f) { synchronized (mFeaturesBySlot) { // Get SparseArray for Features, by querying slot Id @@ -246,8 +370,7 @@ public class ImsService extends Service { } } - private void removeImsFeature(int slotId, int featureType, - IImsFeatureStatusCallback c) { + private void removeImsFeature(int slotId, int featureType) { synchronized (mFeaturesBySlot) { // get ImsFeature associated with the slot/feature SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId); @@ -262,7 +385,6 @@ public class ImsService extends Service { + featureType + " exists on slot " + slotId); return; } - f.removeImsFeatureStatusCallback(c); f.onFeatureRemoved(); features.remove(featureType); } @@ -297,6 +419,20 @@ public class ImsService extends Service { } /** + * The optional capabilities that this ImsService supports. + * <p> + * This should be a static configuration and should not change at runtime. + * @return The optional static capabilities of this ImsService implementation. + */ + // ImsService follows a different convention, since it is a stub class. The on* methods are + // final and call back into the framework with a state update. + @SuppressLint("OnNameExpected") + public @ImsServiceCapability long getImsServiceCapabilities() { + // Stub implementation to be implemented by ImsService. + return 0L; + } + + /** * The ImsService has been bound and is ready for ImsFeature creation based on the Features that * the ImsService has registered for with the framework, either in the manifest or via * {@link #querySupportedImsFeatures()}. @@ -367,4 +503,63 @@ public class ImsService extends Service { public ImsRegistrationImplBase getRegistration(int slotId) { return new ImsRegistrationImplBase(); } + + /** + * Return the {@link SipTransportImplBase} implementation associated with the provided slot. + * <p> + * This is an optional interface used for devices that must support IMS single registration and + * proxy SIP traffic to remote IMS applications. If this is not supported for this IMS service, + * this method should return {@code null}. If this feature is supported, then this method must + * never be {@code null} and the optional ImsService capability flag + * {@link #CAPABILITY_SIP_DELEGATE_CREATION} must be set in + * {@link #getImsServiceCapabilities()}. Otherwise the framework will assume this feature is not + * supported for this ImsService. + * @param slotId The slot that is associated with the SipTransport implementation. + * @return the SipTransport implementation for the specified slot. + */ + // ImsService follows a different convention, since it is a stub class. The on* methods are + // final and call back into the framework with a state update. + @SuppressLint("OnNameExpected") + public @Nullable SipTransportImplBase getSipTransport(int slotId) { + // Stub implementation for ImsServices that do not support SipTransport. + return null; + } + + private static long sanitizeCapabilities(@ImsServiceCapability long caps) { + long filter = 0xFFFFFFFFFFFFFFFFL; + // pad the "allowed" set with zeros + filter <<= CAPABILITY_MAX_INDEX + 1; + // remove values above the allowed set. + caps &= ~filter; + // CAPABILITY_EMERGENCY_OVER_MMTEL should also not be set here, will be set by telephony + // internally. + caps &= ~CAPABILITY_EMERGENCY_OVER_MMTEL; + return caps; + } + + /** + * @return A string representation of the ImsService capabilities for logging. + * @hide + */ + public static String getCapabilitiesString(@ImsServiceCapability long caps) { + StringBuffer result = new StringBuffer(); + result.append("capabilities={ "); + // filter incrementally fills 0s from left to right. This is used to keep filtering out + // more bits in the long until the remaining leftmost bits are all zero. + long filter = 0xFFFFFFFFFFFFFFFFL; + // position of iterator to potentially print capability. + long i = 0; + while ((caps & filter) != 0 && i <= 63) { + long bitToCheck = (1L << i); + if ((caps & bitToCheck) != 0) { + result.append(CAPABILITIES_LOG_MAP.getOrDefault(bitToCheck, bitToCheck + "?")); + result.append(" "); + } + // shift left by one and fill in another 1 on the leftmost bit. + filter <<= 1; + i++; + } + result.append("}"); + return result.toString(); + } } diff --git a/telephony/java/android/telephony/ims/ImsSsData.java b/telephony/java/android/telephony/ims/ImsSsData.java index fb8e5d37875b..868dea6a3121 100644 --- a/telephony/java/android/telephony/ims/ImsSsData.java +++ b/telephony/java/android/telephony/ims/ImsSsData.java @@ -72,7 +72,7 @@ public final class ImsSsData implements Parcelable { /**@hide*/ - @IntDef(flag = true, prefix = {"SS_"}, value = { + @IntDef(prefix = {"SS_"}, value = { SS_ACTIVATION, SS_DEACTIVATION, SS_INTERROGATION, @@ -89,7 +89,7 @@ public final class ImsSsData implements Parcelable { public static final int SS_ERASURE = 4; /**@hide*/ - @IntDef(flag = true, prefix = {"SS_"}, value = { + @IntDef(prefix = {"SS_"}, value = { SS_ALL_TELE_AND_BEARER_SERVICES, SS_ALL_TELESEVICES, SS_TELEPHONY, @@ -190,7 +190,7 @@ public final class ImsSsData implements Parcelable { public static final int RESULT_SUCCESS = 0; /** @hide */ - @IntDef(flag = true, prefix = { "SS_" }, value = { + @IntDef(prefix = { "SS_" }, value = { SS_CFU, SS_CF_BUSY, SS_CF_NO_REPLY, diff --git a/telephony/java/android/telephony/ims/ImsSsInfo.java b/telephony/java/android/telephony/ims/ImsSsInfo.java index 27b56b8c5b47..bc53b628131e 100644 --- a/telephony/java/android/telephony/ims/ImsSsInfo.java +++ b/telephony/java/android/telephony/ims/ImsSsInfo.java @@ -21,6 +21,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; +import android.os.Build; import android.os.Parcel; import android.os.Parcelable; @@ -155,10 +156,10 @@ public final class ImsSsInfo implements Parcelable { // 0: disabled, 1: enabled /** @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public int mStatus; /** @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public String mIcbNum; /** @hide */ public int mProvisionStatus = SERVICE_PROVISIONING_UNKNOWN; @@ -166,7 +167,7 @@ public final class ImsSsInfo implements Parcelable { private int mClirOutgoingState = CLIR_OUTGOING_DEFAULT; /**@hide*/ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public ImsSsInfo() { } diff --git a/telephony/java/android/telephony/ims/ImsStreamMediaProfile.java b/telephony/java/android/telephony/ims/ImsStreamMediaProfile.java index 131cb1a505fb..d924baee5ab9 100644 --- a/telephony/java/android/telephony/ims/ImsStreamMediaProfile.java +++ b/telephony/java/android/telephony/ims/ImsStreamMediaProfile.java @@ -17,8 +17,10 @@ package android.telephony.ims; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; +import android.os.Build; import android.os.Parcel; import android.os.Parcelable; @@ -84,16 +86,19 @@ public final class ImsStreamMediaProfile implements Parcelable { // Audio related information /** @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public int mAudioQuality; /** @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public int mAudioDirection; + // Audio codec attributes + private AudioCodecAttributes mAudioCodecAttributes; + // Video related information /** @hide */ public int mVideoQuality; /** @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public int mVideoDirection; // Rtt related information /** @hide */ @@ -164,7 +169,7 @@ public final class ImsStreamMediaProfile implements Parcelable { } /** @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public ImsStreamMediaProfile() { mAudioQuality = AUDIO_QUALITY_NONE; mAudioDirection = DIRECTION_SEND_RECEIVE; @@ -190,6 +195,7 @@ public final class ImsStreamMediaProfile implements Parcelable { public void copyFrom(ImsStreamMediaProfile profile) { mAudioQuality = profile.mAudioQuality; mAudioDirection = profile.mAudioDirection; + mAudioCodecAttributes = profile.mAudioCodecAttributes; mVideoQuality = profile.mVideoQuality; mVideoDirection = profile.mVideoDirection; mRttMode = profile.mRttMode; @@ -198,12 +204,13 @@ public final class ImsStreamMediaProfile implements Parcelable { @NonNull @Override public String toString() { - return "{ audioQuality=" + mAudioQuality + - ", audioDirection=" + mAudioDirection + - ", videoQuality=" + mVideoQuality + - ", videoDirection=" + mVideoDirection + - ", rttMode=" + mRttMode + - ", hasRttAudioSpeech=" + mIsReceivingRttAudio + " }"; + return "{ audioQuality=" + mAudioQuality + + ", audioDirection=" + mAudioDirection + + ", audioCodecAttribute=" + mAudioCodecAttributes + + ", videoQuality=" + mVideoQuality + + ", videoDirection=" + mVideoDirection + + ", rttMode=" + mRttMode + + ", hasRttAudioSpeech=" + mIsReceivingRttAudio + " }"; } @Override @@ -215,6 +222,7 @@ public final class ImsStreamMediaProfile implements Parcelable { public void writeToParcel(Parcel out, int flags) { out.writeInt(mAudioQuality); out.writeInt(mAudioDirection); + out.writeTypedObject(mAudioCodecAttributes, flags); out.writeInt(mVideoQuality); out.writeInt(mVideoDirection); out.writeInt(mRttMode); @@ -224,6 +232,7 @@ public final class ImsStreamMediaProfile implements Parcelable { private void readFromParcel(Parcel in) { mAudioQuality = in.readInt(); mAudioDirection = in.readInt(); + mAudioCodecAttributes = in.readTypedObject(AudioCodecAttributes.CREATOR); mVideoQuality = in.readInt(); mVideoDirection = in.readInt(); mRttMode = in.readInt(); @@ -274,6 +283,23 @@ public final class ImsStreamMediaProfile implements Parcelable { return mAudioDirection; } + /** + * Get the audio codec attributes {@link AudioCodecAttributes} which may be {@code null} if + * ImsService doesn't support this information. + * @return audio codec attributes + */ + public @Nullable AudioCodecAttributes getAudioCodecAttributes() { + return mAudioCodecAttributes; + } + + /** + * Set the audio codec attributes {@link AudioCodecAttributes} which includes bitrate and + * bandwidth information. + */ + public void setAudioCodecAttributes(@NonNull AudioCodecAttributes audioCodecAttributes) { + mAudioCodecAttributes = audioCodecAttributes; + } + public int getVideoQuality() { return mVideoQuality; } diff --git a/telephony/java/android/telephony/ims/ImsUtListener.java b/telephony/java/android/telephony/ims/ImsUtListener.java index baa0576cdf13..754814facb71 100644 --- a/telephony/java/android/telephony/ims/ImsUtListener.java +++ b/telephony/java/android/telephony/ims/ImsUtListener.java @@ -178,4 +178,11 @@ public class ImsUtListener { public ImsUtListener(IImsUtListener serviceInterface) { mServiceInterface = serviceInterface; } + + /** + * @hide + */ + public IImsUtListener getListenerInterface() { + return mServiceInterface; + } } diff --git a/telephony/java/android/telephony/ims/ImsVideoCallProvider.java b/telephony/java/android/telephony/ims/ImsVideoCallProvider.java index 2fca4096f447..64bdcbba265a 100644 --- a/telephony/java/android/telephony/ims/ImsVideoCallProvider.java +++ b/telephony/java/android/telephony/ims/ImsVideoCallProvider.java @@ -19,6 +19,7 @@ package android.telephony.ims; import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; import android.net.Uri; +import android.os.Build; import android.os.Handler; import android.os.Looper; import android.os.Message; @@ -179,7 +180,7 @@ public abstract class ImsVideoCallProvider { * Returns binder object which can be used across IPC methods. * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public final IImsVideoCallProvider getInterface() { return mBinder; } diff --git a/telephony/java/android/telephony/ims/OWNERS b/telephony/java/android/telephony/ims/OWNERS new file mode 100644 index 000000000000..0854c5d45603 --- /dev/null +++ b/telephony/java/android/telephony/ims/OWNERS @@ -0,0 +1,5 @@ +# Bug component: 20868 + +rgreenwalt@google.com +tgunn@google.com +breadley@google.com diff --git a/telephony/java/android/telephony/ims/ProvisioningManager.java b/telephony/java/android/telephony/ims/ProvisioningManager.java index 48d14b13854b..abc5606e6743 100644 --- a/telephony/java/android/telephony/ims/ProvisioningManager.java +++ b/telephony/java/android/telephony/ims/ProvisioningManager.java @@ -21,16 +21,20 @@ import android.annotation.CallbackExecutor; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; +import android.annotation.SdkConstant; import android.annotation.StringDef; import android.annotation.SystemApi; import android.annotation.WorkerThread; +import android.content.pm.PackageManager; import android.os.Binder; import android.os.RemoteException; import android.os.ServiceSpecificException; import android.telephony.CarrierConfigManager; import android.telephony.SubscriptionManager; import android.telephony.TelephonyFrameworkInitializer; +import android.telephony.TelephonyManager; import android.telephony.ims.aidl.IImsConfigCallback; +import android.telephony.ims.aidl.IRcsConfigCallback; import android.telephony.ims.feature.MmTelFeature; import android.telephony.ims.feature.RcsFeature; import android.telephony.ims.stub.ImsConfigImplBase; @@ -849,6 +853,36 @@ public class ProvisioningManager { public static final int KEY_RTT_ENABLED = 66; /** + * An obfuscated string defined by the carrier to indicate VoWiFi entitlement status. + * + * <p>Implementation note: how to generate the value and how it affects VoWiFi service + * should follow carrier requirements. For example, set an empty string could result in + * VoWiFi being disabled by IMS service, and set to a specific string could enable. + * + * <p>Value is in String format. + * @see #setProvisioningStringValue(int, String) + * @see #getProvisioningStringValue(int) + */ + public static final int KEY_VOICE_OVER_WIFI_ENTITLEMENT_ID = 67; + + /** + * An integer key representing the voice over IMS opt-in provisioning status for the + * associated subscription. Determines whether the user can see for voice services over + * IMS. + * + * <p> The flag will force to show the VoLTE option in settings irrespective of others VoLTE + * carrier config which hide the VoLTE option (e.g. + * {@link CarrierConfigManager#KEY_HIDE_ENHANCED_4G_LTE_BOOL}). + * + * <p>Use {@link #PROVISIONING_VALUE_ENABLED} to enable VoIMS provisioning and + * {@link #PROVISIONING_VALUE_DISABLED} to disable VoIMS provisioning. + * @see #setProvisioningIntValue(int, int) + * @see #getProvisioningIntValue(int) + * @hide + */ + public static final int KEY_VOIMS_OPT_IN_STATUS = 68; + + /** * Callback for IMS provisioning changes. */ public static class Callback { @@ -864,7 +898,7 @@ public class ProvisioningManager { @Override public final void onIntConfigChanged(int item, int value) { - long callingIdentity = Binder.clearCallingIdentity(); + final long callingIdentity = Binder.clearCallingIdentity(); try { mExecutor.execute(() -> mLocalConfigurationCallback.onProvisioningIntChanged(item, value)); @@ -875,7 +909,7 @@ public class ProvisioningManager { @Override public final void onStringConfigChanged(int item, String value) { - long callingIdentity = Binder.clearCallingIdentity(); + final long callingIdentity = Binder.clearCallingIdentity(); try { mExecutor.execute(() -> mLocalConfigurationCallback.onProvisioningStringChanged(item, value)); @@ -923,6 +957,140 @@ public class ProvisioningManager { private int mSubId; /** + * The callback for RCS provisioning changes. + */ + public static class RcsProvisioningCallback { + private static class CallbackBinder extends IRcsConfigCallback.Stub { + + private final RcsProvisioningCallback mLocalCallback; + private Executor mExecutor; + + private CallbackBinder(RcsProvisioningCallback localCallback) { + mLocalCallback = localCallback; + } + + @Override + public void onConfigurationChanged(byte[] configXml) { + final long identity = Binder.clearCallingIdentity(); + try { + mExecutor.execute(() -> mLocalCallback.onConfigurationChanged(configXml)); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + @Override + public void onAutoConfigurationErrorReceived(int errorCode, String errorString) { + final long identity = Binder.clearCallingIdentity(); + try { + mExecutor.execute(() -> mLocalCallback.onAutoConfigurationErrorReceived( + errorCode, errorString)); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + @Override + public void onConfigurationReset() { + final long identity = Binder.clearCallingIdentity(); + try { + mExecutor.execute(() -> mLocalCallback.onConfigurationReset()); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + @Override + public void onRemoved() { + final long identity = Binder.clearCallingIdentity(); + try { + mExecutor.execute(() -> mLocalCallback.onRemoved()); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + @Override + public void onPreProvisioningReceived(byte[] configXml) { + final long identity = Binder.clearCallingIdentity(); + try { + mExecutor.execute(() -> mLocalCallback.onPreProvisioningReceived(configXml)); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + private void setExecutor(Executor executor) { + mExecutor = executor; + } + } + + private final CallbackBinder mBinder = new CallbackBinder(this); + + /** + * RCS configuration received via OTA provisioning. Configuration may change + * due to various triggers defined in GSMA RCC.14 for ACS(auto configuration + * server) or other operator defined triggers. If RCS provisioning is already + * completed at the time of callback registration, then this method shall be + * invoked with the current configuration. + * @param configXml The RCS configuration XML received by OTA. It is defined + * by GSMA RCC.07. + */ + public void onConfigurationChanged(@NonNull byte[] configXml) {} + + /** + * Errors during autoconfiguration connection setup are notified by the + * ACS(auto configuration server) client using this interface. + * @param errorCode HTTP error received during connection setup defined in + * GSMA RCC.14 2.4.3, like {@link java.net.HttpURLConnection#HTTP_UNAUTHORIZED}, + * {@link java.net.HttpURLConnection#HTTP_FORBIDDEN}, etc. + * @param errorString reason phrase received with the error + */ + public void onAutoConfigurationErrorReceived(int errorCode, + @NonNull String errorString) {} + + /** + * When the previously valid RCS configuration is cleaned up by telephony for + * any case like SIM removed, default messaging application changed, etc., + * this method will be invoked to notify the application regarding this change. + */ + public void onConfigurationReset() {} + + /** + * When the RCS application is no longer the Default messaging application, + * or when the subscription associated with this callback is removed (SIM + * removed, ESIM swap,etc...), callback will automatically be removed and + * the below method is invoked. There is a possibility that the method is + * invoked after the subscription has become inactive + */ + public void onRemoved() {} + + /** + * Some carriers using ACS (auto configuration server) may send a carrier-specific + * pre-provisioning configuration XML if the user has not been provisioned for RCS + * services yet. When this provisioning XML is received, the framework will move + * into a "not provisioned" state for RCS. In order for provisioning to proceed, + * the application must parse this configuration XML and perform the carrier specific + * opt-in flow for RCS services. If the user accepts, {@link #triggerRcsReconfiguration} + * must be called in order for the device to move out of this state and try to fetch + * the RCS provisioning information. + * + * @param configXml the pre-provisioning config in carrier specified format. + */ + public void onPreProvisioningReceived(@NonNull byte[] configXml) {} + + /**@hide*/ + public final IRcsConfigCallback getBinder() { + return mBinder; + } + + /**@hide*/ + public void setExecutor(Executor executor) { + mBinder.setExecutor(executor); + } + } + + /** * Create a new {@link ProvisioningManager} for the subscription specified. * * @param subId The ID of the subscription that this ProvisioningManager will use. @@ -1175,7 +1343,7 @@ public class ProvisioningManager { * provisioning. * <p> * Requires Permission: Manifest.permission.MODIFY_PHONE_STATE or that the calling app has - * carrier privileges (see {@link #hasCarrierPrivileges}). + * carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}). * @param config The XML file to be read. ASCII/UTF8 encoded text if not compressed. * @param isCompressed The XML file is compressed in gzip format and must be decompressed * before being read. @@ -1194,6 +1362,212 @@ public class ProvisioningManager { } + /** + * Provides the single registration capability of the device and the carrier. + * + * <p>This intent only provides the capability and not the current provisioning status of + * the RCS VoLTE single registration feature. Only default messaging application may receive + * the intent. + * + * <p>Contains {@link #EXTRA_SUBSCRIPTION_ID} to specify the subscription index for which + * the intent is valid. and {@link #EXTRA_STATUS} to specify RCS VoLTE single registration + * status. + */ + @RequiresPermission(Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION) + @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_RCS_SINGLE_REGISTRATION_CAPABILITY_UPDATE = + "android.telephony.ims.action.RCS_SINGLE_REGISTRATION_CAPABILITY_UPDATE"; + + /** + * Integer extra to specify subscription index. + */ + public static final String EXTRA_SUBSCRIPTION_ID = + "android.telephony.ims.extra.SUBSCRIPTION_ID"; + + /** + * Integer extra to specify RCS single registration status + * + * <p>The value can be {@link #STATUS_CAPABLE}, {@link #STATUS_DEVICE_NOT_CAPABLE}, + * {@link #STATUS_CARRIER_NOT_CAPABLE}, or bitwise OR of + * {@link #STATUS_DEVICE_NOT_CAPABLE} and {@link #STATUS_CARRIER_NOT_CAPABLE}. + */ + public static final String EXTRA_STATUS = "android.telephony.ims.extra.STATUS"; + + /** + * RCS VoLTE single registration is supported by the device and carrier. + */ + public static final int STATUS_CAPABLE = 0; + + /** + * RCS VoLTE single registration is not supported by the device. + */ + public static final int STATUS_DEVICE_NOT_CAPABLE = 0x01; + + /** + * RCS VoLTE single registration is not supported by the carrier + */ + public static final int STATUS_CARRIER_NOT_CAPABLE = 0x01 << 1; + + /** + * Provide the client configuration parameters of the RCS application. + * + * <p>When this application is also the default messaging application, and RCS + * provisioning is done using autoconfiguration, then these parameters shall be + * sent in the HTTP get request to fetch the RCS provisioning. RCS client + * configuration must be provided by the application before registering for the + * provisioning status events {@link #registerRcsProvisioningCallback()} + * When the IMS/RCS service receives the RCS client configuration, it will detect + * the change in the configuration, and trigger the auto-configuration as needed. + * @param rcc RCS client configuration {@link RcsClientConfiguration} + */ + @RequiresPermission(Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION) + public void setRcsClientConfiguration( + @NonNull RcsClientConfiguration rcc) throws ImsException { + try { + getITelephony().setRcsClientConfiguration(mSubId, rcc); + } catch (ServiceSpecificException e) { + throw new ImsException(e.getMessage(), e.errorCode); + } catch (RemoteException | IllegalStateException e) { + throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); + } + } + + /** + * Returns a flag to indicate whether or not the device supports IMS single registration for + * MMTEL and RCS features as well as if the carrier has provisioned the feature. + * + * <p> Requires Permission: + * <ul> + * <li>{@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE},</li> + * <li>{@link android.Manifest.permission#PERFORM_IMS_SINGLE_REGISTRATION},</li> + * <li>or that the caller has carrier privileges (see + * {@link TelephonyManager#hasCarrierPrivileges()}).</li> + * </ul> + * @return true if IMS single registration is capable at this time, or false otherwise + * @throws ImsException If the remote ImsService is not available for + * any reason or the subscription associated with this instance is no + * longer active. See {@link ImsException#getCode()} for more + * information. + * @see PackageManager#FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION for whether or not this + * device supports IMS single registration. + */ + @RequiresPermission(anyOf = {Manifest.permission.READ_PRIVILEGED_PHONE_STATE, + Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION}) + public boolean isRcsVolteSingleRegistrationCapable() throws ImsException { + try { + return getITelephony().isRcsVolteSingleRegistrationCapable(mSubId); + } catch (RemoteException | IllegalStateException e) { + throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); + } + } + + /** + * Registers a new {@link RcsProvisioningCallback} to listen to changes to + * RCS provisioning xml. + * + * <p>RCS application must be the default messaging application and must + * have already registered its {@link RcsClientConfiguration} by using + * {@link #setRcsClientConfiguration} before it registers the provisioning + * callback. If ProvisioningManager has a valid RCS configuration at the + * time of callback registration and a reconfiguration is not required + * due to RCS client parameters change, then the callback shall be invoked + * immediately with the xml. + * When the subscription associated with this callback is removed (SIM removed, + * ESIM swap,etc...), this callback will automatically be removed. + * <p> Requires Permission: + * <ul> + * <li>{@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE},</li> + * <li>{@link android.Manifest.permission#PERFORM_IMS_SINGLE_REGISTRATION},</li> + * <li>or that the caller has carrier privileges (see + * {@link TelephonyManager#hasCarrierPrivileges()}).</li> + * </ul> + * + * @param executor The {@link Executor} to call the callback methods on + * @param callback The rcs provisioning callback to be registered. + * @see #unregisterRcsProvisioningCallback(RcsProvisioningCallback) + * @see SubscriptionManager.OnSubscriptionsChangedListener + * @throws IllegalArgumentException if the subscription associated with this + * callback is not active (SIM is not inserted, ESIM inactive) or the + * subscription is invalid. + * @throws ImsException if the subscription associated with this callback is + * valid, but the {@link ImsService} associated with the subscription is not + * available. This can happen if the service crashed, for example. + * It shall also throw this exception when the RCS client parameters for the + * application are not valid. In that case application must set the client + * params (See {@link #setRcsClientConfiguration}) and re register the + * callback. + * See {@link ImsException#getCode()} for a more detailed reason. + */ + @RequiresPermission(anyOf = {Manifest.permission.READ_PRIVILEGED_PHONE_STATE, + Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION}) + public void registerRcsProvisioningCallback( + @NonNull @CallbackExecutor Executor executor, + @NonNull RcsProvisioningCallback callback) throws ImsException { + callback.setExecutor(executor); + try { + getITelephony().registerRcsProvisioningCallback(mSubId, callback.getBinder()); + } catch (ServiceSpecificException e) { + throw new ImsException(e.getMessage(), e.errorCode); + } catch (RemoteException | IllegalStateException e) { + throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); + } + } + + /** + * Unregister an existing {@link RcsProvisioningCallback}. Application can + * unregister when its no longer interested in the provisioning updates + * like when a user disables RCS from the UI/settings. + * When the subscription associated with this callback is removed (SIM + * removed, ESIM swap, etc...), this callback will automatically be + * removed. If this method is called for an inactive subscription, it + * will result in a no-op. + * <p> Requires Permission: + * <ul> + * <li>{@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE},</li> + * <li>{@link android.Manifest.permission#PERFORM_IMS_SINGLE_REGISTRATION},</li> + * <li>or that the caller has carrier privileges (see + * {@link TelephonyManager#hasCarrierPrivileges()}).</li> + * </ul> + * + * @param callback The existing {@link RcsProvisioningCallback} to be + * removed. + * @see #registerRcsProvisioningCallback(Executor, RcsProvisioningCallback) + * @throws IllegalArgumentException if the subscription associated with + * this callback is invalid. + */ + @RequiresPermission(anyOf = {Manifest.permission.READ_PRIVILEGED_PHONE_STATE, + Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION}) + public void unregisterRcsProvisioningCallback( + @NonNull RcsProvisioningCallback callback) { + try { + getITelephony().unregisterRcsProvisioningCallback( + mSubId, callback.getBinder()); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + /** + * Reconfiguration triggered by the RCS application. Most likely cause + * is the 403 forbidden to a HTTP request. + * + * <p>When this api is called, the RCS configuration for the associated + * subscription will be removed, and the application which has registered + * {@link RcsProvisioningCallback} may expect to receive + * {@link RcsProvisioningCallback#onConfigurationReset}, then + * {@link RcsProvisioningCallback#onConfigurationChanged} when the new + * RCS configuration is received and notified by + * {@link #notifyRcsAutoConfigurationReceived} + */ + @RequiresPermission(Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION) + public void triggerRcsReconfiguration() { + try { + getITelephony().triggerRcsReconfiguration(mSubId); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + private static ITelephony getITelephony() { ITelephony binder = ITelephony.Stub.asInterface( TelephonyFrameworkInitializer diff --git a/telephony/java/android/telephony/ims/RcsClientConfiguration.aidl b/telephony/java/android/telephony/ims/RcsClientConfiguration.aidl new file mode 100644 index 000000000000..a702f0f42c54 --- /dev/null +++ b/telephony/java/android/telephony/ims/RcsClientConfiguration.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2020 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.ims; + +parcelable RcsClientConfiguration; diff --git a/telephony/java/android/telephony/ims/RcsClientConfiguration.java b/telephony/java/android/telephony/ims/RcsClientConfiguration.java new file mode 100644 index 000000000000..793c37745de6 --- /dev/null +++ b/telephony/java/android/telephony/ims/RcsClientConfiguration.java @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2018 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.ims; + +import android.annotation.NonNull; +import android.annotation.StringDef; +import android.annotation.SystemApi; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.Objects; + +/** + * The container of RCS application related configs. + * + * @hide + */ +@SystemApi +public final class RcsClientConfiguration implements Parcelable { + + /**@hide*/ + @StringDef(prefix = "RCS_PROFILE_", + value = {RCS_PROFILE_1_0, RCS_PROFILE_2_3}) + public @interface StringRcsProfile {} + + /** + * RCS profile UP 1.0 + */ + public static final String RCS_PROFILE_1_0 = "UP_1.0"; + /** + * RCS profile UP 2.3 + */ + public static final String RCS_PROFILE_2_3 = "UP_2.3"; + + private String mRcsVersion; + private String mRcsProfile; + private String mClientVendor; + private String mClientVersion; + + /** + * Create a RcsClientConfiguration object. + * Default messaging application must pass a valid configuration object + * @param rcsVersion The parameter identifies the RCS version supported + * by the client. Refer to GSMA RCC.07 "rcs_version" parameter. + * @param rcsProfile Identifies a fixed set of RCS services that are + * supported by the client. See {@link #RCS_PROFILE_1_0 } or + * {@link #RCS_PROFILE_2_3 } + * @param clientVendor Identifies the vendor providing the RCS client. + * @param clientVersion Identifies the RCS client version. Refer to GSMA + * RCC.07 "client_version" parameter. + * Example:client_version=RCSAndrd-1.0 + */ + public RcsClientConfiguration(@NonNull String rcsVersion, + @NonNull @StringRcsProfile String rcsProfile, + @NonNull String clientVendor, @NonNull String clientVersion) { + mRcsVersion = rcsVersion; + mRcsProfile = rcsProfile; + mClientVendor = clientVendor; + mClientVersion = clientVersion; + } + + /** + * Returns RCS version supported. + */ + public @NonNull String getRcsVersion() { + return mRcsVersion; + } + + /** + * Returns RCS profile supported. + */ + public @NonNull @StringRcsProfile String getRcsProfile() { + return mRcsProfile; + } + + /** + * Returns the name of the vendor providing the RCS client. + */ + public @NonNull String getClientVendor() { + return mClientVendor; + } + + /** + * Returns the RCS client version. + */ + public @NonNull String getClientVersion() { + return mClientVersion; + } + + /** + * {@link Parcelable#writeToParcel} + */ + @Override + public void writeToParcel(@NonNull Parcel out, int flags) { + out.writeString(mRcsVersion); + out.writeString(mRcsProfile); + out.writeString(mClientVendor); + out.writeString(mClientVersion); + } + + /** + * {@link Parcelable.Creator} + * + */ + public static final @android.annotation.NonNull Parcelable.Creator< + RcsClientConfiguration> CREATOR = new Creator<RcsClientConfiguration>() { + @Override + public RcsClientConfiguration createFromParcel(Parcel in) { + String rcsVersion = in.readString(); + String rcsProfile = in.readString(); + String clientVendor = in.readString(); + String clientVersion = in.readString(); + return new RcsClientConfiguration(rcsVersion, rcsProfile, + clientVendor, clientVersion); + } + + @Override + public RcsClientConfiguration[] newArray(int size) { + return new RcsClientConfiguration[size]; + } + }; + + /** + * {@link Parcelable#describeContents} + */ + @Override + public int describeContents() { + return 0; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof RcsClientConfiguration)) { + return false; + } + + RcsClientConfiguration other = (RcsClientConfiguration) obj; + + return mRcsVersion.equals(other.mRcsVersion) && mRcsProfile.equals(other.mRcsProfile) + && mClientVendor.equals(other.mClientVendor) + && mClientVersion.equals(other.mClientVersion); + } + + @Override + public int hashCode() { + return Objects.hash(mRcsVersion, mRcsProfile, mClientVendor, mClientVersion); + } +} diff --git a/telephony/java/android/telephony/ims/RcsConfig.java b/telephony/java/android/telephony/ims/RcsConfig.java new file mode 100644 index 000000000000..fd8d8a76353b --- /dev/null +++ b/telephony/java/android/telephony/ims/RcsConfig.java @@ -0,0 +1,489 @@ +/* + * Copyright (C) 2020 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.ims; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.ContentValues; +import android.content.Context; +import android.database.Cursor; +import android.os.Build; +import android.provider.Telephony.SimInfo; +import android.text.TextUtils; +import android.util.ArrayMap; +import android.util.ArraySet; + +import com.android.telephony.Rlog; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlPullParserFactory; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.zip.GZIPInputStream; +import java.util.zip.GZIPOutputStream; + +/** + * RCS config data and methods to process the config + * @hide + */ +public final class RcsConfig { + private static final String LOG_TAG = "RcsConfig"; + private static final boolean DBG = Build.IS_ENG; + + // Tag and attribute defined in RCC.07 A.2 + private static final String TAG_CHARACTERISTIC = "characteristic"; + private static final String TAG_PARM = "parm"; + private static final String ATTRIBUTE_TYPE = "type"; + private static final String ATTRIBUTE_NAME = "name"; + private static final String ATTRIBUTE_VALUE = "value"; + // Keyword for Rcs Volte single registration defined in RCC.07 A.1.6.2 + private static final String PARM_SINGLE_REGISTRATION = "rcsVolteSingleRegistration"; + + /** + * Characteristic of the RCS provisioning config + */ + public static class Characteristic { + private String mType; + private final Map<String, String> mParms = new ArrayMap<>(); + private final Set<Characteristic> mSubs = new ArraySet<>(); + private final Characteristic mParent; + + private Characteristic(String type, Characteristic parent) { + mType = type; + mParent = parent; + } + + private String getType() { + return mType; + } + + private Map<String, String> getParms() { + return mParms; + } + + private Set<Characteristic> getSubs() { + return mSubs; + } + + private Characteristic getParent() { + return mParent; + } + + private Characteristic getSubByType(String type) { + if (TextUtils.equals(mType, type)) { + return this; + } + Characteristic result = null; + for (Characteristic sub : mSubs) { + result = sub.getSubByType(type); + if (result != null) { + break; + } + } + return result; + } + + private boolean hasSubByType(String type) { + return getSubByType(type) != null; + } + + private String getParmValue(String name) { + String value = mParms.get(name); + if (value == null) { + for (Characteristic sub : mSubs) { + value = sub.getParmValue(name); + if (value != null) { + break; + } + } + } + return value; + } + + boolean hasParm(String name) { + if (mParms.containsKey(name)) { + return true; + } + + for (Characteristic sub : mSubs) { + if (sub.hasParm(name)) { + return true; + } + } + + return false; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder(); + sb.append("[" + mType + "]: "); + if (DBG) { + sb.append(mParms); + } + for (Characteristic sub : mSubs) { + sb.append("\n"); + sb.append(sub.toString().replace("\n", "\n\t")); + } + return sb.toString(); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof Characteristic)) { + return false; + } + + Characteristic o = (Characteristic) obj; + + return TextUtils.equals(mType, o.mType) && mParms.equals(o.mParms) + && mSubs.equals(o.mSubs); + } + + @Override + public int hashCode() { + return Objects.hash(mType, mParms, mSubs); + } + } + + private final Characteristic mRoot; + private Characteristic mCurrent; + private final byte[] mData; + + public RcsConfig(byte[] data) throws IllegalArgumentException { + if (data == null || data.length == 0) { + throw new IllegalArgumentException("Empty data"); + } + mRoot = new Characteristic(null, null); + mCurrent = mRoot; + mData = data; + Characteristic current = mRoot; + ByteArrayInputStream inputStream = new ByteArrayInputStream(data); + try { + XmlPullParserFactory factory = XmlPullParserFactory.newInstance(); + factory.setNamespaceAware(true); + XmlPullParser xpp = factory.newPullParser(); + xpp.setInput(inputStream, null); + int eventType = xpp.getEventType(); + String tag = null; + while (eventType != XmlPullParser.END_DOCUMENT && current != null) { + if (eventType == XmlPullParser.START_TAG) { + tag = xpp.getName().trim().toLowerCase(); + if (TAG_CHARACTERISTIC.equals(tag)) { + int count = xpp.getAttributeCount(); + String type = null; + if (count > 0) { + for (int i = 0; i < count; i++) { + String name = xpp.getAttributeName(i).trim().toLowerCase(); + if (ATTRIBUTE_TYPE.equals(name)) { + type = xpp.getAttributeValue(xpp.getAttributeNamespace(i), + name).trim().toLowerCase(); + break; + } + } + } + Characteristic next = new Characteristic(type, current); + current.getSubs().add(next); + current = next; + } else if (TAG_PARM.equals(tag)) { + int count = xpp.getAttributeCount(); + String key = null; + String value = null; + if (count > 1) { + for (int i = 0; i < count; i++) { + String name = xpp.getAttributeName(i).trim().toLowerCase(); + if (ATTRIBUTE_NAME.equals(name)) { + key = xpp.getAttributeValue(xpp.getAttributeNamespace(i), + name).trim().toLowerCase(); + } else if (ATTRIBUTE_VALUE.equals(name)) { + value = xpp.getAttributeValue(xpp.getAttributeNamespace(i), + name).trim(); + } + } + } + if (key != null && value != null) { + current.getParms().put(key, value); + } + } + } else if (eventType == XmlPullParser.END_TAG) { + tag = xpp.getName().trim().toLowerCase(); + if (TAG_CHARACTERISTIC.equals(tag)) { + current = current.getParent(); + } + tag = null; + } + eventType = xpp.next(); + } + } catch (IOException | XmlPullParserException e) { + throw new IllegalArgumentException(e); + } finally { + try { + inputStream.close(); + } catch (IOException e) { + loge("error to close input stream, skip."); + } + } + } + + /** + * Retrieve a String value of the config item with the tag + * + * @param tag The name of the config to retrieve. + * @param defaultVal Value to return if the config does not exist. + * + * @return Returns the config value if it exists, or defaultVal. + */ + public @Nullable String getString(@NonNull String tag, @Nullable String defaultVal) { + String value = mCurrent.getParmValue(tag.trim().toLowerCase()); + return value != null ? value : defaultVal; + } + + /** + * Retrieve a int value of the config item with the tag + * + * @param tag The name of the config to retrieve. + * @param defaultVal Value to return if the config does not exist or not valid. + * + * @return Returns the config value if it exists and is a valid int, or defaultVal. + */ + public int getInteger(@NonNull String tag, int defaultVal) { + try { + return Integer.parseInt(getString(tag, null)); + } catch (NumberFormatException e) { + logd("error to getInteger for " + tag + " due to " + e); + } + return defaultVal; + } + + /** + * Retrieve a boolean value of the config item with the tag + * + * @param tag The name of the config to retrieve. + * @param defaultVal Value to return if the config does not exist. + * + * @return Returns the config value if it exists, or defaultVal. + */ + public boolean getBoolean(@NonNull String tag, boolean defaultVal) { + String value = getString(tag, null); + return value != null ? Boolean.parseBoolean(value) : defaultVal; + } + + /** + * Check whether the config item exists + * + * @param tag The name of the config to retrieve. + * + * @return Returns true if it exists, or false. + */ + public boolean hasConfig(@NonNull String tag) { + return mCurrent.hasParm(tag.trim().toLowerCase()); + } + + /** + * Return the Characteristic with the given type + */ + public @Nullable Characteristic getCharacteristic(@NonNull String type) { + return mCurrent.getSubByType(type.trim().toLowerCase()); + } + + /** + * Check whether the Characteristic with the given type exists + */ + public boolean hasCharacteristic(@NonNull String type) { + return mCurrent.getSubByType(type.trim().toLowerCase()) != null; + } + + /** + * Set current Characteristic to given Characteristic + */ + public void setCurrentCharacteristic(@NonNull Characteristic current) { + if (current != null) { + mCurrent = current; + } + } + + /** + * Move current Characteristic to parent layer + */ + public boolean moveToParent() { + if (mCurrent.getParent() == null) { + return false; + } + mCurrent = mCurrent.getParent(); + return true; + } + + /** + * Move current Characteristic to the root + */ + public void moveToRoot() { + mCurrent = mRoot; + } + + /** + * Return root Characteristic + */ + public @NonNull Characteristic getRoot() { + return mRoot; + } + + /** + * Return current Characteristic + */ + public @NonNull Characteristic getCurrentCharacteristic() { + return mCurrent; + } + + /** + * Check whether Rcs Volte single registration is supported by the config. + */ + public boolean isRcsVolteSingleRegistrationSupported(boolean isRoaming) { + int val = getInteger(PARM_SINGLE_REGISTRATION, 1); + return isRoaming ? val == 1 : val > 0; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder(); + sb.append("[RCS Config]"); + if (DBG) { + sb.append("=== Root ===\n"); + sb.append(mRoot); + sb.append("=== Current ===\n"); + sb.append(mCurrent); + } + return sb.toString(); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof RcsConfig)) { + return false; + } + + RcsConfig other = (RcsConfig) obj; + + return mRoot.equals(other.mRoot) && mCurrent.equals(other.mCurrent); + } + + @Override + public int hashCode() { + return Objects.hash(mRoot, mCurrent); + } + + /** + * compress the gzip format data + */ + public static @Nullable byte[] compressGzip(@NonNull byte[] data) { + if (data == null || data.length == 0) { + return data; + } + byte[] out = null; + try { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(data.length); + GZIPOutputStream gzipCompressingStream = + new GZIPOutputStream(outputStream); + gzipCompressingStream.write(data); + gzipCompressingStream.close(); + out = outputStream.toByteArray(); + outputStream.close(); + } catch (IOException e) { + loge("Error to compressGzip due to " + e); + } + return out; + } + + /** + * decompress the gzip format data + */ + public static @Nullable byte[] decompressGzip(@NonNull byte[] data) { + if (data == null || data.length == 0) { + return data; + } + byte[] out = null; + try { + ByteArrayInputStream inputStream = new ByteArrayInputStream(data); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + GZIPInputStream gzipDecompressingStream = + new GZIPInputStream(inputStream); + byte[] buf = new byte[1024]; + int size = gzipDecompressingStream.read(buf); + while (size >= 0) { + outputStream.write(buf, 0, size); + size = gzipDecompressingStream.read(buf); + } + gzipDecompressingStream.close(); + inputStream.close(); + out = outputStream.toByteArray(); + outputStream.close(); + } catch (IOException e) { + loge("Error to decompressGzip due to " + e); + } + return out; + } + + /** + * save the config to siminfo db. It is only used internally. + */ + public static void updateConfigForSub(@NonNull Context cxt, int subId, + @NonNull byte[] config, boolean isCompressed) { + //always store gzip compressed data + byte[] data = isCompressed ? config : compressGzip(config); + ContentValues values = new ContentValues(); + values.put(SimInfo.COLUMN_RCS_CONFIG, data); + cxt.getContentResolver().update(SimInfo.CONTENT_URI, values, + SimInfo.COLUMN_UNIQUE_KEY_SUBSCRIPTION_ID + "=" + subId, null); + } + + /** + * load the config from siminfo db. It is only used internally. + */ + public static @Nullable byte[] loadRcsConfigForSub(@NonNull Context cxt, + int subId, boolean isCompressed) { + + byte[] data = null; + + Cursor cursor = cxt.getContentResolver().query(SimInfo.CONTENT_URI, null, + SimInfo.COLUMN_UNIQUE_KEY_SUBSCRIPTION_ID + "=" + subId, null, null); + try { + if (cursor != null && cursor.moveToFirst()) { + data = cursor.getBlob(cursor.getColumnIndexOrThrow(SimInfo.COLUMN_RCS_CONFIG)); + } + } catch (Exception e) { + loge("error to load rcs config for sub:" + subId + " due to " + e); + } finally { + if (cursor != null) { + cursor.close(); + } + } + return isCompressed ? data : decompressGzip(data); + } + + private static void logd(String msg) { + Rlog.d(LOG_TAG, msg); + } + + private static void loge(String msg) { + Rlog.e(LOG_TAG, msg); + } +} diff --git a/telephony/java/android/telephony/ims/RcsContactPresenceTuple.aidl b/telephony/java/android/telephony/ims/RcsContactPresenceTuple.aidl new file mode 100644 index 000000000000..a70470294794 --- /dev/null +++ b/telephony/java/android/telephony/ims/RcsContactPresenceTuple.aidl @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2020 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.ims; + +parcelable RcsContactPresenceTuple; diff --git a/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java b/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java new file mode 100644 index 000000000000..9c28c36521f5 --- /dev/null +++ b/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java @@ -0,0 +1,566 @@ +/* + * Copyright (C) 2020 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.ims; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.StringDef; +import android.annotation.SystemApi; +import android.net.Uri; +import android.os.Build; +import android.os.Parcel; +import android.os.Parcelable; +import android.text.TextUtils; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.time.Instant; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Represents a PIDF tuple element that is part of the presence element returned from the carrier + * network during a SUBSCRIBE request. See RFC3863 for more information. + * @hide + */ +@SystemApi +public final class RcsContactPresenceTuple implements Parcelable { + + private static final String LOG_TAG = "RcsContactPresenceTuple"; + + /** + * The service ID used to indicate that service discovery via presence is available. + * <p> + * See RCC.07 v5.0 specification for more information. + * @hide + */ + public static final String SERVICE_ID_PRESENCE = + "org.3gpp.urn:urn-7:3gpp-application.ims.iari.rcse.dp"; + + /** + * The service ID used to indicate that MMTEL service is available. + * <p> + * See the GSMA RCC.07 specification for more information. + */ + public static final String SERVICE_ID_MMTEL = "org.3gpp.urn:urn-7:3gpp-service.ims.icsi.mmtel"; + + /** + * The service ID used to indicate that the chat(v1.0) is available. + * <p> + * See the GSMA RCC.07 specification for more information. + */ + public static final String SERVICE_ID_CHAT_V1 = "org.openmobilealliance:IM-session"; + + /** + * The service ID used to indicate that the chat(v2.0) is available. + * <p> + * See the GSMA RCC.07 specification for more information. + */ + public static final String SERVICE_ID_CHAT_V2 = "org.openmobilealliance:ChatSession"; + + /** + * The service ID used to indicate that the File Transfer is available. + * <p> + * See the GSMA RCC.07 specification for more information. + */ + public static final String SERVICE_ID_FT = "org.openmobilealliance:File-Transfer-HTTP"; + + /** + * The service ID used to indicate that the File Transfer over SMS is available. + * <p> + * See the GSMA RCC.07 specification for more information. + */ + public static final String SERVICE_ID_FT_OVER_SMS = + "org.3gpp.urn:urn-7:3gpp-application.ims.iari.rcs.ftsms"; + + /** + * The service ID used to indicate that the Geolocation Push is available. + * <p> + * See the GSMA RCC.07 specification for more information. + */ + public static final String SERVICE_ID_GEO_PUSH = + "org.3gpp.urn:urn-7:3gpp-application.ims.iari.rcs.geopush"; + + /** + * The service ID used to indicate that the Geolocation Push via SMS is available. + * <p> + * See the GSMA RCC.07 specification for more information. + */ + public static final String SERVICE_ID_GEO_PUSH_VIA_SMS = + "org.3gpp.urn:urn-7:3gpp-application.ims.iari.rcs.geosms"; + + /** + * The service ID used to indicate that the Call Composer is available. + * <p> + * See the GSMA RCC.07 specification for more information. + */ + public static final String SERVICE_ID_CALL_COMPOSER = + "org.3gpp.urn:urn-7:3gpp-service.ims.icsi.gsma.callcomposer"; + + /** + * The service ID used to indicate that the Post Call is available. + * <p> + * See the GSMA RCC.07 specification for more information. + */ + public static final String SERVICE_ID_POST_CALL = + "org.3gpp.urn:urn-7:3gpp-service.ims.icsi.gsma.callunanswered"; + + /** + * The service ID used to indicate that the Shared Map is available. + * <p> + * See the GSMA RCC.07 specification for more information. + */ + public static final String SERVICE_ID_SHARED_MAP = + "org.3gpp.urn:urn-7:3gpp-service.ims.icsi.gsma.sharedmap"; + + /** + * The service ID used to indicate that the Shared Sketch is available. + * <p> + * See the GSMA RCC.07 specification for more information. + */ + public static final String SERVICE_ID_SHARED_SKETCH = + "org.3gpp.urn:urn-7:3gpp-service.ims.icsi.gsma.sharedsketch"; + + /** + * The service ID used to indicate that the Chatbot using Session is available. + * <p> + * See the GSMA RCC.07 specification for more information. + */ + public static final String SERVICE_ID_CHATBOT = + "org.3gpp.urn:urn-7:3gpp-application.ims.iari.rcs.chatbot"; + + /** + * The service ID used to indicate that the Standalone Messaging is available. + * <p> + * See the GSMA RCC.07 specification for more information. + */ + public static final String SERVICE_ID_CHATBOT_STANDALONE = + " org.3gpp.urn:urn-7:3gpp-application.ims.iari.rcs.chatbot.sa"; + + /** + * The service ID used to indicate that the Chatbot Role is available. + * <p> + * See the GSMA RCC.07 specification for more information. + */ + public static final String SERVICE_ID_CHATBOT_ROLE = "org.gsma.rcs.isbot"; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @StringDef(prefix = "SERVICE_ID_", value = { + SERVICE_ID_MMTEL, + SERVICE_ID_CHAT_V1, + SERVICE_ID_CHAT_V2, + SERVICE_ID_FT, + SERVICE_ID_FT_OVER_SMS, + SERVICE_ID_GEO_PUSH, + SERVICE_ID_GEO_PUSH_VIA_SMS, + SERVICE_ID_CALL_COMPOSER, + SERVICE_ID_POST_CALL, + SERVICE_ID_SHARED_MAP, + SERVICE_ID_SHARED_SKETCH, + SERVICE_ID_CHATBOT, + SERVICE_ID_CHATBOT_STANDALONE, + SERVICE_ID_CHATBOT_ROLE + }) + public @interface ServiceId {} + + /** The service capabilities is available. */ + public static final String TUPLE_BASIC_STATUS_OPEN = "open"; + + /** The service capabilities is unavailable. */ + public static final String TUPLE_BASIC_STATUS_CLOSED = "closed"; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @StringDef(prefix = "TUPLE_BASIC_STATUS_", value = { + TUPLE_BASIC_STATUS_OPEN, + TUPLE_BASIC_STATUS_CLOSED + }) + public @interface BasicStatus {} + + /** + * An optional addition to the PIDF Presence Tuple containing service capabilities, which is + * defined in the servcaps element. See RFC5196, section 3.2.1. + */ + public static final class ServiceCapabilities implements Parcelable { + + /** The service can simultaneously send and receive data. */ + public static final String DUPLEX_MODE_FULL = "full"; + + /** The service can alternate between sending and receiving data.*/ + public static final String DUPLEX_MODE_HALF = "half"; + + /** The service can only receive data. */ + public static final String DUPLEX_MODE_RECEIVE_ONLY = "receive-only"; + + /** The service can only send data. */ + public static final String DUPLEX_MODE_SEND_ONLY = "send-only"; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @StringDef(prefix = "DUPLEX_MODE_", value = { + DUPLEX_MODE_FULL, + DUPLEX_MODE_HALF, + DUPLEX_MODE_RECEIVE_ONLY, + DUPLEX_MODE_SEND_ONLY + }) + public @interface DuplexMode {} + + /** + * Builder to help construct {@link ServiceCapabilities} instances. + */ + public static final class Builder { + + private ServiceCapabilities mCapabilities; + + /** + * Create the ServiceCapabilities builder, which can be used to set service capabilities + * as well as custom capability extensions. + * @param isAudioCapable Whether the audio is capable or not. + * @param isVideoCapable Whether the video is capable or not. + */ + public Builder(boolean isAudioCapable, boolean isVideoCapable) { + mCapabilities = new ServiceCapabilities(isAudioCapable, isVideoCapable); + } + + /** + * Add the supported duplex mode. + * @param mode The supported duplex mode + */ + public @NonNull Builder addSupportedDuplexMode(@NonNull @DuplexMode String mode) { + mCapabilities.mSupportedDuplexModeList.add(mode); + return this; + } + + /** + * Add the unsupported duplex mode. + * @param mode The unsupported duplex mode + */ + public @NonNull Builder addUnsupportedDuplexMode(@NonNull @DuplexMode String mode) { + mCapabilities.mUnsupportedDuplexModeList.add(mode); + return this; + } + + /** + * @return the ServiceCapabilities instance. + */ + public @NonNull ServiceCapabilities build() { + return mCapabilities; + } + } + + private final boolean mIsAudioCapable; + private final boolean mIsVideoCapable; + private final @DuplexMode List<String> mSupportedDuplexModeList = new ArrayList<>(); + private final @DuplexMode List<String> mUnsupportedDuplexModeList = new ArrayList<>(); + + /** + * Use {@link Builder} to build an instance of this interface. + * @param isAudioCapable Whether the audio is capable. + * @param isVideoCapable Whether the video is capable. + */ + ServiceCapabilities(boolean isAudioCapable, boolean isVideoCapable) { + mIsAudioCapable = isAudioCapable; + mIsVideoCapable = isVideoCapable; + } + + private ServiceCapabilities(Parcel in) { + mIsAudioCapable = in.readBoolean(); + mIsVideoCapable = in.readBoolean(); + in.readStringList(mSupportedDuplexModeList); + in.readStringList(mUnsupportedDuplexModeList); + } + + @Override + public void writeToParcel(@NonNull Parcel out, int flags) { + out.writeBoolean(mIsAudioCapable); + out.writeBoolean(mIsVideoCapable); + out.writeStringList(mSupportedDuplexModeList); + out.writeStringList(mUnsupportedDuplexModeList); + } + + @Override + public int describeContents() { + return 0; + } + + public static final @NonNull Creator<ServiceCapabilities> CREATOR = + new Creator<ServiceCapabilities>() { + @Override + public ServiceCapabilities createFromParcel(Parcel in) { + return new ServiceCapabilities(in); + } + + @Override + public ServiceCapabilities[] newArray(int size) { + return new ServiceCapabilities[size]; + } + }; + + /** + * Query the audio capable. + * @return true if the audio is capable, false otherwise. + */ + public boolean isAudioCapable() { + return mIsAudioCapable; + } + + /** + * Query the video capable. + * @return true if the video is capable, false otherwise. + */ + public boolean isVideoCapable() { + return mIsVideoCapable; + } + + /** + * Get the supported duplex mode list. + * @return The list of supported duplex mode + */ + public @NonNull @DuplexMode List<String> getSupportedDuplexModes() { + return Collections.unmodifiableList(mSupportedDuplexModeList); + } + + /** + * Get the unsupported duplex mode list. + * @return The list of unsupported duplex mode + */ + public @NonNull @DuplexMode List<String> getUnsupportedDuplexModes() { + return Collections.unmodifiableList(mUnsupportedDuplexModeList); + } + + @Override + public String toString() { + return "servCaps{" + "a=" + mIsAudioCapable + ", v=" + mIsVideoCapable + + ", supported=" + mSupportedDuplexModeList + ", unsupported=" + + mUnsupportedDuplexModeList + '}'; + } + } + + /** + * Builder to help construct {@link RcsContactPresenceTuple} instances. + */ + public static final class Builder { + + private final RcsContactPresenceTuple mPresenceTuple; + + /** + * Builds a RcsContactPresenceTuple instance. + * @param status The status associated with the service capability. See RFC3865 for more + * information. + * @param serviceId The OMA Presence service-id associated with this capability. See the + * OMA Presence SIMPLE specification v1.1, section 10.5.1. + * @param serviceVersion The OMA Presence version associated with the service capability. + * See the OMA Presence SIMPLE specification v1.1, section 10.5.1. + */ + public Builder(@NonNull @BasicStatus String status, @NonNull @ServiceId String serviceId, + @NonNull String serviceVersion) { + mPresenceTuple = new RcsContactPresenceTuple(status, serviceId, serviceVersion); + } + + /** + * The optional SIP Contact URI associated with the PIDF tuple element if the network + * expects the user to use the URI instead of the contact URI to contact it. + */ + public @NonNull Builder setContactUri(@NonNull Uri contactUri) { + mPresenceTuple.mContactUri = contactUri; + return this; + } + + /** + * The optional timestamp indicating the data and time of the status change of this tuple. + * Per RFC3863 section 4.1.7, the timestamp is formatted as an IMPP datetime format + * string per RFC3339. + */ + public @NonNull Builder setTime(@NonNull Instant timestamp) { + mPresenceTuple.mTimestamp = timestamp; + return this; + } + + /** + * An optional parameter containing the description element of the service-description. See + * OMA Presence SIMPLE specification v1.1 + */ + public @NonNull Builder setServiceDescription(@NonNull String description) { + mPresenceTuple.mServiceDescription = description; + return this; + } + + /** + * An optional parameter containing the service capabilities of the presence tuple if they + * are present in the servcaps element. + */ + public @NonNull Builder setServiceCapabilities(@NonNull ServiceCapabilities caps) { + mPresenceTuple.mServiceCapabilities = caps; + return this; + } + + /** + * @return the constructed instance. + */ + public @NonNull RcsContactPresenceTuple build() { + return mPresenceTuple; + } + } + + private Uri mContactUri; + private Instant mTimestamp; + private @BasicStatus String mStatus; + + // The service information in the service-description element. + private String mServiceId; + private String mServiceVersion; + private String mServiceDescription; + + private ServiceCapabilities mServiceCapabilities; + + private RcsContactPresenceTuple(@NonNull @BasicStatus String status, @NonNull String serviceId, + @NonNull String serviceVersion) { + mStatus = status; + mServiceId = serviceId; + mServiceVersion = serviceVersion; + } + + private RcsContactPresenceTuple(Parcel in) { + mContactUri = in.readParcelable(Uri.class.getClassLoader()); + mTimestamp = convertStringFormatTimeToInstant(in.readString()); + mStatus = in.readString(); + mServiceId = in.readString(); + mServiceVersion = in.readString(); + mServiceDescription = in.readString(); + mServiceCapabilities = in.readParcelable(ServiceCapabilities.class.getClassLoader()); + } + + @Override + public void writeToParcel(@NonNull Parcel out, int flags) { + out.writeParcelable(mContactUri, flags); + out.writeString(convertInstantToStringFormat(mTimestamp)); + out.writeString(mStatus); + out.writeString(mServiceId); + out.writeString(mServiceVersion); + out.writeString(mServiceDescription); + out.writeParcelable(mServiceCapabilities, flags); + } + + @Override + public int describeContents() { + return 0; + } + + public static final @NonNull Creator<RcsContactPresenceTuple> CREATOR = + new Creator<RcsContactPresenceTuple>() { + @Override + public RcsContactPresenceTuple createFromParcel(Parcel in) { + return new RcsContactPresenceTuple(in); + } + + @Override + public RcsContactPresenceTuple[] newArray(int size) { + return new RcsContactPresenceTuple[size]; + } + }; + + // Convert the Instant to the string format + private String convertInstantToStringFormat(Instant instant) { + if (instant == null) { + return ""; + } + return instant.toString(); + } + + // Convert the time string format to Instant + private @Nullable Instant convertStringFormatTimeToInstant(String timestamp) { + if (TextUtils.isEmpty(timestamp)) { + return null; + } + try { + return DateTimeFormatter.ISO_OFFSET_DATE_TIME.parse(timestamp, Instant::from); + } catch (DateTimeParseException e) { + return null; + } + } + + /** @return the status of the tuple element. */ + public @NonNull @BasicStatus String getStatus() { + return mStatus; + } + + /** @return the service-id element of the service-description */ + public @NonNull String getServiceId() { + return mServiceId; + } + + /** @return the version element of the service-description */ + public @NonNull String getServiceVersion() { + return mServiceVersion; + } + + /** @return the SIP URI contained in the contact element of the tuple if it exists. */ + public @Nullable Uri getContactUri() { + return mContactUri; + } + + /** @return the timestamp element contained in the tuple if it exists */ + public @Nullable Instant getTime() { + return mTimestamp; + } + + /** @return the description element contained in the service-description if it exists */ + public @Nullable String getServiceDescription() { + return mServiceDescription; + } + + /** @return the {@link ServiceCapabilities} of the tuple if it exists. */ + public @Nullable ServiceCapabilities getServiceCapabilities() { + return mServiceCapabilities; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder("{"); + if (Build.IS_ENG) { + builder.append("u="); + builder.append(mContactUri); + } else { + builder.append("u="); + builder.append(mContactUri != null ? "XXX" : "null"); + } + builder.append(", id="); + builder.append(mServiceId); + builder.append(", v="); + builder.append(mServiceVersion); + builder.append(", s="); + builder.append(mStatus); + if (mTimestamp != null) { + builder.append(", timestamp="); + builder.append(mTimestamp); + } + if (mServiceDescription != null) { + builder.append(", servDesc="); + builder.append(mServiceDescription); + } + if (mServiceCapabilities != null) { + builder.append(", servCaps="); + builder.append(mServiceCapabilities); + } + builder.append("}"); + return builder.toString(); + } +} diff --git a/telephony/java/android/telephony/ims/RcsContactTerminatedReason.aidl b/telephony/java/android/telephony/ims/RcsContactTerminatedReason.aidl new file mode 100644 index 000000000000..cd1ee8400d3e --- /dev/null +++ b/telephony/java/android/telephony/ims/RcsContactTerminatedReason.aidl @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2020 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.ims; + +parcelable RcsContactTerminatedReason; diff --git a/telephony/java/android/telephony/ims/RcsContactTerminatedReason.java b/telephony/java/android/telephony/ims/RcsContactTerminatedReason.java new file mode 100644 index 000000000000..ee02564267c0 --- /dev/null +++ b/telephony/java/android/telephony/ims/RcsContactTerminatedReason.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2020 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.ims; + +import android.annotation.NonNull; +import android.net.Uri; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * When the resource for the presence subscribe event has been terminated, the method + * SubscribeResponseCallback#onResourceTerminated wil be called with a list of + * RcsContactTerminatedReason. + * @hide + */ +public final class RcsContactTerminatedReason implements Parcelable { + private final Uri mContactUri; + private final String mReason; + + public RcsContactTerminatedReason(Uri contact, String reason) { + mContactUri = contact; + mReason = reason; + } + + private RcsContactTerminatedReason(Parcel in) { + mContactUri = in.readParcelable(Uri.class.getClassLoader()); + mReason = in.readString(); + } + + @Override + public void writeToParcel(@NonNull Parcel out, int flags) { + out.writeParcelable(mContactUri, flags); + out.writeString(mReason); + } + + @Override + public int describeContents() { + return 0; + } + + public static final @NonNull Creator<RcsContactTerminatedReason> CREATOR = + new Creator<RcsContactTerminatedReason>() { + @Override + public RcsContactTerminatedReason createFromParcel(Parcel in) { + return new RcsContactTerminatedReason(in); + } + + @Override + public RcsContactTerminatedReason[] newArray(int size) { + return new RcsContactTerminatedReason[size]; + } + }; + + public Uri getContactUri() { + return mContactUri; + } + + public String getReason() { + return mReason; + } +} diff --git a/telephony/java/android/telephony/ims/RcsContactUceCapability.java b/telephony/java/android/telephony/ims/RcsContactUceCapability.java index dc36edf5aad9..acfa13380948 100644 --- a/telephony/java/android/telephony/ims/RcsContactUceCapability.java +++ b/telephony/java/android/telephony/ims/RcsContactUceCapability.java @@ -16,10 +16,12 @@ package android.telephony.ims; -import android.annotation.LongDef; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SystemApi; import android.net.Uri; +import android.os.Build; import android.os.Parcel; import android.os.Parcelable; @@ -27,124 +29,91 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Collections; -import java.util.HashMap; +import java.util.HashSet; import java.util.List; -import java.util.Map; +import java.util.Set; /** * Contains the User Capability Exchange capabilities corresponding to a contact's URI. * @hide */ +@SystemApi public final class RcsContactUceCapability implements Parcelable { - /** Supports 1-to-1 chat */ - public static final int CAPABILITY_CHAT_STANDALONE = (1 << 0); - /** Supports group chat */ - public static final int CAPABILITY_CHAT_SESSION = (1 << 1); - /** Supports full store and forward group chat information. */ - public static final int CAPABILITY_CHAT_SESSION_STORE_FORWARD = (1 << 2); + /** Contains presence information associated with the contact */ + public static final int CAPABILITY_MECHANISM_PRESENCE = 1; + + /** Contains OPTIONS information associated with the contact */ + public static final int CAPABILITY_MECHANISM_OPTIONS = 2; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = "CAPABILITY_MECHANISM_", value = { + CAPABILITY_MECHANISM_PRESENCE, + CAPABILITY_MECHANISM_OPTIONS + }) + public @interface CapabilityMechanism {} + + /** + * The capabilities of this contact were requested recently enough to still be considered in + * the availability window. + */ + public static final int SOURCE_TYPE_NETWORK = 0; + + /** + * The capabilities of this contact were retrieved from the cached information in the Enhanced + * Address Book. + */ + public static final int SOURCE_TYPE_CACHED = 1; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = "SOURCE_TYPE_", value = { + SOURCE_TYPE_NETWORK, + SOURCE_TYPE_CACHED + }) + public @interface SourceType {} + + /** + * Capability information for the requested contact has expired and can not be refreshed due to + * a temporary network error. This is a temporary error and the capabilities of the contact + * should be queried again at a later time. + */ + public static final int REQUEST_RESULT_UNKNOWN = 0; + + /** + * The requested contact was found to be offline when queried. This is only applicable to + * contact capabilities that were queried via OPTIONS requests and the network returned a + * 408/480 response. + */ + public static final int REQUEST_RESULT_NOT_ONLINE = 1; + + /** + * Capability information for the requested contact was not found. The contact should not be + * considered an RCS user. + */ + public static final int REQUEST_RESULT_NOT_FOUND = 2; + /** - * Supports file transfer via Message Session Relay Protocol (MSRP) without Store and Forward. + * Capability information for the requested contact was found successfully. */ - public static final int CAPABILITY_FILE_TRANSFER = (1 << 3); - /** Supports File Transfer Thumbnail */ - public static final int CAPABILITY_FILE_TRANSFER_THUMBNAIL = (1 << 4); - /** Supports File Transfer with Store and Forward */ - public static final int CAPABILITY_FILE_TRANSFER_STORE_FORWARD = (1 << 5); - /** Supports File Transfer via HTTP */ - public static final int CAPABILITY_FILE_TRANSFER_HTTP = (1 << 6); - /** Supports file transfer via SMS */ - public static final int CAPABILITY_FILE_TRANSFER_SMS = (1 << 7); - /** Supports image sharing */ - public static final int CAPABILITY_IMAGE_SHARE = (1 << 8); - /** Supports video sharing during a circuit-switch call (IR.74)*/ - public static final int CAPABILITY_VIDEO_SHARE_DURING_CS_CALL = (1 << 9); - /** Supports video share outside of voice call (IR.84) */ - public static final int CAPABILITY_VIDEO_SHARE = (1 << 10); - /** Supports social presence information */ - public static final int CAPABILITY_SOCIAL_PRESENCE = (1 << 11); - /** Supports capability discovery via presence */ - public static final int CAPABILITY_DISCOVERY_VIA_PRESENCE = (1 << 12); - /** Supports IP Voice calling over LTE or IWLAN (IR.92/IR.51) */ - public static final int CAPABILITY_IP_VOICE_CALL = (1 << 13); - /** Supports IP video calling (IR.94) */ - public static final int CAPABILITY_IP_VIDEO_CALL = (1 << 14); - /** Supports Geolocation PUSH during 1-to-1 or multiparty chat */ - public static final int CAPABILITY_GEOLOCATION_PUSH = (1 << 15); - /** Supports Geolocation PUSH via SMS for fallback. */ - public static final int CAPABILITY_GEOLOCATION_PUSH_SMS = (1 << 16); - /** Supports Geolocation pull. */ - public static final int CAPABILITY_GEOLOCATION_PULL = (1 << 17); - /** Supports Geolocation pull using file transfer support. */ - public static final int CAPABILITY_GEOLOCATION_PULL_FILE_TRANSFER = (1 << 18); - /** Supports RCS voice calling */ - public static final int CAPABILITY_RCS_VOICE_CALL = (1 << 19); - /** Supports RCS video calling */ - public static final int CAPABILITY_RCS_VIDEO_CALL = (1 << 20); - /** Supports RCS video calling, where video media can not be dropped. */ - public static final int CAPABILITY_RCS_VIDEO_ONLY_CALL = (1 << 21); - /** Supports call composer, where outgoing calls can be enriched with pre-call content.*/ - public static final int CAPABILITY_CALL_COMPOSER = (1 << 22); - /** Supports post call information that is included in the call if the call is missed.*/ - public static final int CAPABILITY_POST_CALL = (1 << 23); - /** Supports sharing a map where the user can draw, share markers, and share their position. */ - public static final int CAPABILITY_SHARED_MAP = (1 << 24); - /** Supports sharing a canvas, where users can draw, add images, and change background colors.*/ - public static final int CAPABILITY_SHARED_SKETCH = (1 << 25); - /** Supports communication with Chatbots. */ - public static final int CAPABILITY_CHAT_BOT = (1 << 26); - /** Supports Chatbot roles. */ - public static final int CAPABILITY_CHAT_BOT_ROLE = (1 << 27); - /** Supports the unidirectional plug-ins framework. */ - public static final int CAPABILITY_PLUG_IN = (1 << 28); - /** Supports standalone Chatbot communication. */ - public static final int CAPABILITY_STANDALONE_CHAT_BOT = (1 << 29); - /** Supports MMTEL based call composer. */ - public static final int CAPABILITY_MMTEL_CALL_COMPOSER = (1 << 30); - - - - /** @hide*/ + public static final int REQUEST_RESULT_FOUND = 3; + + /** @hide */ @Retention(RetentionPolicy.SOURCE) - @LongDef(prefix = "CAPABILITY_", flag = true, value = { - CAPABILITY_CHAT_STANDALONE, - CAPABILITY_CHAT_SESSION, - CAPABILITY_CHAT_SESSION_STORE_FORWARD, - CAPABILITY_FILE_TRANSFER, - CAPABILITY_FILE_TRANSFER_THUMBNAIL, - CAPABILITY_FILE_TRANSFER_STORE_FORWARD, - CAPABILITY_FILE_TRANSFER_HTTP, - CAPABILITY_FILE_TRANSFER_SMS, - CAPABILITY_IMAGE_SHARE, - CAPABILITY_VIDEO_SHARE_DURING_CS_CALL, - CAPABILITY_VIDEO_SHARE, - CAPABILITY_SOCIAL_PRESENCE, - CAPABILITY_DISCOVERY_VIA_PRESENCE, - CAPABILITY_IP_VOICE_CALL, - CAPABILITY_IP_VIDEO_CALL, - CAPABILITY_GEOLOCATION_PUSH, - CAPABILITY_GEOLOCATION_PUSH_SMS, - CAPABILITY_GEOLOCATION_PULL, - CAPABILITY_GEOLOCATION_PULL_FILE_TRANSFER, - CAPABILITY_RCS_VOICE_CALL, - CAPABILITY_RCS_VIDEO_CALL, - CAPABILITY_RCS_VIDEO_ONLY_CALL, - CAPABILITY_CALL_COMPOSER, - CAPABILITY_POST_CALL, - CAPABILITY_SHARED_MAP, - CAPABILITY_SHARED_SKETCH, - CAPABILITY_CHAT_BOT, - CAPABILITY_CHAT_BOT_ROLE, - CAPABILITY_PLUG_IN, - CAPABILITY_STANDALONE_CHAT_BOT, - CAPABILITY_MMTEL_CALL_COMPOSER + @IntDef(prefix = "REQUEST_RESULT_", value = { + REQUEST_RESULT_UNKNOWN, + REQUEST_RESULT_NOT_ONLINE, + REQUEST_RESULT_NOT_FOUND, + REQUEST_RESULT_FOUND }) - public @interface CapabilityFlag {} + public @interface RequestResult {} /** - * Builder to help construct {@link RcsContactUceCapability} instances. + * Builder to help construct {@link RcsContactUceCapability} instances when capabilities were + * queried through SIP OPTIONS. */ - public static class Builder { + public static final class OptionsBuilder { private final RcsContactUceCapability mCapabilities; @@ -153,51 +122,38 @@ public final class RcsContactUceCapability implements Parcelable { * capability extensions. * @param contact The contact URI that the capabilities are attached to. */ - public Builder(@NonNull Uri contact) { - mCapabilities = new RcsContactUceCapability(contact); + public OptionsBuilder(@NonNull Uri contact) { + mCapabilities = new RcsContactUceCapability(contact, CAPABILITY_MECHANISM_OPTIONS, + SOURCE_TYPE_NETWORK); } /** - * Add a UCE capability bit-field as well as the associated URI that the framework should - * use for those services. This is mainly used for capabilities that may use a URI separate - * from the contact's URI, for example the URI to use for VT calls. - * @param type The capability to map to a service URI that is different from the contact's - * URI. + * Set the result of the capabilities request. + * @param requestResult the request result + * @return this OptionBuilder */ - public @NonNull Builder add(@CapabilityFlag long type, @NonNull Uri serviceUri) { - mCapabilities.mCapabilities |= type; - // Put each of these capabilities into the map separately. - for (long shift = 0; shift < Integer.SIZE; shift++) { - long cap = type & (1 << shift); - if (cap != 0) { - mCapabilities.mServiceMap.put(cap, serviceUri); - // remove that capability from the field. - type &= ~cap; - } - if (type == 0) { - // no need to keep going, end early. - break; - } - } + public @NonNull OptionsBuilder setRequestResult(@RequestResult int requestResult) { + mCapabilities.mRequestResult = requestResult; return this; } /** - * Add a UCE capability flag that this contact supports. - * @param type the capability that the contact supports. + * Add the feature tag into the capabilities instance. + * @param tag the supported feature tag + * @return this OptionBuilder */ - public @NonNull Builder add(@CapabilityFlag long type) { - mCapabilities.mCapabilities |= type; + public @NonNull OptionsBuilder addFeatureTag(@NonNull String tag) { + mCapabilities.mFeatureTags.add(tag); return this; } /** - * Add a carrier specific service tag. - * @param extension A string containing a carrier specific service tag that is an extension - * of the {@link CapabilityFlag}s that are defined here. + * Add the list of feature tag into the capabilities instance. + * @param tags the list of the supported feature tags + * @return this OptionBuilder */ - public @NonNull Builder add(@NonNull String extension) { - mCapabilities.mExtensionTags.add(extension); + public @NonNull OptionsBuilder addFeatureTags(@NonNull Set<String> tags) { + mCapabilities.mFeatureTags.addAll(tags); return this; } @@ -209,56 +165,91 @@ public final class RcsContactUceCapability implements Parcelable { } } - private final Uri mContactUri; - private long mCapabilities; - private List<String> mExtensionTags = new ArrayList<>(); - private Map<Long, Uri> mServiceMap = new HashMap<>(); - /** - * Use {@link Builder} to build an instance of this interface. - * @param contact The URI associated with this capability information. - * @hide + * Builder to help construct {@link RcsContactUceCapability} instances when capabilities were + * queried through a presence server. */ - RcsContactUceCapability(@NonNull Uri contact) { - mContactUri = contact; - } + public static final class PresenceBuilder { - private RcsContactUceCapability(Parcel in) { - mContactUri = in.readParcelable(Uri.class.getClassLoader()); - mCapabilities = in.readLong(); - in.readStringList(mExtensionTags); - // read mServiceMap as key,value pair - int mapSize = in.readInt(); - for (int i = 0; i < mapSize; i++) { - mServiceMap.put(in.readLong(), in.readParcelable(Uri.class.getClassLoader())); + private final RcsContactUceCapability mCapabilities; + + /** + * Create the builder, which can be used to set UCE capabilities as well as custom + * capability extensions. + * @param contact The contact URI that the capabilities are attached to. + * @param sourceType The type where the capabilities of this contact were retrieved from. + * @param requestResult the request result + */ + public PresenceBuilder(@NonNull Uri contact, @SourceType int sourceType, + @RequestResult int requestResult) { + mCapabilities = new RcsContactUceCapability(contact, CAPABILITY_MECHANISM_PRESENCE, + sourceType); + mCapabilities.mRequestResult = requestResult; } - } - public static final @NonNull Creator<RcsContactUceCapability> CREATOR = - new Creator<RcsContactUceCapability>() { - @Override - public RcsContactUceCapability createFromParcel(Parcel in) { - return new RcsContactUceCapability(in); + /** + * Add the {@link RcsContactPresenceTuple} into the capabilities instance. + * @param tuple The {@link RcsContactPresenceTuple} to be added into. + * @return this PresenceBuilder + */ + public @NonNull PresenceBuilder addCapabilityTuple(@NonNull RcsContactPresenceTuple tuple) { + mCapabilities.mPresenceTuples.add(tuple); + return this; + } + + /** + * Add the list of {@link RcsContactPresenceTuple} into the capabilities instance. + * @param tuples The list of the {@link RcsContactPresenceTuple} to be added into. + * @return this PresenceBuilder + */ + public @NonNull PresenceBuilder addCapabilityTuples( + @NonNull List<RcsContactPresenceTuple> tuples) { + mCapabilities.mPresenceTuples.addAll(tuples); + return this; } - @Override - public RcsContactUceCapability[] newArray(int size) { - return new RcsContactUceCapability[size]; + /** + * @return the RcsContactUceCapability instance. + */ + public @NonNull RcsContactUceCapability build() { + return mCapabilities; } - }; + } + + private final Uri mContactUri; + private @SourceType int mSourceType; + private @CapabilityMechanism int mCapabilityMechanism; + private @RequestResult int mRequestResult; + + private final Set<String> mFeatureTags = new HashSet<>(); + private final List<RcsContactPresenceTuple> mPresenceTuples = new ArrayList<>(); + + private RcsContactUceCapability(@NonNull Uri contactUri, @CapabilityMechanism int mechanism, + @SourceType int sourceType) { + mContactUri = contactUri; + mCapabilityMechanism = mechanism; + mSourceType = sourceType; + } + + private RcsContactUceCapability(Parcel in) { + mContactUri = in.readParcelable(Uri.class.getClassLoader()); + mCapabilityMechanism = in.readInt(); + mSourceType = in.readInt(); + mRequestResult = in.readInt(); + List<String> featureTagList = new ArrayList<>(); + in.readStringList(featureTagList); + mFeatureTags.addAll(featureTagList); + in.readParcelableList(mPresenceTuples, RcsContactPresenceTuple.class.getClassLoader()); + } @Override public void writeToParcel(@NonNull Parcel out, int flags) { - out.writeParcelable(mContactUri, 0); - out.writeLong(mCapabilities); - out.writeStringList(mExtensionTags); - // write mServiceMap as key,value pairs - int mapSize = mServiceMap.keySet().size(); - out.writeInt(mapSize); - for (long key : mServiceMap.keySet()) { - out.writeLong(key); - out.writeParcelable(mServiceMap.get(key), 0); - } + out.writeParcelable(mContactUri, flags); + out.writeInt(mCapabilityMechanism); + out.writeInt(mSourceType); + out.writeInt(mRequestResult); + out.writeStringList(new ArrayList<>(mFeatureTags)); + out.writeParcelableList(mPresenceTuples, flags); } @Override @@ -266,55 +257,130 @@ public final class RcsContactUceCapability implements Parcelable { return 0; } + public static final @NonNull Creator<RcsContactUceCapability> CREATOR = + new Creator<RcsContactUceCapability>() { + @Override + public RcsContactUceCapability createFromParcel(Parcel in) { + return new RcsContactUceCapability(in); + } + + @Override + public RcsContactUceCapability[] newArray(int size) { + return new RcsContactUceCapability[size]; + } + }; + /** - * Query for a capability - * @param type The capability flag to query. - * @return true if the capability flag specified is set, false otherwise. + * @return The mechanism used to get the capabilities. */ - public boolean isCapable(@CapabilityFlag long type) { - return (mCapabilities & type) > 0; + public @CapabilityMechanism int getCapabilityMechanism() { + return mCapabilityMechanism; } /** - * @return true if the extension service tag is set, false otherwise. + * @return The feature tags present in the OPTIONS response from the network. + * <p> + * Note: this is only populated if {@link #getCapabilityMechanism} is + * {@link RcsContactUceCapability#CAPABILITY_MECHANISM_OPTIONS} */ - public boolean isCapable(@NonNull String extensionTag) { - return mExtensionTags.contains(extensionTag); + public @NonNull Set<String> getFeatureTags() { + if (mCapabilityMechanism != CAPABILITY_MECHANISM_OPTIONS) { + return Collections.emptySet(); + } + return Collections.unmodifiableSet(mFeatureTags); } /** - * @return An immutable list containing all of the extension tags that have been set as capable. - * @throws UnsupportedOperationException if this list is modified. + * @return The tuple elements associated with the presence element portion of the PIDF document + * contained in the NOTIFY response from the network. + * <p> + * Note: this is only populated if {@link #getCapabilityMechanism} is + * {@link RcsContactUceCapability#CAPABILITY_MECHANISM_PRESENCE} */ - public @NonNull List<String> getCapableExtensionTags() { - return Collections.unmodifiableList(mExtensionTags); + public @NonNull List<RcsContactPresenceTuple> getCapabilityTuples() { + if (mCapabilityMechanism != CAPABILITY_MECHANISM_PRESENCE) { + return Collections.emptyList(); + } + return Collections.unmodifiableList(mPresenceTuples); } /** - * Retrieves the {@link Uri} associated with the capability being queried. - * <p> - * This will typically be the contact {@link Uri} available via {@link #getContactUri()} unless - * a different service {@link Uri} was associated with this capability using - * {@link Builder#add(long, Uri)}. + * Get the RcsContactPresenceTuple associated with the given service id. + * @param serviceId The service id to get the presence tuple. + * @return The RcsContactPresenceTuple which has the given service id or {@code null} if the + * service id does not exist in the list of presence tuples returned from the network. * - * @return a String containing the {@link Uri} associated with the service tag or - * {@code null} if this capability is not set as capable. - * @see #isCapable(long) + * <p> + * Note: this is only populated if {@link #getCapabilityMechanism} is + * {@link RcsContactUceCapability#CAPABILITY_MECHANISM_PRESENCE} */ - public @Nullable Uri getServiceUri(@CapabilityFlag long type) { - Uri result = mServiceMap.getOrDefault(type, null); - // If the capability is capable, but does not have a service URI associated, use the default - // contact URI. - if (result == null) { - return isCapable(type) ? getContactUri() : null; + public @Nullable RcsContactPresenceTuple getCapabilityTuple(@NonNull String serviceId) { + if (mCapabilityMechanism != CAPABILITY_MECHANISM_PRESENCE) { + return null; + } + for (RcsContactPresenceTuple tuple : mPresenceTuples) { + if (tuple.getServiceId().equals(serviceId)) { + return tuple; + } } - return result; + return null; + } + + /** + * @return the source of the data that was used to populate the capabilities of the requested + * contact. + */ + public @SourceType int getSourceType() { + return mSourceType; } /** + * @return the result of querying the capabilities of the requested contact. + */ + public @RequestResult int getRequestResult() { + return mRequestResult; + } + + /** + * Retrieve the contact URI requested by the applications. * @return the URI representing the contact associated with the capabilities. */ public @NonNull Uri getContactUri() { return mContactUri; } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder("RcsContactUceCapability"); + if (mCapabilityMechanism == CAPABILITY_MECHANISM_PRESENCE) { + builder.append("(presence) {"); + } else if (mCapabilityMechanism == CAPABILITY_MECHANISM_OPTIONS) { + builder.append("(options) {"); + } else { + builder.append("(?) {"); + } + if (Build.IS_ENG) { + builder.append("uri="); + builder.append(mContactUri); + } else { + builder.append("uri (isNull)="); + builder.append(mContactUri != null ? "XXX" : "null"); + } + builder.append(", sourceType="); + builder.append(mSourceType); + builder.append(", requestResult="); + builder.append(mRequestResult); + + if (mCapabilityMechanism == CAPABILITY_MECHANISM_PRESENCE) { + builder.append(", presenceTuples={"); + builder.append(mPresenceTuples); + builder.append("}"); + } else if (mCapabilityMechanism == CAPABILITY_MECHANISM_OPTIONS) { + builder.append(", featureTags={"); + builder.append(mFeatureTags); + builder.append("}"); + } + + return builder.toString(); + } } diff --git a/telephony/java/android/telephony/ims/RcsUceAdapter.java b/telephony/java/android/telephony/ims/RcsUceAdapter.java index 94113b4d9702..dd9102699529 100644 --- a/telephony/java/android/telephony/ims/RcsUceAdapter.java +++ b/telephony/java/android/telephony/ims/RcsUceAdapter.java @@ -27,14 +27,20 @@ import android.net.Uri; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; +import android.os.ServiceSpecificException; import android.telephony.TelephonyFrameworkInitializer; import android.telephony.ims.aidl.IImsRcsController; import android.telephony.ims.aidl.IRcsUceControllerCallback; +import android.telephony.ims.aidl.IRcsUcePublishStateCallback; import android.util.Log; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.concurrent.Executor; /** @@ -46,70 +52,119 @@ public class RcsUceAdapter { private static final String TAG = "RcsUceAdapter"; /** + * This carrier supports User Capability Exchange as, defined by the framework using + * SIP OPTIONS. If set, the RcsFeature should support capability exchange. If not set, this + * RcsFeature should not publish capabilities or service capability requests. + * @hide + */ + public static final int CAPABILITY_TYPE_OPTIONS_UCE = 1 << 0; + + /** + * This carrier supports User Capability Exchange as, defined by the framework using a + * presence server. If set, the RcsFeature should support capability exchange. If not set, this + * RcsFeature should not publish capabilities or service capability requests. + * @hide + */ + @SystemApi + public static final int CAPABILITY_TYPE_PRESENCE_UCE = 1 << 1; + + /**@hide*/ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = "CAPABILITY_TYPE_", value = { + CAPABILITY_TYPE_OPTIONS_UCE, + CAPABILITY_TYPE_PRESENCE_UCE + }) + public @interface RcsImsCapabilityFlag {} + + /** * An unknown error has caused the request to fail. * @hide */ + @SystemApi public static final int ERROR_GENERIC_FAILURE = 1; + /** * The carrier network does not have UCE support enabled for this subscriber. * @hide */ + @SystemApi public static final int ERROR_NOT_ENABLED = 2; + /** * The data network that the device is connected to does not support UCE currently (e.g. it is * 1x only currently). * @hide */ + @SystemApi public static final int ERROR_NOT_AVAILABLE = 3; + /** * The network has responded with SIP 403 error and a reason "User not registered." * @hide */ + @SystemApi public static final int ERROR_NOT_REGISTERED = 4; + /** * The network has responded to this request with a SIP 403 error and reason "not authorized for * presence" for this subscriber. * @hide */ + @SystemApi public static final int ERROR_NOT_AUTHORIZED = 5; + /** * The network has responded to this request with a SIP 403 error and no reason. * @hide */ + @SystemApi public static final int ERROR_FORBIDDEN = 6; + /** - * The contact URI requested is not provisioned for VoLTE or it is not known as an IMS + * The contact URI requested is not provisioned for voice or it is not known as an IMS * subscriber to the carrier network. * @hide */ + @SystemApi public static final int ERROR_NOT_FOUND = 7; + /** * The capabilities request contained too many URIs for the carrier network to handle. Retry * with a lower number of contact numbers. The number varies per carrier. * @hide */ + @SystemApi // TODO: Try to integrate this into the API so that the service will split based on carrier. public static final int ERROR_REQUEST_TOO_LARGE = 8; + /** * The network did not respond to the capabilities request before the request timed out. * @hide */ - public static final int ERROR_REQUEST_TIMEOUT = 10; + @SystemApi + public static final int ERROR_REQUEST_TIMEOUT = 9; + /** * The request failed due to the service having insufficient memory. * @hide */ - public static final int ERROR_INSUFFICIENT_MEMORY = 11; + @SystemApi + public static final int ERROR_INSUFFICIENT_MEMORY = 10; + /** * The network was lost while trying to complete the request. * @hide */ - public static final int ERROR_LOST_NETWORK = 12; + @SystemApi + public static final int ERROR_LOST_NETWORK = 11; + /** - * The request has failed because the same request has already been added to the queue. + * The network is temporarily unavailable or busy. Retries should only be done after the retry + * time returned in {@link CapabilitiesCallback#onError} has elapsed. * @hide */ - public static final int ERROR_ALREADY_IN_QUEUE = 13; + @SystemApi + public static final int ERROR_SERVER_UNAVAILABLE = 12; /**@hide*/ @Retention(RetentionPolicy.SOURCE) @@ -125,41 +180,148 @@ public class RcsUceAdapter { ERROR_REQUEST_TIMEOUT, ERROR_INSUFFICIENT_MEMORY, ERROR_LOST_NETWORK, - ERROR_ALREADY_IN_QUEUE + ERROR_SERVER_UNAVAILABLE }) public @interface ErrorCode {} /** + * A capability update has been requested but the reason is unknown. + * @hide + */ + @SystemApi + public static final int CAPABILITY_UPDATE_TRIGGER_UNKNOWN = 0; + + /** + * A capability update has been requested due to the Entity Tag (ETag) expiring. + * @hide + */ + @SystemApi + public static final int CAPABILITY_UPDATE_TRIGGER_ETAG_EXPIRED = 1; + + /** + * A capability update has been requested due to moving to LTE with VoPS disabled. + * @hide + */ + @SystemApi + public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_LTE_VOPS_DISABLED = 2; + + /** + * A capability update has been requested due to moving to LTE with VoPS enabled. + * @hide + */ + @SystemApi + public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_LTE_VOPS_ENABLED = 3; + + /** + * A capability update has been requested due to moving to eHRPD. + * @hide + */ + @SystemApi + public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_EHRPD = 4; + + /** + * A capability update has been requested due to moving to HSPA+. + * @hide + */ + @SystemApi + public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_HSPAPLUS = 5; + + /** + * A capability update has been requested due to moving to 3G. + * @hide + */ + @SystemApi + public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_3G = 6; + + /** + * A capability update has been requested due to moving to 2G. + * @hide + */ + @SystemApi + public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_2G = 7; + + /** + * A capability update has been requested due to moving to WLAN + * @hide + */ + @SystemApi + public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_WLAN = 8; + + /** + * A capability update has been requested due to moving to IWLAN + * @hide + */ + @SystemApi + public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_IWLAN = 9; + + /** + * A capability update has been requested due to moving to 5G NR with VoPS disabled. + * @hide + */ + @SystemApi + public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_NR5G_VOPS_DISABLED = 10; + + /** + * A capability update has been requested due to moving to 5G NR with VoPS enabled. + * @hide + */ + @SystemApi + public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_NR5G_VOPS_ENABLED = 11; + + /**@hide*/ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = "ERROR_", value = { + CAPABILITY_UPDATE_TRIGGER_UNKNOWN, + CAPABILITY_UPDATE_TRIGGER_ETAG_EXPIRED, + CAPABILITY_UPDATE_TRIGGER_MOVE_TO_LTE_VOPS_DISABLED, + CAPABILITY_UPDATE_TRIGGER_MOVE_TO_LTE_VOPS_ENABLED, + CAPABILITY_UPDATE_TRIGGER_MOVE_TO_EHRPD, + CAPABILITY_UPDATE_TRIGGER_MOVE_TO_HSPAPLUS, + CAPABILITY_UPDATE_TRIGGER_MOVE_TO_3G, + CAPABILITY_UPDATE_TRIGGER_MOVE_TO_2G, + CAPABILITY_UPDATE_TRIGGER_MOVE_TO_WLAN, + CAPABILITY_UPDATE_TRIGGER_MOVE_TO_IWLAN, + CAPABILITY_UPDATE_TRIGGER_MOVE_TO_NR5G_VOPS_DISABLED, + CAPABILITY_UPDATE_TRIGGER_MOVE_TO_NR5G_VOPS_ENABLED + }) + public @interface StackPublishTriggerType {} + + /** * The last publish has resulted in a "200 OK" response or the device is using SIP OPTIONS for * UCE. * @hide */ + @SystemApi public static final int PUBLISH_STATE_OK = 1; /** * The hasn't published its capabilities since boot or hasn't gotten any publish response yet. * @hide */ + @SystemApi public static final int PUBLISH_STATE_NOT_PUBLISHED = 2; /** * The device has tried to publish its capabilities, which has resulted in an error. This error - * is related to the fact that the device is not VoLTE provisioned. + * is related to the fact that the device is not provisioned for voice. * @hide */ - public static final int PUBLISH_STATE_VOLTE_PROVISION_ERROR = 3; + @SystemApi + public static final int PUBLISH_STATE_VOICE_PROVISION_ERROR = 3; /** * The device has tried to publish its capabilities, which has resulted in an error. This error * is related to the fact that the device is not RCS or UCE provisioned. * @hide */ + @SystemApi public static final int PUBLISH_STATE_RCS_PROVISION_ERROR = 4; /** * The last publish resulted in a "408 Request Timeout" response. * @hide */ + @SystemApi public static final int PUBLISH_STATE_REQUEST_TIMEOUT = 5; /** @@ -169,6 +331,7 @@ public class RcsUceAdapter { * Device shall retry with exponential back-off. * @hide */ + @SystemApi public static final int PUBLISH_STATE_OTHER_ERROR = 6; /**@hide*/ @@ -176,66 +339,145 @@ public class RcsUceAdapter { @IntDef(prefix = "PUBLISH_STATE_", value = { PUBLISH_STATE_OK, PUBLISH_STATE_NOT_PUBLISHED, - PUBLISH_STATE_VOLTE_PROVISION_ERROR, + PUBLISH_STATE_VOICE_PROVISION_ERROR, PUBLISH_STATE_RCS_PROVISION_ERROR, PUBLISH_STATE_REQUEST_TIMEOUT, PUBLISH_STATE_OTHER_ERROR }) public @interface PublishState {} + /** + * An application can use {@link #addOnPublishStateChangedListener} to register a + * {@link OnPublishStateChangedListener ), which will notify the user when the publish state to + * the network changes. + * @hide + */ + @SystemApi + public interface OnPublishStateChangedListener { + /** + * Notifies the callback when the publish state has changed. + * @param publishState The latest update to the publish state. + */ + void onPublishStateChange(@PublishState int publishState); + } + + /** + * An application can use {@link #addOnPublishStateChangedListener} to register a + * {@link OnPublishStateChangedListener ), which will notify the user when the publish state to + * the network changes. + * @hide + */ + public static class PublishStateCallbackAdapter { + + private static class PublishStateBinder extends IRcsUcePublishStateCallback.Stub { + private final OnPublishStateChangedListener mPublishStateChangeListener; + private final Executor mExecutor; + + PublishStateBinder(Executor executor, OnPublishStateChangedListener listener) { + mExecutor = executor; + mPublishStateChangeListener = listener; + } + + @Override + public void onPublishStateChanged(int publishState) { + if (mPublishStateChangeListener == null) return; + + final long callingIdentity = Binder.clearCallingIdentity(); + try { + mExecutor.execute(() -> + mPublishStateChangeListener.onPublishStateChange(publishState)); + } finally { + restoreCallingIdentity(callingIdentity); + } + } + } + + private final PublishStateBinder mBinder; + + public PublishStateCallbackAdapter(@NonNull Executor executor, + @NonNull OnPublishStateChangedListener listener) { + mBinder = new PublishStateBinder(executor, listener); + } + + /**@hide*/ + public final IRcsUcePublishStateCallback getBinder() { + return mBinder; + } + } /** - * Provides a one-time callback for the response to a UCE request. After this callback is called - * by the framework, the reference to this callback will be discarded on the service side. + * A callback for the response to a UCE request. The method + * {@link CapabilitiesCallback#onCapabilitiesReceived} will be called zero or more times as the + * capabilities are received for each requested contact. + * <p> + * This request will take a varying amount of time depending on if the contacts requested are + * cached or if it requires a network query. The timeout time of these requests can vary + * depending on the network, however in poor cases it could take up to a minute for a request + * to timeout. In that time only a subset of capabilities may have been retrieved. + * <p> + * After {@link CapabilitiesCallback#onComplete} or {@link CapabilitiesCallback#onError} has + * been called, the reference to this callback will be discarded on the service side. * @see #requestCapabilities(Executor, List, CapabilitiesCallback) * @hide */ - public static class CapabilitiesCallback { + @SystemApi + public interface CapabilitiesCallback { /** - * Notify this application that the pending capability request has returned successfully. + * Notify this application that the pending capability request has returned successfully + * for one or more of the requested contacts. * @param contactCapabilities List of capabilities associated with each contact requested. */ - public void onCapabilitiesReceived( - @NonNull List<RcsContactUceCapability> contactCapabilities) { + void onCapabilitiesReceived(@NonNull List<RcsContactUceCapability> contactCapabilities); - } + /** + * The pending request has completed successfully due to all requested contacts information + * being delivered. The callback {@link #onCapabilitiesReceived(List)} + * for each contacts is required to be called before {@link #onComplete} is called. + */ + void onComplete(); /** * The pending request has resulted in an error and may need to be retried, depending on the * error code. * @param errorCode The reason for the framework being unable to process the request. + * @param retryIntervalMillis The time in milliseconds the requesting application should + * wait before retrying, if non-zero. */ - public void onError(@ErrorCode int errorCode) { - - } + void onError(@ErrorCode int errorCode, long retryIntervalMillis); } private final Context mContext; private final int mSubId; + private final Map<OnPublishStateChangedListener, PublishStateCallbackAdapter> + mPublishStateCallbacks; /** - * Not to be instantiated directly, use - * {@link ImsRcsManager#getUceAdapter()} to instantiate this manager class. + * Not to be instantiated directly, use {@link ImsRcsManager#getUceAdapter()} to instantiate + * this manager class. * @hide */ RcsUceAdapter(Context context, int subId) { mContext = context; mSubId = subId; + mPublishStateCallbacks = new HashMap<>(); } /** * Request the User Capability Exchange capabilities for one or more contacts. * <p> + * This will return the cached capabilities of the contact and will not perform a capability + * poll on the network unless there are contacts being queried with stale information. + * <p> * Be sure to check the availability of this feature using - * {@link ImsRcsManager#isAvailable(int)} and ensuring + * {@link ImsRcsManager#isAvailable(int, int)} and ensuring * {@link RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_OPTIONS_UCE} or * {@link RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_PRESENCE_UCE} is enabled or else * this operation will fail with {@link #ERROR_NOT_AVAILABLE} or {@link #ERROR_NOT_ENABLED}. * + * @param contactNumbers A list of numbers that the capabilities are being requested for. * @param executor The executor that will be used when the request is completed and the * {@link CapabilitiesCallback} is called. - * @param contactNumbers A list of numbers that the capabilities are being requested for. * @param c A one-time callback for when the request for capabilities completes or there is an * error processing the request. * @throws ImsException if the subscription associated with this instance of @@ -244,12 +486,14 @@ public class RcsUceAdapter { * becomes inactive. See {@link ImsException#getCode()} for more information on the error codes. * @hide */ - @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) - public void requestCapabilities(@NonNull @CallbackExecutor Executor executor, - @NonNull List<Uri> contactNumbers, + @SystemApi + @RequiresPermission(allOf = {Manifest.permission.ACCESS_RCS_USER_CAPABILITY_EXCHANGE, + Manifest.permission.READ_CONTACTS}) + public void requestCapabilities(@NonNull Collection<Uri> contactNumbers, + @NonNull @CallbackExecutor Executor executor, @NonNull CapabilitiesCallback c) throws ImsException { if (c == null) { - throw new IllegalArgumentException("Must include a non-null AvailabilityCallback."); + throw new IllegalArgumentException("Must include a non-null CapabilitiesCallback."); } if (executor == null) { throw new IllegalArgumentException("Must include a non-null Executor."); @@ -268,19 +512,27 @@ public class RcsUceAdapter { IRcsUceControllerCallback internalCallback = new IRcsUceControllerCallback.Stub() { @Override public void onCapabilitiesReceived(List<RcsContactUceCapability> contactCapabilities) { - long callingIdentity = Binder.clearCallingIdentity(); + final long callingIdentity = Binder.clearCallingIdentity(); + try { + executor.execute(() -> c.onCapabilitiesReceived(contactCapabilities)); + } finally { + restoreCallingIdentity(callingIdentity); + } + } + @Override + public void onComplete() { + final long callingIdentity = Binder.clearCallingIdentity(); try { - executor.execute(() -> - c.onCapabilitiesReceived(contactCapabilities)); + executor.execute(() -> c.onComplete()); } finally { restoreCallingIdentity(callingIdentity); } } @Override - public void onError(int errorCode) { - long callingIdentity = Binder.clearCallingIdentity(); + public void onError(int errorCode, long retryAfterMilliseconds) { + final long callingIdentity = Binder.clearCallingIdentity(); try { - executor.execute(() -> c.onError(errorCode)); + executor.execute(() -> c.onError(errorCode, retryAfterMilliseconds)); } finally { restoreCallingIdentity(callingIdentity); } @@ -289,7 +541,9 @@ public class RcsUceAdapter { try { imsRcsController.requestCapabilities(mSubId, mContext.getOpPackageName(), - mContext.getAttributionTag(), contactNumbers, internalCallback); + mContext.getAttributionTag(), new ArrayList(contactNumbers), internalCallback); + } catch (ServiceSpecificException e) { + throw new ImsException(e.toString(), e.errorCode); } catch (RemoteException e) { Log.e(TAG, "Error calling IImsRcsController#requestCapabilities", e); throw new ImsException("Remote IMS Service is not available", @@ -298,6 +552,98 @@ public class RcsUceAdapter { } /** + * Ignore the device cache and perform a capability discovery for one contact, also called + * "availability fetch." + * <p> + * This will always perform a query to the network as long as requests are over the carrier + * availability fetch throttling threshold. If too many network requests are sent too quickly, + * #ERROR_TOO_MANY_REQUESTS will be returned. + * + * <p> + * Be sure to check the availability of this feature using + * {@link ImsRcsManager#isAvailable(int, int)} and ensuring + * {@link RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_OPTIONS_UCE} or + * {@link RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_PRESENCE_UCE} is + * enabled or else this operation will fail with + * {@link #ERROR_NOT_AVAILABLE} or {@link #ERROR_NOT_ENABLED}. + * + * @param contactNumber The contact of the capabilities is being requested for. + * @param executor The executor that will be used when the request is completed and the + * {@link CapabilitiesCallback} is called. + * @param c A one-time callback for when the request for capabilities completes or there is + * an error processing the request. + * @throws ImsException if the subscription associated with this instance of + * {@link RcsUceAdapter} is valid, but the ImsService associated with the subscription is not + * available. This can happen if the ImsService has crashed, for example, or if the subscription + * becomes inactive. See {@link ImsException#getCode()} for more information on the error codes. + * @hide + */ + @SystemApi + @RequiresPermission(allOf = {Manifest.permission.ACCESS_RCS_USER_CAPABILITY_EXCHANGE, + Manifest.permission.READ_CONTACTS}) + public void requestAvailability(@NonNull Uri contactNumber, + @NonNull @CallbackExecutor Executor executor, + @NonNull CapabilitiesCallback c) throws ImsException { + if (executor == null) { + throw new IllegalArgumentException("Must include a non-null Executor."); + } + if (contactNumber == null) { + throw new IllegalArgumentException("Must include non-null contact number."); + } + if (c == null) { + throw new IllegalArgumentException("Must include a non-null CapabilitiesCallback."); + } + + IImsRcsController imsRcsController = getIImsRcsController(); + if (imsRcsController == null) { + Log.e(TAG, "requestAvailability: IImsRcsController is null"); + throw new ImsException("Cannot find remote IMS service", + ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); + } + + IRcsUceControllerCallback internalCallback = new IRcsUceControllerCallback.Stub() { + @Override + public void onCapabilitiesReceived(List<RcsContactUceCapability> contactCapabilities) { + final long callingIdentity = Binder.clearCallingIdentity(); + try { + executor.execute(() -> c.onCapabilitiesReceived(contactCapabilities)); + } finally { + restoreCallingIdentity(callingIdentity); + } + } + @Override + public void onComplete() { + final long callingIdentity = Binder.clearCallingIdentity(); + try { + executor.execute(() -> c.onComplete()); + } finally { + restoreCallingIdentity(callingIdentity); + } + } + @Override + public void onError(int errorCode, long retryAfterMilliseconds) { + final long callingIdentity = Binder.clearCallingIdentity(); + try { + executor.execute(() -> c.onError(errorCode, retryAfterMilliseconds)); + } finally { + restoreCallingIdentity(callingIdentity); + } + } + }; + + try { + imsRcsController.requestAvailability(mSubId, mContext.getOpPackageName(), + mContext.getAttributionTag(), contactNumber, internalCallback); + } catch (ServiceSpecificException e) { + throw new ImsException(e.toString(), e.errorCode); + } catch (RemoteException e) { + Log.e(TAG, "Error calling IImsRcsController#requestAvailability", e); + throw new ImsException("Remote IMS Service is not available", + ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); + } + } + + /** * Gets the last publish result from the UCE service if the device is using an RCS presence * server. * @return The last publish result from the UCE service. If the device is using SIP OPTIONS, @@ -308,6 +654,7 @@ public class RcsUceAdapter { * becomes inactive. See {@link ImsException#getCode()} for more information on the error codes. * @hide */ + @SystemApi @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public @PublishState int getUcePublishState() throws ImsException { IImsRcsController imsRcsController = getIImsRcsController(); @@ -319,6 +666,8 @@ public class RcsUceAdapter { try { return imsRcsController.getUcePublishState(mSubId); + } catch (ServiceSpecificException e) { + throw new ImsException(e.getMessage(), e.errorCode); } catch (RemoteException e) { Log.e(TAG, "Error calling IImsRcsController#getUcePublishState", e); throw new ImsException("Remote IMS Service is not available", @@ -327,6 +676,100 @@ public class RcsUceAdapter { } /** + * Registers a {@link OnPublishStateChangedListener} with the system, which will provide publish + * state updates for the subscription specified in {@link ImsManager@getRcsManager(subid)}. + * <p> + * Use {@link SubscriptionManager.OnSubscriptionsChangedListener} to listen to subscription + * changed events and call {@link #unregisterPublishStateCallback} to clean up. + * <p> + * The registered {@link OnPublishStateChangedListener} will also receive a callback when it is + * registered with the current publish state. + * + * @param executor The executor the listener callback events should be run on. + * @param listener The {@link OnPublishStateChangedListener} to be added. + * @throws ImsException if the subscription associated with this callback is valid, but + * the {@link ImsService} associated with the subscription is not available. This can happen if + * the service crashed, for example. See {@link ImsException#getCode()} for a more detailed + * reason. + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public void addOnPublishStateChangedListener(@NonNull @CallbackExecutor Executor executor, + @NonNull OnPublishStateChangedListener listener) throws ImsException { + if (executor == null) { + throw new IllegalArgumentException("Must include a non-null Executor."); + } + if (listener == null) { + throw new IllegalArgumentException( + "Must include a non-null OnPublishStateChangedListener."); + } + + IImsRcsController imsRcsController = getIImsRcsController(); + if (imsRcsController == null) { + Log.e(TAG, "addOnPublishStateChangedListener : IImsRcsController is null"); + throw new ImsException("Cannot find remote IMS service", + ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); + } + + PublishStateCallbackAdapter stateCallback = addPublishStateCallback(executor, listener); + try { + imsRcsController.registerUcePublishStateCallback(mSubId, stateCallback.getBinder()); + } catch (ServiceSpecificException e) { + throw new ImsException(e.getMessage(), e.errorCode); + } catch (RemoteException e) { + Log.e(TAG, "Error calling IImsRcsController#registerUcePublishStateCallback", e); + throw new ImsException("Remote IMS Service is not available", + ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); + } + } + + /** + * Removes an existing {@link OnPublishStateChangedListener}. + * <p> + * When the subscription associated with this callback is removed + * (SIM removed, ESIM swap,etc...), this callback will automatically be removed. If this method + * is called for an inactive subscription, it will result in a no-op. + * + * @param listener The callback to be unregistered. + * @throws ImsException if the subscription associated with this callback is valid, but + * the {@link ImsService} associated with the subscription is not available. This can happen if + * the service crashed, for example. See {@link ImsException#getCode()} for a more detailed + * reason. + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public void removeOnPublishStateChangedListener( + @NonNull OnPublishStateChangedListener listener) throws ImsException { + if (listener == null) { + throw new IllegalArgumentException( + "Must include a non-null OnPublishStateChangedListener."); + } + IImsRcsController imsRcsController = getIImsRcsController(); + if (imsRcsController == null) { + Log.e(TAG, "removeOnPublishStateChangedListener: IImsRcsController is null"); + throw new ImsException("Cannot find remote IMS service", + ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); + } + + PublishStateCallbackAdapter callback = removePublishStateCallback(listener); + if (callback == null) { + return; + } + + try { + imsRcsController.unregisterUcePublishStateCallback(mSubId, callback.getBinder()); + } catch (android.os.ServiceSpecificException e) { + throw new ImsException(e.getMessage(), e.errorCode); + } catch (RemoteException e) { + Log.e(TAG, "Error calling IImsRcsController#unregisterUcePublishStateCallback", e); + throw new ImsException("Remote IMS Service is not available", + ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); + } + } + + /** * The user’s setting for whether or not User Capability Exchange (UCE) is enabled for the * associated subscription. * <p> @@ -396,6 +839,36 @@ public class RcsUceAdapter { } } + /** + * Add the {@link OnPublishStateChangedListener} to collection for tracking. + * @param executor The executor that will be used when the publish state is changed and the + * {@link OnPublishStateChangedListener} is called. + * @param listener The {@link OnPublishStateChangedListener} to call the publish state changed. + * @return The {@link PublishStateCallbackAdapter} to wrapper the + * {@link OnPublishStateChangedListener} + */ + private PublishStateCallbackAdapter addPublishStateCallback(@NonNull Executor executor, + @NonNull OnPublishStateChangedListener listener) { + PublishStateCallbackAdapter adapter = new PublishStateCallbackAdapter(executor, listener); + synchronized (mPublishStateCallbacks) { + mPublishStateCallbacks.put(listener, adapter); + } + return adapter; + } + + /** + * Remove the existing {@link OnPublishStateChangedListener}. + * @param listener The {@link OnPublishStateChangedListener} to remove from the collection. + * @return The wrapper class {@link PublishStateCallbackAdapter} associated with the + * {@link OnPublishStateChangedListener}. + */ + private PublishStateCallbackAdapter removePublishStateCallback( + @NonNull OnPublishStateChangedListener listener) { + synchronized (mPublishStateCallbacks) { + return mPublishStateCallbacks.remove(listener); + } + } + private IImsRcsController getIImsRcsController() { IBinder binder = TelephonyFrameworkInitializer .getTelephonyServiceManager() diff --git a/telephony/java/android/telephony/ims/RegistrationManager.java b/telephony/java/android/telephony/ims/RegistrationManager.java index e085dec10546..a2015cd8f22c 100644 --- a/telephony/java/android/telephony/ims/RegistrationManager.java +++ b/telephony/java/android/telephony/ims/RegistrationManager.java @@ -24,7 +24,9 @@ import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.net.Uri; import android.os.Binder; +import android.os.Bundle; import android.telephony.AccessNetworkConstants; +import android.telephony.NetworkRegistrationInfo; import android.telephony.ims.aidl.IImsRegistrationCallback; import android.telephony.ims.feature.ImsFeature; import android.telephony.ims.stub.ImsRegistrationImplBase; @@ -70,7 +72,6 @@ public interface RegistrationManager { */ int REGISTRATION_STATE_REGISTERED = 2; - /**@hide*/ // Translate ImsRegistrationImplBase API to new AccessNetworkConstant because WLAN // and WWAN are more accurate constants. @@ -78,13 +79,51 @@ public interface RegistrationManager { new HashMap<Integer, Integer>() {{ // Map NONE to -1 to make sure that we handle the REGISTRATION_TECH_NONE // case, since it is defined. - put(ImsRegistrationImplBase.REGISTRATION_TECH_NONE, -1); + put(ImsRegistrationImplBase.REGISTRATION_TECH_NONE, + AccessNetworkConstants.TRANSPORT_TYPE_INVALID); put(ImsRegistrationImplBase.REGISTRATION_TECH_LTE, AccessNetworkConstants.TRANSPORT_TYPE_WWAN); + put(ImsRegistrationImplBase.REGISTRATION_TECH_NR, + AccessNetworkConstants.TRANSPORT_TYPE_WWAN); put(ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN, AccessNetworkConstants.TRANSPORT_TYPE_WLAN); + /* As the cross sim will be using ePDG tunnel over internet, it behaves + like IWLAN in most cases. Hence setting the access type as IWLAN + */ + put(ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM, + AccessNetworkConstants.TRANSPORT_TYPE_WLAN); }}; + /** @hide */ + @NonNull + static String registrationStateToString( + final @NetworkRegistrationInfo.RegistrationState int value) { + switch (value) { + case REGISTRATION_STATE_NOT_REGISTERED: + return "REGISTRATION_STATE_NOT_REGISTERED"; + case REGISTRATION_STATE_REGISTERING: + return "REGISTRATION_STATE_REGISTERING"; + case REGISTRATION_STATE_REGISTERED: + return "REGISTRATION_STATE_REGISTERED"; + default: + return Integer.toString(value); + } + } + + /** + * @param regtech The registration technology. + * @return The Access Network type from registration technology. + * @hide + */ + static int getAccessType(int regtech) { + if (!RegistrationManager.IMS_REG_TO_ACCESS_TYPE_MAP.containsKey(regtech)) { + Log.w("RegistrationManager", "getAccessType - invalid regType returned: " + + regtech); + return AccessNetworkConstants.TRANSPORT_TYPE_INVALID; + } + return RegistrationManager.IMS_REG_TO_ACCESS_TYPE_MAP.get(regtech); + } + /** * Callback class for receiving IMS network Registration callback events. * @see #registerImsRegistrationCallback(Executor, RegistrationCallback) @@ -96,32 +135,31 @@ public interface RegistrationManager { private final RegistrationCallback mLocalCallback; private Executor mExecutor; + private Bundle mBundle = new Bundle(); RegistrationBinder(RegistrationCallback localCallback) { mLocalCallback = localCallback; } @Override - public void onRegistered(int imsRadioTech) { + public void onRegistered(ImsRegistrationAttributes attr) { if (mLocalCallback == null) return; - long callingIdentity = Binder.clearCallingIdentity(); + final long callingIdentity = Binder.clearCallingIdentity(); try { - mExecutor.execute(() -> - mLocalCallback.onRegistered(getAccessType(imsRadioTech))); + mExecutor.execute(() -> mLocalCallback.onRegistered(attr)); } finally { restoreCallingIdentity(callingIdentity); } } @Override - public void onRegistering(int imsRadioTech) { + public void onRegistering(ImsRegistrationAttributes attr) { if (mLocalCallback == null) return; - long callingIdentity = Binder.clearCallingIdentity(); + final long callingIdentity = Binder.clearCallingIdentity(); try { - mExecutor.execute(() -> - mLocalCallback.onRegistering(getAccessType(imsRadioTech))); + mExecutor.execute(() -> mLocalCallback.onRegistering(attr)); } finally { restoreCallingIdentity(callingIdentity); } @@ -131,7 +169,7 @@ public interface RegistrationManager { public void onDeregistered(ImsReasonInfo info) { if (mLocalCallback == null) return; - long callingIdentity = Binder.clearCallingIdentity(); + final long callingIdentity = Binder.clearCallingIdentity(); try { mExecutor.execute(() -> mLocalCallback.onUnregistered(info)); } finally { @@ -143,7 +181,7 @@ public interface RegistrationManager { public void onTechnologyChangeFailed(int imsRadioTech, ImsReasonInfo info) { if (mLocalCallback == null) return; - long callingIdentity = Binder.clearCallingIdentity(); + final long callingIdentity = Binder.clearCallingIdentity(); try { mExecutor.execute(() -> mLocalCallback.onTechnologyChangeFailed( getAccessType(imsRadioTech), info)); @@ -155,7 +193,7 @@ public interface RegistrationManager { public void onSubscriberAssociatedUriChanged(Uri[] uris) { if (mLocalCallback == null) return; - long callingIdentity = Binder.clearCallingIdentity(); + final long callingIdentity = Binder.clearCallingIdentity(); try { mExecutor.execute(() -> mLocalCallback.onSubscriberAssociatedUriChanged(uris)); } finally { @@ -166,15 +204,6 @@ public interface RegistrationManager { private void setExecutor(Executor executor) { mExecutor = executor; } - - private static int getAccessType(int regType) { - if (!RegistrationManager.IMS_REG_TO_ACCESS_TYPE_MAP.containsKey(regType)) { - Log.w("RegistrationManager", "RegistrationBinder - invalid regType returned: " - + regType); - return -1; - } - return RegistrationManager.IMS_REG_TO_ACCESS_TYPE_MAP.get(regType); - } } private final RegistrationBinder mBinder = new RegistrationBinder(this); @@ -183,19 +212,43 @@ public interface RegistrationManager { * Notifies the framework when the IMS Provider is registered to the IMS network. * * @param imsTransportType the radio access technology. + * @deprecated Use {@link #onRegistered(ImsRegistrationAttributes)} instead. */ + @Deprecated public void onRegistered(@AccessNetworkConstants.TransportType int imsTransportType) { } /** + * Notifies the framework when the IMS Provider is registered to the IMS network + * with corresponding attributes. + * + * @param attributes The attributes associated with this IMS registration. + */ + public void onRegistered(@NonNull ImsRegistrationAttributes attributes) { + // Default impl to keep backwards compatibility with old implementations + onRegistered(attributes.getTransportType()); + } + + /** * Notifies the framework when the IMS Provider is trying to register the IMS network. * * @param imsTransportType the radio access technology. + * @deprecated Use {@link #onRegistering(ImsRegistrationAttributes)} instead. */ public void onRegistering(@AccessNetworkConstants.TransportType int imsTransportType) { } /** + * Notifies the framework when the IMS Provider is trying to register the IMS network. + * + * @param attributes The attributes associated with this IMS registration. + */ + public void onRegistering(@NonNull ImsRegistrationAttributes attributes) { + // Default impl to keep backwards compatibility with old implementations + onRegistering(attributes.getTransportType()); + } + + /** * Notifies the framework when the IMS Provider is unregistered from the IMS network. * * @param info the {@link ImsReasonInfo} associated with why registration was disconnected. diff --git a/telephony/java/android/telephony/ims/RtpHeaderExtension.aidl b/telephony/java/android/telephony/ims/RtpHeaderExtension.aidl new file mode 100644 index 000000000000..cbf79d352ad8 --- /dev/null +++ b/telephony/java/android/telephony/ims/RtpHeaderExtension.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2020 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.ims; + +parcelable RtpHeaderExtension;
\ No newline at end of file diff --git a/telephony/java/android/telephony/ims/RtpHeaderExtension.java b/telephony/java/android/telephony/ims/RtpHeaderExtension.java new file mode 100644 index 000000000000..8815cefae241 --- /dev/null +++ b/telephony/java/android/telephony/ims/RtpHeaderExtension.java @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2020 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.ims; + +import android.annotation.IntRange; +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.Arrays; +import java.util.Objects; + +/** + * A representation of an RTP header extension. + * <p> + * Per RFC8285, an RTP header extension consists of both a local identifier in the range 1-14, an + * 8-bit length indicator and a number of extension data bytes equivalent to the stated length. + * @hide + */ +@SystemApi +public final class RtpHeaderExtension implements Parcelable { + private int mLocalIdentifier; + private byte[] mExtensionData; + + /** + * Creates a new {@link RtpHeaderExtension}. + * @param localIdentifier The local identifier for this RTP header extension. + * @param extensionData The data for this RTP header extension. + * @throws IllegalArgumentException if {@code extensionId} is not in the range 1-14. + * @throws NullPointerException if {@code extensionData} is null. + */ + public RtpHeaderExtension(@IntRange(from = 1, to = 14) int localIdentifier, + @NonNull byte[] extensionData) { + if (localIdentifier < 1 || localIdentifier > 13) { + throw new IllegalArgumentException("localIdentifier must be in range 1-14"); + } + if (extensionData == null) { + throw new NullPointerException("extensionDa is required."); + } + mLocalIdentifier = localIdentifier; + mExtensionData = extensionData; + } + + /** + * Creates a new instance of {@link RtpHeaderExtension} from a parcel. + * @param in The parceled data to read. + */ + private RtpHeaderExtension(@NonNull Parcel in) { + mLocalIdentifier = in.readInt(); + mExtensionData = in.createByteArray(); + } + + /** + * The local identifier for the RTP header extension. + * <p> + * Per RFC8285, the extension ID is a value in the range 1-14 (0 is reserved for padding and + * 15 is reserved for the one-byte header form. + * <p> + * Within the current call, this extension ID will match one of the + * {@link RtpHeaderExtensionType#getLocalIdentifier()}s. + * + * @return The local identifier for this RTP header extension. + */ + @IntRange(from = 1, to = 14) + public int getLocalIdentifier() { + return mLocalIdentifier; + } + + /** + * The data payload for the RTP header extension. + * <p> + * Per RFC8285 Sec 4.3, an RTP header extension includes an 8-bit length field which indicate + * how many bytes of data are present in the RTP header extension. The extension includes this + * many bytes of actual data. + * <p> + * We represent this as a byte array who's length is equivalent to the 8-bit length field. + * @return RTP header extension data payload. The payload may be up to 255 bytes in length. + */ + public @NonNull byte[] getExtensionData() { + return mExtensionData; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mLocalIdentifier); + dest.writeByteArray(mExtensionData); + } + + public static final @NonNull Creator<RtpHeaderExtension> CREATOR = + new Creator<RtpHeaderExtension>() { + @Override + public RtpHeaderExtension createFromParcel(@NonNull Parcel in) { + return new RtpHeaderExtension(in); + } + + @Override + public RtpHeaderExtension[] newArray(int size) { + return new RtpHeaderExtension[size]; + } + }; + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + RtpHeaderExtension that = (RtpHeaderExtension) o; + return mLocalIdentifier == that.mLocalIdentifier + && Arrays.equals(mExtensionData, that.mExtensionData); + } + + @Override + public int hashCode() { + int result = Objects.hash(mLocalIdentifier); + result = 31 * result + Arrays.hashCode(mExtensionData); + return result; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("RtpHeaderExtension{mLocalIdentifier="); + sb.append(mLocalIdentifier); + sb.append(", mData="); + for (byte b : mExtensionData) { + sb.append(Integer.toBinaryString(b)); + sb.append("b_"); + } + sb.append("}"); + + return sb.toString(); + } +} diff --git a/telephony/java/android/telephony/ims/RtpHeaderExtensionType.aidl b/telephony/java/android/telephony/ims/RtpHeaderExtensionType.aidl new file mode 100644 index 000000000000..3e62ffff5127 --- /dev/null +++ b/telephony/java/android/telephony/ims/RtpHeaderExtensionType.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2020 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.ims; + +parcelable RtpHeaderExtensionType;
\ No newline at end of file diff --git a/telephony/java/android/telephony/ims/RtpHeaderExtensionType.java b/telephony/java/android/telephony/ims/RtpHeaderExtensionType.java new file mode 100644 index 000000000000..af4e23476331 --- /dev/null +++ b/telephony/java/android/telephony/ims/RtpHeaderExtensionType.java @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2020 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.ims; + +import android.annotation.IntRange; +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.net.Uri; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.Objects; + +/** + * Defines a mapping between a local identifier and a {@link Uri} which identifies an RTP header + * extension. + * <p> + * According to RFC8285, SDP (Session Description Protocol) signalling for a call provides a means + * for the supported RTP header extensions for a call to be negotiated at call initiation time. + * The types of RTP header extensions potentially usable in a session are identified by a local + * identifier ({@link #getLocalIdentifier()}) when RTP header extensions are present on RTP packets. + * A {@link Uri} ({@link #getUri()}) provides a unique identifier for the RTP header extension + * format which parties in a call can use to identify supported RTP header extensions. + * @hide + */ +@SystemApi +public final class RtpHeaderExtensionType implements Parcelable { + private int mLocalIdentifier; + private Uri mUri; + + /** + * Create a new RTP header extension type. + * @param localIdentifier the local identifier. + * @param uri the {@link Uri} identifying the RTP header extension type. + * @throws IllegalArgumentException if {@code localIdentifier} is out of the expected range. + * @throws NullPointerException if {@code uri} is null. + */ + public RtpHeaderExtensionType(@IntRange(from = 1, to = 14) int localIdentifier, + @NonNull Uri uri) { + if (localIdentifier < 1 || localIdentifier > 13) { + throw new IllegalArgumentException("localIdentifier must be in range 1-14"); + } + if (uri == null) { + throw new NullPointerException("uri is required."); + } + mLocalIdentifier = localIdentifier; + mUri = uri; + } + + private RtpHeaderExtensionType(Parcel in) { + mLocalIdentifier = in.readInt(); + mUri = in.readParcelable(Uri.class.getClassLoader()); + } + + public static final @NonNull Creator<RtpHeaderExtensionType> CREATOR = + new Creator<RtpHeaderExtensionType>() { + @Override + public RtpHeaderExtensionType createFromParcel(@NonNull Parcel in) { + return new RtpHeaderExtensionType(in); + } + + @Override + public @NonNull RtpHeaderExtensionType[] newArray(int size) { + return new RtpHeaderExtensionType[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mLocalIdentifier); + dest.writeParcelable(mUri, flags); + } + + /** + * The local identifier for this RTP header extension type. + * <p> + * {@link RtpHeaderExtension}s which indicate a {@link RtpHeaderExtension#getLocalIdentifier()} + * matching this local identifier will have the format specified by {@link #getUri()}. + * <p> + * Per RFC8285, the extension ID is a value in the range 1-14 (0 is reserved for padding and + * 15 is reserved for the one-byte header form. + * + * @return The local identifier associated with this {@link #getUri()}. + */ + public @IntRange(from = 1, to = 14) int getLocalIdentifier() { + return mLocalIdentifier; + } + + /** + * A {@link Uri} which identifies the format of the RTP extension header. + * <p> + * According to RFC8285 section 5, URIs MUST be absolute and SHOULD contain a month/date pair + * in the form mmyyyy to indicate versioning of the extension. Extension headers defined in an + * RFC are typically defined using URNs starting with {@code urn:ietf:params:rtp-hdrext:}. + * For example, RFC6464 defines {@code urn:ietf:params:rtp-hdrext:ssrc-audio-level} which is an + * RTP header extension for communicating client to mixer audio level indications. + * + * @return A unique {@link Uri} identifying the format of the RTP extension header. + */ + public @NonNull Uri getUri() { + return mUri; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + RtpHeaderExtensionType that = (RtpHeaderExtensionType) o; + return mLocalIdentifier == that.mLocalIdentifier + && mUri.equals(that.mUri); + } + + @Override + public int hashCode() { + return Objects.hash(mLocalIdentifier, mUri); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("RtpHeaderExtensionType{mLocalIdentifier="); + sb.append(mLocalIdentifier); + sb.append(", mUri="); + sb.append(mUri); + sb.append("}"); + + return sb.toString(); + } +} diff --git a/telephony/java/android/telephony/ims/SipDelegateConfiguration.aidl b/telephony/java/android/telephony/ims/SipDelegateConfiguration.aidl new file mode 100644 index 000000000000..2fc9c48fb9fc --- /dev/null +++ b/telephony/java/android/telephony/ims/SipDelegateConfiguration.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.ims; + +parcelable SipDelegateConfiguration; diff --git a/telephony/java/android/telephony/ims/SipDelegateConfiguration.java b/telephony/java/android/telephony/ims/SipDelegateConfiguration.java new file mode 100644 index 000000000000..1bf5cad49c53 --- /dev/null +++ b/telephony/java/android/telephony/ims/SipDelegateConfiguration.java @@ -0,0 +1,932 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.ims; + +import android.annotation.IntDef; +import android.annotation.IntRange; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.net.InetAddresses; +import android.net.Uri; +import android.os.Parcel; +import android.os.Parcelable; +import android.telephony.ims.stub.SipDelegate; +import android.util.Log; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.UnknownHostException; +import java.util.Objects; + +/** + * The IMS registration and other attributes that the {@link SipDelegateConnection} used by the + * IMS application will need to be aware of to correctly generate outgoing {@link SipMessage}s. + * <p> + * The IMS service must generate new instances of this configuration as the IMS configuration + * managed by the IMS service changes. Along with each {@link SipDelegateConfiguration} instance + * containing the configuration is the "version", which should be incremented every time a new + * {@link SipDelegateConfiguration} instance is created. The {@link SipDelegateConnection} will + * include the version of the {@link SipDelegateConfiguration} instance that it used in order for + * the {@link SipDelegate} to easily identify if the IMS application used a now stale configuration + * to generate the {@link SipMessage} and return + * {@link SipDelegateManager#MESSAGE_FAILURE_REASON_STALE_IMS_CONFIGURATION} in + * {@link DelegateMessageCallback#onMessageSendFailure(String, int)} so that the IMS application can + * regenerate that {@link SipMessage} using the correct {@link SipDelegateConfiguration} + * instance. + * <p> + * Every time the IMS configuration state changes in the IMS service, a full configuration should + * be generated. The new {@link SipDelegateConfiguration} instance should not be an incremental + * update. + * @see Builder + * @hide + */ +@SystemApi +public final class SipDelegateConfiguration implements Parcelable { + + /** + * The SIP transport uses UDP. + */ + public static final int SIP_TRANSPORT_UDP = 0; + + /** + * The SIP transport uses TCP. + */ + public static final int SIP_TRANSPORT_TCP = 1; + + /**@hide*/ + @IntDef(prefix = "SIP_TRANSPORT_", value = { + SIP_TRANSPORT_UDP, + SIP_TRANSPORT_TCP + }) + @Retention(RetentionPolicy.SOURCE) + public @interface TransportType {} + + /** + * The value returned by {@link #getMaxUdpPayloadSizeBytes()} when it is not defined. + */ + public static final int UDP_PAYLOAD_SIZE_UNDEFINED = -1; + + /** + * SIP over IPSec configuration + */ + public static final class IpSecConfiguration { + private final int mLocalTxPort; + private final int mLocalRxPort; + private final int mLastLocalTxPort; + private final int mRemoteTxPort; + private final int mRemoteRxPort; + private final int mLastRemoteTxPort; + private final String mSecurityHeader; + + /** + * Describes the SIP over IPSec configuration the SipDelegate will need to use. + * + * @param localTxPort Local SIP port number used to send traffic. + * @param localRxPort Local SIP port number used to receive traffic. + * @param lastLocalTxPort Local SIP port number used for the previous IPsec security + * association. + * @param remoteTxPort Remote port number used by the SIP server to send SIP traffic. + * @param remoteRxPort Remote port number used by the SIP server to receive incoming SIP + * traffic. + * @param lastRemoteTxPort Remote port number used by the SIP server to send SIP traffic on + * the previous IPSec security association. + * @param securityHeader The value of the SIP security verify header. + */ + public IpSecConfiguration(int localTxPort, int localRxPort, int lastLocalTxPort, + int remoteTxPort, int remoteRxPort, int lastRemoteTxPort, + @NonNull String securityHeader) { + mLocalTxPort = localTxPort; + mLocalRxPort = localRxPort; + mLastLocalTxPort = lastLocalTxPort; + mRemoteTxPort = remoteTxPort; + mRemoteRxPort = remoteRxPort; + mLastRemoteTxPort = lastRemoteTxPort; + mSecurityHeader = securityHeader; + } + + /** + * @return The local SIP port number used to send traffic. + */ + public int getLocalTxPort() { + return mLocalTxPort; + } + + /** + * @return The Local SIP port number used to receive traffic. + */ + public int getLocalRxPort() { + return mLocalRxPort; + } + + /** + * @return The last local SIP port number used for the previous IPsec security association. + */ + public int getLastLocalTxPort() { + return mLastLocalTxPort; + } + + /** + * @return The remote port number used by the SIP server to send SIP traffic. + */ + public int getRemoteTxPort() { + return mRemoteTxPort; + } + + /** + * @return the remote port number used by the SIP server to receive incoming SIP traffic. + */ + public int getRemoteRxPort() { + return mRemoteRxPort; + } + + /** + * @return the remote port number used by the SIP server to send SIP traffic on the previous + * IPSec security association. + */ + public int getLastRemoteTxPort() { + return mLastRemoteTxPort; + } + + /** + * @return The value of the SIP security verify header. + */ + public @NonNull String getSipSecurityVerifyHeader() { + return mSecurityHeader; + } + + /** + * Helper for parcelling this object. + * @hide + */ + public void addToParcel(Parcel dest) { + dest.writeInt(mLocalTxPort); + dest.writeInt(mLocalRxPort); + dest.writeInt(mLastLocalTxPort); + dest.writeInt(mRemoteTxPort); + dest.writeInt(mRemoteRxPort); + dest.writeInt(mLastRemoteTxPort); + dest.writeString(mSecurityHeader); + } + + /** + * Helper for unparcelling this object. + * @hide + */ + public static IpSecConfiguration fromParcel(Parcel source) { + return new IpSecConfiguration(source.readInt(), source.readInt(), source.readInt(), + source.readInt(), source.readInt(), source.readInt(), source.readString()); + } + + @Override + public String toString() { + return "IpSecConfiguration{" + "localTx=" + mLocalTxPort + ", localRx=" + mLocalRxPort + + ", lastLocalTx=" + mLastLocalTxPort + ", remoteTx=" + mRemoteTxPort + + ", remoteRx=" + mRemoteRxPort + ", lastRemoteTx=" + mLastRemoteTxPort + + ", securityHeader=" + mSecurityHeader + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + IpSecConfiguration that = (IpSecConfiguration) o; + return mLocalTxPort == that.mLocalTxPort + && mLocalRxPort == that.mLocalRxPort + && mLastLocalTxPort == that.mLastLocalTxPort + && mRemoteTxPort == that.mRemoteTxPort + && mRemoteRxPort == that.mRemoteRxPort + && mLastRemoteTxPort == that.mLastRemoteTxPort + && Objects.equals(mSecurityHeader, that.mSecurityHeader); + } + + @Override + public int hashCode() { + return Objects.hash(mLocalTxPort, mLocalRxPort, mLastLocalTxPort, mRemoteTxPort, + mRemoteRxPort, mLastRemoteTxPort, mSecurityHeader); + } + } + + /** + * Creates a new instance of {@link SipDelegateConfiguration} composed from optional + * configuration items. + */ + public static final class Builder { + private final SipDelegateConfiguration mConfig; + + /** + * + * @param version The version associated with the {@link SipDelegateConfiguration} instance + * being built. See {@link #getVersion()} for more information. + * @param transportType The transport type to use for SIP signalling. + * @param localAddr The local socket address used for SIP traffic. + * @param serverAddr The SIP server or P-CSCF default IP address for sip traffic. + * @see InetAddresses#parseNumericAddress(String) for how to create an + * {@link InetAddress} without requiring a DNS lookup. + */ + public Builder(@IntRange(from = 0) long version, @TransportType int transportType, + @NonNull InetSocketAddress localAddr, @NonNull InetSocketAddress serverAddr) { + mConfig = new SipDelegateConfiguration(version, transportType, localAddr, + serverAddr); + } + + /** + * Create a new {@link SipDelegateConfiguration} instance with the same exact configuration + * as the passed in instance, except for the version parameter, which will be incremented + * by 1. + * <p> + * This method is useful for cases where only a small subset of configurations have changed + * and the new configuration is based off of the old configuration. + * @param c The older {@link SipDelegateConfiguration} instance to base this instance's + * configuration off of. + */ + public Builder(@NonNull SipDelegateConfiguration c) { + mConfig = c.copyAndIncrementVersion(); + } + + /** + * Sets whether or not SIP compact form is enabled for the associated SIP delegate. + * <p> + * If unset, this configuration defaults to {@code false}. + * @param isEnabled {@code true} if SIP compact form is enabled for the associated SIP + * Delegate, {@code false} if it is not. + * @return this Builder instance with the compact form configuration set. + */ + public @NonNull Builder setSipCompactFormEnabled(boolean isEnabled) { + mConfig.mIsSipCompactFormEnabled = isEnabled; + return this; + } + + /** + * Sets whether or not underlying SIP keepalives are enabled for the associated SIP + * delegate. + * <p> + * If unset, this configuration defaults to {@code false}. + * @param isEnabled {@code true} if SIP keepalives are enabled for the associated SIP + * Delegate, {@code false} if it is not. + * @return this Builder instance with the new configuration set. + */ + public @NonNull Builder setSipKeepaliveEnabled(boolean isEnabled) { + mConfig.mIsSipKeepaliveEnabled = isEnabled; + return this; + } + + /** + * Sets the max SIP payload size in bytes to be sent on UDP. If the SIP message payload is + * greater than the max UDP payload size, then TCP must be used. + * <p> + * If unset, this configuration defaults to {@link #UDP_PAYLOAD_SIZE_UNDEFINED}, or no + * size specified. + * @param size The maximum SIP payload size in bytes for UDP. + * @return this Builder instance with the new configuration set. + */ + public @NonNull Builder setMaxUdpPayloadSizeBytes(@IntRange(from = 1) int size) { + mConfig.mMaxUdpPayloadSize = size; + return this; + } + + /** + * Sets the IMS public user identifier. + * <p> + * If unset, this configuration defaults to {@code null}, or no identifier specified. + * @param id The IMS public user identifier. + * @return this Builder instance with the new configuration set. + */ + public @NonNull Builder setPublicUserIdentifier(@Nullable String id) { + mConfig.mPublicUserIdentifier = id; + return this; + } + + /** + * Sets the IMS private user identifier. + * <p> + * If unset, this configuration defaults to {@code null}, or no identifier specified. + * @param id The IMS private user identifier. + * @return this Builder instance with the new configuration set. + */ + public @NonNull Builder setPrivateUserIdentifier(@Nullable String id) { + mConfig.mPrivateUserIdentifier = id; + return this; + } + + /** + * Sets the IMS home domain. + * <p> + * If unset, this configuration defaults to {@code null}, or no domain specified. + * @param domain The IMS home domain. + * @return this Builder instance with the new configuration set. + */ + public @NonNull Builder setHomeDomain(@Nullable String domain) { + mConfig.mHomeDomain = domain; + return this; + } + + /** + * Sets the IMEI of the associated device. + * <p> + * Application can include the Instance-ID feature tag {@code "+sip.instance"} in the + * Contact header with a value of the device IMEI in the form + * {@code "urn:gsma:imei:<device IMEI>"}. + * <p> + * If unset, this configuration defaults to {@code null}, or no IMEI string specified. + * @param imei The IMEI of the device. + * @return this Builder instance with the new configuration set. + */ + public @NonNull Builder setImei(@Nullable String imei) { + mConfig.mImei = imei; + return this; + } + + /** + * Set the optional {@link IpSecConfiguration} instance used if the associated SipDelegate + * is communicating over IPSec. + * <p> + * If unset, this configuration defaults to {@code null} + * @param c The IpSecConfiguration instance to set. + * @return this Builder instance with IPSec configuration set. + */ + public @NonNull Builder setIpSecConfiguration(@Nullable IpSecConfiguration c) { + mConfig.mIpSecConfiguration = c; + return this; + } + + /** + * Describes the Device's Public IP Address and port that is set when Network Address + * Translation is enabled and the device is behind a NAT. + * <p> + * If unset, this configuration defaults to {@code null} + * @param addr The {@link InetAddress} representing the device's public IP address and port + * when behind a NAT. + * @return this Builder instance with the new configuration set. + * @see InetAddresses#parseNumericAddress(String) For an example of how to create an + * instance of {@link InetAddress} without causing a DNS lookup. + */ + public @NonNull Builder setNatSocketAddress(@Nullable InetSocketAddress addr) { + mConfig.mNatAddress = addr; + return this; + } + + /** + * Sets the optional URI of the device's Globally routable user-agent URI (GRUU) if this + * feature is enabled for the SIP delegate. + * <p> + * If unset, this configuration defaults to {@code null} + * @param uri The GRUU to set. + * @return this builder instance with the new configuration set. + */ + public @NonNull Builder setPublicGruuUri(@Nullable Uri uri) { + mConfig.mGruu = uri; + return this; + } + + /** + * Sets the SIP authentication header value. + * <p> + * If unset, this configuration defaults to {@code null}. + * @param header The SIP authentication header's value. + * @return this builder instance with the new configuration set. + */ + public @NonNull Builder setSipAuthenticationHeader(@Nullable String header) { + mConfig.mSipAuthHeader = header; + return this; + } + + /** + * Sets the SIP authentication nonce. + * <p> + * If unset, this configuration defaults to {@code null}. + * @param nonce The SIP authentication nonce. + * @return this builder instance with the new configuration set. + */ + public @NonNull Builder setSipAuthenticationNonce(@Nullable String nonce) { + mConfig.mSipAuthNonce = nonce; + return this; + } + + /** + * Sets the SIP service route header value. + * <p> + * If unset, this configuration defaults to {@code null}. + * @param header The SIP service route header value. + * @return this builder instance with the new configuration set. + */ + public @NonNull Builder setSipServiceRouteHeader(@Nullable String header) { + mConfig.mServiceRouteHeader = header; + return this; + } + + /** + * Sets the SIP path header value. + * <p> + * If unset, this configuration defaults to {@code null}. + * @param header The SIP path header value. + * @return this builder instance with the new configuration set. + */ + public @NonNull Builder setSipPathHeader(@Nullable String header) { + mConfig.mPathHeader = header; + return this; + } + + /** + * Sets the SIP User-Agent header value. + * <p> + * If unset, this configuration defaults to {@code null}. + * @param header The SIP User-Agent header value. + * @return this builder instance with the new configuration set. + */ + public @NonNull Builder setSipUserAgentHeader(@Nullable String header) { + mConfig.mUserAgentHeader = header; + return this; + } + + /** + * Sets the SIP Contact header's User parameter value. + * <p> + * If unset, this configuration defaults to {@code null}. + * @param param The SIP Contact header's User parameter value. + * @return this builder instance with the new configuration set. + */ + public @NonNull Builder setSipContactUserParameter(@Nullable String param) { + mConfig.mContactUserParam = param; + return this; + } + + /** + * Sets the SIP P-Access-Network-Info (P-ANI) header value. Populated for networks that + * require this information to be provided as part of outgoing SIP messages. + * <p> + * If unset, this configuration defaults to {@code null}. + * @param header The SIP P-ANI header value. + * @return this builder instance with the new configuration set. + */ + public @NonNull Builder setSipPaniHeader(@Nullable String header) { + mConfig.mPaniHeader = header; + return this; + } + + /** + * Sets the SIP P-Last-Access-Network-Info (P-LANI) header value. Populated for + * networks that require this information to be provided as part of outgoing SIP messages. + * <p> + * If unset, this configuration defaults to {@code null}. + * @param header The SIP P-LANI header value. + * @return this builder instance with the new configuration set. + */ + public @NonNull Builder setSipPlaniHeader(@Nullable String header) { + mConfig.mPlaniHeader = header; + return this; + } + + /** + * Sets the SIP Cellular-Network-Info (CNI) header value (See 3GPP 24.229, section 7.2.15), + * populated for networks that require this information to be provided as part of outgoing + * SIP messages. + * <p> + * If unset, this configuration defaults to {@code null}. + * @param header The SIP P-LANI header value. + * @return this builder instance with the new configuration set. + */ + public @NonNull Builder setSipCniHeader(@Nullable String header) { + mConfig.mCniHeader = header; + return this; + } + + /** + * Sets the SIP P-associated-uri header value. + * <p> + * If unset, this configuration defaults to {@code null}. + * @param header The SIP P-associated-uri header value. + * @return this builder instance with the new configuration set. + */ + public @NonNull Builder setSipAssociatedUriHeader(@Nullable String header) { + mConfig.mAssociatedUriHeader = header; + return this; + } + + /** + * @return A {@link SipDelegateConfiguration} instance with the optional configurations set. + */ + public @NonNull SipDelegateConfiguration build() { + return mConfig; + } + } + + private final long mVersion; + private final int mTransportType; + private final InetSocketAddress mLocalAddress; + private final InetSocketAddress mSipServerAddress; + private boolean mIsSipCompactFormEnabled = false; + private boolean mIsSipKeepaliveEnabled = false; + private int mMaxUdpPayloadSize = -1; + private String mPublicUserIdentifier = null; + private String mPrivateUserIdentifier = null; + private String mHomeDomain = null; + private String mImei = null; + private Uri mGruu = null; + private String mSipAuthHeader = null; + private String mSipAuthNonce = null; + private String mServiceRouteHeader = null; + private String mPathHeader = null; + private String mUserAgentHeader = null; + private String mContactUserParam = null; + private String mPaniHeader = null; + private String mPlaniHeader = null; + private String mCniHeader = null; + private String mAssociatedUriHeader = null; + private IpSecConfiguration mIpSecConfiguration = null; + private InetSocketAddress mNatAddress = null; + + + private SipDelegateConfiguration(long version, int transportType, + InetSocketAddress localAddress, InetSocketAddress sipServerAddress) { + mVersion = version; + mTransportType = transportType; + mLocalAddress = localAddress; + mSipServerAddress = sipServerAddress; + } + + private SipDelegateConfiguration(Parcel source) { + mVersion = source.readLong(); + mTransportType = source.readInt(); + mLocalAddress = readAddressFromParcel(source); + mSipServerAddress = readAddressFromParcel(source); + mIsSipCompactFormEnabled = source.readBoolean(); + mIsSipKeepaliveEnabled = source.readBoolean(); + mMaxUdpPayloadSize = source.readInt(); + mPublicUserIdentifier = source.readString(); + mPrivateUserIdentifier = source.readString(); + mHomeDomain = source.readString(); + mImei = source.readString(); + mGruu = source.readParcelable(null); + mSipAuthHeader = source.readString(); + mSipAuthNonce = source.readString(); + mServiceRouteHeader = source.readString(); + mPathHeader = source.readString(); + mUserAgentHeader = source.readString(); + mContactUserParam = source.readString(); + mPaniHeader = source.readString(); + mPlaniHeader = source.readString(); + mCniHeader = source.readString(); + mAssociatedUriHeader = source.readString(); + boolean isIpsecConfigAvailable = source.readBoolean(); + if (isIpsecConfigAvailable) { + mIpSecConfiguration = IpSecConfiguration.fromParcel(source); + } + boolean isNatConfigAvailable = source.readBoolean(); + if (isNatConfigAvailable) { + mNatAddress = readAddressFromParcel(source); + } + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeLong(mVersion); + dest.writeInt(mTransportType); + writeAddressToParcel(mLocalAddress, dest); + writeAddressToParcel(mSipServerAddress, dest); + dest.writeBoolean(mIsSipCompactFormEnabled); + dest.writeBoolean(mIsSipKeepaliveEnabled); + dest.writeInt(mMaxUdpPayloadSize); + dest.writeString(mPublicUserIdentifier); + dest.writeString(mPrivateUserIdentifier); + dest.writeString(mHomeDomain); + dest.writeString(mImei); + dest.writeParcelable(mGruu, flags); + dest.writeString(mSipAuthHeader); + dest.writeString(mSipAuthNonce); + dest.writeString(mServiceRouteHeader); + dest.writeString(mPathHeader); + dest.writeString(mUserAgentHeader); + dest.writeString(mContactUserParam); + dest.writeString(mPaniHeader); + dest.writeString(mPlaniHeader); + dest.writeString(mCniHeader); + dest.writeString(mAssociatedUriHeader); + dest.writeBoolean(mIpSecConfiguration != null); + if (mIpSecConfiguration != null) { + mIpSecConfiguration.addToParcel(dest); + } + dest.writeBoolean(mNatAddress != null); + if (mNatAddress != null) { + writeAddressToParcel(mNatAddress, dest); + } + } + + /** + * @return A copy of this instance with an incremented version. + * @hide + */ + public SipDelegateConfiguration copyAndIncrementVersion() { + SipDelegateConfiguration c = new SipDelegateConfiguration(getVersion() + 1, mTransportType, + mLocalAddress, mSipServerAddress); + c.mIsSipCompactFormEnabled = mIsSipCompactFormEnabled; + c.mIsSipKeepaliveEnabled = mIsSipKeepaliveEnabled; + c.mMaxUdpPayloadSize = mMaxUdpPayloadSize; + c.mIpSecConfiguration = mIpSecConfiguration; + c.mNatAddress = mNatAddress; + c.mPublicUserIdentifier = mPublicUserIdentifier; + c.mPrivateUserIdentifier = mPrivateUserIdentifier; + c.mHomeDomain = mHomeDomain; + c.mImei = mImei; + c.mGruu = mGruu; + c.mSipAuthHeader = mSipAuthHeader; + c.mSipAuthNonce = mSipAuthNonce; + c.mServiceRouteHeader = mServiceRouteHeader; + c.mPathHeader = mPathHeader; + c.mUserAgentHeader = mUserAgentHeader; + c.mContactUserParam = mContactUserParam; + c.mPaniHeader = mPaniHeader; + c.mPlaniHeader = mPlaniHeader; + c.mCniHeader = mCniHeader; + c.mAssociatedUriHeader = mAssociatedUriHeader; + return c; + } + + /** + * An integer representing the version number of this SipDelegateImsConfiguration. + * {@link SipMessage}s that are created using this configuration will also have a this + * version number associated with them, which will allow the IMS service to validate that the + * {@link SipMessage} was using the latest configuration during creation and not a stale + * configuration due to race conditions between the configuration being updated and the RCS + * application not receiving the updated configuration before generating a new message. + * <p> + * The version number should be a positive number that starts at 0 and increments sequentially + * as new {@link SipDelegateConfiguration} instances are created to update the IMS + * configuration state. + * + * @return the version number associated with this {@link SipDelegateConfiguration}. + */ + public @IntRange(from = 0) long getVersion() { + return mVersion; + } + + /** + * @return The Transport type of the SIP delegate. + */ + public @TransportType int getTransportType() { + return mTransportType; + } + + /** + * @return The local IP address and port used for SIP traffic. + */ + public @NonNull InetSocketAddress getLocalAddress() { + return mLocalAddress; + } + + /** + * @return The default IP Address and port of the SIP server or P-CSCF used for SIP traffic. + */ + public @NonNull InetSocketAddress getSipServerAddress() { + return mSipServerAddress; + } + + /** + * @return {@code true} if SIP compact form is enabled for the associated SIP Delegate, + * {@code false} if it is not. + */ + public boolean isSipCompactFormEnabled() { + return mIsSipCompactFormEnabled; + } + + /** + * @return {@code true} if SIP keepalives are enabled for the associated SIP Delegate, + * {@code false} if it is not. + */ + public boolean isSipKeepaliveEnabled() { + return mIsSipKeepaliveEnabled; + } + + /** + * @return The maximum SIP payload size in bytes for UDP or {code -1} if no value was set. + */ + public int getMaxUdpPayloadSizeBytes() { + return mMaxUdpPayloadSize; + } + + /** + * @return The IMS public user identifier or {@code null} if it was not set. + */ + public @Nullable String getPublicUserIdentifier() { + return mPublicUserIdentifier; + } + + /** + * @return The IMS private user identifier or {@code null} if it was not set. + */ + public @Nullable String getPrivateUserIdentifier() { + return mPrivateUserIdentifier; + } + + /** + * @return The IMS home domain or {@code null} if it was not set. + */ + public @Nullable String getHomeDomain() { + return mHomeDomain; + } + + /** + * get the IMEI of the associated device. + * <p> + * Application can include the Instance-ID feature tag {@code "+sip.instance"} in the Contact + * header with a value of the device IMEI in the form {@code "urn:gsma:imei:<device IMEI>"}. + * @return The IMEI of the device or {@code null} if it was not set. + */ + public @Nullable String getImei() { + return mImei; + } + + /** + * @return The IPSec configuration that must be used because SIP is communicating over IPSec. + * This returns {@code null} SIP is not communicating over IPSec. + */ + public @Nullable IpSecConfiguration getIpSecConfiguration() { + return mIpSecConfiguration; + } + + /** + * @return The public IP address and port of the device due to it being behind a NAT. + * This returns {@code null} if the device is not behind a NAT. + */ + public @Nullable InetSocketAddress getNatSocketAddress() { + return mNatAddress; + } + + /** + * @return The device's Globally routable user-agent URI (GRUU) or {@code null} if this feature + * is not enabled for the SIP delegate. + */ + public @Nullable Uri getPublicGruuUri() { + return mGruu; + } + + /** + * @return The value of the SIP authentication header or {@code null} if there is none set. + */ + public @Nullable String getSipAuthenticationHeader() { + return mSipAuthHeader; + } + + /** + * @return The value of the SIP authentication nonce or {@code null} if there is none set. + */ + public @Nullable String getSipAuthenticationNonce() { + return mSipAuthNonce; + } + + /** + * @return The value of the SIP service route header or {@code null} if there is none set. + */ + public @Nullable String getSipServiceRouteHeader() { + return mServiceRouteHeader; + } + + /** + * @return The value of the SIP path header or {@code null} if there is none set. + */ + public @Nullable String getSipPathHeader() { + return mPathHeader; + } + + /** + * @return The value of the SIP User-Agent header or {@code null} if there is none set. + */ + public @Nullable String getSipUserAgentHeader() { + return mUserAgentHeader; + } + /** + * @return The value of the SIP Contact header's User parameter or {@code null} if there is + * none set. + */ + public @Nullable String getSipContactUserParameter() { + return mContactUserParam; + } + + /** + * @return The value of the SIP P-Access-Network-Info (P-ANI) header or {@code null} if there is + * none set. + */ + public @Nullable String getSipPaniHeader() { + return mPaniHeader; + } + /** + * @return The value of the SIP P-Last-Access-Network-Info (P-LANI) header or {@code null} if + * there is none set. + */ + public @Nullable String getSipPlaniHeader() { + return mPlaniHeader; + } + + /** + * @return The value of the SIP Cellular-Network-Info (CNI) header or {@code null} if there is + * none set. + */ + public @Nullable String getSipCniHeader() { + return mCniHeader; + } + + /** + * @return The value of the SIP P-associated-uri header or {@code null} if there is none set. + */ + public @Nullable String getSipAssociatedUriHeader() { + return mAssociatedUriHeader; + } + + private void writeAddressToParcel(InetSocketAddress addr, Parcel dest) { + dest.writeByteArray(addr.getAddress().getAddress()); + dest.writeInt(addr.getPort()); + } + + private InetSocketAddress readAddressFromParcel(Parcel source) { + final byte[] addressBytes = source.createByteArray(); + final int port = source.readInt(); + try { + return new InetSocketAddress(InetAddress.getByAddress(addressBytes), port); + } catch (UnknownHostException e) { + // Should not happen, as length of array was verified before parcelling. + Log.e("SipDelegateConfiguration", "exception reading address, returning null"); + return null; + } + } + + @Override + public int describeContents() { + return 0; + } + + public static final @NonNull Creator<SipDelegateConfiguration> CREATOR = + new Creator<SipDelegateConfiguration>() { + @Override + public SipDelegateConfiguration createFromParcel(Parcel source) { + return new SipDelegateConfiguration(source); + } + + @Override + public SipDelegateConfiguration[] newArray(int size) { + return new SipDelegateConfiguration[size]; + } + }; + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + SipDelegateConfiguration that = (SipDelegateConfiguration) o; + return mVersion == that.mVersion + && mTransportType == that.mTransportType + && mIsSipCompactFormEnabled == that.mIsSipCompactFormEnabled + && mIsSipKeepaliveEnabled == that.mIsSipKeepaliveEnabled + && mMaxUdpPayloadSize == that.mMaxUdpPayloadSize + && Objects.equals(mLocalAddress, that.mLocalAddress) + && Objects.equals(mSipServerAddress, that.mSipServerAddress) + && Objects.equals(mPublicUserIdentifier, that.mPublicUserIdentifier) + && Objects.equals(mPrivateUserIdentifier, that.mPrivateUserIdentifier) + && Objects.equals(mHomeDomain, that.mHomeDomain) + && Objects.equals(mImei, that.mImei) + && Objects.equals(mGruu, that.mGruu) + && Objects.equals(mSipAuthHeader, that.mSipAuthHeader) + && Objects.equals(mSipAuthNonce, that.mSipAuthNonce) + && Objects.equals(mServiceRouteHeader, that.mServiceRouteHeader) + && Objects.equals(mPathHeader, that.mPathHeader) + && Objects.equals(mUserAgentHeader, that.mUserAgentHeader) + && Objects.equals(mContactUserParam, that.mContactUserParam) + && Objects.equals(mPaniHeader, that.mPaniHeader) + && Objects.equals(mPlaniHeader, that.mPlaniHeader) + && Objects.equals(mCniHeader, that.mCniHeader) + && Objects.equals(mAssociatedUriHeader, that.mAssociatedUriHeader) + && Objects.equals(mIpSecConfiguration, that.mIpSecConfiguration) + && Objects.equals(mNatAddress, that.mNatAddress); + } + + @Override + public int hashCode() { + return Objects.hash(mVersion, mTransportType, mLocalAddress, mSipServerAddress, + mIsSipCompactFormEnabled, mIsSipKeepaliveEnabled, mMaxUdpPayloadSize, + mPublicUserIdentifier, mPrivateUserIdentifier, mHomeDomain, mImei, mGruu, + mSipAuthHeader, mSipAuthNonce, mServiceRouteHeader, mPathHeader, mUserAgentHeader, + mContactUserParam, mPaniHeader, mPlaniHeader, mCniHeader, mAssociatedUriHeader, + mIpSecConfiguration, mNatAddress); + } + + @Override + public String toString() { + return "SipDelegateConfiguration{ mVersion=" + mVersion + ", mTransportType=" + + mTransportType + '}'; + } +} diff --git a/telephony/java/android/telephony/ims/SipDelegateConnection.java b/telephony/java/android/telephony/ims/SipDelegateConnection.java new file mode 100644 index 000000000000..4dbb08d14ccd --- /dev/null +++ b/telephony/java/android/telephony/ims/SipDelegateConnection.java @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2020 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.ims; + +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.telephony.ims.stub.SipDelegate; + +/** + * Represents a connection to the remote {@link SipDelegate} that is managed by the + * {@link ImsService} implementing IMS for the subscription that is associated with it. + * <p> + * The remote delegate will handle messages sent by this {@link SipDelegateConnection}, notifying + * the associated {@link DelegateMessageCallback} when the message was either sent successfully or + * failed to be sent. + * <p> + * It is also the responsibility of this {@link SipDelegateConnection} to acknowledge when incoming + * SIP messages have been received successfully via + * {@link DelegateMessageCallback#onMessageReceived(SipMessage)} or when there was an error + * receiving the message using {@link #notifyMessageReceived(String)} and + * {@link #notifyMessageReceiveError(String, int)}. + * + * @see SipDelegateManager#createSipDelegate + * @hide + */ +@SystemApi +public interface SipDelegateConnection { + + /** + * Send a SIP message to the SIP delegate to be sent over the carrier’s network. The + * {@link SipMessage} will either be acknowledged with + * {@link DelegateMessageCallback#onMessageSent(String)} upon successful sending of this message + * or {@link DelegateMessageCallback#onMessageSendFailure(String, int)} if there was an error + * sending the message. + * @param sipMessage The SipMessage to be sent. + * @param configVersion The SipDelegateImsConfiguration version used to construct the + * SipMessage. See {@link SipDelegateConfiguration#getVersion} for more + */ + void sendMessage(@NonNull SipMessage sipMessage, long configVersion); + + /** + * Notify the {@link SipDelegate} that a SIP message received from + * {@link DelegateMessageCallback#onMessageReceived(SipMessage)} has been received successfully + * and is being processed. + * @param viaTransactionId Per RFC3261 Sec 8.1.1.7 the transaction ID associated with the Via + * branch parameter. + */ + void notifyMessageReceived(@NonNull String viaTransactionId); + + /** + * The SIP Dialog associated with the provided Call-ID is being closed and routing resources + * associated with the SIP dialog are free to be released. + * <p> + * Calling this method is also mandatory for situations where the framework IMS stack is waiting + * for pending SIP dialogs to be closed before it can perform a handover or apply a provisioning + * change. See {@link DelegateRegistrationState} for more information about + * the scenarios where this can occur. + * <p> + * This method will need to be called for each SIP dialog managed by this application when it is + * closed. + * @param callId The call-ID header value associated with the ongoing SIP Dialog that is + * closing. + * @deprecated closeDialog does not capture INVITE forking. Use {@link #cleanupSession} instead. + */ + @Deprecated + default void closeDialog(@NonNull String callId) { + cleanupSession(callId); + } + + /** + * The SIP session associated with the provided Call-ID is being closed and routing resources + * associated with the session are free to be released. Each SIP session may contain multiple + * dialogs due to SIP INVITE forking, so this method must be called after all SIP dialogs + * associated with the session has closed. + * <p> + * Calling this method is also mandatory for situations where the framework IMS stack is waiting + * for pending SIP sessions to be closed before it can perform a handover or apply a + * provisioning change. See {@link DelegateRegistrationState} for more information about + * the scenarios where this can occur. + * <p> + * This method will need to be called for each SIP session managed by this application when it + * is closed. + * @param callId The call-ID header value associated with the ongoing SIP Dialog that is + * closing. + */ + default void cleanupSession(@NonNull String callId) { } + + /** + * Notify the SIP delegate that the SIP message has been received from + * {@link DelegateMessageCallback#onMessageReceived(SipMessage)}, however there was an error + * processing it. + * @param viaTransactionId Per RFC3261 Sec 8.1.1.7 the transaction ID associated with the Via + * branch parameter. + * @param reason The reason why the error occurred. + */ + void notifyMessageReceiveError(@NonNull String viaTransactionId, + @SipDelegateManager.MessageFailureReason int reason); +} diff --git a/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.aidl b/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.aidl new file mode 100644 index 000000000000..44ae1b130046 --- /dev/null +++ b/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2020 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.ims; + +parcelable SipDelegateImsConfiguration; diff --git a/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.java b/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.java new file mode 100644 index 000000000000..08513c23291a --- /dev/null +++ b/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.java @@ -0,0 +1,594 @@ +/* + * Copyright (C) 2020 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.ims; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.StringDef; +import android.annotation.SuppressLint; +import android.annotation.SystemApi; +import android.net.InetAddresses; +import android.net.Uri; +import android.os.Parcel; +import android.os.Parcelable; +import android.os.PersistableBundle; +import android.text.TextUtils; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.net.InetSocketAddress; + +/** + * @hide + * @deprecated Use {@link SipDelegateConfiguration} instead. + */ +@Deprecated +@SystemApi +public final class SipDelegateImsConfiguration implements Parcelable { + + /** + * IPV4 Address type. + * <p> + * Used as a potential value for {@link #KEY_SIP_CONFIG_IPTYPE_STRING}. + */ + public static final String IPTYPE_IPV4 = "IPV4"; + + /** + * IPV6 Address type. + * <p> + * Used as a potential value for {@link #KEY_SIP_CONFIG_IPTYPE_STRING}. + */ + public static final String IPTYPE_IPV6 = "IPV6"; + + /** + * The SIP transport uses UDP. + * <p> + * Used as a potential value for {@link #KEY_SIP_CONFIG_TRANSPORT_TYPE_STRING}. + */ + public static final String SIP_TRANSPORT_UDP = "UDP"; + + /** + * The SIP transport uses TCP. + * <p> + * Used as a potential value for {@link #KEY_SIP_CONFIG_TRANSPORT_TYPE_STRING}. + */ + public static final String SIP_TRANSPORT_TCP = "TCP"; + + /** + * Flag specifying if SIP compact form is enabled + */ + public static final String KEY_SIP_CONFIG_IS_COMPACT_FORM_ENABLED_BOOL = + "sip_config_is_compact_form_enabled_bool"; + + /** + * Flag specifying if SIP keepalives are enabled + */ + public static final String KEY_SIP_CONFIG_IS_KEEPALIVE_ENABLED_BOOL = + "sip_config_is_keepalive_enabled_bool"; + + /** + * Maximum SIP payload to be sent on UDP. If the SIP message payload is greater than max udp + * payload size, then TCP must be used + */ + public static final String KEY_SIP_CONFIG_MAX_PAYLOAD_SIZE_ON_UDP_INT = + "sip_config_udp_max_payload_size_int"; + + /** + * Transport protocol used for SIP signaling. + * Available options are: {@link #SIP_TRANSPORT_UDP }, {@link #SIP_TRANSPORT_TCP } + */ + public static final String KEY_SIP_CONFIG_TRANSPORT_TYPE_STRING = + "sip_config_protocol_type_string"; + + /** + * IMS public user identifier string + */ + public static final String KEY_SIP_CONFIG_UE_PUBLIC_USER_ID_STRING = + "sip_config_ue_public_user_id_string"; + + /** + * IMS private user identifier string + */ + public static final String KEY_SIP_CONFIG_UE_PRIVATE_USER_ID_STRING = + "sip_config_ue_private_user_id_string"; + + /** + * IMS home domain string + */ + public static final String KEY_SIP_CONFIG_HOME_DOMAIN_STRING = "sip_config_home_domain_string"; + + /** + * IMEI string. Application can include the Instance-ID feature tag " +sip.instance" in the + * Contact header with a value of the device IMEI in the form "urn:gsma:imei:<device IMEI>". + */ + public static final String KEY_SIP_CONFIG_IMEI_STRING = "sip_config_imei_string"; + + /** + * IP address type for SIP signaling. + * Available options are: {@link #IPTYPE_IPV6}, {@link #IPTYPE_IPV4} + */ + public static final String KEY_SIP_CONFIG_IPTYPE_STRING = "sip_config_iptype_string"; + + /** + * Local IPaddress used for SIP signaling. + */ + public static final String KEY_SIP_CONFIG_UE_DEFAULT_IPADDRESS_STRING = + "sip_config_ue_default_ipaddress_string"; + + /** + * Local port used for sending SIP traffic + */ + public static final String KEY_SIP_CONFIG_UE_DEFAULT_PORT_INT = + "sip_config_ue_default_port_int"; + + /** + * SIP server / PCSCF default ip address + */ + public static final String KEY_SIP_CONFIG_SERVER_DEFAULT_IPADDRESS_STRING = + "sip_config_server_default_ipaddress_string"; + + /** + * SIP server / PCSCF port used for sending SIP traffic + */ + public static final String KEY_SIP_CONFIG_SERVER_DEFAULT_PORT_INT = + "sip_config_server_default_port_int"; + + /** + * Flag specifying if Network Address Translation is enabled and UE is behind a NAT. + */ + public static final String KEY_SIP_CONFIG_IS_NAT_ENABLED_BOOL = + "sip_config_is_nat_enabled_bool"; + + /** + * UE's public IPaddress when UE is behind a NAT. + * <p> + * This key will not exist if {@link #KEY_SIP_CONFIG_IS_NAT_ENABLED_BOOL} is {@code false}. + */ + public static final String KEY_SIP_CONFIG_UE_PUBLIC_IPADDRESS_WITH_NAT_STRING = + "sip_config_ue_public_ipaddress_with_nat_string"; + + /** + * UE's public SIP port when UE is behind a NAT. + * <p> + * This key will not exist if {@link #KEY_SIP_CONFIG_IS_NAT_ENABLED_BOOL} is {@code false}. + */ + public static final String KEY_SIP_CONFIG_UE_PUBLIC_PORT_WITH_NAT_INT = + "sip_config_ue_public_port_with_nat_int"; + + /** + * Flag specifying if Globally routable user-agent uri (GRUU) is enabled as per TS 23.808 + */ + public static final String KEY_SIP_CONFIG_IS_GRUU_ENABLED_BOOL = + "sip_config_is_gruu_enabled_bool"; + + /** + * UE's Globally routable user-agent uri if this feature is enabled. + * <p> + * This key will not exist if {@link #KEY_SIP_CONFIG_IS_GRUU_ENABLED_BOOL} is {@code false}. + */ + public static final String KEY_SIP_CONFIG_UE_PUBLIC_GRUU_STRING = + "sip_config_ue_public_gruu_string"; + + /** + * Flag specifying if SIP over IPSec is enabled. + */ + public static final String KEY_SIP_CONFIG_IS_IPSEC_ENABLED_BOOL = + "sip_config_is_ipsec_enabled_bool"; + /** + * UE's SIP port used to send traffic when IPSec is enabled. + * <p> + * This key will not exist if {@link #KEY_SIP_CONFIG_IS_IPSEC_ENABLED_BOOL} is {@code false}. + */ + public static final String KEY_SIP_CONFIG_UE_IPSEC_CLIENT_PORT_INT = + "sip_config_ue_ipsec_client_port_int"; + + /** + * UE's SIP port used to receive traffic when IPSec is enabled. + * <p> + * This key will not exist if {@link #KEY_SIP_CONFIG_IS_IPSEC_ENABLED_BOOL} is {@code false}. + */ + public static final String KEY_SIP_CONFIG_UE_IPSEC_SERVER_PORT_INT = + "sip_config_ue_ipsec_server_port_int"; + + /** + * UE's SIP port used for the previous IPsec security association if IPSec is enabled. + * <p> + * This key will not exist if {@link #KEY_SIP_CONFIG_IS_IPSEC_ENABLED_BOOL} is {@code false}. + */ + public static final String KEY_SIP_CONFIG_UE_IPSEC_OLD_CLIENT_PORT_INT = + "sip_config_ue_ipsec_old_client_port_int"; + + /** + * Port number used by the SIP server to send SIP traffic when IPSec is enabled. + * <p> + * This key will not exist if {@link #KEY_SIP_CONFIG_IS_IPSEC_ENABLED_BOOL} is {@code false}. + */ + public static final String KEY_SIP_CONFIG_SERVER_IPSEC_CLIENT_PORT_INT = + "sip_config_server_ipsec_client_port_int"; + + /** + * Port number used by the SIP server to receive incoming SIP traffic when IPSec is enabled. + * <p> + * This key will not exist if {@link #KEY_SIP_CONFIG_IS_IPSEC_ENABLED_BOOL} is {@code false}. + */ + public static final String KEY_SIP_CONFIG_SERVER_IPSEC_SERVER_PORT_INT = + "sip_config_server_ipsec_server_port_int"; + + /** + * Port number used by the SIP server to send SIP traffic on the previous IPSec security + * association when IPSec is enabled. + * <p> + * This key will not exist if {@link #KEY_SIP_CONFIG_IS_IPSEC_ENABLED_BOOL} is {@code false}. + */ + public static final String KEY_SIP_CONFIG_SERVER_IPSEC_OLD_CLIENT_PORT_INT = + "sip_config_server_ipsec_old_client_port_int"; + /** + * SIP Authentication header string + */ + public static final String KEY_SIP_CONFIG_AUTHENTICATION_HEADER_STRING = + "sip_config_auhentication_header_string"; + + /** + * SIP Authentication nonce string + */ + public static final String KEY_SIP_CONFIG_AUTHENTICATION_NONCE_STRING = + "sip_config_authentication_nonce_string"; + + /** + * SIP service route header string + */ + public static final String KEY_SIP_CONFIG_SERVICE_ROUTE_HEADER_STRING = + "sip_config_service_route_header_string"; + + /** + * SIP security verify header string + */ + public static final String KEY_SIP_CONFIG_SECURITY_VERIFY_HEADER_STRING = + "sip_config_security_verify_header_string"; + + /** + * SIP Path header string + */ + public static final String KEY_SIP_CONFIG_PATH_HEADER_STRING = + "sip_config_path_header_string"; + + /** + * The SIP User-Agent header value used by the IMS stack during IMS registration. + */ + public static final String KEY_SIP_CONFIG_USER_AGENT_HEADER_STRING = + "sip_config_sip_user_agent_header_string"; + + /** + * SIP User part string in contact header + */ + public static final String KEY_SIP_CONFIG_URI_USER_PART_STRING = + "sip_config_uri_user_part_string"; + + /** + * SIP P-access-network-info header string + */ + public static final String KEY_SIP_CONFIG_P_ACCESS_NETWORK_INFO_HEADER_STRING = + "sip_config_p_access_network_info_header_string"; + + /** + * The SIP P-last-access-network-info header value, populated for networks that require this + * information to be provided in outgoing SIP messages. + */ + public static final String KEY_SIP_CONFIG_P_LAST_ACCESS_NETWORK_INFO_HEADER_STRING = + "sip_config_p_last_access_network_info_header_string"; + + /** + * The Cellular-Network-Info header value (See 3GPP 24.229, section 7.2.15), populated for + * networks that require this information to be provided as part of outgoing SIP messages. + */ + public static final String KEY_SIP_CONFIG_CELLULAR_NETWORK_INFO_HEADER_STRING = + "sip_config_cellular_network_info_header_string"; + + /** + * SIP P-associated-uri header string + */ + public static final String KEY_SIP_CONFIG_P_ASSOCIATED_URI_HEADER_STRING = + "sip_config_p_associated_uri_header_string"; + + /**@hide*/ + @StringDef(prefix = "KEY_SIP_CONFIG", suffix = "_STRING", value = { + KEY_SIP_CONFIG_TRANSPORT_TYPE_STRING, + KEY_SIP_CONFIG_UE_PUBLIC_USER_ID_STRING, + KEY_SIP_CONFIG_UE_PRIVATE_USER_ID_STRING, + KEY_SIP_CONFIG_HOME_DOMAIN_STRING, + KEY_SIP_CONFIG_IMEI_STRING, + KEY_SIP_CONFIG_IPTYPE_STRING, + KEY_SIP_CONFIG_UE_DEFAULT_IPADDRESS_STRING, + KEY_SIP_CONFIG_SERVER_DEFAULT_IPADDRESS_STRING, + KEY_SIP_CONFIG_UE_PUBLIC_IPADDRESS_WITH_NAT_STRING, + KEY_SIP_CONFIG_UE_PUBLIC_GRUU_STRING, + KEY_SIP_CONFIG_AUTHENTICATION_HEADER_STRING, + KEY_SIP_CONFIG_AUTHENTICATION_NONCE_STRING, + KEY_SIP_CONFIG_SERVICE_ROUTE_HEADER_STRING, + KEY_SIP_CONFIG_SECURITY_VERIFY_HEADER_STRING, + KEY_SIP_CONFIG_PATH_HEADER_STRING, + KEY_SIP_CONFIG_USER_AGENT_HEADER_STRING, + KEY_SIP_CONFIG_URI_USER_PART_STRING, + KEY_SIP_CONFIG_P_ACCESS_NETWORK_INFO_HEADER_STRING, + KEY_SIP_CONFIG_P_LAST_ACCESS_NETWORK_INFO_HEADER_STRING, + KEY_SIP_CONFIG_CELLULAR_NETWORK_INFO_HEADER_STRING, + KEY_SIP_CONFIG_P_ASSOCIATED_URI_HEADER_STRING + }) + @Retention(RetentionPolicy.SOURCE) + public @interface StringConfigKey {} + + /**@hide*/ + @StringDef(prefix = "KEY_SIP_CONFIG", suffix = "_INT", value = { + KEY_SIP_CONFIG_MAX_PAYLOAD_SIZE_ON_UDP_INT, + KEY_SIP_CONFIG_UE_DEFAULT_PORT_INT, + KEY_SIP_CONFIG_SERVER_DEFAULT_PORT_INT, + KEY_SIP_CONFIG_UE_PUBLIC_PORT_WITH_NAT_INT, + KEY_SIP_CONFIG_UE_IPSEC_CLIENT_PORT_INT, + KEY_SIP_CONFIG_UE_IPSEC_SERVER_PORT_INT, + KEY_SIP_CONFIG_UE_IPSEC_OLD_CLIENT_PORT_INT, + KEY_SIP_CONFIG_SERVER_IPSEC_CLIENT_PORT_INT, + KEY_SIP_CONFIG_SERVER_IPSEC_SERVER_PORT_INT, + KEY_SIP_CONFIG_SERVER_IPSEC_OLD_CLIENT_PORT_INT + }) + @Retention(RetentionPolicy.SOURCE) + public @interface IntConfigKey {} + + /**@hide*/ + @StringDef(prefix = "KEY_SIP_CONFIG", suffix = "_BOOL", value = { + KEY_SIP_CONFIG_IS_COMPACT_FORM_ENABLED_BOOL, + KEY_SIP_CONFIG_IS_KEEPALIVE_ENABLED_BOOL, + KEY_SIP_CONFIG_IS_NAT_ENABLED_BOOL, + KEY_SIP_CONFIG_IS_GRUU_ENABLED_BOOL, + KEY_SIP_CONFIG_IS_IPSEC_ENABLED_BOOL + }) + @Retention(RetentionPolicy.SOURCE) + public @interface BooleanConfigKey {} + + /** + * Builder class to be used when constructing a new SipDelegateImsConfiguration. + */ + public static final class Builder { + private final long mVersion; + private final PersistableBundle mBundle; + + /** + * Creates an empty implementation of SipDelegateImsConfiguration. + * @param version The version associated with the SipDelegateImsConfiguration being built. + * See {@link #getVersion} for more information. + */ + public Builder(int version) { + mVersion = version; + mBundle = new PersistableBundle(); + } + /** + * Clones an existing implementation of SipDelegateImsConfiguration to handle situations + * where only a small number of parameters have changed from the previous configuration. + * <p> + * Automatically increments the version of this configuration by 1. See {@link #getVersion} + * for more information. + */ + public Builder(@NonNull SipDelegateImsConfiguration config) { + mVersion = config.getVersion() + 1; + mBundle = config.copyBundle(); + } + /** + * Put a string value into this configuration bundle for the given key. + */ + // getString is available below. + @SuppressLint("MissingGetterMatchingBuilder") + public @NonNull Builder addString(@NonNull @StringConfigKey String key, + @NonNull String value) { + mBundle.putString(key, value); + return this; + } + + /** + * Replace the existing default value with a new value for a given key. + */ + // getInt is available below. + @SuppressLint("MissingGetterMatchingBuilder") + public @NonNull Builder addInt(@NonNull @IntConfigKey String key, int value) { + mBundle.putInt(key, value); + return this; + } + + /** + * Replace the existing default value with a new value for a given key. + */ + // getBoolean is available below. + @SuppressLint("MissingGetterMatchingBuilder") + public @NonNull Builder addBoolean(@NonNull @BooleanConfigKey String key, boolean value) { + mBundle.putBoolean(key, value); + return this; + } + + /** + * @return a new SipDelegateImsConfiguration from this Builder. + */ + public @NonNull SipDelegateImsConfiguration build() { + return new SipDelegateImsConfiguration(mVersion, mBundle); + } + } + + private final long mVersion; + private final PersistableBundle mBundle; + + private SipDelegateImsConfiguration(long version, PersistableBundle bundle) { + mVersion = version; + mBundle = bundle; + } + + private SipDelegateImsConfiguration(Parcel source) { + mVersion = source.readLong(); + mBundle = source.readPersistableBundle(); + } + + /** + * @return {@code true} if this configuration object has a an entry for the key specified, + * {@code false} if it does not. + */ + public boolean containsKey(@NonNull String key) { + return mBundle.containsKey(key); + } + + /** + * @return the string value associated with a given key or {@code null} if it doesn't exist. + */ + public @Nullable @StringConfigKey String getString(@NonNull String key) { + return mBundle.getString(key); + } + + /** + * @return the integer value associated with a given key if it exists or the supplied default + * value if it does not. + */ + public @IntConfigKey int getInt(@NonNull String key, int defaultValue) { + if (!mBundle.containsKey(key)) { + return defaultValue; + } + return mBundle.getInt(key); + } + + /** + * @return the boolean value associated with a given key or the supplied default value if the + * value doesn't exist in the bundle. + */ + public @BooleanConfigKey boolean getBoolean(@NonNull String key, boolean defaultValue) { + if (!mBundle.containsKey(key)) { + return defaultValue; + } + return mBundle.getBoolean(key); + } + + /** + * @return a shallow copy of the full configuration. + */ + public @NonNull PersistableBundle copyBundle() { + return new PersistableBundle(mBundle); + } + + /** + * An integer representing the version number of this SipDelegateImsConfiguration. + * {@link SipMessage}s that are created using this configuration will also have a this + * version number associated with them, which will allow the IMS service to validate that the + * {@link SipMessage} was using the latest configuration during creation and not a stale + * configuration due to race conditions between the configuration being updated and the RCS + * application not receiving the updated configuration before generating a new message. + * <p> + * The version number should be a positive number that starts at 0 and increments sequentially + * as new {@link SipDelegateImsConfiguration} instances are created to update the IMS + * configuration state. + * + * @return the version number associated with this {@link SipDelegateImsConfiguration}. + */ + public long getVersion() { + return mVersion; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeLong(mVersion); + dest.writePersistableBundle(mBundle); + } + + public static final @NonNull Creator<SipDelegateImsConfiguration> CREATOR = + new Creator<SipDelegateImsConfiguration>() { + @Override + public SipDelegateImsConfiguration createFromParcel(Parcel source) { + return new SipDelegateImsConfiguration(source); + } + + @Override + public SipDelegateImsConfiguration[] newArray(int size) { + return new SipDelegateImsConfiguration[size]; + } + }; + + /** + * Temporary helper to transition from old form of config to new form. + * @return new config + * @hide + */ + public SipDelegateConfiguration toNewConfig() { + // IP version is now included in call to InetSocketAddr + String transportTypeString = getString(KEY_SIP_CONFIG_TRANSPORT_TYPE_STRING); + int transportType = (transportTypeString != null + && transportTypeString.equals(SIP_TRANSPORT_UDP)) + ? SipDelegateConfiguration.SIP_TRANSPORT_UDP + : SipDelegateConfiguration.SIP_TRANSPORT_TCP; + SipDelegateConfiguration.Builder builder = new SipDelegateConfiguration.Builder(mVersion, + transportType, + getSocketAddr(getString(KEY_SIP_CONFIG_UE_DEFAULT_IPADDRESS_STRING), + getInt(KEY_SIP_CONFIG_UE_DEFAULT_PORT_INT, -1)), + getSocketAddr(getString(KEY_SIP_CONFIG_SERVER_DEFAULT_IPADDRESS_STRING), + getInt(KEY_SIP_CONFIG_SERVER_DEFAULT_PORT_INT, -1))); + builder.setSipCompactFormEnabled( + getBoolean(KEY_SIP_CONFIG_IS_COMPACT_FORM_ENABLED_BOOL, false)); + builder.setSipKeepaliveEnabled( + getBoolean(KEY_SIP_CONFIG_IS_KEEPALIVE_ENABLED_BOOL, false)); + builder.setMaxUdpPayloadSizeBytes(getInt(KEY_SIP_CONFIG_MAX_PAYLOAD_SIZE_ON_UDP_INT, -1)); + builder.setPublicUserIdentifier(getString(KEY_SIP_CONFIG_UE_PUBLIC_USER_ID_STRING)); + builder.setPrivateUserIdentifier(getString(KEY_SIP_CONFIG_UE_PRIVATE_USER_ID_STRING)); + builder.setHomeDomain(getString(KEY_SIP_CONFIG_HOME_DOMAIN_STRING)); + builder.setImei(getString(KEY_SIP_CONFIG_IMEI_STRING)); + builder.setSipAuthenticationHeader(getString(KEY_SIP_CONFIG_AUTHENTICATION_HEADER_STRING)); + builder.setSipAuthenticationNonce(getString(KEY_SIP_CONFIG_AUTHENTICATION_NONCE_STRING)); + builder.setSipServiceRouteHeader(getString(KEY_SIP_CONFIG_SERVICE_ROUTE_HEADER_STRING)); + builder.setSipPathHeader(getString(KEY_SIP_CONFIG_PATH_HEADER_STRING)); + builder.setSipUserAgentHeader(getString(KEY_SIP_CONFIG_USER_AGENT_HEADER_STRING)); + builder.setSipContactUserParameter(getString(KEY_SIP_CONFIG_URI_USER_PART_STRING)); + builder.setSipPaniHeader(getString(KEY_SIP_CONFIG_P_ACCESS_NETWORK_INFO_HEADER_STRING)); + builder.setSipPlaniHeader( + getString(KEY_SIP_CONFIG_P_LAST_ACCESS_NETWORK_INFO_HEADER_STRING)); + builder.setSipCniHeader(getString(KEY_SIP_CONFIG_CELLULAR_NETWORK_INFO_HEADER_STRING)); + builder.setSipAssociatedUriHeader(getString(KEY_SIP_CONFIG_P_ASSOCIATED_URI_HEADER_STRING)); + if (getBoolean(KEY_SIP_CONFIG_IS_GRUU_ENABLED_BOOL, false)) { + String uri = getString(KEY_SIP_CONFIG_UE_PUBLIC_GRUU_STRING); + Uri gruuUri = null; + if (!TextUtils.isEmpty(uri)) { + gruuUri = Uri.parse(uri); + } + builder.setPublicGruuUri(gruuUri); + } + if (getBoolean(KEY_SIP_CONFIG_IS_IPSEC_ENABLED_BOOL, false)) { + builder.setIpSecConfiguration(new SipDelegateConfiguration.IpSecConfiguration( + getInt(KEY_SIP_CONFIG_UE_IPSEC_CLIENT_PORT_INT, -1), + getInt(KEY_SIP_CONFIG_UE_IPSEC_SERVER_PORT_INT, -1), + getInt(KEY_SIP_CONFIG_UE_IPSEC_OLD_CLIENT_PORT_INT, -1), + getInt(KEY_SIP_CONFIG_SERVER_IPSEC_CLIENT_PORT_INT, -1), + getInt(KEY_SIP_CONFIG_SERVER_IPSEC_SERVER_PORT_INT, -1), + getInt(KEY_SIP_CONFIG_SERVER_IPSEC_OLD_CLIENT_PORT_INT, -1), + getString(KEY_SIP_CONFIG_SECURITY_VERIFY_HEADER_STRING)) + ); + } + if (getBoolean(KEY_SIP_CONFIG_IS_NAT_ENABLED_BOOL, false)) { + builder.setNatSocketAddress(getSocketAddr( + getString(KEY_SIP_CONFIG_UE_PUBLIC_IPADDRESS_WITH_NAT_STRING), + getInt(KEY_SIP_CONFIG_UE_PUBLIC_PORT_WITH_NAT_INT, -1))); + } + return builder.build(); + } + + private InetSocketAddress getSocketAddr(String ipAddr, int port) { + return new InetSocketAddress(InetAddresses.parseNumericAddress(ipAddr), port); + } +} diff --git a/telephony/java/android/telephony/ims/SipDelegateManager.java b/telephony/java/android/telephony/ims/SipDelegateManager.java new file mode 100644 index 000000000000..5a8066320e99 --- /dev/null +++ b/telephony/java/android/telephony/ims/SipDelegateManager.java @@ -0,0 +1,449 @@ +/* + * Copyright (C) 2020 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.ims; + +import android.Manifest; +import android.annotation.IntDef; +import android.annotation.IntRange; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.RequiresPermission; +import android.annotation.SystemApi; +import android.content.Context; +import android.content.pm.PackageManager; +import android.os.RemoteException; +import android.os.ServiceSpecificException; +import android.telephony.BinderCacheManager; +import android.telephony.CarrierConfigManager; +import android.telephony.ims.aidl.IImsRcsController; +import android.telephony.ims.aidl.SipDelegateConnectionAidlWrapper; +import android.telephony.ims.stub.DelegateConnectionMessageCallback; +import android.telephony.ims.stub.DelegateConnectionStateCallback; +import android.telephony.ims.stub.SipDelegate; +import android.util.ArrayMap; + +import com.android.internal.annotations.VisibleForTesting; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Objects; +import java.util.concurrent.Executor; + +/** + * Manages the creation and destruction of SipDelegates for the {@link ImsService} managing IMS + * for the subscription ID that this SipDelegateManager has been created for. + * + * This allows multiple IMS applications to forward SIP messages to/from their application for the + * purposes of providing a single IMS registration to the carrier's IMS network from potentially + * many IMS stacks implementing a subset of the supported MMTEL/RCS features. + * <p> + * This API is only supported if the device supports the + * {@link PackageManager#FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION} feature. + * @hide + */ +@SystemApi +public class SipDelegateManager { + + /** + * The SIP message has failed being sent or received for an unknown reason. + * <p> + * The caller should retry a message that failed with this response. + */ + public static final int MESSAGE_FAILURE_REASON_UNKNOWN = 0; + + /** + * The remote service associated with this connection has died and the message was not + * properly sent/received. + * <p> + * This is considered a permanent error and the system will automatically begin the teardown and + * destruction of the SipDelegate. No further messages should be sent on this transport. + */ + public static final int MESSAGE_FAILURE_REASON_DELEGATE_DEAD = 1; + + /** + * The message has not been sent/received because the delegate is in the process of closing and + * has become unavailable. No further messages should be sent/received on this delegate. + */ + public static final int MESSAGE_FAILURE_REASON_DELEGATE_CLOSED = 2; + + /** + * The SIP message has an invalid start line and the message can not be sent or the start line + * failed validation due to the request containing a restricted SIP request method. + * {@link SipDelegateConnection}s can not send SIP requests for the methods: REGISTER, PUBLISH, + * or OPTIONS. + */ + public static final int MESSAGE_FAILURE_REASON_INVALID_START_LINE = 3; + + /** + * One or more of the header fields in the header section of the outgoing SIP message is invalid + * or contains a restricted header value and the SIP message can not be sent. + * {@link SipDelegateConnection}s can not send SIP SUBSCRIBE requests for the "Event" header + * value of "presence". + */ + public static final int MESSAGE_FAILURE_REASON_INVALID_HEADER_FIELDS = 4; + + /** + * The body content of the SIP message is invalid and the message can not be sent. + */ + public static final int MESSAGE_FAILURE_REASON_INVALID_BODY_CONTENT = 5; + + /** + * The feature tag associated with the outgoing message does not match any known feature tags + * or it matches a denied tag and this message can not be sent. + */ + public static final int MESSAGE_FAILURE_REASON_INVALID_FEATURE_TAG = 6; + + /** + * The feature tag associated with the outgoing message is not enabled for the associated + * SipDelegateConnection and can not be sent. + */ + public static final int MESSAGE_FAILURE_REASON_TAG_NOT_ENABLED_FOR_DELEGATE = 7; + + /** + * The link to the network has been lost and the outgoing message has failed to send. + * <p> + * This message should be retried when connectivity to the network is re-established. See + * {@link android.net.ConnectivityManager.NetworkCallback} for how this can be determined. + */ + public static final int MESSAGE_FAILURE_REASON_NETWORK_NOT_AVAILABLE = 8; + + /** + * The outgoing SIP message has not been sent due to the SipDelegate not being registered for + * IMS at this time. + * <p> + * This is considered a temporary failure, the message should not be retried until an IMS + * registration change callback is received via + * {@link DelegateConnectionStateCallback#onFeatureTagStatusChanged} + */ + public static final int MESSAGE_FAILURE_REASON_NOT_REGISTERED = 9; + + /** + * The outgoing SIP message has not been sent because the {@link SipDelegateConfiguration} + * version associated with the outgoing {@link SipMessage} is now stale and has failed + * validation checks. + * <p> + * The @link SipMessage} should be recreated using the newest + * {@link SipDelegateConfiguration} and sent again. + */ + public static final int MESSAGE_FAILURE_REASON_STALE_IMS_CONFIGURATION = 10; + + /** + * The outgoing SIP message has not been sent because the internal state of the associated + * {@link SipDelegate} is changing and has temporarily brought the transport down. + * <p> + * This is considered a temporary error and the {@link SipDelegateConnection} should resend the + * message once {@link DelegateRegistrationState#DEREGISTERING_REASON_FEATURE_TAGS_CHANGING} is + * no longer reported. + */ + public static final int MESSAGE_FAILURE_REASON_INTERNAL_DELEGATE_STATE_TRANSITION = 11; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = "MESSAGE_FAILURE_REASON_", value = { + MESSAGE_FAILURE_REASON_UNKNOWN, + MESSAGE_FAILURE_REASON_DELEGATE_DEAD, + MESSAGE_FAILURE_REASON_DELEGATE_CLOSED, + MESSAGE_FAILURE_REASON_INVALID_START_LINE, + MESSAGE_FAILURE_REASON_INVALID_HEADER_FIELDS, + MESSAGE_FAILURE_REASON_INVALID_BODY_CONTENT, + MESSAGE_FAILURE_REASON_INVALID_FEATURE_TAG, + MESSAGE_FAILURE_REASON_TAG_NOT_ENABLED_FOR_DELEGATE, + MESSAGE_FAILURE_REASON_NETWORK_NOT_AVAILABLE, + MESSAGE_FAILURE_REASON_NOT_REGISTERED, + MESSAGE_FAILURE_REASON_STALE_IMS_CONFIGURATION, + MESSAGE_FAILURE_REASON_INTERNAL_DELEGATE_STATE_TRANSITION + }) + public @interface MessageFailureReason {} + + /**@hide*/ + public static final ArrayMap<Integer, String> MESSAGE_FAILURE_REASON_STRING_MAP = + new ArrayMap<>(11); + static { + MESSAGE_FAILURE_REASON_STRING_MAP.append(MESSAGE_FAILURE_REASON_UNKNOWN, + "MESSAGE_FAILURE_REASON_UNKNOWN"); + MESSAGE_FAILURE_REASON_STRING_MAP.append(MESSAGE_FAILURE_REASON_DELEGATE_DEAD, + "MESSAGE_FAILURE_REASON_DELEGATE_DEAD"); + MESSAGE_FAILURE_REASON_STRING_MAP.append(MESSAGE_FAILURE_REASON_DELEGATE_CLOSED, + "MESSAGE_FAILURE_REASON_DELEGATE_CLOSED"); + MESSAGE_FAILURE_REASON_STRING_MAP.append(MESSAGE_FAILURE_REASON_INVALID_HEADER_FIELDS, + "MESSAGE_FAILURE_REASON_INVALID_HEADER_FIELDS"); + MESSAGE_FAILURE_REASON_STRING_MAP.append(MESSAGE_FAILURE_REASON_INVALID_BODY_CONTENT, + "MESSAGE_FAILURE_REASON_INVALID_BODY_CONTENT"); + MESSAGE_FAILURE_REASON_STRING_MAP.append(MESSAGE_FAILURE_REASON_INVALID_FEATURE_TAG, + "MESSAGE_FAILURE_REASON_INVALID_FEATURE_TAG"); + MESSAGE_FAILURE_REASON_STRING_MAP.append( + MESSAGE_FAILURE_REASON_TAG_NOT_ENABLED_FOR_DELEGATE, + "MESSAGE_FAILURE_REASON_TAG_NOT_ENABLED_FOR_DELEGATE"); + MESSAGE_FAILURE_REASON_STRING_MAP.append(MESSAGE_FAILURE_REASON_NETWORK_NOT_AVAILABLE, + "MESSAGE_FAILURE_REASON_NETWORK_NOT_AVAILABLE"); + MESSAGE_FAILURE_REASON_STRING_MAP.append(MESSAGE_FAILURE_REASON_NOT_REGISTERED, + "MESSAGE_FAILURE_REASON_NOT_REGISTERED"); + MESSAGE_FAILURE_REASON_STRING_MAP.append(MESSAGE_FAILURE_REASON_STALE_IMS_CONFIGURATION, + "MESSAGE_FAILURE_REASON_STALE_IMS_CONFIGURATION"); + MESSAGE_FAILURE_REASON_STRING_MAP.append( + MESSAGE_FAILURE_REASON_INTERNAL_DELEGATE_STATE_TRANSITION, + "MESSAGE_FAILURE_REASON_INTERNAL_DELEGATE_STATE_TRANSITION"); + } + + /** + * Access to use this feature tag has been denied for an unknown reason. + */ + public static final int DENIED_REASON_UNKNOWN = 0; + + /** + * This feature tag is allowed to be used by this SipDelegateConnection, but it is in use by + * another SipDelegateConnection and can not be associated with this delegate. The feature tag + * will stay in this state until the feature tag is release by the other application. + */ + public static final int DENIED_REASON_IN_USE_BY_ANOTHER_DELEGATE = 1; + + /** + * Access to use this feature tag has been denied because this application does not have the + * permissions required to access this feature tag. + */ + public static final int DENIED_REASON_NOT_ALLOWED = 2; + + /** + * Access to use this feature tag has been denied because single registration is not allowed by + * the carrier at this time. The application should fall back to dual registration if + * applicable. + */ + public static final int DENIED_REASON_SINGLE_REGISTRATION_NOT_ALLOWED = 3; + + /** + * This feature tag is not recognized as a valid feature tag by the SipDelegate and has been + * denied. + */ + public static final int DENIED_REASON_INVALID = 4; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = "DENIED_REASON_", value = { + DENIED_REASON_UNKNOWN, + DENIED_REASON_IN_USE_BY_ANOTHER_DELEGATE, + DENIED_REASON_NOT_ALLOWED, + DENIED_REASON_SINGLE_REGISTRATION_NOT_ALLOWED, + DENIED_REASON_INVALID + }) + public @interface DeniedReason {} + + /** + * The SipDelegate has closed due to an unknown reason. + */ + public static final int SIP_DELEGATE_DESTROY_REASON_UNKNOWN = 0; + + /** + * The SipDelegate has closed because the IMS service has died unexpectedly. + */ + public static final int SIP_DELEGATE_DESTROY_REASON_SERVICE_DEAD = 1; + + /** + * The SipDelegate has closed because the IMS application has requested that the connection be + * destroyed. + */ + public static final int SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP = 2; + + /** + * The SipDelegate has been closed due to the user disabling RCS. + */ + public static final int SIP_DELEGATE_DESTROY_REASON_USER_DISABLED_RCS = 3; + + /** + * The SipDelegate has been closed due to the subscription associated with this delegate being + * torn down. + */ + public static final int SIP_DELEGATE_DESTROY_REASON_SUBSCRIPTION_TORN_DOWN = 4; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = "SIP_DELEGATE_DESTROY_REASON", value = { + SIP_DELEGATE_DESTROY_REASON_UNKNOWN, + SIP_DELEGATE_DESTROY_REASON_SERVICE_DEAD, + SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP, + SIP_DELEGATE_DESTROY_REASON_USER_DISABLED_RCS, + SIP_DELEGATE_DESTROY_REASON_SUBSCRIPTION_TORN_DOWN + }) + public @interface SipDelegateDestroyReason {} + + private final Context mContext; + private final int mSubId; + private final BinderCacheManager<IImsRcsController> mBinderCache; + + /** + * Only visible for testing. To instantiate an instance of this class, please use + * {@link ImsManager#getSipDelegateManager(int)}. + * @hide + */ + @VisibleForTesting + public SipDelegateManager(Context context, int subId, + BinderCacheManager<IImsRcsController> binderCache) { + mContext = context; + mSubId = subId; + mBinderCache = binderCache; + } + + /** + * Determines if creating SIP delegates are supported for the subscription specified. + * <p> + * If SIP delegates are not supported on this device or the carrier associated with this + * subscription, creating a SIP delegate will always fail, as this feature is not supported. + * @return true if this device supports creating a SIP delegate and the carrier associated with + * this subscription supports single registration, false if creating SIP delegates is not + * supported. + * @throws ImsException If the remote ImsService is not available for any reason or the + * subscription associated with this instance is no longer active. See + * {@link ImsException#getCode()} for more information. + * + * @see CarrierConfigManager.Ims#KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL + * @see PackageManager#FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION + */ + @RequiresPermission(anyOf = {Manifest.permission.READ_PRIVILEGED_PHONE_STATE, + Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION}) + public boolean isSupported() throws ImsException { + try { + IImsRcsController controller = mBinderCache.getBinder(); + if (controller == null) { + throw new ImsException("Telephony server is down", + ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); + } + return controller.isSipDelegateSupported(mSubId); + } catch (ServiceSpecificException e) { + throw new ImsException(e.getMessage(), e.errorCode); + } catch (RemoteException e) { + throw new ImsException(e.getMessage(), + ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); + } + } + + /** + * Request that the ImsService implementation create a SipDelegate, which will configure the + * ImsService to forward SIP traffic that matches the filtering criteria set in supplied + * {@link DelegateRequest} to the application that the supplied callbacks are registered for. + * <p> + * This API requires that the caller is running as part of a long-running process and will + * always be available to handle incoming messages. One mechanism that can be used for this is + * the {@link android.service.carrier.CarrierMessagingClientService}, which the framework keeps + * a persistent binding to when the app is the default SMS application. + * <p> + * Note: the ability to create SipDelegates is only available applications running as the + * primary user. + * @param request The parameters that are associated with the SipDelegate creation request that + * will be used to create the SipDelegate connection. + * @param executor The executor that will be used to call the callbacks associated with this + * SipDelegate. + * @param dc The callback that will be used to notify the listener of the creation/destruction + * of the remote SipDelegate as well as changes to the state of the remote SipDelegate + * connection. + * @param mc The callback that will be used to notify the listener of new incoming SIP messages + * as well as the status of messages that were sent by the associated + * SipDelegateConnection. + * @throws ImsException Thrown if there was a problem communicating with the ImsService + * associated with this SipDelegateManager. See {@link ImsException#getCode()}. + */ + @RequiresPermission(Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION) + public void createSipDelegate(@NonNull DelegateRequest request, @NonNull Executor executor, + @NonNull DelegateConnectionStateCallback dc, + @NonNull DelegateConnectionMessageCallback mc) throws ImsException { + Objects.requireNonNull(request, "The DelegateRequest must not be null."); + Objects.requireNonNull(executor, "The Executor must not be null."); + Objects.requireNonNull(dc, "The DelegateConnectionStateCallback must not be null."); + Objects.requireNonNull(mc, "The DelegateConnectionMessageCallback must not be null."); + try { + SipDelegateConnectionAidlWrapper wrapper = + new SipDelegateConnectionAidlWrapper(executor, dc, mc); + IImsRcsController controller = mBinderCache.listenOnBinder(wrapper, + wrapper::binderDied); + if (controller == null) { + throw new ImsException("Telephony server is down", + ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); + } + controller.createSipDelegate(mSubId, request, mContext.getOpPackageName(), + wrapper.getStateCallbackBinder(), wrapper.getMessageCallbackBinder()); + } catch (ServiceSpecificException e) { + throw new ImsException(e.getMessage(), e.errorCode); + } catch (RemoteException e) { + throw new ImsException(e.getMessage(), + ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); + } + } + + /** + * Destroy a previously created {@link SipDelegateConnection} that was created using + * {@link #createSipDelegate}. + * <p> + * This will also clean up all related callbacks in the associated ImsService. + * @param delegateConnection The SipDelegateConnection to destroy. + * @param reason The reason for why this SipDelegateConnection was destroyed. + */ + @RequiresPermission(Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION) + public void destroySipDelegate(@NonNull SipDelegateConnection delegateConnection, + @SipDelegateDestroyReason int reason) { + Objects.requireNonNull(delegateConnection, "SipDelegateConnection can not be null."); + if (delegateConnection instanceof SipDelegateConnectionAidlWrapper) { + SipDelegateConnectionAidlWrapper w = + (SipDelegateConnectionAidlWrapper) delegateConnection; + try { + IImsRcsController c = mBinderCache.removeRunnable(w); + c.destroySipDelegate(mSubId, w.getSipDelegateBinder(), reason); + } catch (RemoteException e) { + // Connection to telephony died, but this will signal destruction of SipDelegate + // eventually anyway, so return normally. + try { + w.getStateCallbackBinder().onDestroyed( + SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP); + } catch (RemoteException ignore) { + // Local to process. + } + } + } else { + throw new IllegalArgumentException("Unknown SipDelegateConnection implementation passed" + + " into this method"); + } + } + + /** + * Trigger a full network registration as required by receiving a SIP message containing a + * permanent error from the network or never receiving a response to a SIP transaction request. + * + * @param connection The {@link SipDelegateConnection} that was being used when this error was + * received. + * @param sipCode The SIP code response associated with the SIP message request that + * triggered this condition. + * @param sipReason The SIP reason code associated with the SIP message request that triggered + * this condition. May be {@code null} if there was no reason String provided from the + * network. + */ + @RequiresPermission(Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION) + public void triggerFullNetworkRegistration(@NonNull SipDelegateConnection connection, + @IntRange(from = 100, to = 699) int sipCode, @Nullable String sipReason) { + Objects.requireNonNull(connection, "SipDelegateConnection can not be null."); + if (connection instanceof SipDelegateConnectionAidlWrapper) { + SipDelegateConnectionAidlWrapper w = (SipDelegateConnectionAidlWrapper) connection; + try { + IImsRcsController controller = mBinderCache.getBinder(); + controller.triggerNetworkRegistration(mSubId, w.getSipDelegateBinder(), sipCode, + sipReason); + } catch (RemoteException e) { + // Connection to telephony died, but this will signal destruction of SipDelegate + // eventually anyway, so return. + } + } else { + throw new IllegalArgumentException("Unknown SipDelegateConnection implementation passed" + + " into this method"); + } + } +} diff --git a/telephony/java/android/telephony/ims/SipMessage.aidl b/telephony/java/android/telephony/ims/SipMessage.aidl new file mode 100644 index 000000000000..5140f8ac357f --- /dev/null +++ b/telephony/java/android/telephony/ims/SipMessage.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2020 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.ims; + +parcelable SipMessage; diff --git a/telephony/java/android/telephony/ims/SipMessage.java b/telephony/java/android/telephony/ims/SipMessage.java new file mode 100644 index 000000000000..391372ac8ac1 --- /dev/null +++ b/telephony/java/android/telephony/ims/SipMessage.java @@ -0,0 +1,226 @@ +/* + * Copyright (C) 2020 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.ims; + +import static java.nio.charset.StandardCharsets.UTF_8; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.os.Build; +import android.os.Parcel; +import android.os.Parcelable; +import android.text.TextUtils; + +import com.android.internal.telephony.SipMessageParsingUtils; + +import java.util.Arrays; +import java.util.Objects; + +/** + * Represents a partially encoded SIP message. See RFC 3261 for more information on how SIP + * messages are structured and used. + * <p> + * The SIP message is represented in a partially encoded form in order to allow for easier + * verification and should not be used as a generic SIP message container. + * @hide + */ +@SystemApi +public final class SipMessage implements Parcelable { + // Should not be set to true for production! + private static final boolean IS_DEBUGGING = Build.IS_ENG; + private static final String CRLF = "\r\n"; + + private final String mStartLine; + private final String mHeaderSection; + private final byte[] mContent; + private final String mViaBranchParam; + private final String mCallIdParam; + + /** + * Represents a partially encoded SIP message. + * + * @param startLine The start line of the message, containing either the request-line or + * status-line. + * @param headerSection A String containing the full unencoded SIP message header. + * @param content UTF-8 encoded SIP message body. + */ + public SipMessage(@NonNull String startLine, @NonNull String headerSection, + @NonNull byte[] content) { + Objects.requireNonNull(startLine, "Required parameter is null: startLine"); + Objects.requireNonNull(headerSection, "Required parameter is null: headerSection"); + Objects.requireNonNull(content, "Required parameter is null: content"); + + mStartLine = startLine; + mHeaderSection = headerSection; + mContent = content; + + mViaBranchParam = SipMessageParsingUtils.getTransactionId(mHeaderSection); + if (TextUtils.isEmpty(mViaBranchParam)) { + throw new IllegalArgumentException("header section MUST contain a branch parameter " + + "inside of the Via header."); + } + mCallIdParam = SipMessageParsingUtils.getCallId(mHeaderSection); + } + + /** + * Private constructor used only for unparcelling. + */ + private SipMessage(Parcel source) { + mStartLine = source.readString(); + mHeaderSection = source.readString(); + mContent = new byte[source.readInt()]; + source.readByteArray(mContent); + mViaBranchParam = source.readString(); + mCallIdParam = source.readString(); + } + + /** + * @return The start line of the SIP message, which contains either the request-line or + * status-line. + */ + public @NonNull String getStartLine() { + return mStartLine; + } + + /** + * @return The full, unencoded header section of the SIP message. + */ + public @NonNull String getHeaderSection() { + return mHeaderSection; + } + + /** + * @return only the UTF-8 encoded SIP message body. + */ + public @NonNull byte[] getContent() { + return mContent; + } + + /** + * @return the branch parameter enclosed in the Via header key's value. See RFC 3261 section + * 20.42 for more information on the Via header. + */ + public @NonNull String getViaBranchParameter() { + return mViaBranchParam; + } + + /** + * @return the value associated with the call-id header of this SIP message. See RFC 3261 + * section 20.8 for more information on the call-id header. If {@code null}, then there was no + * call-id header found in this SIP message's headers. + */ + public @Nullable String getCallIdParameter() { + return mCallIdParam; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeString(mStartLine); + dest.writeString(mHeaderSection); + dest.writeInt(mContent.length); + dest.writeByteArray(mContent); + dest.writeString(mViaBranchParam); + dest.writeString(mCallIdParam); + } + + public static final @NonNull Creator<SipMessage> CREATOR = new Creator<SipMessage>() { + @Override + public SipMessage createFromParcel(Parcel source) { + return new SipMessage(source); + } + + @Override + public SipMessage[] newArray(int size) { + return new SipMessage[size]; + } + }; + + @Override + public String toString() { + StringBuilder b = new StringBuilder(); + b.append("StartLine: ["); + if (IS_DEBUGGING) { + b.append(mStartLine); + } else { + b.append(sanitizeStartLineRequest(mStartLine)); + } + b.append("], Header: ["); + if (IS_DEBUGGING) { + b.append(mHeaderSection); + } else { + // only identify transaction id/call ID when it is available. + b.append("***"); + } + b.append("], Content: "); + b.append(getContent().length == 0 ? "[NONE]" : "[NOT SHOWN]"); + return b.toString(); + } + + /** + * Detect if this is a REQUEST and redact Request-URI portion here, as it contains PII. + */ + private String sanitizeStartLineRequest(String startLine) { + if (!SipMessageParsingUtils.isSipRequest(startLine)) return startLine; + String[] splitLine = startLine.split(" "); + return splitLine[0] + " <Request-URI> " + splitLine[2]; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + SipMessage that = (SipMessage) o; + return mStartLine.equals(that.mStartLine) + && mHeaderSection.equals(that.mHeaderSection) + && Arrays.equals(mContent, that.mContent); + } + + @Override + public int hashCode() { + int result = Objects.hash(mStartLine, mHeaderSection); + result = 31 * result + Arrays.hashCode(mContent); + return result; + } + + /** + * According RFC-3261 section 7, SIP is a text protocol and uses the UTF-8 charset. Its format + * consists of a start-line, one or more header fields, an empty line indicating the end of the + * header fields, and an optional message-body. + * + * <p> + * Returns a byte array with UTF-8 format representation of the encoded SipMessage. + * + * @return byte array with UTF-8 format representation of the encoded SipMessage. + */ + public @NonNull byte[] toEncodedMessage() { + byte[] header = new StringBuilder() + .append(mStartLine) + .append(mHeaderSection) + .append(CRLF) + .toString().getBytes(UTF_8); + byte[] sipMessage = new byte[header.length + mContent.length]; + System.arraycopy(header, 0, sipMessage, 0, header.length); + System.arraycopy(mContent, 0, sipMessage, header.length, mContent.length); + return sipMessage; + } +} diff --git a/telephony/java/android/telephony/ims/aidl/CapabilityExchangeAidlWrapper.java b/telephony/java/android/telephony/ims/aidl/CapabilityExchangeAidlWrapper.java new file mode 100644 index 000000000000..c3d7325f2e0a --- /dev/null +++ b/telephony/java/android/telephony/ims/aidl/CapabilityExchangeAidlWrapper.java @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2020 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.ims.aidl; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.net.Uri; +import android.os.Binder; +import android.os.RemoteException; +import android.telephony.ims.ImsException; +import android.telephony.ims.RcsContactUceCapability; +import android.telephony.ims.stub.CapabilityExchangeEventListener; +import android.util.Log; + +import java.util.ArrayList; +import java.util.Set; + +/** + * The ICapabilityExchangeEventListener wrapper class to store the listener which is registered by + * the framework. This wrapper class also delivers the request to the framework when receive the + * request from the network. + * @hide + */ +public class CapabilityExchangeAidlWrapper implements CapabilityExchangeEventListener { + + private static final String LOG_TAG = "CapExchangeListener"; + + private final ICapabilityExchangeEventListener mListenerBinder; + + public CapabilityExchangeAidlWrapper(@Nullable ICapabilityExchangeEventListener listener) { + mListenerBinder = listener; + } + + /** + * Receives the request of publishing capabilities from the network and deliver this request + * to the framework via the registered capability exchange event listener. + */ + public void onRequestPublishCapabilities(int publishTriggerType) throws ImsException { + ICapabilityExchangeEventListener listener = mListenerBinder; + if (listener == null) { + return; + } + try { + listener.onRequestPublishCapabilities(publishTriggerType); + } catch (RemoteException e) { + Log.w(LOG_TAG, "request publish capabilities exception: " + e); + throw new ImsException("Remote is not available", + ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); + } + } + + /** + * Receives the unpublish notification and deliver this callback to the framework. + */ + public void onUnpublish() throws ImsException { + ICapabilityExchangeEventListener listener = mListenerBinder; + if (listener == null) { + return; + } + try { + listener.onUnpublish(); + } catch (RemoteException e) { + Log.w(LOG_TAG, "Unpublish exception: " + e); + throw new ImsException("Remote is not available", + ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); + } + } + + /** + * Receives the callback of the remote capability request from the network and deliver this + * request to the framework. + */ + public void onRemoteCapabilityRequest(@NonNull Uri contactUri, + @NonNull Set<String> remoteCapabilities, @NonNull OptionsRequestCallback callback) + throws ImsException { + ICapabilityExchangeEventListener listener = mListenerBinder; + if (listener == null) { + return; + } + + IOptionsRequestCallback internalCallback = new IOptionsRequestCallback.Stub() { + @Override + public void respondToCapabilityRequest(RcsContactUceCapability ownCapabilities, + boolean isBlocked) { + final long callingIdentity = Binder.clearCallingIdentity(); + try { + callback.onRespondToCapabilityRequest(ownCapabilities, isBlocked); + } finally { + restoreCallingIdentity(callingIdentity); + } + } + @Override + public void respondToCapabilityRequestWithError(int code, String reason) { + final long callingIdentity = Binder.clearCallingIdentity(); + try { + callback.onRespondToCapabilityRequestWithError(code, reason); + } finally { + restoreCallingIdentity(callingIdentity); + } + } + }; + + try { + listener.onRemoteCapabilityRequest(contactUri, new ArrayList<>(remoteCapabilities), + internalCallback); + } catch (RemoteException e) { + Log.w(LOG_TAG, "Remote capability request exception: " + e); + throw new ImsException("Remote is not available", + ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); + } + } +} diff --git a/telephony/java/android/telephony/ims/aidl/ICapabilityExchangeEventListener.aidl b/telephony/java/android/telephony/ims/aidl/ICapabilityExchangeEventListener.aidl new file mode 100644 index 000000000000..078ac919b75e --- /dev/null +++ b/telephony/java/android/telephony/ims/aidl/ICapabilityExchangeEventListener.aidl @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2020 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.ims.aidl; + +import android.net.Uri; +import android.telephony.ims.aidl.IOptionsRequestCallback; + +import java.util.List; + +/** + * Listener interface for the ImsService to use to notify the framework of UCE + * events. + * + * See CapabilityExchangeEventListener for more information. + * {@hide} + */ +oneway interface ICapabilityExchangeEventListener { + void onRequestPublishCapabilities(int publishTriggerType); + void onUnpublish(); + void onRemoteCapabilityRequest(in Uri contactUri, + in List<String> remoteCapabilities, IOptionsRequestCallback cb); +} diff --git a/telephony/java/android/telephony/ims/aidl/IImsCallSessionListener.aidl b/telephony/java/android/telephony/ims/aidl/IImsCallSessionListener.aidl index 36d2067ad016..ed0375251ffb 100644 --- a/telephony/java/android/telephony/ims/aidl/IImsCallSessionListener.aidl +++ b/telephony/java/android/telephony/ims/aidl/IImsCallSessionListener.aidl @@ -21,9 +21,12 @@ import android.telephony.ims.ImsStreamMediaProfile; import android.telephony.ims.ImsCallProfile; import android.telephony.ims.ImsReasonInfo; import android.telephony.ims.ImsConferenceState; +import android.telephony.ims.RtpHeaderExtension; import com.android.ims.internal.IImsCallSession; import android.telephony.ims.ImsSuppServiceNotification; +import java.util.List; + /** * A listener type for receiving notification on IMS call session events. * When an event is generated for an {@link IImsCallSession}, the application is notified @@ -34,6 +37,8 @@ oneway interface IImsCallSessionListener { /** * Notifies the result of the basic session operation (setup / terminate). */ + void callSessionInitiating(in ImsCallProfile profile); + void callSessionInitiatingFailed(in ImsReasonInfo reasonInfo); void callSessionProgressing(in ImsStreamMediaProfile profile); void callSessionInitiated(in ImsCallProfile profile); void callSessionInitiatedFailed(in ImsReasonInfo reasonInfo); @@ -153,9 +158,17 @@ oneway interface IImsCallSessionListener { void callSessionTransferred(); void callSessionTransferFailed(in ImsReasonInfo reasonInfo); + void callSessionDtmfReceived(char dtmf); + /** * Notifies of a change to the call quality. * @param callQuality then updated call quality */ void callQualityChanged(in CallQuality callQuality); + + /** + * Notifies of incoming RTP header extensions from the network. + * @param extensions the RTP header extensions received. + */ + void callSessionRtpHeaderExtensionsReceived(in List<RtpHeaderExtension> extensions); } diff --git a/telephony/java/android/telephony/ims/aidl/IImsConfig.aidl b/telephony/java/android/telephony/ims/aidl/IImsConfig.aidl index 57206c9f059a..1b5e5603ec66 100644 --- a/telephony/java/android/telephony/ims/aidl/IImsConfig.aidl +++ b/telephony/java/android/telephony/ims/aidl/IImsConfig.aidl @@ -18,8 +18,9 @@ package android.telephony.ims.aidl; import android.os.PersistableBundle; - import android.telephony.ims.aidl.IImsConfigCallback; +import android.telephony.ims.aidl.IRcsConfigCallback; +import android.telephony.ims.RcsClientConfiguration; import com.android.ims.ImsConfigListener; @@ -41,4 +42,11 @@ interface IImsConfig { int setConfigString(int item, String value); void updateImsCarrierConfigs(in PersistableBundle bundle); void notifyRcsAutoConfigurationReceived(in byte[] config, boolean isCompressed); + void notifyRcsAutoConfigurationRemoved(); + void addRcsConfigCallback(IRcsConfigCallback c); + void removeRcsConfigCallback(IRcsConfigCallback c); + void triggerRcsReconfiguration(); + void setRcsClientConfiguration(in RcsClientConfiguration rcc); + void notifyIntImsConfigChanged(int item, int value); + void notifyStringImsConfigChanged(int item, String value); } diff --git a/telephony/java/android/telephony/ims/aidl/IImsMmTelFeature.aidl b/telephony/java/android/telephony/ims/aidl/IImsMmTelFeature.aidl index b9a6b3c38a92..37fec7a73634 100644 --- a/telephony/java/android/telephony/ims/aidl/IImsMmTelFeature.aidl +++ b/telephony/java/android/telephony/ims/aidl/IImsMmTelFeature.aidl @@ -21,6 +21,7 @@ import android.telephony.ims.aidl.IImsMmTelListener; import android.telephony.ims.aidl.IImsSmsListener; import android.telephony.ims.aidl.IImsCapabilityCallback; import android.telephony.ims.feature.CapabilityChangeRequest; +import android.telephony.ims.RtpHeaderExtensionType; import android.telephony.ims.ImsCallProfile; import com.android.ims.internal.IImsCallSession; @@ -29,6 +30,8 @@ import com.android.ims.internal.IImsMultiEndpoint; import com.android.ims.internal.IImsRegistrationListener; import com.android.ims.internal.IImsUt; +import java.util.List; + /** * See MmTelFeature for more information. * {@hide} @@ -37,6 +40,7 @@ interface IImsMmTelFeature { void setListener(IImsMmTelListener l); int getFeatureState(); ImsCallProfile createCallProfile(int callSessionType, int callType); + void changeOfferedRtpHeaderExtensionTypes(in List<RtpHeaderExtensionType> types); IImsCallSession createCallSession(in ImsCallProfile profile); int shouldProcessCall(in String[] uris); IImsUt getUtInterface(); diff --git a/telephony/java/android/telephony/ims/aidl/IImsMmTelListener.aidl b/telephony/java/android/telephony/ims/aidl/IImsMmTelListener.aidl index 7bbe30a444b9..52464703c608 100644 --- a/telephony/java/android/telephony/ims/aidl/IImsMmTelListener.aidl +++ b/telephony/java/android/telephony/ims/aidl/IImsMmTelListener.aidl @@ -27,8 +27,11 @@ import com.android.ims.internal.IImsCallSession; * See MmTelFeature#Listener for more information. * {@hide} */ -oneway interface IImsMmTelListener { + // This interface is not considered oneway because we need to ensure that these operations are + // processed by telephony before the control flow returns to the ImsService to perform + // operations on the IImsCallSession. +interface IImsMmTelListener { void onIncomingCall(IImsCallSession c, in Bundle extras); void onRejectedCall(in ImsCallProfile callProfile, in ImsReasonInfo reason); - void onVoiceMessageCountUpdate(int count); + oneway void onVoiceMessageCountUpdate(int count); } diff --git a/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl b/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl index 483c66eedc0c..8931a78709ed 100644 --- a/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl +++ b/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl @@ -17,10 +17,18 @@ package android.telephony.ims.aidl; import android.net.Uri; +import android.telephony.ims.DelegateRequest; import android.telephony.ims.aidl.IImsCapabilityCallback; -import android.telephony.ims.aidl.IRcsUceControllerCallback; import android.telephony.ims.aidl.IImsRegistrationCallback; +import android.telephony.ims.aidl.IRcsUceControllerCallback; +import android.telephony.ims.aidl.IRcsUcePublishStateCallback; +import android.telephony.ims.aidl.IRcsUcePublishStateCallback; +import android.telephony.ims.aidl.ISipDelegate; +import android.telephony.ims.aidl.ISipDelegateMessageCallback; +import android.telephony.ims.aidl.ISipDelegateConnectionStateCallback; +import com.android.ims.ImsFeatureContainer; +import com.android.ims.internal.IImsServiceFeatureCallback; import com.android.internal.telephony.IIntegerConsumer; /** @@ -39,12 +47,30 @@ interface IImsRcsController { void registerRcsAvailabilityCallback(int subId, IImsCapabilityCallback c); void unregisterRcsAvailabilityCallback(int subId, IImsCapabilityCallback c); boolean isCapable(int subId, int capability, int radioTech); - boolean isAvailable(int subId, int capability); + boolean isAvailable(int subId, int capability, int radioTech); // ImsUceAdapter specific void requestCapabilities(int subId, String callingPackage, String callingFeatureId, in List<Uri> contactNumbers, IRcsUceControllerCallback c); + void requestAvailability(int subId, String callingPackage, + String callingFeatureId, in Uri contactNumber, + IRcsUceControllerCallback c); int getUcePublishState(int subId); boolean isUceSettingEnabled(int subId, String callingPackage, String callingFeatureId); void setUceSettingEnabled(int subId, boolean isEnabled); + void registerUcePublishStateCallback(int subId, IRcsUcePublishStateCallback c); + void unregisterUcePublishStateCallback(int subId, IRcsUcePublishStateCallback c); + + // SipDelegateManager + boolean isSipDelegateSupported(int subId); + void createSipDelegate(int subId, in DelegateRequest request, String packageName, + ISipDelegateConnectionStateCallback delegateState, + ISipDelegateMessageCallback delegateMessage); + void destroySipDelegate(int subId, ISipDelegate connection, int reason); + void triggerNetworkRegistration(int subId, ISipDelegate connection, int sipCode, + String sipReason); + + // Internal commands that should not be made public + void registerRcsFeatureCallback(int slotId, in IImsServiceFeatureCallback callback); + void unregisterImsFeatureCallback(in IImsServiceFeatureCallback callback); } diff --git a/telephony/java/android/telephony/ims/aidl/IImsRcsFeature.aidl b/telephony/java/android/telephony/ims/aidl/IImsRcsFeature.aidl index 4b98b79f1095..10c50bed34d7 100644 --- a/telephony/java/android/telephony/ims/aidl/IImsRcsFeature.aidl +++ b/telephony/java/android/telephony/ims/aidl/IImsRcsFeature.aidl @@ -18,8 +18,11 @@ package android.telephony.ims.aidl; import android.net.Uri; import android.telephony.ims.RcsContactUceCapability; +import android.telephony.ims.aidl.ICapabilityExchangeEventListener; import android.telephony.ims.aidl.IImsCapabilityCallback; -import android.telephony.ims.aidl.IRcsFeatureListener; +import android.telephony.ims.aidl.IOptionsResponseCallback; +import android.telephony.ims.aidl.IPublishResponseCallback; +import android.telephony.ims.aidl.ISubscribeResponseCallback; import android.telephony.ims.feature.CapabilityChangeRequest; import java.util.List; @@ -30,7 +33,6 @@ import java.util.List; */ interface IImsRcsFeature { // Not oneway because we need to verify this completes before doing anything else. - void setListener(IRcsFeatureListener listener); int queryCapabilityStatus(); // Inherited from ImsFeature int getFeatureState(); @@ -40,14 +42,10 @@ interface IImsRcsFeature { IImsCapabilityCallback c); oneway void queryCapabilityConfiguration(int capability, int radioTech, IImsCapabilityCallback c); - // RcsPresenceExchangeImplBase specific api - oneway void requestCapabilities(in List<Uri> uris, int operationToken); - oneway void updateCapabilities(in RcsContactUceCapability capabilities, int operationToken); - // RcsSipOptionsImplBase specific api - oneway void sendCapabilityRequest(in Uri contactUri, - in RcsContactUceCapability capabilities, int operationToken); - oneway void respondToCapabilityRequest(in String contactUri, - in RcsContactUceCapability ownCapabilities, int operationToken); - oneway void respondToCapabilityRequestWithError(in Uri contactUri, int code, in String reason, - int operationToken); -}
\ No newline at end of file + // RcsCapabilityExchangeImplBase specific api + oneway void setCapabilityExchangeEventListener(ICapabilityExchangeEventListener listener); + oneway void publishCapabilities(in String pidfXml, IPublishResponseCallback cb); + oneway void subscribeForCapabilities(in List<Uri> uris, ISubscribeResponseCallback cb); + oneway void sendOptionsCapabilityRequest(in Uri contactUri, + in List<String> myCapabilities, IOptionsResponseCallback cb); +} diff --git a/telephony/java/android/telephony/ims/aidl/IImsRegistration.aidl b/telephony/java/android/telephony/ims/aidl/IImsRegistration.aidl index 4ae0a75ad027..4fd904041365 100644 --- a/telephony/java/android/telephony/ims/aidl/IImsRegistration.aidl +++ b/telephony/java/android/telephony/ims/aidl/IImsRegistration.aidl @@ -28,4 +28,7 @@ interface IImsRegistration { int getRegistrationTechnology(); oneway void addRegistrationCallback(IImsRegistrationCallback c); oneway void removeRegistrationCallback(IImsRegistrationCallback c); -}
\ No newline at end of file + oneway void triggerFullNetworkRegistration(int sipCode, String sipReason); + oneway void triggerUpdateSipDelegateRegistration(); + oneway void triggerSipDelegateDeregistration(); +} diff --git a/telephony/java/android/telephony/ims/aidl/IImsRegistrationCallback.aidl b/telephony/java/android/telephony/ims/aidl/IImsRegistrationCallback.aidl index 749b1916962e..179407c983e5 100644 --- a/telephony/java/android/telephony/ims/aidl/IImsRegistrationCallback.aidl +++ b/telephony/java/android/telephony/ims/aidl/IImsRegistrationCallback.aidl @@ -21,6 +21,7 @@ import android.net.Uri; import android.telephony.ims.stub.ImsFeatureConfiguration; import android.telephony.ims.ImsReasonInfo; +import android.telephony.ims.ImsRegistrationAttributes; /** * See {@link ImsManager#RegistrationCallback} for more information. @@ -28,8 +29,8 @@ import android.telephony.ims.ImsReasonInfo; * {@hide} */ oneway interface IImsRegistrationCallback { - void onRegistered(int imsRadioTech); - void onRegistering(int imsRadioTech); + void onRegistered(in ImsRegistrationAttributes attr); + void onRegistering(in ImsRegistrationAttributes attr); void onDeregistered(in ImsReasonInfo info); void onTechnologyChangeFailed(int imsRadioTech, in ImsReasonInfo info); void onSubscriberAssociatedUriChanged(in Uri[] uris); diff --git a/telephony/java/android/telephony/ims/aidl/IImsServiceController.aidl b/telephony/java/android/telephony/ims/aidl/IImsServiceController.aidl index c7da681b86a3..c6966b3cf53e 100644 --- a/telephony/java/android/telephony/ims/aidl/IImsServiceController.aidl +++ b/telephony/java/android/telephony/ims/aidl/IImsServiceController.aidl @@ -21,6 +21,7 @@ import android.telephony.ims.aidl.IImsRcsFeature; import android.telephony.ims.aidl.IImsConfig; import android.telephony.ims.aidl.IImsRegistration; import android.telephony.ims.aidl.IImsServiceControllerListener; +import android.telephony.ims.aidl.ISipTransport; import android.telephony.ims.stub.ImsFeatureConfiguration; import com.android.ims.internal.IImsFeatureStatusCallback; @@ -31,14 +32,18 @@ import com.android.ims.internal.IImsFeatureStatusCallback; */ interface IImsServiceController { void setListener(IImsServiceControllerListener l); - IImsMmTelFeature createMmTelFeature(int slotId, in IImsFeatureStatusCallback c); - IImsRcsFeature createRcsFeature(int slotId, in IImsFeatureStatusCallback c); + IImsMmTelFeature createMmTelFeature(int slotId); + IImsRcsFeature createRcsFeature(int slotId); ImsFeatureConfiguration querySupportedImsFeatures(); + long getImsServiceCapabilities(); + void addFeatureStatusCallback(int slotId, int featureType, in IImsFeatureStatusCallback c); + void removeFeatureStatusCallback(int slotId, int featureType, in IImsFeatureStatusCallback c); // Synchronous call to ensure the ImsService is ready before continuing with feature creation. void notifyImsServiceReadyForFeatureCreation(); - void removeImsFeature(int slotId, int featureType, in IImsFeatureStatusCallback c); + void removeImsFeature(int slotId, int featureType); IImsConfig getConfig(int slotId); IImsRegistration getRegistration(int slotId); + ISipTransport getSipTransport(int slotId); oneway void enableIms(int slotId); oneway void disableIms(int slotId); } diff --git a/telephony/java/android/telephony/ims/aidl/IOptionsRequestCallback.aidl b/telephony/java/android/telephony/ims/aidl/IOptionsRequestCallback.aidl new file mode 100644 index 000000000000..8eecbca7e6a7 --- /dev/null +++ b/telephony/java/android/telephony/ims/aidl/IOptionsRequestCallback.aidl @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2020 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.ims.aidl; + +import android.telephony.ims.RcsContactUceCapability; + +/** + * Interface used by the framework to respond to OPTIONS requests. + * {@hide} + */ +oneway interface IOptionsRequestCallback { + /** + * Respond to a remote capability request from the contact specified with the capabilities + * of this device. + * @param ownCapabilities The capabilities of this device. + * @param isBlocked True if the user has blocked the number sending this request. + */ + void respondToCapabilityRequest(in RcsContactUceCapability ownCapabilities, boolean isBlocked); + + /** + * Respond to a remote capability request from the contact specified with the + * specified error. + * @param code The SIP response code to respond with. + * @param reason A non-null String containing the reason associated with the SIP code. + */ + void respondToCapabilityRequestWithError(int code, String reason); +} diff --git a/telephony/java/android/telephony/ims/aidl/IOptionsResponseCallback.aidl b/telephony/java/android/telephony/ims/aidl/IOptionsResponseCallback.aidl new file mode 100644 index 000000000000..a8c8329fe55e --- /dev/null +++ b/telephony/java/android/telephony/ims/aidl/IOptionsResponseCallback.aidl @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2020 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.ims.aidl; + +import java.util.List; + +/** + * Interface used by the framework to receive the response from the remote user + * through {@link RcsCapabilityExchangeImplBase#sendOptionsCapabilityRequest} + * {@hide} + */ +oneway interface IOptionsResponseCallback { + void onCommandError(int code); + void onNetworkResponse(int code, String reason, in List<String> theirCaps); +} diff --git a/telephony/java/android/telephony/ims/aidl/IPublishResponseCallback.aidl b/telephony/java/android/telephony/ims/aidl/IPublishResponseCallback.aidl new file mode 100644 index 000000000000..b99d8a7d6d38 --- /dev/null +++ b/telephony/java/android/telephony/ims/aidl/IPublishResponseCallback.aidl @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2020 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.ims.aidl; + +import java.util.List; + +/** + * Interface used by the framework to receive the response of the publish + * request through {@link RcsCapabilityExchangeImplBase#publishCapabilities} + * {@hide} + */ +oneway interface IPublishResponseCallback { + void onCommandError(int code); + void onNetworkResponse(int code, String reason); + void onNetworkRespHeader(int code, String reasonPhrase, int reasonHeaderCause, String reasonHeaderText); +} diff --git a/telephony/java/android/telephony/ims/aidl/IRcsConfigCallback.aidl b/telephony/java/android/telephony/ims/aidl/IRcsConfigCallback.aidl new file mode 100644 index 000000000000..d0853d1846ac --- /dev/null +++ b/telephony/java/android/telephony/ims/aidl/IRcsConfigCallback.aidl @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2020 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.ims.aidl; + +/** + * The callback for RCS provisioning changes. + * {@hide} + */ +oneway interface IRcsConfigCallback { + void onConfigurationChanged(in byte[] config); + void onAutoConfigurationErrorReceived(int errorCode, String errorString); + void onConfigurationReset(); + void onRemoved(); + void onPreProvisioningReceived(in byte[] config); +} + diff --git a/telephony/java/android/telephony/ims/aidl/IRcsFeatureListener.aidl b/telephony/java/android/telephony/ims/aidl/IRcsFeatureListener.aidl deleted file mode 100644 index 70cf651d3924..000000000000 --- a/telephony/java/android/telephony/ims/aidl/IRcsFeatureListener.aidl +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2018 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.ims.aidl; - -import android.net.Uri; -import android.telephony.ims.RcsContactUceCapability; - -import java.util.List; - -/** - * Listener interface for updates from the RcsFeature back to the framework. - * {@hide} - */ -interface IRcsFeatureListener { - //RcsCapabilityExchange specific - oneway void onCommandUpdate(int commandCode, int operationToken); - // RcsPresenceExchangeImplBase Specific - oneway void onNetworkResponse(int code, in String reason, int operationToken); - oneway void onCapabilityRequestResponsePresence(in List<RcsContactUceCapability> infos, - int operationToken); - oneway void onNotifyUpdateCapabilities(int publishTriggerType); - oneway void onUnpublish(); - // RcsSipOptionsImplBase specific - oneway void onCapabilityRequestResponseOptions(int code, in String reason, - in RcsContactUceCapability info, int operationToken); - oneway void onRemoteCapabilityRequest(in Uri contactUri, in RcsContactUceCapability remoteInfo, - int operationToken); -} diff --git a/telephony/java/android/telephony/ims/aidl/IRcsUceControllerCallback.aidl b/telephony/java/android/telephony/ims/aidl/IRcsUceControllerCallback.aidl index 5975930d5cfa..0f627b92a24c 100644 --- a/telephony/java/android/telephony/ims/aidl/IRcsUceControllerCallback.aidl +++ b/telephony/java/android/telephony/ims/aidl/IRcsUceControllerCallback.aidl @@ -25,5 +25,6 @@ import android.telephony.ims.RcsContactUceCapability; */ oneway interface IRcsUceControllerCallback { void onCapabilitiesReceived(in List<RcsContactUceCapability> contactCapabilities); - void onError(int errorCode); + void onComplete(); + void onError(int errorCode, long retryAfterMilliseconds); } diff --git a/telephony/java/android/telephony/ims/aidl/IRcsUcePublishStateCallback.aidl b/telephony/java/android/telephony/ims/aidl/IRcsUcePublishStateCallback.aidl new file mode 100644 index 000000000000..b6e84154f80f --- /dev/null +++ b/telephony/java/android/telephony/ims/aidl/IRcsUcePublishStateCallback.aidl @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2020 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.ims.aidl; + +/** + * Interface for RCS UCE publish state change callbacks. + * + * {@hide} + */ +oneway interface IRcsUcePublishStateCallback { + void onPublishStateChanged(int publishState); +} diff --git a/telephony/java/android/telephony/ims/aidl/ISipDelegate.aidl b/telephony/java/android/telephony/ims/aidl/ISipDelegate.aidl new file mode 100644 index 000000000000..ff1a8f03350e --- /dev/null +++ b/telephony/java/android/telephony/ims/aidl/ISipDelegate.aidl @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2020 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.ims.aidl; + +import android.telephony.ims.SipMessage; + +/** + * See {@link SipDelegate} and {@link SipDelegateConnection} for docs regarding this callback. + * {@hide} + */ +oneway interface ISipDelegate { + void sendMessage(in SipMessage sipMessage, long configVersion); + void notifyMessageReceived(in String viaTransactionId); + void notifyMessageReceiveError(in String viaTransactionId, int reason); + void cleanupSession(in String callId); +} diff --git a/telephony/java/android/telephony/ims/aidl/ISipDelegateConnectionStateCallback.aidl b/telephony/java/android/telephony/ims/aidl/ISipDelegateConnectionStateCallback.aidl new file mode 100644 index 000000000000..855000bb57fb --- /dev/null +++ b/telephony/java/android/telephony/ims/aidl/ISipDelegateConnectionStateCallback.aidl @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2020 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.ims.aidl; + +import android.telephony.ims.DelegateRegistrationState; +import android.telephony.ims.FeatureTagState; +import android.telephony.ims.SipDelegateConfiguration; +import android.telephony.ims.SipDelegateImsConfiguration; +import android.telephony.ims.aidl.ISipDelegate; + +/** + * See {@link SipDelegateConnectionStateCallback} for docs regarding this callback. + * {@hide} + */ +oneway interface ISipDelegateConnectionStateCallback { + void onCreated(ISipDelegate c); + void onFeatureTagStatusChanged(in DelegateRegistrationState registrationState, + in List<FeatureTagState> deniedFeatureTags); + void onImsConfigurationChanged(in SipDelegateImsConfiguration registeredSipConfig); + void onConfigurationChanged(in SipDelegateConfiguration registeredSipConfig); + void onDestroyed(int reason); +} diff --git a/telephony/java/android/telephony/ims/aidl/ISipDelegateMessageCallback.aidl b/telephony/java/android/telephony/ims/aidl/ISipDelegateMessageCallback.aidl new file mode 100644 index 000000000000..30b7d6c70647 --- /dev/null +++ b/telephony/java/android/telephony/ims/aidl/ISipDelegateMessageCallback.aidl @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2020 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.ims.aidl; + +import android.telephony.ims.SipMessage; + +/** + * See {@link DelegateMessageCallback} and {@link DelegateConnectionMessageCallback} for docs + * regarding this callback. + * {@hide} + */ +oneway interface ISipDelegateMessageCallback { + void onMessageReceived(in SipMessage message); + void onMessageSent(in String viaTransactionId); + void onMessageSendFailure(in String viaTransactionId, int reason); +} diff --git a/telephony/java/android/telephony/ims/aidl/ISipDelegateStateCallback.aidl b/telephony/java/android/telephony/ims/aidl/ISipDelegateStateCallback.aidl new file mode 100644 index 000000000000..4c3c93d36188 --- /dev/null +++ b/telephony/java/android/telephony/ims/aidl/ISipDelegateStateCallback.aidl @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2020 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.ims.aidl; + +import android.telephony.ims.DelegateRegistrationState; +import android.telephony.ims.FeatureTagState; +import android.telephony.ims.SipDelegateConfiguration; +import android.telephony.ims.SipDelegateImsConfiguration; +import android.telephony.ims.aidl.ISipDelegate; + +/** + * See {@link SipDelegateStateCallback} for docs regarding this callback. + * {@hide} + */ +oneway interface ISipDelegateStateCallback { + void onCreated(ISipDelegate c, in List<FeatureTagState> deniedFeatureTags); + void onFeatureTagRegistrationChanged(in DelegateRegistrationState registrationState); + void onImsConfigurationChanged(in SipDelegateImsConfiguration registeredSipConfig); + void onConfigurationChanged(in SipDelegateConfiguration registeredSipConfig); + void onDestroyed(int reason); +} diff --git a/telephony/java/android/telephony/ims/aidl/ISipTransport.aidl b/telephony/java/android/telephony/ims/aidl/ISipTransport.aidl new file mode 100644 index 000000000000..3438587b7348 --- /dev/null +++ b/telephony/java/android/telephony/ims/aidl/ISipTransport.aidl @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2020 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.ims.aidl; + +import android.telephony.ims.DelegateRequest; +import android.telephony.ims.aidl.ISipDelegate; +import android.telephony.ims.aidl.ISipDelegateMessageCallback; +import android.telephony.ims.aidl.ISipDelegateStateCallback; + +/** + * Interface for commands to the SIP Transport implementation. + * {@hide} + */ +oneway interface ISipTransport { + void createSipDelegate(int subId, in DelegateRequest request, ISipDelegateStateCallback dc, + ISipDelegateMessageCallback mc); + void destroySipDelegate(ISipDelegate delegate, int reason); +} diff --git a/telephony/java/android/telephony/ims/aidl/ISubscribeResponseCallback.aidl b/telephony/java/android/telephony/ims/aidl/ISubscribeResponseCallback.aidl new file mode 100644 index 000000000000..8cc8020df29a --- /dev/null +++ b/telephony/java/android/telephony/ims/aidl/ISubscribeResponseCallback.aidl @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2020 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.ims.aidl; + +import android.net.Uri; +import android.telephony.ims.RcsContactTerminatedReason; + +import java.util.List; +import java.util.Map; + +/** + * Interface used by the framework to receive the response of the subscribe + * request through {@link RcsCapabilityExchangeImplBase#subscribeForCapabilities} + * {@hide} + */ +oneway interface ISubscribeResponseCallback { + void onCommandError(int code); + void onNetworkResponse(int code, in String reason); + void onNetworkRespHeader(int code, String reasonPhrase, int reasonHeaderCause, String reasonHeaderText); + void onNotifyCapabilitiesUpdate(in List<String> pidfXmls); + void onResourceTerminated(in List<RcsContactTerminatedReason> uriTerminatedReason); + void onTerminated(in String reason, long retryAfterMilliseconds); +} diff --git a/telephony/java/android/telephony/ims/aidl/OWNERS b/telephony/java/android/telephony/ims/aidl/OWNERS new file mode 100644 index 000000000000..0854c5d45603 --- /dev/null +++ b/telephony/java/android/telephony/ims/aidl/OWNERS @@ -0,0 +1,5 @@ +# Bug component: 20868 + +rgreenwalt@google.com +tgunn@google.com +breadley@google.com diff --git a/telephony/java/android/telephony/ims/aidl/RcsOptionsResponseAidlWrapper.java b/telephony/java/android/telephony/ims/aidl/RcsOptionsResponseAidlWrapper.java new file mode 100644 index 000000000000..47a96af1cba1 --- /dev/null +++ b/telephony/java/android/telephony/ims/aidl/RcsOptionsResponseAidlWrapper.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2020 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.ims.aidl; + +import android.os.RemoteException; +import android.telephony.ims.ImsException; +import android.telephony.ims.stub.RcsCapabilityExchangeImplBase.OptionsResponseCallback; + +import java.util.List; + +/** + * Implementation of the callback OptionsResponseCallback by wrapping the internal AIDL from + * telephony. + * @hide + */ +public class RcsOptionsResponseAidlWrapper implements OptionsResponseCallback { + + private final IOptionsResponseCallback mResponseBinder; + + public RcsOptionsResponseAidlWrapper(IOptionsResponseCallback responseBinder) { + mResponseBinder = responseBinder; + } + + @Override + public void onCommandError(int code) { + try { + mResponseBinder.onCommandError(code); + } catch (RemoteException e) { + } + } + + @Override + public void onNetworkResponse(int code, String reason, List<String> theirCaps) + throws ImsException { + try { + mResponseBinder.onNetworkResponse(code, reason, theirCaps); + } catch (RemoteException e) { + } + } +} diff --git a/telephony/java/android/telephony/ims/aidl/RcsPublishResponseAidlWrapper.java b/telephony/java/android/telephony/ims/aidl/RcsPublishResponseAidlWrapper.java new file mode 100644 index 000000000000..65415ea441b5 --- /dev/null +++ b/telephony/java/android/telephony/ims/aidl/RcsPublishResponseAidlWrapper.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2020 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.ims.aidl; + +import android.os.RemoteException; +import android.telephony.ims.ImsException; +import android.telephony.ims.stub.RcsCapabilityExchangeImplBase.PublishResponseCallback; + +/** + * Implementation of the callback PublishResponseCallback by wrapping the internal AIDL from + * telephony. + * @hide + */ +public class RcsPublishResponseAidlWrapper implements PublishResponseCallback { + + private final IPublishResponseCallback mResponseBinder; + + public RcsPublishResponseAidlWrapper(IPublishResponseCallback responseBinder) { + mResponseBinder = responseBinder; + } + + @Override + public void onCommandError(int code) throws ImsException { + try { + mResponseBinder.onCommandError(code); + } catch (RemoteException e) { + throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); + } + } + + @Override + public void onNetworkResponse(int code, String reason) throws ImsException { + try { + mResponseBinder.onNetworkResponse(code, reason); + } catch (RemoteException e) { + throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); + } + } + + @Override + public void onNetworkResponse(int code, String reasonPhrase, int reasonHeaderCause, + String reasonHeaderText) throws ImsException { + try { + mResponseBinder.onNetworkRespHeader(code, reasonPhrase, reasonHeaderCause, + reasonHeaderText); + } catch (RemoteException e) { + throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); + } + } +} diff --git a/telephony/java/android/telephony/ims/aidl/RcsSubscribeResponseAidlWrapper.java b/telephony/java/android/telephony/ims/aidl/RcsSubscribeResponseAidlWrapper.java new file mode 100644 index 000000000000..11118c0617c2 --- /dev/null +++ b/telephony/java/android/telephony/ims/aidl/RcsSubscribeResponseAidlWrapper.java @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2020 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.ims.aidl; + +import android.net.Uri; +import android.os.RemoteException; +import android.telephony.ims.ImsException; +import android.telephony.ims.RcsContactTerminatedReason; +import android.telephony.ims.stub.RcsCapabilityExchangeImplBase.SubscribeResponseCallback; +import android.util.Pair; + +import java.util.ArrayList; +import java.util.List; + +/** + * Implementation of the callback OptionsResponseCallback by wrapping the internal AIDL from + * telephony. + * @hide + */ +public class RcsSubscribeResponseAidlWrapper implements SubscribeResponseCallback { + + private final ISubscribeResponseCallback mResponseBinder; + + public RcsSubscribeResponseAidlWrapper(ISubscribeResponseCallback responseBinder) { + mResponseBinder = responseBinder; + } + + @Override + public void onCommandError(int code) throws ImsException { + try { + mResponseBinder.onCommandError(code); + } catch (RemoteException e) { + throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); + } + } + + @Override + public void onNetworkResponse(int code, String reason) throws ImsException { + try { + mResponseBinder.onNetworkResponse(code, reason); + } catch (RemoteException e) { + throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); + } + } + + @Override + public void onNetworkResponse(int code, String reasonPhrase, int reasonHeaderCause, + String reasonHeaderText) throws ImsException { + try { + mResponseBinder.onNetworkRespHeader(code, reasonPhrase, reasonHeaderCause, + reasonHeaderText); + } catch (RemoteException e) { + throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); + } + } + + @Override + public void onNotifyCapabilitiesUpdate(List<String> pidfXmls) throws ImsException { + try { + mResponseBinder.onNotifyCapabilitiesUpdate(pidfXmls); + } catch (RemoteException e) { + throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); + } + } + + @Override + public void onResourceTerminated(List<Pair<Uri, String>> uriTerminatedReason) + throws ImsException { + try { + mResponseBinder.onResourceTerminated(getTerminatedReasonList(uriTerminatedReason)); + } catch (RemoteException e) { + throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); + } + } + + private List<RcsContactTerminatedReason> getTerminatedReasonList( + List<Pair<Uri, String>> uriTerminatedReason) { + List<RcsContactTerminatedReason> uriTerminatedReasonList = new ArrayList<>(); + if (uriTerminatedReason != null) { + for (Pair<Uri, String> pair : uriTerminatedReason) { + RcsContactTerminatedReason reason = + new RcsContactTerminatedReason(pair.first, pair.second); + uriTerminatedReasonList.add(reason); + } + } + return uriTerminatedReasonList; + } + + @Override + public void onTerminated(String reason, long retryAfterMilliseconds) throws ImsException { + try { + mResponseBinder.onTerminated(reason, retryAfterMilliseconds); + } catch (RemoteException e) { + throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); + } + } +} diff --git a/telephony/java/android/telephony/ims/aidl/SipDelegateAidlWrapper.java b/telephony/java/android/telephony/ims/aidl/SipDelegateAidlWrapper.java new file mode 100644 index 000000000000..c18ab33eb2c9 --- /dev/null +++ b/telephony/java/android/telephony/ims/aidl/SipDelegateAidlWrapper.java @@ -0,0 +1,203 @@ +/* + * Copyright (C) 2020 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.ims.aidl; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Binder; +import android.os.RemoteException; +import android.telephony.ims.DelegateMessageCallback; +import android.telephony.ims.DelegateRegistrationState; +import android.telephony.ims.DelegateStateCallback; +import android.telephony.ims.FeatureTagState; +import android.telephony.ims.SipDelegateConfiguration; +import android.telephony.ims.SipDelegateImsConfiguration; +import android.telephony.ims.SipDelegateManager; +import android.telephony.ims.SipMessage; +import android.telephony.ims.stub.SipDelegate; + +import java.util.ArrayList; +import java.util.Set; +import java.util.concurrent.Executor; + +/** + * Implementation of callbacks by wrapping the internal AIDL from telephony. Also implements + * ISipDelegate internally when {@link DelegateStateCallback#onCreated(SipDelegate, Set)} is called + * in order to trampoline events back to telephony. + * @hide + */ +public class SipDelegateAidlWrapper implements DelegateStateCallback, DelegateMessageCallback { + private static final String LOG_TAG = "SipDelegateAW"; + + private final ISipDelegate.Stub mDelegateBinder = new ISipDelegate.Stub() { + @Override + public void sendMessage(SipMessage sipMessage, long configVersion) { + SipDelegate d = mDelegate; + final long token = Binder.clearCallingIdentity(); + try { + mExecutor.execute(() -> d.sendMessage(sipMessage, configVersion)); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public void notifyMessageReceived(String viaTransactionId) { + SipDelegate d = mDelegate; + final long token = Binder.clearCallingIdentity(); + try { + mExecutor.execute(() -> d.notifyMessageReceived(viaTransactionId)); + } finally { + Binder.restoreCallingIdentity(token); + } + + } + + @Override + public void notifyMessageReceiveError(String viaTransactionId, int reason) { + SipDelegate d = mDelegate; + final long token = Binder.clearCallingIdentity(); + try { + mExecutor.execute(() -> d.notifyMessageReceiveError(viaTransactionId, reason)); + } finally { + Binder.restoreCallingIdentity(token); + } + + } + + @Override + public void cleanupSession(String callId) { + SipDelegate d = mDelegate; + final long token = Binder.clearCallingIdentity(); + try { + mExecutor.execute(() -> d.cleanupSession(callId)); + } finally { + Binder.restoreCallingIdentity(token); + } + } + }; + + private final ISipDelegateMessageCallback mMessageBinder; + private final ISipDelegateStateCallback mStateBinder; + private final Executor mExecutor; + + private volatile SipDelegate mDelegate; + + public SipDelegateAidlWrapper(Executor executor, ISipDelegateStateCallback stateBinder, + ISipDelegateMessageCallback messageBinder) { + mExecutor = executor; + mStateBinder = stateBinder; + mMessageBinder = messageBinder; + } + + @Override + public void onMessageReceived(SipMessage message) { + try { + mMessageBinder.onMessageReceived(message); + } catch (RemoteException e) { + // BinderDied will be called on SipTransport instance to trigger destruction. Notify + // failure message failure locally for now. + SipDelegate d = mDelegate; + if (d != null) { + notifyLocalMessageFailedToBeReceived(message, + SipDelegateManager.MESSAGE_FAILURE_REASON_DELEGATE_DEAD); + } + } + } + + @Override + public void onMessageSent(String viaTransactionId) { + try { + mMessageBinder.onMessageSent(viaTransactionId); + } catch (RemoteException e) { + // BinderDied will trigger destroySipDelegate, so just ignore this locally. + } + } + + @Override + public void onMessageSendFailure(String viaTransactionId, int reason) { + try { + mMessageBinder.onMessageSendFailure(viaTransactionId, reason); + } catch (RemoteException e) { + // BinderDied will trigger destroySipDelegate, so just ignore this locally. + } + } + + @Override + public void onCreated(@NonNull SipDelegate delegate, + @Nullable Set<FeatureTagState> deniedTags) { + mDelegate = delegate; + try { + mStateBinder.onCreated(mDelegateBinder, new ArrayList<>(deniedTags)); + } catch (RemoteException e) { + // BinderDied will trigger destroySipDelegate, so just ignore this locally. + } + } + + @Override + public void onFeatureTagRegistrationChanged(DelegateRegistrationState registrationState) { + try { + mStateBinder.onFeatureTagRegistrationChanged(registrationState); + } catch (RemoteException e) { + // BinderDied will trigger destroySipDelegate, so just ignore this locally. + } + } + + @Override + public void onImsConfigurationChanged(@NonNull SipDelegateImsConfiguration config) { + try { + mStateBinder.onImsConfigurationChanged(config); + } catch (RemoteException e) { + // BinderDied will trigger destroySipDelegate, so just ignore this locally. + } + } + + @Override + public void onConfigurationChanged(@NonNull SipDelegateConfiguration config) { + try { + mStateBinder.onConfigurationChanged(config); + } catch (RemoteException e) { + // BinderDied will trigger destroySipDelegate, so just ignore this locally. + } + } + + @Override + public void onDestroyed(int reasonCode) { + mDelegate = null; + try { + mStateBinder.onDestroyed(reasonCode); + } catch (RemoteException e) { + // Do not worry about this if the remote side is already dead. + } + } + + public SipDelegate getDelegate() { + return mDelegate; + } + + public ISipDelegate getDelegateBinder() { + return mDelegateBinder; + } + + private void notifyLocalMessageFailedToBeReceived(SipMessage m, int reason) { + String transactionId = m.getViaBranchParameter(); + SipDelegate d = mDelegate; + if (d != null) { + mExecutor.execute(() -> d.notifyMessageReceiveError(transactionId, reason)); + } + } +} diff --git a/telephony/java/android/telephony/ims/aidl/SipDelegateConnectionAidlWrapper.java b/telephony/java/android/telephony/ims/aidl/SipDelegateConnectionAidlWrapper.java new file mode 100644 index 000000000000..47ddcb9696db --- /dev/null +++ b/telephony/java/android/telephony/ims/aidl/SipDelegateConnectionAidlWrapper.java @@ -0,0 +1,283 @@ +/* + * Copyright (C) 2020 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.ims.aidl; + +import android.os.Binder; +import android.os.IBinder; +import android.os.RemoteException; +import android.telephony.ims.DelegateRegistrationState; +import android.telephony.ims.FeatureTagState; +import android.telephony.ims.SipDelegateConfiguration; +import android.telephony.ims.SipDelegateConnection; +import android.telephony.ims.SipDelegateImsConfiguration; +import android.telephony.ims.SipDelegateManager; +import android.telephony.ims.SipMessage; +import android.telephony.ims.stub.DelegateConnectionMessageCallback; +import android.telephony.ims.stub.DelegateConnectionStateCallback; +import android.telephony.ims.stub.SipDelegate; +import android.util.ArraySet; +import android.util.Log; + +import java.util.List; +import java.util.NoSuchElementException; +import java.util.concurrent.Executor; +import java.util.concurrent.atomic.AtomicReference; + +/** + * Wrapper class implementing {@link SipDelegateConnection} using AIDL, which is returned to the + * local process. Also holds a reference to incoming connection message and state AIDL impl to + * trampoline events to callbacks as well as notify the local process in the event that the remote + * process becomes unavailable. + * <p> + * When the remote {@link SipDelegate} is created, this instance tracks the + * {@link ISipDelegate} associated with it and implements the + * {@link SipDelegateConnection} sent back to the local callback. + * @hide + */ +public class SipDelegateConnectionAidlWrapper implements SipDelegateConnection, + IBinder.DeathRecipient { + private static final String LOG_TAG = "SipDelegateCAW"; + + private final ISipDelegateConnectionStateCallback.Stub mStateBinder = + new ISipDelegateConnectionStateCallback.Stub() { + @Override + public void onCreated(ISipDelegate c) { + associateSipDelegate(c); + final long token = Binder.clearCallingIdentity(); + try { + mExecutor.execute(() -> + mStateCallback.onCreated(SipDelegateConnectionAidlWrapper.this)); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public void onFeatureTagStatusChanged(DelegateRegistrationState registrationState, + List<FeatureTagState> deniedFeatureTags) { + final long token = Binder.clearCallingIdentity(); + try { + mExecutor.execute(() -> + mStateCallback.onFeatureTagStatusChanged(registrationState, + new ArraySet<>(deniedFeatureTags))); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public void onImsConfigurationChanged(SipDelegateImsConfiguration registeredSipConfig) { + final long token = Binder.clearCallingIdentity(); + try { + mExecutor.execute(() -> + mStateCallback.onImsConfigurationChanged(registeredSipConfig)); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public void onConfigurationChanged(SipDelegateConfiguration registeredSipConfig) { + final long token = Binder.clearCallingIdentity(); + try { + mExecutor.execute(() -> + mStateCallback.onConfigurationChanged(registeredSipConfig)); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public void onDestroyed(int reason) { + invalidateSipDelegateBinder(); + final long token = Binder.clearCallingIdentity(); + try { + mExecutor.execute(() -> + mStateCallback.onDestroyed(reason)); + } finally { + Binder.restoreCallingIdentity(token); + } + } + }; + + private final ISipDelegateMessageCallback.Stub mMessageBinder = + new ISipDelegateMessageCallback.Stub() { + @Override + public void onMessageReceived(SipMessage message) { + final long token = Binder.clearCallingIdentity(); + try { + mExecutor.execute(() -> + mMessageCallback.onMessageReceived(message)); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public void onMessageSent(String viaTransactionId) { + final long token = Binder.clearCallingIdentity(); + try { + mExecutor.execute(() -> + mMessageCallback.onMessageSent(viaTransactionId)); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public void onMessageSendFailure(String viaTransactionId, int reason) { + final long token = Binder.clearCallingIdentity(); + try { + mExecutor.execute(() -> + mMessageCallback.onMessageSendFailure(viaTransactionId, reason)); + } finally { + Binder.restoreCallingIdentity(token); + } + } + }; + + + private final Executor mExecutor; + private final DelegateConnectionStateCallback mStateCallback; + private final DelegateConnectionMessageCallback mMessageCallback; + private final AtomicReference<ISipDelegate> mDelegateBinder = + new AtomicReference<>(); + + /** + * Wrap the local state and message callbacks, calling the implementation of these interfaces + * when the remote process calls these methods. + */ + public SipDelegateConnectionAidlWrapper(Executor executor, + DelegateConnectionStateCallback stateCallback, + DelegateConnectionMessageCallback messageCallback) { + mExecutor = executor; + mStateCallback = stateCallback; + mMessageCallback = messageCallback; + } + + @Override + public void sendMessage(SipMessage sipMessage, long configVersion) { + try { + ISipDelegate conn = getSipDelegateBinder(); + if (conn == null) { + notifyLocalMessageFailedToSend(sipMessage, + SipDelegateManager.MESSAGE_FAILURE_REASON_DELEGATE_CLOSED); + return; + } + conn.sendMessage(sipMessage, configVersion); + } catch (RemoteException e) { + notifyLocalMessageFailedToSend(sipMessage, + SipDelegateManager.MESSAGE_FAILURE_REASON_DELEGATE_DEAD); + } + } + + @Override + public void notifyMessageReceived(String viaTransactionId) { + try { + ISipDelegate conn = getSipDelegateBinder(); + if (conn == null) { + return; + } + conn.notifyMessageReceived(viaTransactionId); + } catch (RemoteException e) { + // Nothing to do here, app will eventually get remote death callback. + } + } + + @Override + public void notifyMessageReceiveError(String viaTransactionId, int reason) { + try { + ISipDelegate conn = getSipDelegateBinder(); + if (conn == null) { + return; + } + conn.notifyMessageReceiveError(viaTransactionId, reason); + } catch (RemoteException e) { + // Nothing to do here, app will eventually get remote death callback. + } + } + + @Override + public void cleanupSession(String callId) { + try { + ISipDelegate conn = getSipDelegateBinder(); + if (conn == null) { + return; + } + conn.cleanupSession(callId); + } catch (RemoteException e) { + // Nothing to do here, app will eventually get remote death callback. + } + } + + // Also called upon IImsRcsController death (telephony process dies). + @Override + public void binderDied() { + invalidateSipDelegateBinder(); + mExecutor.execute(() -> mStateCallback.onDestroyed( + SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_SERVICE_DEAD)); + } + + /** + * @return Implementation of state binder. + */ + public ISipDelegateConnectionStateCallback getStateCallbackBinder() { + return mStateBinder; + } + + /** + * @return Implementation of message binder. + */ + public ISipDelegateMessageCallback getMessageCallbackBinder() { + return mMessageBinder; + } + + /** + * @return The ISipDelegateConnection associated with this wrapper. + */ + public ISipDelegate getSipDelegateBinder() { + return mDelegateBinder.get(); + } + + private void associateSipDelegate(ISipDelegate c) { + if (c != null) { + try { + c.asBinder().linkToDeath(this, 0 /*flags*/); + } catch (RemoteException e) { + // already dead. + c = null; + } + } + mDelegateBinder.set(c); + } + + private void invalidateSipDelegateBinder() { + ISipDelegate oldVal = mDelegateBinder.getAndUpdate((unused) -> null); + if (oldVal != null) { + try { + oldVal.asBinder().unlinkToDeath(this, 0 /*flags*/); + } catch (NoSuchElementException e) { + Log.i(LOG_TAG, "invalidateSipDelegateBinder: " + e); + } + } + } + + private void notifyLocalMessageFailedToSend(SipMessage m, int reason) { + String transactionId = m.getViaBranchParameter(); + mExecutor.execute(() -> mMessageCallback.onMessageSendFailure(transactionId, reason)); + } +} diff --git a/telephony/java/android/telephony/ims/compat/ImsService.java b/telephony/java/android/telephony/ims/compat/ImsService.java index eafbb14539f5..303ba183c12e 100644 --- a/telephony/java/android/telephony/ims/compat/ImsService.java +++ b/telephony/java/android/telephony/ims/compat/ImsService.java @@ -20,8 +20,8 @@ import android.annotation.Nullable; import android.app.Service; import android.compat.annotation.UnsupportedAppUsage; import android.content.Intent; +import android.os.Build; import android.os.IBinder; -import android.os.RemoteException; import android.telephony.CarrierConfigManager; import android.telephony.ims.compat.feature.ImsFeature; import android.telephony.ims.compat.feature.MMTelFeature; @@ -87,33 +87,43 @@ public class ImsService extends Service { /** * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) protected final IBinder mImsServiceController = new IImsServiceController.Stub() { @Override - public IImsMMTelFeature createEmergencyMMTelFeature(int slotId, - IImsFeatureStatusCallback c) { - return createEmergencyMMTelFeatureInternal(slotId, c); + public IImsMMTelFeature createEmergencyMMTelFeature(int slotId) { + return createEmergencyMMTelFeatureInternal(slotId); } @Override - public IImsMMTelFeature createMMTelFeature(int slotId, IImsFeatureStatusCallback c) { - return createMMTelFeatureInternal(slotId, c); + public IImsMMTelFeature createMMTelFeature(int slotId) { + return createMMTelFeatureInternal(slotId); } @Override - public IImsRcsFeature createRcsFeature(int slotId, IImsFeatureStatusCallback c) { - return createRcsFeatureInternal(slotId, c); + public IImsRcsFeature createRcsFeature(int slotId) { + return createRcsFeatureInternal(slotId); } @Override - public void removeImsFeature(int slotId, int featureType, IImsFeatureStatusCallback c) - throws RemoteException { - ImsService.this.removeImsFeature(slotId, featureType, c); + public void removeImsFeature(int slotId, int featureType) { + ImsService.this.removeImsFeature(slotId, featureType); + } + + @Override + public void addFeatureStatusCallback(int slotId, int featureType, + IImsFeatureStatusCallback c) { + addImsFeatureStatusCallback(slotId, featureType, c); + } + + @Override + public void removeFeatureStatusCallback(int slotId, int featureType, + IImsFeatureStatusCallback c) { + removeImsFeatureStatusCallback(slotId, featureType, c); } }; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public ImsService() { } @@ -137,46 +147,40 @@ public class ImsService extends Service { return mFeaturesBySlot.get(slotId); } - private IImsMMTelFeature createEmergencyMMTelFeatureInternal(int slotId, - IImsFeatureStatusCallback c) { + private IImsMMTelFeature createEmergencyMMTelFeatureInternal(int slotId) { MMTelFeature f = onCreateEmergencyMMTelImsFeature(slotId); if (f != null) { - setupFeature(f, slotId, ImsFeature.EMERGENCY_MMTEL, c); + setupFeature(f, slotId, ImsFeature.EMERGENCY_MMTEL); return f.getBinder(); } else { return null; } } - private IImsMMTelFeature createMMTelFeatureInternal(int slotId, - IImsFeatureStatusCallback c) { + private IImsMMTelFeature createMMTelFeatureInternal(int slotId) { MMTelFeature f = onCreateMMTelImsFeature(slotId); if (f != null) { - setupFeature(f, slotId, ImsFeature.MMTEL, c); + setupFeature(f, slotId, ImsFeature.MMTEL); return f.getBinder(); } else { return null; } } - private IImsRcsFeature createRcsFeatureInternal(int slotId, - IImsFeatureStatusCallback c) { + private IImsRcsFeature createRcsFeatureInternal(int slotId) { RcsFeature f = onCreateRcsFeature(slotId); if (f != null) { - setupFeature(f, slotId, ImsFeature.RCS, c); + setupFeature(f, slotId, ImsFeature.RCS); return f.getBinder(); } else { return null; } } - private void setupFeature(ImsFeature f, int slotId, int featureType, - IImsFeatureStatusCallback c) { + private void setupFeature(ImsFeature f, int slotId, int featureType) { f.setContext(this); f.setSlotId(slotId); - f.addImsFeatureStatusCallback(c); addImsFeature(slotId, featureType, f); - // TODO: Remove once new onFeatureReady AIDL is merged in. f.onFeatureReady(); } @@ -193,12 +197,45 @@ public class ImsService extends Service { } } - private void removeImsFeature(int slotId, int featureType, + private void addImsFeatureStatusCallback(int slotId, int featureType, + IImsFeatureStatusCallback c) { + synchronized (mFeaturesBySlot) { + // get ImsFeature associated with the slot/feature + SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId); + if (features == null) { + Log.w(LOG_TAG, "Can not add ImsFeatureStatusCallback. No ImsFeatures exist on" + + " slot " + slotId); + return; + } + ImsFeature f = features.get(featureType); + if (f != null) { + f.addImsFeatureStatusCallback(c); + } + } + } + + private void removeImsFeatureStatusCallback(int slotId, int featureType, IImsFeatureStatusCallback c) { synchronized (mFeaturesBySlot) { // get ImsFeature associated with the slot/feature SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId); if (features == null) { + Log.w(LOG_TAG, "Can not remove ImsFeatureStatusCallback. No ImsFeatures exist on" + + " slot " + slotId); + return; + } + ImsFeature f = features.get(featureType); + if (f != null) { + f.removeImsFeatureStatusCallback(c); + } + } + } + + private void removeImsFeature(int slotId, int featureType) { + synchronized (mFeaturesBySlot) { + // get ImsFeature associated with the slot/feature + SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId); + if (features == null) { Log.w(LOG_TAG, "Can not remove ImsFeature. No ImsFeatures exist on slot " + slotId); return; @@ -209,7 +246,6 @@ public class ImsService extends Service { + featureType + " exists on slot " + slotId); return; } - f.removeImsFeatureStatusCallback(c); f.onFeatureRemoved(); features.remove(featureType); } diff --git a/telephony/java/android/telephony/ims/compat/OWNERS b/telephony/java/android/telephony/ims/compat/OWNERS new file mode 100644 index 000000000000..0854c5d45603 --- /dev/null +++ b/telephony/java/android/telephony/ims/compat/OWNERS @@ -0,0 +1,5 @@ +# Bug component: 20868 + +rgreenwalt@google.com +tgunn@google.com +breadley@google.com diff --git a/telephony/java/android/telephony/ims/compat/feature/ImsFeature.java b/telephony/java/android/telephony/ims/compat/feature/ImsFeature.java index 5a9e8e2dafc4..6038a18a28b1 100644 --- a/telephony/java/android/telephony/ims/compat/feature/ImsFeature.java +++ b/telephony/java/android/telephony/ims/compat/feature/ImsFeature.java @@ -19,6 +19,7 @@ package android.telephony.ims.compat.feature; import android.annotation.IntDef; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; +import android.os.Build; import android.os.IInterface; import android.os.RemoteException; import android.telephony.SubscriptionManager; @@ -78,12 +79,12 @@ public abstract class ImsFeature { mSlotId = slotId; } - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public int getFeatureState() { return mState; } - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) protected final void setFeatureState(@ImsState int state) { if (mState != state) { mState = state; diff --git a/telephony/java/android/telephony/ims/compat/feature/MMTelFeature.java b/telephony/java/android/telephony/ims/compat/feature/MMTelFeature.java index b52c37106049..d32e9b7f5122 100644 --- a/telephony/java/android/telephony/ims/compat/feature/MMTelFeature.java +++ b/telephony/java/android/telephony/ims/compat/feature/MMTelFeature.java @@ -18,6 +18,7 @@ package android.telephony.ims.compat.feature; import android.app.PendingIntent; import android.compat.annotation.UnsupportedAppUsage; +import android.os.Build; import android.os.Message; import android.os.RemoteException; import android.telephony.ims.ImsCallProfile; @@ -48,7 +49,7 @@ public class MMTelFeature extends ImsFeature { // Lock for feature synchronization private final Object mLock = new Object(); - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public MMTelFeature() { } diff --git a/telephony/java/android/telephony/ims/compat/feature/OWNERS b/telephony/java/android/telephony/ims/compat/feature/OWNERS new file mode 100644 index 000000000000..0854c5d45603 --- /dev/null +++ b/telephony/java/android/telephony/ims/compat/feature/OWNERS @@ -0,0 +1,5 @@ +# Bug component: 20868 + +rgreenwalt@google.com +tgunn@google.com +breadley@google.com diff --git a/telephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java b/telephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java index 06aa6428b1b2..8dcd711a96a9 100755 --- a/telephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java +++ b/telephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java @@ -19,6 +19,7 @@ package android.telephony.ims.compat.stub; import android.annotation.NonNull; import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; +import android.os.Build; import android.os.Message; import android.os.RemoteException; import android.telephony.CallQuality; @@ -29,11 +30,14 @@ import android.telephony.ims.ImsConferenceState; import android.telephony.ims.ImsReasonInfo; import android.telephony.ims.ImsStreamMediaProfile; import android.telephony.ims.ImsSuppServiceNotification; +import android.telephony.ims.RtpHeaderExtension; import android.telephony.ims.aidl.IImsCallSessionListener; import com.android.ims.internal.IImsCallSession; import com.android.ims.internal.IImsVideoCallProvider; +import java.util.List; + /** * Compat implementation of ImsCallSessionImplBase for older implementations. * @@ -45,7 +49,7 @@ import com.android.ims.internal.IImsVideoCallProvider; public class ImsCallSessionImplBase extends IImsCallSession.Stub { - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public ImsCallSessionImplBase() { } @@ -404,6 +408,15 @@ public class ImsCallSessionImplBase extends IImsCallSession.Stub { } /** + * Device sends RTP header extensions. + * @param headerExtensions The header extensions to send. + */ + @Override + public void sendRtpHeaderExtensions(@NonNull List<RtpHeaderExtension> headerExtensions) { + // no-op; not supported in compat layer. + } + + /** * There are two different ImsCallSessionListeners that need to reconciled here, we need to * convert the "old" version of the com.android.ims.internal.IImsCallSessionListener to the * "new" version of the Listener android.telephony.ims.ImsCallSessionListener when calling diff --git a/telephony/java/android/telephony/ims/compat/stub/ImsConfigImplBase.java b/telephony/java/android/telephony/ims/compat/stub/ImsConfigImplBase.java index aae6f9214c70..a8278ae0d734 100644 --- a/telephony/java/android/telephony/ims/compat/stub/ImsConfigImplBase.java +++ b/telephony/java/android/telephony/ims/compat/stub/ImsConfigImplBase.java @@ -19,6 +19,7 @@ package android.telephony.ims.compat.stub; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.Intent; +import android.os.Build; import android.os.RemoteException; import android.util.Log; @@ -59,7 +60,7 @@ public class ImsConfigImplBase { ImsConfigStub mImsConfigStub; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public ImsConfigImplBase(Context context) { mImsConfigStub = new ImsConfigStub(this, context); } @@ -89,7 +90,7 @@ public class ImsConfigImplBase { /** * Sets the value for IMS service/capabilities parameters by the operator device * management entity. It sets the config item value in the provisioned storage - * from which the master value is derived. Synchronous blocking call. + * from which the main value is derived. Synchronous blocking call. * * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants. * @param value in Integer format. @@ -102,7 +103,7 @@ public class ImsConfigImplBase { /** * Sets the value for IMS service/capabilities parameters by the operator device * management entity. It sets the config item value in the provisioned storage - * from which the master value is derived. Synchronous blocking call. + * from which the main value is derived. Synchronous blocking call. * * @param item as defined in com.android.ims.ImsConfig#ConfigConstants. * @param value in String format. @@ -114,7 +115,7 @@ public class ImsConfigImplBase { /** * Gets the value of the specified IMS feature item for specified network type. - * This operation gets the feature config value from the master storage (i.e. final + * This operation gets the feature config value from the main storage (i.e. final * value). Asynchronous non-blocking call. * * @param feature as defined in com.android.ims.ImsConfig#FeatureConstants. @@ -127,7 +128,7 @@ public class ImsConfigImplBase { /** * Sets the value for IMS feature item for specified network type. - * This operation stores the user setting in setting db from which master db + * This operation stores the user setting in setting db from which main db * is derived. * * @param feature as defined in com.android.ims.ImsConfig#FeatureConstants. @@ -164,7 +165,7 @@ public class ImsConfigImplBase { public void setVideoQuality(int quality, ImsConfigListener listener) throws RemoteException { } - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public IImsConfig getIImsConfig() { return mImsConfigStub; } /** @@ -268,7 +269,7 @@ public class ImsConfigImplBase { /** * Sets the value for IMS service/capabilities parameters by the operator device * management entity. It sets the config item value in the provisioned storage - * from which the master value is derived, and write it into local cache. + * from which the main value is derived, and write it into local cache. * Synchronous blocking call. * * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants. @@ -292,7 +293,7 @@ public class ImsConfigImplBase { /** * Sets the value for IMS service/capabilities parameters by the operator device * management entity. It sets the config item value in the provisioned storage - * from which the master value is derived, and write it into local cache. + * from which the main value is derived, and write it into local cache. * Synchronous blocking call. * * @param item as defined in com.android.ims.ImsConfig#ConfigConstants. diff --git a/telephony/java/android/telephony/ims/compat/stub/ImsUtListenerImplBase.java b/telephony/java/android/telephony/ims/compat/stub/ImsUtListenerImplBase.java index ce291d4d14c6..c689460a5cec 100644 --- a/telephony/java/android/telephony/ims/compat/stub/ImsUtListenerImplBase.java +++ b/telephony/java/android/telephony/ims/compat/stub/ImsUtListenerImplBase.java @@ -17,6 +17,7 @@ package android.telephony.ims.compat.stub; import android.compat.annotation.UnsupportedAppUsage; +import android.os.Build; import android.os.Bundle; import android.os.RemoteException; import android.telephony.ims.ImsCallForwardInfo; @@ -40,7 +41,7 @@ import com.android.ims.internal.IImsUtListener; public class ImsUtListenerImplBase extends IImsUtListener.Stub { - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public ImsUtListenerImplBase() { } diff --git a/telephony/java/android/telephony/ims/compat/stub/OWNERS b/telephony/java/android/telephony/ims/compat/stub/OWNERS new file mode 100644 index 000000000000..0854c5d45603 --- /dev/null +++ b/telephony/java/android/telephony/ims/compat/stub/OWNERS @@ -0,0 +1,5 @@ +# Bug component: 20868 + +rgreenwalt@google.com +tgunn@google.com +breadley@google.com diff --git a/telephony/java/android/telephony/ims/feature/CapabilityChangeRequest.java b/telephony/java/android/telephony/ims/feature/CapabilityChangeRequest.java index 87a5094a95f3..f3791d1c6f96 100644 --- a/telephony/java/android/telephony/ims/feature/CapabilityChangeRequest.java +++ b/telephony/java/android/telephony/ims/feature/CapabilityChangeRequest.java @@ -28,28 +28,26 @@ import java.util.List; import java.util.Set; /** - * Request to send to IMS provider, which will try to enable/disable capabilities that are added to - * the request. + * Used by the framework to enable and disable MMTEL and RCS capabilities. See + * MmTelFeature#changeEnabledCapabilities and RcsFeature#changeEnabledCapabilities. * {@hide} */ @SystemApi public final class CapabilityChangeRequest implements Parcelable { /** - * Contains a feature capability, defined as - * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VOICE}, - * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VIDEO}, - * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_UT}, or - * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_SMS}, - * along with an associated technology, defined as + * Contains a MMTEL feature capability {@link MmTelFeature.MmTelCapabilities} and RCS feature + * capability {@link RcsFeature.RcsImsCapabilities}, along with an associated technology, + * defined as * {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE} or * {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN} + * {@link ImsRegistrationImplBase#REGISTRATION_TECH_CROSS_SIM} */ public static class CapabilityPair { private final int mCapability; private final int radioTech; - public CapabilityPair(@MmTelFeature.MmTelCapabilities.MmTelCapability int capability, + public CapabilityPair(int capability, @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) { this.mCapability = capability; this.radioTech = radioTech; @@ -80,20 +78,18 @@ public final class CapabilityChangeRequest implements Parcelable { } /** - * @return The stored capability, defined as - * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VOICE}, - * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VIDEO}, - * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_UT}, or - * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_SMS} + * @return The stored capability, defined as {@link MmTelFeature.MmTelCapabilities} and + * {@link RcsFeature.RcsImsCapabilities} */ - public @MmTelFeature.MmTelCapabilities.MmTelCapability int getCapability() { + public int getCapability() { return mCapability; } /** * @return the stored radio technology, defined as - * {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE} or - * {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN} + * {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE}, + * {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN} or + * {@link ImsRegistrationImplBase#REGISTRATION_TECH_CROSS_SIM} */ public @ImsRegistrationImplBase.ImsRegistrationTech int getRadioTech() { return radioTech; @@ -123,12 +119,11 @@ public final class CapabilityChangeRequest implements Parcelable { * Add one or many capabilities to the request to be enabled. * * @param capabilities A bitfield of capabilities to enable, valid values are defined in - * {@link MmTelFeature.MmTelCapabilities.MmTelCapability}. + * {@link MmTelFeature.MmTelCapabilities} and {@link RcsFeature.RcsImsCapabilities}. * @param radioTech the radio tech that these capabilities should be enabled for, valid * values are in {@link ImsRegistrationImplBase.ImsRegistrationTech}. */ - public void addCapabilitiesToEnableForTech( - @MmTelFeature.MmTelCapabilities.MmTelCapability int capabilities, + public void addCapabilitiesToEnableForTech(int capabilities, @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) { addAllCapabilities(mCapabilitiesToEnable, capabilities, radioTech); } @@ -136,12 +131,11 @@ public final class CapabilityChangeRequest implements Parcelable { /** * Add one or many capabilities to the request to be disabled. * @param capabilities A bitfield of capabilities to diable, valid values are defined in - * {@link MmTelFeature.MmTelCapabilities.MmTelCapability}. + * {@link MmTelFeature.MmTelCapabilities} and {@link RcsFeature.RcsImsCapabilities}. * @param radioTech the radio tech that these capabilities should be disabled for, valid * values are in {@link ImsRegistrationImplBase.ImsRegistrationTech}. */ - public void addCapabilitiesToDisableForTech( - @MmTelFeature.MmTelCapabilities.MmTelCapability int capabilities, + public void addCapabilitiesToDisableForTech(int capabilities, @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) { addAllCapabilities(mCapabilitiesToDisable, capabilities, radioTech); } diff --git a/telephony/java/android/telephony/ims/feature/ImsFeature.java b/telephony/java/android/telephony/ims/feature/ImsFeature.java index b0a7b62c88ab..b56aa9687aee 100644 --- a/telephony/java/android/telephony/ims/feature/ImsFeature.java +++ b/telephony/java/android/telephony/ims/feature/ImsFeature.java @@ -199,8 +199,9 @@ public abstract class ImsFeature { * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_UT}, or * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_SMS}. * @param radioTech The radio tech that this capability failed for, defined as - * {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE} or - * {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN}. + * {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE}, + * {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN} or + * {@link ImsRegistrationImplBase#REGISTRATION_TECH_CROSS_SIM}. * @param reason The reason this capability was unable to be changed, defined as * {@link #CAPABILITY_ERROR_GENERIC} or {@link #CAPABILITY_SUCCESS}. */ @@ -336,7 +337,7 @@ public abstract class ImsFeature { /** * @hide */ - public final void initialize(Context context, int slotId) { + public void initialize(Context context, int slotId) { mContext = context; mSlotId = slotId; } @@ -509,6 +510,7 @@ public abstract class ImsFeature { * @return true if the capability is enabled, false otherwise. * @hide */ + @SuppressWarnings("HiddenAbstractMethod") public abstract boolean queryCapabilityConfiguration(int capability, int radioTech); /** @@ -547,5 +549,6 @@ public abstract class ImsFeature { * @return Binder instance that the framework will use to communicate with this feature. * @hide */ + @SuppressWarnings("HiddenAbstractMethod") protected abstract IInterface getBinder(); } diff --git a/telephony/java/android/telephony/ims/feature/MmTelFeature.java b/telephony/java/android/telephony/ims/feature/MmTelFeature.java index d3d194bc1355..9a3f592480d1 100644 --- a/telephony/java/android/telephony/ims/feature/MmTelFeature.java +++ b/telephony/java/android/telephony/ims/feature/MmTelFeature.java @@ -27,6 +27,8 @@ import android.telecom.TelecomManager; import android.telephony.ims.ImsCallProfile; import android.telephony.ims.ImsCallSession; import android.telephony.ims.ImsReasonInfo; +import android.telephony.ims.ImsService; +import android.telephony.ims.RtpHeaderExtensionType; import android.telephony.ims.aidl.IImsCapabilityCallback; import android.telephony.ims.aidl.IImsMmTelFeature; import android.telephony.ims.aidl.IImsMmTelListener; @@ -37,6 +39,7 @@ import android.telephony.ims.stub.ImsMultiEndpointImplBase; import android.telephony.ims.stub.ImsRegistrationImplBase; import android.telephony.ims.stub.ImsSmsImplBase; import android.telephony.ims.stub.ImsUtImplBase; +import android.util.ArraySet; import com.android.ims.internal.IImsCallSession; import com.android.ims.internal.IImsEcbm; @@ -45,6 +48,8 @@ import com.android.ims.internal.IImsUt; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.List; +import java.util.Set; /** * Base implementation for Voice and SMS (IR-92) and Video (IR-94) IMS support. @@ -93,6 +98,18 @@ public class MmTelFeature extends ImsFeature { } @Override + public void changeOfferedRtpHeaderExtensionTypes(List<RtpHeaderExtensionType> types) + throws RemoteException { + synchronized (mLock) { + try { + MmTelFeature.this.changeOfferedRtpHeaderExtensionTypes(new ArraySet<>(types)); + } catch (Exception e) { + throw new RemoteException(e.getMessage()); + } + } + } + + @Override public IImsCallSession createCallSession(ImsCallProfile profile) throws RemoteException { synchronized (mLock) { return createCallSessionInterface(profile); @@ -214,8 +231,9 @@ public class MmTelFeature extends ImsFeature { * The capabilities that are used in MmTelFeature are defined as * {@link MmTelCapabilities#CAPABILITY_TYPE_VOICE}, * {@link MmTelCapabilities#CAPABILITY_TYPE_VIDEO}, - * {@link MmTelCapabilities#CAPABILITY_TYPE_UT}, and - * {@link MmTelCapabilities#CAPABILITY_TYPE_SMS}. + * {@link MmTelCapabilities#CAPABILITY_TYPE_UT}, + * {@link MmTelCapabilities#CAPABILITY_TYPE_SMS}, and + * {@link MmTelCapabilities#CAPABILITY_TYPE_CALL_COMPOSER}. * * The capabilities of this MmTelFeature will be set by the framework. */ @@ -258,7 +276,8 @@ public class MmTelFeature extends ImsFeature { CAPABILITY_TYPE_VOICE, CAPABILITY_TYPE_VIDEO, CAPABILITY_TYPE_UT, - CAPABILITY_TYPE_SMS + CAPABILITY_TYPE_SMS, + CAPABILITY_TYPE_CALL_COMPOSER }) @Retention(RetentionPolicy.SOURCE) public @interface MmTelCapability {} @@ -284,8 +303,13 @@ public class MmTelFeature extends ImsFeature { public static final int CAPABILITY_TYPE_SMS = 1 << 3; /** - * @hide - */ + * This MmTelFeature supports Call Composer (section 2.4 of RC.20) + */ + public static final int CAPABILITY_TYPE_CALL_COMPOSER = 1 << 4; + + /** + * @hide + */ @Override @SystemApi public final void addCapabilities(@MmTelCapability int capabilities) { @@ -293,8 +317,8 @@ public class MmTelFeature extends ImsFeature { } /** - * @hide - */ + * @hide + */ @Override @SystemApi public final void removeCapabilities(@MmTelCapability int capability) { @@ -302,17 +326,18 @@ public class MmTelFeature extends ImsFeature { } /** - * @hide - */ + * @param capabilities a bitmask of one or more capabilities. + * + * @return true if all queried capabilities are true, otherwise false. + */ @Override - @SystemApi public final boolean isCapable(@MmTelCapability int capabilities) { return super.isCapable(capabilities); } /** - * @hide - */ + * @hide + */ @NonNull @Override public String toString() { @@ -325,6 +350,8 @@ public class MmTelFeature extends ImsFeature { builder.append(isCapable(CAPABILITY_TYPE_UT)); builder.append(" SMS: "); builder.append(isCapable(CAPABILITY_TYPE_SMS)); + builder.append(" CALL_COMPOSER: "); + builder.append(isCapable(CAPABILITY_TYPE_CALL_COMPOSER)); builder.append("]"); return builder.toString(); } @@ -622,6 +649,24 @@ public class MmTelFeature extends ImsFeature { } /** + * Called by the framework to report a change to the RTP header extension types which should be + * offered during SDP negotiation (see RFC8285 for more information). + * <p> + * The {@link ImsService} should report the RTP header extensions which were accepted during + * SDP negotiation using {@link ImsCallProfile#setAcceptedRtpHeaderExtensionTypes(Set)}. + * + * @param extensionTypes The RTP header extensions the framework wishes to offer during + * outgoing and incoming call setup. An empty list indicates that there + * are no framework defined RTP header extension types to offer. + * @hide + */ + @SystemApi + public void changeOfferedRtpHeaderExtensionTypes( + @NonNull Set<RtpHeaderExtensionType> extensionTypes) { + // Base implementation - should be overridden if RTP header extension handling is supported. + } + + /** * @hide */ public IImsCallSession createCallSessionInterface(ImsCallProfile profile) diff --git a/telephony/java/android/telephony/ims/feature/OWNERS b/telephony/java/android/telephony/ims/feature/OWNERS new file mode 100644 index 000000000000..0854c5d45603 --- /dev/null +++ b/telephony/java/android/telephony/ims/feature/OWNERS @@ -0,0 +1,5 @@ +# Bug component: 20868 + +rgreenwalt@google.com +tgunn@google.com +breadley@google.com diff --git a/telephony/java/android/telephony/ims/feature/RcsFeature.java b/telephony/java/android/telephony/ims/feature/RcsFeature.java index b8ae146784d4..18cc37d7fbda 100644 --- a/telephony/java/android/telephony/ims/feature/RcsFeature.java +++ b/telephony/java/android/telephony/ims/feature/RcsFeature.java @@ -19,22 +19,35 @@ package android.telephony.ims.feature; import android.annotation.CallbackExecutor; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.SystemApi; +import android.content.Context; import android.net.Uri; import android.os.RemoteException; -import android.telephony.ims.RcsContactUceCapability; +import android.telephony.ims.RcsUceAdapter; +import android.telephony.ims.aidl.CapabilityExchangeAidlWrapper; +import android.telephony.ims.aidl.ICapabilityExchangeEventListener; import android.telephony.ims.aidl.IImsCapabilityCallback; import android.telephony.ims.aidl.IImsRcsFeature; -import android.telephony.ims.aidl.IRcsFeatureListener; +import android.telephony.ims.aidl.IOptionsResponseCallback; +import android.telephony.ims.aidl.IPublishResponseCallback; +import android.telephony.ims.aidl.ISubscribeResponseCallback; +import android.telephony.ims.aidl.RcsOptionsResponseAidlWrapper; +import android.telephony.ims.aidl.RcsPublishResponseAidlWrapper; +import android.telephony.ims.aidl.RcsSubscribeResponseAidlWrapper; +import android.telephony.ims.stub.CapabilityExchangeEventListener; import android.telephony.ims.stub.ImsRegistrationImplBase; -import android.telephony.ims.stub.RcsPresenceExchangeImplBase; -import android.telephony.ims.stub.RcsSipOptionsImplBase; +import android.telephony.ims.stub.RcsCapabilityExchangeImplBase; +import android.telephony.ims.stub.RcsCapabilityExchangeImplBase.OptionsResponseCallback; +import android.telephony.ims.stub.RcsCapabilityExchangeImplBase.PublishResponseCallback; +import android.telephony.ims.stub.RcsCapabilityExchangeImplBase.SubscribeResponseCallback; import android.util.Log; import com.android.internal.telephony.util.TelephonyUtils; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.HashSet; import java.util.List; import java.util.concurrent.CancellationException; import java.util.concurrent.CompletableFuture; @@ -65,11 +78,6 @@ public class RcsFeature extends ImsFeature { } @Override - public void setListener(IRcsFeatureListener listener) { - mReference.setListener(listener); - } - - @Override public int queryCapabilityStatus() throws RemoteException { return executeMethodAsyncForResult( () -> mReference.queryCapabilityStatus().mCapabilities, @@ -106,44 +114,40 @@ public class RcsFeature extends ImsFeature { return executeMethodAsyncForResult(mReference::getFeatureState, "getFeatureState"); } - // RcsPresenceExchangeImplBase specific APIS + // RcsCapabilityExchangeImplBase specific APIs @Override - public void requestCapabilities(List<Uri> uris, int operationToken) throws RemoteException { - executeMethodAsync(() -> mReference.getPresenceExchangeInternal() - .requestCapabilities(uris, operationToken), "requestCapabilities"); + public void setCapabilityExchangeEventListener( + @Nullable ICapabilityExchangeEventListener listener) throws RemoteException { + CapabilityExchangeEventListener listenerWrapper = + new CapabilityExchangeAidlWrapper(listener); + executeMethodAsync(() -> mReference.setCapabilityExchangeEventListener(listenerWrapper), + "setCapabilityExchangeEventListener"); } - @Override - public void updateCapabilities(RcsContactUceCapability capabilities, int operationToken) - throws RemoteException { - executeMethodAsync(() -> mReference.getPresenceExchangeInternal() - .updateCapabilities(capabilities, operationToken), - "updateCapabilities"); - } - // RcsSipOptionsImplBase specific APIS @Override - public void sendCapabilityRequest(Uri contactUri, RcsContactUceCapability capabilities, - int operationToken) throws RemoteException { - executeMethodAsync(() -> mReference.getOptionsExchangeInternal() - .sendCapabilityRequest(contactUri, capabilities, operationToken), - "sendCapabilityRequest"); - + public void publishCapabilities(@NonNull String pidfXml, + @NonNull IPublishResponseCallback callback) throws RemoteException { + PublishResponseCallback callbackWrapper = new RcsPublishResponseAidlWrapper(callback); + executeMethodAsync(() -> mReference.getCapabilityExchangeImplBaseInternal() + .publishCapabilities(pidfXml, callbackWrapper), "publishCapabilities"); } - @Override - public void respondToCapabilityRequest(String contactUri, - RcsContactUceCapability ownCapabilities, int operationToken) - throws RemoteException { - executeMethodAsync(() -> mReference.getOptionsExchangeInternal() - .respondToCapabilityRequest(contactUri, ownCapabilities, - operationToken), "respondToCapabilityRequest"); + @Override + public void subscribeForCapabilities(@NonNull List<Uri> uris, + @NonNull ISubscribeResponseCallback callback) throws RemoteException { + SubscribeResponseCallback wrapper = new RcsSubscribeResponseAidlWrapper(callback); + executeMethodAsync(() -> mReference.getCapabilityExchangeImplBaseInternal() + .subscribeForCapabilities(uris, wrapper), "subscribeForCapabilities"); } + @Override - public void respondToCapabilityRequestWithError(Uri contactUri, int code, String reason, - int operationToken) throws RemoteException { - executeMethodAsync(() -> mReference.getOptionsExchangeInternal() - .respondToCapabilityRequestWithError(contactUri, code, reason, - operationToken), "respondToCapabilityRequestWithError"); + public void sendOptionsCapabilityRequest(@NonNull Uri contactUri, + @NonNull List<String> myCapabilities, @NonNull IOptionsResponseCallback callback) + throws RemoteException { + OptionsResponseCallback callbackWrapper = new RcsOptionsResponseAidlWrapper(callback); + executeMethodAsync(() -> mReference.getCapabilityExchangeImplBaseInternal() + .sendOptionsCapabilityRequest(contactUri, new HashSet<>(myCapabilities), + callbackWrapper), "sendOptionsCapabilityRequest"); } // Call the methods with a clean calling identity on the executor and wait indefinitely for @@ -182,8 +186,8 @@ public class RcsFeature extends ImsFeature { * Contains the capabilities defined and supported by a {@link RcsFeature} in the * form of a bitmask. The capabilities that are used in the RcsFeature are * defined as: - * {@link RcsImsCapabilityFlag#CAPABILITY_TYPE_OPTIONS_UCE} - * {@link RcsImsCapabilityFlag#CAPABILITY_TYPE_PRESENCE_UCE} + * {@link RcsUceAdatper.RcsImsCapabilityFlag#CAPABILITY_TYPE_OPTIONS_UCE} + * {@link RceUceAdapter.RcsImsCapabilityFlag#CAPABILITY_TYPE_PRESENCE_UCE} * * The enabled capabilities of this RcsFeature will be set by the framework * using {@link #changeEnabledCapabilities(CapabilityChangeRequest, CapabilityCallbackProxy)}. @@ -191,7 +195,6 @@ public class RcsFeature extends ImsFeature { * of the capability and notify the capability status as true using * {@link #notifyCapabilitiesStatusChanged(RcsImsCapabilities)}. This will signal to the * framework that the capability is available for usage. - * @hide */ public static class RcsImsCapabilities extends Capabilities { /** @hide*/ @@ -223,34 +226,43 @@ public class RcsFeature extends ImsFeature { */ public static final int CAPABILITY_TYPE_PRESENCE_UCE = 1 << 1; - public RcsImsCapabilities(@RcsImsCapabilityFlag int capabilities) { + /** + * Create a new {@link RcsImsCapabilities} instance with the provided capabilities. + * @param capabilities The capabilities that are supported for RCS in the form of a + * bitfield. + */ + public RcsImsCapabilities(@RcsUceAdapter.RcsImsCapabilityFlag int capabilities) { super(capabilities); } - private RcsImsCapabilities(Capabilities c) { - super(c.getMask()); + /** + * Create a new {@link RcsImsCapabilities} instance with the provided capabilities. + * @param capabilities The capabilities instance that are supported for RCS + */ + private RcsImsCapabilities(Capabilities capabilities) { + super(capabilities.getMask()); } @Override - public void addCapabilities(@RcsImsCapabilityFlag int capabilities) { + public void addCapabilities(@RcsUceAdapter.RcsImsCapabilityFlag int capabilities) { super.addCapabilities(capabilities); } @Override - public void removeCapabilities(@RcsImsCapabilityFlag int capabilities) { + public void removeCapabilities(@RcsUceAdapter.RcsImsCapabilityFlag int capabilities) { super.removeCapabilities(capabilities); } @Override - public boolean isCapable(@RcsImsCapabilityFlag int capabilities) { + public boolean isCapable(@RcsUceAdapter.RcsImsCapabilityFlag int capabilities) { return super.isCapable(capabilities); } } + private final Executor mExecutor; private final RcsFeatureBinder mImsRcsBinder; - private IRcsFeatureListener mListenerBinder; - private RcsPresenceExchangeImplBase mPresExchange; - private RcsSipOptionsImplBase mSipOptions; + private RcsCapabilityExchangeImplBase mCapabilityExchangeImpl; + private CapabilityExchangeEventListener mCapExchangeEventListener; /** * Create a new RcsFeature. @@ -258,26 +270,45 @@ public class RcsFeature extends ImsFeature { * Method stubs called from the framework will be called asynchronously. To specify the * {@link Executor} that the methods stubs will be called, use * {@link RcsFeature#RcsFeature(Executor)} instead. + * + * @deprecated Use {@link #RcsFeature(Executor)} to create the RcsFeature. */ + @Deprecated public RcsFeature() { super(); + mExecutor = Runnable::run; // Run on the Binder threads that call them. - mImsRcsBinder = new RcsFeatureBinder(this, Runnable::run); + mImsRcsBinder = new RcsFeatureBinder(this, mExecutor); } /** * Create a new RcsFeature using the Executor specified for methods being called by the * framework. - * @param executor The executor for the framework to use when making calls to this service. - * @hide + * @param executor The executor for the framework to use when executing the methods overridden + * by the implementation of RcsFeature. */ public RcsFeature(@NonNull Executor executor) { super(); if (executor == null) { throw new IllegalArgumentException("executor can not be null."); } + mExecutor = executor; // Run on the Binder thread by default. - mImsRcsBinder = new RcsFeatureBinder(this, executor); + mImsRcsBinder = new RcsFeatureBinder(this, mExecutor); + } + + /** + * Called when the RcsFeature is initialized. + * + * @param context The context that is used in the ImsService. + * @param slotId The slot ID associated with the RcsFeature. + * @hide + */ + @Override + public void initialize(Context context, int slotId) { + super.initialize(context, slotId); + // Notify that the RcsFeature is ready. + mExecutor.execute(() -> onFeatureReady()); } /** @@ -285,7 +316,7 @@ public class RcsFeature extends ImsFeature { * set, the {@link RcsFeature} has brought up the capability and is ready for framework * requests. To change the status of the capabilities * {@link #notifyCapabilitiesStatusChanged(RcsImsCapabilities)} should be called. - * @hide + * @return A copy of the current RcsFeature capability status. */ @Override public @NonNull final RcsImsCapabilities queryCapabilityStatus() { @@ -296,13 +327,13 @@ public class RcsFeature extends ImsFeature { * Notify the framework that the capabilities status has changed. If a capability is enabled, * this signals to the framework that the capability has been initialized and is ready. * Call {@link #queryCapabilityStatus()} to return the current capability status. - * @hide + * @param capabilities The current capability status of the RcsFeature. */ - public final void notifyCapabilitiesStatusChanged(@NonNull RcsImsCapabilities c) { - if (c == null) { + public final void notifyCapabilitiesStatusChanged(@NonNull RcsImsCapabilities capabilities) { + if (capabilities == null) { throw new IllegalArgumentException("RcsImsCapabilities must be non-null!"); } - super.notifyCapabilitiesStatusChanged(c); + super.notifyCapabilitiesStatusChanged(capabilities); } /** @@ -311,10 +342,12 @@ public class RcsFeature extends ImsFeature { * {@link #changeEnabledCapabilities(CapabilityChangeRequest, CapabilityCallbackProxy)} to * enable or disable capability A, this method should return the correct configuration for * capability A afterwards (until it has changed). - * @hide + * @param capability The capability that we are querying the configuration for. + * @param radioTech The radio technology type that we are querying. + * @return true if the capability is enabled, false otherwise. */ public boolean queryCapabilityConfiguration( - @RcsImsCapabilities.RcsImsCapabilityFlag int capability, + @RcsUceAdapter.RcsImsCapabilityFlag int capability, @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) { // Base Implementation - Override to provide functionality return false; @@ -333,46 +366,43 @@ public class RcsFeature extends ImsFeature { * If for some reason one or more of these capabilities can not be enabled/disabled, * {@link CapabilityCallbackProxy#onChangeCapabilityConfigurationError(int, int, int)} should * be called for each capability change that resulted in an error. - * @hide + * @param request The request to change the capability. + * @param callback To notify the framework that the result of the capability changes. */ @Override public void changeEnabledCapabilities(@NonNull CapabilityChangeRequest request, - @NonNull CapabilityCallbackProxy c) { + @NonNull CapabilityCallbackProxy callback) { // Base Implementation - Override to provide functionality } /** - * Retrieve the implementation of SIP OPTIONS for this {@link RcsFeature}. - * <p> - * Will only be requested by the framework if capability exchange via SIP OPTIONS is - * configured as capable during a + * Retrieve the implementation of UCE for this {@link RcsFeature}, which can use either + * presence or OPTIONS for capability exchange. + * + * Will only be requested by the framework if capability exchange is configured + * as capable during a * {@link #changeEnabledCapabilities(CapabilityChangeRequest, CapabilityCallbackProxy)} * operation and the RcsFeature sets the status of the capability to true using * {@link #notifyCapabilitiesStatusChanged(RcsImsCapabilities)}. * - * @return An instance of {@link RcsSipOptionsImplBase} that implements SIP options exchange if - * it is supported by the device. - * @hide + * @param listener A {@link CapabilityExchangeEventListener} to send the capability exchange + * event to the framework. + * @return An instance of {@link RcsCapabilityExchangeImplBase} that implements capability + * exchange if it is supported by the device. */ - public @NonNull RcsSipOptionsImplBase getOptionsExchangeImpl() { + public @NonNull RcsCapabilityExchangeImplBase createCapabilityExchangeImpl( + @NonNull CapabilityExchangeEventListener listener) { // Base Implementation, override to implement functionality - return new RcsSipOptionsImplBase(); + return new RcsCapabilityExchangeImplBase(); } /** - * Retrieve the implementation of UCE presence for this {@link RcsFeature}. - * Will only be requested by the framework if presence exchang is configured as capable during - * a {@link #changeEnabledCapabilities(CapabilityChangeRequest, CapabilityCallbackProxy)} - * operation and the RcsFeature sets the status of the capability to true using - * {@link #notifyCapabilitiesStatusChanged(RcsImsCapabilities)}. - * - * @return An instance of {@link RcsPresenceExchangeImplBase} that implements presence - * exchange if it is supported by the device. - * @hide + * Remove the given CapabilityExchangeImplBase instance. + * @param capExchangeImpl The {@link RcsCapabilityExchangeImplBase} instance to be destroyed. */ - public @NonNull RcsPresenceExchangeImplBase getPresenceExchangeImpl() { - // Base Implementation, override to implement functionality. - return new RcsPresenceExchangeImplBase(); + public void destroyCapabilityExchangeImpl( + @NonNull RcsCapabilityExchangeImplBase capExchangeImpl) { + // Override to implement the process of destroying RcsCapabilityExchangeImplBase instance. } /**{@inheritDoc}*/ @@ -395,39 +425,56 @@ public class RcsFeature extends ImsFeature { return mImsRcsBinder; } - /**@hide*/ - public IRcsFeatureListener getListener() { - synchronized (mLock) { - return mListenerBinder; - } - } - - private void setListener(IRcsFeatureListener listener) { + /** + * Set the capability exchange listener. + * @param listener A {@link CapabilityExchangeEventListener} to send the capability exchange + * event to the framework. + */ + private void setCapabilityExchangeEventListener( + @Nullable CapabilityExchangeEventListener listener) { synchronized (mLock) { - mListenerBinder = listener; - if (mListenerBinder != null) { - onFeatureReady(); + mCapExchangeEventListener = listener; + if (mCapExchangeEventListener != null) { + initRcsCapabilityExchangeImplBase(mCapExchangeEventListener); + } else { + // Remove the RcsCapabilityExchangeImplBase instance when the capability exchange + // instance has been removed in the framework. + if (mCapabilityExchangeImpl != null) { + destroyCapabilityExchangeImpl(mCapabilityExchangeImpl); + } + mCapabilityExchangeImpl = null; } } } - private RcsPresenceExchangeImplBase getPresenceExchangeInternal() { + /** + * Initialize the RcsCapabilityExchangeImplBase instance if the capability exchange instance + * has already been created in the framework. + * @param listener A {@link CapabilityExchangeEventListener} to send the capability exchange + * event to the framework. + */ + private void initRcsCapabilityExchangeImplBase( + @NonNull CapabilityExchangeEventListener listener) { synchronized (mLock) { - if (mPresExchange == null) { - mPresExchange = getPresenceExchangeImpl(); - mPresExchange.initialize(this); + // Remove the original instance + if (mCapabilityExchangeImpl != null) { + destroyCapabilityExchangeImpl(mCapabilityExchangeImpl); } - return mPresExchange; + mCapabilityExchangeImpl = createCapabilityExchangeImpl(listener); } } - private RcsSipOptionsImplBase getOptionsExchangeInternal() { + /** + * @return the {@link RcsCapabilityExchangeImplBase} associated with the RcsFeature. + */ + private @NonNull RcsCapabilityExchangeImplBase getCapabilityExchangeImplBaseInternal() { synchronized (mLock) { - if (mSipOptions == null) { - mSipOptions = getOptionsExchangeImpl(); - mSipOptions.initialize(this); + // The method should not be called if the instance of RcsCapabilityExchangeImplBase has + // not been created yet. + if (mCapabilityExchangeImpl == null) { + throw new IllegalStateException("Session is not available."); } - return mSipOptions; + return mCapabilityExchangeImpl; } } } diff --git a/telephony/java/android/telephony/ims/stub/CapabilityExchangeEventListener.java b/telephony/java/android/telephony/ims/stub/CapabilityExchangeEventListener.java new file mode 100644 index 000000000000..a3be8dab2891 --- /dev/null +++ b/telephony/java/android/telephony/ims/stub/CapabilityExchangeEventListener.java @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2020 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.ims.stub; + +import android.annotation.IntRange; +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.net.Uri; +import android.telephony.ims.ImsException; +import android.telephony.ims.RcsContactUceCapability; +import android.telephony.ims.RcsUceAdapter; +import android.telephony.ims.feature.ImsFeature; +import android.telephony.ims.feature.RcsFeature; + +import java.util.Set; + +/** + * The interface that is used by the framework to listen to events from the vendor RCS stack + * regarding capabilities exchange using presence server and OPTIONS. + * @hide + */ +@SystemApi +public interface CapabilityExchangeEventListener { + /** + * Interface used by the framework to respond to OPTIONS requests. + */ + interface OptionsRequestCallback { + /** + * Respond to a remote capability request from the contact specified with the + * capabilities of this device. + * @param ownCapabilities The capabilities of this device. + * @param isBlocked Whether or not the user has blocked the number requesting the + * capabilities of this device. If true, the device should respond to the OPTIONS + * request with a 200 OK response and no capabilities. + */ + void onRespondToCapabilityRequest(@NonNull RcsContactUceCapability ownCapabilities, + boolean isBlocked); + + /** + * Respond to a remote capability request from the contact specified with the + * specified error. + * @param code The SIP response code to respond with. + * @param reason A non-null String containing the reason associated with the SIP code. + */ + void onRespondToCapabilityRequestWithError(@IntRange(from = 100, to = 699) int code, + @NonNull String reason); + } + + /** + * Trigger the framework to provide a capability update using + * {@link RcsCapabilityExchangeImplBase#publishCapabilities}. + * <p> + * This is typically used when trying to generate an initial PUBLISH for a new subscription to + * the network. The device will cache all presence publications after boot until this method is + * called the first time. + * @param publishTriggerType The reason for the capability update request. + * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is not currently + * connected to the framework. This can happen if the {@link RcsFeature} is not + * {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received the + * {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare cases when the + * Telephony stack has crashed. + */ + void onRequestPublishCapabilities( + @RcsUceAdapter.StackPublishTriggerType int publishTriggerType) throws ImsException; + + /** + * Notify the framework that the device's capabilities have been unpublished + * from the network. + * + * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is not currently + * connected to the framework. This can happen if the {@link RcsFeature} is not + * {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received the + * {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare cases when the + * Telephony stack has crashed. + */ + void onUnpublish() throws ImsException; + + /** + * Inform the framework of an OPTIONS query from a remote device for this device's UCE + * capabilities. + * <p> + * The framework will respond via the + * {@link OptionsRequestCallback#onRespondToCapabilityRequest} or + * {@link OptionsRequestCallback#onRespondToCapabilityRequestWithError}. + * @param contactUri The URI associated with the remote contact that is + * requesting capabilities. + * @param remoteCapabilities The remote contact's capability information. The capability + * information is in the format defined in RCC.07 section 2.6.1.3. + * @param callback The callback of this request which is sent from the remote user. + * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is not + * currently connected to the framework. This can happen if the {@link RcsFeature} is not + * {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received + * the {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare + * cases when the Telephony stack has crashed. + */ + void onRemoteCapabilityRequest(@NonNull Uri contactUri, + @NonNull Set<String> remoteCapabilities, + @NonNull OptionsRequestCallback callback) throws ImsException; +} diff --git a/telephony/java/android/telephony/ims/stub/DelegateConnectionMessageCallback.java b/telephony/java/android/telephony/ims/stub/DelegateConnectionMessageCallback.java new file mode 100644 index 000000000000..eefe8493aef1 --- /dev/null +++ b/telephony/java/android/telephony/ims/stub/DelegateConnectionMessageCallback.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2020 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.ims.stub; + +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.telephony.ims.SipDelegateConnection; +import android.telephony.ims.SipDelegateManager; +import android.telephony.ims.SipMessage; + +/** + * The callback associated with a {@link SipDelegateConnection}, which handles newly received + * messages as well as the result of sending a SIP message. + * @hide + */ +@SystemApi +public interface DelegateConnectionMessageCallback { + + /** + * A new {@link SipMessage} has been received from the delegate. + * @param message the {@link SipMessage} routed to this RCS application. + */ + void onMessageReceived(@NonNull SipMessage message); + + /** + * A message previously sent to the SIP delegate using + * {@link SipDelegateConnection#sendMessage} has been successfully sent. + * @param viaTransactionId The transaction ID found in the via header field of the + * previously sent {@link SipMessage}. + */ + void onMessageSent(@NonNull String viaTransactionId); + + /** + * A message previously sent to the SIP delegate using + * {@link SipDelegateConnection#sendMessage} has failed to be sent. + * @param viaTransactionId The Transaction ID found in the via header field of the + * previously sent {@link SipMessage}. + * @param reason The reason for the failure. + */ + void onMessageSendFailure(@NonNull String viaTransactionId, + @SipDelegateManager.MessageFailureReason int reason); +} diff --git a/telephony/java/android/telephony/ims/stub/DelegateConnectionStateCallback.java b/telephony/java/android/telephony/ims/stub/DelegateConnectionStateCallback.java new file mode 100644 index 000000000000..c078637e3791 --- /dev/null +++ b/telephony/java/android/telephony/ims/stub/DelegateConnectionStateCallback.java @@ -0,0 +1,174 @@ +/* + * Copyright (C) 2020 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.ims.stub; + +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.telephony.ims.DelegateRegistrationState; +import android.telephony.ims.DelegateRequest; +import android.telephony.ims.FeatureTagState; +import android.telephony.ims.SipDelegateConfiguration; +import android.telephony.ims.SipDelegateConnection; +import android.telephony.ims.SipDelegateImsConfiguration; +import android.telephony.ims.SipDelegateManager; + +import java.util.Set; + +/** + * The callback associated with a {@link SipDelegateConnection} that manages the state of the + * SipDelegateConnection. + * <p> + * After {@link SipDelegateManager#createSipDelegate} is used to request a new + * {@link SipDelegateConnection} be created, {@link #onCreated} will be called with the + * {@link SipDelegateConnection} instance that must be used to communicate with the remote + * {@link SipDelegate}. + * <p> + * After, {@link #onFeatureTagStatusChanged} will always be called at least once with the current + * status of the feature tags that have been requested. The application may receive multiple + * {@link #onFeatureTagStatusChanged} callbacks over the lifetime of the associated + * {@link SipDelegateConnection}, which will signal changes to how SIP messages associated with + * those feature tags will be handled. + * <p> + * In order to start sending SIP messages, the SIP configuration parameters will need to be + * received, so the messaging application should make no assumptions about these parameters and wait + * until {@link #onConfigurationChanged(SipDelegateConfiguration)} has been called. This is + * guaranteed to happen after the first {@link #onFeatureTagStatusChanged} if there is at least one + * feature tag that has been successfully associated with the {@link SipDelegateConnection}. If all + * feature tags were denied, no IMS configuration will be sent. + * <p> + * The {@link SipDelegateConnection} will stay associated with this RCS application until either the + * RCS application calls {@link SipDelegateManager#destroySipDelegate} or telephony destroys the + * {@link SipDelegateConnection}. In both cases, {@link #onDestroyed(int)} will be called. + * Telephony destroying the {@link SipDelegateConnection} instance is rare and will only happen in + * rare cases, such as if telephony itself or IMS service dies unexpectedly. See + * {@link SipDelegateManager.SipDelegateDestroyReason} reasons for more information on all of the + * cases that will trigger the {@link SipDelegateConnection} to be destroyed. + * + * @hide + */ +@SystemApi +public interface DelegateConnectionStateCallback { + + /** + * A {@link SipDelegateConnection} has been successfully created for the + * {@link DelegateRequest} used when calling {@link SipDelegateManager#createSipDelegate}. + */ + void onCreated(@NonNull SipDelegateConnection c); + + /** + * The status of the RCS feature tags that were requested as part of the initial + * {@link DelegateRequest}. + * <p> + * There are four states that each RCS feature tag can be in: registered, deregistering, + * deregistered, and denied. + * <p> + * When a feature tag is considered registered, SIP messages associated with that feature tag + * may be sent and received freely. + * <p> + * When a feature tag is deregistering, the network IMS registration still contains the feature + * tag, however the IMS service and associated {@link SipDelegate} is in the progress of + * modifying the IMS registration to remove this feature tag and requires the application to + * perform an action before the IMS registration can change. The specific action required for + * the SipDelegate to continue modifying the IMS registration can be found in the definition of + * each {@link DelegateRegistrationState.DeregisteringReason}. + * <p> + * When a feature tag is in the deregistered state, new out-of-dialog SIP messages for that + * feature tag will be rejected, however due to network race conditions, the RCS application + * should still be able to handle new out-of-dialog SIP requests from the network. This may not + * be possible, however, if the IMS registration itself was lost. See the + * {@link DelegateRegistrationState.DeregisteredReason} reasons for more information on how SIP + * messages are handled in each of these cases. + * <p> + * If a feature tag is denied, no incoming messages will be routed to the associated + * {@link DelegateConnectionMessageCallback} and all outgoing SIP messages related to this + * feature tag will be rejected. See {@link SipDelegateManager.DeniedReason} + * reasons for more information about the conditions when this will happen. + * <p> + * The set of feature tags contained in the registered, deregistering, deregistered, and denied + * lists will always equal the set of feature tags requested in the initial + * {@link DelegateRequest}. + * <p> + * Transitions of feature tags from registered, deregistering, and deregistered and vice-versa + * may happen quite often, however transitions to/from denied are rare and only occur if the + * user has changed the role of your application to add/remove support for one or more requested + * feature tags or carrier provisioning has enabled or disabled single registration entirely. + * Please see {@link SipDelegateManager.DeniedReason} reasons for an explanation of each of + * these cases as well as what may cause them to change. + * + * @param registrationState The new IMS registration state of each of the feature tags + * associated with the {@link SipDelegate}. + * @param deniedFeatureTags A list of {@link FeatureTagState} objects, each containing a feature + * tag associated with this {@link SipDelegateConnection} that has no access to + * send/receive SIP messages as well as a reason for why the feature tag is denied. For more + * information on the reason why the feature tag was denied access, see the + * {@link SipDelegateManager.DeniedReason} reasons. + */ + void onFeatureTagStatusChanged(@NonNull DelegateRegistrationState registrationState, + @NonNull Set<FeatureTagState> deniedFeatureTags); + + + /** + * IMS configuration of the underlying IMS stack used by this IMS application for construction + * of the SIP messages that will be sent over the carrier's network. + * <p> + * There should never be assumptions made about the configuration of the underling IMS stack and + * the IMS application should wait for this indication before sending out any outgoing SIP + * messages. + * <p> + * Configuration may change due to IMS registration changes as well as + * other optional events on the carrier network. If IMS stack is already registered at the time + * of callback registration, then this method shall be invoked with the current configuration. + * Otherwise, there may be a delay in this method being called if initial IMS registration has + * not compleed yet. + * + * @param registeredSipConfig The configuration of the IMS stack registered on the IMS network. + * @deprecated Will not be in final API, use + * {@link #onConfigurationChanged(SipDelegateConfiguration)} instead}. + */ + @Deprecated + default void onImsConfigurationChanged( + @NonNull SipDelegateImsConfiguration registeredSipConfig) { + onConfigurationChanged(registeredSipConfig.toNewConfig()); + } + + /** + * IMS configuration of the underlying IMS stack used by this IMS application for construction + * of the SIP messages that will be sent over the carrier's network. + * <p> + * There should never be assumptions made about the configuration of the underling IMS stack and + * the IMS application should wait for this indication before sending out any outgoing SIP + * messages. + * <p> + * Configuration may change due to IMS registration changes as well as + * other optional events on the carrier network. If IMS stack is already registered at the time + * of callback registration, then this method shall be invoked with the current configuration. + * Otherwise, there may be a delay in this method being called if initial IMS registration has + * not compleed yet. + * + * @param registeredSipConfig The configuration of the IMS stack registered on the IMS network. + */ + default void onConfigurationChanged(@NonNull SipDelegateConfiguration registeredSipConfig) {} + + /** + * The previously created {@link SipDelegateConnection} instance delivered via + * {@link #onCreated(SipDelegateConnection)} has been destroyed. This interface should no longer + * be used for any SIP message handling. + * + * @param reason The reason for the failure. + */ + void onDestroyed(@SipDelegateManager.SipDelegateDestroyReason int reason); +} diff --git a/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java b/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java index 48bc0d675ed5..a3a6cb864fa5 100644 --- a/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java +++ b/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java @@ -26,10 +26,17 @@ import android.telephony.ims.ImsCallSessionListener; import android.telephony.ims.ImsReasonInfo; import android.telephony.ims.ImsStreamMediaProfile; import android.telephony.ims.ImsVideoCallProvider; +import android.telephony.ims.RtpHeaderExtension; +import android.telephony.ims.RtpHeaderExtensionType; import android.telephony.ims.aidl.IImsCallSessionListener; +import android.util.ArraySet; import com.android.ims.internal.IImsCallSession; import com.android.ims.internal.IImsVideoCallProvider; + +import java.util.List; +import java.util.Set; + /** * Base implementation of IImsCallSession, which implements stub versions of the methods available. * @@ -277,6 +284,12 @@ public class ImsCallSessionImplBase implements AutoCloseable { public void sendRttMessage(String rttMessage) { ImsCallSessionImplBase.this.sendRttMessage(rttMessage); } + + @Override + public void sendRtpHeaderExtensions(@NonNull List<RtpHeaderExtension> extensions) { + ImsCallSessionImplBase.this.sendRtpHeaderExtensions( + new ArraySet<RtpHeaderExtension>(extensions)); + } }; /** @@ -437,8 +450,8 @@ public class ImsCallSessionImplBase implements AutoCloseable { * Transfer an established call to given number * * @param number number to transfer the call - * @param isConfirmationRequired if {@code True}, indicates Assured transfer, - * if {@code False} it indicates Blind transfer. + * @param isConfirmationRequired if {@code True}, indicates a confirmed transfer, + * if {@code False} it indicates an unconfirmed transfer. * @hide */ public void transfer(@NonNull String number, boolean isConfirmationRequired) { @@ -636,6 +649,22 @@ public class ImsCallSessionImplBase implements AutoCloseable { public void sendRttMessage(String rttMessage) { } + /** + * Device requests that {@code rtpHeaderExtensions} are sent as a header extension with the next + * RTP packet sent by the IMS stack. + * <p> + * The {@link RtpHeaderExtensionType}s negotiated during SDP (Session Description Protocol) + * signalling determine the {@link RtpHeaderExtension}s which can be sent using this method. + * See RFC8285 for more information. + * <p> + * By specification, the RTP header extension is an unacknowledged transmission and there is no + * guarantee that the header extension will be delivered by the network to the other end of the + * call. + * @param rtpHeaderExtensions The RTP header extensions to be included in the next RTP header. + */ + public void sendRtpHeaderExtensions(@NonNull Set<RtpHeaderExtension> rtpHeaderExtensions) { + } + /** @hide */ public IImsCallSession getServiceImpl() { return mServiceImpl; diff --git a/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java index dc72a228f8f5..d75da9035124 100644 --- a/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java +++ b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java @@ -23,8 +23,11 @@ import android.content.Context; import android.os.PersistableBundle; import android.os.RemoteException; import android.telephony.ims.ProvisioningManager; +import android.telephony.ims.RcsClientConfiguration; +import android.telephony.ims.RcsConfig; import android.telephony.ims.aidl.IImsConfig; import android.telephony.ims.aidl.IImsConfigCallback; +import android.telephony.ims.aidl.IRcsConfigCallback; import android.util.Log; import com.android.ims.ImsConfig; @@ -34,6 +37,7 @@ import com.android.internal.telephony.util.RemoteCallbackListExt; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.ref.WeakReference; +import java.util.Arrays; import java.util.HashMap; /** @@ -126,7 +130,7 @@ public class ImsConfigImplBase { */ @Override public synchronized String getConfigString(int item) throws RemoteException { - if (mProvisionedIntValue.containsKey(item)) { + if (mProvisionedStringValue.containsKey(item)) { return mProvisionedStringValue.get(item); } else { String retVal = getImsConfigImpl().getConfigString(item); @@ -140,7 +144,7 @@ public class ImsConfigImplBase { /** * Sets the value for IMS service/capabilities parameters by the operator device * management entity. It sets the config item value in the provisioned storage - * from which the master value is derived, and write it into local cache. + * from which the main value is derived, and write it into local cache. * Synchronous blocking call. * * @param item integer key @@ -165,7 +169,7 @@ public class ImsConfigImplBase { /** * Sets the value for IMS service/capabilities parameters by the operator device * management entity. It sets the config item value in the provisioned storage - * from which the master value is derived, and write it into local cache. + * from which the main value is derived, and write it into local cache. * Synchronous blocking call. * * @param item as defined in com.android.ims.ImsConfig#ConfigConstants. @@ -202,7 +206,13 @@ public class ImsConfigImplBase { @Override public void notifyRcsAutoConfigurationReceived(byte[] config, boolean isCompressed) throws RemoteException { - getImsConfigImpl().notifyRcsAutoConfigurationReceived(config, isCompressed); + getImsConfigImpl().onNotifyRcsAutoConfigurationReceived(config, isCompressed); + } + + @Override + public void notifyRcsAutoConfigurationRemoved() + throws RemoteException { + getImsConfigImpl().onNotifyRcsAutoConfigurationRemoved(); } private void notifyImsConfigChanged(int item, int value) throws RemoteException { @@ -228,6 +238,36 @@ public class ImsConfigImplBase { notifyImsConfigChanged(item, value); } } + + @Override + public void addRcsConfigCallback(IRcsConfigCallback c) throws RemoteException { + getImsConfigImpl().addRcsConfigCallback(c); + } + + @Override + public void removeRcsConfigCallback(IRcsConfigCallback c) throws RemoteException { + getImsConfigImpl().removeRcsConfigCallback(c); + } + + @Override + public void triggerRcsReconfiguration() throws RemoteException { + getImsConfigImpl().triggerAutoConfiguration(); + } + + @Override + public void setRcsClientConfiguration(RcsClientConfiguration rcc) throws RemoteException { + getImsConfigImpl().setRcsClientConfiguration(rcc); + } + + @Override + public void notifyIntImsConfigChanged(int item, int value) throws RemoteException { + notifyImsConfigChanged(item, value); + } + + @Override + public void notifyStringImsConfigChanged(int item, String value) throws RemoteException { + notifyImsConfigChanged(item, value); + } } /** @@ -257,6 +297,9 @@ public class ImsConfigImplBase { private final RemoteCallbackListExt<IImsConfigCallback> mCallbacks = new RemoteCallbackListExt<>(); + private final RemoteCallbackListExt<IRcsConfigCallback> mRcsCallbacks = + new RemoteCallbackListExt<>(); + private byte[] mRcsConfigData; ImsConfigStub mImsConfigStub; /** @@ -320,6 +363,56 @@ public class ImsConfigImplBase { }); } + private void addRcsConfigCallback(IRcsConfigCallback c) { + mRcsCallbacks.register(c); + if (mRcsConfigData != null) { + try { + c.onConfigurationChanged(mRcsConfigData); + } catch (RemoteException e) { + Log.w(TAG, "dead binder to call onConfigurationChanged, skipping."); + } + } + } + + private void removeRcsConfigCallback(IRcsConfigCallback c) { + mRcsCallbacks.unregister(c); + } + + private void onNotifyRcsAutoConfigurationReceived(byte[] config, boolean isCompressed) { + // cache uncompressed config + config = isCompressed ? RcsConfig.decompressGzip(config) : config; + if (Arrays.equals(mRcsConfigData, config)) { + return; + } + mRcsConfigData = config; + + // can be null in testing + if (mRcsCallbacks != null) { + mRcsCallbacks.broadcastAction(c -> { + try { + c.onConfigurationChanged(mRcsConfigData); + } catch (RemoteException e) { + Log.w(TAG, "dead binder in notifyRcsAutoConfigurationReceived, skipping."); + } + }); + } + notifyRcsAutoConfigurationReceived(config, isCompressed); + } + + private void onNotifyRcsAutoConfigurationRemoved() { + mRcsConfigData = null; + if (mRcsCallbacks != null) { + mRcsCallbacks.broadcastAction(c -> { + try { + c.onConfigurationReset(); + } catch (RemoteException e) { + Log.w(TAG, "dead binder in notifyRcsAutoConfigurationRemoved, skipping."); + } + }); + } + notifyRcsAutoConfigurationRemoved(); + } + /** * @hide */ @@ -369,6 +462,12 @@ public class ImsConfigImplBase { } /** + * The RCS autoconfiguration XML file is removed or invalid. + */ + public void notifyRcsAutoConfigurationRemoved() { + } + + /** * Sets the configuration value for this ImsService. * * @param item an integer key. @@ -421,4 +520,66 @@ public class ImsConfigImplBase { public void updateImsCarrierConfigs(PersistableBundle bundle) { // Base Implementation - Should be overridden } + + /** + * Default messaging application parameters are sent to the ACS client + * using this interface. + * @param rcc RCS client configuration {@link RcsClientConfiguration} + */ + public void setRcsClientConfiguration(@NonNull RcsClientConfiguration rcc) { + // Base Implementation - Should be overridden + } + + /** + * Reconfiguration triggered by the RCS application. Most likely cause + * is the 403 forbidden to a SIP/HTTP request + */ + public void triggerAutoConfiguration() { + // Base Implementation - Should be overridden + } + + /** + * Errors during autoconfiguration connection setup are notified by the + * ACS client using this interface. + * @param errorCode HTTP error received during connection setup. + * @param errorString reason phrase received with the error + */ + public final void notifyAutoConfigurationErrorReceived(int errorCode, + @NonNull String errorString) { + // can be null in testing + if (mRcsCallbacks == null) { + return; + } + mRcsCallbacks.broadcastAction(c -> { + try { + c.onAutoConfigurationErrorReceived(errorCode, errorString); + } catch (RemoteException e) { + Log.w(TAG, "dead binder in notifyAutoConfigurationErrorReceived, skipping."); + } + }); + } + + /** + * Notifies application that pre-provisioning config is received. + * + * <p>Some carriers using ACS (auto configuration server) may send a carrier-specific + * pre-provisioning configuration XML if the user has not been provisioned for RCS + * services yet. When such provisioning XML is received, ACS client must call this + * method to notify the application with the XML. + * + * @param configXml the pre-provisioning config in carrier specified format. + */ + public final void notifyPreProvisioningReceived(@NonNull byte[] configXml) { + // can be null in testing + if (mRcsCallbacks == null) { + return; + } + mRcsCallbacks.broadcastAction(c -> { + try { + c.onPreProvisioningReceived(configXml); + } catch (RemoteException e) { + Log.w(TAG, "dead binder in notifyPreProvisioningReceived, skipping."); + } + }); + } } diff --git a/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java b/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java index 06c35eaec6dd..8ad40ed1032c 100644 --- a/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java +++ b/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java @@ -23,6 +23,8 @@ import android.util.Log; import com.android.ims.internal.IImsEcbm; import com.android.ims.internal.IImsEcbmListener; +import java.util.Objects; + /** * Base implementation of ImsEcbm, which implements stub versions of the methods * in the IImsEcbm AIDL. Override the methods that your implementation of ImsEcbm supports. @@ -36,11 +38,31 @@ import com.android.ims.internal.IImsEcbmListener; public class ImsEcbmImplBase { private static final String TAG = "ImsEcbmImplBase"; + private final Object mLock = new Object(); private IImsEcbmListener mListener; - private IImsEcbm mImsEcbm = new IImsEcbm.Stub() { + private final IImsEcbm mImsEcbm = new IImsEcbm.Stub() { @Override public void setListener(IImsEcbmListener listener) { - mListener = listener; + synchronized (mLock) { + if (mListener != null && !mListener.asBinder().isBinderAlive()) { + Log.w(TAG, "setListener: discarding dead Binder"); + mListener = null; + } + if (mListener != null && listener != null && Objects.equals( + mListener.asBinder(), listener.asBinder())) { + return; + } + if (listener == null) { + mListener = null; + } else if (listener != null && mListener == null) { + mListener = listener; + } else { + // Warn that the listener is being replaced while active + Log.w(TAG, "setListener is being called when there is already an active " + + "listener"); + mListener = listener; + } + } } @Override @@ -69,9 +91,13 @@ public class ImsEcbmImplBase { */ public final void enteredEcbm() { Log.d(TAG, "Entered ECBM."); - if (mListener != null) { + IImsEcbmListener listener; + synchronized (mLock) { + listener = mListener; + } + if (listener != null) { try { - mListener.enteredECBM(); + listener.enteredECBM(); } catch (RemoteException e) { throw new RuntimeException(e); } @@ -85,9 +111,13 @@ public class ImsEcbmImplBase { */ public final void exitedEcbm() { Log.d(TAG, "Exited ECBM."); - if (mListener != null) { + IImsEcbmListener listener; + synchronized (mLock) { + listener = mListener; + } + if (listener != null) { try { - mListener.exitedECBM(); + listener.exitedECBM(); } catch (RemoteException e) { throw new RuntimeException(e); } diff --git a/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java b/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java index d002903a11b6..ec1c7b3a92a8 100644 --- a/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java +++ b/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java @@ -25,6 +25,7 @@ import com.android.ims.internal.IImsExternalCallStateListener; import com.android.ims.internal.IImsMultiEndpoint; import java.util.List; +import java.util.Objects; /** * Base implementation of ImsMultiEndpoint, which implements stub versions of the methods @@ -41,10 +42,32 @@ public class ImsMultiEndpointImplBase { private static final String TAG = "MultiEndpointImplBase"; private IImsExternalCallStateListener mListener; - private IImsMultiEndpoint mImsMultiEndpoint = new IImsMultiEndpoint.Stub() { + private final Object mLock = new Object(); + private final IImsMultiEndpoint mImsMultiEndpoint = new IImsMultiEndpoint.Stub() { + @Override public void setListener(IImsExternalCallStateListener listener) throws RemoteException { - mListener = listener; + synchronized (mLock) { + if (mListener != null && !mListener.asBinder().isBinderAlive()) { + Log.w(TAG, "setListener: discarding dead Binder"); + mListener = null; + } + if (mListener != null && listener != null && Objects.equals( + mListener.asBinder(), listener.asBinder())) { + return; + } + + if (listener == null) { + mListener = null; + } else if (listener != null && mListener == null) { + mListener = listener; + } else { + // Warn that the listener is being replaced while active + Log.w(TAG, "setListener is being called when there is already an active " + + "listener"); + mListener = listener; + } + } } @Override @@ -65,9 +88,13 @@ public class ImsMultiEndpointImplBase { */ public final void onImsExternalCallStateUpdate(List<ImsExternalCallState> externalCallDialogs) { Log.d(TAG, "ims external call state update triggered."); - if (mListener != null) { + IImsExternalCallStateListener listener; + synchronized (mLock) { + listener = mListener; + } + if (listener != null) { try { - mListener.onImsExternalCallStateUpdate(externalCallDialogs); + listener.onImsExternalCallStateUpdate(externalCallDialogs); } catch (RemoteException e) { throw new RuntimeException(e); } diff --git a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java index aff92b59a63d..02bcdec621c1 100644 --- a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java +++ b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java @@ -17,17 +17,21 @@ package android.telephony.ims.stub; import android.annotation.IntDef; +import android.annotation.IntRange; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.SystemApi; import android.net.Uri; import android.os.RemoteException; import android.telephony.ims.ImsReasonInfo; +import android.telephony.ims.ImsRegistrationAttributes; import android.telephony.ims.RegistrationManager; import android.telephony.ims.aidl.IImsRegistration; import android.telephony.ims.aidl.IImsRegistrationCallback; import android.util.Log; -import com.android.internal.annotations.VisibleForTesting; import com.android.internal.telephony.util.RemoteCallbackListExt; +import com.android.internal.util.ArrayUtils; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -35,6 +39,9 @@ import java.lang.annotation.RetentionPolicy; /** * Controls IMS registration for this ImsService and notifies the framework when the IMS * registration for this ImsService has changed status. + * <p> + * Note: There is no guarantee on the thread that the calls from the framework will be called on. It + * is the implementors responsibility to handle moving the calls to a working thread if required. * @hide */ @SystemApi @@ -46,11 +53,12 @@ public class ImsRegistrationImplBase { * @hide */ // Defines the underlying radio technology type that we have registered for IMS over. - @IntDef(flag = true, - value = { + @IntDef(value = { REGISTRATION_TECH_NONE, REGISTRATION_TECH_LTE, - REGISTRATION_TECH_IWLAN + REGISTRATION_TECH_IWLAN, + REGISTRATION_TECH_CROSS_SIM, + REGISTRATION_TECH_NR }) @Retention(RetentionPolicy.SOURCE) public @interface ImsRegistrationTech {} @@ -59,14 +67,24 @@ public class ImsRegistrationImplBase { */ public static final int REGISTRATION_TECH_NONE = -1; /** - * IMS is registered to IMS via LTE. + * This ImsService is registered to IMS via LTE. */ public static final int REGISTRATION_TECH_LTE = 0; /** - * IMS is registered to IMS via IWLAN. + * This ImsService is registered to IMS via IWLAN. */ public static final int REGISTRATION_TECH_IWLAN = 1; + /** + * This ImsService is registered to IMS via internet over second subscription. + */ + public static final int REGISTRATION_TECH_CROSS_SIM = 2; + + /** + * This ImsService is registered to IMS via NR. + */ + public static final int REGISTRATION_TECH_NR = 3; + // Registration states, used to notify new ImsRegistrationImplBase#Callbacks of the current // state. // The unknown state is set as the initialization state. This is so that we do not call back @@ -78,7 +96,10 @@ public class ImsRegistrationImplBase { @Override public @ImsRegistrationTech int getRegistrationTechnology() throws RemoteException { - return getConnectionType(); + synchronized (mLock) { + return (mRegistrationAttributes == null) ? REGISTRATION_TECH_NONE + : mRegistrationAttributes.getRegistrationTechnology(); + } } @Override @@ -90,19 +111,38 @@ public class ImsRegistrationImplBase { public void removeRegistrationCallback(IImsRegistrationCallback c) throws RemoteException { ImsRegistrationImplBase.this.removeRegistrationCallback(c); } + + @Override + public void triggerFullNetworkRegistration(int sipCode, String sipReason) { + ImsRegistrationImplBase.this.triggerFullNetworkRegistration(sipCode, sipReason); + } + + @Override + public void triggerUpdateSipDelegateRegistration() { + ImsRegistrationImplBase.this.updateSipDelegateRegistration(); + } + + @Override + public void triggerSipDelegateDeregistration() { + ImsRegistrationImplBase.this.triggerSipDelegateDeregistration(); + } }; private final RemoteCallbackListExt<IImsRegistrationCallback> mCallbacks = new RemoteCallbackListExt<>(); private final Object mLock = new Object(); // Locked on mLock - private @ImsRegistrationTech - int mConnectionType = REGISTRATION_TECH_NONE; + private ImsRegistrationAttributes mRegistrationAttributes; // Locked on mLock private int mRegistrationState = REGISTRATION_STATE_UNKNOWN; // Locked on mLock, create unspecified disconnect cause. private ImsReasonInfo mLastDisconnectCause = new ImsReasonInfo(); + // We hold onto the uris each time they change so that we can send it to a callback when its + // first added. + private Uri[] mUris = new Uri[0]; + private boolean mUrisSet = false; + /** * @hide */ @@ -120,19 +160,74 @@ public class ImsRegistrationImplBase { } /** + * Called by the framework to request that the ImsService perform the network registration + * of all SIP delegates associated with this ImsService. + * <p> + * If the SIP delegate feature tag configuration has changed, then this method will be + * called in order to let the ImsService know that it can pick up these changes in the IMS + * registration. + */ + public void updateSipDelegateRegistration() { + // Stub implementation, ImsService should implement this + } + + + /** + * Called by the framework to request that the ImsService perform the network deregistration of + * all SIP delegates associated with this ImsService. + * <p> + * This is typically called in situations where the user has changed the configuration of the + * device (for example, the default messaging application) and the framework is reconfiguring + * the tags associated with each IMS application. + * <p> + * This should not affect the registration of features managed by the ImsService itself, such as + * feature tags related to MMTEL registration. + */ + public void triggerSipDelegateDeregistration() { + // Stub implementation, ImsService should implement this + } + + /** + * Called by the framework to notify the ImsService that a SIP delegate connection has received + * a SIP message containing a permanent failure response (such as a 403) or an indication that a + * SIP response timer has timed out in response to an outgoing SIP message. This method will be + * called when this condition occurs to trigger the ImsService to tear down the full IMS + * registration and re-register again. + * + * @param sipCode The SIP error code that represents a permanent failure that was received in + * response to a request generated by the IMS application. See RFC3261 7.2 for the general + * classes of responses available here, however the codes that generate this condition may + * be carrier specific. + * @param sipReason The reason associated with the SIP error code. {@code null} if there was no + * reason associated with the error. + */ + public void triggerFullNetworkRegistration(@IntRange(from = 100, to = 699) int sipCode, + @Nullable String sipReason) { + // Stub implementation, ImsService should implement this + } + + + /** * Notify the framework that the device is connected to the IMS network. * - * @param imsRadioTech the radio access technology. Valid values are defined as - * {@link #REGISTRATION_TECH_LTE} and {@link #REGISTRATION_TECH_IWLAN}. + * @param imsRadioTech the radio access technology. */ public final void onRegistered(@ImsRegistrationTech int imsRadioTech) { - updateToState(imsRadioTech, RegistrationManager.REGISTRATION_STATE_REGISTERED); + onRegistered(new ImsRegistrationAttributes.Builder(imsRadioTech).build()); + } + + /** + * Notify the framework that the device is connected to the IMS network. + * + * @param attributes The attributes associated with the IMS registration. + */ + public final void onRegistered(@NonNull ImsRegistrationAttributes attributes) { + updateToState(attributes, RegistrationManager.REGISTRATION_STATE_REGISTERED); mCallbacks.broadcastAction((c) -> { try { - c.onRegistered(imsRadioTech); + c.onRegistered(attributes); } catch (RemoteException e) { - Log.w(LOG_TAG, e + " " + "onRegistrationConnected() - Skipping " + - "callback."); + Log.w(LOG_TAG, e + "onRegistered(int, Set) - Skipping callback."); } }); } @@ -140,17 +235,24 @@ public class ImsRegistrationImplBase { /** * Notify the framework that the device is trying to connect the IMS network. * - * @param imsRadioTech the radio access technology. Valid values are defined as - * {@link #REGISTRATION_TECH_LTE} and {@link #REGISTRATION_TECH_IWLAN}. + * @param imsRadioTech the radio access technology. */ public final void onRegistering(@ImsRegistrationTech int imsRadioTech) { - updateToState(imsRadioTech, RegistrationManager.REGISTRATION_STATE_REGISTERING); + onRegistering(new ImsRegistrationAttributes.Builder(imsRadioTech).build()); + } + + /** + * Notify the framework that the device is trying to connect the IMS network. + * + * @param attributes The attributes associated with the IMS registration. + */ + public final void onRegistering(@NonNull ImsRegistrationAttributes attributes) { + updateToState(attributes, RegistrationManager.REGISTRATION_STATE_REGISTERING); mCallbacks.broadcastAction((c) -> { try { - c.onRegistering(imsRadioTech); + c.onRegistering(attributes); } catch (RemoteException e) { - Log.w(LOG_TAG, e + " " + "onRegistrationProcessing() - Skipping " + - "callback."); + Log.w(LOG_TAG, e + "onRegistering(int, Set) - Skipping callback."); } }); } @@ -179,8 +281,7 @@ public class ImsRegistrationImplBase { try { c.onDeregistered(reasonInfo); } catch (RemoteException e) { - Log.w(LOG_TAG, e + " " + "onRegistrationDisconnected() - Skipping " + - "callback."); + Log.w(LOG_TAG, e + "onDeregistered() - Skipping callback."); } }); } @@ -189,7 +290,8 @@ public class ImsRegistrationImplBase { * Notify the framework that the handover from the current radio technology to the technology * defined in {@code imsRadioTech} has failed. * @param imsRadioTech The technology that has failed to be changed. Valid values are - * {@link #REGISTRATION_TECH_LTE} and {@link #REGISTRATION_TECH_IWLAN}. + * {@link #REGISTRATION_TECH_LTE}, {@link #REGISTRATION_TECH_IWLAN} and + * {@link #REGISTRATION_TECH_CROSS_SIM}. * @param info The {@link ImsReasonInfo} for the failure to change technology. */ public final void onTechnologyChangeFailed(@ImsRegistrationTech int imsRadioTech, @@ -199,31 +301,37 @@ public class ImsRegistrationImplBase { try { c.onTechnologyChangeFailed(imsRadioTech, reasonInfo); } catch (RemoteException e) { - Log.w(LOG_TAG, e + " " + "onRegistrationChangeFailed() - Skipping " + - "callback."); + Log.w(LOG_TAG, e + "onTechnologyChangeFailed() - Skipping callback."); } }); } /** - * The this device's subscriber associated {@link Uri}s have changed, which are used to filter - * out this device's {@link Uri}s during conference calling. - * @param uris + * Invoked when the {@link Uri}s associated to this device's subscriber have changed. + * These {@link Uri}s' are filtered out during conference calls. + * + * The {@link Uri}s are not guaranteed to be different between subsequent calls. + * @param uris changed uris */ public final void onSubscriberAssociatedUriChanged(Uri[] uris) { - mCallbacks.broadcastAction((c) -> { - try { - c.onSubscriberAssociatedUriChanged(uris); - } catch (RemoteException e) { - Log.w(LOG_TAG, e + " " + "onSubscriberAssociatedUriChanged() - Skipping " + - "callback."); - } - }); + synchronized (mLock) { + mUris = ArrayUtils.cloneOrNull(uris); + mUrisSet = true; + } + mCallbacks.broadcastAction((c) -> onSubscriberAssociatedUriChanged(c, uris)); } - private void updateToState(@ImsRegistrationTech int connType, int newState) { + private void onSubscriberAssociatedUriChanged(IImsRegistrationCallback callback, Uri[] uris) { + try { + callback.onSubscriberAssociatedUriChanged(uris); + } catch (RemoteException e) { + Log.w(LOG_TAG, e + "onSubscriberAssociatedUriChanged() - Skipping callback."); + } + } + + private void updateToState(ImsRegistrationAttributes attributes, int newState) { synchronized (mLock) { - mConnectionType = connType; + mRegistrationAttributes = attributes; mRegistrationState = newState; mLastDisconnectCause = null; } @@ -231,7 +339,11 @@ public class ImsRegistrationImplBase { private void updateToDisconnectedState(ImsReasonInfo info) { synchronized (mLock) { - updateToState(REGISTRATION_TECH_NONE, + //We don't want to send this info over if we are disconnected + mUrisSet = false; + mUris = null; + + updateToState(new ImsRegistrationAttributes.Builder(REGISTRATION_TECH_NONE).build(), RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED); if (info != null) { mLastDisconnectCause = info; @@ -243,27 +355,22 @@ public class ImsRegistrationImplBase { } /** - * @return the current registration connection type. Valid values are - * {@link #REGISTRATION_TECH_LTE} and {@link #REGISTRATION_TECH_IWLAN} - * @hide - */ - @VisibleForTesting - public final @ImsRegistrationTech int getConnectionType() { - synchronized (mLock) { - return mConnectionType; - } - } - - /** * @param c the newly registered callback that will be updated with the current registration * state. */ - private void updateNewCallbackWithState(IImsRegistrationCallback c) throws RemoteException { + private void updateNewCallbackWithState(IImsRegistrationCallback c) + throws RemoteException { int state; + ImsRegistrationAttributes attributes; ImsReasonInfo disconnectInfo; + boolean urisSet; + Uri[] uris; synchronized (mLock) { state = mRegistrationState; + attributes = mRegistrationAttributes; disconnectInfo = mLastDisconnectCause; + urisSet = mUrisSet; + uris = mUris; } switch (state) { case RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED: { @@ -271,11 +378,11 @@ public class ImsRegistrationImplBase { break; } case RegistrationManager.REGISTRATION_STATE_REGISTERING: { - c.onRegistering(getConnectionType()); + c.onRegistering(attributes); break; } case RegistrationManager.REGISTRATION_STATE_REGISTERED: { - c.onRegistered(getConnectionType()); + c.onRegistered(attributes); break; } case REGISTRATION_STATE_UNKNOWN: { @@ -283,5 +390,8 @@ public class ImsRegistrationImplBase { break; } } + if (urisSet) { + onSubscriberAssociatedUriChanged(c, uris); + } } } diff --git a/telephony/java/android/telephony/ims/stub/ImsSmsImplBase.java b/telephony/java/android/telephony/ims/stub/ImsSmsImplBase.java index 36ece958d501..2783e299236b 100644 --- a/telephony/java/android/telephony/ims/stub/ImsSmsImplBase.java +++ b/telephony/java/android/telephony/ims/stub/ImsSmsImplBase.java @@ -401,8 +401,7 @@ public class ImsSmsImplBase { message.mWrappedSmsMessage.mMessageRef, STATUS_REPORT_STATUS_ERROR); } else { - Log.w(LOG_TAG, - "onSmsStatusReportReceivedWithoutMessageRef: Invalid pdu entered."); + Log.w(LOG_TAG, "onSmsStatusReportReceived: Invalid pdu entered."); acknowledgeSmsReport(token, 0, STATUS_REPORT_STATUS_ERROR); } } diff --git a/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java b/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java index f5219d5b49e8..eb3e8ed5a8e4 100644 --- a/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java +++ b/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java @@ -23,12 +23,14 @@ import android.annotation.SystemApi; import android.os.Bundle; import android.os.RemoteException; import android.telephony.ims.ImsUtListener; +import android.util.Log; import com.android.ims.internal.IImsUt; import com.android.ims.internal.IImsUtListener; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.Objects; /** * Base implementation of IMS UT interface, which implements stubs. Override these methods to @@ -40,6 +42,7 @@ import java.lang.annotation.RetentionPolicy; // will break other implementations of ImsUt maintained by other ImsServices. @SystemApi public class ImsUtImplBase { + private static final String TAG = "ImsUtImplBase"; /** * Bar all incoming calls. (See 3GPP TS 24.611) * @hide @@ -116,7 +119,10 @@ public class ImsUtImplBase { */ public static final int INVALID_RESULT = -1; - private IImsUt.Stub mServiceImpl = new IImsUt.Stub() { + private final IImsUt.Stub mServiceImpl = new IImsUt.Stub() { + private final Object mLock = new Object(); + private ImsUtListener mUtListener; + @Override public void close() throws RemoteException { ImsUtImplBase.this.close(); @@ -202,7 +208,30 @@ public class ImsUtImplBase { @Override public void setListener(IImsUtListener listener) throws RemoteException { - ImsUtImplBase.this.setListener(new ImsUtListener(listener)); + synchronized (mLock) { + if (mUtListener != null + && !mUtListener.getListenerInterface().asBinder().isBinderAlive()) { + Log.w(TAG, "setListener: discarding dead Binder"); + mUtListener = null; + } + if (mUtListener != null && listener != null && Objects.equals( + mUtListener.getListenerInterface().asBinder(), listener.asBinder())) { + return; + } + + if (listener == null) { + mUtListener = null; + } else if (listener != null && mUtListener == null) { + mUtListener = new ImsUtListener(listener); + } else { + // Warn that the listener is being replaced while active + Log.w(TAG, "setListener is being called when there is already an active " + + "listener"); + mUtListener = new ImsUtListener(listener); + } + } + + ImsUtImplBase.this.setListener(mUtListener); } @Override diff --git a/telephony/java/android/telephony/ims/stub/OWNERS b/telephony/java/android/telephony/ims/stub/OWNERS new file mode 100644 index 000000000000..0854c5d45603 --- /dev/null +++ b/telephony/java/android/telephony/ims/stub/OWNERS @@ -0,0 +1,5 @@ +# Bug component: 20868 + +rgreenwalt@google.com +tgunn@google.com +breadley@google.com diff --git a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchange.java b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchange.java deleted file mode 100644 index fda295a27111..000000000000 --- a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchange.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright (C) 2018 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.ims.stub; - -import android.annotation.IntDef; -import android.os.RemoteException; -import android.telephony.ims.ImsException; -import android.telephony.ims.aidl.IRcsFeatureListener; -import android.telephony.ims.feature.ImsFeature; -import android.telephony.ims.feature.RcsFeature; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * Base class for different types of Capability exchange, presence using - * {@link RcsPresenceExchangeImplBase} and SIP OPTIONS exchange using {@link RcsSipOptionsImplBase}. - * - * @hide - */ -public class RcsCapabilityExchange { - - /** Service is unknown. */ - public static final int COMMAND_CODE_SERVICE_UNKNOWN = 0; - /** The command completed successfully. */ - public static final int COMMAND_CODE_SUCCESS = 1; - /** The command failed with an unknown error. */ - public static final int COMMAND_CODE_GENERIC_FAILURE = 2; - /** Invalid parameter(s). */ - public static final int COMMAND_CODE_INVALID_PARAM = 3; - /** Fetch error. */ - public static final int COMMAND_CODE_FETCH_ERROR = 4; - /** Request timed out. */ - public static final int COMMAND_CODE_REQUEST_TIMEOUT = 5; - /** Failure due to insufficient memory available. */ - public static final int COMMAND_CODE_INSUFFICIENT_MEMORY = 6; - /** Network connection is lost. */ - public static final int COMMAND_CODE_LOST_NETWORK_CONNECTION = 7; - /** Requested feature/resource is not supported. */ - public static final int COMMAND_CODE_NOT_SUPPORTED = 8; - /** Contact or resource is not found. */ - public static final int COMMAND_CODE_NOT_FOUND = 9; - /** Service is not available. */ - public static final int COMMAND_CODE_SERVICE_UNAVAILABLE = 10; - /** No Change in Capabilities */ - public static final int COMMAND_CODE_NO_CHANGE_IN_CAP = 11; - - /** @hide*/ - @Retention(RetentionPolicy.SOURCE) - @IntDef(prefix = "COMMAND_CODE_", value = { - COMMAND_CODE_SERVICE_UNKNOWN, - COMMAND_CODE_SUCCESS, - COMMAND_CODE_GENERIC_FAILURE, - COMMAND_CODE_INVALID_PARAM, - COMMAND_CODE_FETCH_ERROR, - COMMAND_CODE_REQUEST_TIMEOUT, - COMMAND_CODE_INSUFFICIENT_MEMORY, - COMMAND_CODE_LOST_NETWORK_CONNECTION, - COMMAND_CODE_NOT_SUPPORTED, - COMMAND_CODE_NOT_FOUND, - COMMAND_CODE_SERVICE_UNAVAILABLE, - COMMAND_CODE_NO_CHANGE_IN_CAP - }) - public @interface CommandCode {} - - - private RcsFeature mFeature; - - /** @hide */ - public final void initialize(RcsFeature feature) { - mFeature = feature; - } - - /** @hide */ - protected final IRcsFeatureListener getListener() throws ImsException { - IRcsFeatureListener listener = mFeature.getListener(); - if (listener == null) { - throw new ImsException("Connection to Framework has not been established, wait for " - + "onFeatureReady().", ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); - } - return mFeature.getListener(); - } - - /** - * Provides the framework with an update as to whether or not a command completed successfully - * locally. This includes capabilities requests and updates from the network. If it does not - * complete successfully, then the framework may retry the command again later, depending on the - * error. If the command does complete successfully, the framework will then wait for network - * updates. - * - * @param code The result of the pending command. If {@link #COMMAND_CODE_SUCCESS}, further - * updates will be sent for this command using the associated operationToken. - * @param operationToken the token associated with the pending command. - * @throws ImsException If this {@link RcsCapabilityExchange} instance is not currently - * connected to the framework. This can happen if the {@link RcsFeature} is not - * {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received the - * {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare cases when the - * Telephony stack has crashed. - */ - public final void onCommandUpdate(@CommandCode int code, int operationToken) - throws ImsException { - try { - getListener().onCommandUpdate(code, operationToken); - } catch (RemoteException e) { - throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); - } - } -} diff --git a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java new file mode 100644 index 000000000000..474eb05ca19c --- /dev/null +++ b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java @@ -0,0 +1,442 @@ +/* + * Copyright (c) 2020 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.ims.stub; + +import android.annotation.IntDef; +import android.annotation.IntRange; +import android.annotation.NonNull; +import android.annotation.SuppressLint; +import android.annotation.SystemApi; +import android.net.Uri; +import android.telephony.ims.ImsException; +import android.telephony.ims.RcsUceAdapter; +import android.telephony.ims.feature.ImsFeature; +import android.telephony.ims.feature.RcsFeature; +import android.util.Log; +import android.util.Pair; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Collection; +import java.util.List; +import java.util.Set; +import java.util.concurrent.Executor; + +/** + * Extend this base class to implement RCS User Capability Exchange (UCE) for the AOSP platform + * using the vendor ImsService. + * <p> + * See RCC.07 for more details on UCE as well as how UCE should be implemented. + * @hide + */ +@SystemApi +public class RcsCapabilityExchangeImplBase { + + private static final String LOG_TAG = "RcsCapExchangeImplBase"; + + /** + * Service is unknown. + */ + public static final int COMMAND_CODE_SERVICE_UNKNOWN = 0; + + /** + * The command failed with an unknown error. + */ + public static final int COMMAND_CODE_GENERIC_FAILURE = 1; + + /** + * Invalid parameter(s). + */ + public static final int COMMAND_CODE_INVALID_PARAM = 2; + + /** + * Fetch error. + */ + public static final int COMMAND_CODE_FETCH_ERROR = 3; + + /** + * Request timed out. + */ + public static final int COMMAND_CODE_REQUEST_TIMEOUT = 4; + + /** + * Failure due to insufficient memory available. + */ + public static final int COMMAND_CODE_INSUFFICIENT_MEMORY = 5; + + /** + * Network connection is lost. + */ + public static final int COMMAND_CODE_LOST_NETWORK_CONNECTION = 6; + + /** + * Requested feature/resource is not supported. + */ + public static final int COMMAND_CODE_NOT_SUPPORTED = 7; + + /** + * Contact or resource is not found. + */ + public static final int COMMAND_CODE_NOT_FOUND = 8; + + /** + * Service is not available. + */ + public static final int COMMAND_CODE_SERVICE_UNAVAILABLE = 9; + + /** + * Command resulted in no change in state, ignoring. + */ + public static final int COMMAND_CODE_NO_CHANGE = 10; + + /**@hide*/ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = "COMMAND_CODE_", value = { + COMMAND_CODE_SERVICE_UNKNOWN, + COMMAND_CODE_GENERIC_FAILURE, + COMMAND_CODE_INVALID_PARAM, + COMMAND_CODE_FETCH_ERROR, + COMMAND_CODE_REQUEST_TIMEOUT, + COMMAND_CODE_INSUFFICIENT_MEMORY, + COMMAND_CODE_LOST_NETWORK_CONNECTION, + COMMAND_CODE_NOT_SUPPORTED, + COMMAND_CODE_NOT_FOUND, + COMMAND_CODE_SERVICE_UNAVAILABLE, + COMMAND_CODE_NO_CHANGE + }) + public @interface CommandCode {} + + /** + * Interface used by the framework to receive the response of the publish request. + */ + public interface PublishResponseCallback { + /** + * Notify the framework that the command associated with the + * {@link #publishCapabilities(String, PublishResponseCallback)} has failed. + * + * @param code The reason why the associated command has failed. + * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is + * not currently connected to the framework. This can happen if the {@link RcsFeature} + * is not {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received + * the {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare cases + * when the Telephony stack has crashed. + */ + void onCommandError(@CommandCode int code) throws ImsException; + + /** + * Provide the framework with a subsequent network response update to + * {@link #publishCapabilities(String, PublishResponseCallback)}. + * + * If this network response also contains a “Reason” header, then the + * {@link #onNetworkResponse(int, String, int, String)} method should be used instead. + * + * @param sipCode The SIP response code sent from the network for the operation + * token specified. + * @param reason The optional reason response from the network. If there is a reason header + * included in the response, that should take precedence over the reason provided in the + * status line. If the network provided no reason with the sip code, the string should be + * empty. + * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is + * not currently connected to the framework. This can happen if the {@link RcsFeature} + * is not {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received + * the {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare cases + * when the Telephony stack has crashed. + */ + void onNetworkResponse(@IntRange(from = 100, to = 699) int sipCode, + @NonNull String reason) throws ImsException; + + /** + * Provide the framework with a subsequent network response update to + * {@link #publishCapabilities(String, PublishResponseCallback)} that also + * includes a reason provided in the “reason” header. See RFC3326 for more + * information. + * + * @param sipCode The SIP response code sent from the network. + * @param reasonPhrase The optional reason response from the network. If the + * network provided no reason with the sip code, the string should be empty. + * @param reasonHeaderCause The “cause” parameter of the “reason” header + * included in the SIP message. + * @param reasonHeaderText The “text” parameter of the “reason” header + * included in the SIP message. + * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is + * not currently connected to the framework. This can happen if the + * {@link RcsFeature} is not + * {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received + * the {@link ImsFeature#onFeatureReady()} callback. This may also happen in + * rare cases when the Telephony stack has crashed. + */ + void onNetworkResponse(@IntRange(from = 100, to = 699) int sipCode, + @NonNull String reasonPhrase, + @IntRange(from = 100, to = 699) int reasonHeaderCause, + @NonNull String reasonHeaderText) throws ImsException; + } + + /** + * Interface used by the framework to respond to OPTIONS requests. + */ + public interface OptionsResponseCallback { + /** + * Notify the framework that the command associated with this callback has failed. + * + * @param code The reason why the associated command has failed. + * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is + * not currently connected to the framework. This can happen if the + * {@link RcsFeature} is not {@link ImsFeature#STATE_READY} and the {@link RcsFeature} + * has not received the {@link ImsFeature#onFeatureReady()} callback. This may also happen + * in rare cases when the Telephony stack has crashed. + */ + void onCommandError(@CommandCode int code) throws ImsException; + + /** + * Send the response of a SIP OPTIONS capability exchange to the framework. + * @param sipCode The SIP response code that was sent by the network in response + * to the request sent by {@link #sendOptionsCapabilityRequest}. + * @param reason The optional SIP response reason sent by the network. + * If none was sent, this should be an empty string. + * @param theirCaps the contact's UCE capabilities associated with the + * capability request. + * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is not + * currently connected to the framework. This can happen if the + * {@link RcsFeature} is not {@link ImsFeature#STATE_READY} and the + * {@link RcsFeature} has not received the + * {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare + * cases when the Telephony stack has crashed. + */ + void onNetworkResponse(int sipCode, @NonNull String reason, + @NonNull List<String> theirCaps) throws ImsException; + } + + /** + * Interface used by the framework to receive the response of the subscribe request. + */ + public interface SubscribeResponseCallback { + /** + * Notify the framework that the command associated with this callback has failed. + * <p> + * Must only be called when there was an error generating a SUBSCRIBE request due to an + * IMS stack error. This is a terminating event, so no other callback event will be + * expected after this callback. + * + * @param code The reason why the associated command has failed. + * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is + * not currently connected to the framework. This can happen if the + * {@link RcsFeature} is not + * {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received + * the {@link ImsFeature#onFeatureReady()} callback. This may also happen in + * rare cases when the Telephony stack has crashed. + */ + void onCommandError(@CommandCode int code) throws ImsException; + + /** + * Notify the framework of the response to the SUBSCRIBE request from + * {@link #subscribeForCapabilities(Collection, SubscribeResponseCallback)}. + * <p> + * If the carrier network responds to the SUBSCRIBE request with a 2XX response, then the + * framework will expect the IMS stack to call {@link #onNotifyCapabilitiesUpdate}, + * {@link #onResourceTerminated}, and {@link #onTerminated} as required for the + * subsequent NOTIFY responses to the subscription. + * + * If this network response also contains a “Reason” header, then the + * {@link #onNetworkResponse(int, String, int, String)} method should be used instead. + * + * @param sipCode The SIP response code sent from the network for the operation + * token specified. + * @param reason The optional reason response from the network. If the network + * provided no reason with the sip code, the string should be empty. + * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is + * not currently connected to the framework. This can happen if the + * {@link RcsFeature} is not {@link ImsFeature#STATE_READY} and the + * {@link RcsFeature} has not received the {@link ImsFeature#onFeatureReady()} callback. + * This may also happen in rare cases when the Telephony stack has crashed. + */ + void onNetworkResponse(@IntRange(from = 100, to = 699) int sipCode, + @NonNull String reason) throws ImsException; + + /** + * Notify the framework of the response to the SUBSCRIBE request from + * {@link #subscribeForCapabilities(Collection, SubscribeResponseCallback)} that also + * includes a reason provided in the “reason” header. See RFC3326 for more + * information. + * + * @param sipCode The SIP response code sent from the network, + * @param reasonPhrase The optional reason response from the network. If the + * network provided no reason with the sip code, the string should be empty. + * @param reasonHeaderCause The “cause” parameter of the “reason” header + * included in the SIP message. + * @param reasonHeaderText The “text” parameter of the “reason” header + * included in the SIP message. + * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is + * not currently connected to the framework. This can happen if the + * {@link RcsFeature} is not + * {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received + * the {@link ImsFeature#onFeatureReady()} callback. This may also happen in + * rare cases when the Telephony stack has crashed. + */ + void onNetworkResponse(@IntRange(from = 100, to = 699) int sipCode, + @NonNull String reasonPhrase, + @IntRange(from = 100, to = 699) int reasonHeaderCause, + @NonNull String reasonHeaderText) throws ImsException; + + /** + * Notify the framework of the latest XML PIDF documents included in the network response + * for the requested contacts' capabilities requested by the Framework using + * {@link RcsUceAdapter#requestCapabilities(List, Executor, + * RcsUceAdapter.CapabilitiesCallback)}. + * <p> + * The expected format for the PIDF XML is defined in RFC3861. Each XML document must be a + * "application/pidf+xml" object and start with a root <presence> element. For NOTIFY + * responses that contain RLMI information and potentially multiple PIDF XMLs, each + * PIDF XML should be separated and added as a separate item in the List. This should be + * called every time a new NOTIFY event is received with new capability information. + * + * @param pidfXmls The list of the PIDF XML data for the contact URIs that it subscribed + * for. + * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is + * not currently connected to the framework. + * This can happen if the {@link RcsFeature} is not {@link ImsFeature#STATE_READY} and the + * {@link RcsFeature} {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not + * received the {@link ImsFeature#onFeatureReady()} callback. This may also happen in + * rare cases when the Telephony stack has crashed. + */ + void onNotifyCapabilitiesUpdate(@NonNull List<String> pidfXmls) throws ImsException; + + /** + * Notify the framework that a resource in the RLMI XML contained in the NOTIFY response + * for the ongoing SUBSCRIBE dialog has been terminated. + * <p> + * This will be used to notify the framework that a contact URI that the IMS stack has + * subscribed to on the Resource List Server has been terminated as well as the reason why. + * Usually this means that there will not be any capability information for the contact URI + * that they subscribed for. See RFC 4662 for more information. + * + * @param uriTerminatedReason The contact URIs which have been terminated. Each pair in the + * list is the contact URI and its terminated reason. + * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is + * not currently connected to the framework. + * This can happen if the {@link RcsFeature} is not {@link ImsFeature#STATE_READY} and the + * {@link RcsFeature} {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not + * received the {@link ImsFeature#onFeatureReady()} callback. This may also happen in + * rare cases when the Telephony stack has crashed. + */ + void onResourceTerminated( + @NonNull List<Pair<Uri, String>> uriTerminatedReason) throws ImsException; + + /** + * The subscription associated with a previous + * {@link RcsUceAdapter#requestCapabilities(List, Executor, + * RcsUceAdapter.CapabilitiesCallback)} + * operation has been terminated. This will mostly be due to the network sending a final + * NOTIFY response due to the subscription expiring, but this may also happen due to a + * network error. + * + * @param reason The reason for the request being unable to process. + * @param retryAfterMilliseconds The time in milliseconds the requesting application should + * wait before retrying, if non-zero. + * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is + * not currently connected to the framework. + * This can happen if the {@link RcsFeature} is not {@link ImsFeature#STATE_READY} and the + * {@link RcsFeature} {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not + * received the {@link ImsFeature#onFeatureReady()} callback. This may also happen in + * rare cases when the Telephony stack has crashed. + */ + void onTerminated(@NonNull String reason, long retryAfterMilliseconds) throws ImsException; + } + + /** + * Create a new RcsCapabilityExchangeImplBase instance. + */ + public RcsCapabilityExchangeImplBase() { + } + + /** + * The user capabilities of one or multiple contacts have been requested by the framework. + * <p> + * The implementer must follow up this call with an + * {@link SubscribeResponseCallback#onCommandError} call to indicate this operation has failed. + * The response from the network to the SUBSCRIBE request must be sent back to the framework + * using {@link SubscribeResponseCallback#onNetworkResponse(int, String)}. + * As NOTIFY requests come in from the network, the requested contact’s capabilities should be + * sent back to the framework using + * {@link SubscribeResponseCallback#onNotifyCapabilitiesUpdate(List<String>}) and + * {@link SubscribeResponseCallback#onResourceTerminated(List<Pair<Uri, String>>)} + * should be called with the presence information for the contacts specified. + * <p> + * Once the subscription is terminated, + * {@link SubscribeResponseCallback#onTerminated(String, long)} must be called for the + * framework to finish listening for NOTIFY responses. + * + * @param uris A {@link Collection} of the {@link Uri}s that the framework is requesting the + * UCE capabilities for. + * @param cb The callback of the subscribe request. + */ + // executor used is defined in the constructor. + @SuppressLint("ExecutorRegistration") + public void subscribeForCapabilities(@NonNull Collection<Uri> uris, + @NonNull SubscribeResponseCallback cb) { + // Stub - to be implemented by service + Log.w(LOG_TAG, "subscribeForCapabilities called with no implementation."); + try { + cb.onCommandError(COMMAND_CODE_NOT_SUPPORTED); + } catch (ImsException e) { + // Do not do anything, this is a stub implementation. + } + } + + /** + * The capabilities of this device have been updated and should be published to the network. + * <p> + * If this operation succeeds, network response updates should be sent to the framework using + * {@link PublishResponseCallback#onNetworkResponse(int, String)}. + * @param pidfXml The XML PIDF document containing the capabilities of this device to be sent + * to the carrier’s presence server. + * @param cb The callback of the publish request + */ + // executor used is defined in the constructor. + @SuppressLint("ExecutorRegistration") + public void publishCapabilities(@NonNull String pidfXml, @NonNull PublishResponseCallback cb) { + // Stub - to be implemented by service + Log.w(LOG_TAG, "publishCapabilities called with no implementation."); + try { + cb.onCommandError(COMMAND_CODE_NOT_SUPPORTED); + } catch (ImsException e) { + // Do not do anything, this is a stub implementation. + } + } + + /** + * Push one's own capabilities to a remote user via the SIP OPTIONS presence exchange mechanism + * in order to receive the capabilities of the remote user in response. + * <p> + * The implementer must use {@link OptionsResponseCallback} to send the response of + * this query from the network back to the framework. + * @param contactUri The URI of the remote user that we wish to get the capabilities of. + * @param myCapabilities The capabilities of this device to send to the remote user. + * @param callback The callback of this request which is sent from the remote user. + */ + // executor used is defined in the constructor. + @SuppressLint("ExecutorRegistration") + public void sendOptionsCapabilityRequest(@NonNull Uri contactUri, + @NonNull Set<String> myCapabilities, @NonNull OptionsResponseCallback callback) { + // Stub - to be implemented by service + Log.w(LOG_TAG, "sendOptionsCapabilityRequest called with no implementation."); + try { + callback.onCommandError(COMMAND_CODE_NOT_SUPPORTED); + } catch (ImsException e) { + // Do not do anything, this is a stub implementation. + } + } +} diff --git a/telephony/java/android/telephony/ims/stub/RcsPresenceExchangeImplBase.java b/telephony/java/android/telephony/ims/stub/RcsPresenceExchangeImplBase.java deleted file mode 100644 index bb034489a296..000000000000 --- a/telephony/java/android/telephony/ims/stub/RcsPresenceExchangeImplBase.java +++ /dev/null @@ -1,294 +0,0 @@ -/* - * Copyright (C) 2018 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.ims.stub; - -import android.annotation.IntDef; -import android.annotation.NonNull; -import android.net.Uri; -import android.os.RemoteException; -import android.telephony.ims.ImsException; -import android.telephony.ims.RcsContactUceCapability; -import android.telephony.ims.feature.ImsFeature; -import android.telephony.ims.feature.RcsFeature; -import android.util.Log; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.util.List; - -/** - * Base implementation for RCS User Capability Exchange using Presence. Any ImsService implementing - * this service must implement the stub methods {@link #requestCapabilities(List, int)} and - * {@link #updateCapabilities(RcsContactUceCapability, int)}. - * - * @hide - */ -public class RcsPresenceExchangeImplBase extends RcsCapabilityExchange { - - private static final String LOG_TAG = "RcsPresenceExchangeIB"; - - /** - * The request has resulted in any other 4xx/5xx/6xx that is not covered below. No retry will be - * attempted. - */ - public static final int RESPONSE_SUBSCRIBE_GENERIC_FAILURE = -1; - - /** - * The request has succeeded with a “200” message from the network. - */ - public static final int RESPONSE_SUCCESS = 0; - - /** - * The request has resulted in a “403” (User Not Registered) error from the network. Will retry - * capability polling with an exponential backoff. - */ - public static final int RESPONSE_NOT_REGISTERED = 1; - - /** - * The request has resulted in a “403” (not authorized (Requestor)) error from the network. No - * retry will be attempted. - */ - public static final int RESPONSE_NOT_AUTHORIZED_FOR_PRESENCE = 2; - - /** - * The request has resulted in a "403” (Forbidden) or other “403” error from the network and - * will be handled the same as “404” Not found. No retry will be attempted. - */ - public static final int RESPONSE_FORBIDDEN = 3; - - /** - * The request has resulted in a “404” (Not found) result from the network. No retry will be - * attempted. - */ - public static final int RESPONSE_NOT_FOUND = 4; - - /** - * The request has resulted in a “408” response. Retry after exponential backoff. - */ - public static final int RESPONSE_SIP_REQUEST_TIMEOUT = 5; - - /** - * The network has responded with a “413” (Too Large) response from the network. Capability - * request contains too many items and must be shrunk before the request will be accepted. - */ - public static final int RESPONSE_SUBSCRIBE_TOO_LARGE = 6; - - /** - * The request has resulted in a “423” response. Retry after exponential backoff. - */ - public static final int RESPONSE_SIP_INTERVAL_TOO_SHORT = 7; - - /** - * The request has resulted in a “503” response. Retry after exponential backoff. - */ - public static final int RESPONSE_SIP_SERVICE_UNAVAILABLE = 8; - - /** @hide*/ - @Retention(RetentionPolicy.SOURCE) - @IntDef(prefix = "RESPONSE_", value = { - RESPONSE_SUBSCRIBE_GENERIC_FAILURE, - RESPONSE_SUCCESS, - RESPONSE_NOT_REGISTERED, - RESPONSE_NOT_AUTHORIZED_FOR_PRESENCE, - RESPONSE_FORBIDDEN, - RESPONSE_NOT_FOUND, - RESPONSE_SIP_REQUEST_TIMEOUT, - RESPONSE_SUBSCRIBE_TOO_LARGE, - RESPONSE_SIP_INTERVAL_TOO_SHORT, - RESPONSE_SIP_SERVICE_UNAVAILABLE - }) - public @interface PresenceResponseCode {} - - - /** A capability update has been requested due to the Entity Tag (ETag) expiring. */ - public static final int CAPABILITY_UPDATE_TRIGGER_ETAG_EXPIRED = 0; - /** A capability update has been requested due to moving to LTE with VoPS disabled. */ - public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_LTE_VOPS_DISABLED = 1; - /** A capability update has been requested due to moving to LTE with VoPS enabled. */ - public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_LTE_VOPS_ENABLED = 2; - /** A capability update has been requested due to moving to eHRPD. */ - public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_EHRPD = 3; - /** A capability update has been requested due to moving to HSPA+. */ - public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_HSPAPLUS = 4; - /** A capability update has been requested due to moving to 3G. */ - public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_3G = 5; - /** A capability update has been requested due to moving to 2G. */ - public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_2G = 6; - /** A capability update has been requested due to moving to WLAN */ - public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_WLAN = 7; - /** A capability update has been requested due to moving to IWLAN */ - public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_IWLAN = 8; - /** A capability update has been requested but the reason is unknown. */ - public static final int CAPABILITY_UPDATE_TRIGGER_UNKNOWN = 9; - /** A capability update has been requested due to moving to 5G NR with VoPS disabled. */ - public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_NR5G_VOPS_DISABLED = 10; - /** A capability update has been requested due to moving to 5G NR with VoPS enabled. */ - public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_NR5G_VOPS_ENABLED = 11; - - /** @hide*/ - @IntDef(value = { - CAPABILITY_UPDATE_TRIGGER_ETAG_EXPIRED, - CAPABILITY_UPDATE_TRIGGER_MOVE_TO_LTE_VOPS_DISABLED, - CAPABILITY_UPDATE_TRIGGER_MOVE_TO_LTE_VOPS_ENABLED, - CAPABILITY_UPDATE_TRIGGER_MOVE_TO_EHRPD, - CAPABILITY_UPDATE_TRIGGER_MOVE_TO_HSPAPLUS, - CAPABILITY_UPDATE_TRIGGER_MOVE_TO_3G, - CAPABILITY_UPDATE_TRIGGER_MOVE_TO_2G, - CAPABILITY_UPDATE_TRIGGER_MOVE_TO_WLAN, - CAPABILITY_UPDATE_TRIGGER_MOVE_TO_IWLAN, - CAPABILITY_UPDATE_TRIGGER_UNKNOWN, - CAPABILITY_UPDATE_TRIGGER_MOVE_TO_NR5G_VOPS_DISABLED, - CAPABILITY_UPDATE_TRIGGER_MOVE_TO_NR5G_VOPS_ENABLED - }, prefix = "CAPABILITY_UPDATE_TRIGGER_") - @Retention(RetentionPolicy.SOURCE) - public @interface StackPublishTriggerType { - } - - /** - * Provide the framework with a subsequent network response update to - * {@link #updateCapabilities(RcsContactUceCapability, int)} and - * {@link #requestCapabilities(List, int)} operations. - * - * @param code The SIP response code sent from the network for the operation token specified. - * @param reason The optional reason response from the network. If the network provided no - * reason with the code, the string should be empty. - * @param operationToken The token associated with the operation this service is providing a - * response for. - * @throws ImsException If this {@link RcsPresenceExchangeImplBase} instance is not currently - * connected to the framework. This can happen if the {@link RcsFeature} is not - * {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received the - * {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare cases when the - * Telephony stack has crashed. - */ - public final void onNetworkResponse(@PresenceResponseCode int code, @NonNull String reason, - int operationToken) throws ImsException { - try { - getListener().onNetworkResponse(code, reason, operationToken); - } catch (RemoteException e) { - throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); - } - } - - /** - * Provides the framework with the requested contacts’ capabilities requested by the framework - * using {@link #requestCapabilities(List, int)}. - * - * @throws ImsException If this {@link RcsPresenceExchangeImplBase} instance is not currently - * connected to the framework. This can happen if the {@link RcsFeature} is not - * {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received the - * {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare cases when the - * Telephony stack has crashed. - */ - public final void onCapabilityRequestResponse(@NonNull List<RcsContactUceCapability> infos, - int operationToken) throws ImsException { - try { - getListener().onCapabilityRequestResponsePresence(infos, operationToken); - } catch (RemoteException e) { - throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); - } - } - - /** - * Trigger the framework to provide a capability update using - * {@link #updateCapabilities(RcsContactUceCapability, int)}. - * <p> - * This is typically used when trying to generate an initial PUBLISH for a new subscription to - * the network. The device will cache all presence publications after boot until this method is - * called once. - * @param publishTriggerType {@link StackPublishTriggerType} The reason for the capability - * update request. - * @throws ImsException If this {@link RcsPresenceExchangeImplBase} instance is not currently - * connected to the framework. This can happen if the {@link RcsFeature} is not - * {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received the - * {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare cases when the - * Telephony stack has crashed. - */ - public final void onNotifyUpdateCapabilites(@StackPublishTriggerType int publishTriggerType) - throws ImsException { - try { - getListener().onNotifyUpdateCapabilities(publishTriggerType); - } catch (RemoteException e) { - throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); - } - } - - /** - * Notify the framework that the device’s capabilities have been unpublished from the network. - * - * @throws ImsException If this {@link RcsPresenceExchangeImplBase} instance is not currently - * connected to the framework. This can happen if the {@link RcsFeature} is not - * {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received the - * {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare cases when the - * Telephony stack has crashed. - */ - public final void onUnpublish() throws ImsException { - try { - getListener().onUnpublish(); - } catch (RemoteException e) { - throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); - } - } - - /** - * The user capabilities of one or multiple contacts have been requested by the framework. - * <p> - * The implementer must follow up this call with an {@link #onCommandUpdate(int, int)} call to - * indicate whether or not this operation succeeded. If this operation succeeds, network - * response updates should be sent to the framework using - * {@link #onNetworkResponse(int, String, int)}. When the operation is completed, - * {@link #onCapabilityRequestResponse(List, int)} should be called with the presence - * information for the contacts specified. - * @param uris A {@link List} of the {@link Uri}s that the framework is requesting the UCE - * capabilities for. - * @param operationToken The token associated with this operation. Updates to this request using - * {@link #onCommandUpdate(int, int)}, {@link #onNetworkResponse(int, String, int)}, and - * {@link #onCapabilityRequestResponse(List, int)} must use the same operation token - * in response. - */ - public void requestCapabilities(@NonNull List<Uri> uris, int operationToken) { - // Stub - to be implemented by service - Log.w(LOG_TAG, "requestCapabilities called with no implementation."); - try { - getListener().onCommandUpdate(COMMAND_CODE_NOT_SUPPORTED, operationToken); - } catch (RemoteException | ImsException e) { - // Do not do anything, this is a stub implementation. - } - } - - /** - * The capabilities of this device have been updated and should be published to the network. - * <p> - * The implementer must follow up this call with an {@link #onCommandUpdate(int, int)} call to - * indicate whether or not this operation succeeded. If this operation succeeds, network - * response updates should be sent to the framework using - * {@link #onNetworkResponse(int, String, int)}. - * @param capabilities The capabilities for this device. - * @param operationToken The token associated with this operation. Any subsequent - * {@link #onCommandUpdate(int, int)} or {@link #onNetworkResponse(int, String, int)} - * calls regarding this update must use the same token. - */ - public void updateCapabilities(@NonNull RcsContactUceCapability capabilities, - int operationToken) { - // Stub - to be implemented by service - Log.w(LOG_TAG, "updateCapabilities called with no implementation."); - try { - getListener().onCommandUpdate(COMMAND_CODE_NOT_SUPPORTED, operationToken); - } catch (RemoteException | ImsException e) { - // Do not do anything, this is a stub implementation. - } - } -} diff --git a/telephony/java/android/telephony/ims/stub/RcsSipOptionsImplBase.java b/telephony/java/android/telephony/ims/stub/RcsSipOptionsImplBase.java deleted file mode 100644 index 2035fac4fae0..000000000000 --- a/telephony/java/android/telephony/ims/stub/RcsSipOptionsImplBase.java +++ /dev/null @@ -1,208 +0,0 @@ -/* - * Copyright (C) 2018 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.ims.stub; - -import android.annotation.IntDef; -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.net.Uri; -import android.os.RemoteException; -import android.telephony.ims.ImsException; -import android.telephony.ims.RcsContactUceCapability; -import android.telephony.ims.feature.ImsFeature; -import android.telephony.ims.feature.RcsFeature; -import android.util.Log; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * Base implementation for RCS User Capability Exchange using SIP OPTIONS. - * - * @hide - */ -public class RcsSipOptionsImplBase extends RcsCapabilityExchange { - - private static final String LOG_TAG = "RcsSipOptionsImplBase"; - - /** - * Indicates a SIP response from the remote user other than 200, 480, 408, 404, or 604. - */ - public static final int RESPONSE_GENERIC_FAILURE = -1; - - /** - * Indicates that the remote user responded with a 200 OK response. - */ - public static final int RESPONSE_SUCCESS = 0; - - /** - * Indicates that the remote user responded with a 480 TEMPORARY UNAVAILABLE response. - */ - public static final int RESPONSE_TEMPORARILY_UNAVAILABLE = 1; - - /** - * Indicates that the remote user responded with a 408 REQUEST TIMEOUT response. - */ - public static final int RESPONSE_REQUEST_TIMEOUT = 2; - - /** - * Indicates that the remote user responded with a 404 NOT FOUND response. - */ - public static final int RESPONSE_NOT_FOUND = 3; - - /** - * Indicates that the remote user responded with a 604 DOES NOT EXIST ANYWHERE response. - */ - public static final int RESPONSE_DOES_NOT_EXIST_ANYWHERE = 4; - - /** - * Indicates that the remote user responded with a 400 BAD REQUEST response. - */ - public static final int RESPONSE_BAD_REQUEST = 5; - - /** @hide*/ - @Retention(RetentionPolicy.SOURCE) - @IntDef(prefix = "RESPONSE_", value = { - RESPONSE_GENERIC_FAILURE, - RESPONSE_SUCCESS, - RESPONSE_TEMPORARILY_UNAVAILABLE, - RESPONSE_REQUEST_TIMEOUT, - RESPONSE_NOT_FOUND, - RESPONSE_DOES_NOT_EXIST_ANYWHERE, - RESPONSE_BAD_REQUEST - }) - public @interface SipResponseCode {} - - /** - * Send the response of a SIP OPTIONS capability exchange to the framework. If {@code code} is - * {@link #RESPONSE_SUCCESS}, info must be non-null. - * @param code The SIP response code that was sent by the network in response to the request - * sent by {@link #sendCapabilityRequest(Uri, RcsContactUceCapability, int)}. - * @param reason The optional SIP response reason sent by the network. If none was sent, this - * should be an empty string. - * @param info the contact's UCE capabilities associated with the capability request. - * @param operationToken The token associated with the original capability request, set by - * {@link #sendCapabilityRequest(Uri, RcsContactUceCapability, int)}. - * @throws ImsException If this {@link RcsSipOptionsImplBase} instance is not currently - * connected to the framework. This can happen if the {@link RcsFeature} is not - * {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received the - * {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare cases when the - * Telephony stack has crashed. - */ - public final void onCapabilityRequestResponse(@SipResponseCode int code, @NonNull String reason, - @Nullable RcsContactUceCapability info, int operationToken) throws ImsException { - try { - getListener().onCapabilityRequestResponseOptions(code, reason, info, operationToken); - } catch (RemoteException e) { - throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); - } - } - - /** - * Inform the framework of a query for this device's UCE capabilities. - * <p> - * The framework will respond via the - * {@link #respondToCapabilityRequest(String, RcsContactUceCapability, int)} or - * {@link #respondToCapabilityRequestWithError(Uri, int, String, int)} method. - * @param contactUri The URI associated with the remote contact that is requesting capabilities. - * @param remoteInfo The remote contact's capability information. - * @param operationToken An unique operation token that you have generated that will be returned - * by the framework in - * {@link #respondToCapabilityRequest(String, RcsContactUceCapability, int)}. - * @throws ImsException If this {@link RcsSipOptionsImplBase} instance is not currently - * connected to the framework. This can happen if the {@link RcsFeature} is not - * {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received the - * {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare cases when the - * Telephony stack has crashed. - */ - public final void onRemoteCapabilityRequest(@NonNull Uri contactUri, - @NonNull RcsContactUceCapability remoteInfo, int operationToken) throws ImsException { - try { - getListener().onRemoteCapabilityRequest(contactUri, remoteInfo, operationToken); - } catch (RemoteException e) { - throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); - } - } - - /** - * Push one's own capabilities to a remote user via the SIP OPTIONS presence exchange mechanism - * in order to receive the capabilities of the remote user in response. - * <p> - * The implementer must call - * {@link #onCapabilityRequestResponse(int, String, RcsContactUceCapability, int)} to send the - * response of this query back to the framework. - * @param contactUri The URI of the remote user that we wish to get the capabilities of. - * @param capabilities The capabilities of this device to send to the remote user. - * @param operationToken A token generated by the framework that will be passed through - * {@link #onCapabilityRequestResponse(int, String, RcsContactUceCapability, int)} when this - * operation has succeeded. - */ - public void sendCapabilityRequest(@NonNull Uri contactUri, - @NonNull RcsContactUceCapability capabilities, int operationToken) { - // Stub - to be implemented by service - Log.w(LOG_TAG, "sendCapabilityRequest called with no implementation."); - try { - getListener().onCommandUpdate(COMMAND_CODE_NOT_SUPPORTED, operationToken); - } catch (RemoteException | ImsException e) { - // Do not do anything, this is a stub implementation. - } - } - - /** - * Respond to a remote capability request from the contact specified with the capabilities of - * this device. - * <p> - * The framework will use the same token and uri as what was passed in to - * {@link #onRemoteCapabilityRequest(Uri, RcsContactUceCapability, int)}. - * @param contactUri The URI of the remote contact. - * @param ownCapabilities The capabilities of this device. - * @param operationToken The token generated by the framework that this service obtained when - * {@link #onRemoteCapabilityRequest(Uri, RcsContactUceCapability, int)} was called. - */ - public void respondToCapabilityRequest(@NonNull String contactUri, - @NonNull RcsContactUceCapability ownCapabilities, int operationToken) { - // Stub - to be implemented by service - Log.w(LOG_TAG, "respondToCapabilityRequest called with no implementation."); - try { - getListener().onCommandUpdate(COMMAND_CODE_NOT_SUPPORTED, operationToken); - } catch (RemoteException | ImsException e) { - // Do not do anything, this is a stub implementation. - } - } - - /** - * Respond to a remote capability request from the contact specified with the specified error. - * <p> - * The framework will use the same token and uri as what was passed in to - * {@link #onRemoteCapabilityRequest(Uri, RcsContactUceCapability, int)}. - * @param contactUri A URI containing the remote contact. - * @param code The SIP response code to respond with. - * @param reason A non-null String containing the reason associated with the SIP code. - * @param operationToken The token provided by the framework when - * {@link #onRemoteCapabilityRequest(Uri, RcsContactUceCapability, int)} was called. - */ - public void respondToCapabilityRequestWithError(@NonNull Uri contactUri, - @SipResponseCode int code, @NonNull String reason, int operationToken) { - // Stub - to be implemented by service - Log.w(LOG_TAG, "respondToCapabiltyRequestWithError called with no implementation."); - try { - getListener().onCommandUpdate(COMMAND_CODE_NOT_SUPPORTED, operationToken); - } catch (RemoteException | ImsException e) { - // Do not do anything, this is a stub implementation. - } - } -} diff --git a/telephony/java/android/telephony/ims/stub/SipDelegate.java b/telephony/java/android/telephony/ims/stub/SipDelegate.java new file mode 100644 index 000000000000..997d00bc91c7 --- /dev/null +++ b/telephony/java/android/telephony/ims/stub/SipDelegate.java @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2020 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.ims.stub; + +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.telephony.ims.DelegateMessageCallback; +import android.telephony.ims.DelegateRegistrationState; +import android.telephony.ims.ImsService; +import android.telephony.ims.SipDelegateConfiguration; +import android.telephony.ims.SipDelegateConnection; +import android.telephony.ims.SipDelegateManager; +import android.telephony.ims.SipMessage; + +/** + * The {@link SipDelegate} is implemented by the {@link ImsService} and allows a privileged + * IMS application to use this delegate to send SIP messages as well as acknowledge the receipt of + * incoming SIP messages delivered to the application over the existing IMS registration, allowing + * for a single IMS registration for multiple IMS applications. + * <p> + * Once the SIP delegate is created for that application, + * {@link ImsRegistrationImplBase#updateSipDelegateRegistration()} will be called, indicating that + * the application is finished setting up SipDelegates and the existing IMS registration may be + * modified to include the features managed by these SipDelegates. + * <p> + * This SipDelegate will need to notify the remote application of the registration of these features + * as well as the associated {@link SipDelegateConfiguration} before the application can start + * sending/receiving SIP messages via the transport. See + * {@link android.telephony.ims.DelegateStateCallback} for more information. + * @hide + */ +@SystemApi +public interface SipDelegate { + + /** + * The framework calls this method when a remote RCS application wishes to send a new outgoing + * SIP message. + * <p> + * Once sent, this SIP delegate should notify the remote application of the success or + * failure using {@link DelegateMessageCallback#onMessageSent(String)} or + * {@link DelegateMessageCallback#onMessageSendFailure(String, int)}. + * @param message The SIP message to be sent over the operator’s network. + * @param configVersion The SipDelegateImsConfiguration version used to construct the + * SipMessage. See {@link SipDelegateConfiguration} for more information. If the + * version specified here does not match the most recently constructed + * {@link SipDelegateConfiguration}, this message should fail validation checks and + * {@link DelegateMessageCallback#onMessageSendFailure} should be called with code + * {@link SipDelegateManager#MESSAGE_FAILURE_REASON_STALE_IMS_CONFIGURATION}. + */ + void sendMessage(@NonNull SipMessage message, long configVersion); + + /** + * The framework is requesting that routing resources associated with the SIP dialog using the + * provided Call-ID to be cleaned up. + * <p> + * Typically, a SIP Dialog close event will be signalled by that dialog receiving a BYE or + * 200 OK message, however, the IMS application will still call + * {@link SipDelegateConnection#closeDialog(String)} to signal to the framework that resources + * can be released. In some cases, the framework will request that the ImsService close the + * dialog due to the open dialog holding up an event such as applying a provisioning change or + * handing over to another transport type. See {@link DelegateRegistrationState}. + * + * @param callId The call-ID header value associated with the ongoing SIP Dialog that the + * framework is requesting be closed. + * @deprecated This method does not take into account INVITE forking. Use + * {@link #cleanupSession(String)} instead. + */ + @Deprecated + default void closeDialog(@NonNull String callId) { } + + /** + * The remote IMS application has closed a SIP session and the routing resources associated + * with the SIP session using the provided Call-ID may now be cleaned up. + * <p> + * Typically, a SIP session will be considered closed when all associated dialogs receive a + * BYE request. After the session has been closed, the IMS application will call + * {@link SipDelegateConnection#cleanupSession(String)} to signal to the framework that + * resources can be released. In some cases, the framework will request that the ImsService + * close the session due to the open SIP session holding up an event such as applying a + * provisioning change or handing over to another transport type. See + * {@link DelegateRegistrationState}. + * + * @param callId The call-ID header value associated with the ongoing SIP Session that the + * framework is requesting be cleaned up. + */ + default void cleanupSession(@NonNull String callId) { + closeDialog(callId); + } + + /** + * The remote application has received the SIP message and is processing it. + * @param viaTransactionId The Transaction ID found in the via header field of the + * previously sent {@link SipMessage}. + */ + void notifyMessageReceived(@NonNull String viaTransactionId); + + /** + * The remote application has either not received the SIP message or there was an error + * processing it. + * @param viaTransactionId The Transaction ID found in the via header field of the + * previously sent {@link SipMessage}. + * @param reason The reason why the message was not correctly received. + */ + void notifyMessageReceiveError(@NonNull String viaTransactionId, + @SipDelegateManager.MessageFailureReason int reason); +} diff --git a/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java b/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java new file mode 100644 index 000000000000..1f74c09af0f6 --- /dev/null +++ b/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java @@ -0,0 +1,181 @@ +/* + * Copyright (C) 2020 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.ims.stub; + +import android.annotation.NonNull; +import android.annotation.SuppressLint; +import android.annotation.SystemApi; +import android.os.Binder; +import android.os.IBinder; +import android.telephony.ims.DelegateMessageCallback; +import android.telephony.ims.DelegateRequest; +import android.telephony.ims.DelegateStateCallback; +import android.telephony.ims.SipDelegateManager; +import android.telephony.ims.aidl.ISipDelegate; +import android.telephony.ims.aidl.ISipDelegateMessageCallback; +import android.telephony.ims.aidl.ISipDelegateStateCallback; +import android.telephony.ims.aidl.ISipTransport; +import android.telephony.ims.aidl.SipDelegateAidlWrapper; +import android.util.Log; + +import java.util.ArrayList; +import java.util.Objects; +import java.util.concurrent.Executor; + +/** + * The ImsService implements this class to manage the creation and destruction of + * {@link SipDelegate}s. + * + * {@link SipDelegate}s allow the ImsService to forward SIP traffic generated and consumed by IMS + * applications as a delegate to the associated carrier's IMS Network in order to support using a + * single IMS registration for all MMTEL and RCS signalling traffic. + * @hide + */ +@SystemApi +public class SipTransportImplBase { + private static final String LOG_TAG = "SipTransportIB"; + + private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() { + @Override + public void binderDied() { + mBinderExecutor.execute(() -> binderDiedInternal()); + } + }; + + private final ISipTransport.Stub mSipTransportImpl = new ISipTransport.Stub() { + @Override + public void createSipDelegate(int subId, DelegateRequest request, + ISipDelegateStateCallback dc, ISipDelegateMessageCallback mc) { + final long token = Binder.clearCallingIdentity(); + try { + mBinderExecutor.execute(() -> createSipDelegateInternal(subId, request, dc, mc)); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public void destroySipDelegate(ISipDelegate delegate, int reason) { + final long token = Binder.clearCallingIdentity(); + try { + mBinderExecutor.execute(() -> destroySipDelegateInternal(delegate, reason)); + } finally { + Binder.restoreCallingIdentity(token); + } + } + }; + + private final Executor mBinderExecutor; + private final ArrayList<SipDelegateAidlWrapper> mDelegates = new ArrayList<>(); + + /** + * Create an implementation of SipTransportImplBase. + * + * @param executor The executor that remote calls from the framework will be called on. This + * includes the methods here as well as the methods in {@link SipDelegate}. + */ + public SipTransportImplBase(@NonNull Executor executor) { + if (executor == null) { + throw new IllegalArgumentException("executor must not be null"); + } + + mBinderExecutor = executor; + } + + /** + * Called by the Telephony framework to request the creation of a new {@link SipDelegate}. + * <p> + * The implementation must call + * {@link DelegateStateCallback#onCreated(SipDelegate, java.util.Set)} with + * the {@link SipDelegate} that is associated with the {@link DelegateRequest}. + * <p> + * This method will be called on the Executor specified in + * {@link SipTransportImplBase#SipTransportImplBase(Executor)}. + * + * @param subscriptionId The subscription ID associated with the requested {@link SipDelegate}. + * @param request A SIP delegate request containing the parameters that the remote RCS + * application wishes to use. + * @param dc A callback back to the remote application to be used to communicate state callbacks + * for the SipDelegate. + * @param mc A callback back to the remote application to be used to send SIP messages to the + * remote application and acknowledge the sending of outgoing SIP messages. + */ + // executor used is defined in the constructor. + @SuppressLint("ExecutorRegistration") + public void createSipDelegate(int subscriptionId, @NonNull DelegateRequest request, + @NonNull DelegateStateCallback dc, @NonNull DelegateMessageCallback mc) { + throw new UnsupportedOperationException("createSipDelegate not implemented!"); + } + + /** + * Destroys the SipDelegate associated with a remote IMS application. + * <p> + * After the delegate is destroyed, {@link DelegateStateCallback#onDestroyed(int)} must be + * called to notify listeners of its destruction to release associated resources. + * <p> + * This method will be called on the Executor specified in + * {@link SipTransportImplBase#SipTransportImplBase(Executor)}. + * @param delegate The delegate to be destroyed. + * @param reason The reason the remote connection to this {@link SipDelegate} is being + * destroyed. + */ + public void destroySipDelegate(@NonNull SipDelegate delegate, + @SipDelegateManager.SipDelegateDestroyReason int reason) { + throw new UnsupportedOperationException("destroySipDelegate not implemented!"); + } + + private void createSipDelegateInternal(int subId, DelegateRequest r, + ISipDelegateStateCallback cb, ISipDelegateMessageCallback mc) { + SipDelegateAidlWrapper wrapper = new SipDelegateAidlWrapper(mBinderExecutor, cb, mc); + mDelegates.add(wrapper); + createSipDelegate(subId, r, wrapper, wrapper); + } + + private void destroySipDelegateInternal(ISipDelegate d, int reason) { + SipDelegateAidlWrapper result = null; + for (SipDelegateAidlWrapper w : mDelegates) { + if (Objects.equals(d, w.getDelegateBinder())) { + result = w; + break; + } + } + + if (result != null) { + mDelegates.remove(result); + destroySipDelegate(result.getDelegate(), reason); + } else { + Log.w(LOG_TAG, "destroySipDelegateInternal, could not findSipDelegate corresponding to " + + d); + } + } + + private void binderDiedInternal() { + for (SipDelegateAidlWrapper w : mDelegates) { + destroySipDelegate(w.getDelegate(), + SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_SERVICE_DEAD); + } + mDelegates.clear(); + } + + /** + * @return The IInterface used by the framework. + * @hide + */ + public ISipTransport getBinder() { + return mSipTransportImpl; + } +} diff --git a/telephony/java/android/telephony/mbms/InternalDownloadProgressListener.java b/telephony/java/android/telephony/mbms/InternalDownloadProgressListener.java index 6a135694869c..a413ef8a8738 100644 --- a/telephony/java/android/telephony/mbms/InternalDownloadProgressListener.java +++ b/telephony/java/android/telephony/mbms/InternalDownloadProgressListener.java @@ -43,7 +43,7 @@ public class InternalDownloadProgressListener extends IDownloadProgressListener. return; } - long token = Binder.clearCallingIdentity(); + final long token = Binder.clearCallingIdentity(); try { mExecutor.execute(new Runnable() { @Override diff --git a/telephony/java/android/telephony/mbms/InternalDownloadSessionCallback.java b/telephony/java/android/telephony/mbms/InternalDownloadSessionCallback.java index ce32477b443b..67539a0ad7ee 100644 --- a/telephony/java/android/telephony/mbms/InternalDownloadSessionCallback.java +++ b/telephony/java/android/telephony/mbms/InternalDownloadSessionCallback.java @@ -40,7 +40,7 @@ public class InternalDownloadSessionCallback extends IMbmsDownloadSessionCallbac return; } - long token = Binder.clearCallingIdentity(); + final long token = Binder.clearCallingIdentity(); try { mExecutor.execute(new Runnable() { @Override @@ -59,7 +59,7 @@ public class InternalDownloadSessionCallback extends IMbmsDownloadSessionCallbac return; } - long token = Binder.clearCallingIdentity(); + final long token = Binder.clearCallingIdentity(); try { mExecutor.execute(new Runnable() { @Override @@ -78,7 +78,7 @@ public class InternalDownloadSessionCallback extends IMbmsDownloadSessionCallbac return; } - long token = Binder.clearCallingIdentity(); + final long token = Binder.clearCallingIdentity(); try { mExecutor.execute(new Runnable() { @Override diff --git a/telephony/java/android/telephony/mbms/InternalDownloadStatusListener.java b/telephony/java/android/telephony/mbms/InternalDownloadStatusListener.java index 87163ff8b32c..ce96a8faeb49 100644 --- a/telephony/java/android/telephony/mbms/InternalDownloadStatusListener.java +++ b/telephony/java/android/telephony/mbms/InternalDownloadStatusListener.java @@ -42,7 +42,7 @@ public class InternalDownloadStatusListener extends IDownloadStatusListener.Stub return; } - long token = Binder.clearCallingIdentity(); + final long token = Binder.clearCallingIdentity(); try { mExecutor.execute(new Runnable() { @Override diff --git a/telephony/java/android/telephony/mbms/InternalGroupCallCallback.java b/telephony/java/android/telephony/mbms/InternalGroupCallCallback.java index c7600b6c7843..5e1f1f170f60 100644 --- a/telephony/java/android/telephony/mbms/InternalGroupCallCallback.java +++ b/telephony/java/android/telephony/mbms/InternalGroupCallCallback.java @@ -38,7 +38,7 @@ public class InternalGroupCallCallback extends IGroupCallCallback.Stub { return; } - long token = Binder.clearCallingIdentity(); + final long token = Binder.clearCallingIdentity(); try { mExecutor.execute(new Runnable() { @Override @@ -57,7 +57,7 @@ public class InternalGroupCallCallback extends IGroupCallCallback.Stub { return; } - long token = Binder.clearCallingIdentity(); + final long token = Binder.clearCallingIdentity(); try { mExecutor.execute(new Runnable() { @Override @@ -76,7 +76,7 @@ public class InternalGroupCallCallback extends IGroupCallCallback.Stub { return; } - long token = Binder.clearCallingIdentity(); + final long token = Binder.clearCallingIdentity(); try { mExecutor.execute(new Runnable() { @Override diff --git a/telephony/java/android/telephony/mbms/InternalGroupCallSessionCallback.java b/telephony/java/android/telephony/mbms/InternalGroupCallSessionCallback.java index 0b7667ec525c..ca4190c6b4f1 100644 --- a/telephony/java/android/telephony/mbms/InternalGroupCallSessionCallback.java +++ b/telephony/java/android/telephony/mbms/InternalGroupCallSessionCallback.java @@ -39,7 +39,7 @@ public class InternalGroupCallSessionCallback extends IMbmsGroupCallSessionCallb return; } - long token = Binder.clearCallingIdentity(); + final long token = Binder.clearCallingIdentity(); try { mExecutor.execute(new Runnable() { @Override @@ -58,7 +58,7 @@ public class InternalGroupCallSessionCallback extends IMbmsGroupCallSessionCallb return; } - long token = Binder.clearCallingIdentity(); + final long token = Binder.clearCallingIdentity(); try { mExecutor.execute(new Runnable() { @Override @@ -77,7 +77,7 @@ public class InternalGroupCallSessionCallback extends IMbmsGroupCallSessionCallb return; } - long token = Binder.clearCallingIdentity(); + final long token = Binder.clearCallingIdentity(); try { mExecutor.execute(new Runnable() { @Override @@ -96,7 +96,7 @@ public class InternalGroupCallSessionCallback extends IMbmsGroupCallSessionCallb return; } - long token = Binder.clearCallingIdentity(); + final long token = Binder.clearCallingIdentity(); try { mExecutor.execute(new Runnable() { @Override diff --git a/telephony/java/android/telephony/mbms/InternalStreamingServiceCallback.java b/telephony/java/android/telephony/mbms/InternalStreamingServiceCallback.java index 3a4ed08ed954..d62add193e77 100644 --- a/telephony/java/android/telephony/mbms/InternalStreamingServiceCallback.java +++ b/telephony/java/android/telephony/mbms/InternalStreamingServiceCallback.java @@ -39,7 +39,7 @@ public class InternalStreamingServiceCallback extends IStreamingServiceCallback. return; } - long token = Binder.clearCallingIdentity(); + final long token = Binder.clearCallingIdentity(); try { mExecutor.execute(new Runnable() { @Override @@ -58,7 +58,7 @@ public class InternalStreamingServiceCallback extends IStreamingServiceCallback. return; } - long token = Binder.clearCallingIdentity(); + final long token = Binder.clearCallingIdentity(); try { mExecutor.execute(new Runnable() { @Override @@ -77,7 +77,7 @@ public class InternalStreamingServiceCallback extends IStreamingServiceCallback. return; } - long token = Binder.clearCallingIdentity(); + final long token = Binder.clearCallingIdentity(); try { mExecutor.execute(new Runnable() { @Override @@ -96,7 +96,7 @@ public class InternalStreamingServiceCallback extends IStreamingServiceCallback. return; } - long token = Binder.clearCallingIdentity(); + final long token = Binder.clearCallingIdentity(); try { mExecutor.execute(new Runnable() { @Override @@ -115,7 +115,7 @@ public class InternalStreamingServiceCallback extends IStreamingServiceCallback. return; } - long token = Binder.clearCallingIdentity(); + final long token = Binder.clearCallingIdentity(); try { mExecutor.execute(new Runnable() { @Override diff --git a/telephony/java/android/telephony/mbms/InternalStreamingSessionCallback.java b/telephony/java/android/telephony/mbms/InternalStreamingSessionCallback.java index 2eb280e74106..f4ee4dc069a9 100644 --- a/telephony/java/android/telephony/mbms/InternalStreamingSessionCallback.java +++ b/telephony/java/android/telephony/mbms/InternalStreamingSessionCallback.java @@ -40,7 +40,7 @@ public class InternalStreamingSessionCallback extends IMbmsStreamingSessionCallb return; } - long token = Binder.clearCallingIdentity(); + final long token = Binder.clearCallingIdentity(); try { mExecutor.execute(new Runnable() { @Override @@ -60,7 +60,7 @@ public class InternalStreamingSessionCallback extends IMbmsStreamingSessionCallb return; } - long token = Binder.clearCallingIdentity(); + final long token = Binder.clearCallingIdentity(); try { mExecutor.execute(new Runnable() { @Override @@ -79,7 +79,7 @@ public class InternalStreamingSessionCallback extends IMbmsStreamingSessionCallb return; } - long token = Binder.clearCallingIdentity(); + final long token = Binder.clearCallingIdentity(); try { mExecutor.execute(new Runnable() { @Override diff --git a/telephony/java/android/telephony/mbms/MbmsErrors.java b/telephony/java/android/telephony/mbms/MbmsErrors.java index 52e4d333b29d..40f3ae82d778 100644 --- a/telephony/java/android/telephony/mbms/MbmsErrors.java +++ b/telephony/java/android/telephony/mbms/MbmsErrors.java @@ -16,8 +16,12 @@ package android.telephony.mbms; +import android.annotation.IntDef; import android.telephony.MbmsStreamingSession; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + public class MbmsErrors { /** * Indicates that the middleware has sent an error code that is not defined in the version of @@ -138,6 +142,13 @@ public class MbmsErrors { /** Indicates the the middleware has no record of the supplied {@link FileInfo} */ public static final int ERROR_UNKNOWN_FILE_INFO = 403; + + /** + * Indicates that the service announcement descriptor passed via + * {@link android.telephony.MbmsDownloadSession#addServiceAnnouncement(byte[])} + * is malformed. + */ + public static final int ERROR_MALFORMED_SERVICE_ANNOUNCEMENT = 404; } /** @@ -156,5 +167,35 @@ public class MbmsErrors { public static final int ERROR_DUPLICATE_START_GROUP_CALL = 502; } + /** @hide */ + @IntDef(value = { + SUCCESS, + ERROR_NO_UNIQUE_MIDDLEWARE, + ERROR_MIDDLEWARE_NOT_BOUND, + ERROR_MIDDLEWARE_LOST, + InitializationErrors.ERROR_DUPLICATE_INITIALIZE, + InitializationErrors.ERROR_APP_PERMISSIONS_NOT_GRANTED, + InitializationErrors.ERROR_UNABLE_TO_INITIALIZE, + GeneralErrors.ERROR_MIDDLEWARE_NOT_YET_READY, + GeneralErrors.ERROR_OUT_OF_MEMORY, + GeneralErrors.ERROR_MIDDLEWARE_TEMPORARILY_UNAVAILABLE, + GeneralErrors.ERROR_IN_E911, + GeneralErrors.ERROR_NOT_CONNECTED_TO_HOME_CARRIER_LTE, + GeneralErrors.ERROR_UNABLE_TO_READ_SIM, + GeneralErrors.ERROR_CARRIER_CHANGE_NOT_ALLOWED, + StreamingErrors.ERROR_CONCURRENT_SERVICE_LIMIT_REACHED, + StreamingErrors.ERROR_UNABLE_TO_START_SERVICE, + StreamingErrors.ERROR_DUPLICATE_START_STREAM, + DownloadErrors.ERROR_CANNOT_CHANGE_TEMP_FILE_ROOT, + DownloadErrors.ERROR_UNKNOWN_DOWNLOAD_REQUEST, + DownloadErrors.ERROR_UNKNOWN_FILE_INFO, + DownloadErrors.ERROR_MALFORMED_SERVICE_ANNOUNCEMENT, + GroupCallErrors.ERROR_UNABLE_TO_START_SERVICE, + GroupCallErrors.ERROR_DUPLICATE_START_GROUP_CALL, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface MbmsError { + } + private MbmsErrors() {} } diff --git a/telephony/java/android/telephony/mbms/MbmsTempFileProvider.java b/telephony/java/android/telephony/mbms/MbmsTempFileProvider.java index 689becd7169a..17adede86c51 100644 --- a/telephony/java/android/telephony/mbms/MbmsTempFileProvider.java +++ b/telephony/java/android/telephony/mbms/MbmsTempFileProvider.java @@ -91,7 +91,7 @@ public class MbmsTempFileProvider extends ContentProvider { public void attachInfo(Context context, ProviderInfo info) { super.attachInfo(context, info); - // Sanity check our security + // Correctness check our security if (info.exported) { throw new SecurityException("Provider must not be exported"); } diff --git a/telephony/java/android/telephony/mbms/OWNERS b/telephony/java/android/telephony/mbms/OWNERS new file mode 100644 index 000000000000..718e0a292605 --- /dev/null +++ b/telephony/java/android/telephony/mbms/OWNERS @@ -0,0 +1,5 @@ +# Bug component: 20868 + +rgreenwalt@google.com +tgunn@google.com +hallliu@google.com diff --git a/telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl b/telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl index 445087fb78d5..04efd53eb743 100755 --- a/telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl +++ b/telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl @@ -35,6 +35,8 @@ interface IMbmsDownloadService int setTempFileRootDirectory(int subId, String rootDirectoryPath); + int addServiceAnnouncement(int subId, in byte[] contents); + int download(in DownloadRequest downloadRequest); int addStatusListener(in DownloadRequest downloadRequest, diff --git a/telephony/java/android/telephony/mbms/vendor/IMbmsStreamingService.aidl b/telephony/java/android/telephony/mbms/vendor/IMbmsStreamingService.aidl index c140127237d4..a4abfac74177 100755 --- a/telephony/java/android/telephony/mbms/vendor/IMbmsStreamingService.aidl +++ b/telephony/java/android/telephony/mbms/vendor/IMbmsStreamingService.aidl @@ -26,17 +26,17 @@ import android.telephony.mbms.StreamingServiceInfo; */ interface IMbmsStreamingService { - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) int initialize(IMbmsStreamingSessionCallback callback, int subId); - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) int requestUpdateStreamingServices(int subId, in List<String> serviceClasses); - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) int startStreaming(int subId, String serviceId, IStreamingServiceCallback callback); - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) Uri getPlaybackUri(int subId, String serviceId); void stopStreaming(int subId, String serviceId); diff --git a/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java b/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java index 4e6fc0b3c1a3..ffc1d2efe2ae 100644 --- a/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java +++ b/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java @@ -214,6 +214,29 @@ public class MbmsDownloadServiceBase extends IMbmsDownloadService.Stub { } /** + * Called when the client application wishes to receive file information according to a + * service announcement descriptor received from a group call server. + * + * The service announcement descriptor is in the format of a multipart MIME file with XML parts, + * though no validation is performed on the contents of the {@code contents} argument -- + * implementing middleware applications should perform their own validation and return + * {@link MbmsErrors.DownloadErrors#ERROR_MALFORMED_SERVICE_ANNOUNCEMENT} if the descriptor is + * malformed. + * + * @param subscriptionId The subscription id the service announcement applies to. + * @param contents The contents of the service announcement descriptor. + * @return {@link MbmsErrors#SUCCESS}, or + * {@link MbmsErrors.DownloadErrors#ERROR_MALFORMED_SERVICE_ANNOUNCEMENT} + */ + // TODO: are there any public specifications of what the file format is that I can link to? + @Override + public @MbmsErrors.MbmsError int addServiceAnnouncement( + int subscriptionId, @NonNull byte[] contents) { + throw new UnsupportedOperationException("addServiceAnnouncement not supported by" + + " this middleware."); + } + + /** * Issues a request to download a set of files. * * The middleware should expect that {@link #setTempFileRootDirectory(int, String)} has been diff --git a/telephony/java/android/telephony/mbms/vendor/OWNERS b/telephony/java/android/telephony/mbms/vendor/OWNERS new file mode 100644 index 000000000000..718e0a292605 --- /dev/null +++ b/telephony/java/android/telephony/mbms/vendor/OWNERS @@ -0,0 +1,5 @@ +# Bug component: 20868 + +rgreenwalt@google.com +tgunn@google.com +hallliu@google.com |