Automatic sources dropoff on 2020-06-10 18:32:38.095721
The change is generated with prebuilt drop tool.
Change-Id: I24cbf6ba6db262a1ae1445db1427a08fee35b3b4
diff --git a/android/telephony/ims/ImsCallForwardInfo.java b/android/telephony/ims/ImsCallForwardInfo.java
new file mode 100644
index 0000000..d53a2e6
--- /dev/null
+++ b/android/telephony/ims/ImsCallForwardInfo.java
@@ -0,0 +1,267 @@
+/*
+ * 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.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Provides the call forward information for the supplementary service configuration.
+ *
+ * @hide
+ */
+@SystemApi
+@TestApi
+public final class ImsCallForwardInfo implements Parcelable {
+
+ /**
+ * CDIV (Communication Diversion, 3GPP TS 24.604) call forwarding reason for unconditional call
+ * forwarding. See TC 27.007
+ */
+ public static final int CDIV_CF_REASON_UNCONDITIONAL = 0;
+
+ /**
+ * CDIV (Communication Diversion, 3GPP TS 24.604) call forwarding reason for call forwarding
+ * when the user is busy.
+ */
+ public static final int CDIV_CF_REASON_BUSY = 1;
+
+ /**
+ * CDIV (Communication Diversion, 3GPP TS 24.604) call forwarding reason for call forwarding
+ * when there is no reply from the user.
+ */
+ public static final int CDIV_CF_REASON_NO_REPLY = 2;
+
+ /**
+ * CDIV (Communication Diversion, 3GPP TS 24.604) call forwarding reason for call forwarding
+ * when the user is not reachable.
+ */
+ public static final int CDIV_CF_REASON_NOT_REACHABLE = 3;
+
+ /**
+ * CDIV (Communication Diversion, 3GPP TS 24.604) call forwarding reason for setting all call
+ * forwarding reasons simultaneously (i.e. unconditional, busy, no reply, and not reachable).
+ */
+ public static final int CDIV_CF_REASON_ALL = 4;
+
+ /**
+ * CDIV (Communication Diversion, 3GPP TS 24.604) call forwarding reason for setting all
+ * conditional call forwarding reasons simultaneously (i.e. if busy, if no reply, and if not
+ * reachable).
+ */
+ public static final int CDIV_CF_REASON_ALL_CONDITIONAL = 5;
+
+ /**
+ * CDIV (Communication Diversion) IMS only call forwarding reason for call forwarding when the
+ * user is not logged in.
+ */
+ public static final int CDIV_CF_REASON_NOT_LOGGED_IN = 6;
+
+ /**@hide*/
+ @IntDef(prefix = {"CDIV_CF_REASON_"}, value = {
+ CDIV_CF_REASON_UNCONDITIONAL,
+ CDIV_CF_REASON_BUSY,
+ CDIV_CF_REASON_NO_REPLY,
+ CDIV_CF_REASON_NOT_REACHABLE,
+ CDIV_CF_REASON_ALL,
+ CDIV_CF_REASON_ALL_CONDITIONAL,
+ CDIV_CF_REASON_NOT_LOGGED_IN})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface CallForwardReasons{}
+
+ /**
+ * Call forwarding is not active for any service class.
+ */
+ public static final int STATUS_NOT_ACTIVE = 0;
+
+ /**
+ * Call forwarding is active for one or more service classes.
+ */
+ public static final int STATUS_ACTIVE = 1;
+
+ /**@hide*/
+ @IntDef(prefix = {"STATUS_"}, value = {
+ STATUS_NOT_ACTIVE,
+ STATUS_ACTIVE})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface CallForwardStatus{}
+
+ /**
+ * The address defined in {@link #getNumber()} is in an unknown format.
+ *
+ * See TS 27.007, section 7.11 for more information.
+ */
+ public static final int TYPE_OF_ADDRESS_UNKNOWN = 0x81;
+ /**
+ * The address defined in {@link #getNumber()} is in E.164 international format, which includes
+ * international access code "+".
+ *
+ * See TS 27.007, section 7.11 for more information.
+ */
+ public static final int TYPE_OF_ADDRESS_INTERNATIONAL = 0x91;
+
+ /**@hide*/
+ @IntDef(prefix = {"TYPE_OF_ADDRESS_"}, value = {
+ TYPE_OF_ADDRESS_INTERNATIONAL,
+ TYPE_OF_ADDRESS_UNKNOWN})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface TypeOfAddress{}
+
+ /**@hide*/
+ @UnsupportedAppUsage
+ public @CallForwardReasons int mCondition;
+ /** @hide */
+ @UnsupportedAppUsage
+ public @CallForwardStatus int mStatus;
+ /** @hide */
+ @UnsupportedAppUsage
+ public @TypeOfAddress int mToA;
+ /** @hide */
+ @UnsupportedAppUsage
+ public @ImsSsData.ServiceClassFlags int mServiceClass;
+ /** @hide */
+ @UnsupportedAppUsage
+ public String mNumber;
+ /** @hide */
+ @UnsupportedAppUsage
+ public int mTimeSeconds;
+
+ /** @hide */
+ @UnsupportedAppUsage
+ public ImsCallForwardInfo() {
+ }
+
+ /**
+ * IMS Call Forward Information.
+ */
+ public ImsCallForwardInfo(@CallForwardReasons int reason, @CallForwardStatus int status,
+ @TypeOfAddress int toA, @ImsSsData.ServiceClassFlags int serviceClass,
+ @NonNull String number, int replyTimerSec) {
+ mCondition = reason;
+ mStatus = status;
+ mToA = toA;
+ mServiceClass = serviceClass;
+ mNumber = number;
+ mTimeSeconds = replyTimerSec;
+ }
+
+ /** @hide */
+ public ImsCallForwardInfo(Parcel in) {
+ readFromParcel(in);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(mCondition);
+ out.writeInt(mStatus);
+ out.writeInt(mToA);
+ out.writeString(mNumber);
+ out.writeInt(mTimeSeconds);
+ out.writeInt(mServiceClass);
+ }
+
+ @NonNull
+ @Override
+ public String toString() {
+ return super.toString() + ", Condition: " + mCondition
+ + ", Status: " + ((mStatus == 0) ? "disabled" : "enabled")
+ + ", ToA: " + mToA
+ + ", Service Class: " + mServiceClass
+ + ", Number=" + mNumber
+ + ", Time (seconds): " + mTimeSeconds;
+ }
+
+ private void readFromParcel(Parcel in) {
+ mCondition = in.readInt();
+ mStatus = in.readInt();
+ mToA = in.readInt();
+ mNumber = in.readString();
+ mTimeSeconds = in.readInt();
+ mServiceClass = in.readInt();
+ }
+
+ public static final @android.annotation.NonNull Creator<ImsCallForwardInfo> CREATOR =
+ new Creator<ImsCallForwardInfo>() {
+ @Override
+ public ImsCallForwardInfo createFromParcel(Parcel in) {
+ return new ImsCallForwardInfo(in);
+ }
+
+ @Override
+ public ImsCallForwardInfo[] newArray(int size) {
+ return new ImsCallForwardInfo[size];
+ }
+ };
+
+ /**
+ * @return the condition of call forwarding for the service classes specified.
+ */
+ public @CallForwardReasons int getCondition() {
+ return mCondition;
+ }
+
+ /**
+ * @return The call forwarding status.
+ */
+ public @CallForwardStatus int getStatus() {
+ return mStatus;
+ }
+
+ /**
+ * @return the type of address (ToA) for the number.
+ * @see #getNumber()
+ */
+ public @TypeOfAddress int getToA() {
+ return mToA;
+ }
+
+ /**
+ * @return a bitfield containing the service classes that are enabled for call forwarding.
+ */
+ public @ImsSsData.ServiceClassFlags int getServiceClass() {
+ return mServiceClass;
+ }
+
+ /**
+ * @return the call forwarding number associated with call forwarding, with a number type
+ * defined by {@link #getToA()}.
+ */
+ public String getNumber() {
+ return mNumber;
+ }
+
+ /**
+ * @return the number in seconds to wait before the call is forwarded for call forwarding
+ * condition {@link #CDIV_CF_REASON_NO_REPLY}
+ */
+ public int getTimeSeconds() {
+ return mTimeSeconds;
+ }
+}
diff --git a/android/telephony/ims/ImsCallProfile.java b/android/telephony/ims/ImsCallProfile.java
new file mode 100644
index 0000000..f31fcf4
--- /dev/null
+++ b/android/telephony/ims/ImsCallProfile.java
@@ -0,0 +1,1089 @@
+/*
+ * 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.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telecom.VideoProfile;
+import android.telephony.emergency.EmergencyNumber;
+import android.telephony.emergency.EmergencyNumber.EmergencyCallRouting;
+import android.telephony.emergency.EmergencyNumber.EmergencyServiceCategories;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.telephony.util.TelephonyUtils;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A Parcelable object to handle the IMS call profile, which provides the service, call type, and
+ * additional information related to the call.
+ * <p>
+ * See the following specifications for more information about this class: GSMA IR.92/IR.94,
+ * 3GPP TS 24.229/TS 26.114/TS26.111.
+ * @hide
+ */
+@SystemApi
+@TestApi
+public final class ImsCallProfile implements Parcelable {
+ private static final String TAG = "ImsCallProfile";
+
+ /**
+ * Service types
+ */
+ /**
+ * It is for a special case. It helps that the application can make a call
+ * without IMS connection (not registered).
+ * In the moment of the call initiation, the device try to connect to the IMS network
+ * and initiates the call.
+ */
+ public static final int SERVICE_TYPE_NONE = 0;
+ /**
+ * It is a default type and can be selected when the device is connected to the IMS network.
+ */
+ public static final int SERVICE_TYPE_NORMAL = 1;
+ /**
+ * It is for an emergency call.
+ */
+ public static final int SERVICE_TYPE_EMERGENCY = 2;
+
+ /**
+ * Call types
+ */
+ /**
+ * IMSPhone to support IR.92 & IR.94 (voice + video upgrade/downgrade)
+ */
+ public static final int CALL_TYPE_VOICE_N_VIDEO = 1;
+ /**
+ * IR.92 (Voice only)
+ */
+ public static final int CALL_TYPE_VOICE = 2;
+ /**
+ * VT to support IR.92 & IR.94 (voice + video upgrade/downgrade)
+ */
+ public static final int CALL_TYPE_VIDEO_N_VOICE = 3;
+ /**
+ * Video Telephony (audio / video two way)
+ */
+ public static final int CALL_TYPE_VT = 4;
+ /**
+ * Video Telephony (audio two way / video TX one way)
+ */
+ public static final int CALL_TYPE_VT_TX = 5;
+ /**
+ * Video Telephony (audio two way / video RX one way)
+ */
+ public static final int CALL_TYPE_VT_RX = 6;
+ /**
+ * Video Telephony (audio two way / video inactive)
+ */
+ public static final int CALL_TYPE_VT_NODIR = 7;
+ /**
+ * VideoShare (video two way)
+ */
+ public static final int CALL_TYPE_VS = 8;
+ /**
+ * VideoShare (video TX one way)
+ */
+ public static final int CALL_TYPE_VS_TX = 9;
+ /**
+ * VideoShare (video RX one way)
+ */
+ public static final int CALL_TYPE_VS_RX = 10;
+
+ /**
+ * Extra properties for IMS call.
+ */
+ /**
+ * Boolean extra properties - "true" / "false"
+ * conference : Indicates if the session is for the conference call or not.
+ * e_call : Indicates if the session is for the emergency call or not.
+ * vms : Indicates if the session is connected to the voice mail system or not.
+ * call_mode_changeable : Indicates if the session is able to upgrade/downgrade
+ * the video during voice call.
+ * conference_avail : Indicates if the session can be extended to the conference.
+ */
+ /**
+ * @hide
+ */
+ public static final String EXTRA_CONFERENCE = "conference";
+
+ /**
+ * Boolean extra property set on an {@link ImsCallProfile} to indicate that this call is an
+ * emergency call. The {@link ImsService} sets this on a call to indicate that the network has
+ * identified the call as an emergency call.
+ */
+ public static final String EXTRA_EMERGENCY_CALL = "e_call";
+
+ /**
+ * @hide
+ */
+ public static final String EXTRA_VMS = "vms";
+ /**
+ * @hide
+ */
+ public static final String EXTRA_CALL_MODE_CHANGEABLE = "call_mode_changeable";
+ /**
+ * @hide
+ */
+ public static final String EXTRA_CONFERENCE_AVAIL = "conference_avail";
+
+ /**
+ * Extra key used to store a Bundle containing proprietary extras to send to the ImsService.
+ * Use {@link #getProprietaryCallExtras()} instead.
+ * @hide
+ */
+ @TestApi
+ public static final String EXTRA_OEM_EXTRAS = "android.telephony.ims.extra.OEM_EXTRAS";
+
+ /**
+ * Rule for originating identity (number) presentation, MO/MT.
+ * {@link ImsCallProfile#OIR_DEFAULT}
+ * {@link ImsCallProfile#OIR_PRESENTATION_RESTRICTED}
+ * {@link ImsCallProfile#OIR_PRESENTATION_NOT_RESTRICTED}
+ */
+ public static final String EXTRA_OIR = "oir";
+ /**
+ * Rule for calling name presentation
+ * {@link ImsCallProfile#OIR_DEFAULT}
+ * {@link ImsCallProfile#OIR_PRESENTATION_RESTRICTED}
+ * {@link ImsCallProfile#OIR_PRESENTATION_NOT_RESTRICTED}
+ */
+ public static final String EXTRA_CNAP = "cnap";
+ /**
+ * To identify the Ims call type, MO
+ * {@link ImsCallProfile#DIALSTRING_NORMAL}
+ * {@link ImsCallProfile#DIALSTRING_SS_CONF}
+ * {@link ImsCallProfile#DIALSTRING_USSD}
+ */
+ public static final String EXTRA_DIALSTRING = "dialstring";
+ /**
+ * This extra holds call fail cause because of which redial is attempted.
+ * see {@link android.telephony.ims.ImsReasonInfo} {@code CODE_*}
+ * for possible values this extra can hold.
+ *
+ * @hide
+ */
+ public static final String EXTRA_RETRY_CALL_FAIL_REASON =
+ "android.telephony.ims.extra.RETRY_CALL_FAIL_REASON";
+ /**
+ * This extra holds call network type on which lower layers
+ * may try attempting redial.
+ * See {@link TelephonyManager} {@code NETWORK_TYPE_*}
+ * for possible values this extra can hold.
+ *
+ * @hide
+ */
+ public static final String EXTRA_RETRY_CALL_FAIL_NETWORKTYPE =
+ "android.telephony.ims.extra.RETRY_CALL_FAIL_NETWORKTYPE";
+
+ /**
+ * Values for EXTRA_OIR / EXTRA_CNAP
+ */
+ /**
+ * Default presentation for Originating Identity.
+ */
+ public static final int OIR_DEFAULT = 0; // "user subscription default value"
+ /**
+ * Restricted presentation for Originating Identity.
+ */
+ public static final int OIR_PRESENTATION_RESTRICTED = 1;
+ /**
+ * Not restricted presentation for Originating Identity.
+ */
+ public static final int OIR_PRESENTATION_NOT_RESTRICTED = 2;
+ /**
+ * Presentation unknown for Originating Identity.
+ */
+ public static final int OIR_PRESENTATION_UNKNOWN = 3;
+ /**
+ * Payphone presentation for Originating Identity.
+ */
+ public static final int OIR_PRESENTATION_PAYPHONE = 4;
+
+ //Values for EXTRA_DIALSTRING
+ /**
+ * A default or normal normal call.
+ */
+ public static final int DIALSTRING_NORMAL = 0;
+ /**
+ * Call for SIP-based user configuration
+ */
+ public static final int DIALSTRING_SS_CONF = 1;
+ /**
+ * Call for USSD message
+ */
+ public static final int DIALSTRING_USSD = 2;
+
+ /**
+ * Call is not restricted on peer side and High Definition media is supported
+ */
+ public static final int CALL_RESTRICT_CAUSE_NONE = 0;
+
+ /**
+ * High Definition media is not supported on the peer side due to the Radio Access Technology
+ * (RAT) it is are connected to.
+ */
+ public static final int CALL_RESTRICT_CAUSE_RAT = 1;
+
+ /**
+ * The service has been disabled on the peer side.
+ */
+ public static final int CALL_RESTRICT_CAUSE_DISABLED = 2;
+
+ /**
+ * High definition media is not currently supported.
+ */
+ public static final int CALL_RESTRICT_CAUSE_HD = 3;
+
+ /**@hide*/
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = "CALL_RESTRICT_CAUSE_", value = {
+ CALL_RESTRICT_CAUSE_NONE,
+ CALL_RESTRICT_CAUSE_RAT,
+ CALL_RESTRICT_CAUSE_DISABLED,
+ CALL_RESTRICT_CAUSE_HD
+ })
+ public @interface CallRestrictCause {}
+
+ /**
+ * String extra properties
+ * oi : Originating identity (number), MT only
+ * cna : Calling name
+ * ussd : For network-initiated USSD, MT only
+ * remote_uri : Connected user identity (it can be used for the conference)
+ * ChildNum: Child number info.
+ * Codec: Codec info.
+ * DisplayText: Display text for the call.
+ * AdditionalCallInfo: Additional call info.
+ * CallPull: Boolean value specifying if the call is a pulled call.
+ */
+ public static final String EXTRA_OI = "oi";
+ public static final String EXTRA_CNA = "cna";
+ public static final String EXTRA_USSD = "ussd";
+ public static final String EXTRA_REMOTE_URI = "remote_uri";
+ public static final String EXTRA_CHILD_NUMBER = "ChildNum";
+ public static final String EXTRA_CODEC = "Codec";
+ public static final String EXTRA_DISPLAY_TEXT = "DisplayText";
+ public static final String EXTRA_ADDITIONAL_CALL_INFO = "AdditionalCallInfo";
+ public static final String EXTRA_IS_CALL_PULL = "CallPull";
+
+ /**
+ * String extra property
+ * Containing fields from the SIP INVITE message for an IMS call
+ */
+ public static final String EXTRA_ADDITIONAL_SIP_INVITE_FIELDS =
+ "android.telephony.ims.extra.ADDITIONAL_SIP_INVITE_FIELDS";
+
+ /**
+ * CallDisconnectCause: Specify call disconnect cause. This extra should be a code
+ * corresponding to ImsReasonInfo and should only be populated in the case that the
+ * call has already been missed
+ */
+ public static final String EXTRA_CALL_DISCONNECT_CAUSE =
+ "android.telephony.ims.extra.CALL_DISCONNECT_CAUSE";
+
+ /**
+ * Extra key which the RIL can use to indicate the radio technology used for a call.
+ * Valid values are:
+ * {@link android.telephony.ServiceState#RIL_RADIO_TECHNOLOGY_LTE},
+ * {@link android.telephony.ServiceState#RIL_RADIO_TECHNOLOGY_IWLAN}, and the other defined
+ * {@code RIL_RADIO_TECHNOLOGY_*} constants.
+ * Note: Despite the fact the {@link android.telephony.ServiceState} values are integer
+ * constants, the values passed for the {@link #EXTRA_CALL_RAT_TYPE} should be strings (e.g.
+ * "14" vs (int) 14).
+ * Note: This is used by {@link com.android.internal.telephony.imsphone.ImsPhoneConnection#
+ * updateImsCallRatFromExtras(Bundle)} to determine whether to set the
+ * {@link android.telecom.TelecomManager#EXTRA_CALL_NETWORK_TYPE} extra value and
+ * {@link android.telecom.Connection#PROPERTY_WIFI} property on a connection.
+ * @deprecated the constants associated with this extra are hidden, instead use
+ * {@link #EXTRA_CALL_NETWORK_TYPE}.
+ */
+ @Deprecated
+ public static final String EXTRA_CALL_RAT_TYPE = "CallRadioTech";
+
+ /**
+ * Extra key with an {@code int} value which can be set in {@link #setCallExtraInt(String, int)}
+ * to indicate the network type used for a call.
+ * <p>
+ * Valid values are defined by {@code TelephonyManager.NETWORK_TYPE_*} constants. An example may
+ * be {@link android.telephony.TelephonyManager#NETWORK_TYPE_LTE}.
+ */
+ public static final String EXTRA_CALL_NETWORK_TYPE =
+ "android.telephony.ims.extra.CALL_NETWORK_TYPE";
+
+ /**
+ * Similar to {@link #EXTRA_CALL_RAT_TYPE}, except with a lowercase 'c'. Used to ensure
+ * compatibility with modems that are non-compliant with the {@link #EXTRA_CALL_RAT_TYPE}
+ * extra key. Should be removed when the non-compliant modems are fixed.
+ * @hide
+ * @deprecated Use {@link #EXTRA_CALL_NETWORK_TYPE} instead.
+ */
+ @Deprecated
+ public static final String EXTRA_CALL_RAT_TYPE_ALT = "callRadioTech";
+
+ /**
+ * String extra property containing forwarded numbers associated with the current connection
+ * for an IMS call. The value is string array, and it can include multiple numbers, and
+ * the array values are expected E164 (e.g. +1 (650) 253-0000) format.
+ */
+ public static final String EXTRA_FORWARDED_NUMBER =
+ "android.telephony.ims.extra.FORWARDED_NUMBER";
+
+ /** @hide */
+ public int mServiceType;
+ /** @hide */
+ @UnsupportedAppUsage
+ public int mCallType;
+ /** @hide */
+ @UnsupportedAppUsage
+ public @CallRestrictCause int mRestrictCause = CALL_RESTRICT_CAUSE_NONE;
+
+ /**
+ * The VERSTAT for an incoming call's phone number.
+ */
+ private @VerificationStatus int mCallerNumberVerificationStatus;
+
+ /**
+ * Indicates that the network could not perform verification.
+ */
+ public static final int VERIFICATION_STATUS_NOT_VERIFIED = 0;
+
+ /**
+ * Indicates that verification by the network passed. This indicates there is a high likelihood
+ * that the call originated from a valid source.
+ */
+ public static final int VERIFICATION_STATUS_PASSED = 1;
+
+ /**
+ * Indicates that verification by the network failed. This indicates there is a high likelihood
+ * that the call did not originate from a valid source.
+ */
+ public static final int VERIFICATION_STATUS_FAILED = 2;
+
+ /**@hide*/
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = "VERIFICATION_STATUS_", value = {
+ VERIFICATION_STATUS_NOT_VERIFIED,
+ VERIFICATION_STATUS_PASSED,
+ VERIFICATION_STATUS_FAILED
+ })
+ public @interface VerificationStatus {}
+
+ /**
+ * The emergency service categories, only valid if {@link #getServiceType} returns
+ * {@link #SERVICE_TYPE_EMERGENCY}
+ *
+ * If valid, the value is the bitwise-OR combination of the following constants:
+ * <ol>
+ * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED} </li>
+ * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_POLICE} </li>
+ * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_AMBULANCE} </li>
+ * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE} </li>
+ * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD} </li>
+ * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_MOUNTAIN_RESCUE} </li>
+ * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_MIEC} </li>
+ * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_AIEC} </li>
+ * </ol>
+ *
+ * Reference: 3gpp 23.167, Section 6 - Functional description;
+ * 3gpp 22.101, Section 10 - Emergency Calls.
+ */
+ private @EmergencyServiceCategories int mEmergencyServiceCategories =
+ EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED;
+
+ /**
+ * The emergency Uniform Resource Names (URN), only valid if {@link #getServiceType} returns
+ * {@link #SERVICE_TYPE_EMERGENCY}.
+ *
+ * Reference: 3gpp 24.503, Section 5.1.6.8.1 - General;
+ * 3gpp 22.101, Section 10 - Emergency Calls.
+ */
+ private List<String> mEmergencyUrns = new ArrayList<>();
+
+ /**
+ * The emergency call routing, only valid if {@link #getServiceType} returns
+ * {@link #SERVICE_TYPE_EMERGENCY}
+ *
+ * If valid, the value is any of the following constants:
+ * <ol>
+ * <li>{@link EmergencyNumber#EMERGENCY_CALL_ROUTING_UNKNOWN} </li>
+ * <li>{@link EmergencyNumber#EMERGENCY_CALL_ROUTING_NORMAL} </li>
+ * <li>{@link EmergencyNumber#EMERGENCY_CALL_ROUTING_EMERGENCY} </li>
+ * </ol>
+ */
+ private @EmergencyCallRouting int mEmergencyCallRouting =
+ EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN;
+
+ /** Indicates if the call is for testing purpose */
+ private boolean mEmergencyCallTesting = false;
+
+ /** Indicates if we have known the intent of the user for the call is emergency */
+ private boolean mHasKnownUserIntentEmergency = false;
+
+ /**
+ * Extras associated with this {@link ImsCallProfile}.
+ * <p>
+ * Valid data types include:
+ * <ul>
+ * <li>{@link Integer} (and int)</li>
+ * <li>{@link Long} (and long)</li>
+ * <li>{@link Double} (and double)</li>
+ * <li>{@link String}</li>
+ * <li>{@code int[]}</li>
+ * <li>{@code long[]}</li>
+ * <li>{@code double[]}</li>
+ * <li>{@code String[]}</li>
+ * <li>{@link android.os.PersistableBundle}</li>
+ * <li>{@link Boolean} (and boolean)</li>
+ * <li>{@code boolean[]}</li>
+ * <li>Other {@link Parcelable} classes in the {@code android.*} namespace.</li>
+ * </ul>
+ * <p>
+ * Invalid types will be removed when the {@link ImsCallProfile} is parceled for transmit across
+ * a {@link android.os.Binder}.
+ */
+ /** @hide */
+ @UnsupportedAppUsage
+ public Bundle mCallExtras;
+ /** @hide */
+ @UnsupportedAppUsage
+ public ImsStreamMediaProfile mMediaProfile;
+
+ /** @hide */
+ public ImsCallProfile(Parcel in) {
+ readFromParcel(in);
+ }
+
+ /**
+ * Default Constructor that initializes the call profile with service type
+ * {@link #SERVICE_TYPE_NORMAL} and call type {@link #CALL_TYPE_VIDEO_N_VOICE}
+ */
+ public ImsCallProfile() {
+ mServiceType = SERVICE_TYPE_NORMAL;
+ mCallType = CALL_TYPE_VOICE_N_VIDEO;
+ mCallExtras = new Bundle();
+ mMediaProfile = new ImsStreamMediaProfile();
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param serviceType the service type for the call. Can be one of the following:
+ * {@link #SERVICE_TYPE_NONE},
+ * {@link #SERVICE_TYPE_NORMAL},
+ * {@link #SERVICE_TYPE_EMERGENCY}
+ * @param callType the call type. Can be one of the following:
+ * {@link #CALL_TYPE_VOICE_N_VIDEO},
+ * {@link #CALL_TYPE_VOICE},
+ * {@link #CALL_TYPE_VIDEO_N_VOICE},
+ * {@link #CALL_TYPE_VT},
+ * {@link #CALL_TYPE_VT_TX},
+ * {@link #CALL_TYPE_VT_RX},
+ * {@link #CALL_TYPE_VT_NODIR},
+ * {@link #CALL_TYPE_VS},
+ * {@link #CALL_TYPE_VS_TX},
+ * {@link #CALL_TYPE_VS_RX}
+ */
+ public ImsCallProfile(int serviceType, int callType) {
+ mServiceType = serviceType;
+ mCallType = callType;
+ mCallExtras = new Bundle();
+ mMediaProfile = new ImsStreamMediaProfile();
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param serviceType the service type for the call. Can be one of the following:
+ * {@link #SERVICE_TYPE_NONE},
+ * {@link #SERVICE_TYPE_NORMAL},
+ * {@link #SERVICE_TYPE_EMERGENCY}
+ * @param callType the call type. Can be one of the following:
+ * {@link #CALL_TYPE_VOICE_N_VIDEO},
+ * {@link #CALL_TYPE_VOICE},
+ * {@link #CALL_TYPE_VIDEO_N_VOICE},
+ * {@link #CALL_TYPE_VT},
+ * {@link #CALL_TYPE_VT_TX},
+ * {@link #CALL_TYPE_VT_RX},
+ * {@link #CALL_TYPE_VT_NODIR},
+ * {@link #CALL_TYPE_VS},
+ * {@link #CALL_TYPE_VS_TX},
+ * {@link #CALL_TYPE_VS_RX}
+ * @param callExtras A bundle with the call extras.
+ * @param mediaProfile The IMS stream media profile.
+ */
+ public ImsCallProfile(int serviceType, int callType, Bundle callExtras,
+ ImsStreamMediaProfile mediaProfile) {
+ mServiceType = serviceType;
+ mCallType = callType;
+ mCallExtras = callExtras;
+ mMediaProfile = mediaProfile;
+ }
+
+ public String getCallExtra(String name) {
+ return getCallExtra(name, "");
+ }
+
+ public String getCallExtra(String name, String defaultValue) {
+ if (mCallExtras == null) {
+ return defaultValue;
+ }
+
+ return mCallExtras.getString(name, defaultValue);
+ }
+
+ public boolean getCallExtraBoolean(String name) {
+ return getCallExtraBoolean(name, false);
+ }
+
+ public boolean getCallExtraBoolean(String name, boolean defaultValue) {
+ if (mCallExtras == null) {
+ return defaultValue;
+ }
+
+ return mCallExtras.getBoolean(name, defaultValue);
+ }
+
+ public int getCallExtraInt(String name) {
+ return getCallExtraInt(name, -1);
+ }
+
+ public int getCallExtraInt(String name, int defaultValue) {
+ if (mCallExtras == null) {
+ return defaultValue;
+ }
+
+ return mCallExtras.getInt(name, defaultValue);
+ }
+
+ public void setCallExtra(String name, String value) {
+ if (mCallExtras != null) {
+ mCallExtras.putString(name, value);
+ }
+ }
+
+ public void setCallExtraBoolean(String name, boolean value) {
+ if (mCallExtras != null) {
+ mCallExtras.putBoolean(name, value);
+ }
+ }
+
+ public void setCallExtraInt(String name, int value) {
+ if (mCallExtras != null) {
+ mCallExtras.putInt(name, value);
+ }
+ }
+
+ /**
+ * Set the call restrict cause, which provides the reason why a call has been restricted from
+ * using High Definition media.
+ */
+ public void setCallRestrictCause(@CallRestrictCause int cause) {
+ mRestrictCause = cause;
+ }
+
+ public void updateCallType(ImsCallProfile profile) {
+ mCallType = profile.mCallType;
+ }
+
+ public void updateCallExtras(ImsCallProfile profile) {
+ mCallExtras.clear();
+ mCallExtras = (Bundle) profile.mCallExtras.clone();
+ }
+
+ /**
+ * Updates the media profile for the call.
+ *
+ * @param profile Call profile with new media profile.
+ */
+ public void updateMediaProfile(ImsCallProfile profile) {
+ mMediaProfile = profile.mMediaProfile;
+ }
+
+ /**
+ * Sets the verification status for the phone number of an incoming call as identified in
+ * ATIS-1000082.
+ * <p>
+ * The ImsService should parse the verstat information from the SIP INVITE headers for the call
+ * to determine this information. It is typically found in the P-Asserted-Identity OR From
+ * header fields.
+ * @param callerNumberVerificationStatus the new verification status.
+ */
+ public void setCallerNumberVerificationStatus(
+ @VerificationStatus int callerNumberVerificationStatus) {
+ mCallerNumberVerificationStatus = callerNumberVerificationStatus;
+ }
+
+ /**
+ * Gets the verification status for the phone number of an incoming call as identified in
+ * ATIS-1000082.
+ * @return the verification status.
+ */
+ public @VerificationStatus int getCallerNumberVerificationStatus() {
+ return mCallerNumberVerificationStatus;
+ }
+
+ @NonNull
+ @Override
+ public String toString() {
+ return "{ serviceType=" + mServiceType
+ + ", callType=" + mCallType
+ + ", restrictCause=" + mRestrictCause
+ + ", mediaProfile=" + (mMediaProfile != null ? mMediaProfile.toString() : "null")
+ + ", emergencyServiceCategories=" + mEmergencyServiceCategories
+ + ", emergencyUrns=" + mEmergencyUrns
+ + ", emergencyCallRouting=" + mEmergencyCallRouting
+ + ", emergencyCallTesting=" + mEmergencyCallTesting
+ + ", hasKnownUserIntentEmergency=" + mHasKnownUserIntentEmergency
+ + ", mRestrictCause=" + mRestrictCause
+ + ", mCallerNumberVerstat= " + mCallerNumberVerificationStatus + " }";
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ Bundle filteredExtras = maybeCleanseExtras(mCallExtras);
+ out.writeInt(mServiceType);
+ out.writeInt(mCallType);
+ out.writeBundle(filteredExtras);
+ out.writeParcelable(mMediaProfile, 0);
+ out.writeInt(mEmergencyServiceCategories);
+ out.writeStringList(mEmergencyUrns);
+ out.writeInt(mEmergencyCallRouting);
+ out.writeBoolean(mEmergencyCallTesting);
+ out.writeBoolean(mHasKnownUserIntentEmergency);
+ out.writeInt(mRestrictCause);
+ out.writeInt(mCallerNumberVerificationStatus);
+ }
+
+ private void readFromParcel(Parcel in) {
+ mServiceType = in.readInt();
+ mCallType = in.readInt();
+ mCallExtras = in.readBundle();
+ mMediaProfile = in.readParcelable(ImsStreamMediaProfile.class.getClassLoader());
+ mEmergencyServiceCategories = in.readInt();
+ mEmergencyUrns = in.createStringArrayList();
+ mEmergencyCallRouting = in.readInt();
+ mEmergencyCallTesting = in.readBoolean();
+ mHasKnownUserIntentEmergency = in.readBoolean();
+ mRestrictCause = in.readInt();
+ mCallerNumberVerificationStatus = in.readInt();
+ }
+
+ public static final @android.annotation.NonNull Creator<ImsCallProfile> CREATOR = new Creator<ImsCallProfile>() {
+ @Override
+ public ImsCallProfile createFromParcel(Parcel in) {
+ return new ImsCallProfile(in);
+ }
+
+ @Override
+ public ImsCallProfile[] newArray(int size) {
+ return new ImsCallProfile[size];
+ }
+ };
+
+ public int getServiceType() {
+ return mServiceType;
+ }
+
+ public int getCallType() {
+ return mCallType;
+ }
+
+ /**
+ * @return The call restrict cause, which provides the reason why a call has been restricted
+ * from using High Definition media.
+ */
+ public @CallRestrictCause int getRestrictCause() {
+ return mRestrictCause;
+ }
+
+ public Bundle getCallExtras() {
+ return mCallExtras;
+ }
+
+ /**
+ * Get the proprietary extras set for this ImsCallProfile.
+ * @return A {@link Bundle} containing proprietary call extras that were not set by the
+ * platform.
+ */
+ public @NonNull Bundle getProprietaryCallExtras() {
+ if (mCallExtras == null) {
+ return new Bundle();
+ }
+ Bundle proprietaryExtras = mCallExtras.getBundle(EXTRA_OEM_EXTRAS);
+ if (proprietaryExtras == null) {
+ return new Bundle();
+ }
+ // Make a copy so users do not accidentally change this copy of the extras.
+ return new Bundle(proprietaryExtras);
+ }
+
+ public ImsStreamMediaProfile getMediaProfile() {
+ return mMediaProfile;
+ }
+
+ /**
+ * Converts from the call types defined in {@link ImsCallProfile} to the
+ * video state values defined in {@link VideoProfile}.
+ *
+ * @param callProfile The call profile.
+ * @return The video state.
+ */
+ public static int getVideoStateFromImsCallProfile(ImsCallProfile callProfile) {
+ int videostate = getVideoStateFromCallType(callProfile.mCallType);
+ if (callProfile.isVideoPaused() && !VideoProfile.isAudioOnly(videostate)) {
+ videostate |= VideoProfile.STATE_PAUSED;
+ } else {
+ videostate &= ~VideoProfile.STATE_PAUSED;
+ }
+ return videostate;
+ }
+
+ /**
+ * Translates a {@link ImsCallProfile} {@code CALL_TYPE_*} constant into a video state.
+ * @param callType The call type.
+ * @return The video state.
+ */
+ public static int getVideoStateFromCallType(int callType) {
+ int videostate = VideoProfile.STATE_AUDIO_ONLY;
+ switch (callType) {
+ case CALL_TYPE_VT_TX:
+ videostate = VideoProfile.STATE_TX_ENABLED;
+ break;
+ case CALL_TYPE_VT_RX:
+ videostate = VideoProfile.STATE_RX_ENABLED;
+ break;
+ case CALL_TYPE_VT:
+ videostate = VideoProfile.STATE_BIDIRECTIONAL;
+ break;
+ case CALL_TYPE_VOICE:
+ videostate = VideoProfile.STATE_AUDIO_ONLY;
+ break;
+ default:
+ videostate = VideoProfile.STATE_AUDIO_ONLY;
+ break;
+ }
+ return videostate;
+ }
+
+ /**
+ * Converts from the video state values defined in {@link VideoProfile}
+ * to the call types defined in {@link ImsCallProfile}.
+ *
+ * @param videoState The video state.
+ * @return The call type.
+ */
+ public static int getCallTypeFromVideoState(int videoState) {
+ boolean videoTx = isVideoStateSet(videoState, VideoProfile.STATE_TX_ENABLED);
+ boolean videoRx = isVideoStateSet(videoState, VideoProfile.STATE_RX_ENABLED);
+ boolean isPaused = isVideoStateSet(videoState, VideoProfile.STATE_PAUSED);
+ if (isPaused) {
+ return ImsCallProfile.CALL_TYPE_VT_NODIR;
+ } else if (videoTx && !videoRx) {
+ return ImsCallProfile.CALL_TYPE_VT_TX;
+ } else if (!videoTx && videoRx) {
+ return ImsCallProfile.CALL_TYPE_VT_RX;
+ } else if (videoTx && videoRx) {
+ return ImsCallProfile.CALL_TYPE_VT;
+ }
+ return ImsCallProfile.CALL_TYPE_VOICE;
+ }
+
+ /**
+ * Badly named old method, kept for compatibility.
+ * See {@link #presentationToOir(int)}.
+ * @hide
+ */
+ @UnsupportedAppUsage
+ public static int presentationToOIR(int presentation) {
+ switch (presentation) {
+ case PhoneConstants.PRESENTATION_RESTRICTED:
+ return ImsCallProfile.OIR_PRESENTATION_RESTRICTED;
+ case PhoneConstants.PRESENTATION_ALLOWED:
+ return ImsCallProfile.OIR_PRESENTATION_NOT_RESTRICTED;
+ case PhoneConstants.PRESENTATION_PAYPHONE:
+ return ImsCallProfile.OIR_PRESENTATION_PAYPHONE;
+ case PhoneConstants.PRESENTATION_UNKNOWN:
+ return ImsCallProfile.OIR_PRESENTATION_UNKNOWN;
+ default:
+ return ImsCallProfile.OIR_DEFAULT;
+ }
+ }
+
+ /**
+ * Translate presentation value to OIR value
+ * @param presentation
+ * @return OIR values
+ */
+ public static int presentationToOir(int presentation) {
+ return presentationToOIR(presentation);
+ }
+
+ /**
+ * Translate OIR value to presentation value
+ * @param oir value
+ * @return presentation value
+ * @hide
+ */
+ public static int OIRToPresentation(int oir) {
+ switch(oir) {
+ case ImsCallProfile.OIR_PRESENTATION_RESTRICTED:
+ return PhoneConstants.PRESENTATION_RESTRICTED;
+ case ImsCallProfile.OIR_PRESENTATION_NOT_RESTRICTED:
+ return PhoneConstants.PRESENTATION_ALLOWED;
+ case ImsCallProfile.OIR_PRESENTATION_PAYPHONE:
+ return PhoneConstants.PRESENTATION_PAYPHONE;
+ case ImsCallProfile.OIR_PRESENTATION_UNKNOWN:
+ return PhoneConstants.PRESENTATION_UNKNOWN;
+ default:
+ return PhoneConstants.PRESENTATION_UNKNOWN;
+ }
+ }
+
+ /**
+ * Checks if video call is paused
+ * @return true if call is video paused
+ */
+ public boolean isVideoPaused() {
+ return mMediaProfile.mVideoDirection == ImsStreamMediaProfile.DIRECTION_INACTIVE;
+ }
+
+ /**
+ * Determines if the {@link ImsCallProfile} represents a video call.
+ *
+ * @return {@code true} if the profile is for a video call, {@code false} otherwise.
+ */
+ public boolean isVideoCall() {
+ return VideoProfile.isVideo(getVideoStateFromCallType(mCallType));
+ }
+
+ /**
+ * Cleanses a {@link Bundle} to ensure that it contains only data of type:
+ * 1. Primitive data types (e.g. int, bool, and other values determined by
+ * {@link android.os.PersistableBundle#isValidType(Object)}).
+ * 2. Other Bundles.
+ * 3. {@link Parcelable} objects in the {@code android.*} namespace.
+ * @param extras the source {@link Bundle}
+ * @return where all elements are valid types the source {@link Bundle} is returned unmodified,
+ * otherwise a copy of the {@link Bundle} with the invalid elements is returned.
+ */
+ private Bundle maybeCleanseExtras(Bundle extras) {
+ if (extras == null) {
+ return null;
+ }
+
+ int startSize = extras.size();
+ Bundle filtered = TelephonyUtils.filterValues(extras);
+ int endSize = filtered.size();
+ if (startSize != endSize) {
+ Log.i(TAG, "maybeCleanseExtras: " + (startSize - endSize) + " extra values were "
+ + "removed - only primitive types and system parcelables are permitted.");
+ }
+ return filtered;
+ }
+
+ /**
+ * Determines if a video state is set in a video state bit-mask.
+ *
+ * @param videoState The video state bit mask.
+ * @param videoStateToCheck The particular video state to check.
+ * @return True if the video state is set in the bit-mask.
+ */
+ private static boolean isVideoStateSet(int videoState, int videoStateToCheck) {
+ return (videoState & videoStateToCheck) == videoStateToCheck;
+ }
+
+ /**
+ * Set the emergency number information. The set value is valid
+ * only if {@link #getServiceType} returns {@link #SERVICE_TYPE_EMERGENCY}
+ *
+ * Reference: 3gpp 23.167, Section 6 - Functional description;
+ * 3gpp 24.503, Section 5.1.6.8.1 - General;
+ * 3gpp 22.101, Section 10 - Emergency Calls.
+ *
+ * @hide
+ */
+ public void setEmergencyCallInfo(EmergencyNumber num, boolean hasKnownUserIntentEmergency) {
+ setEmergencyServiceCategories(num.getEmergencyServiceCategoryBitmaskInternalDial());
+ setEmergencyUrns(num.getEmergencyUrns());
+ setEmergencyCallRouting(num.getEmergencyCallRouting());
+ setEmergencyCallTesting(num.getEmergencyNumberSourceBitmask()
+ == EmergencyNumber.EMERGENCY_NUMBER_SOURCE_TEST);
+ setHasKnownUserIntentEmergency(hasKnownUserIntentEmergency);
+ }
+
+ /**
+ * Set the emergency service categories. The set value is valid only if
+ * {@link #getServiceType} returns {@link #SERVICE_TYPE_EMERGENCY}
+ *
+ * If valid, the value is the bitwise-OR combination of the following constants:
+ * <ol>
+ * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED} </li>
+ * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_POLICE} </li>
+ * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_AMBULANCE} </li>
+ * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE} </li>
+ * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD} </li>
+ * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_MOUNTAIN_RESCUE} </li>
+ * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_MIEC} </li>
+ * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_AIEC} </li>
+ * </ol>
+ *
+ * Reference: 3gpp 23.167, Section 6 - Functional description;
+ * 3gpp 22.101, Section 10 - Emergency Calls.
+ */
+ @VisibleForTesting
+ public void setEmergencyServiceCategories(
+ @EmergencyServiceCategories int emergencyServiceCategories) {
+ mEmergencyServiceCategories = emergencyServiceCategories;
+ }
+
+ /**
+ * Set the emergency Uniform Resource Names (URN), only valid if {@link #getServiceType}
+ * returns {@link #SERVICE_TYPE_EMERGENCY}.
+ *
+ * Reference: 3gpp 24.503, Section 5.1.6.8.1 - General;
+ * 3gpp 22.101, Section 10 - Emergency Calls.
+ */
+ @VisibleForTesting
+ public void setEmergencyUrns(@NonNull List<String> emergencyUrns) {
+ mEmergencyUrns = emergencyUrns;
+ }
+
+ /**
+ * Set the emergency call routing, only valid if {@link #getServiceType} returns
+ * {@link #SERVICE_TYPE_EMERGENCY}
+ *
+ * If valid, the value is any of the following constants:
+ * <ol>
+ * <li>{@link EmergencyNumber#EMERGENCY_CALL_ROUTING_UNKNOWN} </li>
+ * <li>{@link EmergencyNumber#EMERGENCY_CALL_ROUTING_NORMAL} </li>
+ * <li>{@link EmergencyNumber#EMERGENCY_CALL_ROUTING_EMERGENCY} </li>
+ * </ol>
+ */
+ @VisibleForTesting
+ public void setEmergencyCallRouting(@EmergencyCallRouting int emergencyCallRouting) {
+ mEmergencyCallRouting = emergencyCallRouting;
+ }
+
+ /**
+ * Set if this is for testing emergency call, only valid if {@link #getServiceType} returns
+ * {@link #SERVICE_TYPE_EMERGENCY}.
+ */
+ @VisibleForTesting
+ public void setEmergencyCallTesting(boolean isTesting) {
+ mEmergencyCallTesting = isTesting;
+ }
+
+ /**
+ * Set if we have known the user intent of the call is emergency.
+ *
+ * This is only used to specify when the dialed number is ambiguous when it can be identified
+ * as both emergency number and any other non-emergency number; e.g. in some situation, 611
+ * could be both an emergency number in a country and a non-emergency number of a carrier's
+ * customer service hotline.
+ */
+ @VisibleForTesting
+ public void setHasKnownUserIntentEmergency(boolean hasKnownUserIntentEmergency) {
+ mHasKnownUserIntentEmergency = hasKnownUserIntentEmergency;
+ }
+
+ /**
+ * Get the emergency service categories, only valid if {@link #getServiceType} returns
+ * {@link #SERVICE_TYPE_EMERGENCY}
+ *
+ * @return the emergency service categories,
+ *
+ * If valid, the value is the bitwise-OR combination of the following constants:
+ * <ol>
+ * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED} </li>
+ * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_POLICE} </li>
+ * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_AMBULANCE} </li>
+ * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE} </li>
+ * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD} </li>
+ * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_MOUNTAIN_RESCUE} </li>
+ * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_MIEC} </li>
+ * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_AIEC} </li>
+ * </ol>
+ *
+ * Reference: 3gpp 23.167, Section 6 - Functional description;
+ * 3gpp 22.101, Section 10 - Emergency Calls.
+ */
+ public @EmergencyServiceCategories int getEmergencyServiceCategories() {
+ return mEmergencyServiceCategories;
+ }
+
+ /**
+ * Get the emergency Uniform Resource Names (URN), only valid if {@link #getServiceType}
+ * returns {@link #SERVICE_TYPE_EMERGENCY}.
+ *
+ * Reference: 3gpp 24.503, Section 5.1.6.8.1 - General;
+ * 3gpp 22.101, Section 10 - Emergency Calls.
+ */
+ public @NonNull List<String> getEmergencyUrns() {
+ return mEmergencyUrns;
+ }
+
+ /**
+ * Get the emergency call routing, only valid if {@link #getServiceType} returns
+ * {@link #SERVICE_TYPE_EMERGENCY}
+ *
+ * If valid, the value is any of the following constants:
+ * <ol>
+ * <li>{@link EmergencyNumber#EMERGENCY_CALL_ROUTING_UNKNOWN} </li>
+ * <li>{@link EmergencyNumber#EMERGENCY_CALL_ROUTING_NORMAL} </li>
+ * <li>{@link EmergencyNumber#EMERGENCY_CALL_ROUTING_EMERGENCY} </li>
+ * </ol>
+ */
+ public @EmergencyCallRouting int getEmergencyCallRouting() {
+ return mEmergencyCallRouting;
+ }
+
+ /**
+ * Get if the emergency call is for testing purpose.
+ */
+ public boolean isEmergencyCallTesting() {
+ return mEmergencyCallTesting;
+ }
+
+ /**
+ * Checks if we have known the user intent of the call is emergency.
+ *
+ * This is only used to specify when the dialed number is ambiguous when it can be identified
+ * as both emergency number and any other non-emergency number; e.g. in some situation, 611
+ * could be both an emergency number in a country and a non-emergency number of a carrier's
+ * customer service hotline.
+ */
+ public boolean hasKnownUserIntentEmergency() {
+ return mHasKnownUserIntentEmergency;
+ }
+}
diff --git a/android/telephony/ims/ImsCallSession.java b/android/telephony/ims/ImsCallSession.java
new file mode 100644
index 0000000..80c38cb
--- /dev/null
+++ b/android/telephony/ims/ImsCallSession.java
@@ -0,0 +1,1508 @@
+/*
+ * 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.Nullable;
+import android.os.Message;
+import android.os.RemoteException;
+import android.telephony.CallQuality;
+import android.telephony.ims.aidl.IImsCallSessionListener;
+import android.util.Log;
+
+import com.android.ims.internal.IImsCallSession;
+import com.android.ims.internal.IImsVideoCallProvider;
+
+/**
+ * Provides the call initiation/termination, and media exchange between two IMS endpoints.
+ * It directly communicates with IMS service which implements the IMS protocol behavior.
+ *
+ * @hide
+ */
+public class ImsCallSession {
+ private static final String TAG = "ImsCallSession";
+
+ /**
+ * Defines IMS call session state. Please use
+ * {@link android.telephony.ims.stub.ImsCallSessionImplBase.State} definition.
+ * This is kept around for capability reasons.
+ */
+ public static class State {
+ public static final int IDLE = 0;
+ public static final int INITIATED = 1;
+ public static final int NEGOTIATING = 2;
+ public static final int ESTABLISHING = 3;
+ public static final int ESTABLISHED = 4;
+
+ public static final int RENEGOTIATING = 5;
+ public static final int REESTABLISHING = 6;
+
+ public static final int TERMINATING = 7;
+ public static final int TERMINATED = 8;
+
+ public static final int INVALID = (-1);
+
+ /**
+ * Converts the state to string.
+ */
+ public static String toString(int state) {
+ switch (state) {
+ case IDLE:
+ return "IDLE";
+ case INITIATED:
+ return "INITIATED";
+ case NEGOTIATING:
+ return "NEGOTIATING";
+ case ESTABLISHING:
+ return "ESTABLISHING";
+ case ESTABLISHED:
+ return "ESTABLISHED";
+ case RENEGOTIATING:
+ return "RENEGOTIATING";
+ case REESTABLISHING:
+ return "REESTABLISHING";
+ case TERMINATING:
+ return "TERMINATING";
+ case TERMINATED:
+ return "TERMINATED";
+ default:
+ return "UNKNOWN";
+ }
+ }
+
+ private State() {
+ }
+ }
+
+ /**
+ * Listener for events relating to an IMS session, such as when a session is being
+ * recieved ("on ringing") or a call is outgoing ("on calling").
+ * <p>Many of these events are also received by {@link ImsCall.Listener}.</p>
+ * @hide
+ */
+ public static class Listener {
+ /**
+ * Called when a request is sent out to initiate a new session
+ * and 1xx response is received from the network.
+ *
+ * @param session the session object that carries out the IMS session
+ */
+ public void callSessionProgressing(ImsCallSession session,
+ ImsStreamMediaProfile profile) {
+ // no-op
+ }
+
+ /**
+ * Called when the session is established.
+ *
+ * @param session the session object that carries out the IMS session
+ */
+ public void callSessionStarted(ImsCallSession session,
+ ImsCallProfile profile) {
+ // no-op
+ }
+
+ /**
+ * Called when the session establishment is failed.
+ *
+ * @param session the session object that carries out the IMS session
+ * @param reasonInfo detailed reason of the session establishment failure
+ */
+ public void callSessionStartFailed(ImsCallSession session,
+ ImsReasonInfo reasonInfo) {
+ }
+
+ /**
+ * Called when the session is terminated.
+ *
+ * @param session the session object that carries out the IMS session
+ * @param reasonInfo detailed reason of the session termination
+ */
+ public void callSessionTerminated(ImsCallSession session,
+ ImsReasonInfo reasonInfo) {
+ }
+
+ /**
+ * Called when the session is in hold.
+ *
+ * @param session the session object that carries out the IMS session
+ */
+ public void callSessionHeld(ImsCallSession session,
+ ImsCallProfile profile) {
+ }
+
+ /**
+ * Called when the session hold is failed.
+ *
+ * @param session the session object that carries out the IMS session
+ * @param reasonInfo detailed reason of the session hold failure
+ */
+ public void callSessionHoldFailed(ImsCallSession session,
+ ImsReasonInfo reasonInfo) {
+ }
+
+ /**
+ * Called when the session hold is received from the remote user.
+ *
+ * @param session the session object that carries out the IMS session
+ */
+ public void callSessionHoldReceived(ImsCallSession session,
+ ImsCallProfile profile) {
+ }
+
+ /**
+ * Called when the session resume is done.
+ *
+ * @param session the session object that carries out the IMS session
+ */
+ public void callSessionResumed(ImsCallSession session,
+ ImsCallProfile profile) {
+ }
+
+ /**
+ * Called when the session resume is failed.
+ *
+ * @param session the session object that carries out the IMS session
+ * @param reasonInfo detailed reason of the session resume failure
+ */
+ public void callSessionResumeFailed(ImsCallSession session,
+ ImsReasonInfo reasonInfo) {
+ }
+
+ /**
+ * Called when the session resume is received from the remote user.
+ *
+ * @param session the session object that carries out the IMS session
+ */
+ public void callSessionResumeReceived(ImsCallSession session,
+ ImsCallProfile profile) {
+ }
+
+ /**
+ * Called when the session merge has been started. At this point, the {@code newSession}
+ * represents the session which has been initiated to the IMS conference server for the
+ * new merged conference.
+ *
+ * @param session the session object that carries out the IMS session
+ * @param newSession the session object that is merged with an active & hold session
+ */
+ public void callSessionMergeStarted(ImsCallSession session,
+ ImsCallSession newSession, ImsCallProfile profile) {
+ }
+
+ /**
+ * Called when the session merge is successful and the merged session is active.
+ *
+ * @param session the session object that carries out the IMS session
+ */
+ public void callSessionMergeComplete(ImsCallSession session) {
+ }
+
+ /**
+ * Called when the session merge has failed.
+ *
+ * @param session the session object that carries out the IMS session
+ * @param reasonInfo detailed reason of the call merge failure
+ */
+ public void callSessionMergeFailed(ImsCallSession session,
+ ImsReasonInfo reasonInfo) {
+ }
+
+ /**
+ * Called when the session is updated (except for hold/unhold).
+ *
+ * @param session the session object that carries out the IMS session
+ */
+ public void callSessionUpdated(ImsCallSession session,
+ ImsCallProfile profile) {
+ }
+
+ /**
+ * Called when the session update is failed.
+ *
+ * @param session the session object that carries out the IMS session
+ * @param reasonInfo detailed reason of the session update failure
+ */
+ public void callSessionUpdateFailed(ImsCallSession session,
+ ImsReasonInfo reasonInfo) {
+ }
+
+ /**
+ * Called when the session update is received from the remote user.
+ *
+ * @param session the session object that carries out the IMS session
+ */
+ public void callSessionUpdateReceived(ImsCallSession session,
+ ImsCallProfile profile) {
+ // no-op
+ }
+
+ /**
+ * Called when the session is extended to the conference session.
+ *
+ * @param session the session object that carries out the IMS session
+ * @param newSession the session object that is extended to the conference
+ * from the active session
+ */
+ public void callSessionConferenceExtended(ImsCallSession session,
+ ImsCallSession newSession, ImsCallProfile profile) {
+ }
+
+ /**
+ * Called when the conference extension is failed.
+ *
+ * @param session the session object that carries out the IMS session
+ * @param reasonInfo detailed reason of the conference extension failure
+ */
+ public void callSessionConferenceExtendFailed(ImsCallSession session,
+ ImsReasonInfo reasonInfo) {
+ }
+
+ /**
+ * Called when the conference extension is received from the remote user.
+ *
+ * @param session the session object that carries out the IMS session
+ */
+ public void callSessionConferenceExtendReceived(ImsCallSession session,
+ ImsCallSession newSession, ImsCallProfile profile) {
+ // no-op
+ }
+
+ /**
+ * Called when the invitation request of the participants is delivered to the conference
+ * server.
+ *
+ * @param session the session object that carries out the IMS session
+ */
+ public void callSessionInviteParticipantsRequestDelivered(ImsCallSession session) {
+ // no-op
+ }
+
+ /**
+ * Called when the invitation request of the participants is failed.
+ *
+ * @param session the session object that carries out the IMS session
+ * @param reasonInfo detailed reason of the conference invitation failure
+ */
+ public void callSessionInviteParticipantsRequestFailed(ImsCallSession session,
+ ImsReasonInfo reasonInfo) {
+ // no-op
+ }
+
+ /**
+ * Called when the removal request of the participants is delivered to the conference
+ * server.
+ *
+ * @param session the session object that carries out the IMS session
+ */
+ public void callSessionRemoveParticipantsRequestDelivered(ImsCallSession session) {
+ // no-op
+ }
+
+ /**
+ * Called when the removal request of the participants is failed.
+ *
+ * @param session the session object that carries out the IMS session
+ * @param reasonInfo detailed reason of the conference removal failure
+ */
+ public void callSessionRemoveParticipantsRequestFailed(ImsCallSession session,
+ ImsReasonInfo reasonInfo) {
+ // no-op
+ }
+
+ /**
+ * Called when the conference state is updated.
+ *
+ * @param session the session object that carries out the IMS session
+ */
+ public void callSessionConferenceStateUpdated(ImsCallSession session,
+ ImsConferenceState state) {
+ // no-op
+ }
+
+ /**
+ * Called when the USSD message is received from the network.
+ *
+ * @param mode mode of the USSD message (REQUEST / NOTIFY)
+ * @param ussdMessage USSD message
+ */
+ public void callSessionUssdMessageReceived(ImsCallSession session,
+ int mode, String ussdMessage) {
+ // no-op
+ }
+
+ /**
+ * Called when an {@link ImsCallSession} may handover from one network type to another.
+ * For example, the session may handover from WIFI to LTE if conditions are right.
+ * <p>
+ * If handover is attempted,
+ * {@link #callSessionHandover(ImsCallSession, int, int, ImsReasonInfo)} or
+ * {@link #callSessionHandoverFailed(ImsCallSession, int, int, ImsReasonInfo)} will be
+ * called to indicate the success or failure of the handover.
+ *
+ * @param session IMS session object
+ * @param srcNetworkType original network type
+ * @param targetNetworkType new network type
+ */
+ public void callSessionMayHandover(ImsCallSession session, int srcNetworkType,
+ int targetNetworkType) {
+ // no-op
+ }
+
+ /**
+ * Called when session network type changes
+ *
+ * @param session IMS session object
+ * @param srcNetworkType original network type
+ * @param targetNetworkType new network type
+ * @param reasonInfo
+ */
+ public void callSessionHandover(ImsCallSession session,
+ int srcNetworkType, int targetNetworkType,
+ ImsReasonInfo reasonInfo) {
+ // no-op
+ }
+
+ /**
+ * Called when session access technology change fails
+ *
+ * @param session IMS session object
+ * @param srcNetworkType original access technology
+ * @param targetNetworkType new access technology
+ * @param reasonInfo handover failure reason
+ */
+ public void callSessionHandoverFailed(ImsCallSession session,
+ int srcNetworkType, int targetNetworkType,
+ ImsReasonInfo reasonInfo) {
+ // no-op
+ }
+
+ /**
+ * Called when TTY mode of remote party changed
+ *
+ * @param session IMS session object
+ * @param mode TTY mode of remote party
+ */
+ public void callSessionTtyModeReceived(ImsCallSession session,
+ int mode) {
+ // no-op
+ }
+
+ /**
+ * Notifies of a change to the multiparty state for this {@code ImsCallSession}.
+ *
+ * @param session The call session.
+ * @param isMultiParty {@code true} if the session became multiparty, {@code false}
+ * otherwise.
+ */
+ public void callSessionMultipartyStateChanged(ImsCallSession session,
+ boolean isMultiParty) {
+ // no-op
+ }
+
+ /**
+ * Called when the session supplementary service is received
+ *
+ * @param session the session object that carries out the IMS session
+ */
+ public void callSessionSuppServiceReceived(ImsCallSession session,
+ ImsSuppServiceNotification suppServiceInfo) {
+ }
+
+ /**
+ * Received RTT modify request from Remote Party
+ */
+ public void callSessionRttModifyRequestReceived(ImsCallSession session,
+ ImsCallProfile callProfile) {
+ // no-op
+ }
+
+ /**
+ * Received response for RTT modify request
+ */
+ public void callSessionRttModifyResponseReceived(int status) {
+ // no -op
+ }
+
+ /**
+ * Device received RTT message from Remote UE
+ */
+ public void callSessionRttMessageReceived(String rttMessage) {
+ // no-op
+ }
+
+ /**
+ * While in call, there has been a change in RTT audio indicator.
+ */
+ public void callSessionRttAudioIndicatorChanged(ImsStreamMediaProfile profile) {
+ // no-op
+ }
+
+ /**
+ * Received success response for call transfer request.
+ */
+ public void callSessionTransferred(@NonNull ImsCallSession session) {
+ // no-op
+ }
+
+ /**
+ * Received failure response for call transfer request.
+ */
+ public void callSessionTransferFailed(@NonNull ImsCallSession session,
+ @Nullable ImsReasonInfo reasonInfo) {
+ // no-op
+ }
+
+ /**
+ * Called when the IMS service reports a change to the call quality.
+ */
+ public void callQualityChanged(CallQuality callQuality) {
+ // no-op
+ }
+ }
+
+ private final IImsCallSession miSession;
+ private boolean mClosed = false;
+ private Listener mListener;
+
+ /** @hide */
+ public ImsCallSession(IImsCallSession iSession) {
+ miSession = iSession;
+
+ if (iSession != null) {
+ try {
+ iSession.setListener(new IImsCallSessionListenerProxy());
+ } catch (RemoteException e) {
+ }
+ } else {
+ mClosed = true;
+ }
+ }
+
+ /** @hide */
+ public ImsCallSession(IImsCallSession iSession, Listener listener) {
+ this(iSession);
+ setListener(listener);
+ }
+
+ /**
+ * Closes this object. This object is not usable after being closed.
+ */
+ public void close() {
+ synchronized (this) {
+ if (mClosed) {
+ return;
+ }
+
+ try {
+ miSession.close();
+ mClosed = true;
+ } catch (RemoteException e) {
+ }
+ }
+ }
+
+ /**
+ * Gets the call ID of the session.
+ *
+ * @return the call ID
+ */
+ public String getCallId() {
+ if (mClosed) {
+ return null;
+ }
+
+ try {
+ return miSession.getCallId();
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Gets the call profile that this session is associated with
+ *
+ * @return the call profile that this session is associated with
+ */
+ public ImsCallProfile getCallProfile() {
+ if (mClosed) {
+ return null;
+ }
+
+ try {
+ return miSession.getCallProfile();
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Gets the local call profile that this session is associated with
+ *
+ * @return the local call profile that this session is associated with
+ */
+ public ImsCallProfile getLocalCallProfile() {
+ if (mClosed) {
+ return null;
+ }
+
+ try {
+ return miSession.getLocalCallProfile();
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Gets the remote call profile that this session is associated with
+ *
+ * @return the remote call profile that this session is associated with
+ */
+ public ImsCallProfile getRemoteCallProfile() {
+ if (mClosed) {
+ return null;
+ }
+
+ try {
+ return miSession.getRemoteCallProfile();
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Gets the video call provider for the session.
+ *
+ * @return The video call provider.
+ * @hide
+ */
+ public IImsVideoCallProvider getVideoCallProvider() {
+ if (mClosed) {
+ return null;
+ }
+
+ try {
+ return miSession.getVideoCallProvider();
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Gets the value associated with the specified property of this session.
+ *
+ * @return the string value associated with the specified property
+ */
+ public String getProperty(String name) {
+ if (mClosed) {
+ return null;
+ }
+
+ try {
+ return miSession.getProperty(name);
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Gets the session state.
+ * The value returned must be one of the states in {@link State}.
+ *
+ * @return the session state
+ */
+ public int getState() {
+ if (mClosed) {
+ return State.INVALID;
+ }
+
+ try {
+ return miSession.getState();
+ } catch (RemoteException e) {
+ return State.INVALID;
+ }
+ }
+
+ /**
+ * Determines if the {@link ImsCallSession} is currently alive (e.g. not in a terminated or
+ * closed state).
+ *
+ * @return {@code True} if the session is alive.
+ */
+ public boolean isAlive() {
+ if (mClosed) {
+ return false;
+ }
+
+ int state = getState();
+ switch (state) {
+ case State.IDLE:
+ case State.INITIATED:
+ case State.NEGOTIATING:
+ case State.ESTABLISHING:
+ case State.ESTABLISHED:
+ case State.RENEGOTIATING:
+ case State.REESTABLISHING:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * Gets the native IMS call session.
+ * @hide
+ */
+ public IImsCallSession getSession() {
+ return miSession;
+ }
+
+ /**
+ * Checks if the session is in call.
+ *
+ * @return true if the session is in call
+ */
+ public boolean isInCall() {
+ if (mClosed) {
+ return false;
+ }
+
+ try {
+ return miSession.isInCall();
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Sets the listener to listen to the session events. A {@link ImsCallSession}
+ * can only hold one listener at a time. Subsequent calls to this method
+ * override the previous listener.
+ *
+ * @param listener to listen to the session events of this object
+ * @hide
+ */
+ public void setListener(Listener listener) {
+ mListener = listener;
+ }
+
+ /**
+ * Mutes or unmutes the mic for the active call.
+ *
+ * @param muted true if the call is muted, false otherwise
+ */
+ public void setMute(boolean muted) {
+ if (mClosed) {
+ return;
+ }
+
+ try {
+ miSession.setMute(muted);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Initiates an IMS call with the specified target and call profile.
+ * The session listener is called back upon defined session events.
+ * The method is only valid to call when the session state is in
+ * {@link ImsCallSession.State#IDLE}.
+ *
+ * @param callee dialed string to make the call to
+ * @param profile call profile to make the call with the specified service type,
+ * call type and media information
+ * @see Listener#callSessionStarted, Listener#callSessionStartFailed
+ */
+ public void start(String callee, ImsCallProfile profile) {
+ if (mClosed) {
+ return;
+ }
+
+ try {
+ miSession.start(callee, profile);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Initiates an IMS conference call with the specified target and call profile.
+ * The session listener is called back upon defined session events.
+ * The method is only valid to call when the session state is in
+ * {@link ImsCallSession.State#IDLE}.
+ *
+ * @param participants participant list to initiate an IMS conference call
+ * @param profile call profile to make the call with the specified service type,
+ * call type and media information
+ * @see Listener#callSessionStarted, Listener#callSessionStartFailed
+ */
+ public void start(String[] participants, ImsCallProfile profile) {
+ if (mClosed) {
+ return;
+ }
+
+ try {
+ miSession.startConference(participants, profile);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Accepts an incoming call or session update.
+ *
+ * @param callType call type specified in {@link ImsCallProfile} to be answered
+ * @param profile stream media profile {@link ImsStreamMediaProfile} to be answered
+ * @see Listener#callSessionStarted
+ */
+ public void accept(int callType, ImsStreamMediaProfile profile) {
+ if (mClosed) {
+ return;
+ }
+
+ try {
+ miSession.accept(callType, profile);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Deflects an incoming call.
+ *
+ * @param number number to be deflected to
+ */
+ public void deflect(String number) {
+ if (mClosed) {
+ return;
+ }
+
+ try {
+ miSession.deflect(number);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Rejects an incoming call or session update.
+ *
+ * @param reason reason code to reject an incoming call
+ * @see Listener#callSessionStartFailed
+ */
+ public void reject(int reason) {
+ if (mClosed) {
+ return;
+ }
+
+ try {
+ miSession.reject(reason);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Transfers an ongoing call.
+ *
+ * @param number number to be transferred to.
+ * @param isConfirmationRequired indicates blind or assured transfer.
+ */
+ public void transfer(@NonNull String number, boolean isConfirmationRequired) {
+ if (mClosed) {
+ return;
+ }
+
+ try {
+ miSession.transfer(number, isConfirmationRequired);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Transfers a call to another ongoing call.
+ *
+ * @param transferToSession the other ImsCallSession to which this session will be transferred.
+ */
+ public void transfer(@NonNull ImsCallSession transferToSession) {
+ if (mClosed) {
+ return;
+ }
+
+ try {
+ if (transferToSession != null) {
+ miSession.consultativeTransfer(transferToSession.getSession());
+ }
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Terminates a call.
+ *
+ * @see Listener#callSessionTerminated
+ */
+ public void terminate(int reason) {
+ if (mClosed) {
+ return;
+ }
+
+ try {
+ miSession.terminate(reason);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Puts a call on hold. When it succeeds, {@link Listener#callSessionHeld} is called.
+ *
+ * @param profile stream media profile {@link ImsStreamMediaProfile} to hold the call
+ * @see Listener#callSessionHeld, Listener#callSessionHoldFailed
+ */
+ public void hold(ImsStreamMediaProfile profile) {
+ if (mClosed) {
+ return;
+ }
+
+ try {
+ miSession.hold(profile);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Continues a call that's on hold. When it succeeds,
+ * {@link Listener#callSessionResumed} is called.
+ *
+ * @param profile stream media profile {@link ImsStreamMediaProfile} to resume the call
+ * @see Listener#callSessionResumed, Listener#callSessionResumeFailed
+ */
+ public void resume(ImsStreamMediaProfile profile) {
+ if (mClosed) {
+ return;
+ }
+
+ try {
+ miSession.resume(profile);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Merges the active & hold call. When it succeeds,
+ * {@link Listener#callSessionMergeStarted} is called.
+ *
+ * @see Listener#callSessionMergeStarted , Listener#callSessionMergeFailed
+ */
+ public void merge() {
+ if (mClosed) {
+ return;
+ }
+
+ try {
+ miSession.merge();
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Updates the current call's properties (ex. call mode change: video upgrade / downgrade).
+ *
+ * @param callType call type specified in {@link ImsCallProfile} to be updated
+ * @param profile stream media profile {@link ImsStreamMediaProfile} to be updated
+ * @see Listener#callSessionUpdated, Listener#callSessionUpdateFailed
+ */
+ public void update(int callType, ImsStreamMediaProfile profile) {
+ if (mClosed) {
+ return;
+ }
+
+ try {
+ miSession.update(callType, profile);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Extends this call to the conference call with the specified recipients.
+ *
+ * @param participants list to be invited to the conference call after extending the call
+ * @see Listener#callSessionConferenceExtended
+ * @see Listener#callSessionConferenceExtendFailed
+ */
+ public void extendToConference(String[] participants) {
+ if (mClosed) {
+ return;
+ }
+
+ try {
+ miSession.extendToConference(participants);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Requests the conference server to invite an additional participants to the conference.
+ *
+ * @param participants list to be invited to the conference call
+ * @see Listener#callSessionInviteParticipantsRequestDelivered
+ * @see Listener#callSessionInviteParticipantsRequestFailed
+ */
+ public void inviteParticipants(String[] participants) {
+ if (mClosed) {
+ return;
+ }
+
+ try {
+ miSession.inviteParticipants(participants);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Requests the conference server to remove the specified participants from the conference.
+ *
+ * @param participants participant list to be removed from the conference call
+ * @see Listener#callSessionRemoveParticipantsRequestDelivered
+ * @see Listener#callSessionRemoveParticipantsRequestFailed
+ */
+ public void removeParticipants(String[] participants) {
+ if (mClosed) {
+ return;
+ }
+
+ try {
+ miSession.removeParticipants(participants);
+ } catch (RemoteException e) {
+ }
+ }
+
+
+ /**
+ * Sends a DTMF code. According to <a href="http://tools.ietf.org/html/rfc2833">RFC 2833</a>,
+ * event 0 ~ 9 maps to decimal value 0 ~ 9, '*' to 10, '#' to 11, event 'A' ~ 'D' to 12 ~ 15,
+ * and event flash to 16. Currently, event flash is not supported.
+ *
+ * @param c the DTMF to send. '0' ~ '9', 'A' ~ 'D', '*', '#' are valid inputs.
+ */
+ public void sendDtmf(char c, Message result) {
+ if (mClosed) {
+ return;
+ }
+
+ try {
+ miSession.sendDtmf(c, result);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Starts a DTMF code. According to <a href="http://tools.ietf.org/html/rfc2833">RFC 2833</a>,
+ * event 0 ~ 9 maps to decimal value 0 ~ 9, '*' to 10, '#' to 11, event 'A' ~ 'D' to 12 ~ 15,
+ * and event flash to 16. Currently, event flash is not supported.
+ *
+ * @param c the DTMF to send. '0' ~ '9', 'A' ~ 'D', '*', '#' are valid inputs.
+ */
+ public void startDtmf(char c) {
+ if (mClosed) {
+ return;
+ }
+
+ try {
+ miSession.startDtmf(c);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Stops a DTMF code.
+ */
+ public void stopDtmf() {
+ if (mClosed) {
+ return;
+ }
+
+ try {
+ miSession.stopDtmf();
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Sends an USSD message.
+ *
+ * @param ussdMessage USSD message to send
+ */
+ public void sendUssd(String ussdMessage) {
+ if (mClosed) {
+ return;
+ }
+
+ try {
+ miSession.sendUssd(ussdMessage);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Determines if the session is multiparty.
+ *
+ * @return {@code True} if the session is multiparty.
+ */
+ public boolean isMultiparty() {
+ if (mClosed) {
+ return false;
+ }
+
+ try {
+ return miSession.isMultiparty();
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Sends Rtt Message
+ *
+ * @param rttMessage rtt text to be sent
+ */
+ public void sendRttMessage(String rttMessage) {
+ if (mClosed) {
+ return;
+ }
+
+ try {
+ miSession.sendRttMessage(rttMessage);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Sends RTT Upgrade or downgrade request
+ *
+ * @param to Profile with the RTT flag set to the desired value
+ */
+ public void sendRttModifyRequest(ImsCallProfile to) {
+ if (mClosed) {
+ return;
+ }
+
+ try {
+ miSession.sendRttModifyRequest(to);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Sends RTT Upgrade response
+ *
+ * @param response : response for upgrade
+ */
+ public void sendRttModifyResponse(boolean response) {
+ if (mClosed) {
+ return;
+ }
+
+ try {
+ miSession.sendRttModifyResponse(response);
+ } 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
+ * the {@link IImsCallSessionListener}.
+ */
+ private class IImsCallSessionListenerProxy extends IImsCallSessionListener.Stub {
+ /**
+ * Notifies the result of the basic session operation (setup / terminate).
+ */
+ @Override
+ public void callSessionProgressing(ImsStreamMediaProfile profile) {
+ if (mListener != null) {
+ mListener.callSessionProgressing(ImsCallSession.this, profile);
+ }
+ }
+
+ @Override
+ public void callSessionInitiated(ImsCallProfile profile) {
+ if (mListener != null) {
+ mListener.callSessionStarted(ImsCallSession.this, profile);
+ }
+ }
+
+ @Override
+ public void callSessionInitiatedFailed(ImsReasonInfo reasonInfo) {
+ if (mListener != null) {
+ mListener.callSessionStartFailed(ImsCallSession.this, reasonInfo);
+ }
+ }
+
+ @Override
+ public void callSessionTerminated(ImsReasonInfo reasonInfo) {
+ if (mListener != null) {
+ mListener.callSessionTerminated(ImsCallSession.this, reasonInfo);
+ }
+ }
+
+ /**
+ * Notifies the result of the call hold/resume operation.
+ */
+ @Override
+ public void callSessionHeld(ImsCallProfile profile) {
+ if (mListener != null) {
+ mListener.callSessionHeld(ImsCallSession.this, profile);
+ }
+ }
+
+ @Override
+ public void callSessionHoldFailed(ImsReasonInfo reasonInfo) {
+ if (mListener != null) {
+ mListener.callSessionHoldFailed(ImsCallSession.this, reasonInfo);
+ }
+ }
+
+ @Override
+ public void callSessionHoldReceived(ImsCallProfile profile) {
+ if (mListener != null) {
+ mListener.callSessionHoldReceived(ImsCallSession.this, profile);
+ }
+ }
+
+ @Override
+ public void callSessionResumed(ImsCallProfile profile) {
+ if (mListener != null) {
+ mListener.callSessionResumed(ImsCallSession.this, profile);
+ }
+ }
+
+ @Override
+ public void callSessionResumeFailed(ImsReasonInfo reasonInfo) {
+ if (mListener != null) {
+ mListener.callSessionResumeFailed(ImsCallSession.this, reasonInfo);
+ }
+ }
+
+ @Override
+ public void callSessionResumeReceived(ImsCallProfile profile) {
+ if (mListener != null) {
+ mListener.callSessionResumeReceived(ImsCallSession.this, profile);
+ }
+ }
+
+ /**
+ * Notifies the start of a call merge operation.
+ *
+ * @param newSession The merged call session.
+ * @param profile The call profile.
+ */
+ @Override
+ public void callSessionMergeStarted(IImsCallSession newSession, ImsCallProfile profile) {
+ // This callback can be used for future use to add additional
+ // functionality that may be needed between conference start and complete
+ Log.d(TAG, "callSessionMergeStarted");
+ }
+
+ /**
+ * Notifies the successful completion of a call merge operation.
+ *
+ * @param newSession The call session.
+ */
+ @Override
+ public void callSessionMergeComplete(IImsCallSession newSession) {
+ if (mListener != null) {
+ if (newSession != null) {
+ // New session created after conference
+ mListener.callSessionMergeComplete(new ImsCallSession(newSession));
+ } else {
+ // Session already exists. Hence no need to pass
+ mListener.callSessionMergeComplete(null);
+ }
+ }
+ }
+
+ /**
+ * Notifies of a failure to perform a call merge operation.
+ *
+ * @param reasonInfo The merge failure reason.
+ */
+ @Override
+ public void callSessionMergeFailed(ImsReasonInfo reasonInfo) {
+ if (mListener != null) {
+ mListener.callSessionMergeFailed(ImsCallSession.this, reasonInfo);
+ }
+ }
+
+ /**
+ * Notifies the result of call upgrade / downgrade or any other call updates.
+ */
+ @Override
+ public void callSessionUpdated(ImsCallProfile profile) {
+ if (mListener != null) {
+ mListener.callSessionUpdated(ImsCallSession.this, profile);
+ }
+ }
+
+ @Override
+ public void callSessionUpdateFailed(ImsReasonInfo reasonInfo) {
+ if (mListener != null) {
+ mListener.callSessionUpdateFailed(ImsCallSession.this, reasonInfo);
+ }
+ }
+
+ @Override
+ public void callSessionUpdateReceived(ImsCallProfile profile) {
+ if (mListener != null) {
+ mListener.callSessionUpdateReceived(ImsCallSession.this, profile);
+ }
+ }
+
+ /**
+ * Notifies the result of conference extension.
+ */
+ @Override
+ public void callSessionConferenceExtended(IImsCallSession newSession,
+ ImsCallProfile profile) {
+ if (mListener != null) {
+ mListener.callSessionConferenceExtended(ImsCallSession.this,
+ new ImsCallSession(newSession), profile);
+ }
+ }
+
+ @Override
+ public void callSessionConferenceExtendFailed(ImsReasonInfo reasonInfo) {
+ if (mListener != null) {
+ mListener.callSessionConferenceExtendFailed(ImsCallSession.this, reasonInfo);
+ }
+ }
+
+ @Override
+ public void callSessionConferenceExtendReceived(IImsCallSession newSession,
+ ImsCallProfile profile) {
+ if (mListener != null) {
+ mListener.callSessionConferenceExtendReceived(ImsCallSession.this,
+ new ImsCallSession(newSession), profile);
+ }
+ }
+
+ /**
+ * Notifies the result of the participant invitation / removal to/from
+ * the conference session.
+ */
+ @Override
+ public void callSessionInviteParticipantsRequestDelivered() {
+ if (mListener != null) {
+ mListener.callSessionInviteParticipantsRequestDelivered(ImsCallSession.this);
+ }
+ }
+
+ @Override
+ public void callSessionInviteParticipantsRequestFailed(ImsReasonInfo reasonInfo) {
+ if (mListener != null) {
+ mListener.callSessionInviteParticipantsRequestFailed(ImsCallSession.this,
+ reasonInfo);
+ }
+ }
+
+ @Override
+ public void callSessionRemoveParticipantsRequestDelivered() {
+ if (mListener != null) {
+ mListener.callSessionRemoveParticipantsRequestDelivered(ImsCallSession.this);
+ }
+ }
+
+ @Override
+ public void callSessionRemoveParticipantsRequestFailed(ImsReasonInfo reasonInfo) {
+ if (mListener != null) {
+ mListener.callSessionRemoveParticipantsRequestFailed(ImsCallSession.this,
+ reasonInfo);
+ }
+ }
+
+ /**
+ * Notifies the changes of the conference info. in the conference session.
+ */
+ @Override
+ public void callSessionConferenceStateUpdated(ImsConferenceState state) {
+ if (mListener != null) {
+ mListener.callSessionConferenceStateUpdated(ImsCallSession.this, state);
+ }
+ }
+
+ /**
+ * Notifies the incoming USSD message.
+ */
+ @Override
+ public void callSessionUssdMessageReceived(int mode, String ussdMessage) {
+ if (mListener != null) {
+ mListener.callSessionUssdMessageReceived(ImsCallSession.this, mode, ussdMessage);
+ }
+ }
+
+ /**
+ * Notifies of a case where a {@link ImsCallSession} may
+ * potentially handover from one radio technology to another.
+ * @param srcNetworkType The source network type; one of the network type constants defined
+ * in {@link android.telephony.TelephonyManager}. For example
+ * {@link android.telephony.TelephonyManager#NETWORK_TYPE_LTE}.
+ * @param targetNetworkType The target radio access technology; one of the network type
+ * constants defined in {@link android.telephony.TelephonyManager}.
+ * For example
+ * {@link android.telephony.TelephonyManager#NETWORK_TYPE_LTE}.
+ */
+ @Override
+ public void callSessionMayHandover(int srcNetworkType, int targetNetworkType) {
+ if (mListener != null) {
+ mListener.callSessionMayHandover(ImsCallSession.this, srcNetworkType,
+ targetNetworkType);
+ }
+ }
+
+ /**
+ * Notifies of handover information for this call
+ */
+ @Override
+ public void callSessionHandover(int srcNetworkType, int targetNetworkType,
+ ImsReasonInfo reasonInfo) {
+ if (mListener != null) {
+ mListener.callSessionHandover(ImsCallSession.this, srcNetworkType,
+ targetNetworkType, reasonInfo);
+ }
+ }
+
+ /**
+ * Notifies of handover failure info for this call
+ */
+ @Override
+ public void callSessionHandoverFailed(int srcNetworkType, int targetNetworkType,
+ ImsReasonInfo reasonInfo) {
+ if (mListener != null) {
+ mListener.callSessionHandoverFailed(ImsCallSession.this, srcNetworkType,
+ targetNetworkType, reasonInfo);
+ }
+ }
+
+ /**
+ * Notifies the TTY mode received from remote party.
+ */
+ @Override
+ public void callSessionTtyModeReceived(int mode) {
+ if (mListener != null) {
+ mListener.callSessionTtyModeReceived(ImsCallSession.this, mode);
+ }
+ }
+
+ /**
+ * Notifies of a change to the multiparty state for this {@code ImsCallSession}.
+ *
+ * @param isMultiParty {@code true} if the session became multiparty, {@code false}
+ * otherwise.
+ */
+ public void callSessionMultipartyStateChanged(boolean isMultiParty) {
+ if (mListener != null) {
+ mListener.callSessionMultipartyStateChanged(ImsCallSession.this, isMultiParty);
+ }
+ }
+
+ @Override
+ public void callSessionSuppServiceReceived(ImsSuppServiceNotification suppServiceInfo ) {
+ if (mListener != null) {
+ mListener.callSessionSuppServiceReceived(ImsCallSession.this, suppServiceInfo);
+ }
+ }
+
+ /**
+ * Received RTT modify request from remote party
+ */
+ @Override
+ public void callSessionRttModifyRequestReceived(ImsCallProfile callProfile) {
+ if (mListener != null) {
+ mListener.callSessionRttModifyRequestReceived(ImsCallSession.this, callProfile);
+ }
+ }
+
+ /**
+ * Received response for RTT modify request
+ */
+ @Override
+ public void callSessionRttModifyResponseReceived(int status) {
+ if (mListener != null) {
+ mListener.callSessionRttModifyResponseReceived(status);
+ }
+ }
+
+ /**
+ * RTT Message received
+ */
+ @Override
+ public void callSessionRttMessageReceived(String rttMessage) {
+ if (mListener != null) {
+ mListener.callSessionRttMessageReceived(rttMessage);
+ }
+ }
+
+ /**
+ * While in call, there has been a change in RTT audio indicator.
+ */
+ @Override
+ public void callSessionRttAudioIndicatorChanged(ImsStreamMediaProfile profile) {
+ if (mListener != null) {
+ mListener.callSessionRttAudioIndicatorChanged(profile);
+ }
+ }
+
+ @Override
+ public void callSessionTransferred() {
+ if (mListener != null) {
+ mListener.callSessionTransferred(ImsCallSession.this);
+ }
+ }
+
+ @Override
+ public void callSessionTransferFailed(@Nullable ImsReasonInfo reasonInfo) {
+ if (mListener != null) {
+ mListener.callSessionTransferFailed(ImsCallSession.this, reasonInfo);
+ }
+ }
+
+ /**
+ * Call quality updated
+ */
+ @Override
+ public void callQualityChanged(CallQuality callQuality) {
+ if (mListener != null) {
+ mListener.callQualityChanged(callQuality);
+ }
+ }
+ }
+
+ /**
+ * Provides a string representation of the {@link ImsCallSession}. Primarily intended for
+ * use in log statements.
+ *
+ * @return String representation of session.
+ */
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("[ImsCallSession objId:");
+ sb.append(System.identityHashCode(this));
+ sb.append(" state:");
+ sb.append(State.toString(getState()));
+ sb.append(" callId:");
+ sb.append(getCallId());
+ sb.append("]");
+ return sb.toString();
+ }
+}
diff --git a/android/telephony/ims/ImsCallSessionListener.java b/android/telephony/ims/ImsCallSessionListener.java
new file mode 100644
index 0000000..81af99f
--- /dev/null
+++ b/android/telephony/ims/ImsCallSessionListener.java
@@ -0,0 +1,687 @@
+/*
+ * 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.Nullable;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.os.RemoteException;
+import android.telephony.Annotation;
+import android.telephony.CallQuality;
+import android.telephony.ServiceState;
+import android.telephony.ims.aidl.IImsCallSessionListener;
+import android.telephony.ims.stub.ImsCallSessionImplBase;
+
+import com.android.ims.internal.IImsCallSession;
+
+/**
+ * Listener interface for notifying the Framework's {@link ImsCallSession} for updates to an ongoing
+ * IMS call.
+ *
+ * @hide
+ */
+// DO NOT remove or change the existing APIs, only add new ones to this implementation or you
+// will break other implementations of ImsCallSessionListener maintained by other ImsServices.
+// TODO: APIs in here do not conform to API guidelines yet. This can be changed if
+// ImsCallSessionListenerConverter is also changed.
+@SystemApi
+@TestApi
+public class ImsCallSessionListener {
+
+ private final IImsCallSessionListener mListener;
+
+ /** @hide */
+ public ImsCallSessionListener(IImsCallSessionListener l) {
+ mListener = l;
+ }
+
+ /**
+ * A request has been sent out to initiate a new IMS call session and a 1xx response has been
+ * received from the network.
+ */
+ public void callSessionProgressing(ImsStreamMediaProfile profile) {
+ try {
+ mListener.callSessionProgressing(profile);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * The IMS call session has been initiated.
+ *
+ * @param profile the associated {@link ImsCallProfile}.
+ */
+ public void callSessionInitiated(ImsCallProfile profile) {
+ try {
+ mListener.callSessionInitiated(profile);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * The IMS call session establishment has failed.
+ *
+ * @param reasonInfo {@link ImsReasonInfo} detailing the reason of the IMS call session
+ * establishment failure.
+ */
+ public void callSessionInitiatedFailed(ImsReasonInfo reasonInfo) {
+ try {
+ mListener.callSessionInitiatedFailed(reasonInfo);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * The IMS call session has been terminated.
+ *
+ * @param reasonInfo {@link ImsReasonInfo} detailing the reason of the session termination.
+ */
+ public void callSessionTerminated(ImsReasonInfo reasonInfo) {
+ try {
+ mListener.callSessionTerminated(reasonInfo);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * The IMS call session has started the process of holding the call. If it fails,
+ * {@link #callSessionHoldFailed(ImsReasonInfo)} should be called.
+ *
+ * If the IMS call session is resumed, call {@link #callSessionResumed(ImsCallProfile)}.
+ *
+ * @param profile The associated {@link ImsCallProfile} of the call session that has been put
+ * on hold.
+ */
+ public void callSessionHeld(ImsCallProfile profile) {
+ try {
+ mListener.callSessionHeld(profile);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * The IMS call session has failed to be held.
+ *
+ * @param reasonInfo {@link ImsReasonInfo} detailing the reason of the session hold failure.
+ */
+ public void callSessionHoldFailed(ImsReasonInfo reasonInfo) {
+ try {
+ mListener.callSessionHoldFailed(reasonInfo);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * This IMS Call session has been put on hold by the remote party.
+ *
+ * @param profile The {@link ImsCallProfile} associated with this IMS call session.
+ */
+ public void callSessionHoldReceived(ImsCallProfile profile) {
+ try {
+ mListener.callSessionHoldReceived(profile);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * The IMS call session has started the process of resuming the call. If the process of resuming
+ * the call fails, call {@link #callSessionResumeFailed(ImsReasonInfo)}.
+ *
+ * @param profile The {@link ImsCallProfile} associated with this IMS call session.
+ */
+ public void callSessionResumed(ImsCallProfile profile) {
+ try {
+ mListener.callSessionResumed(profile);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * The IMS call session resume has failed.
+ *
+ * @param reasonInfo {@link ImsReasonInfo} containing the detailed reason of the session resume
+ * failure.
+ */
+ public void callSessionResumeFailed(ImsReasonInfo reasonInfo) {
+ try {
+ mListener.callSessionResumeFailed(reasonInfo);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * The remote party has resumed this IMS call session.
+ *
+ * @param profile {@link ImsCallProfile} associated with the IMS call session.
+ */
+ public void callSessionResumeReceived(ImsCallProfile profile) {
+ try {
+ mListener.callSessionResumeReceived(profile);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * The IMS call session merge has been started. At this point, the {@code newSession}
+ * represents the IMS call session which represents the new merged conference and has been
+ * initiated to the IMS conference server.
+ *
+ * @param newSession the {@link ImsCallSessionImplBase} that represents the merged active & held
+ * sessions.
+ * @param profile The {@link ImsCallProfile} associated with this IMS call session.
+ */
+ public void callSessionMergeStarted(ImsCallSessionImplBase newSession, ImsCallProfile profile)
+ {
+ try {
+ mListener.callSessionMergeStarted(newSession != null ?
+ newSession.getServiceImpl() : null, profile);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Compatibility method for older implementations.
+ * See {@link #callSessionMergeStarted(ImsCallSessionImplBase, ImsCallProfile)}.
+ *
+ * @hide
+ */
+ public void callSessionMergeStarted(IImsCallSession newSession, ImsCallProfile profile)
+ {
+ try {
+ mListener.callSessionMergeStarted(newSession, profile);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * The session merge is successful and the merged {@link ImsCallSession} is active.
+ *
+ * @param newSession the new {@link ImsCallSessionImplBase}
+ * that represents the conference IMS call
+ * session.
+ */
+ public void callSessionMergeComplete(ImsCallSessionImplBase newSession) {
+ try {
+ mListener.callSessionMergeComplete(newSession != null ?
+ newSession.getServiceImpl() : null);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Compatibility method for older implementations of ImsService.
+ *
+ * See {@link #callSessionMergeComplete(ImsCallSessionImplBase)}}.
+ *
+ * @hide
+ */
+ public void callSessionMergeComplete(IImsCallSession newSession) {
+ try {
+ mListener.callSessionMergeComplete(newSession);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * The IMS call session merge has failed.
+ *
+ * @param reasonInfo {@link ImsReasonInfo} contining the reason for the call merge failure.
+ */
+ public void callSessionMergeFailed(ImsReasonInfo reasonInfo) {
+ try {
+ mListener.callSessionMergeFailed(reasonInfo);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * The IMS call session profile has been updated. Does not include holding or resuming a call.
+ *
+ * @param profile The {@link ImsCallProfile} associated with the updated IMS call session.
+ */
+ public void callSessionUpdated(ImsCallProfile profile) {
+ try {
+ mListener.callSessionUpdated(profile);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * The IMS call session profile update has failed.
+ *
+ * @param reasonInfo {@link ImsReasonInfo} containing a reason for the session update failure.
+ */
+ public void callSessionUpdateFailed(ImsReasonInfo reasonInfo) {
+ try {
+ mListener.callSessionUpdateFailed(reasonInfo);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * The IMS call session profile has received an update from the remote user.
+ *
+ * @param profile The new {@link ImsCallProfile} associated with the update.
+ */
+ public void callSessionUpdateReceived(ImsCallProfile profile) {
+ try {
+ mListener.callSessionUpdateReceived(profile);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Called when the session has been extended to a conference session.
+ *
+ * If the conference extension fails, call
+ * {@link #callSessionConferenceExtendFailed(ImsReasonInfo)}.
+ *
+ * @param newSession the session object that is extended to the conference from the active
+ * IMS Call session.
+ * @param profile The {@link ImsCallProfile} associated with the IMS call session.
+ */
+ public void callSessionConferenceExtended(ImsCallSessionImplBase newSession,
+ ImsCallProfile profile) {
+ try {
+ mListener.callSessionConferenceExtended(
+ newSession != null ? newSession.getServiceImpl() : null, profile);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Compatibility method to interface with older versions of ImsService.
+ * See {@link #callSessionConferenceExtended(ImsCallSessionImplBase, ImsCallProfile)}.
+ *
+ * @hide
+ */
+ public void callSessionConferenceExtended(IImsCallSession newSession, ImsCallProfile profile) {
+ try {
+ mListener.callSessionConferenceExtended(newSession, profile);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * The previous conference extension has failed.
+ *
+ * @param reasonInfo {@link ImsReasonInfo} containing the detailed reason of the conference
+ * extension failure.
+ */
+ public void callSessionConferenceExtendFailed(ImsReasonInfo reasonInfo) {
+ try {
+ mListener.callSessionConferenceExtendFailed(reasonInfo);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * A conference extension has been received received from the remote party.
+ *
+ * @param newSession An {@link ImsCallSessionImplBase}
+ * representing the extended IMS call session.
+ * @param profile The {@link ImsCallProfile} associated with the new IMS call session.
+ */
+ public void callSessionConferenceExtendReceived(ImsCallSessionImplBase newSession,
+ ImsCallProfile profile) {
+ try {
+ mListener.callSessionConferenceExtendReceived(newSession != null
+ ? newSession.getServiceImpl() : null, profile);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Compatibility method to interface with older versions of ImsService.
+ * See {@link #callSessionConferenceExtendReceived(ImsCallSessionImplBase, ImsCallProfile)}.
+ *
+ * @hide
+ */
+ public void callSessionConferenceExtendReceived(IImsCallSession newSession,
+ ImsCallProfile profile) {
+ try {
+ mListener.callSessionConferenceExtendReceived(newSession, profile);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * The request to invite participants to the conference has been delivered to the conference
+ * server.
+ */
+ public void callSessionInviteParticipantsRequestDelivered() {
+ try {
+ mListener.callSessionInviteParticipantsRequestDelivered();
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * The previous request to invite participants to the conference (see
+ * {@link #callSessionInviteParticipantsRequestDelivered()}) has failed.
+ *
+ * @param reasonInfo {@link ImsReasonInfo} detailing the reason forthe conference invitation
+ * failure.
+ */
+ public void callSessionInviteParticipantsRequestFailed(ImsReasonInfo reasonInfo)
+ {
+ try {
+ mListener.callSessionInviteParticipantsRequestFailed(reasonInfo);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * The request to remove participants from the conference has been delivered to the conference
+ * server.
+ */
+ public void callSessionRemoveParticipantsRequestDelivered() {
+ try {
+ mListener.callSessionRemoveParticipantsRequestDelivered();
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * The previous request to remove participants from the conference (see
+ * {@link #callSessionRemoveParticipantsRequestDelivered()}) has failed.
+ *
+ * @param reasonInfo {@link ImsReasonInfo} detailing the reason for the conference removal
+ * failure.
+ */
+ public void callSessionRemoveParticipantsRequestFailed(ImsReasonInfo reasonInfo)
+ {
+ try {
+ mListener.callSessionInviteParticipantsRequestFailed(reasonInfo);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * The IMS call session's conference state has changed.
+ *
+ * @param state The new {@link ImsConferenceState} associated with the conference.
+ */
+ public void callSessionConferenceStateUpdated(ImsConferenceState state) {
+ try {
+ mListener.callSessionConferenceStateUpdated(state);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * The IMS call session has received a Ussd message.
+ *
+ * @param mode The mode of the USSD message, either
+ * {@link ImsCallSessionImplBase#USSD_MODE_NOTIFY} or
+ * {@link ImsCallSessionImplBase#USSD_MODE_REQUEST}.
+ * @param ussdMessage The USSD message.
+ */
+ public void callSessionUssdMessageReceived(int mode, String ussdMessage)
+ {
+ try {
+ mListener.callSessionUssdMessageReceived(mode, ussdMessage);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * An {@link ImsCallSession} may potentially handover from one radio
+ * technology to another.
+ *
+ * @param srcAccessTech The source radio access technology; one of the access technology
+ * constants defined in {@link android.telephony.ServiceState}. For example
+ * {@link android.telephony.ServiceState#RIL_RADIO_TECHNOLOGY_LTE}.
+ * @param targetAccessTech The target radio access technology; one of the access technology
+ * constants defined in {@link android.telephony.ServiceState}. For example
+ * {@link android.telephony.ServiceState#RIL_RADIO_TECHNOLOGY_LTE}.
+ * @deprecated Uses hidden constants for radio access technology, use
+ * {@link #onMayHandover(int, int)} instead.
+ */
+ @Deprecated
+ public void callSessionMayHandover(int srcAccessTech, int targetAccessTech) {
+ // Use new API internally.
+ onMayHandover(ServiceState.rilRadioTechnologyToNetworkType(srcAccessTech),
+ ServiceState.rilRadioTechnologyToNetworkType(targetAccessTech));
+ }
+
+ /**
+ * Notify the framework that the associated {@link ImsCallSession} may handover from one network
+ * type to another.
+ *
+ * @param srcNetworkType The source network type.
+ * @param targetNetworkType The target network type.
+ */
+ public void onMayHandover(@Annotation.NetworkType int srcNetworkType,
+ @Annotation.NetworkType int targetNetworkType) {
+ try {
+ mListener.callSessionMayHandover(srcNetworkType, targetNetworkType);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * The IMS call session's access technology has changed.
+ *
+ * @param srcAccessTech original access technology, defined in
+ * {@link android.telephony.ServiceState}.
+ * @param targetAccessTech new access technology, defined in
+ * {@link android.telephony.ServiceState}.
+ * @param reasonInfo The {@link ImsReasonInfo} associated with this handover.
+ * @deprecated Uses hidden radio access technology constants, use
+ * {@link #onHandover(int, int, ImsReasonInfo)} instead.
+ */
+ @Deprecated
+ public void callSessionHandover(int srcAccessTech, int targetAccessTech,
+ ImsReasonInfo reasonInfo) {
+ // Use new API internally.
+ onHandover(ServiceState.rilRadioTechnologyToNetworkType(srcAccessTech),
+ ServiceState.rilRadioTechnologyToNetworkType(targetAccessTech), reasonInfo);
+ }
+
+ /**
+ * Notify the framework that the associated {@link ImsCallSession} has handed over from one
+ * network type to another.
+ *
+ * @param srcNetworkType original network type.
+ * @param targetNetworkType target network type after handover..
+ * @param reasonInfo An optional {@link ImsReasonInfo} associated with this handover.
+ */
+ public void onHandover(@Annotation.NetworkType int srcNetworkType,
+ @Annotation.NetworkType int targetNetworkType, @Nullable ImsReasonInfo reasonInfo) {
+ try {
+ mListener.callSessionHandover(srcNetworkType, targetNetworkType, reasonInfo);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * The IMS call session's access technology change has failed..
+ *
+ * @param srcAccessTech original access technology
+ * @param targetAccessTech new access technology
+ * @param reasonInfo An {@link ImsReasonInfo} detailing the reason for the failure.
+ * @deprecated Uses hidden radio access technology constants, use
+ * {@link #onHandoverFailed(int, int, ImsReasonInfo)} instead
+ */
+ @Deprecated
+ public void callSessionHandoverFailed(int srcAccessTech, int targetAccessTech,
+ ImsReasonInfo reasonInfo) {
+ // Use new API internally.
+ onHandoverFailed(ServiceState.rilRadioTechnologyToNetworkType(srcAccessTech),
+ ServiceState.rilRadioTechnologyToNetworkType(targetAccessTech), reasonInfo);
+ }
+
+ /**
+ * The IMS call session's access technology change has failed..
+ *
+ * @param srcNetworkType original network type.
+ * @param targetNetworkType target network type that the handover failed for.
+ * @param reasonInfo An {@link ImsReasonInfo} detailing the reason for the failure.
+ */
+ public void onHandoverFailed(@Annotation.NetworkType int srcNetworkType,
+ @Annotation.NetworkType int targetNetworkType, @NonNull ImsReasonInfo reasonInfo) {
+ try {
+ mListener.callSessionHandoverFailed(srcNetworkType, targetNetworkType, reasonInfo);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * The TTY mode has been changed by the remote party.
+ *
+ * @param mode one of the following: -
+ * {@link com.android.internal.telephony.Phone#TTY_MODE_OFF} -
+ * {@link com.android.internal.telephony.Phone#TTY_MODE_FULL} -
+ * {@link com.android.internal.telephony.Phone#TTY_MODE_HCO} -
+ * {@link com.android.internal.telephony.Phone#TTY_MODE_VCO}
+ */
+ public void callSessionTtyModeReceived(int mode) {
+ try {
+ mListener.callSessionTtyModeReceived(mode);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * The multiparty state has been changed for this {@code ImsCallSession}.
+ *
+ * @param isMultiParty {@code true} if the session became multiparty, {@code false} otherwise.
+ */
+ public void callSessionMultipartyStateChanged(boolean isMultiParty) {
+ try {
+ mListener.callSessionMultipartyStateChanged(isMultiParty);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Supplementary service information has been received for the current IMS call session.
+ *
+ * @param suppSrvNotification The {@link ImsSuppServiceNotification} containing the change.
+ */
+ public void callSessionSuppServiceReceived(ImsSuppServiceNotification suppSrvNotification)
+ {
+ try {
+ mListener.callSessionSuppServiceReceived(suppSrvNotification);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * An RTT modify request has been received from the remote party.
+ *
+ * @param callProfile An {@link ImsCallProfile} with the updated attributes
+ */
+ public void callSessionRttModifyRequestReceived(ImsCallProfile callProfile)
+ {
+ try {
+ mListener.callSessionRttModifyRequestReceived(callProfile);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * An RTT modify response has been received.
+ *
+ * @param status the received response for RTT modify request.
+ */
+ public void callSessionRttModifyResponseReceived(int status) {
+ try {
+ mListener.callSessionRttModifyResponseReceived(status);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * An RTT message has been received from the remote party.
+ *
+ * @param rttMessage The RTT message that has been received.
+ */
+ public void callSessionRttMessageReceived(String rttMessage) {
+ try {
+ mListener.callSessionRttMessageReceived(rttMessage);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * While in call, there has been a change in RTT audio indicator.
+ *
+ * @param profile updated ImsStreamMediaProfile
+ */
+ public void callSessionRttAudioIndicatorChanged(@NonNull ImsStreamMediaProfile profile) {
+ try {
+ mListener.callSessionRttAudioIndicatorChanged(profile);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * The call quality has changed.
+ *
+ * @param callQuality The new call quality
+ */
+ public void callQualityChanged(@NonNull CallQuality callQuality) {
+ try {
+ mListener.callQualityChanged(callQuality);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+}
+
diff --git a/android/telephony/ims/ImsConferenceState.java b/android/telephony/ims/ImsConferenceState.java
new file mode 100644
index 0000000..21bef00
--- /dev/null
+++ b/android/telephony/ims/ImsConferenceState.java
@@ -0,0 +1,220 @@
+/*
+ * 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.SystemApi;
+import android.annotation.TestApi;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telecom.Call;
+import android.telecom.Connection;
+
+import com.android.telephony.Rlog;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map.Entry;
+import java.util.Set;
+
+/**
+ * Provides the conference information (defined in RFC 4575) for IMS conference call.
+ *
+ * @hide
+ */
+@SystemApi
+@TestApi
+public final class ImsConferenceState implements Parcelable {
+ private static final String TAG = "ImsConferenceState";
+ /**
+ * conference-info : user
+ */
+ // user (String) : Tel or SIP URI
+ public static final String USER = "user";
+ // user > display text (String)
+ public static final String DISPLAY_TEXT = "display-text";
+ // user > endpoint (String) : URI or GRUU or Phone number
+ public static final String ENDPOINT = "endpoint";
+ // user > endpoint > status
+ public static final String STATUS = "status";
+
+ /**
+ * status-type (String) :
+ * "pending" : Endpoint is not yet in the session, but it is anticipated that he/she will
+ * join in the near future.
+ * "dialing-out" : Focus has dialed out to connect the endpoint to the conference,
+ * but the endpoint is not yet in the roster (probably being authenticated).
+ * "dialing-in" : Endpoint is dialing into the conference, not yet in the roster
+ * (probably being authenticated).
+ * "alerting" : PSTN alerting or SIP 180 Ringing was returned for the outbound call;
+ * endpoint is being alerted.
+ * "on-hold" : Active signaling dialog exists between an endpoint and a focus,
+ * but endpoint is "on-hold" for this conference, i.e., he/she is neither "hearing"
+ * the conference mix nor is his/her media being mixed in the conference.
+ * "connected" : Endpoint is a participant in the conference. Depending on the media policies,
+ * he/she can send and receive media to and from other participants.
+ * "disconnecting" : Focus is in the process of disconnecting the endpoint
+ * (e.g. in SIP a DISCONNECT or BYE was sent to the endpoint).
+ * "disconnected" : Endpoint is not a participant in the conference, and no active dialog
+ * exists between the endpoint and the focus.
+ * "muted-via-focus" : Active signaling dialog exists beween an endpoint and a focus and
+ * the endpoint can "listen" to the conference, but the endpoint's media is not being
+ * mixed into the conference.
+ * "connect-fail" : Endpoint fails to join the conference by rejecting the conference call.
+ */
+ public static final String STATUS_PENDING = "pending";
+ public static final String STATUS_DIALING_OUT = "dialing-out";
+ public static final String STATUS_DIALING_IN = "dialing-in";
+ public static final String STATUS_ALERTING = "alerting";
+ public static final String STATUS_ON_HOLD = "on-hold";
+ public static final String STATUS_CONNECTED = "connected";
+ public static final String STATUS_DISCONNECTING = "disconnecting";
+ public static final String STATUS_DISCONNECTED = "disconnected";
+ public static final String STATUS_MUTED_VIA_FOCUS = "muted-via-focus";
+ public static final String STATUS_CONNECT_FAIL = "connect-fail";
+ public static final String STATUS_SEND_ONLY = "sendonly";
+ public static final String STATUS_SEND_RECV = "sendrecv";
+
+ /**
+ * conference-info : SIP status code (integer)
+ */
+ public static final String SIP_STATUS_CODE = "sipstatuscode";
+
+ public final HashMap<String, Bundle> mParticipants = new HashMap<String, Bundle>();
+
+ /** @hide */
+ public ImsConferenceState() {
+ }
+
+ private ImsConferenceState(Parcel in) {
+ readFromParcel(in);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(mParticipants.size());
+
+ if (mParticipants.size() > 0) {
+ Set<Entry<String, Bundle>> entries = mParticipants.entrySet();
+
+ if (entries != null) {
+ Iterator<Entry<String, Bundle>> iterator = entries.iterator();
+
+ while (iterator.hasNext()) {
+ Entry<String, Bundle> entry = iterator.next();
+
+ out.writeString(entry.getKey());
+ out.writeParcelable(entry.getValue(), 0);
+ }
+ }
+ }
+ }
+
+ private void readFromParcel(Parcel in) {
+ int size = in.readInt();
+
+ for (int i = 0; i < size; ++i) {
+ String user = in.readString();
+ Bundle state = in.readParcelable(null);
+ mParticipants.put(user, state);
+ }
+ }
+
+ public static final @android.annotation.NonNull Creator<ImsConferenceState> CREATOR =
+ new Creator<ImsConferenceState>() {
+ @Override
+ public ImsConferenceState createFromParcel(Parcel in) {
+ return new ImsConferenceState(in);
+ }
+
+ @Override
+ public ImsConferenceState[] newArray(int size) {
+ return new ImsConferenceState[size];
+ }
+ };
+
+ /**
+ * Translates an {@code ImsConferenceState} status type to a telecom connection state.
+ *
+ * @param status The status type.
+ * @return The corresponding {@link android.telecom.Connection} state.
+ */
+ public static int getConnectionStateForStatus(String status) {
+ if (status.equals(STATUS_PENDING)) {
+ return Connection.STATE_INITIALIZING;
+ } else if (status.equals(STATUS_DIALING_IN)) {
+ return Connection.STATE_RINGING;
+ } else if (status.equals(STATUS_ALERTING) ||
+ status.equals(STATUS_DIALING_OUT)) {
+ return Connection.STATE_DIALING;
+ } else if (status.equals(STATUS_ON_HOLD) ||
+ status.equals(STATUS_SEND_ONLY)) {
+ return Connection.STATE_HOLDING;
+ } else if (status.equals(STATUS_CONNECTED) ||
+ status.equals(STATUS_MUTED_VIA_FOCUS) ||
+ status.equals(STATUS_DISCONNECTING) ||
+ status.equals(STATUS_SEND_RECV)) {
+ return Connection.STATE_ACTIVE;
+ } else if (status.equals(STATUS_DISCONNECTED)) {
+ return Connection.STATE_DISCONNECTED;
+ }
+ return Call.STATE_ACTIVE;
+ }
+
+ @NonNull
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("[");
+ sb.append(ImsConferenceState.class.getSimpleName());
+ sb.append(" ");
+ if (mParticipants.size() > 0) {
+ Set<Entry<String, Bundle>> entries = mParticipants.entrySet();
+
+ if (entries != null) {
+ Iterator<Entry<String, Bundle>> iterator = entries.iterator();
+ sb.append("<");
+ while (iterator.hasNext()) {
+ Entry<String, Bundle> entry = iterator.next();
+ sb.append(Rlog.pii(TAG, entry.getKey()));
+ sb.append(": ");
+ Bundle participantData = entry.getValue();
+
+ 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 {
+ sb.append(participantData.get(key));
+ }
+ sb.append(", ");
+ }
+ }
+ sb.append(">");
+ }
+ }
+ sb.append("]");
+ return sb.toString();
+ }
+}
diff --git a/android/telephony/ims/ImsException.java b/android/telephony/ims/ImsException.java
new file mode 100644
index 0000000..1c3d58d
--- /dev/null
+++ b/android/telephony/ims/ImsException.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims;
+
+import android.annotation.IntDef;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.content.pm.PackageManager;
+import android.telephony.SubscriptionManager;
+import android.text.TextUtils;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * This class defines an IMS-related exception that has been thrown while interacting with a
+ * device or carrier provided ImsService implementation.
+ */
+public final class ImsException extends Exception {
+
+ /**
+ * The operation has failed due to an unknown or unspecified error.
+ */
+ public static final int CODE_ERROR_UNSPECIFIED = 0;
+ /**
+ * The operation has failed because there is no remote process available to service it. This
+ * may be due to a process crash or other illegal state.
+ * <p>
+ * This is a temporary error and the operation may be retried until the connection to the
+ * remote process is restored.
+ */
+ public static final int CODE_ERROR_SERVICE_UNAVAILABLE = 1;
+
+ /**
+ * This device or carrier configuration does not support this feature for this subscription.
+ * <p>
+ * This is a permanent configuration error and there should be no retry until the subscription
+ * changes if this operation is denied due to a carrier configuration. If this is due to a
+ * device configuration, the feature {@link PackageManager#FEATURE_TELEPHONY_IMS} is not
+ * available or the device has no ImsService implementation to service this request.
+ */
+ public static final int CODE_ERROR_UNSUPPORTED_OPERATION = 2;
+
+ /**
+ * The subscription ID associated with this operation is invalid or not active.
+ * <p>
+ * This is a configuration error and there should be no retry. The subscription used for this
+ * operation is either invalid or has become inactive. The active subscriptions can be queried
+ * with {@link SubscriptionManager#getActiveSubscriptionInfoList()}.
+ */
+ public static final int CODE_ERROR_INVALID_SUBSCRIPTION = 3;
+
+ /**@hide*/
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = "CODE_ERROR_", value = {
+ CODE_ERROR_UNSPECIFIED,
+ CODE_ERROR_SERVICE_UNAVAILABLE,
+ CODE_ERROR_UNSUPPORTED_OPERATION,
+ CODE_ERROR_INVALID_SUBSCRIPTION
+ })
+ public @interface ImsErrorCode {}
+
+ private int mCode = CODE_ERROR_UNSPECIFIED;
+
+ /**
+ * A new {@link ImsException} with an unspecified {@link ImsErrorCode} code.
+ * @param message an optional message to detail the error condition more specifically.
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ public ImsException(@Nullable String message) {
+ super(getMessage(message, CODE_ERROR_UNSPECIFIED));
+ }
+
+ /**
+ * A new {@link ImsException} that includes an {@link ImsErrorCode} error code.
+ * @param message an optional message to detail the error condition more specifically.
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ public ImsException(@Nullable String message, @ImsErrorCode int code) {
+ super(getMessage(message, code));
+ mCode = code;
+ }
+
+ /**
+ * A new {@link ImsException} that includes an {@link ImsErrorCode} error code and a
+ * {@link Throwable} that contains the original error that was thrown to lead to this Exception.
+ * @param message an optional message to detail the error condition more specifically.
+ * @param cause the {@link Throwable} that caused this {@link ImsException} to be created.
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ public ImsException(@Nullable String message, @ImsErrorCode int code,
+ @Nullable Throwable cause) {
+ super(getMessage(message, code), cause);
+ mCode = code;
+ }
+
+ /**
+ * @return the IMS Error code that is associated with this {@link ImsException}.
+ */
+ public @ImsErrorCode int getCode() {
+ return mCode;
+ }
+
+ private static String getMessage(String message, int code) {
+ StringBuilder builder;
+ if (!TextUtils.isEmpty(message)) {
+ builder = new StringBuilder(message);
+ builder.append(" (code: ");
+ builder.append(code);
+ builder.append(")");
+ return builder.toString();
+ } else {
+ return "code: " + code;
+ }
+ }
+}
diff --git a/android/telephony/ims/ImsExternalCallState.java b/android/telephony/ims/ImsExternalCallState.java
new file mode 100644
index 0000000..7d73165
--- /dev/null
+++ b/android/telephony/ims/ImsExternalCallState.java
@@ -0,0 +1,240 @@
+/*
+ * 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.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.telephony.Rlog;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Parcelable object to handle MultiEndpoint Dialog Event Package Information.
+ * @hide
+ */
+@SystemApi
+@TestApi
+public final class ImsExternalCallState implements Parcelable {
+
+ private static final String TAG = "ImsExternalCallState";
+
+ // Dialog States
+ /**
+ * The external call is in the confirmed dialog state.
+ */
+ public static final int CALL_STATE_CONFIRMED = 1;
+ /**
+ * The external call is in the terminated dialog state.
+ */
+ public static final int CALL_STATE_TERMINATED = 2;
+
+ /**@hide*/
+ @IntDef(flag = true,
+ value = {
+ CALL_STATE_CONFIRMED,
+ CALL_STATE_TERMINATED
+ },
+ prefix = "CALL_STATE_")
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ExternalCallState {}
+
+ /**@hide*/
+ @IntDef(flag = true,
+ value = {
+ ImsCallProfile.CALL_TYPE_VOICE,
+ ImsCallProfile.CALL_TYPE_VT_TX,
+ ImsCallProfile.CALL_TYPE_VT_RX,
+ ImsCallProfile.CALL_TYPE_VT
+ },
+ prefix = "CALL_TYPE_")
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ExternalCallType {}
+
+
+
+ // Dialog Id
+ private int mCallId;
+ // Number
+ private Uri mAddress;
+ private Uri mLocalAddress;
+ private boolean mIsPullable;
+ // CALL_STATE_CONFIRMED / CALL_STATE_TERMINATED
+ private int mCallState;
+ // ImsCallProfile#CALL_TYPE_*
+ private int mCallType;
+ private boolean mIsHeld;
+
+ /** @hide */
+ public ImsExternalCallState() {
+ }
+
+ /**@hide*/
+ public ImsExternalCallState(int callId, Uri address, boolean isPullable,
+ @ExternalCallState int callState, int callType, boolean isCallheld) {
+ mCallId = callId;
+ mAddress = address;
+ mIsPullable = isPullable;
+ mCallState = callState;
+ mCallType = callType;
+ mIsHeld = isCallheld;
+ Rlog.d(TAG, "ImsExternalCallState = " + this);
+ }
+
+ /**@hide*/
+ public ImsExternalCallState(int callId, Uri address, Uri localAddress,
+ boolean isPullable, @ExternalCallState int callState, int callType,
+ boolean isCallheld) {
+ mCallId = callId;
+ mAddress = address;
+ mLocalAddress = localAddress;
+ mIsPullable = isPullable;
+ mCallState = callState;
+ mCallType = callType;
+ mIsHeld = isCallheld;
+ Rlog.d(TAG, "ImsExternalCallState = " + this);
+ }
+
+ /**
+ * Create a new ImsExternalCallState instance to contain Multiendpoint Dialog information.
+ * @param callId The unique ID of the call, which will be used to identify this external
+ * connection.
+ * @param address A {@link Uri} containing the remote address of this external connection.
+ * @param localAddress A {@link Uri} containing the local address information.
+ * @param isPullable A flag determining if this external connection can be pulled to the current
+ * device.
+ * @param callState The state of the external call.
+ * @param callType The type of external call.
+ * @param isCallheld A flag determining if the external connection is currently held.
+ */
+ public ImsExternalCallState(@NonNull String callId, @NonNull Uri address,
+ @Nullable Uri localAddress, boolean isPullable, @ExternalCallState int callState,
+ @ExternalCallType int callType, boolean isCallheld) {
+ mCallId = getIdForString(callId);
+ mAddress = address;
+ mLocalAddress = localAddress;
+ mIsPullable = isPullable;
+ mCallState = callState;
+ mCallType = callType;
+ mIsHeld = isCallheld;
+ Rlog.d(TAG, "ImsExternalCallState = " + this);
+ }
+
+ /** @hide */
+ public ImsExternalCallState(Parcel in) {
+ mCallId = in.readInt();
+ ClassLoader classLoader = ImsExternalCallState.class.getClassLoader();
+ mAddress = in.readParcelable(classLoader);
+ mLocalAddress = in.readParcelable(classLoader);
+ mIsPullable = (in.readInt() != 0);
+ mCallState = in.readInt();
+ mCallType = in.readInt();
+ mIsHeld = (in.readInt() != 0);
+ Rlog.d(TAG, "ImsExternalCallState const = " + this);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(mCallId);
+ out.writeParcelable(mAddress, 0);
+ out.writeParcelable(mLocalAddress, 0);
+ out.writeInt(mIsPullable ? 1 : 0);
+ out.writeInt(mCallState);
+ out.writeInt(mCallType);
+ out.writeInt(mIsHeld ? 1 : 0);
+ Rlog.d(TAG, "ImsExternalCallState writeToParcel = " + out.toString());
+ }
+
+ public static final @android.annotation.NonNull Parcelable.Creator<ImsExternalCallState> CREATOR =
+ new Parcelable.Creator<ImsExternalCallState>() {
+ @Override
+ public ImsExternalCallState createFromParcel(Parcel in) {
+ return new ImsExternalCallState(in);
+ }
+
+ @Override
+ public ImsExternalCallState[] newArray(int size) {
+ return new ImsExternalCallState[size];
+ }
+ };
+
+ public int getCallId() {
+ return mCallId;
+ }
+
+ public @NonNull Uri getAddress() {
+ return mAddress;
+ }
+
+ /**
+ * @return A {@link Uri} containing the local address from the Multiendpoint Dialog Information.
+ */
+ public @Nullable Uri getLocalAddress() {
+ return mLocalAddress;
+ }
+
+ public boolean isCallPullable() {
+ return mIsPullable;
+ }
+
+ public @ExternalCallState int getCallState() {
+ return mCallState;
+ }
+
+ public @ExternalCallType int getCallType() {
+ return mCallType;
+ }
+
+ public boolean isCallHeld() {
+ return mIsHeld;
+ }
+
+ @NonNull
+ @Override
+ public String toString() {
+ return "ImsExternalCallState { mCallId = " + mCallId +
+ ", mAddress = " + Rlog.pii(TAG, mAddress) +
+ ", mLocalAddress = " + Rlog.pii(TAG, mLocalAddress) +
+ ", mIsPullable = " + mIsPullable +
+ ", mCallState = " + mCallState +
+ ", mCallType = " + mCallType +
+ ", mIsHeld = " + mIsHeld + "}";
+ }
+
+ private int getIdForString(String idString) {
+ try {
+ return Integer.parseInt(idString);
+ } catch (NumberFormatException e) {
+ // In the case that there are alphanumeric characters, we will create a hash of the
+ // String value as a backup.
+ // TODO: Modify call IDs to use Strings as keys instead of integers in telephony/telecom
+ return idString.hashCode();
+ }
+ }
+}
diff --git a/android/telephony/ims/ImsManager.java b/android/telephony/ims/ImsManager.java
new file mode 100644
index 0000000..3984bd7
--- /dev/null
+++ b/android/telephony/ims/ImsManager.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims;
+
+import android.annotation.NonNull;
+import android.annotation.SdkConstant;
+import android.annotation.SuppressLint;
+import android.annotation.SystemService;
+import android.content.Context;
+import android.telephony.SubscriptionManager;
+
+/**
+ * Provides access to information about Telephony IMS services on the device.
+ */
+@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
+ * provisioning change from the network.
+ * May include the {@link SubscriptionManager#EXTRA_SUBSCRIPTION_INDEX} extra to also specify
+ * which subscription the operation was rejected for.
+ * <p class="note">
+ * Carrier applications may listen to this broadcast to be notified of possible IMS provisioning
+ * issues.
+ * @hide
+ */
+ // Moved from TelephonyIntents, need to keep backwards compatibility with OEM apps that have
+ // this value hard-coded in BroadcastReceiver.
+ @SuppressLint("ActionValue")
+ @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_FORBIDDEN_NO_SERVICE_AUTHORIZATION =
+ "com.android.internal.intent.action.ACTION_FORBIDDEN_NO_SERVICE_AUTHORIZATION";
+
+ /**
+ * An intent action indicating that IMS registration for WiFi calling has resulted in an error.
+ * Contains error information that should be displayed to the user.
+ * <p>
+ * This intent will contain the following extra key/value pairs:
+ * {@link #EXTRA_WFC_REGISTRATION_FAILURE_TITLE}
+ * and {@link #EXTRA_WFC_REGISTRATION_FAILURE_MESSAGE}, which contain carrier specific
+ * error information that should be displayed to the user.
+ * <p>
+ * Usage: This intent is sent as an ordered broadcast. If the settings application is going
+ * to show the error information specified to the user, it should respond to
+ * {@link android.content.BroadcastReceiver#setResultCode(int)} with
+ * {@link android.app.Activity#RESULT_CANCELED}, which will signal to the framework that the
+ * event was handled. If the framework does not receive a response to the ordered broadcast,
+ * it will then show a notification to the user indicating that there was a registration
+ * failure.
+ */
+ @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_WFC_IMS_REGISTRATION_ERROR =
+ "android.telephony.ims.action.WFC_IMS_REGISTRATION_ERROR";
+
+ /**
+ * An extra key corresponding to a {@link CharSequence} value which contains the carrier
+ * specific title to be displayed as part of the message shown to the user when there is an
+ * error registering for WiFi calling.
+ */
+ public static final String EXTRA_WFC_REGISTRATION_FAILURE_TITLE =
+ "android.telephony.ims.extra.WFC_REGISTRATION_FAILURE_TITLE";
+
+ /**
+ * An extra key corresponding to a {@link CharSequence} value which contains the carrier
+ * specific message to be displayed as part of the message shown to the user when there is an
+ * error registering for WiFi calling.
+ */
+ public static final String EXTRA_WFC_REGISTRATION_FAILURE_MESSAGE =
+ "android.telephony.ims.extra.WFC_REGISTRATION_FAILURE_MESSAGE";
+
+ /**
+ * Use {@link Context#getSystemService(String)} to get an instance of this class.
+ * @hide
+ */
+ public ImsManager(@NonNull Context context) {
+ mContext = context;
+ }
+
+ /**
+ * Create an instance of ImsRcsManager for the subscription id specified.
+ *
+ * @param subscriptionId The ID of the subscription that this ImsRcsManager will use.
+ * @throws IllegalArgumentException if the subscription is invalid.
+ * @return a ImsRcsManager instance with the specific subscription ID.
+ */
+ @NonNull
+ public ImsRcsManager getImsRcsManager(int subscriptionId) {
+ if (!SubscriptionManager.isValidSubscriptionId(subscriptionId)) {
+ throw new IllegalArgumentException("Invalid subscription ID: " + subscriptionId);
+ }
+
+ return new ImsRcsManager(mContext, subscriptionId);
+ }
+
+ /**
+ * Create an instance of ImsMmTelManager for the subscription id specified.
+ *
+ * @param subscriptionId The ID of the subscription that this ImsMmTelManager will use.
+ * @throws IllegalArgumentException if the subscription is invalid.
+ * @return a ImsMmTelManager instance with the specific subscription ID.
+ */
+ @NonNull
+ public ImsMmTelManager getImsMmTelManager(int subscriptionId) {
+ if (!SubscriptionManager.isValidSubscriptionId(subscriptionId)) {
+ throw new IllegalArgumentException("Invalid subscription ID: " + subscriptionId);
+ }
+
+ return new ImsMmTelManager(subscriptionId);
+ }
+}
diff --git a/android/telephony/ims/ImsMmTelManager.java b/android/telephony/ims/ImsMmTelManager.java
new file mode 100644
index 0000000..43db1d9
--- /dev/null
+++ b/android/telephony/ims/ImsMmTelManager.java
@@ -0,0 +1,1387 @@
+/*
+ * 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.Manifest;
+import android.annotation.CallbackExecutor;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.annotation.SuppressAutoDoc;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.os.Binder;
+import android.os.RemoteException;
+import android.os.ServiceSpecificException;
+import android.telephony.AccessNetworkConstants;
+import android.telephony.CarrierConfigManager;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyFrameworkInitializer;
+import android.telephony.ims.aidl.IImsCapabilityCallback;
+import android.telephony.ims.feature.ImsFeature;
+import android.telephony.ims.feature.MmTelFeature;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.IIntegerConsumer;
+import com.android.internal.telephony.ITelephony;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
+
+/**
+ * A manager for the MmTel (Multimedia Telephony) feature of an IMS network, given an associated
+ * subscription.
+ *
+ * Allows a user to query the IMS MmTel feature information for a subscription, register for
+ * registration and MmTel capability status callbacks, as well as query/modify user settings for the
+ * associated subscription.
+ *
+ * Use {@link android.telephony.ims.ImsManager#getImsMmTelManager(int)} to get an instance of this
+ * manager.
+ */
+public class ImsMmTelManager implements RegistrationManager {
+
+ /**
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = "WIFI_MODE_", value = {
+ WIFI_MODE_WIFI_ONLY,
+ WIFI_MODE_CELLULAR_PREFERRED,
+ WIFI_MODE_WIFI_PREFERRED
+ })
+ public @interface WiFiCallingMode {}
+
+ /**
+ * Register for IMS over IWLAN if WiFi signal quality is high enough. Do not hand over to LTE
+ * registration if signal quality degrades.
+ */
+ public static final int WIFI_MODE_WIFI_ONLY = 0;
+
+ /**
+ * Prefer registering for IMS over LTE if LTE signal quality is high enough.
+ */
+ public static final int WIFI_MODE_CELLULAR_PREFERRED = 1;
+
+ /**
+ * Prefer registering for IMS over IWLAN if possible if WiFi signal quality is high enough.
+ */
+ public static final int WIFI_MODE_WIFI_PREFERRED = 2;
+
+ /**
+ * Callback class for receiving IMS network Registration callback events.
+ * @see #registerImsRegistrationCallback(Executor, RegistrationCallback) (RegistrationCallback)
+ * @see #unregisterImsRegistrationCallback(RegistrationCallback)
+ * @deprecated Use {@link RegistrationManager.RegistrationCallback} instead.
+ * @hide
+ */
+ // Do not add to this class, add to RegistrationManager.RegistrationCallback instead.
+ @Deprecated
+ @SystemApi @TestApi
+ public static class RegistrationCallback extends RegistrationManager.RegistrationCallback {
+
+ /**
+ * Notifies the framework when the IMS Provider is registered to the IMS network.
+ *
+ * @param imsTransportType the radio access technology.
+ */
+ @Override
+ public void onRegistered(@AccessNetworkConstants.TransportType int imsTransportType) {
+ }
+
+ /**
+ * Notifies the framework when the IMS Provider is trying to register the IMS network.
+ *
+ * @param imsTransportType the radio access technology.
+ */
+ @Override
+ public void onRegistering(@AccessNetworkConstants.TransportType int imsTransportType) {
+ }
+
+ /**
+ * Notifies the framework when the IMS Provider is deregistered from the IMS network.
+ *
+ * @param info the {@link ImsReasonInfo} associated with why registration was disconnected.
+ */
+ @Override
+ public void onUnregistered(@NonNull ImsReasonInfo info) {
+ }
+
+ /**
+ * A failure has occurred when trying to handover registration to another technology type.
+ *
+ * @param imsTransportType The transport type that has failed to handover registration to.
+ * @param info A {@link ImsReasonInfo} that identifies the reason for failure.
+ */
+ @Override
+ public void onTechnologyChangeFailed(
+ @AccessNetworkConstants.TransportType int imsTransportType,
+ @NonNull ImsReasonInfo info) {
+ }
+ }
+
+ /**
+ * Receives IMS capability status updates from the ImsService.
+ *
+ * @see #registerMmTelCapabilityCallback(Executor, CapabilityCallback) (CapabilityCallback)
+ * @see #unregisterMmTelCapabilityCallback(CapabilityCallback)
+ */
+ public static class CapabilityCallback {
+
+ private static class CapabilityBinder extends IImsCapabilityCallback.Stub {
+
+ private final CapabilityCallback mLocalCallback;
+ private Executor mExecutor;
+
+ CapabilityBinder(CapabilityCallback c) {
+ mLocalCallback = c;
+ }
+
+ @Override
+ public void onCapabilitiesStatusChanged(int config) {
+ if (mLocalCallback == null) return;
+
+ long callingIdentity = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> mLocalCallback.onCapabilitiesStatusChanged(
+ new MmTelFeature.MmTelCapabilities(config)));
+ } finally {
+ restoreCallingIdentity(callingIdentity);
+ }
+ }
+
+ @Override
+ public void onQueryCapabilityConfiguration(int capability, int radioTech,
+ boolean isEnabled) {
+ // This is not used for public interfaces.
+ }
+
+ @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;
+ }
+ }
+
+ private final CapabilityBinder mBinder = new CapabilityBinder(this);
+
+ /**
+ * The status of the feature's capabilities has changed to either available or unavailable.
+ * If unavailable, the feature is not able to support the unavailable capability at this
+ * time.
+ *
+ * @param capabilities The new availability of the capabilities.
+ */
+ public void onCapabilitiesStatusChanged(
+ @NonNull MmTelFeature.MmTelCapabilities capabilities) {
+ }
+
+ /**@hide*/
+ public final IImsCapabilityCallback getBinder() {
+ return mBinder;
+ }
+
+ /**@hide*/
+ // Only exposed as public method for compatibility with deprecated ImsManager APIs.
+ // TODO: clean up dependencies and change back to private visibility.
+ public final void setExecutor(Executor executor) {
+ mBinder.setExecutor(executor);
+ }
+ }
+
+ private final int mSubId;
+
+ /**
+ * Create an instance of {@link ImsMmTelManager} for the subscription id specified.
+ *
+ * @param subId The ID of the subscription that this ImsMmTelManager will use.
+ * @see android.telephony.SubscriptionManager#getActiveSubscriptionInfoList()
+ *
+ * <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 android.telephony.TelephonyManager#hasCarrierPrivileges}).
+ *
+ * @throws IllegalArgumentException if the subscription is invalid.
+ * @deprecated Use {@link android.telephony.ims.ImsManager#getImsMmTelManager(int)} to get an
+ * instance of this class.
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ @Deprecated
+ @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
+ })
+ @SuppressLint("ManagerLookup")
+ public static @NonNull ImsMmTelManager createForSubscriptionId(int subId) {
+ if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+ throw new IllegalArgumentException("Invalid subscription ID");
+ }
+
+ return new ImsMmTelManager(subId);
+ }
+
+ /**
+ * Only visible for testing, use {@link ImsManager#getImsMmTelManager(int)} instead.
+ * @hide
+ */
+ @VisibleForTesting
+ public ImsMmTelManager(int subId) {
+ mSubId = subId;
+ }
+
+ /**
+ * Registers a {@link RegistrationCallback} with the system, which will provide registration
+ * updates for the subscription specified in {@link ImsManager#getImsMmTelManager(int)}. Use
+ * {@link SubscriptionManager.OnSubscriptionsChangedListener} to listen to Subscription changed
+ * events and call {@link #unregisterImsRegistrationCallback(RegistrationCallback)} to clean up.
+ *
+ * When the callback is registered, it will initiate the callback c to be called with the
+ * current registration state.
+ *
+ * @param executor The executor the callback events should be run on.
+ * @param c The {@link RegistrationCallback} to be added.
+ * @see #unregisterImsRegistrationCallback(RegistrationCallback)
+ * @throws IllegalArgumentException if the subscription associated with this callback is not
+ * active (SIM is not inserted, ESIM inactive) or invalid, or a null {@link Executor} or
+ * {@link CapabilityCallback} callback.
+ * @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.
+ * @deprecated Use {@link RegistrationManager#registerImsRegistrationCallback(Executor,
+ * RegistrationManager.RegistrationCallback)} instead.
+ * @hide
+ */
+ @Deprecated
+ @SystemApi @TestApi
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public void registerImsRegistrationCallback(@NonNull @CallbackExecutor Executor executor,
+ @NonNull RegistrationCallback c) throws ImsException {
+ if (c == null) {
+ throw new IllegalArgumentException("Must include a non-null RegistrationCallback.");
+ }
+ if (executor == null) {
+ throw new IllegalArgumentException("Must include a non-null Executor.");
+ }
+ c.setExecutor(executor);
+
+ ITelephony iTelephony = getITelephony();
+ if (iTelephony == null) {
+ throw new ImsException("Could not find Telephony Service.",
+ ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ }
+
+ try {
+ iTelephony.registerImsRegistrationCallback(mSubId, c.getBinder());
+ } catch (ServiceSpecificException e) {
+ if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
+ // Rethrow as runtime error to keep API compatible.
+ throw new IllegalArgumentException(e.getMessage());
+ } else {
+ throw new ImsException(e.getMessage(), e.errorCode);
+ }
+ } catch (RemoteException | IllegalStateException e) {
+ throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ }
+ }
+
+ /**
+ *
+ * <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 android.telephony.TelephonyManager#hasCarrierPrivileges}).
+ *
+ * {@inheritDoc}
+ *
+ */
+ @Override
+ @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 void registerImsRegistrationCallback(@NonNull @CallbackExecutor Executor executor,
+ @NonNull RegistrationManager.RegistrationCallback c) throws ImsException {
+ if (c == null) {
+ throw new IllegalArgumentException("Must include a non-null RegistrationCallback.");
+ }
+ if (executor == null) {
+ throw new IllegalArgumentException("Must include a non-null Executor.");
+ }
+ c.setExecutor(executor);
+
+ ITelephony iTelephony = getITelephony();
+ if (iTelephony == null) {
+ throw new ImsException("Could not find Telephony Service.",
+ ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ }
+
+ try {
+ iTelephony.registerImsRegistrationCallback(mSubId, c.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);
+ }
+ }
+
+ /**
+ * Removes an existing {@link 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.
+ *
+ * @param c The {@link RegistrationCallback} to be removed.
+ * @see SubscriptionManager.OnSubscriptionsChangedListener
+ * @see #registerImsRegistrationCallback(Executor, RegistrationCallback)
+ * @deprecated Use {@link #unregisterImsRegistrationCallback(
+ * RegistrationManager.RegistrationCallback)}.
+ * @hide
+ */
+ @Deprecated
+ @SystemApi @TestApi
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public void unregisterImsRegistrationCallback(@NonNull RegistrationCallback c) {
+ if (c == null) {
+ throw new IllegalArgumentException("Must include a non-null RegistrationCallback.");
+ }
+
+ ITelephony iTelephony = getITelephony();
+ if (iTelephony == null) {
+ throw new RuntimeException("Could not find Telephony Service.");
+ }
+
+ try {
+ iTelephony.unregisterImsRegistrationCallback(mSubId, c.getBinder());
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ *
+ * <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 android.telephony.TelephonyManager#hasCarrierPrivileges}).
+ * Access by profile owners is deprecated and will be removed in a future release.
+ *
+ *{@inheritDoc}
+ */
+ @Override
+ @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 void unregisterImsRegistrationCallback(
+ @NonNull RegistrationManager.RegistrationCallback c) {
+ if (c == null) {
+ throw new IllegalArgumentException("Must include a non-null RegistrationCallback.");
+ }
+
+ ITelephony iTelephony = getITelephony();
+ if (iTelephony == null) {
+ throw new RuntimeException("Could not find Telephony Service.");
+ }
+
+ try {
+ iTelephony.unregisterImsRegistrationCallback(mSubId, c.getBinder());
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ * @hide
+ */
+ @Override
+ @SystemApi @TestApi
+ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public void getRegistrationState(@NonNull @CallbackExecutor Executor executor,
+ @NonNull @ImsRegistrationState Consumer<Integer> stateCallback) {
+ if (stateCallback == null) {
+ throw new IllegalArgumentException("Must include a non-null callback.");
+ }
+ if (executor == null) {
+ throw new IllegalArgumentException("Must include a non-null Executor.");
+ }
+
+ ITelephony iTelephony = getITelephony();
+ if (iTelephony == null) {
+ throw new RuntimeException("Could not find Telephony Service.");
+ }
+
+ try {
+ iTelephony.getImsMmTelRegistrationState(mSubId, new IIntegerConsumer.Stub() {
+ @Override
+ public void accept(int result) {
+ executor.execute(() -> stateCallback.accept(result));
+ }
+ });
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * <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 android.telephony.TelephonyManager#hasCarrierPrivileges}).
+ * Access by profile owners is deprecated and will be removed in a future release.
+ *
+ *{@inheritDoc}
+ */
+ @Override
+ @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 void getRegistrationTransportType(@NonNull @CallbackExecutor Executor executor,
+ @NonNull @AccessNetworkConstants.TransportType
+ Consumer<Integer> transportTypeCallback) {
+ if (transportTypeCallback == null) {
+ throw new IllegalArgumentException("Must include a non-null callback.");
+ }
+ if (executor == null) {
+ throw new IllegalArgumentException("Must include a non-null Executor.");
+ }
+
+ ITelephony iTelephony = getITelephony();
+ if (iTelephony == null) {
+ throw new RuntimeException("Could not find Telephony Service.");
+ }
+
+ try {
+ iTelephony.getImsMmTelRegistrationTransportType(mSubId,
+ new IIntegerConsumer.Stub() {
+ @Override
+ public void accept(int result) {
+ executor.execute(() -> transportTypeCallback.accept(result));
+ }
+ });
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Registers a {@link CapabilityCallback} with the system, which will provide MmTel service
+ * availability updates for the subscription specified in
+ * {@link ImsManager#getImsMmTelManager(int)}.
+ *
+ * Use {@link SubscriptionManager.OnSubscriptionsChangedListener} to listen to
+ * subscription changed events and call
+ * {@link #unregisterMmTelCapabilityCallback(CapabilityCallback)} to clean up.
+ * <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>
+ * <li>The caller is the default SMS app for the device.</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.
+ *
+ * When the callback is registered, it will initiate the callback c to be called with the
+ * current capabilities.
+ *
+ * @param executor The executor the callback events should be run on.
+ * @param c The MmTel {@link CapabilityCallback} to be registered.
+ * @see #unregisterMmTelCapabilityCallback(CapabilityCallback)
+ * @throws IllegalArgumentException if the subscription associated with this callback is not
+ * active (SIM is not inserted, ESIM inactive) or invalid, or a null {@link Executor} or
+ * {@link CapabilityCallback} callback.
+ * @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.
+ */
+ @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 void registerMmTelCapabilityCallback(@NonNull @CallbackExecutor Executor executor,
+ @NonNull CapabilityCallback c) throws ImsException {
+ if (c == null) {
+ throw new IllegalArgumentException("Must include a non-null RegistrationCallback.");
+ }
+ if (executor == null) {
+ throw new IllegalArgumentException("Must include a non-null Executor.");
+ }
+ c.setExecutor(executor);
+
+ ITelephony iTelephony = getITelephony();
+ if (iTelephony == null) {
+ throw new ImsException("Could not find Telephony Service.",
+ ImsException.CODE_ERROR_INVALID_SUBSCRIPTION);
+ }
+
+ try {
+ iTelephony.registerMmTelCapabilityCallback(mSubId, c.getBinder());
+ } catch (ServiceSpecificException e) {
+ if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
+ // Rethrow as runtime error to keep API compatible.
+ throw new IllegalArgumentException(e.getMessage());
+ } else {
+ throw new ImsException(e.getMessage(), e.errorCode);
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ } catch (IllegalStateException e) {
+ throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ }
+ }
+
+ /**
+ * Removes an existing MmTel {@link CapabilityCallback}.
+ *
+ * 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>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>
+ * <li>The caller is the default SMS app for the device.</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.
+ *
+ * @param c The MmTel {@link CapabilityCallback} to be removed.
+ * @see #registerMmTelCapabilityCallback(Executor, CapabilityCallback)
+ */
+ @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 void unregisterMmTelCapabilityCallback(@NonNull CapabilityCallback c) {
+ if (c == null) {
+ throw new IllegalArgumentException("Must include a non-null RegistrationCallback.");
+ }
+
+ ITelephony iTelephony = getITelephony();
+ if (iTelephony == null) {
+ throw new RuntimeException("Could not find Telephony Service.");
+ }
+
+ try {
+ iTelephony.unregisterMmTelCapabilityCallback(mSubId, c.getBinder());
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Query the user’s setting for “Advanced Calling” or "Enhanced 4G LTE", which is used to
+ * enable MmTel IMS features, depending on the carrier configuration for the current
+ * subscription. If this setting is enabled, IMS voice and video telephony over IWLAN/LTE will
+ * be enabled as long as the carrier has provisioned these services for the specified
+ * subscription. Other IMS services (SMS/UT) are not affected by this user setting and depend on
+ * carrier requirements.
+ * <p>
+ * Note: If the carrier configuration for advanced calling is not editable or hidden, this
+ * method will always return the default value.
+ * <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>
+ * <li>The caller is the default SMS app for the device.</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.
+ *
+ * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL
+ * @see android.telephony.CarrierConfigManager#KEY_EDITABLE_ENHANCED_4G_LTE_BOOL
+ * @see android.telephony.CarrierConfigManager#KEY_HIDE_ENHANCED_4G_LTE_BOOL
+ * @see android.telephony.CarrierConfigManager#KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL
+ * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_AVAILABLE_BOOL
+ * @throws IllegalArgumentException if the subscription associated with this operation is not
+ * active (SIM is not inserted, ESIM inactive) or invalid.
+ * @return true if the user's setting for advanced calling is enabled, false otherwise.
+ */
+ @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 isAdvancedCallingSettingEnabled() {
+ ITelephony iTelephony = getITelephony();
+ if (iTelephony == null) {
+ throw new RuntimeException("Could not find Telephony Service.");
+ }
+
+ try {
+ return iTelephony.isAdvancedCallingSettingEnabled(mSubId);
+ } catch (ServiceSpecificException e) {
+ if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
+ // Rethrow as runtime error to keep API compatible.
+ throw new IllegalArgumentException(e.getMessage());
+ } else {
+ throw new RuntimeException(e.getMessage());
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Modify the user’s setting for “Advanced Calling” or "Enhanced 4G LTE", which is used to
+ * enable MmTel IMS features, depending on the carrier configuration for the current
+ * subscription. If this setting is enabled, IMS voice and video telephony over IWLAN/LTE will
+ * be enabled as long as the carrier has provisioned these services for the specified
+ * subscription. Other IMS services (SMS/UT) are not affected by this user setting and depend on
+ * carrier requirements.
+ *
+ * Modifying this value may also trigger an IMS registration or deregistration, depending on
+ * whether or not the new value is enabled or disabled.
+ *
+ * Note: If the carrier configuration for advanced calling is not editable or hidden, this
+ * method will do nothing and will instead always use the default value.
+ *
+ * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL
+ * @see android.telephony.CarrierConfigManager#KEY_EDITABLE_ENHANCED_4G_LTE_BOOL
+ * @see android.telephony.CarrierConfigManager#KEY_HIDE_ENHANCED_4G_LTE_BOOL
+ * @see android.telephony.CarrierConfigManager#KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL
+ * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_AVAILABLE_BOOL
+ * @see #isAdvancedCallingSettingEnabled()
+ * @throws IllegalArgumentException if the subscription associated with this operation is not
+ * active (SIM is not inserted, ESIM inactive) or invalid.
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ @SystemApi @TestApi
+ public void setAdvancedCallingSettingEnabled(boolean isEnabled) {
+ ITelephony iTelephony = getITelephony();
+ if (iTelephony == null) {
+ throw new RuntimeException("Could not find Telephony Service.");
+ }
+
+ try {
+ iTelephony.setAdvancedCallingSettingEnabled(mSubId, isEnabled);
+ } catch (ServiceSpecificException e) {
+ if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
+ // Rethrow as runtime error to keep API compatible.
+ throw new IllegalArgumentException(e.getMessage());
+ } else {
+ throw new RuntimeException(e.getMessage());
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Query the IMS MmTel capability for a given registration technology. This does not
+ * necessarily mean that we are registered and the capability is available, but rather the
+ * subscription is capable of this service over IMS.
+ *
+ * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_AVAILABLE_BOOL
+ * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VT_AVAILABLE_BOOL
+ * @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}
+ * @return {@code true} if the MmTel IMS capability is capable for this subscription, false
+ * otherwise.
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @SystemApi @TestApi
+ public boolean isCapable(@MmTelFeature.MmTelCapabilities.MmTelCapability int capability,
+ @ImsRegistrationImplBase.ImsRegistrationTech int imsRegTech) {
+ ITelephony iTelephony = getITelephony();
+ if (iTelephony == null) {
+ throw new RuntimeException("Could not find Telephony Service.");
+ }
+
+ try {
+ return iTelephony.isCapable(mSubId, capability, imsRegTech);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Query the availability of an IMS MmTel capability for a given registration technology. If
+ * a capability is available, IMS is registered and the service is currently available over IMS.
+ *
+ * @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}
+ * @return {@code true} if the MmTel IMS capability is available for this subscription, false
+ * otherwise.
+ * @hide
+ */
+ @SystemApi @TestApi
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public boolean isAvailable(@MmTelFeature.MmTelCapabilities.MmTelCapability int capability,
+ @ImsRegistrationImplBase.ImsRegistrationTech int imsRegTech) {
+ ITelephony iTelephony = getITelephony();
+ if (iTelephony == null) {
+ throw new RuntimeException("Could not find Telephony Service.");
+ }
+
+ try {
+ return iTelephony.isAvailable(mSubId, capability, imsRegTech);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Query whether or not the requested MmTel capability is supported by the carrier on the
+ * specified network transport.
+ * <p>
+ * This is a configuration option and does not change. The only time this may change is if a
+ * new IMS configuration is loaded when there is a
+ * {@link CarrierConfigManager#ACTION_CARRIER_CONFIG_CHANGED} broadcast for this subscription.
+ * @param capability The capability that is being queried for support on the carrier network.
+ * @param transportType The transport type of the capability to check support for.
+ * @param executor The executor that the callback will be called with.
+ * @param callback A consumer containing a Boolean result specifying whether or not the
+ * capability is supported on this carrier network for the transport specified.
+ * @throws ImsException if the subscription is no longer valid or the IMS service is not
+ * available.
+ * @hide
+ */
+ @SystemApi @TestApi
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public void isSupported(@MmTelFeature.MmTelCapabilities.MmTelCapability int capability,
+ @AccessNetworkConstants.TransportType int transportType,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull Consumer<Boolean> callback) throws ImsException {
+ if (callback == null) {
+ throw new IllegalArgumentException("Must include a non-null Consumer.");
+ }
+ if (executor == null) {
+ throw new IllegalArgumentException("Must include a non-null Executor.");
+ }
+
+ ITelephony iTelephony = getITelephony();
+ if (iTelephony == null) {
+ throw new ImsException("Could not find Telephony Service.",
+ ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ }
+
+ try {
+ getITelephony().isMmTelCapabilitySupported(mSubId, new IIntegerConsumer.Stub() {
+ @Override
+ public void accept(int result) {
+ executor.execute(() -> callback.accept(result == 1));
+ }
+ }, capability, transportType);
+ } catch (ServiceSpecificException sse) {
+ throw new ImsException(sse.getMessage(), sse.errorCode);
+ } catch (RemoteException e) {
+ e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * The user's setting for whether or not they have enabled the "Video Calling" setting.
+ *
+ * <p>
+ * Note: If the carrier configuration for advanced calling is not editable or hidden, this
+ * method will always return the default value.
+ * <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>
+ * <li>The caller is the default SMS app for the device.</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 IllegalArgumentException if the subscription associated with this operation is not
+ * active (SIM is not inserted, ESIM inactive) or invalid.
+ * @return true if the user’s “Video Calling” setting is currently enabled.
+ */
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+ android.Manifest.permission.READ_PRECISE_PHONE_STATE})
+ @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
+ public boolean isVtSettingEnabled() {
+ ITelephony iTelephony = getITelephony();
+ if (iTelephony == null) {
+ throw new RuntimeException("Could not find Telephony Service.");
+ }
+
+ try {
+ return iTelephony.isVtSettingEnabled(mSubId);
+ } catch (ServiceSpecificException e) {
+ if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
+ // Rethrow as runtime error to keep API compatible.
+ throw new IllegalArgumentException(e.getMessage());
+ } else {
+ throw new RuntimeException(e.getMessage());
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Change the user's setting for Video Telephony and enable the Video Telephony capability.
+ *
+ * @throws IllegalArgumentException if the subscription associated with this operation is not
+ * active (SIM is not inserted, ESIM inactive) or invalid.
+ * @see #isVtSettingEnabled()
+ * @hide
+ */
+ @SystemApi @TestApi
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ public void setVtSettingEnabled(boolean isEnabled) {
+ ITelephony iTelephony = getITelephony();
+ if (iTelephony == null) {
+ throw new RuntimeException("Could not find Telephony Service.");
+ }
+
+ try {
+ iTelephony.setVtSettingEnabled(mSubId, isEnabled);
+ } catch (ServiceSpecificException e) {
+ if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
+ // Rethrow as runtime error to keep API compatible.
+ throw new IllegalArgumentException(e.getMessage());
+ } else {
+ throw new RuntimeException(e.getMessage());
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * @return true if the user's setting for Voice over WiFi is enabled and false if it is not.
+ *
+ * <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>
+ * <li>The caller is the default SMS app for the device.</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 IllegalArgumentException if the subscription associated with this operation is not
+ * active (SIM is not inserted, ESIM inactive) or invalid.
+ */
+ @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 isVoWiFiSettingEnabled() {
+ ITelephony iTelephony = getITelephony();
+ if (iTelephony == null) {
+ throw new RuntimeException("Could not find Telephony Service.");
+ }
+
+ try {
+ return iTelephony.isVoWiFiSettingEnabled(mSubId);
+ } catch (ServiceSpecificException e) {
+ if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
+ // Rethrow as runtime error to keep API compatible.
+ throw new IllegalArgumentException(e.getMessage());
+ } else {
+ throw new RuntimeException(e.getMessage());
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Sets the user's setting for whether or not Voice over WiFi is enabled.
+ *
+ * @throws IllegalArgumentException if the subscription associated with this operation is not
+ * active (SIM is not inserted, ESIM inactive) or invalid.
+ * @param isEnabled true if the user's setting for Voice over WiFi is enabled, false otherwise=
+ * @see #isVoWiFiSettingEnabled()
+ * @hide
+ */
+ @SystemApi @TestApi
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ public void setVoWiFiSettingEnabled(boolean isEnabled) {
+ ITelephony iTelephony = getITelephony();
+ if (iTelephony == null) {
+ throw new RuntimeException("Could not find Telephony Service.");
+ }
+
+ try {
+ iTelephony.setVoWiFiSettingEnabled(mSubId, isEnabled);
+ } catch (ServiceSpecificException e) {
+ if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
+ // Rethrow as runtime error to keep API compatible.
+ throw new IllegalArgumentException(e.getMessage());
+ } else {
+ throw new RuntimeException(e.getMessage());
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Returns the user's voice over WiFi roaming setting associated with the current subscription.
+ *
+ * <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>
+ * <li>The caller is the default SMS app for the device.</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 IllegalArgumentException if the subscription associated with this operation is not
+ * active (SIM is not inserted, ESIM inactive) or invalid.
+ * @return true if the user's setting for Voice over WiFi while roaming is enabled, false
+ * if disabled.
+ */
+ @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 isVoWiFiRoamingSettingEnabled() {
+ ITelephony iTelephony = getITelephony();
+ if (iTelephony == null) {
+ throw new RuntimeException("Could not find Telephony Service.");
+ }
+
+ try {
+ return iTelephony.isVoWiFiRoamingSettingEnabled(mSubId);
+ } catch (ServiceSpecificException e) {
+ if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
+ // Rethrow as runtime error to keep API compatible.
+ throw new IllegalArgumentException(e.getMessage());
+ } else {
+ throw new RuntimeException(e.getMessage());
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Change the user's setting for Voice over WiFi while roaming.
+ *
+ * @param isEnabled true if the user's setting for Voice over WiFi while roaming is enabled,
+ * false otherwise.
+ * @throws IllegalArgumentException if the subscription associated with this operation is not
+ * active (SIM is not inserted, ESIM inactive) or invalid.
+ * @see #isVoWiFiRoamingSettingEnabled()
+ * @hide
+ */
+ @SystemApi @TestApi
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ public void setVoWiFiRoamingSettingEnabled(boolean isEnabled) {
+ ITelephony iTelephony = getITelephony();
+ if (iTelephony == null) {
+ throw new RuntimeException("Could not find Telephony Service.");
+ }
+
+ try {
+ iTelephony.setVoWiFiRoamingSettingEnabled(mSubId, isEnabled);
+ } catch (ServiceSpecificException e) {
+ if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
+ // Rethrow as runtime error to keep API compatible.
+ throw new IllegalArgumentException(e.getMessage());
+ } else {
+ throw new RuntimeException(e.getMessage());
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Overrides the Voice over WiFi capability to true for IMS, but do not persist the setting.
+ * Typically used during the Voice over WiFi registration process for some carriers.
+ *
+ * @param isCapable true if the IMS stack should try to register for IMS over IWLAN, false
+ * otherwise.
+ * @param mode the Voice over WiFi mode preference to set, which can be one of the following:
+ * - {@link #WIFI_MODE_WIFI_ONLY}
+ * - {@link #WIFI_MODE_CELLULAR_PREFERRED}
+ * - {@link #WIFI_MODE_WIFI_PREFERRED}
+ * @throws IllegalArgumentException if the subscription associated with this operation is not
+ * active (SIM is not inserted, ESIM inactive) or invalid.
+ * @see #setVoWiFiSettingEnabled(boolean)
+ * @hide
+ */
+ @SystemApi @TestApi
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ public void setVoWiFiNonPersistent(boolean isCapable, int mode) {
+ ITelephony iTelephony = getITelephony();
+ if (iTelephony == null) {
+ throw new RuntimeException("Could not find Telephony Service.");
+ }
+
+ try {
+ iTelephony.setVoWiFiNonPersistent(mSubId, isCapable, mode);
+ } catch (ServiceSpecificException e) {
+ if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
+ // Rethrow as runtime error to keep API compatible.
+ throw new IllegalArgumentException(e.getMessage());
+ } else {
+ throw new RuntimeException(e.getMessage());
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Returns the user's voice over WiFi Roaming mode setting associated with the device.
+ *
+ * <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>
+ * <li>The caller is the default SMS app for the device.</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 IllegalArgumentException if the subscription associated with this operation is not
+ * active (SIM is not inserted, ESIM inactive) or invalid.
+ * @return The Voice over WiFi Mode preference set by the user, which can be one of the
+ * following:
+ * - {@link #WIFI_MODE_WIFI_ONLY}
+ * - {@link #WIFI_MODE_CELLULAR_PREFERRED}
+ * - {@link #WIFI_MODE_WIFI_PREFERRED}
+ */
+ @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 @WiFiCallingMode int getVoWiFiModeSetting() {
+ ITelephony iTelephony = getITelephony();
+ if (iTelephony == null) {
+ throw new RuntimeException("Could not find Telephony Service.");
+ }
+
+ try {
+ return iTelephony.getVoWiFiModeSetting(mSubId);
+ } catch (ServiceSpecificException e) {
+ if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
+ // Rethrow as runtime error to keep API compatible.
+ throw new IllegalArgumentException(e.getMessage());
+ } else {
+ throw new RuntimeException(e.getMessage());
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Set the user's preference for Voice over WiFi calling mode.
+ * @param mode The user's preference for the technology to register for IMS over, can be one of
+ * the following:
+ * - {@link #WIFI_MODE_WIFI_ONLY}
+ * - {@link #WIFI_MODE_CELLULAR_PREFERRED}
+ * - {@link #WIFI_MODE_WIFI_PREFERRED}
+ * @throws IllegalArgumentException if the subscription associated with this operation is not
+ * active (SIM is not inserted, ESIM inactive) or invalid.
+ * @see #getVoWiFiModeSetting()
+ * @hide
+ */
+ @SystemApi @TestApi
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ public void setVoWiFiModeSetting(@WiFiCallingMode int mode) {
+ ITelephony iTelephony = getITelephony();
+ if (iTelephony == null) {
+ throw new RuntimeException("Could not find Telephony Service.");
+ }
+
+ try {
+ iTelephony.setVoWiFiModeSetting(mSubId, mode);
+ } catch (ServiceSpecificException e) {
+ if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
+ // Rethrow as runtime error to keep API compatible.
+ throw new IllegalArgumentException(e.getMessage());
+ } else {
+ throw new RuntimeException(e.getMessage());
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Set the user's preference for Voice over WiFi calling mode while the device is roaming on
+ * another network.
+ *
+ * @return The user's preference for the technology to register for IMS over when roaming on
+ * another network, can be one of the following:
+ * - {@link #WIFI_MODE_WIFI_ONLY}
+ * - {@link #WIFI_MODE_CELLULAR_PREFERRED}
+ * - {@link #WIFI_MODE_WIFI_PREFERRED}
+ * @throws IllegalArgumentException if the subscription associated with this operation is not
+ * active (SIM is not inserted, ESIM inactive) or invalid.
+ * @see #setVoWiFiRoamingSettingEnabled(boolean)
+ * @hide
+ */
+ @SystemApi @TestApi
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public @WiFiCallingMode int getVoWiFiRoamingModeSetting() {
+ ITelephony iTelephony = getITelephony();
+ if (iTelephony == null) {
+ throw new RuntimeException("Could not find Telephony Service.");
+ }
+
+ try {
+ return iTelephony.getVoWiFiRoamingModeSetting(mSubId);
+ } catch (ServiceSpecificException e) {
+ if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
+ // Rethrow as runtime error to keep API compatible.
+ throw new IllegalArgumentException(e.getMessage());
+ } else {
+ throw new RuntimeException(e.getMessage());
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Set the user's preference for Voice over WiFi mode while the device is roaming on another
+ * network.
+ *
+ * @param mode The user's preference for the technology to register for IMS over when roaming on
+ * another network, can be one of the following:
+ * - {@link #WIFI_MODE_WIFI_ONLY}
+ * - {@link #WIFI_MODE_CELLULAR_PREFERRED}
+ * - {@link #WIFI_MODE_WIFI_PREFERRED}
+ * @throws IllegalArgumentException if the subscription associated with this operation is not
+ * active (SIM is not inserted, ESIM inactive) or invalid.
+ * @see #getVoWiFiRoamingModeSetting()
+ * @hide
+ */
+ @SystemApi @TestApi
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ public void setVoWiFiRoamingModeSetting(@WiFiCallingMode int mode) {
+ ITelephony iTelephony = getITelephony();
+ if (iTelephony == null) {
+ throw new RuntimeException("Could not find Telephony Service.");
+ }
+
+ try {
+ iTelephony.setVoWiFiRoamingModeSetting(mSubId, mode);
+ } catch (ServiceSpecificException e) {
+ if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
+ // Rethrow as runtime error to keep API compatible.
+ throw new IllegalArgumentException(e.getMessage());
+ } else {
+ throw new RuntimeException(e.getMessage());
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Sets the capability of RTT for IMS calls placed on this subscription.
+ *
+ * Note: This does not affect the value of
+ * {@link android.provider.Settings.Secure#RTT_CALLING_MODE}, which is the global user setting
+ * for RTT. That value is enabled/disabled separately by the user through the Accessibility
+ * settings.
+ * @throws IllegalArgumentException if the subscription associated with this operation is not
+ * active (SIM is not inserted, ESIM inactive) or invalid.
+ * @param isEnabled if true RTT should be enabled during calls made on this subscription.
+ * @hide
+ */
+ @SystemApi @TestApi
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ public void setRttCapabilitySetting(boolean isEnabled) {
+ ITelephony iTelephony = getITelephony();
+ if (iTelephony == null) {
+ throw new RuntimeException("Could not find Telephony Service.");
+ }
+
+ try {
+ iTelephony.setRttCapabilitySetting(mSubId, isEnabled);
+ } catch (ServiceSpecificException e) {
+ if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
+ // Rethrow as runtime error to keep API compatible.
+ throw new IllegalArgumentException(e.getMessage());
+ } else {
+ throw new RuntimeException(e.getMessage());
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * @return true if TTY over VoLTE is supported
+ *
+ * <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>
+ * <li>The caller is the default SMS app for the device.</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 IllegalArgumentException if the subscription associated with this operation is not
+ * active (SIM is not inserted, ESIM inactive) or invalid.
+ * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL
+ */
+ @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 isTtyOverVolteEnabled() {
+ ITelephony iTelephony = getITelephony();
+ if (iTelephony == null) {
+ throw new RuntimeException("Could not find Telephony Service.");
+ }
+
+ try {
+ return iTelephony.isTtyOverVolteEnabled(mSubId);
+ } catch (ServiceSpecificException e) {
+ if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
+ // Rethrow as runtime error to keep API compatible.
+ throw new IllegalArgumentException(e.getMessage());
+ } else {
+ throw new RuntimeException(e.getMessage());
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Get the status of the MmTel Feature registered on this subscription.
+ * @param executor The executor that will be used to call the callback.
+ * @param callback A callback containing an Integer describing the current state of the
+ * MmTel feature, Which will be one of the following:
+ * {@link ImsFeature#STATE_UNAVAILABLE},
+ * {@link ImsFeature#STATE_INITIALIZING},
+ * {@link ImsFeature#STATE_READY}. Will be called using the executor
+ * specified when the service state has been retrieved from the IMS service.
+ * @throws ImsException if the IMS service associated with this subscription is not available or
+ * the IMS service is not available.
+ * @hide
+ */
+ @SystemApi @TestApi
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public void getFeatureState(@NonNull @CallbackExecutor Executor executor,
+ @NonNull @ImsFeature.ImsState Consumer<Integer> callback) throws ImsException {
+ if (executor == null) {
+ throw new IllegalArgumentException("Must include a non-null Executor.");
+ }
+ if (callback == null) {
+ throw new IllegalArgumentException("Must include a non-null Consumer.");
+ }
+
+ ITelephony iTelephony = getITelephony();
+ if (iTelephony == null) {
+ throw new ImsException("Could not find Telephony Service.",
+ ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ }
+
+ try {
+ iTelephony.getImsMmTelFeatureState(mSubId, new IIntegerConsumer.Stub() {
+ @Override
+ public void accept(int result) {
+ executor.execute(() -> callback.accept(result));
+ }
+ });
+ } catch (ServiceSpecificException sse) {
+ throw new ImsException(sse.getMessage(), sse.errorCode);
+ } catch (RemoteException e) {
+ e.rethrowAsRuntimeException();
+ }
+ }
+
+ private static ITelephony getITelephony() {
+ ITelephony binder = ITelephony.Stub.asInterface(
+ TelephonyFrameworkInitializer
+ .getTelephonyServiceManager()
+ .getTelephonyServiceRegisterer()
+ .get());
+ return binder;
+ }
+}
diff --git a/android/telephony/ims/ImsRcsManager.java b/android/telephony/ims/ImsRcsManager.java
new file mode 100644
index 0000000..ede67dd
--- /dev/null
+++ b/android/telephony/ims/ImsRcsManager.java
@@ -0,0 +1,455 @@
+/*
+ * 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.Manifest;
+import android.annotation.CallbackExecutor;
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.annotation.SdkConstant;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.provider.Settings;
+import android.telephony.AccessNetworkConstants;
+import android.telephony.CarrierConfigManager;
+import android.telephony.SubscriptionManager;
+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.concurrent.Executor;
+import java.util.function.Consumer;
+
+/**
+ * Manager for interfacing with the framework RCS services, including the User Capability Exchange
+ * (UCE) service, as well as managing user settings.
+ *
+ * Use {@link ImsManager#getImsRcsManager(int)} to create an instance of this manager.
+ */
+public class ImsRcsManager {
+ private static final String TAG = "ImsRcsManager";
+
+ /**
+ * Activity Action: Show the opt-in dialog for enabling or disabling RCS contact discovery
+ * using User Capability Exchange (UCE).
+ * <p>
+ * An application that depends on contact discovery being enabled may send this intent
+ * using {@link Context#startActivity(Intent)} to ask the user to opt-in for contacts upload for
+ * capability exchange if it is currently disabled. Whether or not this setting has been enabled
+ * can be queried using {@link RcsUceAdapter#isUceSettingEnabled()}.
+ * <p>
+ * This intent should only be sent if the carrier supports RCS capability exchange, which can be
+ * queried using the key {@link CarrierConfigManager#KEY_USE_RCS_PRESENCE_BOOL}. Otherwise, the
+ * setting will not be present.
+ * <p>
+ * Input: A mandatory {@link Settings#EXTRA_SUB_ID} extra containing the subscription that the
+ * setting will be be shown for.
+ * <p>
+ * Output: Nothing
+ * @see RcsUceAdapter
+ */
+ @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_SHOW_CAPABILITY_DISCOVERY_OPT_IN =
+ "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)
+ * @hide
+ */
+ public static class AvailabilityCallback {
+
+ private static class CapabilityBinder extends IImsCapabilityCallback.Stub {
+
+ private final AvailabilityCallback mLocalCallback;
+ private Executor mExecutor;
+
+ CapabilityBinder(AvailabilityCallback c) {
+ mLocalCallback = c;
+ }
+
+ @Override
+ public void onCapabilitiesStatusChanged(int config) {
+ if (mLocalCallback == null) return;
+
+ long callingIdentity = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> mLocalCallback.onAvailabilityChanged(
+ new RcsFeature.RcsImsCapabilities(config)));
+ } finally {
+ restoreCallingIdentity(callingIdentity);
+ }
+ }
+
+ @Override
+ public void onQueryCapabilityConfiguration(int capability, int radioTech,
+ boolean isEnabled) {
+ // This is not used for public interfaces.
+ }
+
+ @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;
+ }
+ }
+
+ private final CapabilityBinder mBinder = new CapabilityBinder(this);
+
+ /**
+ * 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) {
+ }
+
+ /**@hide*/
+ public final IImsCapabilityCallback getBinder() {
+ return mBinder;
+ }
+
+ private void setExecutor(Executor executor) {
+ mBinder.setExecutor(executor);
+ }
+ }
+
+ private final int mSubId;
+ private final Context mContext;
+
+ /**
+ * Use {@link ImsManager#getImsRcsManager(int)} to create an instance of this class.
+ * @hide
+ */
+ public ImsRcsManager(Context context, int subId) {
+ mSubId = subId;
+ mContext = context;
+ }
+
+ /**
+ * @return A {@link RcsUceAdapter} used for User Capability Exchange (UCE) operations for
+ * this subscription.
+ */
+ @NonNull
+ public RcsUceAdapter getUceAdapter() {
+ return new RcsUceAdapter(mContext, mSubId);
+ }
+
+ /**
+ * @hide
+ */
+ // @Override add back to RegistrationManager interface once public.
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public void registerImsRegistrationCallback(
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull RegistrationManager.RegistrationCallback c)
+ throws ImsException {
+ if (c == null) {
+ throw new IllegalArgumentException("Must include a non-null RegistrationCallback.");
+ }
+ if (executor == null) {
+ throw new IllegalArgumentException("Must include a non-null Executor.");
+ }
+
+ IImsRcsController imsRcsController = getIImsRcsController();
+ if (imsRcsController == null) {
+ Log.e(TAG, "Register registration callback: IImsRcsController is null");
+ throw new ImsException("Cannot find remote IMS service",
+ ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ }
+
+ c.setExecutor(executor);
+ try {
+ imsRcsController.registerImsRegistrationCallback(mSubId, c.getBinder());
+ } catch (RemoteException | IllegalStateException e) {
+ throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ }
+ }
+
+ /**
+ * @hide
+ */
+ // @Override add back to RegistrationManager interface once public.
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public void unregisterImsRegistrationCallback(
+ @NonNull RegistrationManager.RegistrationCallback c) {
+ if (c == null) {
+ throw new IllegalArgumentException("Must include a non-null RegistrationCallback.");
+ }
+
+ IImsRcsController imsRcsController = getIImsRcsController();
+ if (imsRcsController == null) {
+ Log.e(TAG, "Unregister registration callback: IImsRcsController is null");
+ throw new IllegalStateException("Cannot find remote IMS service");
+ }
+
+ try {
+ imsRcsController.unregisterImsRegistrationCallback(mSubId, c.getBinder());
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * @hide
+ */
+ // @Override add back to RegistrationManager interface once public.
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public void getRegistrationState(@NonNull @CallbackExecutor Executor executor,
+ @NonNull @RegistrationManager.ImsRegistrationState Consumer<Integer> stateCallback) {
+ if (stateCallback == null) {
+ throw new IllegalArgumentException("Must include a non-null stateCallback.");
+ }
+ if (executor == null) {
+ throw new IllegalArgumentException("Must include a non-null Executor.");
+ }
+
+ IImsRcsController imsRcsController = getIImsRcsController();
+ if (imsRcsController == null) {
+ Log.e(TAG, "Get registration state error: IImsRcsController is null");
+ throw new IllegalStateException("Cannot find remote IMS service");
+ }
+
+ try {
+ imsRcsController.getImsRcsRegistrationState(mSubId, new IIntegerConsumer.Stub() {
+ @Override
+ public void accept(int result) {
+ executor.execute(() -> stateCallback.accept(result));
+ }
+ });
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public void getRegistrationTransportType(@NonNull @CallbackExecutor Executor executor,
+ @NonNull @AccessNetworkConstants.TransportType
+ Consumer<Integer> transportTypeCallback) {
+ if (transportTypeCallback == null) {
+ throw new IllegalArgumentException("Must include a non-null transportTypeCallback.");
+ }
+ if (executor == null) {
+ throw new IllegalArgumentException("Must include a non-null Executor.");
+ }
+
+ IImsRcsController imsRcsController = getIImsRcsController();
+ if (imsRcsController == null) {
+ Log.e(TAG, "Get registration transport type error: IImsRcsController is null");
+ throw new IllegalStateException("Cannot find remote IMS service");
+ }
+
+ try {
+ imsRcsController.getImsRcsRegistrationTransportType(mSubId,
+ new IIntegerConsumer.Stub() {
+ @Override
+ public void accept(int result) {
+ executor.execute(() -> transportTypeCallback.accept(result));
+ }
+ });
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Registers an {@link AvailabilityCallback} 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.
+ * <p>
+ * When the callback is registered, it will initiate the callback c 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)
+ * @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
+ */
+ @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.");
+ }
+ if (executor == null) {
+ throw new IllegalArgumentException("Must include a non-null Executor.");
+ }
+
+ IImsRcsController imsRcsController = getIImsRcsController();
+ if (imsRcsController == null) {
+ Log.e(TAG, "Register availability callback: IImsRcsController is null");
+ throw new ImsException("Cannot find remote IMS service",
+ ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ }
+
+ c.setExecutor(executor);
+ try {
+ imsRcsController.registerRcsAvailabilityCallback(mSubId, c.getBinder());
+ } catch (RemoteException e) {
+ Log.e(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}.
+ * <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)
+ * @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
+ */
+ @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.");
+ }
+
+ 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);
+ }
+
+ try {
+ imsRcsController.unregisterRcsAvailabilityCallback(mSubId, c.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);
+ }
+ }
+
+ /**
+ * Query for the capability of an IMS RCS service provided by the framework.
+ * <p>
+ * This only reports the status of RCS capabilities provided by the framework, not necessarily
+ * 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}.
+ * @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 android.telephony.CarrierConfigManager#KEY_USE_RCS_PRESENCE_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
+ */
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public boolean isCapable(@RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability,
+ @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) throws ImsException {
+ IImsRcsController imsRcsController = getIImsRcsController();
+ if (imsRcsController == null) {
+ Log.e(TAG, "isCapable: IImsRcsController is null");
+ throw new ImsException("Cannot find remote IMS service",
+ ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ }
+
+ try {
+ return imsRcsController.isCapable(mSubId, capability, radioTech);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling IImsRcsController#isCapable", e);
+ throw new ImsException("Remote IMS Service is not available",
+ ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ }
+ }
+
+ /**
+ * Query the availability of an IMS RCS capability.
+ * <p>
+ * This only reports the status of RCS capabilities provided by the framework, not necessarily
+ * RCS capabilities provided by over-the-top by applications.
+ *
+ * @param capability the RCS capability to query.
+ * @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.
+ * @see #isCapable(int, int)
+ * @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
+ */
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public boolean isAvailable(@RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability)
+ throws ImsException {
+ IImsRcsController imsRcsController = getIImsRcsController();
+ if (imsRcsController == null) {
+ Log.e(TAG, "isAvailable: IImsRcsController is null");
+ throw new ImsException("Cannot find remote IMS service",
+ ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ }
+
+ try {
+ return imsRcsController.isAvailable(mSubId, capability);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling IImsRcsController#isAvailable", e);
+ throw new ImsException("Remote IMS Service is not available",
+ ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ }
+ }
+
+ private IImsRcsController getIImsRcsController() {
+ IBinder binder = TelephonyFrameworkInitializer
+ .getTelephonyServiceManager()
+ .getTelephonyImsServiceRegisterer()
+ .get();
+ return IImsRcsController.Stub.asInterface(binder);
+ }
+}
diff --git a/android/telephony/ims/ImsReasonInfo.java b/android/telephony/ims/ImsReasonInfo.java
new file mode 100644
index 0000000..0d6b31d
--- /dev/null
+++ b/android/telephony/ims/ImsReasonInfo.java
@@ -0,0 +1,1232 @@
+/*
+ * 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.IntDef;
+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 java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * 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
+ * CODE_CALL_BARRED, then the call was blocked by network call barring configuration and it is not
+ * the device's bug and the user can retry the call when network lift the barring.
+ * Typical use case includes call backs when IMS call state changed with this class as a param
+ * containing details on why IMS call changed state/failed.
+ */
+public final class ImsReasonInfo implements Parcelable {
+
+ /**
+ * The Reason is unspecified.
+ */
+ public static final int CODE_UNSPECIFIED = 0;
+
+
+ // LOCAL
+
+ // IMS -> Telephony
+ /**
+ * The passed argument is invalid.
+ */
+ public static final int CODE_LOCAL_ILLEGAL_ARGUMENT = 101;
+ /**
+ * The operation was invoked while in an invalid call state.
+ */
+ public static final int CODE_LOCAL_ILLEGAL_STATE = 102;
+ /**
+ * IMS service internal error
+ */
+ public static final int CODE_LOCAL_INTERNAL_ERROR = 103;
+ /**
+ * ImsService has crashed (service connection is lost).
+ */
+ public static final int CODE_LOCAL_IMS_SERVICE_DOWN = 106;
+ /**
+ * No pending incoming call exists
+ */
+ public static final int CODE_LOCAL_NO_PENDING_CALL = 107;
+ /**
+ * IMS Call ended during conference merge process
+ */
+ public static final int CODE_LOCAL_ENDED_BY_CONFERENCE_MERGE = 108;
+
+ // IMS -> Telephony
+ /**
+ * Service unavailable; radio power off
+ */
+ public static final int CODE_LOCAL_POWER_OFF = 111;
+ /**
+ * Service unavailable; low battery
+ */
+ public static final int CODE_LOCAL_LOW_BATTERY = 112;
+ /**
+ * Service unavailable; out of service (data service state)
+ */
+ public static final int CODE_LOCAL_NETWORK_NO_SERVICE = 121;
+ /**
+ * Service unavailable; no LTE coverage
+ * (VoLTE is not supported even though IMS is registered)
+ */
+ public static final int CODE_LOCAL_NETWORK_NO_LTE_COVERAGE = 122;
+ /**
+ * Service unavailable; located in roaming area
+ */
+ public static final int CODE_LOCAL_NETWORK_ROAMING = 123;
+ /**
+ * Service unavailable; IP changed
+ */
+ public static final int CODE_LOCAL_NETWORK_IP_CHANGED = 124;
+ /**
+ * Service unavailable; for an unspecified reason
+ */
+ public static final int CODE_LOCAL_SERVICE_UNAVAILABLE = 131;
+ /**
+ * Service unavailable; IMS is not registered
+ */
+ public static final int CODE_LOCAL_NOT_REGISTERED = 132;
+
+ // IMS <-> Telephony
+ /**
+ * Maximum number of simultaneous calls exceeded
+ */
+ public static final int CODE_LOCAL_CALL_EXCEEDED = 141;
+ // IMS <- Telephony
+ /**
+ * The call is busy.
+ */
+ public static final int CODE_LOCAL_CALL_BUSY = 142;
+ /**
+ * The Call has been declined locally on this device.
+ */
+ public static final int CODE_LOCAL_CALL_DECLINE = 143;
+ // IMS -> Telephony
+ /**
+ * Can not complete call; an SRVCC is in progress.
+ */
+ public static final int CODE_LOCAL_CALL_VCC_ON_PROGRESSING = 144;
+ /**
+ * Can not complete call; resource reservation is failed (QoS precondition)
+ */
+ public static final int CODE_LOCAL_CALL_RESOURCE_RESERVATION_FAILED = 145;
+ /**
+ * VoLTE service can't be provided by the network or remote end, retry the call.
+ * Resolve the extra code provided in (EXTRA_CODE_CALL_RETRY_*) if the below code is set
+ */
+ public static final int CODE_LOCAL_CALL_CS_RETRY_REQUIRED = 146;
+ /**
+ * VoLTE service can't be provided by the network temporarily, retry the call.
+ */
+ public static final int CODE_LOCAL_CALL_VOLTE_RETRY_REQUIRED = 147;
+ /**
+ * IMS call is already terminated (in TERMINATED state).
+ */
+ public static final int CODE_LOCAL_CALL_TERMINATED = 148;
+ /**
+ * Call was disconnected because a handover is not feasible due to network conditions.
+ */
+ public static final int CODE_LOCAL_HO_NOT_FEASIBLE = 149;
+ /**
+ * This device does not support IMS.
+ * @hide
+ */
+ public static final int CODE_LOCAL_IMS_NOT_SUPPORTED_ON_DEVICE = 150;
+
+ /*
+ * TIMEOUT (IMS -> Telephony)
+ */
+ /**
+ * 1xx waiting timer is expired after sending INVITE request (MO calls only)
+ */
+ public static final int CODE_TIMEOUT_1XX_WAITING = 201;
+ /**
+ * User didn't answer during call setup operation (MO/MT)
+ * MO : 200 OK to INVITE request is not received,
+ * MT : No action from user after alerting the call
+ */
+ public static final int CODE_TIMEOUT_NO_ANSWER = 202;
+ /**
+ * User no answer during call update operation (MO/MT)
+ * MO : 200 OK to re-INVITE request is not received,
+ * MT : No action from user after alerting the call
+ */
+ public static final int CODE_TIMEOUT_NO_ANSWER_CALL_UPDATE = 203;
+
+ /**
+ * The call was blocked by call barring configuration.
+ */
+ public static final int CODE_CALL_BARRED = 240;
+
+ /**
+ * The operation is restricted to fixed dialing numbers only.
+ */
+ public static final int CODE_FDN_BLOCKED = 241;
+
+ /**
+ * Network rejected the emergency call request because IMEI was used as identification
+ * and this capability is not supported by the network.
+ */
+ public static final int CODE_IMEI_NOT_ACCEPTED = 243;
+
+ //STK CC errors
+ /**
+ * Stk Call Control modified DIAL request to USSD request.
+ */
+ public static final int CODE_DIAL_MODIFIED_TO_USSD = 244;
+ /**
+ * Stk Call Control modified DIAL request to SS request.
+ */
+ public static final int CODE_DIAL_MODIFIED_TO_SS = 245;
+ /**
+ * Stk Call Control modified DIAL request to DIAL with modified data.
+ */
+ public static final int CODE_DIAL_MODIFIED_TO_DIAL = 246;
+ /**
+ * Stk Call Control modified DIAL request to Video DIAL request.
+ */
+ public static final int CODE_DIAL_MODIFIED_TO_DIAL_VIDEO = 247;
+ /**
+ * Stk Call Control modified Video DIAL request to DIAL request.
+ */
+ public static final int CODE_DIAL_VIDEO_MODIFIED_TO_DIAL = 248;
+ /**
+ * Stk Call Control modified Video DIAL request to Video DIAL request.
+ */
+ public static final int CODE_DIAL_VIDEO_MODIFIED_TO_DIAL_VIDEO = 249;
+ /**
+ * Stk Call Control modified Video DIAL request to SS request.
+ */
+ public static final int CODE_DIAL_VIDEO_MODIFIED_TO_SS = 250;
+ /**
+ * Stk Call Control modified Video DIAL request to USSD request.
+ */
+ public static final int CODE_DIAL_VIDEO_MODIFIED_TO_USSD = 251;
+
+ /*
+ * STATUSCODE (SIP response code) (IMS -> Telephony)
+ */
+ // 3xx responses
+ /**
+ * SIP 3xx response: SIP request is redirected
+ */
+ public static final int CODE_SIP_REDIRECTED = 321;
+ // 4xx responses
+ /**
+ * Sip 400 response : Bad Request
+ */
+ public static final int CODE_SIP_BAD_REQUEST = 331;
+ /**
+ * Sip 403 response : Forbidden
+ */
+ public static final int CODE_SIP_FORBIDDEN = 332;
+ /**
+ * Sip 404 response : Not Found
+ */
+ public static final int CODE_SIP_NOT_FOUND = 333;
+ /**
+ * Not supported, because of one of the following:
+ * SIP response 415 : Unsupported Media Type,
+ * SIP response 416 : Unsupported URI Scheme,
+ * SIP response 420 : Bad Extension
+ */
+ public static final int CODE_SIP_NOT_SUPPORTED = 334;
+ /**
+ * SIP response 408 : Request Timeout.
+ */
+ public static final int CODE_SIP_REQUEST_TIMEOUT = 335;
+ /**
+ * SIP response 480 : Temporarily Unavailable
+ */
+ public static final int CODE_SIP_TEMPRARILY_UNAVAILABLE = 336;
+ /**
+ * SIP response 484 : Address Incomplete
+ */
+ public static final int CODE_SIP_BAD_ADDRESS = 337;
+ /**
+ * Returned a busy response, may be one of the following:
+ * SIP response 486 : Busy Here,
+ * SIP response 600 : Busy Everywhere
+ */
+ public static final int CODE_SIP_BUSY = 338;
+ /**
+ * SIP response 487 : Request Terminated
+ */
+ public static final int CODE_SIP_REQUEST_CANCELLED = 339;
+ /**
+ * Received a not acceptable response, will be one of the following:
+ * SIP response 406 : Not Acceptable
+ * SIP response 488 : Not Acceptable Here
+ * SIP response 606 : Not Acceptable
+ */
+ public static final int CODE_SIP_NOT_ACCEPTABLE = 340;
+ /**
+ * Received a not acceptable response, will be one of the following:
+ * SIP response 410 : Gone
+ * SIP response 604 : Does Not Exist Anywhere
+ */
+ public static final int CODE_SIP_NOT_REACHABLE = 341;
+ /**
+ * Received another unspecified error SIP response from the client.
+ */
+ public static final int CODE_SIP_CLIENT_ERROR = 342;
+ /**
+ * SIP response 481: Transaction Does Not Exist
+ */
+ public static final int CODE_SIP_TRANSACTION_DOES_NOT_EXIST = 343;
+ // 5xx responses
+ /**
+ * SIP response 501 : Server Internal Error
+ */
+ public static final int CODE_SIP_SERVER_INTERNAL_ERROR = 351;
+ /**
+ * SIP response 503 : Service Unavailable
+ */
+ public static final int CODE_SIP_SERVICE_UNAVAILABLE = 352;
+ /**
+ * SIP response 504 : Server Time-out
+ */
+ public static final int CODE_SIP_SERVER_TIMEOUT = 353;
+ /**
+ * Received an unspecified SIP server error response.
+ */
+ public static final int CODE_SIP_SERVER_ERROR = 354;
+ // 6xx responses
+ /**
+ * 603 : Decline
+ */
+ public static final int CODE_SIP_USER_REJECTED = 361;
+ /**
+ * Unspecified 6xx error.
+ */
+ public static final int CODE_SIP_GLOBAL_ERROR = 362;
+
+ /**
+ * Emergency call failed in the modem with a temporary fail cause and should be redialed on this
+ * slot.
+ */
+ public static final int CODE_EMERGENCY_TEMP_FAILURE = 363;
+ /**
+ * Emergency call failed in the modem with a permanent fail cause and should not be redialed on
+ * this slot. If there are any other slots available for emergency calling, try those.
+ */
+ public static final int CODE_EMERGENCY_PERM_FAILURE = 364;
+
+ /**
+ * Call failure code during hangup/reject if user marked the call as unwanted.
+ *
+ * Android Telephony will receive information whether ROBO call feature is supported by the
+ * network from modem and propagate the same to AOSP as new ImsCallProfile members. OEMs can
+ * check this information and provide an option to the user to mark the call as unwanted.
+ */
+ public static final int CODE_SIP_USER_MARKED_UNWANTED = 365;
+
+ /**
+ * SIP Response : 405
+ * Method not allowed for the address in the Request URI
+ */
+ public static final int CODE_SIP_METHOD_NOT_ALLOWED = 366;
+
+ /**
+ * SIP Response : 407
+ * The request requires user authentication
+ */
+ public static final int CODE_SIP_PROXY_AUTHENTICATION_REQUIRED = 367;
+
+ /**
+ * SIP Response : 413
+ * Request body too large
+ */
+ public static final int CODE_SIP_REQUEST_ENTITY_TOO_LARGE = 368;
+
+ /**
+ * SIP Response : 414
+ * Request-URI too large
+ */
+ public static final int CODE_SIP_REQUEST_URI_TOO_LARGE = 369;
+
+ /**
+ * SIP Response : 421
+ * Specific extension is required, which is not present in the HEADER
+ */
+ public static final int CODE_SIP_EXTENSION_REQUIRED = 370;
+
+ /**
+ * SIP Response : 422
+ * The session expiration field too small
+ */
+ public static final int CODE_SIP_INTERVAL_TOO_BRIEF = 371;
+
+ /**
+ * SIP Response : 481
+ * Request received by the server does not match any dialog or transaction
+ */
+ public static final int CODE_SIP_CALL_OR_TRANS_DOES_NOT_EXIST = 372;
+
+ /**
+ * SIP Response : 482
+ * Server has detected a loop
+ */
+ public static final int CODE_SIP_LOOP_DETECTED = 373;
+
+ /**
+ * SIP Response : 483
+ * Max-Forwards value reached
+ */
+ public static final int CODE_SIP_TOO_MANY_HOPS = 374;
+
+ /**
+ * SIP Response : 485
+ * Request-URI is ambiguous
+ *
+ */
+ public static final int CODE_SIP_AMBIGUOUS = 376;
+
+ /**
+ * SIP Response : 491
+ * Server has pending request for same dialog
+ */
+ public static final int CODE_SIP_REQUEST_PENDING = 377;
+
+ /**
+ * SIP Response : 493
+ * The request cannot be decrypted by recipient
+ */
+ public static final int CODE_SIP_UNDECIPHERABLE = 378;
+
+ /**
+ * MEDIA (IMS -> Telephony)
+ */
+ /**
+ * Media resource initialization failed
+ */
+ public static final int CODE_MEDIA_INIT_FAILED = 401;
+ /**
+ * RTP timeout (no audio / video traffic in the session)
+ */
+ public static final int CODE_MEDIA_NO_DATA = 402;
+ /**
+ * Media is not supported; so dropped the call
+ */
+ public static final int CODE_MEDIA_NOT_ACCEPTABLE = 403;
+ /**
+ * Unspecified media related error.
+ */
+ public static final int CODE_MEDIA_UNSPECIFIED = 404;
+
+ /*
+ * USER
+ */
+ // Telephony -> IMS
+ /**
+ * User triggers the call to be terminated.
+ */
+ public static final int CODE_USER_TERMINATED = 501;
+ /**
+ * No action was taken while an incoming call was ringing.
+ */
+ public static final int CODE_USER_NOANSWER = 502;
+ /**
+ * User ignored an incoming call.
+ */
+ public static final int CODE_USER_IGNORE = 503;
+ /**
+ * User declined an incoming call.
+ */
+ public static final int CODE_USER_DECLINE = 504;
+ /**
+ * Device declined/ended a call due to a low battery condition.
+ */
+ public static final int CODE_LOW_BATTERY = 505;
+ /**
+ * Device declined a call due to a blacklisted caller ID.
+ */
+ public static final int CODE_BLACKLISTED_CALL_ID = 506;
+ // IMS -> Telephony
+ /**
+ * The call has been terminated by the network or remote user.
+ */
+ public static final int CODE_USER_TERMINATED_BY_REMOTE = 510;
+ /**
+ * Upgrade Downgrade request rejected by
+ * Remote user if the request is MO initiated
+ * Local user if the request is MT initiated
+ */
+ public static final int CODE_USER_REJECTED_SESSION_MODIFICATION = 511;
+
+ /**
+ * Upgrade Downgrade request cancelled by the user who initiated it
+ */
+ public static final int CODE_USER_CANCELLED_SESSION_MODIFICATION = 512;
+
+ /**
+ * UPGRADE DOWNGRADE operation failed
+ * This can happen due to failure from SIP/RTP/SDP generation or a Call end is
+ * triggered/received while Reinvite is in progress.
+ */
+ public static final int CODE_SESSION_MODIFICATION_FAILED = 1517;
+
+ /*
+ * UT
+ */
+ /**
+ * UT is currently not supported on this device.
+ */
+ public static final int CODE_UT_NOT_SUPPORTED = 801;
+ /**
+ * UT services are currently not available on this device.
+ */
+ public static final int CODE_UT_SERVICE_UNAVAILABLE = 802;
+ /**
+ * The requested UT operation is not allowed.
+ */
+ public static final int CODE_UT_OPERATION_NOT_ALLOWED = 803;
+ /**
+ * The UT request resulted in a network error.
+ */
+ public static final int CODE_UT_NETWORK_ERROR = 804;
+ /**
+ * The password entered for UT operations does not match the stored password.
+ */
+ public static final int CODE_UT_CB_PASSWORD_MISMATCH = 821;
+ //STK CC errors
+ /**
+ * Sim Toolkit Call Control modified the UT operation to a dial command.
+ */
+ public static final int CODE_UT_SS_MODIFIED_TO_DIAL = 822;
+ /**
+ * Sim Toolkit Call Control modified the UT operation to a USSD command.
+ */
+ public static final int CODE_UT_SS_MODIFIED_TO_USSD = 823;
+ /**
+ * Sim Toolkit Call Control modified the UT operation to another supplementary service command.
+ */
+ public static final int CODE_UT_SS_MODIFIED_TO_SS = 824;
+ /**
+ * Sim Toolkit Call Control modified the UT operation to a video call dial command.
+ */
+ public static final int CODE_UT_SS_MODIFIED_TO_DIAL_VIDEO = 825;
+
+ /**@hide*/
+ @IntDef(value = {
+ CODE_UT_NOT_SUPPORTED,
+ CODE_UT_SERVICE_UNAVAILABLE,
+ CODE_UT_OPERATION_NOT_ALLOWED,
+ CODE_UT_NETWORK_ERROR,
+ CODE_UT_CB_PASSWORD_MISMATCH,
+ CODE_UT_SS_MODIFIED_TO_DIAL,
+ CODE_UT_SS_MODIFIED_TO_USSD,
+ CODE_UT_SS_MODIFIED_TO_SS,
+ CODE_UT_SS_MODIFIED_TO_DIAL_VIDEO
+ }, prefix = "CODE_UT_")
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface UtReason {}
+
+ /**
+ * Emergency callback mode is not supported.
+ */
+ public static final int CODE_ECBM_NOT_SUPPORTED = 901;
+
+ /**
+ * Fail code used to indicate that Multi-endpoint is not supported by the IMS framework.
+ */
+ public static final int CODE_MULTIENDPOINT_NOT_SUPPORTED = 902;
+
+ /**
+ * IMS Registration error code
+ */
+ public static final int CODE_REGISTRATION_ERROR = 1000;
+
+ /*
+ * CALL DROP error codes (Call could drop because of many reasons like Network not available,
+ * handover, failed, etc)
+ */
+ /**
+ * MT call has ended due to a release from the network because the call was answered elsewhere.
+ */
+ public static final int CODE_ANSWERED_ELSEWHERE = 1014;
+
+ /**
+ * For MultiEndpoint - Call Pull request has failed.
+ */
+ public static final int CODE_CALL_PULL_OUT_OF_SYNC = 1015;
+
+ /**
+ * For MultiEndpoint - Call has been pulled from primary to secondary.
+ */
+ public static final int CODE_CALL_END_CAUSE_CALL_PULL = 1016;
+
+ /**
+ * CALL DROP error code for the case when a device is ePDG capable and when the user is on an
+ * active wifi call and at the edge of coverage and there is no qualified LTE network available
+ * to handover the call to. We get a handover NOT_TRIGERRED message from the modem. This error
+ * code is received as part of the handover message.
+ */
+ public static final int CODE_CALL_DROP_IWLAN_TO_LTE_UNAVAILABLE = 1100;
+
+ /**
+ * For MultiEndPoint - Call was rejected elsewhere
+ */
+ public static final int CODE_REJECTED_ELSEWHERE = 1017;
+
+ /**
+ * Supplementary services (HOLD/RESUME) failure error codes.
+ * Values for Supplemetary services failure - Failed, Cancelled and Re-Invite collision.
+ */
+
+ /**
+ * Supplementary Services (HOLD/RESUME) - the command failed.
+ */
+ public static final int CODE_SUPP_SVC_FAILED = 1201;
+ /**
+ * Supplementary Services (HOLD/RESUME) - the command was cancelled.
+ */
+ public static final int CODE_SUPP_SVC_CANCELLED = 1202;
+ /**
+ * Supplementary Services (HOLD/RESUME) - the command resulted in a re-invite collision.
+ */
+ public static final int CODE_SUPP_SVC_REINVITE_COLLISION = 1203;
+
+ /**
+ * DPD Procedure received no response or send failed.
+ */
+ public static final int CODE_IWLAN_DPD_FAILURE = 1300;
+
+ /**
+ * Establishment of the ePDG Tunnel Failed.
+ */
+ public static final int CODE_EPDG_TUNNEL_ESTABLISH_FAILURE = 1400;
+
+ /**
+ * Re-keying of the ePDG Tunnel Failed; may not always result in teardown.
+ */
+ public static final int CODE_EPDG_TUNNEL_REKEY_FAILURE = 1401;
+
+ /**
+ * Connection to the packet gateway is lost.
+ */
+ public static final int CODE_EPDG_TUNNEL_LOST_CONNECTION = 1402;
+
+ /**
+ * The maximum number of calls allowed has been reached. Used in a multi-endpoint scenario
+ * where the number of calls across all connected devices has reached the maximum.
+ */
+ public static final int CODE_MAXIMUM_NUMBER_OF_CALLS_REACHED = 1403;
+
+ /**
+ * Similar to {@link #CODE_LOCAL_CALL_DECLINE}, except indicates that a remote device has
+ * declined the call. Used in a multi-endpoint scenario where a remote device declined an
+ * incoming call.
+ */
+ public static final int CODE_REMOTE_CALL_DECLINE = 1404;
+
+ /**
+ * Indicates the call was disconnected due to the user reaching their data limit.
+ */
+ public static final int CODE_DATA_LIMIT_REACHED = 1405;
+
+ /**
+ * Indicates the call was disconnected due to the user disabling cellular data.
+ */
+ public static final int CODE_DATA_DISABLED = 1406;
+
+ /**
+ * Indicates a call was disconnected due to loss of wifi signal.
+ */
+ public static final int CODE_WIFI_LOST = 1407;
+
+ /**
+ * Indicates the registration attempt on IWLAN failed due to IKEv2 authetication failure
+ * during tunnel establishment.
+ */
+ public static final int CODE_IKEV2_AUTH_FAILURE = 1408;
+
+ /** The call cannot be established because RADIO is OFF */
+ public static final int CODE_RADIO_OFF = 1500;
+
+ /** The call cannot be established because of no valid SIM */
+ public static final int CODE_NO_VALID_SIM = 1501;
+
+ /** The failure is due internal error at modem */
+ public static final int CODE_RADIO_INTERNAL_ERROR = 1502;
+
+ /** The failure is due to UE timer expired while waiting for a response from network */
+ public static final int CODE_NETWORK_RESP_TIMEOUT = 1503;
+
+ /** The failure is due to explicit reject from network */
+ public static final int CODE_NETWORK_REJECT = 1504;
+
+ /** The failure is due to radio access failure. ex. RACH failure */
+ public static final int CODE_RADIO_ACCESS_FAILURE = 1505;
+
+ /** Call/IMS registration failed/dropped because of a RLF */
+ public static final int CODE_RADIO_LINK_FAILURE = 1506;
+
+ /** Call/IMS registration failed/dropped because of radio link lost */
+ public static final int CODE_RADIO_LINK_LOST = 1507;
+
+ /** The call Call/IMS registration failed because of a radio uplink issue */
+ public static final int CODE_RADIO_UPLINK_FAILURE = 1508;
+
+ /** Call failed because of a RRC connection setup failure */
+ public static final int CODE_RADIO_SETUP_FAILURE = 1509;
+
+ /** Call failed/dropped because of RRC connection release from NW */
+ public static final int CODE_RADIO_RELEASE_NORMAL = 1510;
+
+ /** Call failed/dropped because of RRC abnormally released by modem/network */
+ public static final int CODE_RADIO_RELEASE_ABNORMAL = 1511;
+
+ /** Call failed because of access class barring */
+ public static final int CODE_ACCESS_CLASS_BLOCKED = 1512;
+
+ /** Call/IMS registration is failed/dropped because of a network detach */
+ public static final int CODE_NETWORK_DETACH = 1513;
+
+ /**
+ * Call failed due to SIP code 380 (Alternative Service response) while dialing an "undetected
+ * emergency number". This scenario is important in some regions where the carrier network will
+ * identify other non-emergency help numbers (e.g. mountain rescue) when attempting to dial.
+ */
+ public static final int CODE_SIP_ALTERNATE_EMERGENCY_CALL = 1514;
+
+ /**
+ * Call failed because of unobtainable number
+ * @hide
+ */
+ public static final int CODE_UNOBTAINABLE_NUMBER = 1515;
+
+ /**
+ * Call failed because WiFi call could not complete and circuit switch silent redial
+ * is not allowed while roaming on another network.
+ */
+ public static final int CODE_NO_CSFB_IN_CS_ROAM = 1516;
+
+ /**
+ * The rejection cause is not known.
+ * <p>
+ * Used with implicit call rejection.
+ */
+ public static final int CODE_REJECT_UNKNOWN = 1600;
+
+ /**
+ * Ongoing call, and call waiting is disabled.
+ * <p>
+ * Used with implicit call rejection.
+ */
+ public static final int CODE_REJECT_ONGOING_CALL_WAITING_DISABLED = 1601;
+
+ /**
+ * A call is ongoing on another sub.
+ * <p>
+ * Used with implicit call rejection.
+ */
+ public static final int CODE_REJECT_CALL_ON_OTHER_SUB = 1602;
+
+ /**
+ * CDMA call collision.
+ * <p>
+ * Used with implicit call rejection.
+ */
+ public static final int CODE_REJECT_1X_COLLISION = 1603;
+
+ /**
+ * IMS is not registered for service yet.
+ * <p>
+ * Used with implicit call rejection.
+ */
+ public static final int CODE_REJECT_SERVICE_NOT_REGISTERED = 1604;
+
+ /**
+ * The call type is not allowed on the current RAT.
+ * <p>
+ * Used with implicit call rejection.
+ */
+ public static final int CODE_REJECT_CALL_TYPE_NOT_ALLOWED = 1605;
+
+ /**
+ * And emergency call is ongoing.
+ * <p>
+ * Used with implicit call rejection.
+ */
+ public static final int CODE_REJECT_ONGOING_E911_CALL = 1606;
+
+ /**
+ * Another call is in the process of being establilshed.
+ * <p>
+ * Used with implicit call rejection.
+ */
+ public static final int CODE_REJECT_ONGOING_CALL_SETUP = 1607;
+
+ /**
+ * Maximum number of allowed calls are already in progress.
+ * <p>
+ * Used with implicit call rejection.
+ */
+ public static final int CODE_REJECT_MAX_CALL_LIMIT_REACHED = 1608;
+
+ /**
+ * Invalid/unsupported SIP headers received.
+ * <p>
+ * Used with implicit call rejection.
+ */
+ public static final int CODE_REJECT_UNSUPPORTED_SIP_HEADERS = 1609;
+
+ /**
+ * Invalid/unsupported SDP headers received.
+ * <p>
+ * Used with implicit call rejection.
+ */
+ public static final int CODE_REJECT_UNSUPPORTED_SDP_HEADERS = 1610;
+
+ /**
+ * A call transfer is in progress.
+ * <p>
+ * Used with implicit call rejection.
+ */
+ public static final int CODE_REJECT_ONGOING_CALL_TRANSFER = 1611;
+
+ /**
+ * An internal error occured while processing the call.
+ * <p>
+ * Used with implicit call rejection.
+ */
+ public static final int CODE_REJECT_INTERNAL_ERROR = 1612;
+
+ /**
+ * Call failure due to lack of dedicated bearer.
+ * <p>
+ * Used with implicit call rejection.
+ */
+ public static final int CODE_REJECT_QOS_FAILURE = 1613;
+
+ /**
+ * A call handover is in progress.
+ * <p>
+ * Used with implicit call rejection.
+ */
+ public static final int CODE_REJECT_ONGOING_HANDOVER = 1614;
+
+ /**
+ * Video calling not supported with TTY.
+ * <p>
+ * Used with implicit call rejection.
+ */
+ public static final int CODE_REJECT_VT_TTY_NOT_ALLOWED = 1615;
+
+ /**
+ * A call upgrade is in progress.
+ * <p>
+ * Used with implicit call rejection.
+ */
+ public static final int CODE_REJECT_ONGOING_CALL_UPGRADE = 1616;
+
+ /**
+ * Call from conference server, when TTY mode is ON.
+ * <p>
+ * Used with implicit call rejection.
+ */
+ public static final int CODE_REJECT_CONFERENCE_TTY_NOT_ALLOWED = 1617;
+
+ /**
+ * A conference call is ongoing.
+ * <p>
+ * Used with implicit call rejection.
+ */
+ public static final int CODE_REJECT_ONGOING_CONFERENCE_CALL = 1618;
+
+ /**
+ * A video call with AVPF is not supported.
+ * <p>
+ * Used with implicit call rejection.
+ */
+ public static final int CODE_REJECT_VT_AVPF_NOT_ALLOWED = 1619;
+
+ /**
+ * And encrypted call is ongoing; other calls not supported.
+ * <p>
+ * Used with implicit call rejection.
+ */
+ public static final int CODE_REJECT_ONGOING_ENCRYPTED_CALL = 1620;
+
+ /**
+ * A CS call is ongoing.
+ * <p>
+ * Used with implicit call rejection.
+ */
+ public static final int CODE_REJECT_ONGOING_CS_CALL = 1621;
+
+ /**
+ * An attempt was made to place an emergency call over WFC when emergency services is not
+ * currently available in the current location.
+ * @hide
+ */
+ public static final int CODE_EMERGENCY_CALL_OVER_WFC_NOT_AVAILABLE = 1622;
+
+ /**
+ * Indicates that WiFi calling service is not available in the current location.
+ * @hide
+ */
+ public static final int CODE_WFC_SERVICE_NOT_AVAILABLE_IN_THIS_LOCATION = 1623;
+
+ /**
+ * The dialed RTT call should be retried without RTT
+ * @hide
+ */
+ public static final int CODE_RETRY_ON_IMS_WITHOUT_RTT = 3001;
+
+ /*
+ * OEM specific error codes. To be used by OEMs when they don't want to reveal error code which
+ * would be replaced by ERROR_UNSPECIFIED.
+ */
+ public static final int CODE_OEM_CAUSE_1 = 0xf001;
+ public static final int CODE_OEM_CAUSE_2 = 0xf002;
+ public static final int CODE_OEM_CAUSE_3 = 0xf003;
+ public static final int CODE_OEM_CAUSE_4 = 0xf004;
+ public static final int CODE_OEM_CAUSE_5 = 0xf005;
+ public static final int CODE_OEM_CAUSE_6 = 0xf006;
+ public static final int CODE_OEM_CAUSE_7 = 0xf007;
+ public static final int CODE_OEM_CAUSE_8 = 0xf008;
+ public static final int CODE_OEM_CAUSE_9 = 0xf009;
+ public static final int CODE_OEM_CAUSE_10 = 0xf00a;
+ public static final int CODE_OEM_CAUSE_11 = 0xf00b;
+ public static final int CODE_OEM_CAUSE_12 = 0xf00c;
+ public static final int CODE_OEM_CAUSE_13 = 0xf00d;
+ public static final int CODE_OEM_CAUSE_14 = 0xf00e;
+ public static final int CODE_OEM_CAUSE_15 = 0xf00f;
+
+ /**
+ * @hide
+ */
+ @IntDef(value = {
+ CODE_UNSPECIFIED,
+ CODE_LOCAL_ILLEGAL_ARGUMENT,
+ CODE_LOCAL_ILLEGAL_STATE,
+ CODE_LOCAL_INTERNAL_ERROR,
+ CODE_LOCAL_IMS_SERVICE_DOWN,
+ CODE_LOCAL_NO_PENDING_CALL,
+ CODE_LOCAL_ENDED_BY_CONFERENCE_MERGE,
+ CODE_LOCAL_POWER_OFF,
+ CODE_LOCAL_LOW_BATTERY,
+ CODE_LOCAL_NETWORK_NO_SERVICE,
+ CODE_LOCAL_NETWORK_NO_LTE_COVERAGE,
+ CODE_LOCAL_NETWORK_ROAMING,
+ CODE_LOCAL_NETWORK_IP_CHANGED,
+ CODE_LOCAL_SERVICE_UNAVAILABLE,
+ CODE_LOCAL_NOT_REGISTERED,
+ CODE_LOCAL_CALL_EXCEEDED,
+ CODE_LOCAL_CALL_BUSY,
+ CODE_LOCAL_CALL_DECLINE,
+ CODE_LOCAL_CALL_VCC_ON_PROGRESSING,
+ CODE_LOCAL_CALL_RESOURCE_RESERVATION_FAILED,
+ CODE_LOCAL_CALL_CS_RETRY_REQUIRED,
+ CODE_LOCAL_CALL_VOLTE_RETRY_REQUIRED,
+ CODE_LOCAL_CALL_TERMINATED,
+ CODE_LOCAL_HO_NOT_FEASIBLE,
+ CODE_TIMEOUT_1XX_WAITING,
+ CODE_TIMEOUT_NO_ANSWER,
+ CODE_TIMEOUT_NO_ANSWER_CALL_UPDATE,
+ CODE_CALL_BARRED,
+ CODE_FDN_BLOCKED,
+ CODE_IMEI_NOT_ACCEPTED,
+ CODE_DIAL_MODIFIED_TO_USSD,
+ CODE_DIAL_MODIFIED_TO_SS,
+ CODE_DIAL_MODIFIED_TO_DIAL,
+ CODE_DIAL_MODIFIED_TO_DIAL_VIDEO,
+ CODE_DIAL_VIDEO_MODIFIED_TO_DIAL,
+ CODE_DIAL_VIDEO_MODIFIED_TO_DIAL_VIDEO,
+ CODE_DIAL_VIDEO_MODIFIED_TO_SS,
+ CODE_DIAL_VIDEO_MODIFIED_TO_USSD,
+ CODE_SIP_REDIRECTED,
+ CODE_SIP_BAD_REQUEST,
+ CODE_SIP_FORBIDDEN,
+ CODE_SIP_NOT_FOUND,
+ CODE_SIP_NOT_SUPPORTED,
+ CODE_SIP_REQUEST_TIMEOUT,
+ CODE_SIP_TEMPRARILY_UNAVAILABLE,
+ CODE_SIP_BAD_ADDRESS,
+ CODE_SIP_BUSY,
+ CODE_SIP_REQUEST_CANCELLED,
+ CODE_SIP_NOT_ACCEPTABLE,
+ CODE_SIP_NOT_REACHABLE,
+ CODE_SIP_CLIENT_ERROR,
+ CODE_SIP_TRANSACTION_DOES_NOT_EXIST,
+ CODE_SIP_SERVER_INTERNAL_ERROR,
+ CODE_SIP_SERVICE_UNAVAILABLE,
+ CODE_SIP_SERVER_TIMEOUT,
+ CODE_SIP_SERVER_ERROR,
+ CODE_SIP_USER_REJECTED,
+ CODE_SIP_GLOBAL_ERROR,
+ CODE_EMERGENCY_TEMP_FAILURE,
+ CODE_EMERGENCY_PERM_FAILURE,
+ CODE_SIP_USER_MARKED_UNWANTED,
+ CODE_SIP_METHOD_NOT_ALLOWED,
+ CODE_SIP_PROXY_AUTHENTICATION_REQUIRED,
+ CODE_SIP_REQUEST_ENTITY_TOO_LARGE,
+ CODE_SIP_REQUEST_URI_TOO_LARGE,
+ CODE_SIP_EXTENSION_REQUIRED,
+ CODE_SIP_INTERVAL_TOO_BRIEF,
+ CODE_SIP_CALL_OR_TRANS_DOES_NOT_EXIST,
+ CODE_SIP_LOOP_DETECTED,
+ CODE_SIP_TOO_MANY_HOPS,
+ CODE_SIP_AMBIGUOUS,
+ CODE_SIP_REQUEST_PENDING,
+ CODE_SIP_UNDECIPHERABLE,
+ CODE_MEDIA_INIT_FAILED,
+ CODE_MEDIA_NO_DATA,
+ CODE_MEDIA_NOT_ACCEPTABLE,
+ CODE_MEDIA_UNSPECIFIED,
+ CODE_USER_TERMINATED,
+ CODE_USER_NOANSWER,
+ CODE_USER_IGNORE,
+ CODE_USER_DECLINE,
+ CODE_LOW_BATTERY,
+ CODE_BLACKLISTED_CALL_ID,
+ CODE_USER_TERMINATED_BY_REMOTE,
+ CODE_USER_REJECTED_SESSION_MODIFICATION,
+ CODE_USER_CANCELLED_SESSION_MODIFICATION,
+ CODE_SESSION_MODIFICATION_FAILED,
+ CODE_UT_NOT_SUPPORTED,
+ CODE_UT_SERVICE_UNAVAILABLE,
+ CODE_UT_OPERATION_NOT_ALLOWED,
+ CODE_UT_NETWORK_ERROR,
+ CODE_UT_CB_PASSWORD_MISMATCH,
+ CODE_UT_SS_MODIFIED_TO_DIAL,
+ CODE_UT_SS_MODIFIED_TO_USSD,
+ CODE_UT_SS_MODIFIED_TO_SS,
+ CODE_UT_SS_MODIFIED_TO_DIAL_VIDEO,
+ CODE_ECBM_NOT_SUPPORTED,
+ CODE_MULTIENDPOINT_NOT_SUPPORTED,
+ CODE_REGISTRATION_ERROR,
+ CODE_ANSWERED_ELSEWHERE,
+ CODE_CALL_PULL_OUT_OF_SYNC,
+ CODE_CALL_END_CAUSE_CALL_PULL,
+ CODE_CALL_DROP_IWLAN_TO_LTE_UNAVAILABLE,
+ CODE_REJECTED_ELSEWHERE,
+ CODE_SUPP_SVC_FAILED,
+ CODE_SUPP_SVC_CANCELLED,
+ CODE_SUPP_SVC_REINVITE_COLLISION,
+ CODE_IWLAN_DPD_FAILURE,
+ CODE_EPDG_TUNNEL_ESTABLISH_FAILURE,
+ CODE_EPDG_TUNNEL_REKEY_FAILURE,
+ CODE_EPDG_TUNNEL_LOST_CONNECTION,
+ CODE_MAXIMUM_NUMBER_OF_CALLS_REACHED,
+ CODE_REMOTE_CALL_DECLINE,
+ CODE_DATA_LIMIT_REACHED,
+ CODE_DATA_DISABLED,
+ CODE_WIFI_LOST,
+ CODE_IKEV2_AUTH_FAILURE,
+ CODE_RADIO_OFF,
+ CODE_NO_VALID_SIM,
+ CODE_RADIO_INTERNAL_ERROR,
+ CODE_NETWORK_RESP_TIMEOUT,
+ CODE_NETWORK_REJECT,
+ CODE_RADIO_ACCESS_FAILURE,
+ CODE_RADIO_LINK_FAILURE,
+ CODE_RADIO_LINK_LOST,
+ CODE_RADIO_UPLINK_FAILURE,
+ CODE_RADIO_SETUP_FAILURE,
+ CODE_RADIO_RELEASE_NORMAL,
+ CODE_RADIO_RELEASE_ABNORMAL,
+ CODE_ACCESS_CLASS_BLOCKED,
+ CODE_NETWORK_DETACH,
+ CODE_SIP_ALTERNATE_EMERGENCY_CALL,
+ CODE_UNOBTAINABLE_NUMBER,
+ CODE_NO_CSFB_IN_CS_ROAM,
+ CODE_REJECT_UNKNOWN,
+ CODE_REJECT_ONGOING_CALL_WAITING_DISABLED,
+ CODE_REJECT_CALL_ON_OTHER_SUB,
+ CODE_REJECT_1X_COLLISION,
+ CODE_REJECT_SERVICE_NOT_REGISTERED,
+ CODE_REJECT_CALL_TYPE_NOT_ALLOWED,
+ CODE_REJECT_ONGOING_E911_CALL,
+ CODE_REJECT_ONGOING_CALL_SETUP,
+ CODE_REJECT_MAX_CALL_LIMIT_REACHED,
+ CODE_REJECT_UNSUPPORTED_SIP_HEADERS,
+ CODE_REJECT_UNSUPPORTED_SDP_HEADERS,
+ CODE_REJECT_ONGOING_CALL_TRANSFER,
+ CODE_REJECT_INTERNAL_ERROR,
+ CODE_REJECT_QOS_FAILURE,
+ CODE_REJECT_ONGOING_HANDOVER,
+ CODE_REJECT_VT_TTY_NOT_ALLOWED,
+ CODE_REJECT_ONGOING_CALL_UPGRADE,
+ CODE_REJECT_CONFERENCE_TTY_NOT_ALLOWED,
+ CODE_REJECT_ONGOING_CONFERENCE_CALL,
+ CODE_REJECT_VT_AVPF_NOT_ALLOWED,
+ CODE_REJECT_ONGOING_ENCRYPTED_CALL,
+ CODE_REJECT_ONGOING_CS_CALL,
+ CODE_RETRY_ON_IMS_WITHOUT_RTT,
+ CODE_OEM_CAUSE_1,
+ CODE_OEM_CAUSE_2,
+ CODE_OEM_CAUSE_3,
+ CODE_OEM_CAUSE_4,
+ CODE_OEM_CAUSE_5,
+ CODE_OEM_CAUSE_6,
+ CODE_OEM_CAUSE_7,
+ CODE_OEM_CAUSE_8,
+ CODE_OEM_CAUSE_9,
+ CODE_OEM_CAUSE_10,
+ CODE_OEM_CAUSE_11,
+ CODE_OEM_CAUSE_12,
+ CODE_OEM_CAUSE_13,
+ CODE_OEM_CAUSE_14,
+ CODE_OEM_CAUSE_15
+ }, prefix = "CODE_")
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ImsCode {}
+
+ /**
+ * Network string error messages.
+ * mExtraMessage may have these values.
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_MSG_SERVICE_NOT_AUTHORIZED =
+ "Forbidden. Not Authorized for Service";
+
+
+ /*
+ * Extra codes for the specific code value
+ * This value can be referred when the code is CODE_LOCAL_CALL_CS_RETRY_REQUIRED.
+ */
+ /**
+ * 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
+ */
+ public static final int EXTRA_CODE_CALL_RETRY_NORMAL = 1;
+ /**
+ * 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 and do not notify the user.
+ */
+ public static final int EXTRA_CODE_CALL_RETRY_SILENT_REDIAL = 2;
+ /**
+ * 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 by using the settings.
+ */
+ public static final int EXTRA_CODE_CALL_RETRY_BY_SETTINGS = 3;
+
+
+ // For main reason code
+ /** @hide */
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "{@code "
+ + "#getCode()}")
+ public int mCode;
+ // For the extra code value; it depends on the code value.
+ /** @hide */
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "{@code "
+ + "#getExtraCode()}")
+ public int mExtraCode;
+ // For the additional message of the reason info.
+ /** @hide */
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "{@code "
+ + "#getExtraMessage()}")
+ public String mExtraMessage;
+
+ /** @hide */
+ public ImsReasonInfo() {
+ mCode = CODE_UNSPECIFIED;
+ mExtraCode = CODE_UNSPECIFIED;
+ mExtraMessage = null;
+ }
+
+ private ImsReasonInfo(Parcel in) {
+ mCode = in.readInt();
+ mExtraCode = in.readInt();
+ mExtraMessage = in.readString();
+ }
+
+ /** @hide */
+ @UnsupportedAppUsage
+ public ImsReasonInfo(int code, int extraCode) {
+ mCode = code;
+ mExtraCode = extraCode;
+ mExtraMessage = null;
+ }
+
+ public ImsReasonInfo(@ImsCode int code, int extraCode, @Nullable String extraMessage) {
+ mCode = code;
+ mExtraCode = extraCode;
+ mExtraMessage = extraMessage;
+ }
+
+ /**
+ * @return an integer representing more information about the completion of an operation.
+ */
+ public @ImsCode int getCode() {
+ return mCode;
+ }
+
+ /**
+ * @return an optional OEM specified code that provides extra information.
+ */
+ public int getExtraCode() {
+ return mExtraCode;
+ }
+
+ /**
+ * @return an optional OEM specified string that provides extra information about the operation
+ * result.
+ */
+ public @Nullable String getExtraMessage() {
+ return mExtraMessage;
+ }
+
+ /**
+ * @return the string format of {@link ImsReasonInfo}
+ */
+ @NonNull
+ @Override
+ public String toString() {
+ return "ImsReasonInfo :: {" + mCode + ", " + mExtraCode + ", " + mExtraMessage + "}";
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel out, int flags) {
+ out.writeInt(mCode);
+ out.writeInt(mExtraCode);
+ out.writeString(mExtraMessage);
+ }
+
+ public static final @NonNull Creator<ImsReasonInfo> CREATOR = new Creator<ImsReasonInfo>() {
+ @Override
+ public ImsReasonInfo createFromParcel(Parcel in) {
+ return new ImsReasonInfo(in);
+ }
+
+ @Override
+ public ImsReasonInfo[] newArray(int size) {
+ return new ImsReasonInfo[size];
+ }
+ };
+}
diff --git a/android/telephony/ims/ImsService.java b/android/telephony/ims/ImsService.java
new file mode 100644
index 0000000..2b3072e
--- /dev/null
+++ b/android/telephony/ims/ImsService.java
@@ -0,0 +1,372 @@
+/*
+ * Copyright (C) 2017 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.SystemApi;
+import android.annotation.TestApi;
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.telephony.CarrierConfigManager;
+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.aidl.IImsServiceController;
+import android.telephony.ims.aidl.IImsServiceControllerListener;
+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.util.Log;
+import android.util.SparseArray;
+
+import com.android.ims.internal.IImsFeatureStatusCallback;
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * 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.
+ * First, the application must declare that they use the "android.permission.BIND_IMS_SERVICE"
+ * permission. Then, the ImsService definition in the manifest must follow the following format:
+ *
+ * ...
+ * <service android:name=".EgImsService"
+ * android:permission="android.permission.BIND_IMS_SERVICE" >
+ * ...
+ * <intent-filter>
+ * <action android:name="android.telephony.ims.ImsService" />
+ * </intent-filter>
+ * </service>
+ * ...
+ *
+ * The telephony framework will then bind to the ImsService you have defined in your manifest
+ * if you are either:
+ * 1) Defined as the default ImsService for the device in the device overlay using
+ * "config_ims_mmtel_package" or "config_ims_rcs_package".
+ * 2) Defined as a Carrier Provided ImsService in the Carrier Configuration using
+ * {@link CarrierConfigManager#KEY_CONFIG_IMS_MMTEL_PACKAGE_OVERRIDE_STRING} or
+ * {@link CarrierConfigManager#KEY_CONFIG_IMS_RCS_PACKAGE_OVERRIDE_STRING}.
+ *
+ * There are two ways to define to the platform which {@link ImsFeature}s this {@link ImsService}
+ * supports, dynamic or static definitions.
+ *
+ * In the static definition, the {@link ImsFeature}s that are supported are defined in the service
+ * definition of the AndroidManifest.xml file as metadata:
+ * <!-- Apps must declare which features they support as metadata. The different categories are
+ * defined below. In this example, the MMTEL_FEATURE feature is supported. -->
+ * <meta-data android:name="android.telephony.ims.MMTEL_FEATURE" android:value="true" />
+ *
+ * The features that are currently supported in an ImsService are:
+ * - RCS_FEATURE: This ImsService implements the RcsFeature class.
+ * - MMTEL_FEATURE: This ImsService implements the MmTelFeature class.
+ * - EMERGENCY_MMTEL_FEATURE: This ImsService supports Emergency Calling for MMTEL, must be
+ * declared along with the MMTEL_FEATURE. If this is not specified, the framework will use
+ * circuit switch for emergency calling.
+ *
+ * In the dynamic definition, the supported features are not specified in the service definition
+ * of the AndroidManifest. Instead, the framework binds to this service and calls
+ * {@link #querySupportedImsFeatures()}. The {@link ImsService} then returns an
+ * {@link ImsFeatureConfiguration}, which the framework uses to initialize the supported
+ * {@link ImsFeature}s. If at any time, the list of supported {@link ImsFeature}s changes,
+ * {@link #onUpdateSupportedImsFeatures(ImsFeatureConfiguration)} can be called to tell the
+ * framework of the changes.
+ *
+ * @hide
+ */
+@SystemApi
+@TestApi
+public class ImsService extends Service {
+
+ private static final String LOG_TAG = "ImsService";
+
+ /**
+ * The intent that must be defined as an intent-filter in the AndroidManifest of the ImsService.
+ * @hide
+ */
+ public static final String SERVICE_INTERFACE = "android.telephony.ims.ImsService";
+
+ // A map of slot Id -> map of features (indexed by ImsFeature feature id) corresponding to that
+ // slot.
+ // We keep track of this to facilitate cleanup of the IImsFeatureStatusCallback and
+ // call ImsFeature#onFeatureRemoved.
+ private final SparseArray<SparseArray<ImsFeature>> mFeaturesBySlot = new SparseArray<>();
+
+ private IImsServiceControllerListener mListener;
+
+
+ /**
+ * Listener that notifies the framework of ImsService changes.
+ * @hide
+ */
+ public static class Listener extends IImsServiceControllerListener.Stub {
+ /**
+ * The IMS features that this ImsService supports has changed.
+ * @param c a new {@link ImsFeatureConfiguration} containing {@link ImsFeature.FeatureType}s
+ * that this ImsService supports. This may trigger the addition/removal of feature
+ * in this service.
+ */
+ public void onUpdateSupportedImsFeatures(ImsFeatureConfiguration c) {
+ }
+ }
+
+ /**
+ * @hide
+ */
+ protected final IBinder mImsServiceController = new IImsServiceController.Stub() {
+ @Override
+ public void setListener(IImsServiceControllerListener l) {
+ mListener = l;
+ }
+
+ @Override
+ public IImsMmTelFeature createMmTelFeature(int slotId, IImsFeatureStatusCallback c) {
+ return createMmTelFeatureInternal(slotId, c);
+ }
+
+ @Override
+ public IImsRcsFeature createRcsFeature(int slotId, IImsFeatureStatusCallback c) {
+ return createRcsFeatureInternal(slotId, c);
+ }
+
+ @Override
+ public void removeImsFeature(int slotId, int featureType, IImsFeatureStatusCallback c) {
+ ImsService.this.removeImsFeature(slotId, featureType, c);
+ }
+
+ @Override
+ public ImsFeatureConfiguration querySupportedImsFeatures() {
+ return ImsService.this.querySupportedImsFeatures();
+ }
+
+ @Override
+ public void notifyImsServiceReadyForFeatureCreation() {
+ ImsService.this.readyForFeatureCreation();
+ }
+
+ @Override
+ public IImsConfig getConfig(int slotId) {
+ ImsConfigImplBase c = ImsService.this.getConfig(slotId);
+ return c != null ? c.getIImsConfig() : null;
+ }
+
+ @Override
+ public IImsRegistration getRegistration(int slotId) {
+ ImsRegistrationImplBase r = ImsService.this.getRegistration(slotId);
+ return r != null ? r.getBinder() : null;
+ }
+
+ @Override
+ public void enableIms(int slotId) {
+ ImsService.this.enableIms(slotId);
+ }
+
+ @Override
+ public void disableIms(int slotId) {
+ ImsService.this.disableIms(slotId);
+ }
+ };
+
+ /**
+ * @hide
+ */
+ @Override
+ public IBinder onBind(Intent intent) {
+ if(SERVICE_INTERFACE.equals(intent.getAction())) {
+ Log.i(LOG_TAG, "ImsService Bound.");
+ return mImsServiceController;
+ }
+ return null;
+ }
+
+ /**
+ * @hide
+ */
+ @VisibleForTesting
+ public SparseArray<ImsFeature> getFeatures(int slotId) {
+ return mFeaturesBySlot.get(slotId);
+ }
+
+ private IImsMmTelFeature createMmTelFeatureInternal(int slotId,
+ IImsFeatureStatusCallback c) {
+ MmTelFeature f = createMmTelFeature(slotId);
+ if (f != null) {
+ setupFeature(f, slotId, ImsFeature.FEATURE_MMTEL, c);
+ return f.getBinder();
+ } else {
+ Log.e(LOG_TAG, "createMmTelFeatureInternal: null feature returned.");
+ return null;
+ }
+ }
+
+ private IImsRcsFeature createRcsFeatureInternal(int slotId,
+ IImsFeatureStatusCallback c) {
+ RcsFeature f = createRcsFeature(slotId);
+ if (f != null) {
+ setupFeature(f, slotId, ImsFeature.FEATURE_RCS, c);
+ return f.getBinder();
+ } else {
+ Log.e(LOG_TAG, "createRcsFeatureInternal: null feature returned.");
+ return null;
+ }
+ }
+
+ private void setupFeature(ImsFeature f, int slotId, int featureType,
+ IImsFeatureStatusCallback c) {
+ f.initialize(this, slotId);
+ f.addImsFeatureStatusCallback(c);
+ addImsFeature(slotId, featureType, f);
+ }
+
+ private void addImsFeature(int slotId, int featureType, ImsFeature f) {
+ synchronized (mFeaturesBySlot) {
+ // Get SparseArray for Features, by querying slot Id
+ SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId);
+ if (features == null) {
+ // Populate new SparseArray of features if it doesn't exist for this slot yet.
+ features = new SparseArray<>();
+ mFeaturesBySlot.put(slotId, features);
+ }
+ features.put(featureType, f);
+ }
+ }
+
+ private void removeImsFeature(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 ImsFeature. No ImsFeatures exist on slot "
+ + slotId);
+ return;
+ }
+ ImsFeature f = features.get(featureType);
+ if (f == null) {
+ Log.w(LOG_TAG, "Can not remove ImsFeature. No feature with type "
+ + featureType + " exists on slot " + slotId);
+ return;
+ }
+ f.removeImsFeatureStatusCallback(c);
+ f.onFeatureRemoved();
+ features.remove(featureType);
+ }
+ }
+
+ /**
+ * When called, provide the {@link ImsFeatureConfiguration} that this {@link ImsService}
+ * currently supports. This will trigger the framework to set up the {@link ImsFeature}s that
+ * correspond to the {@link ImsFeature}s configured here.
+ *
+ * Use {@link #onUpdateSupportedImsFeatures(ImsFeatureConfiguration)} to change the supported
+ * {@link ImsFeature}s.
+ *
+ * @return an {@link ImsFeatureConfiguration} containing Features this ImsService supports.
+ */
+ public ImsFeatureConfiguration querySupportedImsFeatures() {
+ // Return empty for base implementation
+ return new ImsFeatureConfiguration();
+ }
+
+ /**
+ * Updates the framework with a new {@link ImsFeatureConfiguration} containing the updated
+ * features, that this {@link ImsService} supports. This may trigger the framework to add/remove
+ * new ImsFeatures, depending on the configuration.
+ */
+ public final void onUpdateSupportedImsFeatures(ImsFeatureConfiguration c)
+ throws RemoteException {
+ if (mListener == null) {
+ throw new IllegalStateException("Framework is not ready");
+ }
+ mListener.onUpdateSupportedImsFeatures(c);
+ }
+
+ /**
+ * 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()}.
+ *
+ * The ImsService should use this signal instead of onCreate/onBind or similar to perform
+ * feature initialization because the framework may bind to this service multiple times to
+ * query the ImsService's {@link ImsFeatureConfiguration} via
+ * {@link #querySupportedImsFeatures()}before creating features.
+ */
+ public void readyForFeatureCreation() {
+ }
+
+ /**
+ * The framework has enabled IMS for the slot specified, the ImsService should register for IMS
+ * and perform all appropriate initialization to bring up all ImsFeatures.
+ */
+ public void enableIms(int slotId) {
+ }
+
+ /**
+ * The framework has disabled IMS for the slot specified. The ImsService must deregister for IMS
+ * and set capability status to false for all ImsFeatures.
+ */
+ public void disableIms(int slotId) {
+ }
+
+ /**
+ * When called, the framework is requesting that a new {@link MmTelFeature} is created for the
+ * specified slot.
+ *
+ * @param slotId The slot ID that the MMTEL Feature is being created for.
+ * @return The newly created {@link MmTelFeature} associated with the slot or null if the
+ * feature is not supported.
+ */
+ public MmTelFeature createMmTelFeature(int slotId) {
+ return null;
+ }
+
+ /**
+ * When called, the framework is requesting that a new {@link RcsFeature} is created for the
+ * specified slot.
+ *
+ * @param slotId The slot ID that the RCS Feature is being created for.
+ * @return The newly created {@link RcsFeature} associated with the slot or null if the feature
+ * is not supported.
+ */
+ public RcsFeature createRcsFeature(int slotId) {
+ return null;
+ }
+
+ /**
+ * Return the {@link ImsConfigImplBase} implementation associated with the provided slot. This
+ * will be used by the platform to get/set specific IMS related configurations.
+ *
+ * @param slotId The slot that the IMS configuration is associated with.
+ * @return ImsConfig implementation that is associated with the specified slot.
+ */
+ public ImsConfigImplBase getConfig(int slotId) {
+ return new ImsConfigImplBase();
+ }
+
+ /**
+ * Return the {@link ImsRegistrationImplBase} implementation associated with the provided slot.
+ *
+ * @param slotId The slot that is associated with the IMS Registration.
+ * @return the ImsRegistration implementation associated with the slot.
+ */
+ public ImsRegistrationImplBase getRegistration(int slotId) {
+ return new ImsRegistrationImplBase();
+ }
+}
\ No newline at end of file
diff --git a/android/telephony/ims/ImsSsData.java b/android/telephony/ims/ImsSsData.java
new file mode 100644
index 0000000..70bf0c5
--- /dev/null
+++ b/android/telephony/ims/ImsSsData.java
@@ -0,0 +1,585 @@
+/*
+ * 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.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.telephony.Rlog;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Provides STK Call Control Supplementary Service information.
+ *
+ * {@hide}
+ */
+@SystemApi
+@TestApi
+public final class ImsSsData implements Parcelable {
+
+ private static final String TAG = ImsSsData.class.getCanonicalName();
+
+ // Supplementary Service Type
+ // Call Forwarding
+ public static final int SS_CFU = 0;
+ public static final int SS_CF_BUSY = 1;
+ public static final int SS_CF_NO_REPLY = 2;
+ public static final int SS_CF_NOT_REACHABLE = 3;
+ public static final int SS_CF_ALL = 4;
+ public static final int SS_CF_ALL_CONDITIONAL = 5;
+ public static final int SS_CFUT = 6;
+ // Called Line Presentation
+ public static final int SS_CLIP = 7;
+ public static final int SS_CLIR = 8;
+ public static final int SS_COLP = 9;
+ public static final int SS_COLR = 10;
+ // Calling Name Presentation
+ public static final int SS_CNAP = 11;
+ // Call Waiting
+ public static final int SS_WAIT = 12;
+ // Call Barring
+ public static final int SS_BAOC = 13;
+ public static final int SS_BAOIC = 14;
+ public static final int SS_BAOIC_EXC_HOME = 15;
+ public static final int SS_BAIC = 16;
+ public static final int SS_BAIC_ROAMING = 17;
+ public static final int SS_ALL_BARRING = 18;
+ public static final int SS_OUTGOING_BARRING = 19;
+ public static final int SS_INCOMING_BARRING = 20;
+ public static final int SS_INCOMING_BARRING_DN = 21;
+ public static final int SS_INCOMING_BARRING_ANONYMOUS = 22;
+
+
+ /**@hide*/
+ @IntDef(flag = true, prefix = {"SS_"}, value = {
+ SS_ACTIVATION,
+ SS_DEACTIVATION,
+ SS_INTERROGATION,
+ SS_REGISTRATION,
+ SS_ERASURE})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface RequestType{}
+
+ //Supplementary Service Request Types
+ public static final int SS_ACTIVATION = 0;
+ public static final int SS_DEACTIVATION = 1;
+ public static final int SS_INTERROGATION = 2;
+ public static final int SS_REGISTRATION = 3;
+ public static final int SS_ERASURE = 4;
+
+ /**@hide*/
+ @IntDef(flag = true, prefix = {"SS_"}, value = {
+ SS_ALL_TELE_AND_BEARER_SERVICES,
+ SS_ALL_TELESEVICES,
+ SS_TELEPHONY,
+ SS_ALL_DATA_TELESERVICES,
+ SS_SMS_SERVICES,
+ SS_ALL_TELESERVICES_EXCEPT_SMS})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface TeleserviceType{}
+
+ // Supplementary Service Teleservice Type
+ public static final int SS_ALL_TELE_AND_BEARER_SERVICES = 0;
+ public static final int SS_ALL_TELESEVICES = 1;
+ public static final int SS_TELEPHONY = 2;
+ public static final int SS_ALL_DATA_TELESERVICES = 3;
+ public static final int SS_SMS_SERVICES = 4;
+ public static final int SS_ALL_TELESERVICES_EXCEPT_SMS = 5;
+
+ /**
+ * No call forwarding service class defined.
+ *
+ * See TS 27.007 7.11 (+CCFC) and 7.4 (CLCK)
+ */
+ public static final int SERVICE_CLASS_NONE = 0;
+
+ /**
+ * Service class flag for voice telephony.
+ *
+ * See TS 27.007 7.11 (+CCFC) and 7.4 (CLCK)
+ */
+ public static final int SERVICE_CLASS_VOICE = 1;
+
+ /**
+ * Service class flag for all data bearers (including
+ * {@link #SERVICE_CLASS_DATA_CIRCUIT_SYNC,
+ * {@link #SERVICE_CLASS_DATA_CIRCUIT_ASYNC}, {@link #SERVICE_CLASS_PACKET_ACCESS},
+ * {@link #SERVICE_CLASS_PAD}}) if supported by the carrier.
+ *
+ * See TS 27.007 7.11 (+CCFC) and 7.4 (CLCK)
+ */
+ public static final int SERVICE_CLASS_DATA = (1 << 1);
+ /**
+ * Service class flag for fax services.
+ *
+ * See TS 27.007 7.11 (+CCFC) and 7.4 (CLCK)
+ */
+ public static final int SERVICE_CLASS_FAX = (1 << 2);
+ /**
+ * Service class flag for SMS services.
+ *
+ * See TS 27.007 7.11 (+CCFC) and 7.4 (CLCK)
+ */
+ public static final int SERVICE_CLASS_SMS = (1 << 3);
+ /**
+ * Service class flag for the synchronous bearer service.
+ *
+ * See TS 27.007 7.11 (+CCFC) and 7.4 (CLCK)
+ */
+ public static final int SERVICE_CLASS_DATA_CIRCUIT_SYNC = (1 << 4);
+
+ /**
+ * Service class flag for the asynchronous bearer service.
+ *
+ * See TS 27.007 7.11 (+CCFC) and 7.4 (CLCK)
+ */
+ public static final int SERVICE_CLASS_DATA_CIRCUIT_ASYNC = (1 << 5);
+
+ /**
+ * Service class flag for the packet access bearer service.
+ *
+ * See TS 27.007 7.11 (+CCFC) and 7.4 (CLCK)
+ */
+ public static final int SERVICE_CLASS_DATA_PACKET_ACCESS = (1 << 6);
+
+ /**
+ * Service class flag for the Packet Assembly/Disassembly bearer service.
+ *
+ * See TS 27.007 7.11 (+CCFC) and 7.4 (CLCK)
+ */
+ public static final int SERVICE_CLASS_DATA_PAD = (1 << 7);
+
+ /**@hide*/
+ @IntDef(flag = true, prefix = {"SERVICE_CLASS_"}, value = {
+ SERVICE_CLASS_NONE,
+ SERVICE_CLASS_VOICE,
+ SERVICE_CLASS_DATA,
+ SERVICE_CLASS_FAX,
+ SERVICE_CLASS_SMS,
+ SERVICE_CLASS_DATA_CIRCUIT_SYNC,
+ SERVICE_CLASS_DATA_CIRCUIT_ASYNC,
+ SERVICE_CLASS_DATA_PACKET_ACCESS,
+ SERVICE_CLASS_DATA_PAD})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ServiceClassFlags{}
+
+ /**
+ * Result code used if the operation was successful. See {@link #getResult()}.
+ */
+ public static final int RESULT_SUCCESS = 0;
+
+ /** @hide */
+ @IntDef(flag = true, prefix = { "SS_" }, value = {
+ SS_CFU,
+ SS_CF_BUSY,
+ SS_CF_NO_REPLY,
+ SS_CF_NOT_REACHABLE,
+ SS_CF_ALL,
+ SS_CF_ALL_CONDITIONAL,
+ SS_CFUT,
+ SS_CLIP,
+ SS_CLIR,
+ SS_COLP,
+ SS_COLR,
+ SS_CNAP,
+ SS_WAIT,
+ SS_BAOC,
+ SS_BAOIC,
+ SS_BAOIC_EXC_HOME,
+ SS_BAIC,
+ SS_BAIC_ROAMING,
+ SS_ALL_BARRING,
+ SS_OUTGOING_BARRING,
+ SS_INCOMING_BARRING,
+ SS_INCOMING_BARRING_DN,
+ SS_INCOMING_BARRING_ANONYMOUS
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ServiceType{}
+
+ /**
+ * The Service type of this Supplementary service.
+ * @hide
+ */
+ public final @ServiceType int serviceType;
+
+ /**
+ * Supplementary Service request Type:
+ * {@link #SS_ACTIVATION),
+ * {@link #SS_DEACTIVATION},
+ * {@link #SS_INTERROGATION},
+ * {@link #SS_REGISTRATION},
+ * {@link #SS_ERASURE}
+ * @hide
+ */
+ public final @RequestType int requestType;
+
+ /**
+ * Supplementary Service teleservice type:
+ * {@link #SS_ALL_TELE_AND_BEARER_SERVICES},
+ * {@link #SS_ALL_TELESEVICES},
+ * {@link #SS_TELEPHONY},
+ * {@link #SS_ALL_DATA_TELESERVICES},
+ * {@link #SS_SMS_SERVICES},
+ * {@link #SS_ALL_TELESERVICES_EXCEPT_SMS}
+ *
+ * @hide
+ */
+ public final @TeleserviceType int teleserviceType;
+
+ /**
+ * Supplementary Service service class.
+ *
+ * @hide
+ */
+ public final @ServiceClassFlags int serviceClass;
+
+ /**
+ * Result of Supplementary Service operation. Valid values are:
+ * {@link #RESULT_SUCCESS} if the result is success, or
+ * ImsReasonInfo code if the result is a failure.
+ *
+ * @hide
+ */
+ public final int result;
+
+ private int[] mSsInfo;
+ private List<ImsCallForwardInfo> mCfInfo;
+ private List<ImsSsInfo> mImsSsInfo;
+
+ /**
+ * Builder for optional ImsSsData parameters.
+ */
+ public static final class Builder {
+ private ImsSsData mImsSsData;
+
+ /**
+ * Generate IMS Supplementary Service information.
+ * @param serviceType The Supplementary Service type.
+ * @param requestType Supplementary Service request Type:
+ * {@link #SS_ACTIVATION},
+ * {@link #SS_DEACTIVATION},
+ * {@link #SS_INTERROGATION},
+ * {@link #SS_REGISTRATION},
+ * {@link #SS_ERASURE}
+ * @param teleserviceType Supplementary Service teleservice type:
+ * {@link #SS_ALL_TELE_AND_BEARER_SERVICES},
+ * {@link #SS_ALL_TELESEVICES},
+ * {@link #SS_TELEPHONY},
+ * {@link #SS_ALL_DATA_TELESERVICES},
+ * {@link #SS_SMS_SERVICES},
+ * {@link #SS_ALL_TELESERVICES_EXCEPT_SMS}
+ * @param serviceClass Supplementary Service service class. See See 27.007 +CCFC or +CLCK.
+ * @param result Result of Supplementary Service operation. Valid values are 0 if the result
+ * is success, or {@link ImsReasonInfo} code if the result is a failure.
+ * @return this Builder instance for further constructing.
+ * @see #build()
+ */
+ public Builder(@ServiceType int serviceType, int requestType, int teleserviceType,
+ @ServiceClassFlags int serviceClass, int result) {
+ mImsSsData = new ImsSsData(serviceType, requestType, teleserviceType, serviceClass,
+ result);
+ }
+
+ /**
+ * Set the array of {@link ImsSsInfo}s that are associated with this supplementary service
+ * data.
+ */
+ public @NonNull Builder setSuppServiceInfo(@NonNull List<ImsSsInfo> imsSsInfos) {
+ mImsSsData.mImsSsInfo = imsSsInfos;
+ return this;
+ }
+
+ /**
+ * Set the array of {@link ImsCallForwardInfo}s that are associated with this supplementary
+ * service data.
+ */
+ public @NonNull Builder setCallForwardingInfo(
+ @NonNull List<ImsCallForwardInfo> imsCallForwardInfos) {
+ mImsSsData.mCfInfo = imsCallForwardInfos;
+ return this;
+ }
+
+ /**
+ * @return an {@link ImsSsData} containing optional parameters.
+ */
+ public @NonNull ImsSsData build() {
+ return mImsSsData;
+ }
+ }
+
+ /**
+ * Generate IMS Supplementary Service information.
+ * @param serviceType The Supplementary Service type.
+ * @param requestType Supplementary Service request Type. Valid values are:
+ * {@link #SS_ACTIVATION},
+ * {@link #SS_DEACTIVATION},
+ * {@link #SS_INTERROGATION},
+ * {@link #SS_REGISTRATION},
+ * {@link #SS_ERASURE}
+ * @param teleserviceType Supplementary Service teleservice type:
+ * {@link #SS_ALL_TELE_AND_BEARER_SERVICES},
+ * {@link #SS_ALL_TELESEVICES},
+ * {@link #SS_TELEPHONY},
+ * {@link #SS_ALL_DATA_TELESERVICES},
+ * {@link #SS_SMS_SERVICES},
+ * {@link #SS_ALL_TELESERVICES_EXCEPT_SMS}
+ * @param serviceClass Supplementary Service service class. See See 27.007 +CCFC or +CLCK.
+ * @param result Result of Supplementary Service operation. Valid values are 0 if the result is
+ * success, or ImsReasonInfo code if the result is a failure.
+ */
+ public ImsSsData(@ServiceType int serviceType, int requestType, int teleserviceType,
+ @ServiceClassFlags int serviceClass, int result) {
+ this.serviceType = serviceType;
+ this.requestType = requestType;
+ this.teleserviceType = teleserviceType;
+ this.serviceClass = serviceClass;
+ this.result = result;
+ }
+
+ private ImsSsData(Parcel in) {
+ serviceType = in.readInt();
+ requestType = in.readInt();
+ teleserviceType = in.readInt();
+ serviceClass = in.readInt();
+ result = in.readInt();
+ mSsInfo = in.createIntArray();
+ mCfInfo = in.readParcelableList(new ArrayList<>(), this.getClass().getClassLoader());
+ mImsSsInfo = in.readParcelableList(new ArrayList<>(), this.getClass().getClassLoader());
+ }
+
+ public static final @android.annotation.NonNull Creator<ImsSsData> CREATOR = new Creator<ImsSsData>() {
+ @Override
+ public ImsSsData createFromParcel(Parcel in) {
+ return new ImsSsData(in);
+ }
+
+ @Override
+ public ImsSsData[] newArray(int size) {
+ return new ImsSsData[size];
+ }
+ };
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(getServiceType());
+ out.writeInt(getRequestType());
+ out.writeInt(getTeleserviceType());
+ out.writeInt(getServiceClass());
+ out.writeInt(getResult());
+ out.writeIntArray(mSsInfo);
+ out.writeParcelableList(mCfInfo, 0);
+ out.writeParcelableList(mImsSsInfo, 0);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * Old method, kept for compatibility. See {@link #isTypeCf()}
+ * @hide
+ */
+ public boolean isTypeCF() {
+ return (getServiceType() == SS_CFU || getServiceType() == SS_CF_BUSY
+ || getServiceType() == SS_CF_NO_REPLY || getServiceType() == SS_CF_NOT_REACHABLE
+ || getServiceType() == SS_CF_ALL || getServiceType() == SS_CF_ALL_CONDITIONAL);
+ }
+
+ public boolean isTypeCf() {
+ return isTypeCF();
+ }
+
+ public boolean isTypeUnConditional() {
+ return (getServiceType() == SS_CFU || getServiceType() == SS_CF_ALL);
+ }
+
+ /**
+ * Old method, kept for compatibility. See {@link #isTypeCf()}
+ * @hide
+ */
+ public boolean isTypeCW() {
+ return (getServiceType() == SS_WAIT);
+ }
+
+ public boolean isTypeCw() {
+ return isTypeCW();
+ }
+
+ public boolean isTypeClip() {
+ return (getServiceType() == SS_CLIP);
+ }
+
+ public boolean isTypeColr() {
+ return (getServiceType() == SS_COLR);
+ }
+
+ public boolean isTypeColp() {
+ return (getServiceType() == SS_COLP);
+ }
+
+ public boolean isTypeClir() {
+ return (getServiceType() == SS_CLIR);
+ }
+
+ public boolean isTypeIcb() {
+ return (getServiceType() == SS_INCOMING_BARRING_DN
+ || getServiceType() == SS_INCOMING_BARRING_ANONYMOUS);
+ }
+
+ public boolean isTypeBarring() {
+ return (getServiceType() == SS_BAOC || getServiceType() == SS_BAOIC
+ || getServiceType() == SS_BAOIC_EXC_HOME || getServiceType() == SS_BAIC
+ || getServiceType() == SS_BAIC_ROAMING || getServiceType() == SS_ALL_BARRING
+ || getServiceType() == SS_OUTGOING_BARRING
+ || getServiceType() == SS_INCOMING_BARRING);
+ }
+
+ public boolean isTypeInterrogation() {
+ return (getRequestType() == SS_INTERROGATION);
+ }
+
+ /**
+ * Supplementary Service request Type.
+ */
+ public @RequestType int getRequestType() {
+ return requestType;
+ }
+
+ /**
+ * The Service type of this Supplementary service.
+ */
+ public @ServiceType int getServiceType() {
+ return serviceType;
+ }
+
+ /**
+ * Supplementary Service teleservice type.
+ */
+ public @TeleserviceType int getTeleserviceType() {
+ return teleserviceType;
+ }
+
+ /**
+ * Supplementary Service service class.
+ */
+ public @ServiceClassFlags int getServiceClass() {
+ return serviceClass;
+ }
+
+ /**
+ * Result of Supplementary Service operation. Valid values are:
+ * {@link #RESULT_SUCCESS} if the result is success, or
+ * {@link ImsReasonInfo.UtReason} code if the result is a failure.
+ */
+ public @ImsReasonInfo.UtReason int getResult() {
+ return result;
+ }
+
+ /** @hide */
+ public void setSuppServiceInfo(int[] ssInfo) {
+ mSsInfo = ssInfo;
+ }
+
+ /** @hide */
+ public void setImsSpecificSuppServiceInfo(ImsSsInfo[] imsSsInfo) {
+ mImsSsInfo = Arrays.asList(imsSsInfo);
+ }
+
+ /** @hide */
+ public void setCallForwardingInfo(ImsCallForwardInfo[] cfInfo) {
+ mCfInfo = Arrays.asList(cfInfo);
+ }
+
+ /**
+ * This is a compatibility function to transform the public API to a form that can be processed
+ * by telephony.
+ *
+ * @hide
+ */
+ //TODO: Refactor Telephony to use well defined classes instead of an int[] to process SS.
+ public int[] getSuppServiceInfoCompat() {
+ if (mSsInfo != null) {
+ // Something has set the ssInfo using hidden APIs, so for compatibility just return that
+ // structure directly.
+ return mSsInfo;
+ }
+
+
+ int[] result = new int[2];
+ if (mImsSsInfo == null || mImsSsInfo.size() == 0) {
+ Rlog.e(TAG, "getSuppServiceInfoCompat: Could not parse mImsSsInfo, returning empty "
+ + "int[]");
+ return result;
+ }
+
+ // Convert ImsSsInfo into a form that telephony can read (as per 3GPP 27.007)
+ // CLIR (section 7.7)
+ if (isTypeClir()) {
+ // Assume there will only be one ImsSsInfo.
+ // contains {"n","m"} parameters
+ result[0] = mImsSsInfo.get(0).getClirOutgoingState();
+ result[1] = mImsSsInfo.get(0).getClirInterrogationStatus();
+ return result;
+ }
+ // COLR 7.31
+ if (isTypeColr()) {
+ result[0] = mImsSsInfo.get(0).getProvisionStatus();
+ }
+ // Facility Lock CLCK 7.4 (for call barring), CLIP 7.6, COLP 7.8, as well as any
+ // other result, just return the status for the "n" parameter and provisioning status for
+ // "m" as the default.
+ result[0] = mImsSsInfo.get(0).getStatus();
+ result[1] = mImsSsInfo.get(0).getProvisionStatus();
+ return result;
+ }
+
+ /**
+ * @return an array of {@link ImsSsInfo}s associated with this supplementary service data.
+ */
+ public @NonNull List<ImsSsInfo> getSuppServiceInfo() {
+ return mImsSsInfo;
+ }
+
+ /**
+ * @return an array of {@link ImsCallForwardInfo}s associated with this supplementary service
+ * data.
+ **/
+ public @Nullable List<ImsCallForwardInfo> getCallForwardInfo() {
+ return mCfInfo;
+ }
+
+ @NonNull
+ @Override
+ public String toString() {
+ return "[ImsSsData] " + "ServiceType: " + getServiceType()
+ + " RequestType: " + getRequestType()
+ + " TeleserviceType: " + getTeleserviceType()
+ + " ServiceClass: " + getServiceClass()
+ + " Result: " + getResult();
+ }
+}
diff --git a/android/telephony/ims/ImsSsInfo.java b/android/telephony/ims/ImsSsInfo.java
new file mode 100644
index 0000000..9cce95f
--- /dev/null
+++ b/android/telephony/ims/ImsSsInfo.java
@@ -0,0 +1,368 @@
+/*
+ * 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.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.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Provides the result to the update operation for the supplementary service configuration.
+ *
+ * Also supports IMS specific Incoming Communication Barring (ICB) as well as Anonymous
+ * Communication Rejection (ACR), as per 3GPP 24.611.
+ *
+ * @see Builder
+ * @hide
+ */
+@SystemApi
+@TestApi
+public final class ImsSsInfo implements Parcelable {
+
+ /**@hide*/
+ @IntDef(value = {
+ NOT_REGISTERED,
+ DISABLED,
+ ENABLED
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ServiceStatus {}
+
+ /**
+ * For the status of service registration or activation/deactivation.
+ */
+ public static final int NOT_REGISTERED = (-1);
+ public static final int DISABLED = 0;
+ public static final int ENABLED = 1;
+
+ /**
+ * Provision status of service.
+ * @hide
+ */
+ @IntDef(value = {
+ SERVICE_PROVISIONING_UNKNOWN,
+ SERVICE_NOT_PROVISIONED,
+ SERVICE_PROVISIONED
+ }, prefix = "SERVICE_")
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ServiceProvisionStatus {}
+
+ /**
+ * Unknown provision status for the service.
+ */
+ public static final int SERVICE_PROVISIONING_UNKNOWN = (-1);
+
+ /**
+ * Service is not provisioned.
+ */
+ public static final int SERVICE_NOT_PROVISIONED = 0;
+
+ /**
+ * Service is provisioned.
+ */
+ public static final int SERVICE_PROVISIONED = 1;
+
+ /**@hide*/
+ @IntDef(value = {
+ CLIR_OUTGOING_DEFAULT,
+ CLIR_OUTGOING_INVOCATION,
+ CLIR_OUTGOING_SUPPRESSION
+ }, prefix = "CLIR_OUTGOING_")
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ClirOutgoingState {}
+
+ /**
+ * Calling line identification restriction (CLIR) is set to the default according to the
+ * subscription of the CLIR service.
+ *
+ * See TS 27.007, section 7.7 for more information.
+ */
+ public static final int CLIR_OUTGOING_DEFAULT = 0;
+ /**
+ * Activate Calling line identification restriction for outgoing calls.
+ *
+ * See TS 27.007, section 7.7 for more information.
+ */
+ public static final int CLIR_OUTGOING_INVOCATION = 1;
+ /**
+ * Deactivate Calling line identification restriction for outgoing calls.
+ *
+ * See TS 27.007, section 7.7 for more information.
+ */
+ public static final int CLIR_OUTGOING_SUPPRESSION = 2;
+
+ /**
+ * Calling line identification restriction is currently not provisioned.
+ *
+ * See TS 27.007, section 7.7 for more information.
+ */
+ public static final int CLIR_STATUS_NOT_PROVISIONED = 0;
+ /**
+ * Calling line identification restriction is currently provisioned in permanent mode.
+ *
+ * See TS 27.007, section 7.7 for more information.
+ */
+ public static final int CLIR_STATUS_PROVISIONED_PERMANENT = 1;
+ /**
+ * Calling line identification restriction is currently unknown, e.g. no network, etc.
+ *
+ * See TS 27.007, section 7.7 for more information.
+ */
+ public static final int CLIR_STATUS_UNKNOWN = 2;
+ /**
+ * Calling line identification restriction temporary mode, temporarily restricted.
+ *
+ * See TS 27.007, section 7.7 for more information.
+ */
+ public static final int CLIR_STATUS_TEMPORARILY_RESTRICTED = 3;
+ /**
+ * Calling line identification restriction temporary mode, temporarily allowed.
+ *
+ * See TS 27.007, section 7.7 for more information.
+ */
+ public static final int CLIR_STATUS_TEMPORARILY_ALLOWED = 4;
+
+ /**@hide*/
+ @IntDef(value = {
+ CLIR_STATUS_NOT_PROVISIONED,
+ CLIR_STATUS_PROVISIONED_PERMANENT,
+ CLIR_STATUS_UNKNOWN,
+ CLIR_STATUS_TEMPORARILY_RESTRICTED,
+ CLIR_STATUS_TEMPORARILY_ALLOWED
+ }, prefix = "CLIR_STATUS_")
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ClirInterrogationStatus {}
+
+ // 0: disabled, 1: enabled
+ /** @hide */
+ @UnsupportedAppUsage
+ public int mStatus;
+ /** @hide */
+ @UnsupportedAppUsage
+ public String mIcbNum;
+ /** @hide */
+ public int mProvisionStatus = SERVICE_PROVISIONING_UNKNOWN;
+ private int mClirInterrogationStatus = CLIR_STATUS_UNKNOWN;
+ private int mClirOutgoingState = CLIR_OUTGOING_DEFAULT;
+
+ /**@hide*/
+ @UnsupportedAppUsage
+ public ImsSsInfo() {
+ }
+
+ /**
+ * Builds {@link ImsSsInfo} instances, which may include optional parameters.
+ */
+ public static final class Builder {
+
+ private final ImsSsInfo mImsSsInfo;
+
+ public Builder(@ServiceStatus int status) {
+ mImsSsInfo = new ImsSsInfo();
+ mImsSsInfo.mStatus = status;
+ }
+
+ /**
+ * Set the ICB number for IMS call barring.
+ * @param number The number in E.164 international format.
+ */
+ public @NonNull Builder setIncomingCommunicationBarringNumber(@NonNull String number) {
+ mImsSsInfo.mIcbNum = number;
+ return this;
+ }
+
+ /**
+ * Set the provisioning status for a Supplementary Service interrogation response.
+ */
+ public @NonNull Builder setProvisionStatus(@ServiceProvisionStatus int provisionStatus) {
+ mImsSsInfo.mProvisionStatus = provisionStatus;
+ return this;
+ }
+
+ /**
+ * Set the Calling Line Identification Restriction (CLIR) status for a supplementary service
+ * interrogation response.
+ */
+ public @NonNull Builder setClirInterrogationStatus(@ClirInterrogationStatus int status) {
+ mImsSsInfo.mClirInterrogationStatus = status;
+ return this;
+ }
+
+ /**
+ * Set the Calling line identification Restriction (CLIR) state for outgoing calls.
+ */
+ public @NonNull Builder setClirOutgoingState(@ClirOutgoingState int state) {
+ mImsSsInfo.mClirOutgoingState = state;
+ return this;
+ }
+
+ /**
+ * @return a built {@link ImsSsInfo} containing optional the parameters that were set.
+ */
+ public @NonNull ImsSsInfo build() {
+ return mImsSsInfo;
+ }
+ }
+
+ /**
+ *
+ * @param status The status of the service registration of activation/deactiviation.
+ * @param icbNum The Incoming barring number.
+ * @deprecated use {@link ImsSsInfo.Builder} instead.
+ */
+ @Deprecated
+ public ImsSsInfo(@ServiceStatus int status, @Nullable String icbNum) {
+ mStatus = status;
+ mIcbNum = icbNum;
+ }
+
+ private ImsSsInfo(Parcel in) {
+ readFromParcel(in);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(mStatus);
+ out.writeString(mIcbNum);
+ out.writeInt(mProvisionStatus);
+ out.writeInt(mClirInterrogationStatus);
+ out.writeInt(mClirOutgoingState);
+ }
+
+ @NonNull
+ @Override
+ public String toString() {
+ return super.toString() + ", Status: " + ((mStatus == 0) ? "disabled" : "enabled")
+ + ", ProvisionStatus: " + provisionStatusToString(mProvisionStatus);
+ }
+
+ private static String provisionStatusToString(int pStatus) {
+ switch (pStatus) {
+ case SERVICE_NOT_PROVISIONED:
+ return "Service not provisioned";
+ case SERVICE_PROVISIONED:
+ return "Service provisioned";
+ default:
+ return "Service provisioning unknown";
+ }
+ }
+
+ private void readFromParcel(Parcel in) {
+ mStatus = in.readInt();
+ mIcbNum = in.readString();
+ mProvisionStatus = in.readInt();
+ mClirInterrogationStatus = in.readInt();
+ mClirOutgoingState = in.readInt();
+ }
+
+ public static final @android.annotation.NonNull Creator<ImsSsInfo> CREATOR =
+ new Creator<ImsSsInfo>() {
+ @Override
+ public ImsSsInfo createFromParcel(Parcel in) {
+ return new ImsSsInfo(in);
+ }
+
+ @Override
+ public ImsSsInfo[] newArray(int size) {
+ return new ImsSsInfo[size];
+ }
+ };
+
+ /**
+ * @return Supplementary Service Configuration status.
+ */
+ public @ServiceStatus int getStatus() {
+ return mStatus;
+ }
+
+ /** @deprecated Use {@link #getIncomingCommunicationBarringNumber()} instead.*/
+ @Deprecated
+ public String getIcbNum() {
+ return mIcbNum;
+ }
+
+ /**
+ * @return The Incoming Communication Barring (ICB) number.
+ */
+ public @Nullable String getIncomingCommunicationBarringNumber() {
+ return mIcbNum;
+ }
+
+ /**
+ * @return Supplementary Service Provision status.
+ */
+ public @ServiceProvisionStatus int getProvisionStatus() {
+ return mProvisionStatus;
+ }
+
+ /**
+ * @return the Calling Line Identification Restriction State for outgoing calls with respect to
+ * this subscription. Will be {@link #CLIR_OUTGOING_DEFAULT} if not applicable to this SS info.
+ */
+ public @ClirOutgoingState int getClirOutgoingState() {
+ return mClirOutgoingState;
+ }
+
+ /**
+ * @return the calling line identification restriction provisioning status upon interrogation of
+ * the service for this subscription. Will be {@link #CLIR_STATUS_UNKNOWN} if not applicable to
+ * this SS info.
+ */
+ public @ClirInterrogationStatus int getClirInterrogationStatus() {
+ return mClirInterrogationStatus;
+ }
+
+ /**
+ * Parts of telephony still use the old {m,n} 3GPP definition, so convert to that format.
+ * @hide
+ */
+ public int[] getCompatArray(@ImsSsData.ServiceType int type) {
+ int[] result = new int[2];
+ // Convert ImsSsInfo into a form that telephony can read (as per 3GPP 27.007)
+ // CLIR (section 7.7)
+ if (type == ImsSsData.SS_CLIR) {
+ // Assume there will only be one ImsSsInfo.
+ // contains {"n","m"} parameters
+ result[0] = getClirOutgoingState();
+ result[1] = getClirInterrogationStatus();
+ return result;
+ }
+ // COLR 7.31
+ if (type == ImsSsData.SS_COLR) {
+ result[0] = getProvisionStatus();
+ }
+ // Facility Lock CLCK 7.4 (for call barring), CLIP 7.6, COLP 7.8, as well as any
+ // other result, just return the status for the "n" parameter and provisioning status for
+ // "m" as the default.
+ result[0] = getStatus();
+ result[1] = getProvisionStatus();
+ return result;
+ }
+}
diff --git a/android/telephony/ims/ImsStreamMediaProfile.java b/android/telephony/ims/ImsStreamMediaProfile.java
new file mode 100644
index 0000000..b70fd64
--- /dev/null
+++ b/android/telephony/ims/ImsStreamMediaProfile.java
@@ -0,0 +1,297 @@
+/*
+ * 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.SystemApi;
+import android.annotation.TestApi;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Parcelable object to handle IMS stream media profile.
+ * It provides the media direction, quality of audio and/or video.
+ *
+ * @hide
+ */
+@SystemApi
+@TestApi
+public final class ImsStreamMediaProfile implements Parcelable {
+ private static final String TAG = "ImsStreamMediaProfile";
+
+ /**
+ * Media directions
+ */
+ public static final int DIRECTION_INVALID = (-1);
+ public static final int DIRECTION_INACTIVE = 0;
+ public static final int DIRECTION_RECEIVE = 1;
+ public static final int DIRECTION_SEND = 2;
+ public static final int DIRECTION_SEND_RECEIVE = 3;
+
+ /**
+ * Audio information
+ */
+ public static final int AUDIO_QUALITY_NONE = 0;
+ public static final int AUDIO_QUALITY_AMR = 1;
+ public static final int AUDIO_QUALITY_AMR_WB = 2;
+ public static final int AUDIO_QUALITY_QCELP13K = 3;
+ public static final int AUDIO_QUALITY_EVRC = 4;
+ public static final int AUDIO_QUALITY_EVRC_B = 5;
+ public static final int AUDIO_QUALITY_EVRC_WB = 6;
+ public static final int AUDIO_QUALITY_EVRC_NW = 7;
+ public static final int AUDIO_QUALITY_GSM_EFR = 8;
+ public static final int AUDIO_QUALITY_GSM_FR = 9;
+ public static final int AUDIO_QUALITY_GSM_HR = 10;
+ public static final int AUDIO_QUALITY_G711U = 11;
+ public static final int AUDIO_QUALITY_G723 = 12;
+ public static final int AUDIO_QUALITY_G711A = 13;
+ public static final int AUDIO_QUALITY_G722 = 14;
+ public static final int AUDIO_QUALITY_G711AB = 15;
+ public static final int AUDIO_QUALITY_G729 = 16;
+ public static final int AUDIO_QUALITY_EVS_NB = 17;
+ public static final int AUDIO_QUALITY_EVS_WB = 18;
+ public static final int AUDIO_QUALITY_EVS_SWB = 19;
+ public static final int AUDIO_QUALITY_EVS_FB = 20;
+
+ /**
+ * Video information
+ */
+ public static final int VIDEO_QUALITY_NONE = 0;
+ public static final int VIDEO_QUALITY_QCIF = (1 << 0);
+ public static final int VIDEO_QUALITY_QVGA_LANDSCAPE = (1 << 1);
+ public static final int VIDEO_QUALITY_QVGA_PORTRAIT = (1 << 2);
+ public static final int VIDEO_QUALITY_VGA_LANDSCAPE = (1 << 3);
+ public static final int VIDEO_QUALITY_VGA_PORTRAIT = (1 << 4);
+
+ /**
+ * RTT Modes
+ */
+ public static final int RTT_MODE_DISABLED = 0;
+ public static final int RTT_MODE_FULL = 1;
+
+ // Audio related information
+ /** @hide */
+ @UnsupportedAppUsage
+ public int mAudioQuality;
+ /** @hide */
+ @UnsupportedAppUsage
+ public int mAudioDirection;
+ // Video related information
+ /** @hide */
+ public int mVideoQuality;
+ /** @hide */
+ @UnsupportedAppUsage
+ public int mVideoDirection;
+ // Rtt related information
+ /** @hide */
+ public int mRttMode;
+ // RTT Audio Speech Indicator
+ /** @hide */
+ public boolean mIsReceivingRttAudio = false;
+
+ /** @hide */
+ public ImsStreamMediaProfile(Parcel in) {
+ readFromParcel(in);
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param audioQuality The audio quality. Can be one of the following:
+ * {@link #AUDIO_QUALITY_AMR},
+ * {@link #AUDIO_QUALITY_AMR_WB},
+ * {@link #AUDIO_QUALITY_QCELP13K},
+ * {@link #AUDIO_QUALITY_EVRC},
+ * {@link #AUDIO_QUALITY_EVRC_B},
+ * {@link #AUDIO_QUALITY_EVRC_WB},
+ * {@link #AUDIO_QUALITY_EVRC_NW},
+ * {@link #AUDIO_QUALITY_GSM_EFR},
+ * {@link #AUDIO_QUALITY_GSM_FR},
+ * {@link #AUDIO_QUALITY_GSM_HR},
+ * {@link #AUDIO_QUALITY_G711U},
+ * {@link #AUDIO_QUALITY_G723},
+ * {@link #AUDIO_QUALITY_G711A},
+ * {@link #AUDIO_QUALITY_G722},
+ * {@link #AUDIO_QUALITY_G711AB},
+ * {@link #AUDIO_QUALITY_G729},
+ * {@link #AUDIO_QUALITY_EVS_NB},
+ * {@link #AUDIO_QUALITY_EVS_WB},
+ * {@link #AUDIO_QUALITY_EVS_SWB},
+ * {@link #AUDIO_QUALITY_EVS_FB},
+ * @param audioDirection The audio direction. Can be one of the following:
+ * {@link #DIRECTION_INVALID},
+ * {@link #DIRECTION_INACTIVE},
+ * {@link #DIRECTION_RECEIVE},
+ * {@link #DIRECTION_SEND},
+ * {@link #DIRECTION_SEND_RECEIVE},
+ * @param videoQuality The video quality. Can be one of the following:
+ * {@link #VIDEO_QUALITY_NONE},
+ * {@link #VIDEO_QUALITY_QCIF},
+ * {@link #VIDEO_QUALITY_QVGA_LANDSCAPE},
+ * {@link #VIDEO_QUALITY_QVGA_PORTRAIT},
+ * {@link #VIDEO_QUALITY_VGA_LANDSCAPE},
+ * {@link #VIDEO_QUALITY_VGA_PORTRAIT},
+ * @param videoDirection The video direction. Can be one of the following:
+ * {@link #DIRECTION_INVALID},
+ * {@link #DIRECTION_INACTIVE},
+ * {@link #DIRECTION_RECEIVE},
+ * {@link #DIRECTION_SEND},
+ * {@link #DIRECTION_SEND_RECEIVE},
+ * @param rttMode The rtt mode. Can be one of the following:
+ * {@link #RTT_MODE_DISABLED},
+ * {@link #RTT_MODE_FULL}
+ */
+ public ImsStreamMediaProfile(int audioQuality, int audioDirection,
+ int videoQuality, int videoDirection, int rttMode) {
+ mAudioQuality = audioQuality;
+ mAudioDirection = audioDirection;
+ mVideoQuality = videoQuality;
+ mVideoDirection = videoDirection;
+ mRttMode = rttMode;
+ }
+
+ /** @hide */
+ @UnsupportedAppUsage
+ public ImsStreamMediaProfile() {
+ mAudioQuality = AUDIO_QUALITY_NONE;
+ mAudioDirection = DIRECTION_SEND_RECEIVE;
+ mVideoQuality = VIDEO_QUALITY_NONE;
+ mVideoDirection = DIRECTION_INVALID;
+ mRttMode = RTT_MODE_DISABLED;
+ }
+
+ /** @hide */
+ public ImsStreamMediaProfile(int audioQuality, int audioDirection,
+ int videoQuality, int videoDirection) {
+ mAudioQuality = audioQuality;
+ mAudioDirection = audioDirection;
+ mVideoQuality = videoQuality;
+ mVideoDirection = videoDirection;
+ }
+
+ /** @hide */
+ public ImsStreamMediaProfile(int rttMode) {
+ mRttMode = rttMode;
+ }
+
+ public void copyFrom(ImsStreamMediaProfile profile) {
+ mAudioQuality = profile.mAudioQuality;
+ mAudioDirection = profile.mAudioDirection;
+ mVideoQuality = profile.mVideoQuality;
+ mVideoDirection = profile.mVideoDirection;
+ mRttMode = profile.mRttMode;
+ }
+
+ @NonNull
+ @Override
+ public String toString() {
+ return "{ audioQuality=" + mAudioQuality +
+ ", audioDirection=" + mAudioDirection +
+ ", videoQuality=" + mVideoQuality +
+ ", videoDirection=" + mVideoDirection +
+ ", rttMode=" + mRttMode +
+ ", hasRttAudioSpeech=" + mIsReceivingRttAudio + " }";
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(mAudioQuality);
+ out.writeInt(mAudioDirection);
+ out.writeInt(mVideoQuality);
+ out.writeInt(mVideoDirection);
+ out.writeInt(mRttMode);
+ out.writeBoolean(mIsReceivingRttAudio);
+ }
+
+ private void readFromParcel(Parcel in) {
+ mAudioQuality = in.readInt();
+ mAudioDirection = in.readInt();
+ mVideoQuality = in.readInt();
+ mVideoDirection = in.readInt();
+ mRttMode = in.readInt();
+ mIsReceivingRttAudio = in.readBoolean();
+ }
+
+ public static final @android.annotation.NonNull Creator<ImsStreamMediaProfile> CREATOR =
+ new Creator<ImsStreamMediaProfile>() {
+ @Override
+ public ImsStreamMediaProfile createFromParcel(Parcel in) {
+ return new ImsStreamMediaProfile(in);
+ }
+
+ @Override
+ public ImsStreamMediaProfile[] newArray(int size) {
+ return new ImsStreamMediaProfile[size];
+ }
+ };
+
+ /**
+ * Determines if it's RTT call
+ * @return true if RTT call, false otherwise.
+ */
+ public boolean isRttCall() {
+ return (mRttMode == RTT_MODE_FULL);
+ }
+
+ /**
+ * Updates the RttCall attribute
+ */
+ public void setRttMode(int rttMode) {
+ mRttMode = rttMode;
+ }
+
+ /**
+ * Sets whether the remote party is transmitting audio over the RTT call.
+ * @param audioOn true if audio is being received, false otherwise.
+ */
+ public void setReceivingRttAudio(boolean audioOn) {
+ mIsReceivingRttAudio = audioOn;
+ }
+
+ public int getAudioQuality() {
+ return mAudioQuality;
+ }
+
+ public int getAudioDirection() {
+ return mAudioDirection;
+ }
+
+ public int getVideoQuality() {
+ return mVideoQuality;
+ }
+
+ public int getVideoDirection() {
+ return mVideoDirection;
+ }
+
+ public int getRttMode() {
+ return mRttMode;
+ }
+
+ /**
+ * @return true if remote party is transmitting audio, false otherwise.
+ */
+ public boolean isReceivingRttAudio() {
+ return mIsReceivingRttAudio;
+ }
+}
diff --git a/android/telephony/ims/ImsSuppServiceNotification.java b/android/telephony/ims/ImsSuppServiceNotification.java
new file mode 100644
index 0000000..f67f68e
--- /dev/null
+++ b/android/telephony/ims/ImsSuppServiceNotification.java
@@ -0,0 +1,112 @@
+/*
+ * 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.SystemApi;
+import android.annotation.TestApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Arrays;
+
+
+/**
+ * Parcelable object to handle IMS supplementary service notifications.
+ *
+ * @hide
+ */
+@SystemApi
+@TestApi
+public final class ImsSuppServiceNotification implements Parcelable {
+ private static final String TAG = "ImsSuppServiceNotification";
+
+ /** Type of notification: 0 = MO; 1 = MT */
+ public final int notificationType;
+ /** TS 27.007 7.17 "code1" or "code2" */
+ public final int code;
+ /** TS 27.007 7.17 "index" - Not used currently*/
+ public final int index;
+ /** TS 27.007 7.17 "type" (MT only) - Not used currently */
+ public final int type;
+ /** TS 27.007 7.17 "number" (MT only) */
+ public final String number;
+ /** List of forwarded numbers, if any */
+ public final String[] history;
+
+
+ public ImsSuppServiceNotification(int notificationType, int code, int index, int type,
+ String number, String[] history) {
+ this.notificationType = notificationType;
+ this.code = code;
+ this.index = index;
+ this.type = type;
+ this.number = number;
+ this.history = history;
+ }
+
+ /** @hide */
+ public ImsSuppServiceNotification(Parcel in) {
+ notificationType = in.readInt();
+ code = in.readInt();
+ index = in.readInt();
+ type = in.readInt();
+ number = in.readString();
+ history = in.createStringArray();
+ }
+
+ @NonNull
+ @Override
+ public String toString() {
+ return "{ notificationType=" + notificationType +
+ ", code=" + code +
+ ", index=" + index +
+ ", type=" + type +
+ ", number=" + number +
+ ", history=" + Arrays.toString(history) +
+ " }";
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(notificationType);
+ out.writeInt(code);
+ out.writeInt(index);
+ out.writeInt(type);
+ out.writeString(number);
+ out.writeStringArray(history);
+ }
+
+ public static final @android.annotation.NonNull Creator<ImsSuppServiceNotification> CREATOR =
+ new Creator<ImsSuppServiceNotification>() {
+ @Override
+ public ImsSuppServiceNotification createFromParcel(Parcel in) {
+ return new ImsSuppServiceNotification(in);
+ }
+
+ @Override
+ public ImsSuppServiceNotification[] newArray(int size) {
+ return new ImsSuppServiceNotification[size];
+ }
+ };
+}
diff --git a/android/telephony/ims/ImsUtListener.java b/android/telephony/ims/ImsUtListener.java
new file mode 100644
index 0000000..460a032
--- /dev/null
+++ b/android/telephony/ims/ImsUtListener.java
@@ -0,0 +1,183 @@
+/*
+ * 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.SystemApi;
+import android.annotation.TestApi;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.telephony.ims.stub.ImsUtImplBase;
+import android.util.Log;
+
+import com.android.ims.internal.IImsUtListener;
+
+/**
+ * Listener interface used to receive network responses back from UT supplementary service queries
+ * made by the framework.
+ * @hide
+ */
+// DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
+// will break other implementations of ImsUt maintained by other ImsServices.
+@SystemApi
+@TestApi
+public class ImsUtListener {
+
+ /**
+ * The {@link Bundle} key for a Calling Line Identification Restriction (CLIR) response. The
+ * value will be an int[] with two values:
+ * int[0] contains the 'n' parameter from TS 27.007 7.7, which is the
+ * outgoing CLIR state. See {@link ImsSsInfo#CLIR_OUTGOING_DEFAULT},
+ * {@link ImsSsInfo#CLIR_OUTGOING_INVOCATION}, and {@link ImsSsInfo#CLIR_OUTGOING_SUPPRESSION};
+ * int[1] contains the 'm' parameter from TS 27.007 7.7, which is the CLIR interrogation status.
+ * See {@link ImsSsInfo#CLIR_STATUS_NOT_PROVISIONED},
+ * {@link ImsSsInfo#CLIR_STATUS_PROVISIONED_PERMANENT}, {@link ImsSsInfo#CLIR_STATUS_UNKNOWN},
+ * {@link ImsSsInfo#CLIR_STATUS_TEMPORARILY_RESTRICTED}, and
+ * {@link ImsSsInfo#CLIR_STATUS_TEMPORARILY_ALLOWED}.
+ * @deprecated Use {@link #onLineIdentificationSupplementaryServiceResponse(int, ImsSsInfo)}
+ * instead, this key has been added for backwards compatibility with older proprietary
+ * implementations only and is being phased out.
+ */
+ @Deprecated
+ public static final String BUNDLE_KEY_CLIR = "queryClir";
+
+ /**
+ * The {@link Bundle} key for a Calling Line Identification Presentation (CLIP), Connected Line
+ * Identification Presentation (COLP), or Connected Line Identification Restriction (COLR)
+ * response. The value will be an instance of {@link ImsSsInfo}, which contains the response to
+ * the query.
+ * @deprecated Use {@link #onLineIdentificationSupplementaryServiceResponse(int, ImsSsInfo)}
+ * instead, this key has been added for backwards compatibility with older proprietary
+ * implementations only and is being phased out.
+ */
+ @Deprecated
+ public static final String BUNDLE_KEY_SSINFO = "imsSsInfo";
+
+ private IImsUtListener mServiceInterface;
+ private static final String LOG_TAG = "ImsUtListener";
+
+ public void onUtConfigurationUpdated(int id) {
+ try {
+ mServiceInterface.utConfigurationUpdated(null, id);
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, "utConfigurationUpdated: remote exception");
+ }
+ }
+
+ public void onUtConfigurationUpdateFailed(int id, ImsReasonInfo error) {
+ try {
+ mServiceInterface.utConfigurationUpdateFailed(null, id, error);
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, "utConfigurationUpdateFailed: remote exception");
+ }
+ }
+
+ /**
+ * Notify the framework of a UT configuration response to a {@link ImsUtImplBase#queryClir()},
+ * {@link ImsUtImplBase#queryClip()}, {@link ImsUtImplBase#queryColp()}, or
+ * {@link ImsUtImplBase#queryColr()} query for the transaction ID specified. If the query fails,
+ * {@link #onUtConfigurationQueryFailed(int, ImsReasonInfo)} should be called.
+ * @param id The ID associated with this UT configuration transaction from the framework.
+ * @param configuration A {@link Bundle} containing the result of querying the UT configuration.
+ * Must contain {@link #BUNDLE_KEY_CLIR} if it is a response to
+ * {@link ImsUtImplBase#queryClir()} or
+ * {@link #BUNDLE_KEY_SSINFO} if it is a response to
+ * {@link ImsUtImplBase#queryClip()}, {@link ImsUtImplBase#queryColp()}, or
+ * {@link ImsUtImplBase#queryColr()}.
+ * @deprecated Use {@link #onLineIdentificationSupplementaryServiceResponse(int, ImsSsInfo)}
+ * instead.
+ */
+ @Deprecated
+ public void onUtConfigurationQueried(int id, Bundle configuration) {
+ try {
+ mServiceInterface.utConfigurationQueried(null, id, configuration);
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, "utConfigurationQueried: remote exception");
+ }
+ }
+
+ /**
+ * Notify the framework of a UT configuration response to a {@link ImsUtImplBase#queryClir()},
+ * {@link ImsUtImplBase#queryClip()}, {@link ImsUtImplBase#queryColp()}, or
+ * {@link ImsUtImplBase#queryColr()} query for the transaction ID specified. If the query fails,
+ * the framework should be notified via
+ * {@link #onUtConfigurationQueryFailed(int, ImsReasonInfo)}.
+ * @param id The ID associated with this UT configuration transaction from the framework.
+ * @param configuration An {@link ImsSsInfo} instance containing the configuration for the
+ * line identification supplementary service queried.
+ */
+ public void onLineIdentificationSupplementaryServiceResponse(int id,
+ @NonNull ImsSsInfo configuration) {
+ try {
+ mServiceInterface.lineIdentificationSupplementaryServiceResponse(id, configuration);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Notify the Framework of the line identification query failure.
+ * @param id The ID associated with the UT query transaction.
+ * @param error The query failure reason.
+ */
+ public void onUtConfigurationQueryFailed(int id, ImsReasonInfo error) {
+ try {
+ mServiceInterface.utConfigurationQueryFailed(null, id, error);
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, "utConfigurationQueryFailed: remote exception");
+ }
+ }
+
+ public void onUtConfigurationCallBarringQueried(int id, ImsSsInfo[] cbInfo) {
+ try {
+ mServiceInterface.utConfigurationCallBarringQueried(null, id, cbInfo);
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, "utConfigurationCallBarringQueried: remote exception");
+ }
+ }
+
+ public void onUtConfigurationCallForwardQueried(int id, ImsCallForwardInfo[] cfInfo) {
+ try {
+ mServiceInterface.utConfigurationCallForwardQueried(null, id, cfInfo);
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, "utConfigurationCallForwardQueried: remote exception");
+ }
+ }
+
+ public void onUtConfigurationCallWaitingQueried(int id, ImsSsInfo[] cwInfo) {
+ try {
+ mServiceInterface.utConfigurationCallWaitingQueried(null, id, cwInfo);
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, "utConfigurationCallWaitingQueried: remote exception");
+ }
+ }
+
+ public void onSupplementaryServiceIndication(ImsSsData ssData) {
+ try {
+ mServiceInterface.onSupplementaryServiceIndication(ssData);
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, "onSupplementaryServiceIndication: remote exception");
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public ImsUtListener(IImsUtListener serviceInterface) {
+ mServiceInterface = serviceInterface;
+ }
+}
diff --git a/android/telephony/ims/ImsVideoCallProvider.java b/android/telephony/ims/ImsVideoCallProvider.java
new file mode 100644
index 0000000..569c6d5
--- /dev/null
+++ b/android/telephony/ims/ImsVideoCallProvider.java
@@ -0,0 +1,303 @@
+/*
+ * 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.SystemApi;
+import android.annotation.TestApi;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.telecom.Connection;
+import android.telecom.VideoProfile;
+import android.telecom.VideoProfile.CameraCapabilities;
+import android.view.Surface;
+
+import com.android.ims.internal.IImsVideoCallCallback;
+import com.android.ims.internal.IImsVideoCallProvider;
+import com.android.internal.os.SomeArgs;
+
+/**
+ * @hide
+ */
+@SystemApi
+@TestApi
+public abstract class ImsVideoCallProvider {
+ private static final int MSG_SET_CALLBACK = 1;
+ private static final int MSG_SET_CAMERA = 2;
+ private static final int MSG_SET_PREVIEW_SURFACE = 3;
+ private static final int MSG_SET_DISPLAY_SURFACE = 4;
+ private static final int MSG_SET_DEVICE_ORIENTATION = 5;
+ private static final int MSG_SET_ZOOM = 6;
+ private static final int MSG_SEND_SESSION_MODIFY_REQUEST = 7;
+ private static final int MSG_SEND_SESSION_MODIFY_RESPONSE = 8;
+ private static final int MSG_REQUEST_CAMERA_CAPABILITIES = 9;
+ private static final int MSG_REQUEST_CALL_DATA_USAGE = 10;
+ private static final int MSG_SET_PAUSE_IMAGE = 11;
+
+ private final ImsVideoCallProviderBinder mBinder;
+
+ private IImsVideoCallCallback mCallback;
+
+ /**
+ * Default handler used to consolidate binder method calls onto a single thread.
+ */
+ private final Handler mProviderHandler = new Handler(Looper.getMainLooper()) {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_SET_CALLBACK:
+ mCallback = (IImsVideoCallCallback) msg.obj;
+ break;
+ case MSG_SET_CAMERA:
+ {
+ SomeArgs args = (SomeArgs) msg.obj;
+ try {
+ onSetCamera((String) args.arg1);
+ onSetCamera((String) args.arg1, args.argi1);
+ } finally {
+ args.recycle();
+ }
+ break;
+ }
+ case MSG_SET_PREVIEW_SURFACE:
+ onSetPreviewSurface((Surface) msg.obj);
+ break;
+ case MSG_SET_DISPLAY_SURFACE:
+ onSetDisplaySurface((Surface) msg.obj);
+ break;
+ case MSG_SET_DEVICE_ORIENTATION:
+ onSetDeviceOrientation(msg.arg1);
+ break;
+ case MSG_SET_ZOOM:
+ onSetZoom((Float) msg.obj);
+ break;
+ case MSG_SEND_SESSION_MODIFY_REQUEST: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ try {
+ VideoProfile fromProfile = (VideoProfile) args.arg1;
+ VideoProfile toProfile = (VideoProfile) args.arg2;
+
+ onSendSessionModifyRequest(fromProfile, toProfile);
+ } finally {
+ args.recycle();
+ }
+ break;
+ }
+ case MSG_SEND_SESSION_MODIFY_RESPONSE:
+ onSendSessionModifyResponse((VideoProfile) msg.obj);
+ break;
+ case MSG_REQUEST_CAMERA_CAPABILITIES:
+ onRequestCameraCapabilities();
+ break;
+ case MSG_REQUEST_CALL_DATA_USAGE:
+ onRequestCallDataUsage();
+ break;
+ case MSG_SET_PAUSE_IMAGE:
+ onSetPauseImage((Uri) msg.obj);
+ break;
+ default:
+ break;
+ }
+ }
+ };
+
+ /**
+ * IImsVideoCallProvider stub implementation.
+ */
+ private final class ImsVideoCallProviderBinder extends IImsVideoCallProvider.Stub {
+ public void setCallback(IImsVideoCallCallback callback) {
+ mProviderHandler.obtainMessage(MSG_SET_CALLBACK, callback).sendToTarget();
+ }
+
+ public void setCamera(String cameraId, int uid) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = cameraId;
+ args.argi1 = uid;
+ mProviderHandler.obtainMessage(MSG_SET_CAMERA, args).sendToTarget();
+ }
+
+ public void setPreviewSurface(Surface surface) {
+ mProviderHandler.obtainMessage(MSG_SET_PREVIEW_SURFACE, surface).sendToTarget();
+ }
+
+ public void setDisplaySurface(Surface surface) {
+ mProviderHandler.obtainMessage(MSG_SET_DISPLAY_SURFACE, surface).sendToTarget();
+ }
+
+ public void setDeviceOrientation(int rotation) {
+ mProviderHandler.obtainMessage(MSG_SET_DEVICE_ORIENTATION, rotation, 0).sendToTarget();
+ }
+
+ public void setZoom(float value) {
+ mProviderHandler.obtainMessage(MSG_SET_ZOOM, value).sendToTarget();
+ }
+
+ public void sendSessionModifyRequest(VideoProfile fromProfile, VideoProfile toProfile) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = fromProfile;
+ args.arg2 = toProfile;
+ mProviderHandler.obtainMessage(MSG_SEND_SESSION_MODIFY_REQUEST, args).sendToTarget();
+ }
+
+ public void sendSessionModifyResponse(VideoProfile responseProfile) {
+ mProviderHandler.obtainMessage(
+ MSG_SEND_SESSION_MODIFY_RESPONSE, responseProfile).sendToTarget();
+ }
+
+ public void requestCameraCapabilities() {
+ mProviderHandler.obtainMessage(MSG_REQUEST_CAMERA_CAPABILITIES).sendToTarget();
+ }
+
+ public void requestCallDataUsage() {
+ mProviderHandler.obtainMessage(MSG_REQUEST_CALL_DATA_USAGE).sendToTarget();
+ }
+
+ public void setPauseImage(Uri uri) {
+ mProviderHandler.obtainMessage(MSG_SET_PAUSE_IMAGE, uri).sendToTarget();
+ }
+ }
+
+ public ImsVideoCallProvider() {
+ mBinder = new ImsVideoCallProviderBinder();
+ }
+
+ /**
+ * Returns binder object which can be used across IPC methods.
+ * @hide
+ */
+ @UnsupportedAppUsage
+ public final IImsVideoCallProvider getInterface() {
+ return mBinder;
+ }
+
+ /** @see Connection.VideoProvider#onSetCamera */
+ public abstract void onSetCamera(String cameraId);
+
+ /**
+ * Similar to {@link #onSetCamera(String)}, except includes the UID of the calling process which
+ * the IMS service uses when opening the camera. This ensures camera permissions are verified
+ * by the camera service.
+ *
+ * @param cameraId The id of the camera to be opened.
+ * @param uid The uid of the caller, used when opening the camera for permission verification.
+ * @see Connection.VideoProvider#onSetCamera
+ */
+ public void onSetCamera(String cameraId, int uid) {
+ }
+
+ /** @see Connection.VideoProvider#onSetPreviewSurface */
+ public abstract void onSetPreviewSurface(Surface surface);
+
+ /** @see Connection.VideoProvider#onSetDisplaySurface */
+ public abstract void onSetDisplaySurface(Surface surface);
+
+ /** @see Connection.VideoProvider#onSetDeviceOrientation */
+ public abstract void onSetDeviceOrientation(int rotation);
+
+ /** @see Connection.VideoProvider#onSetZoom */
+ public abstract void onSetZoom(float value);
+
+ /** @see Connection.VideoProvider#onSendSessionModifyRequest */
+ public abstract void onSendSessionModifyRequest(VideoProfile fromProfile,
+ VideoProfile toProfile);
+
+ /** @see Connection.VideoProvider#onSendSessionModifyResponse */
+ public abstract void onSendSessionModifyResponse(VideoProfile responseProfile);
+
+ /** @see Connection.VideoProvider#onRequestCameraCapabilities */
+ public abstract void onRequestCameraCapabilities();
+
+ /** @see Connection.VideoProvider#onRequestCallDataUsage */
+ public abstract void onRequestCallDataUsage();
+
+ /** @see Connection.VideoProvider#onSetPauseImage */
+ public abstract void onSetPauseImage(Uri uri);
+
+ /** @see Connection.VideoProvider#receiveSessionModifyRequest */
+ public void receiveSessionModifyRequest(VideoProfile VideoProfile) {
+ if (mCallback != null) {
+ try {
+ mCallback.receiveSessionModifyRequest(VideoProfile);
+ } catch (RemoteException ignored) {
+ }
+ }
+ }
+
+ /** @see Connection.VideoProvider#receiveSessionModifyResponse */
+ public void receiveSessionModifyResponse(
+ int status, VideoProfile requestedProfile, VideoProfile responseProfile) {
+ if (mCallback != null) {
+ try {
+ mCallback.receiveSessionModifyResponse(status, requestedProfile, responseProfile);
+ } catch (RemoteException ignored) {
+ }
+ }
+ }
+
+ /** @see Connection.VideoProvider#handleCallSessionEvent */
+ public void handleCallSessionEvent(int event) {
+ if (mCallback != null) {
+ try {
+ mCallback.handleCallSessionEvent(event);
+ } catch (RemoteException ignored) {
+ }
+ }
+ }
+
+ /** @see Connection.VideoProvider#changePeerDimensions */
+ public void changePeerDimensions(int width, int height) {
+ if (mCallback != null) {
+ try {
+ mCallback.changePeerDimensions(width, height);
+ } catch (RemoteException ignored) {
+ }
+ }
+ }
+
+ /** @see Connection.VideoProvider#changeCallDataUsage */
+ public void changeCallDataUsage(long dataUsage) {
+ if (mCallback != null) {
+ try {
+ mCallback.changeCallDataUsage(dataUsage);
+ } catch (RemoteException ignored) {
+ }
+ }
+ }
+
+ /** @see Connection.VideoProvider#changeCameraCapabilities */
+ public void changeCameraCapabilities(CameraCapabilities CameraCapabilities) {
+ if (mCallback != null) {
+ try {
+ mCallback.changeCameraCapabilities(CameraCapabilities);
+ } catch (RemoteException ignored) {
+ }
+ }
+ }
+
+ /** @see Connection.VideoProvider#changeVideoQuality */
+ public void changeVideoQuality(int videoQuality) {
+ if (mCallback != null) {
+ try {
+ mCallback.changeVideoQuality(videoQuality);
+ } catch (RemoteException ignored) {
+ }
+ }
+ }
+}
diff --git a/android/telephony/ims/ProvisioningManager.java b/android/telephony/ims/ProvisioningManager.java
new file mode 100644
index 0000000..1a606b7
--- /dev/null
+++ b/android/telephony/ims/ProvisioningManager.java
@@ -0,0 +1,1210 @@
+/*
+ * 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.Manifest;
+import android.annotation.CallbackExecutor;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.StringDef;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.annotation.WorkerThread;
+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.ims.aidl.IImsConfigCallback;
+import android.telephony.ims.feature.MmTelFeature;
+import android.telephony.ims.feature.RcsFeature;
+import android.telephony.ims.stub.ImsConfigImplBase;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
+
+import com.android.internal.telephony.ITelephony;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.Executor;
+
+/**
+ * Manages IMS provisioning and configuration parameters, as well as callbacks for apps to listen
+ * to changes in these configurations.
+ *
+ * IMS provisioning keys are defined per carrier or OEM using OMA-DM or other provisioning
+ * applications and may vary. It is up to the carrier and OEM applications to ensure that the
+ * correct provisioning keys are being used when integrating with a vendor's ImsService.
+ *
+ * Note: For compatibility purposes, the integer values [0 - 99] used in
+ * {@link #setProvisioningIntValue(int, int)} have been reserved for existing provisioning keys
+ * previously defined in the Android framework. Please do not redefine new provisioning keys in this
+ * range or it may generate collisions with existing keys. Some common constants have also been
+ * defined in this class to make integrating with other system apps easier.
+ * @hide
+ */
+@SystemApi
+@TestApi
+public class ProvisioningManager {
+
+ /**@hide*/
+ @StringDef(prefix = "STRING_QUERY_RESULT_ERROR_", value = {
+ STRING_QUERY_RESULT_ERROR_GENERIC,
+ STRING_QUERY_RESULT_ERROR_NOT_READY
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface StringResultError {}
+
+ /**
+ * The query from {@link #getProvisioningStringValue(int)} has resulted in an unspecified error.
+ */
+ public static final String STRING_QUERY_RESULT_ERROR_GENERIC =
+ "STRING_QUERY_RESULT_ERROR_GENERIC";
+
+ /**
+ * The query from {@link #getProvisioningStringValue(int)} has resulted in an error because the
+ * ImsService implementation was not ready for provisioning queries.
+ */
+ public static final String STRING_QUERY_RESULT_ERROR_NOT_READY =
+ "STRING_QUERY_RESULT_ERROR_NOT_READY";
+
+ /**
+ * There is no existing configuration for the queried provisioning key.
+ * @hide
+ */
+ public static final int PROVISIONING_RESULT_UNKNOWN = -1;
+
+ /**
+ * The integer result of provisioning for the queried key is disabled.
+ */
+ public static final int PROVISIONING_VALUE_DISABLED = 0;
+
+ /**
+ * The integer result of provisioning for the queried key is enabled.
+ */
+ public static final int PROVISIONING_VALUE_ENABLED = 1;
+
+
+ // Inheriting values from ImsConfig for backwards compatibility.
+ /**
+ * AMR CODEC Mode Value set, 0-7 in comma separated sequence.
+ * <p>
+ * This corresponds to the {@code mode-set} parameter for the AMR codec.
+ * See 3GPP TS 26.101 Table 1A for more information.
+ * <p>
+ * <UL>
+ * <LI>0 - AMR 4.75 kbit/s</LI>
+ * <LI>1 - AMR 5.15 kbit/s</LI>
+ * <LI>2 - AMR 5.90 kbit/s</LI>
+ * <LI>3 - AMR 6.70 kbit/s (PDC-EFR)</LI>
+ * <LI>4 - AMR 7.40 kbit/s (TDMA-EFR)</LI>
+ * <LI>5 - AMR 7.95 kbit/s</LI>
+ * <LI>6 - AMR 10.2 kbit/s</LI>
+ * <LI>7 - AMR 12.2 kbit/s (GSM-EFR)</LI>
+ * </UL>
+ * <p>
+ * Value is in String format.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ * @hide
+ */
+ public static final int KEY_AMR_CODEC_MODE_SET_VALUES = 0;
+
+ /**
+ * Wide Band AMR CODEC Mode Value set,0-7 in comma separated sequence.
+ * <p>
+ * This corresponds to the {@code mode-set} parameter for the AMR wideband codec.
+ * See 3GPP TS 26.101 Table 1A for more information.
+ * <p>
+ * <UL>
+ * <LI>0 - AMR 4.75 kbit/s</LI>
+ * <LI>1 - AMR 5.15 kbit/s</LI>
+ * <LI>2 - AMR 5.90 kbit/s</LI>
+ * <LI>3 - AMR 6.70 kbit/s (PDC-EFR)</LI>
+ * <LI>4 - AMR 7.40 kbit/s (TDMA-EFR)</LI>
+ * <LI>5 - AMR 7.95 kbit/s</LI>
+ * <LI>6 - AMR 10.2 kbit/s</LI>
+ * <LI>7 - AMR 12.2 kbit/s (GSM-EFR)</LI>
+ * </UL>
+ * <p>
+ * Value is in String format.
+ * @see #setProvisioningStringValue(int, String)
+ * @see #getProvisioningStringValue(int)
+ * @hide
+ */
+ public static final int KEY_AMR_WB_CODEC_MODE_SET_VALUES = 1;
+
+ /**
+ * SIP Session Timer value (seconds).
+ * <p>
+ * See RFC4028 for more information.
+ * <p>
+ * Value is in Integer format.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ * @hide
+ */
+ public static final int KEY_SIP_SESSION_TIMER_SEC = 2;
+
+ /**
+ * Minimum SIP Session Expiration Timer in (seconds).
+ * <p>
+ * See RFC4028 for more information.
+ * <p>
+ * Value is in Integer format.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ * @hide
+ */
+ public static final int KEY_MINIMUM_SIP_SESSION_EXPIRATION_TIMER_SEC = 3;
+
+ /**
+ * SIP_INVITE cancellation time out value (in milliseconds). Integer format.
+ * <p>
+ * See RFC4028 for more information.
+ * <p>
+ * Value is in Integer format.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ * @hide
+ */
+ public static final int KEY_SIP_INVITE_CANCELLATION_TIMER_MS = 4;
+
+ /**
+ * Delay time when an iRAT transitions from eHRPD/HRPD/1xRTT to LTE.
+ * Value is in Integer format.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ * @hide
+ */
+ public static final int KEY_TRANSITION_TO_LTE_DELAY_MS = 5;
+
+ /**
+ * Silent redial status of Enabled (True), or Disabled (False).
+ * Value is in boolean format.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ * @hide
+ */
+ public static final int KEY_ENABLE_SILENT_REDIAL = 6;
+
+ /**
+ * An integer key representing the SIP T1 timer value in milliseconds for the associated
+ * subscription.
+ * <p>
+ * The SIP T1 timer is an estimate of the round-trip time and will retransmit
+ * INVITE transactions that are longer than T1 milliseconds over unreliable transports, doubling
+ * the time before retransmission every time there is no response. See RFC3261, section 17.1.1.1
+ * for more details.
+ * <p>
+ * The value is an integer.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ * @hide
+ */
+ public static final int KEY_T1_TIMER_VALUE_MS = 7;
+
+ /**
+ * SIP T2 timer value in milliseconds. See RFC 3261 for information.
+ * <p>
+ * The T2 timer is the maximum retransmit interval for non-INVITE requests and INVITE responses.
+ * <p>
+ * Value is in Integer format.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ * @hide
+ */
+ public static final int KEY_T2_TIMER_VALUE_MS = 8;
+
+ /**
+ * SIP TF timer value in milliseconds. See RFC 3261 for information.
+ * <p>
+ * The TF timer is the non-INVITE transaction timeout timer.
+ * <p>
+ * Value is in Integer format.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ * @hide
+ */
+ public static final int KEY_TF_TIMER_VALUE_MS = 9;
+
+ /**
+ * An integer key representing the voice over LTE (VoLTE) provisioning status for the
+ * associated subscription. Determines whether the user can register for voice services over
+ * LTE.
+ * <p>
+ * Use {@link #PROVISIONING_VALUE_ENABLED} to enable VoLTE provisioning and
+ * {@link #PROVISIONING_VALUE_DISABLED} to disable VoLTE provisioning.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ * @hide
+ */
+ public static final int KEY_VOLTE_PROVISIONING_STATUS = 10;
+
+ /**
+ * An integer key representing the video telephony (VT) provisioning status for the
+ * associated subscription. Determines whether the user can register for video services over
+ * LTE.
+ * <p>
+ * Use {@link #PROVISIONING_VALUE_ENABLED} to enable VT provisioning and
+ * {@link #PROVISIONING_VALUE_DISABLED} to disable VT provisioning.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ * @hide
+ */
+ public static final int KEY_VT_PROVISIONING_STATUS = 11;
+
+ /**
+ * Domain Name for the device to populate the request URI for REGISTRATION.
+ * Value is in String format.
+ * @see #setProvisioningStringValue(int, String)
+ * @see #getProvisioningStringValue(int)
+ * @hide
+ */
+ public static final int KEY_REGISTRATION_DOMAIN_NAME = 12;
+
+ /**
+ * Device Outgoing SMS based on either 3GPP or 3GPP2 standards.
+ * Value is in Integer format.
+ * Valid values are {@link #SMS_FORMAT_3GPP} and {@link #SMS_FORMAT_3GPP2}.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ * @hide
+ */
+ public static final int KEY_SMS_FORMAT = 13;
+
+ /**
+ * Value used with {@link #KEY_SMS_FORMAT} to indicate 3GPP2 SMS format is used.
+ * See {@link android.telephony.SmsMessage#FORMAT_3GPP2} for more information.
+ * @hide
+ */
+ public static final int SMS_FORMAT_3GPP2 = 0;
+
+ /**
+ * Value used with {@link #KEY_SMS_FORMAT} to indicate 3GPP SMS format is used.
+ * See {@link android.telephony.SmsMessage#FORMAT_3GPP} for more information.
+ * @hide
+ */
+ public static final int SMS_FORMAT_3GPP = 1;
+
+ /**
+ * Turns SMS over IMS ON/OFF on the device.
+ * Value is in Integer format. ON (1), OFF(0).
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ * @hide
+ */
+ public static final int KEY_SMS_OVER_IP_ENABLED = 14;
+
+ /**
+ * An integer key associated with the carrier configured SIP PUBLISH timer, which dictates the
+ * expiration time in seconds for published online availability in RCS presence.
+ * <p>
+ * Value is in Integer format.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ * @hide
+ */
+ public static final int KEY_RCS_PUBLISH_TIMER_SEC = 15;
+
+ /**
+ * An integer key associated with the carrier configured expiration time in seconds for
+ * published offline availability in RCS presence provided, which is provided to the network.
+ * <p>
+ * Value is in Integer format.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ * @hide
+ */
+ public static final int KEY_RCS_PUBLISH_OFFLINE_AVAILABILITY_TIMER_SEC = 16;
+
+ /**
+ * An integer key associated with whether or not capability discovery is provisioned for this
+ * subscription. Any capability requests will be ignored by the RCS service.
+ * <p>
+ * The value is an integer, either {@link #PROVISIONING_VALUE_DISABLED} if capability
+ * discovery is disabled or {@link #PROVISIONING_VALUE_ENABLED} if capability discovery is
+ * enabled.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ * @hide
+ */
+ public static final int KEY_RCS_CAPABILITY_DISCOVERY_ENABLED = 17;
+
+ /**
+ * An integer key associated with the period of time in seconds the capability information of
+ * each contact is cached on the device.
+ * <p>
+ * Seconds are used because this is usually measured in the span of days.
+ * <p>
+ * Value is in Integer format.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ * @hide
+ */
+ public static final int KEY_RCS_CAPABILITIES_CACHE_EXPIRATION_SEC = 18;
+
+ /**
+ * An integer key associated with the period of time in seconds that the availability
+ * information of a contact is cached on the device, which is based on the carrier provisioning
+ * configuration from the network.
+ * <p>
+ * Value is in Integer format.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ * @hide
+ */
+ public static final int KEY_RCS_AVAILABILITY_CACHE_EXPIRATION_SEC = 19;
+
+ /**
+ * An integer key associated with the carrier configured interval in seconds expected between
+ * successive capability polling attempts, which is based on the carrier provisioning
+ * configuration from the network.
+ * <p>
+ * Value is in Integer format.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ * @hide
+ */
+ public static final int KEY_RCS_CAPABILITIES_POLL_INTERVAL_SEC = 20;
+
+ /**
+ * An integer key representing the minimum time allowed between two consecutive presence publish
+ * messages from the device in milliseconds.
+ * <p>
+ * Value is in Integer format.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ * @hide
+ */
+ public static final int KEY_RCS_PUBLISH_SOURCE_THROTTLE_MS = 21;
+
+ /**
+ * An integer key associated with the maximum number of MDNs contained in one SIP Request
+ * Contained List (RCS) used to retrieve the RCS capabilities of the contacts book.
+ * <p>
+ * Value is in Integer format.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ * @hide
+ */
+ public static final int KEY_RCS_MAX_NUM_ENTRIES_IN_RCL = 22;
+
+ /**
+ * An integer associated with the expiration timer used during the SIP subscription of a
+ * Request Contained List (RCL), which is used to retrieve the RCS capabilities of the contact
+ * book. This timer value is sent in seconds to the network.
+ * <p>
+ * Value is in Integer format.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ * @hide
+ */
+ public static final int KEY_RCS_CAPABILITY_POLL_LIST_SUB_EXP_SEC = 23;
+
+ /**
+ * Applies compression to LIST Subscription.
+ * Value is in Integer format. Enable (1), Disable(0).
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ * @hide
+ */
+ public static final int KEY_USE_GZIP_FOR_LIST_SUBSCRIPTION = 24;
+
+ /**
+ * An integer key representing the RCS enhanced address book (EAB) provisioning status for the
+ * associated subscription. Determines whether or not SIP OPTIONS or presence will be used to
+ * retrieve RCS capabilities for the user's contacts.
+ * <p>
+ * Use {@link #PROVISIONING_VALUE_ENABLED} to enable EAB provisioning and
+ * {@link #PROVISIONING_VALUE_DISABLED} to disable EAB provisioning.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ * @hide
+ */
+ public static final int KEY_EAB_PROVISIONING_STATUS = 25;
+
+ /**
+ * Override the user-defined WiFi Roaming enabled setting for this subscription, defined in
+ * {@link SubscriptionManager#WFC_ROAMING_ENABLED_CONTENT_URI}, for the purposes of provisioning
+ * the subscription for WiFi Calling.
+ *
+ * @see #getProvisioningIntValue(int)
+ * @see #setProvisioningIntValue(int, int)
+ */
+ public static final int KEY_VOICE_OVER_WIFI_ROAMING_ENABLED_OVERRIDE = 26;
+
+ /**
+ * Override the user-defined WiFi mode for this subscription, defined in
+ * {@link SubscriptionManager#WFC_MODE_CONTENT_URI}, for the purposes of provisioning
+ * this subscription for WiFi Calling.
+ *
+ * Valid values for this key are:
+ * {@link ImsMmTelManager#WIFI_MODE_WIFI_ONLY},
+ * {@link ImsMmTelManager#WIFI_MODE_CELLULAR_PREFERRED}, or
+ * {@link ImsMmTelManager#WIFI_MODE_WIFI_PREFERRED}.
+ *
+ * @see #getProvisioningIntValue(int)
+ * @see #setProvisioningIntValue(int, int)
+ */
+ public static final int KEY_VOICE_OVER_WIFI_MODE_OVERRIDE = 27;
+
+ /**
+ * Enable voice over wifi. Enabled (1), or Disabled (0).
+ * Value is in Integer format.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ * @hide
+ */
+ public static final int KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE = 28;
+
+ /**
+ * Mobile data enabled.
+ * Value is in Integer format. On (1), OFF(0).
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ * @hide
+ */
+ public static final int KEY_MOBILE_DATA_ENABLED = 29;
+
+ /**
+ * VoLTE user opted in status.
+ * Value is in Integer format. Opted-in (1) Opted-out (0).
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ * @hide
+ */
+ public static final int KEY_VOLTE_USER_OPT_IN_STATUS = 30;
+
+ /**
+ * Proxy for Call Session Control Function(P-CSCF) address for Local-BreakOut(LBO).
+ * Value is in String format.
+ * @hide
+ */
+ public static final int KEY_LOCAL_BREAKOUT_PCSCF_ADDRESS = 31;
+
+ /**
+ * Keep Alive Enabled for SIP.
+ * Value is in Integer format.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ * @hide
+ */
+ public static final int KEY_SIP_KEEP_ALIVE_ENABLED = 32;
+
+ /**
+ * Registration retry Base Time value in seconds, which is based off of the carrier
+ * configuration.
+ * Value is in Integer format.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ * @hide
+ */
+ public static final int KEY_REGISTRATION_RETRY_BASE_TIME_SEC = 33;
+
+ /**
+ * Registration retry Max Time value in seconds, which is based off of the carrier
+ * configuration.
+ * Value is in Integer format.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ * @hide
+ */
+ public static final int KEY_REGISTRATION_RETRY_MAX_TIME_SEC = 34;
+
+ /**
+ * Smallest RTP port for speech codec.
+ * Value is in integer format.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ * @hide
+ */
+
+ public static final int KEY_RTP_SPEECH_START_PORT = 35;
+
+ /**
+ * Largest RTP port for speech code.
+ * Value is in Integer format.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ * @hide
+ */
+ public static final int KEY_RTP_SPEECH_END_PORT = 36;
+
+ /**
+ * SIP Timer A's value in milliseconds. Timer A is the INVITE request retransmit interval (in
+ * milliseconds), for UDP only.
+ * Value is in Integer format.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ * @hide
+ */
+ public static final int KEY_SIP_INVITE_REQUEST_TRANSMIT_INTERVAL_MS = 37;
+
+ /**
+ * SIP Timer B's value in milliseconds. Timer B is the wait time for INVITE message to be,
+ * in milliseconds.
+ * Value is in Integer format.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ * @hide
+ */
+ public static final int KEY_SIP_INVITE_ACK_WAIT_TIME_MS = 38;
+
+ /**
+ * SIP Timer D's value in milliseconds. Timer D is the wait time for response retransmits of
+ * the invite client transactions, in milliseconds.
+ * Value is in Integer format.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ * @hide
+ */
+ public static final int KEY_SIP_INVITE_RESPONSE_RETRANSMIT_WAIT_TIME_MS = 39;
+
+ /**
+ * SIP Timer E's value in milliseconds. Timer E is the value Non-INVITE request retransmit
+ * interval (in milliseconds), for UDP only.
+ * Value is in Integer format.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ * @hide
+ */
+ public static final int KEY_SIP_NON_INVITE_REQUEST_RETRANSMIT_INTERVAL_MS = 40;
+
+ /**
+ * SIP Timer F's value in milliseconds. Timer F is the Non-INVITE transaction timeout timer,
+ * in milliseconds.
+ * Value is in Integer format.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ * @hide
+ */
+ public static final int KEY_SIP_NON_INVITE_TRANSACTION_TIMEOUT_TIMER_MS = 41;
+
+ /**
+ * SIP Timer G's value in milliseconds. Timer G is the value of INVITE response
+ * retransmit interval.
+ * Value is in Integer format.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ * @hide
+ */
+ public static final int KEY_SIP_INVITE_RESPONSE_RETRANSMIT_INTERVAL_MS = 42;
+
+ /**
+ * SIP Timer H's value in milliseconds. Timer H is the value of wait time for
+ * ACK receipt.
+ * Value is in Integer format.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ * @hide
+ */
+ public static final int KEY_SIP_ACK_RECEIPT_WAIT_TIME_MS = 43;
+
+ /**
+ * SIP Timer I's value in milliseconds. Timer I is the value of wait time for
+ * ACK retransmits.
+ * Value is in Integer format.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ * @hide
+ */
+ public static final int KEY_SIP_ACK_RETRANSMIT_WAIT_TIME_MS = 44;
+
+ /**
+ * SIP Timer J's value in milliseconds. Timer J is the value of wait time for
+ * non-invite request retransmission.
+ * Value is in Integer format.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ * @hide
+ */
+ public static final int KEY_SIP_NON_INVITE_REQUEST_RETRANSMISSION_WAIT_TIME_MS = 45;
+
+ /**
+ * SIP Timer K's value in milliseconds. Timer K is the value of wait time for
+ * non-invite response retransmits.
+ * Value is in Integer format.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ * @hide
+ */
+ public static final int KEY_SIP_NON_INVITE_RESPONSE_RETRANSMISSION_WAIT_TIME_MS = 46;
+
+ /**
+ * AMR WB octet aligned dynamic payload type.
+ * Value is in Integer format.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ * @hide
+ */
+ public static final int KEY_AMR_WB_OCTET_ALIGNED_PAYLOAD_TYPE = 47;
+
+ /**
+ * AMR WB bandwidth efficient payload type.
+ * Value is in Integer format.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ * @hide
+ */
+ public static final int KEY_AMR_WB_BANDWIDTH_EFFICIENT_PAYLOAD_TYPE = 48;
+
+ /**
+ * AMR octet aligned dynamic payload type.
+ * Value is in Integer format.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ * @hide
+ */
+ public static final int KEY_AMR_OCTET_ALIGNED_PAYLOAD_TYPE = 49;
+
+ /**
+ * AMR bandwidth efficient payload type.
+ * Value is in Integer format.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ * @hide
+ */
+ public static final int KEY_AMR_BANDWIDTH_EFFICIENT_PAYLOAD_TYPE = 50;
+
+ /**
+ * DTMF WB payload type.
+ * Value is in Integer format.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ * @hide
+ */
+ public static final int KEY_DTMF_WB_PAYLOAD_TYPE = 51;
+
+ /**
+ * DTMF NB payload type.
+ * Value is in Integer format.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ * @hide
+ */
+ public static final int KEY_DTMF_NB_PAYLOAD_TYPE = 52;
+
+ /**
+ * AMR Default encoding mode.
+ * Value is in Integer format.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ * @hide
+ */
+ public static final int KEY_AMR_DEFAULT_ENCODING_MODE = 53;
+
+ /**
+ * SMS Public Service Identity.
+ * Value is in String format.
+ * @hide
+ */
+ public static final int KEY_SMS_PUBLIC_SERVICE_IDENTITY = 54;
+
+ /**
+ * Video Quality - VideoQualityFeatureValuesConstants.
+ * Valid values are: {@link #VIDEO_QUALITY_HIGH} and {@link #VIDEO_QUALITY_LOW}.
+ * Value is in Integer format.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ * @hide
+ */
+ public static final int KEY_VIDEO_QUALITY = 55;
+
+ /**
+ * Used with {@link #KEY_VIDEO_QUALITY} to indicate low video quality.
+ * @hide
+ */
+ public static final int VIDEO_QUALITY_LOW = 0;
+
+ /**
+ * Used with {@link #KEY_VIDEO_QUALITY} to indicate high video quality.
+ * @hide
+ */
+ public static final int VIDEO_QUALITY_HIGH = 1;
+
+ /**
+ * LTE to WIFI handover threshold.
+ * Handover from LTE to WiFi if LTE < THLTE1 and WiFi >= {@link #KEY_WIFI_THRESHOLD_A}.
+ * Value is in Integer format.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ * @hide
+ */
+ public static final int KEY_LTE_THRESHOLD_1 = 56;
+
+ /**
+ * WIFI to LTE handover threshold.
+ * Handover from WiFi to LTE if LTE >= {@link #KEY_LTE_THRESHOLD_3} or (WiFi < {@link
+ * #KEY_WIFI_THRESHOLD_B} and LTE >= {@link #KEY_LTE_THRESHOLD_2}).
+ * Value is in Integer format.
+ *
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ * @hide
+ */
+ public static final int KEY_LTE_THRESHOLD_2 = 57;
+
+ /**
+ * LTE to WIFI handover threshold.
+ * Handover from WiFi to LTE if LTE >= {@link #KEY_LTE_THRESHOLD_3} or (WiFi < {@link
+ * #KEY_WIFI_THRESHOLD_B} and LTE >= {@link #KEY_LTE_THRESHOLD_2}).
+ * Value is in Integer format.
+ *
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ * @hide
+ */
+ public static final int KEY_LTE_THRESHOLD_3 = 58;
+
+ /**
+ * 1x to WIFI handover threshold.
+ * Handover from 1x to WiFi if 1x < {@link #KEY_1X_THRESHOLD}.
+ * Value is in Integer format.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ * @hide
+ */
+ public static final int KEY_1X_THRESHOLD = 59;
+
+ /**
+ * LTE to WIFI threshold A.
+ * Handover from LTE to WiFi if LTE < {@link #KEY_LTE_THRESHOLD_1} and WiFi >= {@link
+ * #KEY_WIFI_THRESHOLD_A}.
+ * Value is in Integer format.
+ *
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ * @hide
+ */
+ public static final int KEY_WIFI_THRESHOLD_A = 60;
+
+ /**
+ * WiFi to LTRE handover threshold B.
+ * Handover from WiFi to LTE if LTE >= {@link #KEY_LTE_THRESHOLD_3} or (WiFi <
+ * {@link #KEY_WIFI_THRESHOLD_B} and LTE >= {@link #KEY_LTE_THRESHOLD_2}).
+ * Value is in Integer format.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ * @hide
+ */
+ public static final int KEY_WIFI_THRESHOLD_B = 61;
+
+ /**
+ * LTE ePDG timer (in seconds).
+ * Device shall not handover back to LTE until the T_ePDG_LTE timer expires.
+ * Value is in Integer format.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ * @hide
+ */
+ public static final int KEY_LTE_EPDG_TIMER_SEC = 62;
+
+ /**
+ * WiFi ePDG timer (in seconds).
+ * Device shall not handover back to WiFi until the T_ePDG_WiFi timer expires.
+ * Value is in Integer format.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ * @hide
+ */
+ public static final int KEY_WIFI_EPDG_TIMER_SEC = 63;
+
+ /**
+ * 1x ePDG timer (in seconds).
+ * Device shall not re-register on 1x until the T_ePDG_1x timer expires.
+ * @hide
+ */
+ public static final int KEY_1X_EPDG_TIMER_SEC = 64;
+
+ /**
+ * MultiEndpoint status: Enabled (1), or Disabled (0).
+ * Value is in Integer format.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ * @hide
+ */
+ public static final int KEY_MULTIENDPOINT_ENABLED = 65;
+
+ /**
+ * RTT status: Enabled (1), or Disabled (0).
+ * Value is in Integer format.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ * @hide
+ */
+ public static final int KEY_RTT_ENABLED = 66;
+
+ /**
+ * Callback for IMS provisioning changes.
+ */
+ public static class Callback {
+
+ private static class CallbackBinder extends IImsConfigCallback.Stub {
+
+ private final Callback mLocalConfigurationCallback;
+ private Executor mExecutor;
+
+ private CallbackBinder(Callback localConfigurationCallback) {
+ mLocalConfigurationCallback = localConfigurationCallback;
+ }
+
+ @Override
+ public final void onIntConfigChanged(int item, int value) {
+ long callingIdentity = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() ->
+ mLocalConfigurationCallback.onProvisioningIntChanged(item, value));
+ } finally {
+ restoreCallingIdentity(callingIdentity);
+ }
+ }
+
+ @Override
+ public final void onStringConfigChanged(int item, String value) {
+ long callingIdentity = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() ->
+ mLocalConfigurationCallback.onProvisioningStringChanged(item, value));
+ } finally {
+ restoreCallingIdentity(callingIdentity);
+ }
+ }
+
+ private void setExecutor(Executor executor) {
+ mExecutor = executor;
+ }
+ }
+
+ private final CallbackBinder mBinder = new CallbackBinder(this);
+
+ /**
+ * Called when a provisioning item has changed.
+ * @param item the IMS provisioning key constant, as defined by the OEM.
+ * @param value the new integer value of the IMS provisioning key.
+ */
+ public void onProvisioningIntChanged(int item, int value) {
+ // Base Implementation
+ }
+
+ /**
+ * Called when a provisioning item has changed.
+ * @param item the IMS provisioning key constant, as defined by the OEM.
+ * @param value the new String value of the IMS configuration constant.
+ */
+ public void onProvisioningStringChanged(int item, @NonNull String value) {
+ // Base Implementation
+ }
+
+ /**@hide*/
+ public final IImsConfigCallback getBinder() {
+ return mBinder;
+ }
+
+ /**@hide*/
+ public void setExecutor(Executor executor) {
+ mBinder.setExecutor(executor);
+ }
+ }
+
+ private int mSubId;
+
+ /**
+ * Create a new {@link ProvisioningManager} for the subscription specified.
+ *
+ * @param subId The ID of the subscription that this ProvisioningManager will use.
+ * @see android.telephony.SubscriptionManager#getActiveSubscriptionInfoList()
+ * @throws IllegalArgumentException if the subscription is invalid.
+ */
+ public static @NonNull ProvisioningManager createForSubscriptionId(int subId) {
+ if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+ throw new IllegalArgumentException("Invalid subscription ID");
+ }
+
+ return new ProvisioningManager(subId);
+ }
+
+ private ProvisioningManager(int subId) {
+ mSubId = subId;
+ }
+
+ /**
+ * Register a new {@link Callback} to listen to changes to changes in IMS provisioning.
+ *
+ * When the subscription associated with this callback is removed (SIM removed, ESIM swap,
+ * etc...), this callback will automatically be removed.
+ * @param executor The {@link Executor} to call the callback methods on
+ * @param callback The provisioning callbackto be registered.
+ * @see #unregisterProvisioningChangedCallback(Callback)
+ * @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. See {@link ImsException#getCode()} for a more detailed
+ * reason.
+ */
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public void registerProvisioningChangedCallback(@NonNull @CallbackExecutor Executor executor,
+ @NonNull Callback callback) throws ImsException {
+ callback.setExecutor(executor);
+ try {
+ getITelephony().registerImsProvisioningChangedCallback(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 Callback}. 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 callback The existing {@link Callback} to be removed.
+ * @see #registerProvisioningChangedCallback(Executor, Callback)
+ *
+ * @throws IllegalArgumentException if the subscription associated with this callback is
+ * invalid.
+ */
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public void unregisterProvisioningChangedCallback(@NonNull Callback callback) {
+ try {
+ getITelephony().unregisterImsProvisioningChangedCallback(mSubId, callback.getBinder());
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Query for the integer value associated with the provided key.
+ *
+ * This operation is blocking and should not be performed on the UI thread.
+ *
+ * @param key An integer that represents the provisioning key, which is defined by the OEM.
+ * @return an integer value for the provided key, or
+ * {@link ImsConfigImplBase#CONFIG_RESULT_UNKNOWN} if the key doesn't exist.
+ * @throws IllegalArgumentException if the key provided was invalid.
+ */
+ @WorkerThread
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public int getProvisioningIntValue(int key) {
+ try {
+ return getITelephony().getImsProvisioningInt(mSubId, key);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Query for the String value associated with the provided key.
+ *
+ * This operation is blocking and should not be performed on the UI thread.
+ *
+ * @param key A String that represents the provisioning key, which is defined by the OEM.
+ * @return a String value for the provided key, {@code null} if the key doesn't exist, or
+ * {@link StringResultError} if there was an error getting the value for the provided key.
+ * @throws IllegalArgumentException if the key provided was invalid.
+ */
+ @WorkerThread
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public @Nullable @StringResultError String getProvisioningStringValue(int key) {
+ try {
+ return getITelephony().getImsProvisioningString(mSubId, key);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Set the integer value associated with the provided key.
+ *
+ * This operation is blocking and should not be performed on the UI thread.
+ *
+ * Use {@link #setProvisioningStringValue(int, String)} with proper namespacing (to be defined
+ * per OEM or carrier) when possible instead to avoid key collision if needed.
+ * @param key An integer that represents the provisioning key, which is defined by the OEM.
+ * @param value a integer value for the provided key.
+ * @return the result of setting the configuration value.
+ */
+ @WorkerThread
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ public @ImsConfigImplBase.SetConfigResult int setProvisioningIntValue(int key, int value) {
+ try {
+ return getITelephony().setImsProvisioningInt(mSubId, key, value);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Set the String value associated with the provided key.
+ *
+ * This operation is blocking and should not be performed on the UI thread.
+ *
+ * @param key A String that represents the provisioning key, which is defined by the OEM and
+ * should be appropriately namespaced to avoid collision.
+ * @param value a String value for the provided key.
+ * @return the result of setting the configuration value.
+ */
+ @WorkerThread
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ public @ImsConfigImplBase.SetConfigResult int setProvisioningStringValue(int key,
+ @NonNull String value) {
+ try {
+ return getITelephony().setImsProvisioningString(mSubId, key, value);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Set the provisioning status for the IMS MmTel capability using the specified subscription.
+ *
+ * Provisioning may or may not be required, depending on the carrier configuration. If
+ * provisioning is not required for the carrier associated with this subscription or the device
+ * does not support the capability/technology combination specified, this operation will be a
+ * no-op.
+ *
+ * @see CarrierConfigManager#KEY_CARRIER_UT_PROVISIONING_REQUIRED_BOOL
+ * @see CarrierConfigManager#KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL
+ * @param isProvisioned true if the device is provisioned for UT over IMS, false otherwise.
+ */
+ @WorkerThread
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ public void setProvisioningStatusForCapability(
+ @MmTelFeature.MmTelCapabilities.MmTelCapability int capability,
+ @ImsRegistrationImplBase.ImsRegistrationTech int tech, boolean isProvisioned) {
+ try {
+ getITelephony().setImsProvisioningStatusForCapability(mSubId, capability, tech,
+ isProvisioned);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Get the provisioning status for the IMS MmTel capability specified.
+ *
+ * If provisioning is not required for the queried
+ * {@link MmTelFeature.MmTelCapabilities.MmTelCapability} and
+ * {@link ImsRegistrationImplBase.ImsRegistrationTech} combination specified, this method will
+ * always return {@code true}.
+ *
+ * @see CarrierConfigManager#KEY_CARRIER_UT_PROVISIONING_REQUIRED_BOOL
+ * @see CarrierConfigManager#KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL
+ * @return true if the device is provisioned for the capability or does not require
+ * provisioning, false if the capability does require provisioning and has not been
+ * provisioned yet.
+ */
+ @WorkerThread
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public boolean getProvisioningStatusForCapability(
+ @MmTelFeature.MmTelCapabilities.MmTelCapability int capability,
+ @ImsRegistrationImplBase.ImsRegistrationTech int tech) {
+ try {
+ return getITelephony().getImsProvisioningStatusForCapability(mSubId, capability, tech);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Get the provisioning status for the IMS RCS capability specified.
+ *
+ * If provisioning is not required for the queried
+ * {@link RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag} this method will always return
+ * {@code true}.
+ *
+ * @see CarrierConfigManager#KEY_CARRIER_RCS_PROVISIONING_REQUIRED_BOOL
+ * @return true if the device is provisioned for the capability or does not require
+ * provisioning, false if the capability does require provisioning and has not been
+ * provisioned yet.
+ */
+ @WorkerThread
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public boolean getRcsProvisioningStatusForCapability(
+ @RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability) {
+ try {
+ return getITelephony().getRcsProvisioningStatusForCapability(mSubId, capability);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Set the provisioning status for the IMS RCS capability using the specified subscription.
+ *
+ * Provisioning may or may not be required, depending on the carrier configuration. If
+ * provisioning is not required for the carrier associated with this subscription or the device
+ * does not support the capability/technology combination specified, this operation will be a
+ * no-op.
+ *
+ * @see CarrierConfigManager#KEY_CARRIER_RCS_PROVISIONING_REQUIRED_BOOL
+ * @param isProvisioned true if the device is provisioned for the RCS capability specified,
+ * false otherwise.
+ */
+ @WorkerThread
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ public void setRcsProvisioningStatusForCapability(
+ @RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability,
+ boolean isProvisioned) {
+ try {
+ getITelephony().setRcsProvisioningStatusForCapability(mSubId, capability,
+ isProvisioned);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Notify the framework that an RCS autoconfiguration XML file has been received for
+ * provisioning.
+ * <p>
+ * Requires Permission: Manifest.permission.MODIFY_PHONE_STATE or that the calling app has
+ * carrier privileges (see {@link #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.
+ *
+ */
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ public void notifyRcsAutoConfigurationReceived(@NonNull byte[] config, boolean isCompressed) {
+ if (config == null) {
+ throw new IllegalArgumentException("Must include a non-null config XML file.");
+ }
+ try {
+ getITelephony().notifyRcsAutoConfigurationReceived(mSubId, config, isCompressed);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+
+ }
+
+ private static ITelephony getITelephony() {
+ ITelephony binder = ITelephony.Stub.asInterface(
+ TelephonyFrameworkInitializer
+ .getTelephonyServiceManager()
+ .getTelephonyServiceRegisterer()
+ .get());
+ if (binder == null) {
+ throw new RuntimeException("Could not find Telephony Service.");
+ }
+ return binder;
+ }
+}
diff --git a/android/telephony/ims/RcsContactUceCapability.java b/android/telephony/ims/RcsContactUceCapability.java
new file mode 100644
index 0000000..dc36edf
--- /dev/null
+++ b/android/telephony/ims/RcsContactUceCapability.java
@@ -0,0 +1,320 @@
+/*
+ * 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.LongDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.Uri;
+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.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Contains the User Capability Exchange capabilities corresponding to a contact's URI.
+ * @hide
+ */
+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);
+ /**
+ * Supports file transfer via Message Session Relay Protocol (MSRP) without Store and Forward.
+ */
+ 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*/
+ @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
+ })
+ public @interface CapabilityFlag {}
+
+ /**
+ * Builder to help construct {@link RcsContactUceCapability} instances.
+ */
+ public static class Builder {
+
+ 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.
+ */
+ public Builder(@NonNull Uri contact) {
+ mCapabilities = new RcsContactUceCapability(contact);
+ }
+
+ /**
+ * 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.
+ */
+ 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;
+ }
+ }
+ return this;
+ }
+
+ /**
+ * Add a UCE capability flag that this contact supports.
+ * @param type the capability that the contact supports.
+ */
+ public @NonNull Builder add(@CapabilityFlag long type) {
+ mCapabilities.mCapabilities |= type;
+ 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.
+ */
+ public @NonNull Builder add(@NonNull String extension) {
+ mCapabilities.mExtensionTags.add(extension);
+ return this;
+ }
+
+ /**
+ * @return the constructed instance.
+ */
+ public @NonNull RcsContactUceCapability build() {
+ return mCapabilities;
+ }
+ }
+
+ 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
+ */
+ RcsContactUceCapability(@NonNull Uri contact) {
+ mContactUri = contact;
+ }
+
+ 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()));
+ }
+ }
+
+ 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];
+ }
+ };
+
+ @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);
+ }
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * Query for a capability
+ * @param type The capability flag to query.
+ * @return true if the capability flag specified is set, false otherwise.
+ */
+ public boolean isCapable(@CapabilityFlag long type) {
+ return (mCapabilities & type) > 0;
+ }
+
+ /**
+ * @return true if the extension service tag is set, false otherwise.
+ */
+ public boolean isCapable(@NonNull String extensionTag) {
+ return mExtensionTags.contains(extensionTag);
+ }
+
+ /**
+ * @return An immutable list containing all of the extension tags that have been set as capable.
+ * @throws UnsupportedOperationException if this list is modified.
+ */
+ public @NonNull List<String> getCapableExtensionTags() {
+ return Collections.unmodifiableList(mExtensionTags);
+ }
+
+ /**
+ * 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)}.
+ *
+ * @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)
+ */
+ 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;
+ }
+ return result;
+ }
+
+ /**
+ * @return the URI representing the contact associated with the capabilities.
+ */
+ public @NonNull Uri getContactUri() {
+ return mContactUri;
+ }
+}
diff --git a/android/telephony/ims/RcsUceAdapter.java b/android/telephony/ims/RcsUceAdapter.java
new file mode 100644
index 0000000..05ab6bd
--- /dev/null
+++ b/android/telephony/ims/RcsUceAdapter.java
@@ -0,0 +1,408 @@
+/*
+ * 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.Manifest;
+import android.annotation.CallbackExecutor;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.content.Context;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.telephony.TelephonyFrameworkInitializer;
+import android.telephony.ims.aidl.IImsRcsController;
+import android.telephony.ims.aidl.IRcsUceControllerCallback;
+import android.telephony.ims.feature.RcsFeature;
+import android.util.Log;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.List;
+import java.util.concurrent.Executor;
+
+/**
+ * Manages RCS User Capability Exchange for the subscription specified.
+ *
+ * @see ImsRcsManager#getUceAdapter() for information on creating an instance of this class.
+ */
+public class RcsUceAdapter {
+ private static final String TAG = "RcsUceAdapter";
+
+ /**
+ * An unknown error has caused the request to fail.
+ * @hide
+ */
+ public static final int ERROR_GENERIC_FAILURE = 1;
+ /**
+ * The carrier network does not have UCE support enabled for this subscriber.
+ * @hide
+ */
+ 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
+ */
+ public static final int ERROR_NOT_AVAILABLE = 3;
+ /**
+ * The network has responded with SIP 403 error and a reason "User not registered."
+ * @hide
+ */
+ 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
+ */
+ public static final int ERROR_NOT_AUTHORIZED = 5;
+ /**
+ * The network has responded to this request with a SIP 403 error and no reason.
+ * @hide
+ */
+ public static final int ERROR_FORBIDDEN = 6;
+ /**
+ * The contact URI requested is not provisioned for VoLTE or it is not known as an IMS
+ * subscriber to the carrier network.
+ * @hide
+ */
+ 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
+ */
+ // 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;
+ /**
+ * The request failed due to the service having insufficient memory.
+ * @hide
+ */
+ public static final int ERROR_INSUFFICIENT_MEMORY = 11;
+ /**
+ * The network was lost while trying to complete the request.
+ * @hide
+ */
+ public static final int ERROR_LOST_NETWORK = 12;
+ /**
+ * The request has failed because the same request has already been added to the queue.
+ * @hide
+ */
+ public static final int ERROR_ALREADY_IN_QUEUE = 13;
+
+ /**@hide*/
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = "ERROR_", value = {
+ ERROR_GENERIC_FAILURE,
+ ERROR_NOT_ENABLED,
+ ERROR_NOT_AVAILABLE,
+ ERROR_NOT_REGISTERED,
+ ERROR_NOT_AUTHORIZED,
+ ERROR_FORBIDDEN,
+ ERROR_NOT_FOUND,
+ ERROR_REQUEST_TOO_LARGE,
+ ERROR_REQUEST_TIMEOUT,
+ ERROR_INSUFFICIENT_MEMORY,
+ ERROR_LOST_NETWORK,
+ ERROR_ALREADY_IN_QUEUE
+ })
+ public @interface ErrorCode {}
+
+ /**
+ * The last publish has resulted in a "200 OK" response or the device is using SIP OPTIONS for
+ * UCE.
+ * @hide
+ */
+ 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
+ */
+ 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.
+ * @hide
+ */
+ public static final int PUBLISH_STATE_VOLTE_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
+ */
+ public static final int PUBLISH_STATE_RCS_PROVISION_ERROR = 4;
+
+ /**
+ * The last publish resulted in a "408 Request Timeout" response.
+ * @hide
+ */
+ public static final int PUBLISH_STATE_REQUEST_TIMEOUT = 5;
+
+ /**
+ * The last publish resulted in another unknown error, such as SIP 503 - "Service Unavailable"
+ * or SIP 423 - "Interval too short".
+ * <p>
+ * Device shall retry with exponential back-off.
+ * @hide
+ */
+ public static final int PUBLISH_STATE_OTHER_ERROR = 6;
+
+ /**@hide*/
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = "PUBLISH_STATE_", value = {
+ PUBLISH_STATE_OK,
+ PUBLISH_STATE_NOT_PUBLISHED,
+ PUBLISH_STATE_VOLTE_PROVISION_ERROR,
+ PUBLISH_STATE_RCS_PROVISION_ERROR,
+ PUBLISH_STATE_REQUEST_TIMEOUT,
+ PUBLISH_STATE_OTHER_ERROR
+ })
+ public @interface PublishState {}
+
+
+ /**
+ * 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.
+ * @see #requestCapabilities(Executor, List, CapabilitiesCallback)
+ * @hide
+ */
+ public static class CapabilitiesCallback {
+
+ /**
+ * Notify this application that the pending capability request has returned successfully.
+ * @param contactCapabilities List of capabilities associated with each contact requested.
+ */
+ public void onCapabilitiesReceived(
+ @NonNull List<RcsContactUceCapability> contactCapabilities) {
+
+ }
+
+ /**
+ * 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.
+ */
+ public void onError(@ErrorCode int errorCode) {
+
+ }
+ }
+
+ private final Context mContext;
+ private final int mSubId;
+
+ /**
+ * Not to be instantiated directly, use
+ * {@link ImsRcsManager#getUceAdapter()} to instantiate this manager class.
+ * @hide
+ */
+ RcsUceAdapter(Context context, int subId) {
+ mContext = context;
+ mSubId = subId;
+ }
+
+ /**
+ * Request the User Capability Exchange capabilities for one or more contacts.
+ * <p>
+ * Be sure to check the availability of this feature using
+ * {@link ImsRcsManager#isAvailable(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 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
+ * {@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
+ */
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public void requestCapabilities(@NonNull @CallbackExecutor Executor executor,
+ @NonNull List<Uri> contactNumbers,
+ @NonNull CapabilitiesCallback c) throws ImsException {
+ if (c == null) {
+ throw new IllegalArgumentException("Must include a non-null AvailabilityCallback.");
+ }
+ if (executor == null) {
+ throw new IllegalArgumentException("Must include a non-null Executor.");
+ }
+ if (contactNumbers == null) {
+ throw new IllegalArgumentException("Must include non-null contact number list.");
+ }
+
+ IImsRcsController imsRcsController = getIImsRcsController();
+ if (imsRcsController == null) {
+ Log.e(TAG, "requestCapabilities: IImsRcsController is null");
+ throw new ImsException("Can not find remote IMS service",
+ ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ }
+
+ IRcsUceControllerCallback internalCallback = new IRcsUceControllerCallback.Stub() {
+ @Override
+ public void onCapabilitiesReceived(List<RcsContactUceCapability> contactCapabilities) {
+ long callingIdentity = Binder.clearCallingIdentity();
+ try {
+ executor.execute(() ->
+ c.onCapabilitiesReceived(contactCapabilities));
+ } finally {
+ restoreCallingIdentity(callingIdentity);
+ }
+ }
+ @Override
+ public void onError(int errorCode) {
+ long callingIdentity = Binder.clearCallingIdentity();
+ try {
+ executor.execute(() -> c.onError(errorCode));
+ } finally {
+ restoreCallingIdentity(callingIdentity);
+ }
+ }
+ };
+
+ try {
+ imsRcsController.requestCapabilities(mSubId, mContext.getOpPackageName(),
+ mContext.getAttributionTag(), contactNumbers, internalCallback);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling IImsRcsController#requestCapabilities", 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,
+ * this method will return {@link #PUBLISH_STATE_OK} as well.
+ * @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
+ */
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public @PublishState int getUcePublishState() throws ImsException {
+ IImsRcsController imsRcsController = getIImsRcsController();
+ if (imsRcsController == null) {
+ Log.e(TAG, "getUcePublishState: IImsRcsController is null");
+ throw new ImsException("Can not find remote IMS service",
+ ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ }
+
+ try {
+ return imsRcsController.getUcePublishState(mSubId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling IImsRcsController#getUcePublishState", 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>
+ * Note: This setting does not affect whether or not the device publishes its service
+ * capabilities if the subscription supports presence publication.
+ *
+ * @return true if the user’s setting for UCE is enabled, false otherwise.
+ * @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.
+ */
+ @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
+ public boolean isUceSettingEnabled() throws ImsException {
+ IImsRcsController imsRcsController = getIImsRcsController();
+ if (imsRcsController == null) {
+ Log.e(TAG, "isUceSettingEnabled: IImsRcsController is null");
+ throw new ImsException("Can not find remote IMS service",
+ ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ }
+ try {
+ // Telephony.SimInfo#IMS_RCS_UCE_ENABLED can also be used to listen to changes to this.
+ return imsRcsController.isUceSettingEnabled(mSubId, mContext.getOpPackageName(),
+ mContext.getAttributionTag());
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling IImsRcsController#isUceSettingEnabled", e);
+ throw new ImsException("Remote IMS Service is not available",
+ ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ }
+ }
+
+ /**
+ * Change the user’s setting for whether or not UCE is enabled for the associated subscription.
+ * <p>
+ * If an application Requires UCE, they may launch an Activity using the Intent
+ * {@link ImsRcsManager#ACTION_SHOW_CAPABILITY_DISCOVERY_OPT_IN}, which will ask the user if
+ * they wish to enable this feature.
+ * <p>
+ * Note: This setting does not affect whether or not the device publishes its service
+ * capabilities if the subscription supports presence publication.
+ *
+ * @param isEnabled the user's setting for whether or not they wish for User
+ * Capability Exchange to be enabled.
+ * @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
+ @TestApi
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ public void setUceSettingEnabled(boolean isEnabled) throws ImsException {
+ IImsRcsController imsRcsController = getIImsRcsController();
+ if (imsRcsController == null) {
+ Log.e(TAG, "setUceSettingEnabled: IImsRcsController is null");
+ throw new ImsException("Can not find remote IMS service",
+ ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ }
+
+ try {
+ imsRcsController.setUceSettingEnabled(mSubId, isEnabled);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling IImsRcsController#setUceSettingEnabled", e);
+ throw new ImsException("Remote IMS Service is not available",
+ ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ }
+ }
+
+ private IImsRcsController getIImsRcsController() {
+ IBinder binder = TelephonyFrameworkInitializer
+ .getTelephonyServiceManager()
+ .getTelephonyImsServiceRegisterer()
+ .get();
+ return IImsRcsController.Stub.asInterface(binder);
+ }
+}
diff --git a/android/telephony/ims/RegistrationManager.java b/android/telephony/ims/RegistrationManager.java
new file mode 100644
index 0000000..e085dec
--- /dev/null
+++ b/android/telephony/ims/RegistrationManager.java
@@ -0,0 +1,306 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims;
+
+import android.Manifest;
+import android.annotation.CallbackExecutor;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.net.Uri;
+import android.os.Binder;
+import android.telephony.AccessNetworkConstants;
+import android.telephony.ims.aidl.IImsRegistrationCallback;
+import android.telephony.ims.feature.ImsFeature;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
+import android.util.Log;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
+
+/**
+ * Manages IMS Service registration state for associated {@link ImsFeature}s.
+ */
+public interface RegistrationManager {
+
+ /**
+ * @hide
+ */
+ // Defines the underlying radio technology type that we have registered for IMS over.
+ @IntDef(prefix = "REGISTRATION_STATE_",
+ value = {
+ REGISTRATION_STATE_NOT_REGISTERED,
+ REGISTRATION_STATE_REGISTERING,
+ REGISTRATION_STATE_REGISTERED
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ImsRegistrationState {}
+
+ /**
+ * The IMS service is currently not registered to the carrier network.
+ */
+ int REGISTRATION_STATE_NOT_REGISTERED = 0;
+
+ /**
+ * The IMS service is currently in the process of registering to the carrier network.
+ */
+ int REGISTRATION_STATE_REGISTERING = 1;
+
+ /**
+ * The IMS service is currently registered to the carrier network.
+ */
+ int REGISTRATION_STATE_REGISTERED = 2;
+
+
+ /**@hide*/
+ // Translate ImsRegistrationImplBase API to new AccessNetworkConstant because WLAN
+ // and WWAN are more accurate constants.
+ Map<Integer, Integer> IMS_REG_TO_ACCESS_TYPE_MAP =
+ 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_LTE,
+ AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+ put(ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN,
+ AccessNetworkConstants.TRANSPORT_TYPE_WLAN);
+ }};
+
+ /**
+ * Callback class for receiving IMS network Registration callback events.
+ * @see #registerImsRegistrationCallback(Executor, RegistrationCallback)
+ * @see #unregisterImsRegistrationCallback(RegistrationCallback)
+ */
+ class RegistrationCallback {
+
+ private static class RegistrationBinder extends IImsRegistrationCallback.Stub {
+
+ private final RegistrationCallback mLocalCallback;
+ private Executor mExecutor;
+
+ RegistrationBinder(RegistrationCallback localCallback) {
+ mLocalCallback = localCallback;
+ }
+
+ @Override
+ public void onRegistered(int imsRadioTech) {
+ if (mLocalCallback == null) return;
+
+ long callingIdentity = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() ->
+ mLocalCallback.onRegistered(getAccessType(imsRadioTech)));
+ } finally {
+ restoreCallingIdentity(callingIdentity);
+ }
+ }
+
+ @Override
+ public void onRegistering(int imsRadioTech) {
+ if (mLocalCallback == null) return;
+
+ long callingIdentity = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() ->
+ mLocalCallback.onRegistering(getAccessType(imsRadioTech)));
+ } finally {
+ restoreCallingIdentity(callingIdentity);
+ }
+ }
+
+ @Override
+ public void onDeregistered(ImsReasonInfo info) {
+ if (mLocalCallback == null) return;
+
+ long callingIdentity = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> mLocalCallback.onUnregistered(info));
+ } finally {
+ restoreCallingIdentity(callingIdentity);
+ }
+ }
+
+ @Override
+ public void onTechnologyChangeFailed(int imsRadioTech, ImsReasonInfo info) {
+ if (mLocalCallback == null) return;
+
+ long callingIdentity = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> mLocalCallback.onTechnologyChangeFailed(
+ getAccessType(imsRadioTech), info));
+ } finally {
+ restoreCallingIdentity(callingIdentity);
+ }
+ }
+
+ public void onSubscriberAssociatedUriChanged(Uri[] uris) {
+ if (mLocalCallback == null) return;
+
+ long callingIdentity = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> mLocalCallback.onSubscriberAssociatedUriChanged(uris));
+ } finally {
+ restoreCallingIdentity(callingIdentity);
+ }
+ }
+
+ 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);
+
+ /**
+ * Notifies the framework when the IMS Provider is registered to the IMS network.
+ *
+ * @param imsTransportType the radio access technology.
+ */
+ public void onRegistered(@AccessNetworkConstants.TransportType int imsTransportType) {
+ }
+
+ /**
+ * Notifies the framework when the IMS Provider is trying to register the IMS network.
+ *
+ * @param imsTransportType the radio access technology.
+ */
+ public void onRegistering(@AccessNetworkConstants.TransportType int imsTransportType) {
+ }
+
+ /**
+ * Notifies the framework when the IMS Provider is unregistered from the IMS network.
+ *
+ * @param info the {@link ImsReasonInfo} associated with why registration was disconnected.
+ */
+ public void onUnregistered(@NonNull ImsReasonInfo info) {
+ }
+
+ /**
+ * A failure has occurred when trying to handover registration to another technology type.
+ *
+ * @param imsTransportType The transport type that has failed to handover registration to.
+ * @param info A {@link ImsReasonInfo} that identifies the reason for failure.
+ */
+ public void onTechnologyChangeFailed(
+ @AccessNetworkConstants.TransportType int imsTransportType,
+ @NonNull ImsReasonInfo info) {
+ }
+
+ /**
+ * Returns a list of subscriber {@link Uri}s associated with this IMS subscription when
+ * it changes. Per RFC3455, an associated URI is a URI that the service provider has
+ * allocated to a user for their own usage. A user's phone number is typically one of the
+ * associated URIs.
+ * @param uris new array of subscriber {@link Uri}s that are associated with this IMS
+ * subscription.
+ * @hide
+ */
+ public void onSubscriberAssociatedUriChanged(@Nullable Uri[] uris) {
+ }
+
+ /**@hide*/
+ public final IImsRegistrationCallback getBinder() {
+ return mBinder;
+ }
+
+ /**@hide*/
+ //Only exposed as public for compatibility with deprecated ImsManager APIs.
+ public void setExecutor(Executor executor) {
+ mBinder.setExecutor(executor);
+ }
+ }
+
+ /**
+ * Registers a {@link RegistrationCallback} with the system. Use
+ * @param executor The {@link Executor} that will be used to call the IMS registration state
+ * callback.
+ * @param c A callback called on the supplied {@link Executor} that will contain the
+ * registration state of the IMS service, which will be one of the
+ * {@see SubscriptionManager.OnSubscriptionsChangedListener} to listen to Subscription changed
+ * events and call {@link #unregisterImsRegistrationCallback(RegistrationCallback)} to clean up.
+ *
+ * When the callback is registered, it will initiate the callback c to be called with the
+ * current registration state.
+ *
+ * @param executor The executor the callback events should be run on.
+ * @param c The {@link RegistrationCallback} to be added.
+ * @see #unregisterImsRegistrationCallback(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.
+ */
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ void registerImsRegistrationCallback(@NonNull @CallbackExecutor Executor executor,
+ @NonNull RegistrationCallback c) throws ImsException;
+
+ /**
+ * Removes an existing {@link 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.
+ *
+ * @param c The {@link RegistrationCallback} to be removed.
+ * @see android.telephony.SubscriptionManager.OnSubscriptionsChangedListener
+ * @see #registerImsRegistrationCallback(Executor, RegistrationCallback)
+ */
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ void unregisterImsRegistrationCallback(@NonNull RegistrationCallback c);
+
+ /**
+ * Gets the registration state of the IMS service.
+ * @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 #REGISTRATION_STATE_NOT_REGISTERED},
+ * {@link #REGISTRATION_STATE_REGISTERING}, or
+ * {@link #REGISTRATION_STATE_REGISTERED}.
+ */
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ void getRegistrationState(@NonNull @CallbackExecutor Executor executor,
+ @NonNull @ImsRegistrationState Consumer<Integer> stateCallback);
+
+ /**
+ * Gets the Transport Type associated with the current IMS registration.
+ * @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)
+ void getRegistrationTransportType(
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull @AccessNetworkConstants.TransportType Consumer<Integer> transportTypeCallback);
+}
diff --git a/android/telephony/ims/compat/ImsService.java b/android/telephony/ims/compat/ImsService.java
new file mode 100644
index 0000000..eafbb14
--- /dev/null
+++ b/android/telephony/ims/compat/ImsService.java
@@ -0,0 +1,243 @@
+/*
+ * 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.compat;
+
+import android.annotation.Nullable;
+import android.app.Service;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.Intent;
+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;
+import android.telephony.ims.compat.feature.RcsFeature;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.ims.internal.IImsFeatureStatusCallback;
+import com.android.ims.internal.IImsMMTelFeature;
+import com.android.ims.internal.IImsRcsFeature;
+import com.android.ims.internal.IImsServiceController;
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * 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.
+ * First, the application must declare that they use the "android.permission.BIND_IMS_SERVICE"
+ * permission. Then, the ImsService definition in the manifest must follow the following format:
+ *
+ * ...
+ * <service android:name=".EgImsService"
+ * android:permission="android.permission.BIND_IMS_SERVICE" >
+ * <!-- Apps must declare which features they support as metadata. The different categories are
+ * defined below. In this example, the RCS_FEATURE feature is supported. -->
+ * <meta-data android:name="android.telephony.ims.RCS_FEATURE" android:value="true" />
+ * <intent-filter>
+ * <action android:name="android.telephony.ims.compat.ImsService" />
+ * </intent-filter>
+ * </service>
+ * ...
+ *
+ * The telephony framework will then bind to the ImsService you have defined in your manifest
+ * if you are either:
+ * 1) Defined as the default ImsService for the device in the device overlay using
+ * "config_ims_package".
+ * 2) Defined as a Carrier Provided ImsService in the Carrier Configuration using
+ * {@link CarrierConfigManager#KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING}.
+ *
+ * The features that are currently supported in an ImsService are:
+ * - RCS_FEATURE: This ImsService implements the RcsFeature class.
+ * - MMTEL_FEATURE: This ImsService implements the MMTelFeature class.
+ * - EMERGENCY_MMTEL_FEATURE: This ImsService implements the MMTelFeature class and will be
+ * available to place emergency calls at all times. This MUST be implemented by the default
+ * ImsService provided in the device overlay.
+ * @hide
+ */
+public class ImsService extends Service {
+
+ private static final String LOG_TAG = "ImsService(Compat)";
+
+ /**
+ * The intent that must be defined as an intent-filter in the AndroidManifest of the ImsService.
+ * @hide
+ */
+ public static final String SERVICE_INTERFACE = "android.telephony.ims.compat.ImsService";
+
+ // A map of slot Id -> map of features (indexed by ImsFeature feature id) corresponding to that
+ // slot.
+ // We keep track of this to facilitate cleanup of the IImsFeatureStatusCallback and
+ // call ImsFeature#onFeatureRemoved.
+ private final SparseArray<SparseArray<ImsFeature>> mFeaturesBySlot = new SparseArray<>();
+
+ /**
+ * @hide
+ */
+ @UnsupportedAppUsage
+ protected final IBinder mImsServiceController = new IImsServiceController.Stub() {
+
+ @Override
+ public IImsMMTelFeature createEmergencyMMTelFeature(int slotId,
+ IImsFeatureStatusCallback c) {
+ return createEmergencyMMTelFeatureInternal(slotId, c);
+ }
+
+ @Override
+ public IImsMMTelFeature createMMTelFeature(int slotId, IImsFeatureStatusCallback c) {
+ return createMMTelFeatureInternal(slotId, c);
+ }
+
+ @Override
+ public IImsRcsFeature createRcsFeature(int slotId, IImsFeatureStatusCallback c) {
+ return createRcsFeatureInternal(slotId, c);
+ }
+
+ @Override
+ public void removeImsFeature(int slotId, int featureType, IImsFeatureStatusCallback c)
+ throws RemoteException {
+ ImsService.this.removeImsFeature(slotId, featureType, c);
+ }
+ };
+
+ @UnsupportedAppUsage
+ public ImsService() {
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public IBinder onBind(Intent intent) {
+ if(SERVICE_INTERFACE.equals(intent.getAction())) {
+ Log.i(LOG_TAG, "ImsService(Compat) Bound.");
+ return mImsServiceController;
+ }
+ return null;
+ }
+
+ /**
+ * @hide
+ */
+ @VisibleForTesting
+ public SparseArray<ImsFeature> getFeatures(int slotId) {
+ return mFeaturesBySlot.get(slotId);
+ }
+
+ private IImsMMTelFeature createEmergencyMMTelFeatureInternal(int slotId,
+ IImsFeatureStatusCallback c) {
+ MMTelFeature f = onCreateEmergencyMMTelImsFeature(slotId);
+ if (f != null) {
+ setupFeature(f, slotId, ImsFeature.EMERGENCY_MMTEL, c);
+ return f.getBinder();
+ } else {
+ return null;
+ }
+ }
+
+ private IImsMMTelFeature createMMTelFeatureInternal(int slotId,
+ IImsFeatureStatusCallback c) {
+ MMTelFeature f = onCreateMMTelImsFeature(slotId);
+ if (f != null) {
+ setupFeature(f, slotId, ImsFeature.MMTEL, c);
+ return f.getBinder();
+ } else {
+ return null;
+ }
+ }
+
+ private IImsRcsFeature createRcsFeatureInternal(int slotId,
+ IImsFeatureStatusCallback c) {
+ RcsFeature f = onCreateRcsFeature(slotId);
+ if (f != null) {
+ setupFeature(f, slotId, ImsFeature.RCS, c);
+ return f.getBinder();
+ } else {
+ return null;
+ }
+ }
+
+ private void setupFeature(ImsFeature f, int slotId, int featureType,
+ IImsFeatureStatusCallback c) {
+ f.setContext(this);
+ f.setSlotId(slotId);
+ f.addImsFeatureStatusCallback(c);
+ addImsFeature(slotId, featureType, f);
+ // TODO: Remove once new onFeatureReady AIDL is merged in.
+ f.onFeatureReady();
+ }
+
+ private void addImsFeature(int slotId, int featureType, ImsFeature f) {
+ synchronized (mFeaturesBySlot) {
+ // Get SparseArray for Features, by querying slot Id
+ SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId);
+ if (features == null) {
+ // Populate new SparseArray of features if it doesn't exist for this slot yet.
+ features = new SparseArray<>();
+ mFeaturesBySlot.put(slotId, features);
+ }
+ features.put(featureType, f);
+ }
+ }
+
+ private void removeImsFeature(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 ImsFeature. No ImsFeatures exist on slot "
+ + slotId);
+ return;
+ }
+ ImsFeature f = features.get(featureType);
+ if (f == null) {
+ Log.w(LOG_TAG, "Can not remove ImsFeature. No feature with type "
+ + featureType + " exists on slot " + slotId);
+ return;
+ }
+ f.removeImsFeatureStatusCallback(c);
+ f.onFeatureRemoved();
+ features.remove(featureType);
+ }
+ }
+
+ /**
+ * @return An implementation of MMTelFeature that will be used by the system for MMTel
+ * functionality. Must be able to handle emergency calls at any time as well.
+ * @hide
+ */
+ public @Nullable MMTelFeature onCreateEmergencyMMTelImsFeature(int slotId) {
+ return null;
+ }
+
+ /**
+ * @return An implementation of MMTelFeature that will be used by the system for MMTel
+ * functionality.
+ * @hide
+ */
+ public @Nullable MMTelFeature onCreateMMTelImsFeature(int slotId) {
+ return null;
+ }
+
+ /**
+ * @return An implementation of RcsFeature that will be used by the system for RCS.
+ * @hide
+ */
+ public @Nullable RcsFeature onCreateRcsFeature(int slotId) {
+ return null;
+ }
+}
diff --git a/android/telephony/ims/compat/feature/ImsFeature.java b/android/telephony/ims/compat/feature/ImsFeature.java
new file mode 100644
index 0000000..5a9e8e2
--- /dev/null
+++ b/android/telephony/ims/compat/feature/ImsFeature.java
@@ -0,0 +1,154 @@
+/*
+ * 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.compat.feature;
+
+import android.annotation.IntDef;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.Context;
+import android.os.IInterface;
+import android.os.RemoteException;
+import android.telephony.SubscriptionManager;
+import android.util.Log;
+
+import com.android.ims.internal.IImsFeatureStatusCallback;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.WeakHashMap;
+
+/**
+ * Base class for all IMS features that are supported by the framework.
+ * @hide
+ */
+public abstract class ImsFeature {
+
+ private static final String LOG_TAG = "ImsFeature";
+
+ // Invalid feature value
+ public static final int INVALID = -1;
+ // ImsFeatures that are defined in the Manifests. Ensure that these values match the previously
+ // defined values in ImsServiceClass for compatibility purposes.
+ public static final int EMERGENCY_MMTEL = 0;
+ public static final int MMTEL = 1;
+ public static final int RCS = 2;
+ // Total number of features defined
+ public static final int MAX = 3;
+
+ // Integer values defining the state of the ImsFeature at any time.
+ @IntDef(flag = true,
+ value = {
+ STATE_NOT_AVAILABLE,
+ STATE_INITIALIZING,
+ STATE_READY,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ImsState {}
+ public static final int STATE_NOT_AVAILABLE = 0;
+ public static final int STATE_INITIALIZING = 1;
+ public static final int STATE_READY = 2;
+
+ private final Set<IImsFeatureStatusCallback> mStatusCallbacks = Collections.newSetFromMap(
+ new WeakHashMap<IImsFeatureStatusCallback, Boolean>());
+ private @ImsState int mState = STATE_NOT_AVAILABLE;
+ private int mSlotId = SubscriptionManager.INVALID_SIM_SLOT_INDEX;
+ protected Context mContext;
+
+ public void setContext(Context context) {
+ mContext = context;
+ }
+
+ public void setSlotId(int slotId) {
+ mSlotId = slotId;
+ }
+
+ @UnsupportedAppUsage
+ public int getFeatureState() {
+ return mState;
+ }
+
+ @UnsupportedAppUsage
+ protected final void setFeatureState(@ImsState int state) {
+ if (mState != state) {
+ mState = state;
+ notifyFeatureState(state);
+ }
+ }
+
+ public void addImsFeatureStatusCallback(IImsFeatureStatusCallback c) {
+ if (c == null) {
+ return;
+ }
+ try {
+ // If we have just connected, send queued status.
+ c.notifyImsFeatureStatus(mState);
+ // Add the callback if the callback completes successfully without a RemoteException.
+ synchronized (mStatusCallbacks) {
+ mStatusCallbacks.add(c);
+ }
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, "Couldn't notify feature state: " + e.getMessage());
+ }
+ }
+
+ public void removeImsFeatureStatusCallback(IImsFeatureStatusCallback c) {
+ if (c == null) {
+ return;
+ }
+ synchronized (mStatusCallbacks) {
+ mStatusCallbacks.remove(c);
+ }
+ }
+
+ /**
+ * Internal method called by ImsFeature when setFeatureState has changed.
+ * @param state
+ */
+ private void notifyFeatureState(@ImsState int state) {
+ synchronized (mStatusCallbacks) {
+ for (Iterator<IImsFeatureStatusCallback> iter = mStatusCallbacks.iterator();
+ iter.hasNext(); ) {
+ IImsFeatureStatusCallback callback = iter.next();
+ try {
+ Log.i(LOG_TAG, "notifying ImsFeatureState=" + state);
+ callback.notifyImsFeatureStatus(state);
+ } catch (RemoteException e) {
+ // remove if the callback is no longer alive.
+ iter.remove();
+ Log.w(LOG_TAG, "Couldn't notify feature state: " + e.getMessage());
+ }
+ }
+ }
+ }
+
+ /**
+ * Called when the feature is ready to use.
+ */
+ public abstract void onFeatureReady();
+
+ /**
+ * Called when the feature is being removed and must be cleaned up.
+ */
+ public abstract void onFeatureRemoved();
+
+ /**
+ * @return Binder instance
+ */
+ public abstract IInterface getBinder();
+}
diff --git a/android/telephony/ims/compat/feature/MMTelFeature.java b/android/telephony/ims/compat/feature/MMTelFeature.java
new file mode 100644
index 0000000..b52c371
--- /dev/null
+++ b/android/telephony/ims/compat/feature/MMTelFeature.java
@@ -0,0 +1,370 @@
+/*
+ * 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.compat.feature;
+
+import android.app.PendingIntent;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Message;
+import android.os.RemoteException;
+import android.telephony.ims.ImsCallProfile;
+import android.telephony.ims.ImsCallSession;
+import android.telephony.ims.stub.ImsEcbmImplBase;
+import android.telephony.ims.stub.ImsMultiEndpointImplBase;
+import android.telephony.ims.stub.ImsUtImplBase;
+
+import com.android.ims.internal.IImsCallSession;
+import com.android.ims.internal.IImsCallSessionListener;
+import com.android.ims.internal.IImsConfig;
+import com.android.ims.internal.IImsEcbm;
+import com.android.ims.internal.IImsMMTelFeature;
+import com.android.ims.internal.IImsMultiEndpoint;
+import com.android.ims.internal.IImsRegistrationListener;
+import com.android.ims.internal.IImsUt;
+
+/**
+ * Base implementation for MMTel.
+ * Any class wishing to use MMTelFeature should extend this class and implement all methods that the
+ * service supports.
+ *
+ * @hide
+ */
+
+public class MMTelFeature extends ImsFeature {
+
+ // Lock for feature synchronization
+ private final Object mLock = new Object();
+
+ @UnsupportedAppUsage
+ public MMTelFeature() {
+ }
+
+ private final IImsMMTelFeature mImsMMTelBinder = new IImsMMTelFeature.Stub() {
+
+ @Override
+ public int startSession(PendingIntent incomingCallIntent,
+ IImsRegistrationListener listener) throws RemoteException {
+ synchronized (mLock) {
+ return MMTelFeature.this.startSession(incomingCallIntent, listener);
+ }
+ }
+
+ @Override
+ public void endSession(int sessionId) throws RemoteException {
+ synchronized (mLock) {
+ MMTelFeature.this.endSession(sessionId);
+ }
+ }
+
+ @Override
+ public boolean isConnected(int callSessionType, int callType)
+ throws RemoteException {
+ synchronized (mLock) {
+ return MMTelFeature.this.isConnected(callSessionType, callType);
+ }
+ }
+
+ @Override
+ public boolean isOpened() throws RemoteException {
+ synchronized (mLock) {
+ return MMTelFeature.this.isOpened();
+ }
+ }
+
+ @Override
+ public int getFeatureStatus() throws RemoteException {
+ synchronized (mLock) {
+ return MMTelFeature.this.getFeatureState();
+ }
+ }
+
+ @Override
+ public void addRegistrationListener(IImsRegistrationListener listener)
+ throws RemoteException {
+ synchronized (mLock) {
+ MMTelFeature.this.addRegistrationListener(listener);
+ }
+ }
+
+ @Override
+ public void removeRegistrationListener(IImsRegistrationListener listener)
+ throws RemoteException {
+ synchronized (mLock) {
+ MMTelFeature.this.removeRegistrationListener(listener);
+ }
+ }
+
+ @Override
+ public ImsCallProfile createCallProfile(int sessionId, int callSessionType, int callType)
+ throws RemoteException {
+ synchronized (mLock) {
+ return MMTelFeature.this.createCallProfile(sessionId, callSessionType, callType);
+ }
+ }
+
+ @Override
+ public IImsCallSession createCallSession(int sessionId, ImsCallProfile profile)
+ throws RemoteException {
+ synchronized (mLock) {
+ return MMTelFeature.this.createCallSession(sessionId, profile, null);
+ }
+ }
+
+ @Override
+ public IImsCallSession getPendingCallSession(int sessionId, String callId)
+ throws RemoteException {
+ synchronized (mLock) {
+ return MMTelFeature.this.getPendingCallSession(sessionId, callId);
+ }
+ }
+
+ @Override
+ public IImsUt getUtInterface() throws RemoteException {
+ synchronized (mLock) {
+ ImsUtImplBase implBase = MMTelFeature.this.getUtInterface();
+ return implBase != null ? implBase.getInterface() : null;
+ }
+ }
+
+ @Override
+ public IImsConfig getConfigInterface() throws RemoteException {
+ synchronized (mLock) {
+ return MMTelFeature.this.getConfigInterface();
+ }
+ }
+
+ @Override
+ public void turnOnIms() throws RemoteException {
+ synchronized (mLock) {
+ MMTelFeature.this.turnOnIms();
+ }
+ }
+
+ @Override
+ public void turnOffIms() throws RemoteException {
+ synchronized (mLock) {
+ MMTelFeature.this.turnOffIms();
+ }
+ }
+
+ @Override
+ public IImsEcbm getEcbmInterface() throws RemoteException {
+ synchronized (mLock) {
+ ImsEcbmImplBase implBase = MMTelFeature.this.getEcbmInterface();
+ return implBase != null ? implBase.getImsEcbm() : null;
+ }
+ }
+
+ @Override
+ public void setUiTTYMode(int uiTtyMode, Message onComplete) throws RemoteException {
+ synchronized (mLock) {
+ MMTelFeature.this.setUiTTYMode(uiTtyMode, onComplete);
+ }
+ }
+
+ @Override
+ public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException {
+ synchronized (mLock) {
+ ImsMultiEndpointImplBase implBase = MMTelFeature.this.getMultiEndpointInterface();
+ return implBase != null ? implBase.getIImsMultiEndpoint() : null;
+ }
+ }
+ };
+
+ /**
+ * @hide
+ */
+ @Override
+ public final IImsMMTelFeature getBinder() {
+ return mImsMMTelBinder;
+ }
+
+ /**
+ * Notifies the MMTel feature that you would like to start a session. This should always be
+ * done before making/receiving IMS calls. The IMS service will register the device to the
+ * operator's network with the credentials (from ISIM) periodically in order to receive calls
+ * from the operator's network. When the IMS service receives a new call, it will send out an
+ * intent with the provided action string. The intent contains a call ID extra
+ * {@link IImsCallSession#getCallId} and it can be used to take a call.
+ *
+ * @param incomingCallIntent When an incoming call is received, the IMS service will call
+ * {@link PendingIntent#send} to send back the intent to the caller with
+ * ImsManager#INCOMING_CALL_RESULT_CODE as the result code and the intent to fill in the call
+ * ID; It cannot be null.
+ * @param listener To listen to IMS registration events; It cannot be null
+ * @return an integer (greater than 0) representing the session id associated with the session
+ * that has been started.
+ */
+ public int startSession(PendingIntent incomingCallIntent, IImsRegistrationListener listener) {
+ return 0;
+ }
+
+ /**
+ * End a previously started session using the associated sessionId.
+ * @param sessionId an integer (greater than 0) representing the ongoing session. See
+ * {@link #startSession}.
+ */
+ public void endSession(int sessionId) {
+ }
+
+ /**
+ * Checks if the IMS service has successfully registered to the IMS network with the specified
+ * service & call type.
+ *
+ * @param callSessionType a service type that is specified in {@link ImsCallProfile}
+ * {@link ImsCallProfile#SERVICE_TYPE_NORMAL}
+ * {@link ImsCallProfile#SERVICE_TYPE_EMERGENCY}
+ * @param callType a call type that is specified in {@link ImsCallProfile}
+ * {@link ImsCallProfile#CALL_TYPE_VOICE_N_VIDEO}
+ * {@link ImsCallProfile#CALL_TYPE_VOICE}
+ * {@link ImsCallProfile#CALL_TYPE_VT}
+ * {@link ImsCallProfile#CALL_TYPE_VS}
+ * @return true if the specified service id is connected to the IMS network; false otherwise
+ */
+ public boolean isConnected(int callSessionType, int callType) {
+ return false;
+ }
+
+ /**
+ * Checks if the specified IMS service is opened.
+ *
+ * @return true if the specified service id is opened; false otherwise
+ */
+ public boolean isOpened() {
+ return false;
+ }
+
+ /**
+ * Add a new registration listener for the client associated with the session Id.
+ * @param listener An implementation of IImsRegistrationListener.
+ */
+ public void addRegistrationListener(IImsRegistrationListener listener) {
+ }
+
+ /**
+ * Remove a previously registered listener using {@link #addRegistrationListener} for the client
+ * associated with the session Id.
+ * @param listener A previously registered IImsRegistrationListener
+ */
+ public void removeRegistrationListener(IImsRegistrationListener listener) {
+ }
+
+ /**
+ * Creates a {@link ImsCallProfile} from the service capabilities & IMS registration state.
+ *
+ * @param sessionId a session id which is obtained from {@link #startSession}
+ * @param callSessionType a service type that is specified in {@link ImsCallProfile}
+ * {@link ImsCallProfile#SERVICE_TYPE_NONE}
+ * {@link ImsCallProfile#SERVICE_TYPE_NORMAL}
+ * {@link ImsCallProfile#SERVICE_TYPE_EMERGENCY}
+ * @param callType a call type that is specified in {@link ImsCallProfile}
+ * {@link ImsCallProfile#CALL_TYPE_VOICE}
+ * {@link ImsCallProfile#CALL_TYPE_VT}
+ * {@link ImsCallProfile#CALL_TYPE_VT_TX}
+ * {@link ImsCallProfile#CALL_TYPE_VT_RX}
+ * {@link ImsCallProfile#CALL_TYPE_VT_NODIR}
+ * {@link ImsCallProfile#CALL_TYPE_VS}
+ * {@link ImsCallProfile#CALL_TYPE_VS_TX}
+ * {@link ImsCallProfile#CALL_TYPE_VS_RX}
+ * @return a {@link ImsCallProfile} object
+ */
+ public ImsCallProfile createCallProfile(int sessionId, int callSessionType, int callType) {
+ return null;
+ }
+
+ /**
+ * Creates an {@link ImsCallSession} with the specified call profile.
+ * Use other methods, if applicable, instead of interacting with
+ * {@link ImsCallSession} directly.
+ *
+ * @param sessionId a session id which is obtained from {@link #startSession}
+ * @param profile a call profile to make the call
+ */
+ public IImsCallSession createCallSession(int sessionId, ImsCallProfile profile,
+ IImsCallSessionListener listener) {
+ return null;
+ }
+
+ /**
+ * Retrieves the call session associated with a pending call.
+ *
+ * @param sessionId a session id which is obtained from {@link #startSession}
+ * @param callId a call id to make the call
+ */
+ public IImsCallSession getPendingCallSession(int sessionId, String callId) {
+ return null;
+ }
+
+ /**
+ * @return The Ut interface for the supplementary service configuration.
+ */
+ public ImsUtImplBase getUtInterface() {
+ return null;
+ }
+
+ /**
+ * @return The config interface for IMS Configuration
+ */
+ public IImsConfig getConfigInterface() {
+ return null;
+ }
+
+ /**
+ * Signal the MMTelFeature to turn on IMS when it has been turned off using {@link #turnOffIms}
+ */
+ public void turnOnIms() {
+ }
+
+ /**
+ * Signal the MMTelFeature to turn off IMS when it has been turned on using {@link #turnOnIms}
+ */
+ public void turnOffIms() {
+ }
+
+ /**
+ * @return The Emergency call-back mode interface for emergency VoLTE calls that support it.
+ */
+ public ImsEcbmImplBase getEcbmInterface() {
+ return null;
+ }
+
+ /**
+ * Sets the current UI TTY mode for the MMTelFeature.
+ * @param uiTtyMode An integer containing the new UI TTY Mode.
+ * @param onComplete A {@link Message} to be used when the mode has been set.
+ */
+ public void setUiTTYMode(int uiTtyMode, Message onComplete) {
+ }
+
+ /**
+ * @return MultiEndpoint interface for DEP notifications
+ */
+ public ImsMultiEndpointImplBase getMultiEndpointInterface() {
+ return null;
+ }
+
+ @Override
+ public void onFeatureReady() {
+
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void onFeatureRemoved() {
+
+ }
+}
diff --git a/android/telephony/ims/compat/feature/RcsFeature.java b/android/telephony/ims/compat/feature/RcsFeature.java
new file mode 100644
index 0000000..228b330
--- /dev/null
+++ b/android/telephony/ims/compat/feature/RcsFeature.java
@@ -0,0 +1,53 @@
+/*
+ * 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.compat.feature;
+
+
+import com.android.ims.internal.IImsRcsFeature;
+
+/**
+ * Base implementation of the RcsFeature APIs. Any ImsService wishing to support RCS should extend
+ * this class and provide implementations of the RcsFeature methods that they support.
+ * @hide
+ */
+
+public class RcsFeature extends ImsFeature {
+
+ private final IImsRcsFeature mImsRcsBinder = new IImsRcsFeature.Stub() {
+ // Empty Default Implementation.
+ };
+
+
+ public RcsFeature() {
+ super();
+ }
+
+ @Override
+ public void onFeatureReady() {
+
+ }
+
+ @Override
+ public void onFeatureRemoved() {
+
+ }
+
+ @Override
+ public final IImsRcsFeature getBinder() {
+ return mImsRcsBinder;
+ }
+}
diff --git a/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java b/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java
new file mode 100644
index 0000000..06aa642
--- /dev/null
+++ b/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java
@@ -0,0 +1,653 @@
+/*
+ * 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.compat.stub;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Message;
+import android.os.RemoteException;
+import android.telephony.CallQuality;
+import android.telephony.ServiceState;
+import android.telephony.ims.ImsCallProfile;
+import android.telephony.ims.ImsCallSession;
+import android.telephony.ims.ImsConferenceState;
+import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.ImsStreamMediaProfile;
+import android.telephony.ims.ImsSuppServiceNotification;
+import android.telephony.ims.aidl.IImsCallSessionListener;
+
+import com.android.ims.internal.IImsCallSession;
+import com.android.ims.internal.IImsVideoCallProvider;
+
+/**
+ * Compat implementation of ImsCallSessionImplBase for older implementations.
+ *
+ * DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
+ * will break other implementations of ImsCallSession maintained by other ImsServices.
+ *
+ * @hide
+ */
+
+public class ImsCallSessionImplBase extends IImsCallSession.Stub {
+
+ @UnsupportedAppUsage
+ public ImsCallSessionImplBase() {
+ }
+
+ @Override
+ // convert to old implementation of listener
+ public final void setListener(IImsCallSessionListener listener)
+ throws RemoteException {
+ setListener(new ImsCallSessionListenerConverter(listener));
+ }
+
+ /**
+ * Sets the listener to listen to the session events. An {@link ImsCallSession}
+ * can only hold one listener at a time. Subsequent calls to this method
+ * override the previous listener.
+ *
+ * @param listener to listen to the session events of this object
+ */
+ public void setListener(com.android.ims.internal.IImsCallSessionListener listener) {
+
+ }
+
+ /**
+ * Closes the object. This {@link ImsCallSessionImplBase} is not usable after being closed.
+ */
+ @Override
+ public void close() {
+
+ }
+
+ /**
+ * @return A String containing the unique call ID of this {@link ImsCallSessionImplBase}.
+ */
+ @Override
+ public String getCallId() {
+ return null;
+ }
+
+ /**
+ * @return The {@link ImsCallProfile} that this {@link ImsCallSessionImplBase} is associated
+ * with.
+ */
+ @Override
+ public ImsCallProfile getCallProfile() {
+ return null;
+ }
+
+ /**
+ * @return The local {@link ImsCallProfile} that this {@link ImsCallSessionImplBase} is
+ * associated with.
+ */
+ @Override
+ public ImsCallProfile getLocalCallProfile() {
+ return null;
+ }
+
+ /**
+ * @return The remote {@link ImsCallProfile} that this {@link ImsCallSessionImplBase} is
+ * associated with.
+ */
+ @Override
+ public ImsCallProfile getRemoteCallProfile() {
+ return null;
+ }
+
+ /**
+ * @param name The String extra key.
+ * @return The string extra value associated with the specified property.
+ */
+ @Override
+ public String getProperty(String name) {
+ return null;
+ }
+
+ /**
+ * @return The {@link ImsCallSessionImplBase} state.
+ */
+ @Override
+ public int getState() {
+ return -1;
+ }
+
+ /**
+ * @return true if the {@link ImsCallSessionImplBase} is in a call, false otherwise.
+ */
+ @Override
+ public boolean isInCall() {
+ return false;
+ }
+
+ /**
+ * Mutes or unmutes the mic for the active call.
+ *
+ * @param muted true if the call should be muted, false otherwise.
+ */
+ @Override
+ public void setMute(boolean muted) {
+ }
+
+ /**
+ * Initiates an IMS call with the specified number and call profile.
+ * The session listener set in {@link #setListener(IImsCallSessionListener)} is called back upon
+ * defined session events.
+ * Only valid to call when the session state is in
+ * {@link ImsCallSession.State#IDLE}.
+ *
+ * @param callee dialed string to make the call to
+ * @param profile call profile to make the call with the specified service type,
+ * call type and media information
+ * @see {@link ImsCallSession.Listener#callSessionStarted},
+ * {@link ImsCallSession.Listener#callSessionStartFailed}
+ */
+ @Override
+ public void start(String callee, ImsCallProfile profile) {
+ }
+
+ /**
+ * Initiates an IMS call with the specified participants and call profile.
+ * The session listener set in {@link #setListener(IImsCallSessionListener)} is called back upon
+ * defined session events.
+ * The method is only valid to call when the session state is in
+ * {@link ImsCallSession.State#IDLE}.
+ *
+ * @param participants participant list to initiate an IMS conference call
+ * @param profile call profile to make the call with the specified service type,
+ * call type and media information
+ * @see {@link ImsCallSession.Listener#callSessionStarted},
+ * {@link ImsCallSession.Listener#callSessionStartFailed}
+ */
+ @Override
+ public void startConference(String[] participants, ImsCallProfile profile) {
+ }
+
+ /**
+ * Accepts an incoming call or session update.
+ *
+ * @param callType call type specified in {@link ImsCallProfile} to be answered
+ * @param profile stream media profile {@link ImsStreamMediaProfile} to be answered
+ * @see {@link ImsCallSession.Listener#callSessionStarted}
+ */
+ @Override
+ public void accept(int callType, ImsStreamMediaProfile profile) {
+ }
+
+ /**
+ * Deflects an incoming call.
+ *
+ * @param deflectNumber number to deflect the call
+ */
+ @Override
+ public void deflect(String deflectNumber) {
+ }
+
+ /**
+ * Transfer an established call to given number, disconnecting the ongoing call
+ * when the transfer is complete.
+ *
+ * @param number number to transfer the call
+ * @param isConfirmationRequired when {@code true}, then the {@link ImsCallSessionImplBase}
+ * should wait until the transfer has successfully completed before disconnecting the current
+ * {@link ImsCallSessionImplBase}. When {@code false}, the {@link ImsCallSessionImplBase}
+ * should signal the network to perform the transfer, but should immediately disconnect the
+ * call regardless of the outcome of the transfer.
+ */
+ @Override
+ public void transfer(@NonNull String number, boolean isConfirmationRequired) {
+ }
+
+ /**
+ * Transfer an established call to an existing ongoing session.
+ * When the transfer is complete, the current call gets disconnected locally.
+ */
+ @Override
+ public void consultativeTransfer(@NonNull IImsCallSession transferToSession) {
+ }
+
+ /**
+ * Rejects an incoming call or session update.
+ *
+ * @param reason reason code to reject an incoming call, defined in {@link ImsReasonInfo}.
+ * {@link ImsCallSession.Listener#callSessionStartFailed}
+ */
+ @Override
+ public void reject(int reason) {
+ }
+
+ /**
+ * Terminates a call.
+ *
+ * @param reason reason code to terminate a call, defined in {@link ImsReasonInfo}.
+ *
+ * @see {@link ImsCallSession.Listener#callSessionTerminated}
+ */
+ @Override
+ public void terminate(int reason) {
+ }
+
+ /**
+ * Puts a call on hold. When it succeeds, {@link ImsCallSession.Listener#callSessionHeld} is
+ * called.
+ *
+ * @param profile stream media profile {@link ImsStreamMediaProfile} to hold the call
+ * @see {@link ImsCallSession.Listener#callSessionHeld},
+ * {@link ImsCallSession.Listener#callSessionHoldFailed}
+ */
+ @Override
+ public void hold(ImsStreamMediaProfile profile) {
+ }
+
+ /**
+ * Continues a call that's on hold. When it succeeds,
+ * {@link ImsCallSession.Listener#callSessionResumed} is called.
+ *
+ * @param profile stream media profile with {@link ImsStreamMediaProfile} to resume the call
+ * @see {@link ImsCallSession.Listener#callSessionResumed},
+ * {@link ImsCallSession.Listener#callSessionResumeFailed}
+ */
+ @Override
+ public void resume(ImsStreamMediaProfile profile) {
+ }
+
+ /**
+ * Merges the active and held call. When the merge starts,
+ * {@link ImsCallSession.Listener#callSessionMergeStarted} is called.
+ * {@link ImsCallSession.Listener#callSessionMergeComplete} is called if the merge is
+ * successful, and {@link ImsCallSession.Listener#callSessionMergeFailed} is called if the merge
+ * fails.
+ *
+ * @see {@link ImsCallSession.Listener#callSessionMergeStarted},
+ * {@link ImsCallSession.Listener#callSessionMergeComplete},
+ * {@link ImsCallSession.Listener#callSessionMergeFailed}
+ */
+ @Override
+ public void merge() {
+ }
+
+ /**
+ * Updates the current call's properties (ex. call mode change: video upgrade / downgrade).
+ *
+ * @param callType call type specified in {@link ImsCallProfile} to be updated
+ * @param profile stream media profile {@link ImsStreamMediaProfile} to be updated
+ * @see {@link ImsCallSession.Listener#callSessionUpdated},
+ * {@link ImsCallSession.Listener#callSessionUpdateFailed}
+ */
+ @Override
+ public void update(int callType, ImsStreamMediaProfile profile) {
+ }
+
+ /**
+ * Extends this call to the conference call with the specified recipients.
+ *
+ * @param participants participant list to be invited to the conference call after extending the
+ * call
+ * @see {@link ImsCallSession.Listener#callSessionConferenceExtended},
+ * {@link ImsCallSession.Listener#callSessionConferenceExtendFailed}
+ */
+ @Override
+ public void extendToConference(String[] participants) {
+ }
+
+ /**
+ * Requests the conference server to invite an additional participants to the conference.
+ *
+ * @param participants participant list to be invited to the conference call
+ * @see {@link ImsCallSession.Listener#callSessionInviteParticipantsRequestDelivered},
+ * {@link ImsCallSession.Listener#callSessionInviteParticipantsRequestFailed}
+ */
+ @Override
+ public void inviteParticipants(String[] participants) {
+ }
+
+ /**
+ * Requests the conference server to remove the specified participants from the conference.
+ *
+ * @param participants participant list to be removed from the conference call
+ * @see {@link ImsCallSession.Listener#callSessionRemoveParticipantsRequestDelivered},
+ * {@link ImsCallSession.Listener#callSessionRemoveParticipantsRequestFailed}
+ */
+ @Override
+ public void removeParticipants(String[] participants) {
+ }
+
+ /**
+ * Sends a DTMF code. According to <a href="http://tools.ietf.org/html/rfc2833">RFC 2833</a>,
+ * event 0 ~ 9 maps to decimal value 0 ~ 9, '*' to 10, '#' to 11, event 'A' ~ 'D' to 12 ~ 15,
+ * and event flash to 16. Currently, event flash is not supported.
+ *
+ * @param c the DTMF to send. '0' ~ '9', 'A' ~ 'D', '*', '#' are valid inputs.
+ */
+ @Override
+ public void sendDtmf(char c, Message result) {
+ }
+
+ /**
+ * Start a DTMF code. According to <a href="http://tools.ietf.org/html/rfc2833">RFC 2833</a>,
+ * event 0 ~ 9 maps to decimal value 0 ~ 9, '*' to 10, '#' to 11, event 'A' ~ 'D' to 12 ~ 15,
+ * and event flash to 16. Currently, event flash is not supported.
+ *
+ * @param c the DTMF to send. '0' ~ '9', 'A' ~ 'D', '*', '#' are valid inputs.
+ */
+ @Override
+ public void startDtmf(char c) {
+ }
+
+ /**
+ * Stop a DTMF code.
+ */
+ @Override
+ public void stopDtmf() {
+ }
+
+ /**
+ * Sends an USSD message.
+ *
+ * @param ussdMessage USSD message to send
+ */
+ @Override
+ public void sendUssd(String ussdMessage) {
+ }
+
+ @Override
+ public IImsVideoCallProvider getVideoCallProvider() {
+ return null;
+ }
+
+ /**
+ * Determines if the current session is multiparty.
+ * @return {@code True} if the session is multiparty.
+ */
+ @Override
+ public boolean isMultiparty() {
+ return false;
+ }
+
+ /**
+ * Device issues RTT modify request
+ * @param toProfile The profile with requested changes made
+ */
+ @Override
+ public void sendRttModifyRequest(ImsCallProfile toProfile) {
+ }
+
+ /**
+ * Device responds to Remote RTT modify request
+ * @param status true if the the request was accepted or false of the request is defined.
+ */
+ @Override
+ public void sendRttModifyResponse(boolean status) {
+ }
+
+ /**
+ * Device sends RTT message
+ * @param rttMessage RTT message to be sent
+ */
+ @Override
+ public void sendRttMessage(String rttMessage) {
+ }
+
+ /**
+ * 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
+ * back to the framework.
+ */
+ private class ImsCallSessionListenerConverter
+ extends com.android.ims.internal.IImsCallSessionListener.Stub {
+
+ private final IImsCallSessionListener mNewListener;
+
+ public ImsCallSessionListenerConverter(IImsCallSessionListener listener) {
+ mNewListener = listener;
+ }
+
+ @Override
+ public void callSessionProgressing(IImsCallSession i,
+ ImsStreamMediaProfile imsStreamMediaProfile) throws RemoteException {
+ mNewListener.callSessionProgressing(imsStreamMediaProfile);
+ }
+
+ @Override
+ public void callSessionStarted(IImsCallSession i, ImsCallProfile imsCallProfile)
+ throws RemoteException {
+ mNewListener.callSessionInitiated(imsCallProfile);
+ }
+
+ @Override
+ public void callSessionStartFailed(IImsCallSession i, ImsReasonInfo imsReasonInfo)
+ throws RemoteException {
+ mNewListener.callSessionInitiatedFailed(imsReasonInfo);
+ }
+
+ @Override
+ public void callSessionTerminated(IImsCallSession i, ImsReasonInfo imsReasonInfo)
+ throws RemoteException {
+ mNewListener.callSessionTerminated(imsReasonInfo);
+ }
+
+ @Override
+ public void callSessionHeld(IImsCallSession i, ImsCallProfile imsCallProfile)
+ throws RemoteException {
+ mNewListener.callSessionHeld(imsCallProfile);
+ }
+
+ @Override
+ public void callSessionHoldFailed(IImsCallSession i, ImsReasonInfo imsReasonInfo)
+ throws RemoteException {
+ mNewListener.callSessionHoldFailed(imsReasonInfo);
+ }
+
+ @Override
+ public void callSessionHoldReceived(IImsCallSession i, ImsCallProfile imsCallProfile)
+ throws RemoteException {
+ mNewListener.callSessionHoldReceived(imsCallProfile);
+ }
+
+ @Override
+ public void callSessionResumed(IImsCallSession i, ImsCallProfile imsCallProfile)
+ throws RemoteException {
+ mNewListener.callSessionResumed(imsCallProfile);
+ }
+
+ @Override
+ public void callSessionResumeFailed(IImsCallSession i, ImsReasonInfo imsReasonInfo)
+ throws RemoteException {
+ mNewListener.callSessionResumeFailed(imsReasonInfo);
+ }
+
+ @Override
+ public void callSessionResumeReceived(IImsCallSession i, ImsCallProfile imsCallProfile)
+ throws RemoteException {
+ mNewListener.callSessionResumeReceived(imsCallProfile);
+ }
+
+ @Override
+ public void callSessionMergeStarted(IImsCallSession i, IImsCallSession newSession,
+ ImsCallProfile profile)
+ throws RemoteException {
+ mNewListener.callSessionMergeStarted(newSession, profile);
+ }
+
+ @Override
+ public void callSessionMergeComplete(IImsCallSession iImsCallSession)
+ throws RemoteException {
+ mNewListener.callSessionMergeComplete(iImsCallSession);
+ }
+
+ @Override
+ public void callSessionMergeFailed(IImsCallSession i, ImsReasonInfo imsReasonInfo)
+ throws RemoteException {
+ mNewListener.callSessionMergeFailed(imsReasonInfo);
+ }
+
+ @Override
+ public void callSessionUpdated(IImsCallSession i, ImsCallProfile imsCallProfile)
+ throws RemoteException {
+ mNewListener.callSessionUpdated(imsCallProfile);
+ }
+
+ @Override
+ public void callSessionUpdateFailed(IImsCallSession i, ImsReasonInfo imsReasonInfo)
+ throws RemoteException {
+ mNewListener.callSessionUpdateFailed(imsReasonInfo);
+ }
+
+ @Override
+ public void callSessionUpdateReceived(IImsCallSession i, ImsCallProfile imsCallProfile)
+ throws RemoteException {
+ mNewListener.callSessionUpdateReceived(imsCallProfile);
+ }
+
+ @Override
+ public void callSessionConferenceExtended(IImsCallSession i, IImsCallSession newSession,
+ ImsCallProfile imsCallProfile) throws RemoteException {
+ mNewListener.callSessionConferenceExtended(newSession, imsCallProfile);
+ }
+
+ @Override
+ public void callSessionConferenceExtendFailed(IImsCallSession i,
+ ImsReasonInfo imsReasonInfo) throws RemoteException {
+ mNewListener.callSessionConferenceExtendFailed(imsReasonInfo);
+ }
+
+ @Override
+ public void callSessionConferenceExtendReceived(IImsCallSession i,
+ IImsCallSession newSession, ImsCallProfile imsCallProfile)
+ throws RemoteException {
+ mNewListener.callSessionConferenceExtendReceived(newSession, imsCallProfile);
+ }
+
+ @Override
+ public void callSessionInviteParticipantsRequestDelivered(IImsCallSession i)
+ throws RemoteException {
+ mNewListener.callSessionInviteParticipantsRequestDelivered();
+ }
+
+ @Override
+ public void callSessionInviteParticipantsRequestFailed(IImsCallSession i,
+ ImsReasonInfo imsReasonInfo) throws RemoteException {
+ mNewListener.callSessionInviteParticipantsRequestFailed(imsReasonInfo);
+ }
+
+ @Override
+ public void callSessionRemoveParticipantsRequestDelivered(IImsCallSession i)
+ throws RemoteException {
+ mNewListener.callSessionRemoveParticipantsRequestDelivered();
+ }
+
+ @Override
+ public void callSessionRemoveParticipantsRequestFailed(IImsCallSession i,
+ ImsReasonInfo imsReasonInfo) throws RemoteException {
+ mNewListener.callSessionRemoveParticipantsRequestFailed(imsReasonInfo);
+ }
+
+ @Override
+ public void callSessionConferenceStateUpdated(IImsCallSession i,
+ ImsConferenceState imsConferenceState) throws RemoteException {
+ mNewListener.callSessionConferenceStateUpdated(imsConferenceState);
+ }
+
+ @Override
+ public void callSessionUssdMessageReceived(IImsCallSession i, int mode, String message)
+ throws RemoteException {
+ mNewListener.callSessionUssdMessageReceived(mode, message);
+ }
+
+ @Override
+ public void callSessionHandover(IImsCallSession i, int srcAccessTech, int targetAccessTech,
+ ImsReasonInfo reasonInfo) throws RemoteException {
+ mNewListener.callSessionHandover(
+ ServiceState.rilRadioTechnologyToNetworkType(srcAccessTech),
+ ServiceState.rilRadioTechnologyToNetworkType(targetAccessTech), reasonInfo);
+ }
+
+ @Override
+ public void callSessionHandoverFailed(IImsCallSession i, int srcAccessTech,
+ int targetAccessTech, ImsReasonInfo reasonInfo) throws RemoteException {
+ mNewListener.callSessionHandoverFailed(
+ ServiceState.rilRadioTechnologyToNetworkType(srcAccessTech),
+ ServiceState.rilRadioTechnologyToNetworkType(targetAccessTech), reasonInfo);
+ }
+
+ @Override
+ public void callSessionMayHandover(IImsCallSession i, int srcAccessTech, int targetAccessTech)
+ throws RemoteException {
+ mNewListener.callSessionMayHandover(
+ ServiceState.rilRadioTechnologyToNetworkType(srcAccessTech),
+ ServiceState.rilRadioTechnologyToNetworkType(targetAccessTech));
+ }
+
+ @Override
+ public void callSessionTtyModeReceived(IImsCallSession iImsCallSession, int mode)
+ throws RemoteException {
+ mNewListener.callSessionTtyModeReceived(mode);
+ }
+
+ @Override
+ public void callSessionMultipartyStateChanged(IImsCallSession i, boolean isMultiparty)
+ throws RemoteException {
+ mNewListener.callSessionMultipartyStateChanged(isMultiparty);
+ }
+
+ @Override
+ public void callSessionSuppServiceReceived(IImsCallSession i,
+ ImsSuppServiceNotification imsSuppServiceNotification) throws RemoteException {
+ mNewListener.callSessionSuppServiceReceived(imsSuppServiceNotification);
+ }
+
+ @Override
+ public void callSessionRttModifyRequestReceived(IImsCallSession i,
+ ImsCallProfile imsCallProfile) throws RemoteException {
+ mNewListener.callSessionRttModifyRequestReceived(imsCallProfile);
+ }
+
+ @Override
+ public void callSessionRttModifyResponseReceived(int status) throws RemoteException {
+ mNewListener.callSessionRttModifyResponseReceived(status);
+ }
+
+ @Override
+ public void callSessionRttMessageReceived(String rttMessage) throws RemoteException {
+ mNewListener.callSessionRttMessageReceived(rttMessage);
+ }
+
+ @Override
+ public void callSessionRttAudioIndicatorChanged(ImsStreamMediaProfile profile)
+ throws RemoteException {
+ mNewListener.callSessionRttAudioIndicatorChanged(profile);
+ }
+
+ @Override
+ public void callSessionTransferred() throws RemoteException {
+ mNewListener.callSessionTransferred();
+ }
+
+ @Override
+ public void callSessionTransferFailed(@Nullable ImsReasonInfo reasonInfo)
+ throws RemoteException {
+ mNewListener.callSessionTransferFailed(reasonInfo);
+ }
+
+ @Override
+ public void callQualityChanged(CallQuality callQuality) throws RemoteException {
+ mNewListener.callQualityChanged(callQuality);
+ }
+ }
+}
diff --git a/android/telephony/ims/compat/stub/ImsConfigImplBase.java b/android/telephony/ims/compat/stub/ImsConfigImplBase.java
new file mode 100644
index 0000000..aae6f92
--- /dev/null
+++ b/android/telephony/ims/compat/stub/ImsConfigImplBase.java
@@ -0,0 +1,394 @@
+/*
+ * Copyright (C) 2017 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.compat.stub;
+
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.Context;
+import android.content.Intent;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.ims.ImsConfig;
+import com.android.ims.ImsConfigListener;
+import com.android.ims.internal.IImsConfig;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.lang.ref.WeakReference;
+import java.util.HashMap;
+
+
+/**
+ * Base implementation of ImsConfig.
+ * Override the methods that your implementation of ImsConfig supports.
+ *
+ * DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
+ * will break other implementations of ImsConfig maintained by other ImsServices.
+ *
+ * Provides APIs to get/set the IMS service feature/capability/parameters.
+ * The config items include:
+ * 1) Items provisioned by the operator.
+ * 2) Items configured by user. Mainly service feature class.
+ *
+ * The inner class {@link ImsConfigStub} implements methods of IImsConfig AIDL interface.
+ * The IImsConfig AIDL interface is called by ImsConfig, which may exist in many other processes.
+ * ImsConfigImpl access to the configuration parameters may be arbitrarily slow, especially in
+ * during initialization, or times when a lot of configuration parameters are being set/get
+ * (such as during boot up or SIM card change). By providing a cache in ImsConfigStub, we can speed
+ * up access to these configuration parameters, so a query to the ImsConfigImpl does not have to be
+ * performed every time.
+ * @hide
+ */
+
+public class ImsConfigImplBase {
+
+ static final private String TAG = "ImsConfigImplBase";
+
+ ImsConfigStub mImsConfigStub;
+
+ @UnsupportedAppUsage
+ public ImsConfigImplBase(Context context) {
+ mImsConfigStub = new ImsConfigStub(this, context);
+ }
+
+ /**
+ * Gets the value for ims service/capabilities parameters from the provisioned
+ * value storage. Synchronous blocking call.
+ *
+ * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
+ * @return value in Integer format.
+ */
+ public int getProvisionedValue(int item) throws RemoteException {
+ return -1;
+ }
+
+ /**
+ * Gets the value for ims service/capabilities parameters from the provisioned
+ * value storage. Synchronous blocking call.
+ *
+ * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
+ * @return value in String format.
+ */
+ public String getProvisionedStringValue(int item) throws RemoteException {
+ return null;
+ }
+
+ /**
+ * 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.
+ *
+ * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
+ * @param value in Integer format.
+ * @return as defined in com.android.ims.ImsConfig#OperationStatusConstants.
+ */
+ public int setProvisionedValue(int item, int value) throws RemoteException {
+ return ImsConfig.OperationStatusConstants.FAILED;
+ }
+
+ /**
+ * 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.
+ *
+ * @param item as defined in com.android.ims.ImsConfig#ConfigConstants.
+ * @param value in String format.
+ * @return as defined in com.android.ims.ImsConfig#OperationStatusConstants.
+ */
+ public int setProvisionedStringValue(int item, String value) throws RemoteException {
+ return ImsConfig.OperationStatusConstants.FAILED;
+ }
+
+ /**
+ * 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
+ * value). Asynchronous non-blocking call.
+ *
+ * @param feature as defined in com.android.ims.ImsConfig#FeatureConstants.
+ * @param network as defined in android.telephony.TelephonyManager#NETWORK_TYPE_XXX.
+ * @param listener feature value returned asynchronously through listener.
+ */
+ public void getFeatureValue(int feature, int network, ImsConfigListener listener)
+ throws RemoteException {
+ }
+
+ /**
+ * Sets the value for IMS feature item for specified network type.
+ * This operation stores the user setting in setting db from which master db
+ * is derived.
+ *
+ * @param feature as defined in com.android.ims.ImsConfig#FeatureConstants.
+ * @param network as defined in android.telephony.TelephonyManager#NETWORK_TYPE_XXX.
+ * @param value as defined in com.android.ims.ImsConfig#FeatureValueConstants.
+ * @param listener, provided if caller needs to be notified for set result.
+ */
+ public void setFeatureValue(int feature, int network, int value, ImsConfigListener listener)
+ throws RemoteException {
+ }
+
+ /**
+ * Gets the value for IMS VoLTE provisioned.
+ * This should be the same as the operator provisioned value if applies.
+ */
+ public boolean getVolteProvisioned() throws RemoteException {
+ return false;
+ }
+
+ /**
+ * Gets the value for IMS feature item video quality.
+ *
+ * @param listener Video quality value returned asynchronously through listener.
+ */
+ public void getVideoQuality(ImsConfigListener listener) throws RemoteException {
+ }
+
+ /**
+ * Sets the value for IMS feature item video quality.
+ *
+ * @param quality, defines the value of video quality.
+ * @param listener, provided if caller needs to be notified for set result.
+ */
+ public void setVideoQuality(int quality, ImsConfigListener listener) throws RemoteException {
+ }
+
+ @UnsupportedAppUsage
+ public IImsConfig getIImsConfig() { return mImsConfigStub; }
+
+ /**
+ * Updates provisioning value and notifies the framework of the change.
+ * Doesn't call #setProvisionedValue and assumes the result succeeded.
+ * This should only be used by modem when they implicitly changed provisioned values.
+ *
+ * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
+ * @param value in Integer format.
+ */
+ public final void notifyProvisionedValueChanged(int item, int value) {
+ mImsConfigStub.updateCachedValue(item, value, true);
+ }
+
+ /**
+ * Updates provisioning value and notifies the framework of the change.
+ * Doesn't call #setProvisionedValue and assumes the result succeeded.
+ * This should only be used by modem when they implicitly changed provisioned values.
+ *
+ * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
+ * @param value in String format.
+ */
+ public final void notifyProvisionedValueChanged(int item, String value) {
+ mImsConfigStub.updateCachedValue(item, value, true);
+ }
+
+ /**
+ * Implements the IImsConfig AIDL interface, which is called by potentially many processes
+ * in order to get/set configuration parameters.
+ *
+ * It holds an object of ImsConfigImplBase class which is usually extended by ImsConfigImpl
+ * with actual implementations from vendors. This class caches provisioned values from
+ * ImsConfigImpl layer because queries through ImsConfigImpl can be slow. When query goes in,
+ * it first checks cache layer. If missed, it will call the vendor implementation of
+ * ImsConfigImplBase API.
+ * and cache the return value if the set succeeds.
+ *
+ * Provides APIs to get/set the IMS service feature/capability/parameters.
+ * The config items include:
+ * 1) Items provisioned by the operator.
+ * 2) Items configured by user. Mainly service feature class.
+ *
+ * @hide
+ */
+ @VisibleForTesting
+ static public class ImsConfigStub extends IImsConfig.Stub {
+ Context mContext;
+ WeakReference<ImsConfigImplBase> mImsConfigImplBaseWeakReference;
+ private HashMap<Integer, Integer> mProvisionedIntValue = new HashMap<>();
+ private HashMap<Integer, String> mProvisionedStringValue = new HashMap<>();
+
+ @VisibleForTesting
+ public ImsConfigStub(ImsConfigImplBase imsConfigImplBase, Context context) {
+ mContext = context;
+ mImsConfigImplBaseWeakReference =
+ new WeakReference<ImsConfigImplBase>(imsConfigImplBase);
+ }
+
+ /**
+ * Gets the value for ims service/capabilities parameters. It first checks its local cache,
+ * if missed, it will call ImsConfigImplBase.getProvisionedValue.
+ * Synchronous blocking call.
+ *
+ * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
+ * @return value in Integer format.
+ */
+ @Override
+ public synchronized int getProvisionedValue(int item) throws RemoteException {
+ if (mProvisionedIntValue.containsKey(item)) {
+ return mProvisionedIntValue.get(item);
+ } else {
+ int retVal = getImsConfigImpl().getProvisionedValue(item);
+ if (retVal != ImsConfig.OperationStatusConstants.UNKNOWN) {
+ updateCachedValue(item, retVal, false);
+ }
+ return retVal;
+ }
+ }
+
+ /**
+ * Gets the value for ims service/capabilities parameters. It first checks its local cache,
+ * if missed, it will call #ImsConfigImplBase.getProvisionedValue.
+ * Synchronous blocking call.
+ *
+ * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
+ * @return value in String format.
+ */
+ @Override
+ public synchronized String getProvisionedStringValue(int item) throws RemoteException {
+ if (mProvisionedIntValue.containsKey(item)) {
+ return mProvisionedStringValue.get(item);
+ } else {
+ String retVal = getImsConfigImpl().getProvisionedStringValue(item);
+ if (retVal != null) {
+ updateCachedValue(item, retVal, false);
+ }
+ return retVal;
+ }
+ }
+
+ /**
+ * 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.
+ * Synchronous blocking call.
+ *
+ * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
+ * @param value in Integer format.
+ * @return as defined in com.android.ims.ImsConfig#OperationStatusConstants.
+ */
+ @Override
+ public synchronized int setProvisionedValue(int item, int value) throws RemoteException {
+ mProvisionedIntValue.remove(item);
+ int retVal = getImsConfigImpl().setProvisionedValue(item, value);
+ if (retVal == ImsConfig.OperationStatusConstants.SUCCESS) {
+ updateCachedValue(item, value, true);
+ } else {
+ Log.d(TAG, "Set provision value of " + item +
+ " to " + value + " failed with error code " + retVal);
+ }
+
+ return retVal;
+ }
+
+ /**
+ * 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.
+ * Synchronous blocking call.
+ *
+ * @param item as defined in com.android.ims.ImsConfig#ConfigConstants.
+ * @param value in String format.
+ * @return as defined in com.android.ims.ImsConfig#OperationStatusConstants.
+ */
+ @Override
+ public synchronized int setProvisionedStringValue(int item, String value)
+ throws RemoteException {
+ mProvisionedStringValue.remove(item);
+ int retVal = getImsConfigImpl().setProvisionedStringValue(item, value);
+ if (retVal == ImsConfig.OperationStatusConstants.SUCCESS) {
+ updateCachedValue(item, value, true);
+ }
+
+ return retVal;
+ }
+
+ /**
+ * Wrapper function to call ImsConfigImplBase.getFeatureValue.
+ */
+ @Override
+ public void getFeatureValue(int feature, int network, ImsConfigListener listener)
+ throws RemoteException {
+ getImsConfigImpl().getFeatureValue(feature, network, listener);
+ }
+
+ /**
+ * Wrapper function to call ImsConfigImplBase.setFeatureValue.
+ */
+ @Override
+ public void setFeatureValue(int feature, int network, int value, ImsConfigListener listener)
+ throws RemoteException {
+ getImsConfigImpl().setFeatureValue(feature, network, value, listener);
+ }
+
+ /**
+ * Wrapper function to call ImsConfigImplBase.getVolteProvisioned.
+ */
+ @Override
+ public boolean getVolteProvisioned() throws RemoteException {
+ return getImsConfigImpl().getVolteProvisioned();
+ }
+
+ /**
+ * Wrapper function to call ImsConfigImplBase.getVideoQuality.
+ */
+ @Override
+ public void getVideoQuality(ImsConfigListener listener) throws RemoteException {
+ getImsConfigImpl().getVideoQuality(listener);
+ }
+
+ /**
+ * Wrapper function to call ImsConfigImplBase.setVideoQuality.
+ */
+ @Override
+ public void setVideoQuality(int quality, ImsConfigListener listener)
+ throws RemoteException {
+ getImsConfigImpl().setVideoQuality(quality, listener);
+ }
+
+ private ImsConfigImplBase getImsConfigImpl() throws RemoteException {
+ ImsConfigImplBase ref = mImsConfigImplBaseWeakReference.get();
+ if (ref == null) {
+ throw new RemoteException("Fail to get ImsConfigImpl");
+ } else {
+ return ref;
+ }
+ }
+
+ private void sendImsConfigChangedIntent(int item, int value) {
+ sendImsConfigChangedIntent(item, Integer.toString(value));
+ }
+
+ private void sendImsConfigChangedIntent(int item, String value) {
+ Intent configChangedIntent = new Intent(ImsConfig.ACTION_IMS_CONFIG_CHANGED);
+ configChangedIntent.putExtra(ImsConfig.EXTRA_CHANGED_ITEM, item);
+ configChangedIntent.putExtra(ImsConfig.EXTRA_NEW_VALUE, value);
+ if (mContext != null) {
+ mContext.sendBroadcast(configChangedIntent);
+ }
+ }
+
+ protected synchronized void updateCachedValue(int item, int value, boolean notifyChange) {
+ mProvisionedIntValue.put(item, value);
+ if (notifyChange) {
+ sendImsConfigChangedIntent(item, value);
+ }
+ }
+
+ protected synchronized void updateCachedValue(
+ int item, String value, boolean notifyChange) {
+ mProvisionedStringValue.put(item, value);
+ if (notifyChange) {
+ sendImsConfigChangedIntent(item, value);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/android/telephony/ims/compat/stub/ImsUtListenerImplBase.java b/android/telephony/ims/compat/stub/ImsUtListenerImplBase.java
new file mode 100644
index 0000000..ce291d4
--- /dev/null
+++ b/android/telephony/ims/compat/stub/ImsUtListenerImplBase.java
@@ -0,0 +1,107 @@
+/*
+ * 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.compat.stub;
+
+import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.telephony.ims.ImsCallForwardInfo;
+import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.ImsSsData;
+import android.telephony.ims.ImsSsInfo;
+
+import com.android.ims.internal.IImsUt;
+import com.android.ims.internal.IImsUtListener;
+
+/**
+ * Base implementation of ImsUtListener, which implements stub versions of the methods
+ * in the IImsUtListener AIDL. Override the methods that your implementation of
+ * ImsUtListener supports.
+ *
+ * DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
+ * will break other implementations of ImsUtListener maintained by other ImsServices.
+ *
+ * @hide
+ */
+
+public class ImsUtListenerImplBase extends IImsUtListener.Stub {
+
+ @UnsupportedAppUsage
+ public ImsUtListenerImplBase() {
+ }
+
+ /**
+ * Notifies the result of the supplementary service configuration udpate.
+ */
+ @Override
+ public void utConfigurationUpdated(IImsUt ut, int id) throws RemoteException {
+ }
+
+ @Override
+ public void utConfigurationUpdateFailed(IImsUt ut, int id, ImsReasonInfo error)
+ throws RemoteException {
+ }
+
+ /**
+ * Notifies the result of the supplementary service configuration query.
+ */
+ @Override
+ public void utConfigurationQueried(IImsUt ut, int id, Bundle ssInfo) throws RemoteException {
+ }
+
+ @Override
+ public void utConfigurationQueryFailed(IImsUt ut, int id, ImsReasonInfo error)
+ throws RemoteException {
+ }
+
+ /**
+ * Notifies the result of a line identification supplementary service query.
+ */
+ @Override
+ public void lineIdentificationSupplementaryServiceResponse(int id, ImsSsInfo config) {
+ }
+
+ /**
+ * Notifies the status of the call barring supplementary service.
+ */
+ @Override
+ public void utConfigurationCallBarringQueried(IImsUt ut, int id, ImsSsInfo[] cbInfo)
+ throws RemoteException {
+ }
+
+ /**
+ * Notifies the status of the call forwarding supplementary service.
+ */
+ @Override
+ public void utConfigurationCallForwardQueried(IImsUt ut, int id, ImsCallForwardInfo[] cfInfo)
+ throws RemoteException {
+ }
+
+ /**
+ * Notifies the status of the call waiting supplementary service.
+ */
+ @Override
+ public void utConfigurationCallWaitingQueried(IImsUt ut, int id, ImsSsInfo[] cwInfo)
+ throws RemoteException {
+ }
+
+ /**
+ * Notifies client when Supplementary Service indication is received
+ */
+ @Override
+ public void onSupplementaryServiceIndication(ImsSsData ssData) {}
+}
diff --git a/android/telephony/ims/feature/CapabilityChangeRequest.java b/android/telephony/ims/feature/CapabilityChangeRequest.java
new file mode 100644
index 0000000..1918bcb
--- /dev/null
+++ b/android/telephony/ims/feature/CapabilityChangeRequest.java
@@ -0,0 +1,258 @@
+/*
+ * 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.feature;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
+import android.util.ArraySet;
+
+import java.util.ArrayList;
+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.
+ * {@hide}
+ */
+@SystemApi
+@TestApi
+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
+ * {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE} or
+ * {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN}
+ */
+ public static class CapabilityPair {
+ private final int mCapability;
+ private final int radioTech;
+
+ public CapabilityPair(@MmTelFeature.MmTelCapabilities.MmTelCapability int capability,
+ @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) {
+ this.mCapability = capability;
+ this.radioTech = radioTech;
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof CapabilityPair)) return false;
+
+ CapabilityPair that = (CapabilityPair) o;
+
+ if (getCapability() != that.getCapability()) return false;
+ return getRadioTech() == that.getRadioTech();
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public int hashCode() {
+ int result = getCapability();
+ result = 31 * result + getRadioTech();
+ return result;
+ }
+
+ /**
+ * @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}
+ */
+ public @MmTelFeature.MmTelCapabilities.MmTelCapability int getCapability() {
+ return mCapability;
+ }
+
+ /**
+ * @return the stored radio technology, defined as
+ * {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE} or
+ * {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN}
+ */
+ public @ImsRegistrationImplBase.ImsRegistrationTech int getRadioTech() {
+ return radioTech;
+ }
+
+ @NonNull
+ @Override
+ public String toString() {
+ return "CapabilityPair{"
+ + "mCapability=" + mCapability
+ + ", radioTech=" + radioTech + '}';
+ }
+ }
+
+ // Pair contains <radio tech, mCapability>
+ private final Set<CapabilityPair> mCapabilitiesToEnable;
+ // Pair contains <radio tech, mCapability>
+ private final Set<CapabilityPair> mCapabilitiesToDisable;
+
+ /** @hide */
+ public CapabilityChangeRequest() {
+ mCapabilitiesToEnable = new ArraySet<>();
+ mCapabilitiesToDisable = new ArraySet<>();
+ }
+
+ /**
+ * 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}.
+ * @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,
+ @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) {
+ addAllCapabilities(mCapabilitiesToEnable, capabilities, radioTech);
+ }
+
+ /**
+ * 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}.
+ * @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,
+ @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) {
+ addAllCapabilities(mCapabilitiesToDisable, capabilities, radioTech);
+ }
+
+ /**
+ * @return a {@link List} of {@link CapabilityPair}s that are requesting to be enabled.
+ */
+ public List<CapabilityPair> getCapabilitiesToEnable() {
+ return new ArrayList<>(mCapabilitiesToEnable);
+ }
+
+ /**
+ * @return a {@link List} of {@link CapabilityPair}s that are requesting to be disabled.
+ */
+ public List<CapabilityPair> getCapabilitiesToDisable() {
+ return new ArrayList<>(mCapabilitiesToDisable);
+ }
+
+ // Iterate through capabilities bitfield and add each one as a pair associated with the radio
+ // technology
+ private void addAllCapabilities(Set<CapabilityPair> set, int capabilities, int tech) {
+ long highestCapability = Long.highestOneBit(capabilities);
+ for (int i = 1; i <= highestCapability; i *= 2) {
+ if ((i & capabilities) > 0) {
+ set.add(new CapabilityPair(/*capability*/ i, /*radioTech*/ tech));
+ }
+ }
+ }
+
+ /**
+ * @hide
+ */
+ protected CapabilityChangeRequest(Parcel in) {
+ int enableSize = in.readInt();
+ mCapabilitiesToEnable = new ArraySet<>(enableSize);
+ for (int i = 0; i < enableSize; i++) {
+ mCapabilitiesToEnable.add(new CapabilityPair(/*capability*/ in.readInt(),
+ /*radioTech*/ in.readInt()));
+ }
+ int disableSize = in.readInt();
+ mCapabilitiesToDisable = new ArraySet<>(disableSize);
+ for (int i = 0; i < disableSize; i++) {
+ mCapabilitiesToDisable.add(new CapabilityPair(/*capability*/ in.readInt(),
+ /*radioTech*/ in.readInt()));
+ }
+ }
+
+ public static final @android.annotation.NonNull Creator<CapabilityChangeRequest> CREATOR =
+ new Creator<CapabilityChangeRequest>() {
+ @Override
+ public CapabilityChangeRequest createFromParcel(Parcel in) {
+ return new CapabilityChangeRequest(in);
+ }
+
+ @Override
+ public CapabilityChangeRequest[] newArray(int size) {
+ return new CapabilityChangeRequest[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mCapabilitiesToEnable.size());
+ for (CapabilityPair pair : mCapabilitiesToEnable) {
+ dest.writeInt(pair.getCapability());
+ dest.writeInt(pair.getRadioTech());
+ }
+ dest.writeInt(mCapabilitiesToDisable.size());
+ for (CapabilityPair pair : mCapabilitiesToDisable) {
+ dest.writeInt(pair.getCapability());
+ dest.writeInt(pair.getRadioTech());
+ }
+ }
+
+ @NonNull
+ @Override
+ public String toString() {
+ return "CapabilityChangeRequest{"
+ + "mCapabilitiesToEnable=" + mCapabilitiesToEnable
+ + ", mCapabilitiesToDisable=" + mCapabilitiesToDisable + '}';
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof CapabilityChangeRequest)) return false;
+
+ CapabilityChangeRequest
+ that = (CapabilityChangeRequest) o;
+
+ if (!mCapabilitiesToEnable.equals(that.mCapabilitiesToEnable)) return false;
+ return mCapabilitiesToDisable.equals(that.mCapabilitiesToDisable);
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public int hashCode() {
+ int result = mCapabilitiesToEnable.hashCode();
+ result = 31 * result + mCapabilitiesToDisable.hashCode();
+ return result;
+ }
+}
diff --git a/android/telephony/ims/feature/ImsFeature.java b/android/telephony/ims/feature/ImsFeature.java
new file mode 100644
index 0000000..e5779b3
--- /dev/null
+++ b/android/telephony/ims/feature/ImsFeature.java
@@ -0,0 +1,552 @@
+/*
+ * 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.feature;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.content.Context;
+import android.os.IInterface;
+import android.os.RemoteException;
+import android.telephony.SubscriptionManager;
+import android.telephony.ims.aidl.IImsCapabilityCallback;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
+import android.util.Log;
+
+import com.android.ims.internal.IImsFeatureStatusCallback;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.util.RemoteCallbackListExt;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Base class for all IMS features that are supported by the framework. Use a concrete subclass
+ * of {@link ImsFeature}, such as {@link MmTelFeature} or {@link RcsFeature}.
+ *
+ * @hide
+ */
+@SystemApi
+@TestApi
+public abstract class ImsFeature {
+
+ private static final String LOG_TAG = "ImsFeature";
+
+ /**
+ * Invalid feature value
+ * @hide
+ */
+ public static final int FEATURE_INVALID = -1;
+ // ImsFeatures that are defined in the Manifests. Ensure that these values match the previously
+ // defined values in ImsServiceClass for compatibility purposes.
+ /**
+ * This feature supports emergency calling over MMTEL. If defined, the framework will try to
+ * place an emergency call over IMS first. If it is not defined, the framework will only use
+ * CSFB for emergency calling.
+ * @hide
+ */
+ @SystemApi @TestApi
+ public static final int FEATURE_EMERGENCY_MMTEL = 0;
+ /**
+ * This feature supports the MMTEL feature.
+ * @hide
+ */
+ @SystemApi @TestApi
+ public static final int FEATURE_MMTEL = 1;
+ /**
+ * This feature supports the RCS feature.
+ * @hide
+ */
+ @SystemApi @TestApi
+ public static final int FEATURE_RCS = 2;
+ /**
+ * Total number of features defined
+ * @hide
+ */
+ public static final int FEATURE_MAX = 3;
+
+ /**
+ * Used for logging purposes.
+ * @hide
+ */
+ public static final Map<Integer, String> FEATURE_LOG_MAP = new HashMap<Integer, String>() {{
+ put(FEATURE_EMERGENCY_MMTEL, "EMERGENCY_MMTEL");
+ put(FEATURE_MMTEL, "MMTEL");
+ put(FEATURE_RCS, "RCS");
+ }};
+
+ /**
+ * Integer values defining IMS features that are supported in ImsFeature.
+ * @hide
+ */
+ @IntDef(flag = true,
+ value = {
+ FEATURE_EMERGENCY_MMTEL,
+ FEATURE_MMTEL,
+ FEATURE_RCS
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface FeatureType {}
+
+ /**
+ * Integer values defining the state of the ImsFeature at any time.
+ * @hide
+ */
+ @IntDef(flag = true,
+ value = {
+ STATE_UNAVAILABLE,
+ STATE_INITIALIZING,
+ STATE_READY,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ImsState {}
+
+ /**
+ * This {@link ImsFeature}'s state is unavailable and should not be communicated with. This will
+ * remove all bindings back to the framework. Any attempt to communicate with the framework
+ * during this time will result in an {@link IllegalStateException}.
+ * @hide
+ */
+ @SystemApi @TestApi
+ public static final int STATE_UNAVAILABLE = 0;
+ /**
+ * This {@link ImsFeature} state is initializing and should not be communicated with. This will
+ * remove all bindings back to the framework. Any attempt to communicate with the framework
+ * during this time will result in an {@link IllegalStateException}.
+ * @hide
+ */
+ @SystemApi @TestApi
+ public static final int STATE_INITIALIZING = 1;
+ /**
+ * This {@link ImsFeature} is ready for communication. Do not attempt to call framework methods
+ * until {@see #onFeatureReady()} is called.
+ * @hide
+ */
+ @SystemApi @TestApi
+ public static final int STATE_READY = 2;
+
+ /**
+ * Used for logging purposes.
+ * @hide
+ */
+ public static final Map<Integer, String> STATE_LOG_MAP = new HashMap<Integer, String>() {{
+ put(STATE_UNAVAILABLE, "UNAVAILABLE");
+ put(STATE_INITIALIZING, "INITIALIZING");
+ put(STATE_READY, "READY");
+ }};
+
+ /**
+ * Integer values defining the result codes that should be returned from
+ * {@link #changeEnabledCapabilities} when the framework tries to set a feature's capability.
+ * @hide
+ */
+ @IntDef(flag = true,
+ value = {
+ CAPABILITY_ERROR_GENERIC,
+ CAPABILITY_SUCCESS
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ImsCapabilityError {}
+
+ /**
+ * The capability was unable to be changed.
+ * @hide
+ */
+ @SystemApi @TestApi
+ public static final int CAPABILITY_ERROR_GENERIC = -1;
+ /**
+ * The capability was able to be changed.
+ * @hide
+ */
+ @SystemApi @TestApi
+ public static final int CAPABILITY_SUCCESS = 0;
+
+ /**
+ * Used by the ImsFeature to call back to the CapabilityCallback that the framework has
+ * provided.
+ */
+ protected static class CapabilityCallbackProxy {
+ private final IImsCapabilityCallback mCallback;
+
+ /** @hide */
+ public CapabilityCallbackProxy(IImsCapabilityCallback c) {
+ mCallback = c;
+ }
+
+ /**
+ * This method notifies the provided framework callback that the request to change the
+ * indicated capability has failed and has not changed.
+ *
+ * @param capability The Capability that will be notified to the framework, 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}.
+ * @param radioTech The radio tech that this capability failed for, defined as
+ * {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE} or
+ * {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN}.
+ * @param reason The reason this capability was unable to be changed, defined as
+ * {@link #CAPABILITY_ERROR_GENERIC} or {@link #CAPABILITY_SUCCESS}.
+ */
+ public void onChangeCapabilityConfigurationError(int capability, int radioTech,
+ @ImsCapabilityError int reason) {
+ if (mCallback == null) {
+ return;
+ }
+ try {
+ mCallback.onChangeCapabilityConfigurationError(capability, radioTech, reason);
+ } catch (RemoteException e) {
+ Log.e(LOG_TAG, "onChangeCapabilityConfigurationError called on dead binder.");
+ }
+ }
+ }
+
+ /**
+ * Contains the IMS capabilities defined and supported by an ImsFeature in the form of a
+ * bit-mask.
+ *
+ * @deprecated This class is not used directly, but rather extended in subclasses of
+ * {@link ImsFeature} to provide service specific capabilities.
+ * @see MmTelFeature.MmTelCapabilities
+ * @hide
+ */
+ // Not Actually deprecated, but we need to remove it from the @SystemApi surface.
+ @Deprecated
+ @SystemApi // SystemApi only because it was leaked through type usage in a previous release.
+ @TestApi
+ public static class Capabilities {
+ /** @deprecated Use getters and accessors instead. */
+ // Not actually deprecated, but we need to remove it from the @SystemApi surface eventually.
+ protected int mCapabilities = 0;
+
+ /**
+ * @hide
+ */
+ public Capabilities() {
+ }
+
+ /**
+ * @hide
+ */
+ protected Capabilities(int capabilities) {
+ mCapabilities = capabilities;
+ }
+
+ /**
+ * @param capabilities Capabilities to be added to the configuration in the form of a
+ * bit mask.
+ * @hide
+ */
+ public void addCapabilities(int capabilities) {
+ mCapabilities |= capabilities;
+ }
+
+ /**
+ * @param capabilities Capabilities to be removed to the configuration in the form of a
+ * bit mask.
+ * @hide
+ */
+ public void removeCapabilities(int capabilities) {
+ mCapabilities &= ~capabilities;
+ }
+
+ /**
+ * @return true if all of the capabilities specified are capable.
+ * @hide
+ */
+ public boolean isCapable(int capabilities) {
+ return (mCapabilities & capabilities) == capabilities;
+ }
+
+ /**
+ * @return a deep copy of the Capabilites.
+ * @hide
+ */
+ public Capabilities copy() {
+ return new Capabilities(mCapabilities);
+ }
+
+ /**
+ * @return a bitmask containing the capability flags directly.
+ * @hide
+ */
+ public int getMask() {
+ return mCapabilities;
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof Capabilities)) return false;
+
+ Capabilities that = (Capabilities) o;
+
+ return mCapabilities == that.mCapabilities;
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public int hashCode() {
+ return mCapabilities;
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public String toString() {
+ return "Capabilities: " + Integer.toBinaryString(mCapabilities);
+ }
+ }
+
+ /** @hide */
+ protected Context mContext;
+ /** @hide */
+ protected final Object mLock = new Object();
+
+ private final RemoteCallbackListExt<IImsFeatureStatusCallback> mStatusCallbacks =
+ new RemoteCallbackListExt<>();
+ private @ImsState int mState = STATE_UNAVAILABLE;
+ private int mSlotId = SubscriptionManager.INVALID_SIM_SLOT_INDEX;
+ private final RemoteCallbackListExt<IImsCapabilityCallback> mCapabilityCallbacks =
+ new RemoteCallbackListExt<>();
+ private Capabilities mCapabilityStatus = new Capabilities();
+
+ /**
+ * @hide
+ */
+ public final void initialize(Context context, int slotId) {
+ mContext = context;
+ mSlotId = slotId;
+ }
+
+ /**
+ * @return The SIM slot index associated with this ImsFeature.
+ *
+ * @see SubscriptionManager#getSubscriptionIds(int) for more information on getting the
+ * subscription IDs associated with this slot.
+ * @hide
+ */
+ @SystemApi @TestApi
+ public final int getSlotIndex() {
+ return mSlotId;
+ }
+
+ /**
+ * @return The current state of the ImsFeature, set previously by {@link #setFeatureState(int)}
+ * or {@link #STATE_UNAVAILABLE} if it has not been updated yet.
+ * @hide
+ */
+ @SystemApi @TestApi
+ public @ImsState int getFeatureState() {
+ synchronized (mLock) {
+ return mState;
+ }
+ }
+
+ /**
+ * Set the state of the ImsFeature. The state is used as a signal to the framework to start or
+ * stop communication, depending on the state sent.
+ * @param state The ImsFeature's state, defined as {@link #STATE_UNAVAILABLE},
+ * {@link #STATE_INITIALIZING}, or {@link #STATE_READY}.
+ * @hide
+ */
+ @SystemApi @TestApi
+ public final void setFeatureState(@ImsState int state) {
+ synchronized (mLock) {
+ if (mState != state) {
+ mState = state;
+ notifyFeatureState(state);
+ }
+ }
+ }
+
+ /**
+ * Not final for testing, but shouldn't be extended!
+ * @hide
+ */
+ @VisibleForTesting
+ public void addImsFeatureStatusCallback(@NonNull IImsFeatureStatusCallback c) {
+ try {
+ // If we have just connected, send queued status.
+ c.notifyImsFeatureStatus(getFeatureState());
+ // Add the callback if the callback completes successfully without a RemoteException.
+ mStatusCallbacks.register(c);
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, "Couldn't notify feature state: " + e.getMessage());
+ }
+ }
+
+ /**
+ * Not final for testing, but shouldn't be extended!
+ * @hide
+ */
+ @VisibleForTesting
+ public void removeImsFeatureStatusCallback(@NonNull IImsFeatureStatusCallback c) {
+ mStatusCallbacks.unregister(c);
+ }
+
+ /**
+ * Internal method called by ImsFeature when setFeatureState has changed.
+ */
+ private void notifyFeatureState(@ImsState int state) {
+ mStatusCallbacks.broadcastAction((c) -> {
+ try {
+ c.notifyImsFeatureStatus(state);
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, e + " notifyFeatureState() - Skipping "
+ + "callback.");
+ }
+ });
+ }
+
+ /**
+ * @hide
+ */
+ public final void addCapabilityCallback(IImsCapabilityCallback c) {
+ mCapabilityCallbacks.register(c);
+ try {
+ // Notify the Capability callback that was just registered of the current capabilities.
+ c.onCapabilitiesStatusChanged(queryCapabilityStatus().mCapabilities);
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, "addCapabilityCallback: error accessing callback: " + e.getMessage());
+ }
+ }
+
+ /**
+ * @hide
+ */
+ final void removeCapabilityCallback(IImsCapabilityCallback c) {
+ mCapabilityCallbacks.unregister(c);
+ }
+
+ /**@hide*/
+ final void queryCapabilityConfigurationInternal(int capability, int radioTech,
+ IImsCapabilityCallback c) {
+ boolean enabled = queryCapabilityConfiguration(capability, radioTech);
+ try {
+ if (c != null) {
+ c.onQueryCapabilityConfiguration(capability, radioTech, enabled);
+ }
+ } catch (RemoteException e) {
+ Log.e(LOG_TAG, "queryCapabilityConfigurationInternal called on dead binder!");
+ }
+ }
+
+ /**
+ * @return the cached capabilities status for this feature.
+ * @hide
+ */
+ @VisibleForTesting
+ public Capabilities queryCapabilityStatus() {
+ synchronized (mLock) {
+ return mCapabilityStatus.copy();
+ }
+ }
+
+ /**
+ * Called internally to request the change of enabled capabilities.
+ * @hide
+ */
+ @VisibleForTesting
+ public final void requestChangeEnabledCapabilities(CapabilityChangeRequest request,
+ IImsCapabilityCallback c) {
+ if (request == null) {
+ throw new IllegalArgumentException(
+ "ImsFeature#requestChangeEnabledCapabilities called with invalid params.");
+ }
+ changeEnabledCapabilities(request, new CapabilityCallbackProxy(c));
+ }
+
+ /**
+ * Called by the ImsFeature when the capabilities status has changed.
+ *
+ * @param caps the new {@link Capabilities} status of the {@link ImsFeature}.
+ *
+ * @hide
+ */
+ protected final void notifyCapabilitiesStatusChanged(Capabilities caps) {
+ synchronized (mLock) {
+ mCapabilityStatus = caps.copy();
+ }
+ mCapabilityCallbacks.broadcastAction((callback) -> {
+ try {
+ callback.onCapabilitiesStatusChanged(caps.mCapabilities);
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, e + " notifyCapabilitiesStatusChanged() - Skipping "
+ + "callback.");
+ }
+ });
+ }
+
+ /**
+ * Provides the ImsFeature with the ability to return the framework Capability Configuration
+ * for a provided Capability. If the framework calls {@link #changeEnabledCapabilities} and
+ * includes a capability A to enable or disable, this method should return the correct enabled
+ * status for capability A.
+ * @param capability The capability that we are querying the configuration for.
+ * @return true if the capability is enabled, false otherwise.
+ * @hide
+ */
+ public abstract boolean queryCapabilityConfiguration(int capability, int radioTech);
+
+ /**
+ * Features should override this method to receive Capability preference change requests from
+ * the framework using the provided {@link CapabilityChangeRequest}. If any of the capabilities
+ * in the {@link CapabilityChangeRequest} are not able to be completed due to an error,
+ * {@link CapabilityCallbackProxy#onChangeCapabilityConfigurationError} should be called for
+ * each failed capability.
+ *
+ * @param request A {@link CapabilityChangeRequest} containing requested capabilities to
+ * enable/disable.
+ * @param c A {@link CapabilityCallbackProxy}, which will be used to call back to the framework
+ * setting a subset of these capabilities fail, using
+ * {@link CapabilityCallbackProxy#onChangeCapabilityConfigurationError}.
+ */
+ public abstract void changeEnabledCapabilities(CapabilityChangeRequest request,
+ CapabilityCallbackProxy c);
+
+ /**
+ * Called when the framework is removing this feature and it needs to be cleaned up.
+ */
+ public abstract void onFeatureRemoved();
+
+ /**
+ * Called after this ImsFeature has been initialized and has been set to the
+ * {@link ImsState#STATE_READY} state.
+ * <p>
+ * Any attempt by this feature to access the framework before this method is called will return
+ * with an {@link IllegalStateException}.
+ * The IMS provider should use this method to trigger registration for this feature on the IMS
+ * network, if needed.
+ */
+ public abstract void onFeatureReady();
+
+ /**
+ * @return Binder instance that the framework will use to communicate with this feature.
+ * @hide
+ */
+ protected abstract IInterface getBinder();
+}
diff --git a/android/telephony/ims/feature/MmTelFeature.java b/android/telephony/ims/feature/MmTelFeature.java
new file mode 100644
index 0000000..b3b7b20
--- /dev/null
+++ b/android/telephony/ims/feature/MmTelFeature.java
@@ -0,0 +1,821 @@
+/*
+ * 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.feature;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.os.Bundle;
+import android.os.Message;
+import android.os.RemoteException;
+import android.telecom.TelecomManager;
+import android.telephony.ims.ImsCallProfile;
+import android.telephony.ims.ImsCallSession;
+import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.aidl.IImsCapabilityCallback;
+import android.telephony.ims.aidl.IImsMmTelFeature;
+import android.telephony.ims.aidl.IImsMmTelListener;
+import android.telephony.ims.aidl.IImsSmsListener;
+import android.telephony.ims.stub.ImsCallSessionImplBase;
+import android.telephony.ims.stub.ImsEcbmImplBase;
+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 com.android.ims.internal.IImsCallSession;
+import com.android.ims.internal.IImsEcbm;
+import com.android.ims.internal.IImsMultiEndpoint;
+import com.android.ims.internal.IImsUt;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Base implementation for Voice and SMS (IR-92) and Video (IR-94) IMS support.
+ *
+ * Any class wishing to use MmTelFeature should extend this class and implement all methods that the
+ * service supports.
+ */
+public class MmTelFeature extends ImsFeature {
+
+ private static final String LOG_TAG = "MmTelFeature";
+
+ /**
+ * @hide
+ */
+ @SystemApi @TestApi
+ public MmTelFeature() {
+ }
+
+ private final IImsMmTelFeature mImsMMTelBinder = new IImsMmTelFeature.Stub() {
+
+ @Override
+ public void setListener(IImsMmTelListener l) {
+ MmTelFeature.this.setListener(l);
+ }
+
+ @Override
+ public int getFeatureState() throws RemoteException {
+ try {
+ return MmTelFeature.this.getFeatureState();
+ } catch (Exception e) {
+ throw new RemoteException(e.getMessage());
+ }
+ }
+
+
+ @Override
+ public ImsCallProfile createCallProfile(int callSessionType, int callType)
+ throws RemoteException {
+ synchronized (mLock) {
+ try {
+ return MmTelFeature.this.createCallProfile(callSessionType, callType);
+ } catch (Exception e) {
+ throw new RemoteException(e.getMessage());
+ }
+ }
+ }
+
+ @Override
+ public IImsCallSession createCallSession(ImsCallProfile profile) throws RemoteException {
+ synchronized (mLock) {
+ return createCallSessionInterface(profile);
+ }
+ }
+
+ @Override
+ public int shouldProcessCall(String[] numbers) {
+ synchronized (mLock) {
+ return MmTelFeature.this.shouldProcessCall(numbers);
+ }
+ }
+
+ @Override
+ public IImsUt getUtInterface() throws RemoteException {
+ synchronized (mLock) {
+ return MmTelFeature.this.getUtInterface();
+ }
+ }
+
+ @Override
+ public IImsEcbm getEcbmInterface() throws RemoteException {
+ synchronized (mLock) {
+ return MmTelFeature.this.getEcbmInterface();
+ }
+ }
+
+ @Override
+ public void setUiTtyMode(int uiTtyMode, Message onCompleteMessage) throws RemoteException {
+ synchronized (mLock) {
+ try {
+ MmTelFeature.this.setUiTtyMode(uiTtyMode, onCompleteMessage);
+ } catch (Exception e) {
+ throw new RemoteException(e.getMessage());
+ }
+ }
+ }
+
+ @Override
+ public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException {
+ synchronized (mLock) {
+ return MmTelFeature.this.getMultiEndpointInterface();
+ }
+ }
+
+ @Override
+ public int queryCapabilityStatus() {
+ return MmTelFeature.this.queryCapabilityStatus().mCapabilities;
+ }
+
+ @Override
+ public void addCapabilityCallback(IImsCapabilityCallback c) {
+ // no need to lock, structure already handles multithreading.
+ MmTelFeature.this.addCapabilityCallback(c);
+ }
+
+ @Override
+ public void removeCapabilityCallback(IImsCapabilityCallback c) {
+ // no need to lock, structure already handles multithreading.
+ MmTelFeature.this.removeCapabilityCallback(c);
+ }
+
+ @Override
+ public void changeCapabilitiesConfiguration(CapabilityChangeRequest request,
+ IImsCapabilityCallback c) {
+ MmTelFeature.this.requestChangeEnabledCapabilities(request, c);
+ }
+
+ @Override
+ public void queryCapabilityConfiguration(int capability, int radioTech,
+ IImsCapabilityCallback c) {
+ queryCapabilityConfigurationInternal(capability, radioTech, c);
+ }
+
+ @Override
+ public void setSmsListener(IImsSmsListener l) {
+ MmTelFeature.this.setSmsListener(l);
+ }
+
+ @Override
+ public void sendSms(int token, int messageRef, String format, String smsc, boolean retry,
+ byte[] pdu) {
+ synchronized (mLock) {
+ MmTelFeature.this.sendSms(token, messageRef, format, smsc, retry, pdu);
+ }
+ }
+
+ @Override
+ public void acknowledgeSms(int token, int messageRef, int result) {
+ synchronized (mLock) {
+ MmTelFeature.this.acknowledgeSms(token, messageRef, result);
+ }
+ }
+
+ @Override
+ public void acknowledgeSmsReport(int token, int messageRef, int result) {
+ synchronized (mLock) {
+ MmTelFeature.this.acknowledgeSmsReport(token, messageRef, result);
+ }
+ }
+
+ @Override
+ public String getSmsFormat() {
+ synchronized (mLock) {
+ return MmTelFeature.this.getSmsFormat();
+ }
+ }
+
+ @Override
+ public void onSmsReady() {
+ synchronized (mLock) {
+ MmTelFeature.this.onSmsReady();
+ }
+ }
+ };
+
+ /**
+ * Contains the capabilities defined and supported by a MmTelFeature in the form of a Bitmask.
+ * 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}.
+ *
+ * The capabilities of this MmTelFeature will be set by the framework.
+ */
+ public static class MmTelCapabilities extends Capabilities {
+
+ /**
+ * Create a new empty {@link MmTelCapabilities} instance.
+ * @see #addCapabilities(int)
+ * @see #removeCapabilities(int)
+ * @hide
+ */
+ @SystemApi @TestApi
+ public MmTelCapabilities() {
+ super();
+ }
+
+ /**@deprecated Use {@link MmTelCapabilities} to construct a new instance instead.
+ * @hide
+ */
+ @Deprecated
+ @SystemApi @TestApi
+ public MmTelCapabilities(Capabilities c) {
+ mCapabilities = c.mCapabilities;
+ }
+
+ /**
+ * Create a new {link @MmTelCapabilities} instance with the provided capabilities.
+ * @param capabilities The capabilities that are supported for MmTel in the form of a
+ * bitfield.
+ * @hide
+ */
+ @SystemApi @TestApi
+ public MmTelCapabilities(@MmTelCapability int capabilities) {
+ super(capabilities);
+ }
+
+ /** @hide */
+ @IntDef(flag = true,
+ value = {
+ CAPABILITY_TYPE_VOICE,
+ CAPABILITY_TYPE_VIDEO,
+ CAPABILITY_TYPE_UT,
+ CAPABILITY_TYPE_SMS
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface MmTelCapability {}
+
+ /**
+ * This MmTelFeature supports Voice calling (IR.92)
+ */
+ public static final int CAPABILITY_TYPE_VOICE = 1 << 0;
+
+ /**
+ * This MmTelFeature supports Video (IR.94)
+ */
+ public static final int CAPABILITY_TYPE_VIDEO = 1 << 1;
+
+ /**
+ * This MmTelFeature supports XCAP over Ut for supplementary services. (IR.92)
+ */
+ public static final int CAPABILITY_TYPE_UT = 1 << 2;
+
+ /**
+ * This MmTelFeature supports SMS (IR.92)
+ */
+ public static final int CAPABILITY_TYPE_SMS = 1 << 3;
+
+ /**
+ * @hide
+ */
+ @Override
+ @SystemApi @TestApi
+ public final void addCapabilities(@MmTelCapability int capabilities) {
+ super.addCapabilities(capabilities);
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ @SystemApi @TestApi
+ public final void removeCapabilities(@MmTelCapability int capability) {
+ super.removeCapabilities(capability);
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ @SystemApi @TestApi
+ public final boolean isCapable(@MmTelCapability int capabilities) {
+ return super.isCapable(capabilities);
+ }
+
+ /**
+ * @hide
+ */
+ @NonNull
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder("MmTel Capabilities - [");
+ builder.append("Voice: ");
+ builder.append(isCapable(CAPABILITY_TYPE_VOICE));
+ builder.append(" Video: ");
+ builder.append(isCapable(CAPABILITY_TYPE_VIDEO));
+ builder.append(" UT: ");
+ builder.append(isCapable(CAPABILITY_TYPE_UT));
+ builder.append(" SMS: ");
+ builder.append(isCapable(CAPABILITY_TYPE_SMS));
+ builder.append("]");
+ return builder.toString();
+ }
+ }
+
+ /**
+ * Listener that the framework implements for communication from the MmTelFeature.
+ * @hide
+ */
+ public static class Listener extends IImsMmTelListener.Stub {
+
+ /**
+ * Called when the IMS provider receives an incoming call.
+ * @param c The {@link ImsCallSession} associated with the new call.
+ * @hide
+ */
+ @Override
+ @SystemApi @TestApi
+ public void onIncomingCall(IImsCallSession c, Bundle extras) {
+
+ }
+
+ /**
+ * Called when the IMS provider implicitly rejects an incoming call during setup.
+ * @param callProfile An {@link ImsCallProfile} with the call details.
+ * @param reason The {@link ImsReasonInfo} reason for call rejection.
+ * @hide
+ */
+ @Override
+ @SystemApi @TestApi
+ public void onRejectedCall(ImsCallProfile callProfile, ImsReasonInfo reason) {
+
+ }
+
+ /**
+ * Updates the Listener when the voice message count for IMS has changed.
+ * @param count an integer representing the new message count.
+ * @hide
+ */
+ @Override
+ @SystemApi @TestApi
+ public void onVoiceMessageCountUpdate(int count) {
+
+ }
+ }
+
+ /**
+ * To be returned by {@link #shouldProcessCall(String[])} when the ImsService should process the
+ * outgoing call as IMS.
+ * @hide
+ */
+ @SystemApi @TestApi
+ public static final int PROCESS_CALL_IMS = 0;
+ /**
+ * To be returned by {@link #shouldProcessCall(String[])} when the telephony framework should
+ * not process the outgoing call as IMS and should instead use circuit switch.
+ * @hide
+ */
+ @SystemApi @TestApi
+ public static final int PROCESS_CALL_CSFB = 1;
+
+ /** @hide */
+ @IntDef(flag = true,
+ value = {
+ PROCESS_CALL_IMS,
+ PROCESS_CALL_CSFB
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ProcessCallResult {}
+
+ /**
+ * If the flag is present and true, it indicates that the incoming call is for USSD.
+ * <p>
+ * This is an optional boolean flag.
+ * @hide
+ */
+ @SystemApi @TestApi
+ public static final String EXTRA_IS_USSD = "android.telephony.ims.feature.extra.IS_USSD";
+
+ /**
+ * If this flag is present and true, this call is marked as an unknown dialing call instead
+ * of an incoming call. An example of such a call is a call that is originated by sending
+ * commands (like AT commands) directly to the modem without Android involvement or dialing
+ * calls appearing over IMS when the modem does a silent redial from circuit-switched to IMS in
+ * certain situations.
+ * <p>
+ * This is an optional boolean flag.
+ * @hide
+ */
+ @SystemApi @TestApi
+ public static final String EXTRA_IS_UNKNOWN_CALL =
+ "android.telephony.ims.feature.extra.IS_UNKNOWN_CALL";
+
+ private IImsMmTelListener mListener;
+
+ /**
+ * @param listener A {@link Listener} used when the MmTelFeature receives an incoming call and
+ * notifies the framework.
+ */
+ private void setListener(IImsMmTelListener listener) {
+ synchronized (mLock) {
+ mListener = listener;
+ if (mListener != null) {
+ onFeatureReady();
+ }
+ }
+ }
+
+ /**
+ * @return the listener associated with this MmTelFeature. May be null if it has not been set
+ * by the framework yet.
+ */
+ private IImsMmTelListener getListener() {
+ synchronized (mLock) {
+ return mListener;
+ }
+ }
+
+ /**
+ * The current capability status that this MmTelFeature has defined is available. This
+ * configuration will be used by the platform to figure out which capabilities are CURRENTLY
+ * available to be used.
+ *
+ * Should be a subset of the capabilities that are enabled by the framework in
+ * {@link #changeEnabledCapabilities}.
+ * @return A copy of the current MmTelFeature capability status.
+ * @hide
+ */
+ @Override
+ @SystemApi @TestApi
+ public @NonNull final MmTelCapabilities queryCapabilityStatus() {
+ return new MmTelCapabilities(super.queryCapabilityStatus());
+ }
+
+ /**
+ * Notify the framework that the status of the Capabilities has changed. Even though the
+ * MmTelFeature capability may be enabled by the framework, the status may be disabled due to
+ * the feature being unavailable from the network.
+ * @param c The current capability status of the MmTelFeature. If a capability is disabled, then
+ * the status of that capability is disabled. This can happen if the network does not currently
+ * support the capability that is enabled. A capability that is disabled by the framework (via
+ * {@link #changeEnabledCapabilities}) should also show the status as disabled.
+ * @hide
+ */
+ @SystemApi @TestApi
+ public final void notifyCapabilitiesStatusChanged(@NonNull MmTelCapabilities c) {
+ if (c == null) {
+ throw new IllegalArgumentException("MmTelCapabilities must be non-null!");
+ }
+ super.notifyCapabilitiesStatusChanged(c);
+ }
+
+ /**
+ * Notify the framework of an incoming call.
+ * @param c The {@link ImsCallSessionImplBase} of the new incoming call.
+ * @param extras A bundle containing extra parameters related to the call. See
+ * {@link #EXTRA_IS_UNKNOWN_CALL} and {@link #EXTRA_IS_USSD} above.
+ * @hide
+ */
+ @SystemApi @TestApi
+ public final void notifyIncomingCall(@NonNull ImsCallSessionImplBase c,
+ @NonNull Bundle extras) {
+ if (c == null || extras == null) {
+ throw new IllegalArgumentException("ImsCallSessionImplBase and Bundle can not be "
+ + "null.");
+ }
+ IImsMmTelListener listener = getListener();
+ if (listener == null) {
+ throw new IllegalStateException("Session is not available.");
+ }
+ try {
+ listener.onIncomingCall(c.getServiceImpl(), extras);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Notify the framework that a call has been implicitly rejected by this MmTelFeature
+ * during call setup.
+ * @param callProfile The {@link ImsCallProfile} IMS call profile with details.
+ * This can be null if no call information is available for the rejected call.
+ * @param reason The {@link ImsReasonInfo} call rejection reason.
+ * @hide
+ */
+ @SystemApi @TestApi
+ public final void notifyRejectedCall(@NonNull ImsCallProfile callProfile,
+ @NonNull ImsReasonInfo reason) {
+ if (callProfile == null || reason == null) {
+ throw new IllegalArgumentException("ImsCallProfile and ImsReasonInfo must not be "
+ + "null.");
+ }
+ IImsMmTelListener listener = getListener();
+ if (listener == null) {
+ throw new IllegalStateException("Session is not available.");
+ }
+ try {
+ listener.onRejectedCall(callProfile, reason);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ *
+ * @hide
+ */
+ public final void notifyIncomingCallSession(IImsCallSession c, Bundle extras) {
+ IImsMmTelListener listener = getListener();
+ if (listener == null) {
+ throw new IllegalStateException("Session is not available.");
+ }
+ try {
+ listener.onIncomingCall(c, extras);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Notify the framework of a change in the Voice Message count.
+ * @link count the new Voice Message count.
+ * @hide
+ */
+ @SystemApi @TestApi
+ public final void notifyVoiceMessageCountUpdate(int count) {
+ IImsMmTelListener listener = getListener();
+ if (listener == null) {
+ throw new IllegalStateException("Session is not available.");
+ }
+ try {
+ listener.onVoiceMessageCountUpdate(count);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Provides the MmTelFeature with the ability to return the framework Capability Configuration
+ * for a provided Capability. If the framework calls {@link #changeEnabledCapabilities} and
+ * includes a capability A to enable or disable, this method should return the correct enabled
+ * status for capability A.
+ * @param capability The capability that we are querying the configuration for.
+ * @return true if the capability is enabled, false otherwise.
+ * @hide
+ */
+ @Override
+ @SystemApi @TestApi
+ public boolean queryCapabilityConfiguration(@MmTelCapabilities.MmTelCapability int capability,
+ @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) {
+ // Base implementation - Override to provide functionality
+ return false;
+ }
+
+ /**
+ * The MmTelFeature should override this method to handle the enabling/disabling of
+ * MmTel Features, defined in {@link MmTelCapabilities.MmTelCapability}. The framework assumes
+ * the {@link CapabilityChangeRequest} was processed successfully. If a subset of capabilities
+ * could not be set to their new values,
+ * {@link CapabilityCallbackProxy#onChangeCapabilityConfigurationError} must be called
+ * individually for each capability whose processing resulted in an error.
+ *
+ * Enabling/Disabling a capability here indicates that the capability should be registered or
+ * deregistered (depending on the capability change) and become available or unavailable to
+ * the framework.
+ * * @hide
+ */
+ @Override
+ @SystemApi @TestApi
+ public void changeEnabledCapabilities(@NonNull CapabilityChangeRequest request,
+ @NonNull CapabilityCallbackProxy c) {
+ // Base implementation, no-op
+ }
+
+ /**
+ * Creates a {@link ImsCallProfile} from the service capabilities & IMS registration state.
+ *
+ * @param callSessionType a service type that is specified in {@link ImsCallProfile}
+ * {@link ImsCallProfile#SERVICE_TYPE_NONE}
+ * {@link ImsCallProfile#SERVICE_TYPE_NORMAL}
+ * {@link ImsCallProfile#SERVICE_TYPE_EMERGENCY}
+ * @param callType a call type that is specified in {@link ImsCallProfile}
+ * {@link ImsCallProfile#CALL_TYPE_VOICE}
+ * {@link ImsCallProfile#CALL_TYPE_VT}
+ * {@link ImsCallProfile#CALL_TYPE_VT_TX}
+ * {@link ImsCallProfile#CALL_TYPE_VT_RX}
+ * {@link ImsCallProfile#CALL_TYPE_VT_NODIR}
+ * {@link ImsCallProfile#CALL_TYPE_VS}
+ * {@link ImsCallProfile#CALL_TYPE_VS_TX}
+ * {@link ImsCallProfile#CALL_TYPE_VS_RX}
+ * @return a {@link ImsCallProfile} object
+ * @hide
+ */
+ @SystemApi @TestApi
+ public @Nullable ImsCallProfile createCallProfile(int callSessionType, int callType) {
+ // Base Implementation - Should be overridden
+ return null;
+ }
+
+ /**
+ * @hide
+ */
+ public IImsCallSession createCallSessionInterface(ImsCallProfile profile)
+ throws RemoteException {
+ ImsCallSessionImplBase s = MmTelFeature.this.createCallSession(profile);
+ return s != null ? s.getServiceImpl() : null;
+ }
+
+ /**
+ * Creates an {@link ImsCallSession} with the specified call profile.
+ * Use other methods, if applicable, instead of interacting with
+ * {@link ImsCallSession} directly.
+ *
+ * @param profile a call profile to make the call
+ * @hide
+ */
+ @SystemApi @TestApi
+ public @Nullable ImsCallSessionImplBase createCallSession(@NonNull ImsCallProfile profile) {
+ // Base Implementation - Should be overridden
+ return null;
+ }
+
+ /**
+ * Called by the framework to determine if the outgoing call, designated by the outgoing
+ * {@link String}s, should be processed as an IMS call or CSFB call. If this method's
+ * functionality is not overridden, the platform will process every call as IMS as long as the
+ * MmTelFeature reports that the {@link MmTelCapabilities#CAPABILITY_TYPE_VOICE} capability is
+ * available.
+ * @param numbers An array of {@link String}s that will be used for placing the call. There can
+ * be multiple {@link String}s listed in the case when we want to place an outgoing
+ * call as a conference.
+ * @return a {@link ProcessCallResult} to the framework, which will be used to determine if the
+ * call will be placed over IMS or via CSFB.
+ * @hide
+ */
+ @SystemApi @TestApi
+ public @ProcessCallResult int shouldProcessCall(@NonNull String[] numbers) {
+ return PROCESS_CALL_IMS;
+ }
+
+ /**
+ *
+ * @hide
+ */
+ protected IImsUt getUtInterface() throws RemoteException {
+ ImsUtImplBase utImpl = getUt();
+ return utImpl != null ? utImpl.getInterface() : null;
+ }
+
+ /**
+ * @hide
+ */
+ protected IImsEcbm getEcbmInterface() throws RemoteException {
+ ImsEcbmImplBase ecbmImpl = getEcbm();
+ return ecbmImpl != null ? ecbmImpl.getImsEcbm() : null;
+ }
+
+ /**
+ * @hide
+ */
+ public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException {
+ ImsMultiEndpointImplBase multiendpointImpl = getMultiEndpoint();
+ return multiendpointImpl != null ? multiendpointImpl.getIImsMultiEndpoint() : null;
+ }
+
+ /**
+ * @return The {@link ImsUtImplBase} Ut interface implementation for the supplementary service
+ * configuration.
+ * @hide
+ */
+ @SystemApi @TestApi
+ public @NonNull ImsUtImplBase getUt() {
+ // Base Implementation - Should be overridden
+ return new ImsUtImplBase();
+ }
+
+ /**
+ * @return The {@link ImsEcbmImplBase} Emergency call-back mode interface for emergency VoLTE
+ * calls that support it.
+ * @hide
+ */
+ @SystemApi @TestApi
+ public @NonNull ImsEcbmImplBase getEcbm() {
+ // Base Implementation - Should be overridden
+ return new ImsEcbmImplBase();
+ }
+
+ /**
+ * @return The {@link ImsMultiEndpointImplBase} implementation for implementing Dialog event
+ * package processing for multi-endpoint.
+ * @hide
+ */
+ @SystemApi @TestApi
+ public @NonNull ImsMultiEndpointImplBase getMultiEndpoint() {
+ // Base Implementation - Should be overridden
+ return new ImsMultiEndpointImplBase();
+ }
+
+ /**
+ * Sets the current UI TTY mode for the MmTelFeature.
+ * @param mode An integer containing the new UI TTY Mode, can consist of
+ * {@link TelecomManager#TTY_MODE_OFF},
+ * {@link TelecomManager#TTY_MODE_FULL},
+ * {@link TelecomManager#TTY_MODE_HCO},
+ * {@link TelecomManager#TTY_MODE_VCO}
+ * @param onCompleteMessage If non-null, this MmTelFeature should call this {@link Message} when
+ * the operation is complete by using the associated {@link android.os.Messenger} in
+ * {@link Message#replyTo}. For example:
+ * {@code
+ * // Set UI TTY Mode and other operations...
+ * try {
+ * // Notify framework that the mode was changed.
+ * Messenger uiMessenger = onCompleteMessage.replyTo;
+ * uiMessenger.send(onCompleteMessage);
+ * } catch (RemoteException e) {
+ * // Remote side is dead
+ * }
+ * }
+ * @hide
+ */
+ @SystemApi @TestApi
+ public void setUiTtyMode(int mode, @Nullable Message onCompleteMessage) {
+ // Base Implementation - Should be overridden
+ }
+
+ private void setSmsListener(IImsSmsListener listener) {
+ getSmsImplementation().registerSmsListener(listener);
+ }
+
+ private void sendSms(int token, int messageRef, String format, String smsc, boolean isRetry,
+ byte[] pdu) {
+ getSmsImplementation().sendSms(token, messageRef, format, smsc, isRetry, pdu);
+ }
+
+ private void acknowledgeSms(int token, int messageRef,
+ @ImsSmsImplBase.DeliverStatusResult int result) {
+ getSmsImplementation().acknowledgeSms(token, messageRef, result);
+ }
+
+ private void acknowledgeSmsReport(int token, int messageRef,
+ @ImsSmsImplBase.StatusReportResult int result) {
+ getSmsImplementation().acknowledgeSmsReport(token, messageRef, result);
+ }
+
+ private void onSmsReady() {
+ getSmsImplementation().onReady();
+ }
+
+ /**
+ * Must be overridden by IMS Provider to be able to support SMS over IMS. Otherwise a default
+ * non-functional implementation is returned.
+ *
+ * @return an instance of {@link ImsSmsImplBase} which should be implemented by the IMS
+ * Provider.
+ * @hide
+ */
+ @SystemApi @TestApi
+ public @NonNull ImsSmsImplBase getSmsImplementation() {
+ return new ImsSmsImplBase();
+ }
+
+ private String getSmsFormat() {
+ return getSmsImplementation().getSmsFormat();
+ }
+
+ /**
+ * {@inheritDoc}
+ * @hide
+ */
+ @Override
+ @SystemApi @TestApi
+ public void onFeatureRemoved() {
+ // Base Implementation - Should be overridden
+ }
+
+ /**
+ * {@inheritDoc}
+ * @hide
+ */
+ @Override
+ @SystemApi @TestApi
+ public void onFeatureReady() {
+ // Base Implementation - Should be overridden
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public final IImsMmTelFeature getBinder() {
+ return mImsMMTelBinder;
+ }
+}
diff --git a/android/telephony/ims/feature/RcsFeature.java b/android/telephony/ims/feature/RcsFeature.java
new file mode 100644
index 0000000..98b0bcf
--- /dev/null
+++ b/android/telephony/ims/feature/RcsFeature.java
@@ -0,0 +1,435 @@
+/*
+ * 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.feature;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.net.Uri;
+import android.os.RemoteException;
+import android.telephony.ims.RcsContactUceCapability;
+import android.telephony.ims.aidl.IImsCapabilityCallback;
+import android.telephony.ims.aidl.IImsRcsFeature;
+import android.telephony.ims.aidl.IRcsFeatureListener;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
+import android.telephony.ims.stub.RcsPresenceExchangeImplBase;
+import android.telephony.ims.stub.RcsSipOptionsImplBase;
+import android.util.Log;
+
+import com.android.internal.telephony.util.TelephonyUtils;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.List;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
+import java.util.function.Supplier;
+
+/**
+ * Base implementation of the RcsFeature APIs. Any ImsService wishing to support RCS should extend
+ * this class and provide implementations of the RcsFeature methods that they support.
+ * @hide
+ */
+@SystemApi
+@TestApi
+public class RcsFeature extends ImsFeature {
+
+ private static final String LOG_TAG = "RcsFeature";
+
+ private static final class RcsFeatureBinder extends IImsRcsFeature.Stub {
+ // Reference the outer class in order to have better test coverage metrics instead of
+ // creating a inner class referencing the outer class directly.
+ private final RcsFeature mReference;
+ private final Executor mExecutor;
+
+ RcsFeatureBinder(RcsFeature classRef, @CallbackExecutor Executor executor) {
+ mReference = classRef;
+ mExecutor = executor;
+ }
+
+ @Override
+ public void setListener(IRcsFeatureListener listener) {
+ mReference.setListener(listener);
+ }
+
+ @Override
+ public int queryCapabilityStatus() throws RemoteException {
+ return executeMethodAsyncForResult(
+ () -> mReference.queryCapabilityStatus().mCapabilities,
+ "queryCapabilityStatus");
+ }
+
+ @Override
+ public void addCapabilityCallback(IImsCapabilityCallback c) throws RemoteException {
+ executeMethodAsync(() -> mReference.addCapabilityCallback(c), "addCapabilityCallback");
+ }
+
+ @Override
+ public void removeCapabilityCallback(IImsCapabilityCallback c) throws RemoteException {
+ executeMethodAsync(() -> mReference.removeCapabilityCallback(c),
+ "removeCapabilityCallback");
+ }
+
+ @Override
+ public void changeCapabilitiesConfiguration(CapabilityChangeRequest r,
+ IImsCapabilityCallback c) throws RemoteException {
+ executeMethodAsync(() -> mReference.requestChangeEnabledCapabilities(r, c),
+ "changeCapabilitiesConfiguration");
+ }
+
+ @Override
+ public void queryCapabilityConfiguration(int capability, int radioTech,
+ IImsCapabilityCallback c) throws RemoteException {
+ executeMethodAsync(() -> mReference.queryCapabilityConfigurationInternal(capability,
+ radioTech, c), "queryCapabilityConfiguration");
+ }
+
+ @Override
+ public int getFeatureState() throws RemoteException {
+ return executeMethodAsyncForResult(mReference::getFeatureState, "getFeatureState");
+ }
+
+ // RcsPresenceExchangeImplBase specific APIS
+ @Override
+ public void requestCapabilities(List<Uri> uris, int operationToken) throws RemoteException {
+ executeMethodAsync(() -> mReference.getPresenceExchangeInternal()
+ .requestCapabilities(uris, operationToken), "requestCapabilities");
+ }
+ @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");
+
+ }
+ @Override
+ public void respondToCapabilityRequest(String contactUri,
+ RcsContactUceCapability ownCapabilities, int operationToken)
+ throws RemoteException {
+ executeMethodAsync(() -> mReference.getOptionsExchangeInternal()
+ .respondToCapabilityRequest(contactUri, ownCapabilities,
+ operationToken), "respondToCapabilityRequest");
+
+ }
+ @Override
+ public void respondToCapabilityRequestWithError(Uri contactUri, int code, String reason,
+ int operationToken) throws RemoteException {
+ executeMethodAsync(() -> mReference.getOptionsExchangeInternal()
+ .respondToCapabilityRequestWithError(contactUri, code, reason,
+ operationToken), "respondToCapabilityRequestWithError");
+ }
+
+ // Call the methods with a clean calling identity on the executor and wait indefinitely for
+ // the future to return.
+ private void executeMethodAsync(Runnable r, String errorLogName)
+ throws RemoteException {
+ // call with a clean calling identity on the executor and wait indefinitely for the
+ // future to return.
+ try {
+ CompletableFuture.runAsync(
+ () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor).join();
+ } catch (CancellationException | CompletionException e) {
+ Log.w(LOG_TAG, "RcsFeatureBinder - " + errorLogName + " exception: "
+ + e.getMessage());
+ throw new RemoteException(e.getMessage());
+ }
+ }
+
+ private <T> T executeMethodAsyncForResult(Supplier<T> r,
+ String errorLogName) throws RemoteException {
+ // call with a clean calling identity on the executor and wait indefinitely for the
+ // future to return.
+ CompletableFuture<T> future = CompletableFuture.supplyAsync(
+ () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor);
+ try {
+ return future.get();
+ } catch (ExecutionException | InterruptedException e) {
+ Log.w(LOG_TAG, "RcsFeatureBinder - " + errorLogName + " exception: "
+ + e.getMessage());
+ throw new RemoteException(e.getMessage());
+ }
+ }
+ }
+
+ /**
+ * 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}
+ *
+ * The enabled capabilities of this RcsFeature will be set by the framework
+ * using {@link #changeEnabledCapabilities(CapabilityChangeRequest, CapabilityCallbackProxy)}.
+ * After the capabilities have been set, the RcsFeature may then perform the necessary bring up
+ * 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*/
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = "CAPABILITY_TYPE_", flag = true, value = {
+ CAPABILITY_TYPE_NONE,
+ CAPABILITY_TYPE_OPTIONS_UCE,
+ CAPABILITY_TYPE_PRESENCE_UCE
+ })
+ public @interface RcsImsCapabilityFlag {}
+
+ /**
+ * Undefined capability type for initialization
+ */
+ public static final int CAPABILITY_TYPE_NONE = 0;
+
+ /**
+ * This carrier supports User Capability Exchange using SIP OPTIONS as defined by the
+ * framework. If set, the RcsFeature should support capability exchange using SIP OPTIONS.
+ * If not set, this RcsFeature should not service capability requests.
+ */
+ public static final int CAPABILITY_TYPE_OPTIONS_UCE = 1 << 0;
+
+ /**
+ * This carrier supports User Capability Exchange using a presence server as defined by the
+ * framework. If set, the RcsFeature should support capability exchange using a presence
+ * server. If not set, this RcsFeature should not publish capabilities or service capability
+ * requests using presence.
+ */
+ public static final int CAPABILITY_TYPE_PRESENCE_UCE = 1 << 1;
+
+ public RcsImsCapabilities(@RcsImsCapabilityFlag int capabilities) {
+ super(capabilities);
+ }
+
+ private RcsImsCapabilities(Capabilities c) {
+ super(c.getMask());
+ }
+
+ @Override
+ public void addCapabilities(@RcsImsCapabilityFlag int capabilities) {
+ super.addCapabilities(capabilities);
+ }
+
+ @Override
+ public void removeCapabilities(@RcsImsCapabilityFlag int capabilities) {
+ super.removeCapabilities(capabilities);
+ }
+
+ @Override
+ public boolean isCapable(@RcsImsCapabilityFlag int capabilities) {
+ return super.isCapable(capabilities);
+ }
+ }
+
+ private final RcsFeatureBinder mImsRcsBinder;
+ private IRcsFeatureListener mListenerBinder;
+ private RcsPresenceExchangeImplBase mPresExchange;
+ private RcsSipOptionsImplBase mSipOptions;
+
+ /**
+ * Create a new RcsFeature.
+ * <p>
+ * 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.
+ */
+ public RcsFeature() {
+ super();
+ // Run on the Binder threads that call them.
+ mImsRcsBinder = new RcsFeatureBinder(this, Runnable::run);
+ }
+
+ /**
+ * 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
+ */
+ public RcsFeature(@NonNull Executor executor) {
+ super();
+ if (executor == null) {
+ throw new IllegalArgumentException("executor can not be null.");
+ }
+ // Run on the Binder thread by default.
+ mImsRcsBinder = new RcsFeatureBinder(this, executor);
+ }
+
+ /**
+ * Query the current {@link RcsImsCapabilities} status set by the RcsFeature. If a capability is
+ * 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
+ */
+ @Override
+ public @NonNull final RcsImsCapabilities queryCapabilityStatus() {
+ return new RcsImsCapabilities(super.queryCapabilityStatus());
+ }
+
+ /**
+ * 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
+ */
+ public final void notifyCapabilitiesStatusChanged(@NonNull RcsImsCapabilities c) {
+ if (c == null) {
+ throw new IllegalArgumentException("RcsImsCapabilities must be non-null!");
+ }
+ super.notifyCapabilitiesStatusChanged(c);
+ }
+
+ /**
+ * Provides the RcsFeature with the ability to return the framework capability configuration set
+ * by the framework. When the framework calls
+ * {@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
+ */
+ public boolean queryCapabilityConfiguration(
+ @RcsImsCapabilities.RcsImsCapabilityFlag int capability,
+ @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) {
+ // Base Implementation - Override to provide functionality
+ return false;
+ }
+ /**
+ * Called from the framework when the {@link RcsImsCapabilities} that have been configured for
+ * this {@link RcsFeature} has changed.
+ * <p>
+ * For each newly enabled capability flag, the corresponding capability should be brought up in
+ * the {@link RcsFeature} and registered on the network. For each newly disabled capability
+ * flag, the corresponding capability should be brought down, and deregistered. Once a new
+ * capability has been initialized and is ready for usage, the status of that capability should
+ * also be set to true using {@link #notifyCapabilitiesStatusChanged(RcsImsCapabilities)}. This
+ * will notify the framework that the capability is ready.
+ * <p>
+ * 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
+ */
+ @Override
+ public void changeEnabledCapabilities(@NonNull CapabilityChangeRequest request,
+ @NonNull CapabilityCallbackProxy c) {
+ // 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
+ * {@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
+ */
+ public @NonNull RcsSipOptionsImplBase getOptionsExchangeImpl() {
+ // Base Implementation, override to implement functionality
+ return new RcsSipOptionsImplBase();
+ }
+
+ /**
+ * 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
+ */
+ public @NonNull RcsPresenceExchangeImplBase getPresenceExchangeImpl() {
+ // Base Implementation, override to implement functionality.
+ return new RcsPresenceExchangeImplBase();
+ }
+
+ /**{@inheritDoc}*/
+ @Override
+ public void onFeatureRemoved() {
+
+ }
+
+ /**{@inheritDoc}*/
+ @Override
+ public void onFeatureReady() {
+
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public final IImsRcsFeature getBinder() {
+ return mImsRcsBinder;
+ }
+
+ /**@hide*/
+ public IRcsFeatureListener getListener() {
+ synchronized (mLock) {
+ return mListenerBinder;
+ }
+ }
+
+ private void setListener(IRcsFeatureListener listener) {
+ synchronized (mLock) {
+ mListenerBinder = listener;
+ if (mListenerBinder != null) {
+ onFeatureReady();
+ }
+ }
+ }
+
+ private RcsPresenceExchangeImplBase getPresenceExchangeInternal() {
+ synchronized (mLock) {
+ if (mPresExchange == null) {
+ mPresExchange = getPresenceExchangeImpl();
+ mPresExchange.initialize(this);
+ }
+ return mPresExchange;
+ }
+ }
+
+ private RcsSipOptionsImplBase getOptionsExchangeInternal() {
+ synchronized (mLock) {
+ if (mSipOptions == null) {
+ mSipOptions = getOptionsExchangeImpl();
+ mSipOptions.initialize(this);
+ }
+ return mSipOptions;
+ }
+ }
+}
diff --git a/android/telephony/ims/stub/ImsCallSessionImplBase.java b/android/telephony/ims/stub/ImsCallSessionImplBase.java
new file mode 100644
index 0000000..73ba0e3
--- /dev/null
+++ b/android/telephony/ims/stub/ImsCallSessionImplBase.java
@@ -0,0 +1,650 @@
+/*
+ * Copyright (C) 2017 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.annotation.TestApi;
+import android.os.Message;
+import android.os.RemoteException;
+import android.telephony.ims.ImsCallProfile;
+import android.telephony.ims.ImsCallSession;
+import android.telephony.ims.ImsCallSessionListener;
+import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.ImsStreamMediaProfile;
+import android.telephony.ims.ImsVideoCallProvider;
+import android.telephony.ims.aidl.IImsCallSessionListener;
+
+import com.android.ims.internal.IImsCallSession;
+import com.android.ims.internal.IImsVideoCallProvider;
+/**
+ * Base implementation of IImsCallSession, which implements stub versions of the methods available.
+ *
+ * Override the methods that your implementation of ImsCallSession supports.
+ *
+ * @hide
+ */
+@SystemApi
+@TestApi
+// DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
+// will break other implementations of ImsCallSession maintained by other ImsServices.
+public class ImsCallSessionImplBase implements AutoCloseable {
+ /**
+ * Notify USSD Mode.
+ */
+ public static final int USSD_MODE_NOTIFY = 0;
+ /**
+ * Request USSD Mode
+ */
+ public static final int USSD_MODE_REQUEST = 1;
+
+ /**
+ * Defines IMS call session state.
+ */
+ public static class State {
+ public static final int IDLE = 0;
+ public static final int INITIATED = 1;
+ public static final int NEGOTIATING = 2;
+ public static final int ESTABLISHING = 3;
+ public static final int ESTABLISHED = 4;
+
+ public static final int RENEGOTIATING = 5;
+ public static final int REESTABLISHING = 6;
+
+ public static final int TERMINATING = 7;
+ public static final int TERMINATED = 8;
+
+ public static final int INVALID = (-1);
+
+ /**
+ * Converts the state to string.
+ */
+ public static String toString(int state) {
+ switch (state) {
+ case IDLE:
+ return "IDLE";
+ case INITIATED:
+ return "INITIATED";
+ case NEGOTIATING:
+ return "NEGOTIATING";
+ case ESTABLISHING:
+ return "ESTABLISHING";
+ case ESTABLISHED:
+ return "ESTABLISHED";
+ case RENEGOTIATING:
+ return "RENEGOTIATING";
+ case REESTABLISHING:
+ return "REESTABLISHING";
+ case TERMINATING:
+ return "TERMINATING";
+ case TERMINATED:
+ return "TERMINATED";
+ default:
+ return "UNKNOWN";
+ }
+ }
+
+ /**
+ * @hide
+ */
+ private State() {
+ }
+ }
+
+ // Non-final for injection by tests
+ private IImsCallSession mServiceImpl = new IImsCallSession.Stub() {
+ @Override
+ public void close() {
+ ImsCallSessionImplBase.this.close();
+ }
+
+ @Override
+ public String getCallId() {
+ return ImsCallSessionImplBase.this.getCallId();
+ }
+
+ @Override
+ public ImsCallProfile getCallProfile() {
+ return ImsCallSessionImplBase.this.getCallProfile();
+ }
+
+ @Override
+ public ImsCallProfile getLocalCallProfile() {
+ return ImsCallSessionImplBase.this.getLocalCallProfile();
+ }
+
+ @Override
+ public ImsCallProfile getRemoteCallProfile() {
+ return ImsCallSessionImplBase.this.getRemoteCallProfile();
+ }
+
+ @Override
+ public String getProperty(String name) {
+ return ImsCallSessionImplBase.this.getProperty(name);
+ }
+
+ @Override
+ public int getState() {
+ return ImsCallSessionImplBase.this.getState();
+ }
+
+ @Override
+ public boolean isInCall() {
+ return ImsCallSessionImplBase.this.isInCall();
+ }
+
+ @Override
+ public void setListener(IImsCallSessionListener listener) {
+ ImsCallSessionImplBase.this.setListener(new ImsCallSessionListener(listener));
+ }
+
+ @Override
+ public void setMute(boolean muted) {
+ ImsCallSessionImplBase.this.setMute(muted);
+ }
+
+ @Override
+ public void start(String callee, ImsCallProfile profile) {
+ ImsCallSessionImplBase.this.start(callee, profile);
+ }
+
+ @Override
+ public void startConference(String[] participants, ImsCallProfile profile) throws
+ RemoteException {
+ ImsCallSessionImplBase.this.startConference(participants, profile);
+ }
+
+ @Override
+ public void accept(int callType, ImsStreamMediaProfile profile) {
+ ImsCallSessionImplBase.this.accept(callType, profile);
+ }
+
+ @Override
+ public void deflect(String deflectNumber) {
+ ImsCallSessionImplBase.this.deflect(deflectNumber);
+ }
+
+ @Override
+ public void reject(int reason) {
+ ImsCallSessionImplBase.this.reject(reason);
+ }
+
+ @Override
+ public void transfer(@NonNull String number, boolean isConfirmationRequired) {
+ ImsCallSessionImplBase.this.transfer(number, isConfirmationRequired);
+ }
+
+ @Override
+ public void consultativeTransfer(@NonNull IImsCallSession transferToSession) {
+ ImsCallSessionImplBase otherSession = new ImsCallSessionImplBase();
+ otherSession.setServiceImpl(transferToSession);
+ ImsCallSessionImplBase.this.transfer(otherSession);
+ }
+
+ @Override
+ public void terminate(int reason) {
+ ImsCallSessionImplBase.this.terminate(reason);
+ }
+
+ @Override
+ public void hold(ImsStreamMediaProfile profile) {
+ ImsCallSessionImplBase.this.hold(profile);
+ }
+
+ @Override
+ public void resume(ImsStreamMediaProfile profile) {
+ ImsCallSessionImplBase.this.resume(profile);
+ }
+
+ @Override
+ public void merge() {
+ ImsCallSessionImplBase.this.merge();
+ }
+
+ @Override
+ public void update(int callType, ImsStreamMediaProfile profile) {
+ ImsCallSessionImplBase.this.update(callType, profile);
+ }
+
+ @Override
+ public void extendToConference(String[] participants) {
+ ImsCallSessionImplBase.this.extendToConference(participants);
+ }
+
+ @Override
+ public void inviteParticipants(String[] participants) {
+ ImsCallSessionImplBase.this.inviteParticipants(participants);
+ }
+
+ @Override
+ public void removeParticipants(String[] participants) {
+ ImsCallSessionImplBase.this.removeParticipants(participants);
+ }
+
+ @Override
+ public void sendDtmf(char c, Message result) {
+ ImsCallSessionImplBase.this.sendDtmf(c, result);
+ }
+
+ @Override
+ public void startDtmf(char c) {
+ ImsCallSessionImplBase.this.startDtmf(c);
+ }
+
+ @Override
+ public void stopDtmf() {
+ ImsCallSessionImplBase.this.stopDtmf();
+ }
+
+ @Override
+ public void sendUssd(String ussdMessage) {
+ ImsCallSessionImplBase.this.sendUssd(ussdMessage);
+ }
+
+ @Override
+ public IImsVideoCallProvider getVideoCallProvider() {
+ return ImsCallSessionImplBase.this.getVideoCallProvider();
+ }
+
+ @Override
+ public boolean isMultiparty() {
+ return ImsCallSessionImplBase.this.isMultiparty();
+ }
+
+ @Override
+ public void sendRttModifyRequest(ImsCallProfile toProfile) {
+ ImsCallSessionImplBase.this.sendRttModifyRequest(toProfile);
+ }
+
+ @Override
+ public void sendRttModifyResponse(boolean status) {
+ ImsCallSessionImplBase.this.sendRttModifyResponse(status);
+ }
+
+ @Override
+ public void sendRttMessage(String rttMessage) {
+ ImsCallSessionImplBase.this.sendRttMessage(rttMessage);
+ }
+ };
+
+ /**
+ * @hide
+ */
+ public final void setListener(IImsCallSessionListener listener) throws RemoteException {
+ setListener(new ImsCallSessionListener(listener));
+ }
+
+ /**
+ * Sets the listener to listen to the session events. An {@link ImsCallSession}
+ * can only hold one listener at a time. Subsequent calls to this method
+ * override the previous listener.
+ *
+ * @param listener {@link ImsCallSessionListener} used to notify the framework of updates
+ * to the ImsCallSession
+ */
+ public void setListener(ImsCallSessionListener listener) {
+ }
+
+ /**
+ * Closes the object. This {@link ImsCallSessionImplBase} is not usable after being closed.
+ */
+ @Override
+ public void close() {
+
+ }
+
+ /**
+ * @return A String containing the unique call ID of this {@link ImsCallSessionImplBase}.
+ */
+ public String getCallId() {
+ return null;
+ }
+
+ /**
+ * @return The {@link ImsCallProfile} that this {@link ImsCallSessionImplBase} is associated
+ * with.
+ */
+ public ImsCallProfile getCallProfile() {
+ return null;
+ }
+
+ /**
+ * @return The local {@link ImsCallProfile} that this {@link ImsCallSessionImplBase} is
+ * associated with.
+ */
+ public ImsCallProfile getLocalCallProfile() {
+ return null;
+ }
+
+ /**
+ * @return The remote {@link ImsCallProfile} that this {@link ImsCallSessionImplBase} is
+ * associated with.
+ */
+ public ImsCallProfile getRemoteCallProfile() {
+ return null;
+ }
+
+ /**
+ * @param name The String extra key.
+ * @return The string extra value associated with the specified property.
+ */
+ public String getProperty(String name) {
+ return null;
+ }
+
+ /**
+ * @return The {@link ImsCallSessionImplBase} state, defined in
+ * {@link ImsCallSessionImplBase.State}.
+ */
+ public int getState() {
+ return ImsCallSessionImplBase.State.INVALID;
+ }
+
+ /**
+ * @return true if the {@link ImsCallSessionImplBase} is in a call, false otherwise.
+ */
+ public boolean isInCall() {
+ return false;
+ }
+
+ /**
+ * Mutes or unmutes the mic for the active call.
+ *
+ * @param muted true if the call should be muted, false otherwise.
+ */
+ public void setMute(boolean muted) {
+ }
+
+ /**
+ * Initiates an IMS call with the specified number and call profile.
+ * The session listener set in {@link #setListener(ImsCallSessionListener)} is called back upon
+ * defined session events.
+ * Only valid to call when the session state is in
+ * {@link ImsCallSession.State#IDLE}.
+ *
+ * @param callee dialed string to make the call to
+ * @param profile call profile to make the call with the specified service type,
+ * call type and media information
+ * @see {@link ImsCallSession.Listener#callSessionStarted},
+ * {@link ImsCallSession.Listener#callSessionStartFailed}
+ */
+ public void start(String callee, ImsCallProfile profile) {
+ }
+
+ /**
+ * Initiates an IMS call with the specified participants and call profile.
+ * The session listener set in {@link #setListener(ImsCallSessionListener)} is called back upon
+ * defined session events.
+ * The method is only valid to call when the session state is in
+ * {@link ImsCallSession.State#IDLE}.
+ *
+ * @param participants participant list to initiate an IMS conference call
+ * @param profile call profile to make the call with the specified service type,
+ * call type and media information
+ * @see {@link ImsCallSession.Listener#callSessionStarted},
+ * {@link ImsCallSession.Listener#callSessionStartFailed}
+ */
+ public void startConference(String[] participants, ImsCallProfile profile) {
+ }
+
+ /**
+ * Accepts an incoming call or session update.
+ *
+ * @param callType call type specified in {@link ImsCallProfile} to be answered
+ * @param profile stream media profile {@link ImsStreamMediaProfile} to be answered
+ * @see {@link ImsCallSession.Listener#callSessionStarted}
+ */
+ public void accept(int callType, ImsStreamMediaProfile profile) {
+ }
+
+ /**
+ * Deflects an incoming call.
+ *
+ * @param deflectNumber number to deflect the call
+ */
+ public void deflect(String deflectNumber) {
+ }
+
+ /**
+ * Rejects an incoming call or session update.
+ *
+ * @param reason reason code to reject an incoming call, defined in {@link ImsReasonInfo}.
+ * The {@link android.telecom.InCallService} (dialer app) can use the
+ * {@link android.telecom.Call#reject(int)} API to reject a call while specifying
+ * a user-indicated reason for rejecting the call.
+ * Normal call declines ({@link android.telecom.Call#REJECT_REASON_DECLINED}) will
+ * map to {@link ImsReasonInfo#CODE_USER_DECLINE}.
+ * Unwanted calls ({@link android.telecom.Call#REJECT_REASON_UNWANTED}) will map
+ * to {@link ImsReasonInfo#CODE_SIP_USER_MARKED_UNWANTED}.
+ * {@link ImsCallSession.Listener#callSessionStartFailed}
+ */
+ public void reject(int reason) {
+ }
+
+ /**
+ * 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.
+ * @hide
+ */
+ public void transfer(@NonNull String number, boolean isConfirmationRequired) {
+ }
+
+ /**
+ * Transfer an established call to another call session
+ *
+ * @param otherSession The other ImsCallSession to transfer the ongoing session to.
+ * @hide
+ */
+ public void transfer(@NonNull ImsCallSessionImplBase otherSession) {
+ }
+
+ /**
+ * Terminates a call.
+ *
+ * @param reason reason code to terminate a call, defined in {@link ImsReasonInfo}.
+ *
+ * @see {@link ImsCallSession.Listener#callSessionTerminated}
+ */
+ public void terminate(int reason) {
+ }
+
+ /**
+ * Puts a call on hold. When it succeeds, {@link ImsCallSession.Listener#callSessionHeld} is
+ * called.
+ *
+ * @param profile stream media profile {@link ImsStreamMediaProfile} to hold the call
+ * @see {@link ImsCallSession.Listener#callSessionHeld},
+ * {@link ImsCallSession.Listener#callSessionHoldFailed}
+ */
+ public void hold(ImsStreamMediaProfile profile) {
+ }
+
+ /**
+ * Continues a call that's on hold. When it succeeds,
+ * {@link ImsCallSession.Listener#callSessionResumed} is called.
+ *
+ * @param profile stream media profile with {@link ImsStreamMediaProfile} to resume the call
+ * @see {@link ImsCallSession.Listener#callSessionResumed},
+ * {@link ImsCallSession.Listener#callSessionResumeFailed}
+ */
+ public void resume(ImsStreamMediaProfile profile) {
+ }
+
+ /**
+ * Merges the active and held call. When the merge starts,
+ * {@link ImsCallSession.Listener#callSessionMergeStarted} is called.
+ * {@link ImsCallSession.Listener#callSessionMergeComplete} is called if the merge is
+ * successful, and {@link ImsCallSession.Listener#callSessionMergeFailed} is called if the merge
+ * fails.
+ *
+ * @see {@link ImsCallSession.Listener#callSessionMergeStarted},
+ * {@link ImsCallSession.Listener#callSessionMergeComplete},
+ * {@link ImsCallSession.Listener#callSessionMergeFailed}
+ */
+ public void merge() {
+ }
+
+ /**
+ * Updates the current call's properties (ex. call mode change: video upgrade / downgrade).
+ *
+ * @param callType call type specified in {@link ImsCallProfile} to be updated
+ * @param profile stream media profile {@link ImsStreamMediaProfile} to be updated
+ * @see {@link ImsCallSession.Listener#callSessionUpdated},
+ * {@link ImsCallSession.Listener#callSessionUpdateFailed}
+ */
+ public void update(int callType, ImsStreamMediaProfile profile) {
+ }
+
+ /**
+ * Extends this call to the conference call with the specified recipients.
+ *
+ * @param participants participant list to be invited to the conference call after extending the
+ * call
+ * @see {@link ImsCallSession.Listener#callSessionConferenceExtended},
+ * {@link ImsCallSession.Listener#callSessionConferenceExtendFailed}
+ */
+ public void extendToConference(String[] participants) {
+ }
+
+ /**
+ * Requests the conference server to invite an additional participants to the conference.
+ *
+ * @param participants participant list to be invited to the conference call
+ * @see {@link ImsCallSession.Listener#callSessionInviteParticipantsRequestDelivered},
+ * {@link ImsCallSession.Listener#callSessionInviteParticipantsRequestFailed}
+ */
+ public void inviteParticipants(String[] participants) {
+ }
+
+ /**
+ * Requests the conference server to remove the specified participants from the conference.
+ *
+ * @param participants participant list to be removed from the conference call
+ * @see {@link ImsCallSession.Listener#callSessionRemoveParticipantsRequestDelivered},
+ * {@link ImsCallSession.Listener#callSessionRemoveParticipantsRequestFailed}
+ */
+ public void removeParticipants(String[] participants) {
+ }
+
+ /**
+ * Sends a DTMF code. According to <a href="http://tools.ietf.org/html/rfc2833">RFC 2833</a>,
+ * event 0 ~ 9 maps to decimal value 0 ~ 9, '*' to 10, '#' to 11, event 'A' ~ 'D' to 12 ~ 15,
+ * and event flash to 16. Currently, event flash is not supported.
+ *
+ * @param c the DTMF to send. '0' ~ '9', 'A' ~ 'D', '*', '#' are valid inputs.
+ * @param result If non-null, the {@link Message} to send when the operation is complete. This
+ * is done by using the associated {@link android.os.Messenger} in
+ * {@link Message#replyTo}. For example:
+ * {@code
+ * // Send DTMF and other operations...
+ * try {
+ * // Notify framework that the DTMF was sent.
+ * Messenger dtmfMessenger = result.replyTo;
+ * if (dtmfMessenger != null) {
+ * dtmfMessenger.send(result);
+ * }
+ * } catch (RemoteException e) {
+ * // Remote side is dead
+ * }
+ * }
+ */
+ public void sendDtmf(char c, Message result) {
+ }
+
+ /**
+ * Start a DTMF code. According to <a href="http://tools.ietf.org/html/rfc2833">RFC 2833</a>,
+ * event 0 ~ 9 maps to decimal value 0 ~ 9, '*' to 10, '#' to 11, event 'A' ~ 'D' to 12 ~ 15,
+ * and event flash to 16. Currently, event flash is not supported.
+ *
+ * @param c the DTMF to send. '0' ~ '9', 'A' ~ 'D', '*', '#' are valid inputs.
+ */
+ public void startDtmf(char c) {
+ }
+
+ /**
+ * Stop a DTMF code.
+ */
+ public void stopDtmf() {
+ }
+
+ /**
+ * Sends an USSD message.
+ *
+ * @param ussdMessage USSD message to send
+ */
+ public void sendUssd(String ussdMessage) {
+ }
+
+ /**
+ * See {@link #getImsVideoCallProvider()}, used directly in older ImsService implementations.
+ * @hide
+ */
+ public IImsVideoCallProvider getVideoCallProvider() {
+ ImsVideoCallProvider provider = getImsVideoCallProvider();
+ return provider != null ? provider.getInterface() : null;
+ }
+
+ /**
+ * @return The {@link ImsVideoCallProvider} implementation contained within the IMS service
+ * process.
+ */
+ public ImsVideoCallProvider getImsVideoCallProvider() {
+ return null;
+ }
+
+ /**
+ * Determines if the current session is multiparty.
+ * @return {@code True} if the session is multiparty.
+ */
+ public boolean isMultiparty() {
+ return false;
+ }
+
+ /**
+ * Device issues RTT modify request
+ * @param toProfile The profile with requested changes made
+ */
+ public void sendRttModifyRequest(ImsCallProfile toProfile) {
+ }
+
+ /**
+ * Device responds to Remote RTT modify request
+ * @param status true if the the request was accepted or false of the request is defined.
+ */
+ public void sendRttModifyResponse(boolean status) {
+ }
+
+ /**
+ * Device sends RTT message
+ * @param rttMessage RTT message to be sent
+ */
+ public void sendRttMessage(String rttMessage) {
+ }
+
+ /** @hide */
+ public IImsCallSession getServiceImpl() {
+ return mServiceImpl;
+ }
+
+ /** @hide */
+ public void setServiceImpl(IImsCallSession serviceImpl) {
+ mServiceImpl = serviceImpl;
+ }
+}
diff --git a/android/telephony/ims/stub/ImsConfigImplBase.java b/android/telephony/ims/stub/ImsConfigImplBase.java
new file mode 100644
index 0000000..6a2638b
--- /dev/null
+++ b/android/telephony/ims/stub/ImsConfigImplBase.java
@@ -0,0 +1,426 @@
+/*
+ * 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.SystemApi;
+import android.annotation.TestApi;
+import android.content.Context;
+import android.os.PersistableBundle;
+import android.os.RemoteException;
+import android.telephony.ims.ProvisioningManager;
+import android.telephony.ims.aidl.IImsConfig;
+import android.telephony.ims.aidl.IImsConfigCallback;
+import android.util.Log;
+
+import com.android.ims.ImsConfig;
+import com.android.internal.annotations.VisibleForTesting;
+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.HashMap;
+
+/**
+ * Controls the modification of IMS specific configurations. For more information on the supported
+ * IMS configuration constants, see {@link ImsConfig}.
+ *
+ * The inner class {@link ImsConfigStub} implements methods of IImsConfig AIDL interface.
+ * The IImsConfig AIDL interface is called by ImsConfig, which may exist in many other processes.
+ * ImsConfigImpl access to the configuration parameters may be arbitrarily slow, especially in
+ * during initialization, or times when a lot of configuration parameters are being set/get
+ * (such as during boot up or SIM card change). By providing a cache in ImsConfigStub, we can speed
+ * up access to these configuration parameters, so a query to the ImsConfigImpl does not have to be
+ * performed every time.
+ * @hide
+ */
+@SystemApi
+@TestApi
+public class ImsConfigImplBase {
+
+ private static final String TAG = "ImsConfigImplBase";
+
+ /**
+ * Implements the IImsConfig AIDL interface, which is called by potentially many processes
+ * in order to get/set configuration parameters.
+ *
+ * It holds an object of ImsConfigImplBase class which is usually extended by ImsConfigImpl
+ * with actual implementations from vendors. This class caches provisioned values from
+ * ImsConfigImpl layer because queries through ImsConfigImpl can be slow. When query goes in,
+ * it first checks cache layer. If missed, it will call the vendor implementation of
+ * ImsConfigImplBase API.
+ * and cache the return value if the set succeeds.
+ *
+ * Provides APIs to get/set the IMS service feature/capability/parameters.
+ * The config items include:
+ * 1) Items provisioned by the operator.
+ * 2) Items configured by user. Mainly service feature class.
+ *
+ * @hide
+ */
+ @VisibleForTesting
+ static public class ImsConfigStub extends IImsConfig.Stub {
+ WeakReference<ImsConfigImplBase> mImsConfigImplBaseWeakReference;
+ private HashMap<Integer, Integer> mProvisionedIntValue = new HashMap<>();
+ private HashMap<Integer, String> mProvisionedStringValue = new HashMap<>();
+
+ @VisibleForTesting
+ public ImsConfigStub(ImsConfigImplBase imsConfigImplBase) {
+ mImsConfigImplBaseWeakReference =
+ new WeakReference<ImsConfigImplBase>(imsConfigImplBase);
+ }
+
+ @Override
+ public void addImsConfigCallback(IImsConfigCallback c) throws RemoteException {
+ getImsConfigImpl().addImsConfigCallback(c);
+ }
+
+ @Override
+ public void removeImsConfigCallback(IImsConfigCallback c) throws RemoteException {
+ getImsConfigImpl().removeImsConfigCallback(c);
+ }
+
+ /**
+ * Gets the value for ims service/capabilities parameters. It first checks its local cache,
+ * if missed, it will call ImsConfigImplBase.getConfigInt.
+ * Synchronous blocking call.
+ *
+ * @param item integer key
+ * @return value in Integer format or {@link #CONFIG_RESULT_UNKNOWN} if
+ * unavailable.
+ */
+ @Override
+ public synchronized int getConfigInt(int item) throws RemoteException {
+ if (mProvisionedIntValue.containsKey(item)) {
+ return mProvisionedIntValue.get(item);
+ } else {
+ int retVal = getImsConfigImpl().getConfigInt(item);
+ if (retVal != ImsConfig.OperationStatusConstants.UNKNOWN) {
+ updateCachedValue(item, retVal, false);
+ }
+ return retVal;
+ }
+ }
+
+ /**
+ * Gets the value for ims service/capabilities parameters. It first checks its local cache,
+ * if missed, it will call #ImsConfigImplBase.getConfigString.
+ * Synchronous blocking call.
+ *
+ * @param item integer key
+ * @return value in String format.
+ */
+ @Override
+ public synchronized String getConfigString(int item) throws RemoteException {
+ if (mProvisionedIntValue.containsKey(item)) {
+ return mProvisionedStringValue.get(item);
+ } else {
+ String retVal = getImsConfigImpl().getConfigString(item);
+ if (retVal != null) {
+ updateCachedValue(item, retVal, false);
+ }
+ return retVal;
+ }
+ }
+
+ /**
+ * 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.
+ * Synchronous blocking call.
+ *
+ * @param item integer key
+ * @param value in Integer format.
+ * @return the result of setting the configuration value, defined as either
+ * {@link #CONFIG_RESULT_FAILED} or {@link #CONFIG_RESULT_SUCCESS}.
+ */
+ @Override
+ public synchronized int setConfigInt(int item, int value) throws RemoteException {
+ mProvisionedIntValue.remove(item);
+ int retVal = getImsConfigImpl().setConfig(item, value);
+ if (retVal == ImsConfig.OperationStatusConstants.SUCCESS) {
+ updateCachedValue(item, value, true);
+ } else {
+ Log.d(TAG, "Set provision value of " + item +
+ " to " + value + " failed with error code " + retVal);
+ }
+
+ return retVal;
+ }
+
+ /**
+ * 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.
+ * Synchronous blocking call.
+ *
+ * @param item as defined in com.android.ims.ImsConfig#ConfigConstants.
+ * @param value in String format.
+ * @return the result of setting the configuration value, defined as either
+ * {@link #CONFIG_RESULT_FAILED} or {@link #CONFIG_RESULT_SUCCESS}.
+ */
+ @Override
+ public synchronized int setConfigString(int item, String value)
+ throws RemoteException {
+ mProvisionedStringValue.remove(item);
+ int retVal = getImsConfigImpl().setConfig(item, value);
+ if (retVal == ImsConfig.OperationStatusConstants.SUCCESS) {
+ updateCachedValue(item, value, true);
+ }
+
+ return retVal;
+ }
+
+ @Override
+ public void updateImsCarrierConfigs(PersistableBundle bundle) throws RemoteException {
+ getImsConfigImpl().updateImsCarrierConfigs(bundle);
+ }
+
+ private ImsConfigImplBase getImsConfigImpl() throws RemoteException {
+ ImsConfigImplBase ref = mImsConfigImplBaseWeakReference.get();
+ if (ref == null) {
+ throw new RemoteException("Fail to get ImsConfigImpl");
+ } else {
+ return ref;
+ }
+ }
+
+ @Override
+ public void notifyRcsAutoConfigurationReceived(byte[] config, boolean isCompressed)
+ throws RemoteException {
+ getImsConfigImpl().notifyRcsAutoConfigurationReceived(config, isCompressed);
+ }
+
+ private void notifyImsConfigChanged(int item, int value) throws RemoteException {
+ getImsConfigImpl().notifyConfigChanged(item, value);
+ }
+
+ private void notifyImsConfigChanged(int item, String value) throws RemoteException {
+ getImsConfigImpl().notifyConfigChanged(item, value);
+ }
+
+ protected synchronized void updateCachedValue(int item, int value, boolean notifyChange)
+ throws RemoteException {
+ mProvisionedIntValue.put(item, value);
+ if (notifyChange) {
+ notifyImsConfigChanged(item, value);
+ }
+ }
+
+ protected synchronized void updateCachedValue(int item, String value,
+ boolean notifyChange) throws RemoteException {
+ mProvisionedStringValue.put(item, value);
+ if (notifyChange) {
+ notifyImsConfigChanged(item, value);
+ }
+ }
+ }
+
+ /**
+ * The configuration requested resulted in an unknown result. This may happen if the
+ * IMS configurations are unavailable.
+ */
+ public static final int CONFIG_RESULT_UNKNOWN = ProvisioningManager.PROVISIONING_RESULT_UNKNOWN;
+
+ /**
+ * Setting the configuration value completed.
+ */
+ public static final int CONFIG_RESULT_SUCCESS = 0;
+ /**
+ * Setting the configuration value failed.
+ */
+ public static final int CONFIG_RESULT_FAILED = 1;
+
+ /**
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = "CONFIG_RESULT_", value = {
+ CONFIG_RESULT_SUCCESS,
+ CONFIG_RESULT_FAILED
+ })
+ public @interface SetConfigResult {}
+
+ private final RemoteCallbackListExt<IImsConfigCallback> mCallbacks =
+ new RemoteCallbackListExt<>();
+ ImsConfigStub mImsConfigStub;
+
+ /**
+ * Used for compatibility between older versions of the ImsService.
+ * @hide
+ */
+ public ImsConfigImplBase(Context context) {
+ mImsConfigStub = new ImsConfigStub(this);
+ }
+
+ public ImsConfigImplBase() {
+ mImsConfigStub = new ImsConfigStub(this);
+ }
+
+ /**
+ * Adds a {@link android.telephony.ims.ProvisioningManager.Callback} to the list of callbacks
+ * notified when a value in the configuration changes.
+ * @param c callback to add.
+ */
+ private void addImsConfigCallback(IImsConfigCallback c) {
+ mCallbacks.register(c);
+ }
+ /**
+ * Removes a {@link android.telephony.ims.ProvisioningManager.Callback} to the list of callbacks
+ * notified when a value in the configuration changes.
+ * @param c callback to remove.
+ */
+ private void removeImsConfigCallback(IImsConfigCallback c) {
+ mCallbacks.unregister(c);
+ }
+
+ /**
+ * @param item
+ * @param value
+ */
+ private final void notifyConfigChanged(int item, int value) {
+ // can be null in testing
+ if (mCallbacks == null) {
+ return;
+ }
+ mCallbacks.broadcastAction(c -> {
+ try {
+ c.onIntConfigChanged(item, value);
+ } catch (RemoteException e) {
+ Log.w(TAG, "notifyConfigChanged(int): dead binder in notify, skipping.");
+ }
+ });
+ }
+
+ private void notifyConfigChanged(int item, String value) {
+ // can be null in testing
+ if (mCallbacks == null) {
+ return;
+ }
+ mCallbacks.broadcastAction(c -> {
+ try {
+ c.onStringConfigChanged(item, value);
+ } catch (RemoteException e) {
+ Log.w(TAG, "notifyConfigChanged(string): dead binder in notify, skipping.");
+ }
+ });
+ }
+
+ /**
+ * @hide
+ */
+ public IImsConfig getIImsConfig() { return mImsConfigStub; }
+
+ /**
+ * Updates provisioning value and notifies the framework of the change.
+ * Doesn't call {@link #setConfig(int,int)} and assumes the result succeeded.
+ * This should only be used when the IMS implementer implicitly changed provisioned values.
+ *
+ * @param item an integer key.
+ * @param value in Integer format.
+ */
+ public final void notifyProvisionedValueChanged(int item, int value) {
+ try {
+ mImsConfigStub.updateCachedValue(item, value, true);
+ } catch (RemoteException e) {
+ Log.w(TAG, "notifyProvisionedValueChanged(int): Framework connection is dead.");
+ }
+ }
+
+ /**
+ * Updates provisioning value and notifies the framework of the change.
+ * Doesn't call {@link #setConfig(int,String)} and assumes the result succeeded.
+ * This should only be used when the IMS implementer implicitly changed provisioned values.
+ *
+ * @param item an integer key.
+ * @param value in String format.
+ */
+ public final void notifyProvisionedValueChanged(int item, String value) {
+ try {
+ mImsConfigStub.updateCachedValue(item, value, true);
+ } catch (RemoteException e) {
+ Log.w(TAG, "notifyProvisionedValueChanged(string): Framework connection is dead.");
+ }
+ }
+
+ /**
+ * The framework has received an RCS autoconfiguration XML file for provisioning.
+ *
+ * @param config The XML file to be read, if not compressed, it should be in ASCII/UTF8 format.
+ * @param isCompressed The XML file is compressed in gzip format and must be decompressed
+ * before being read.
+ *
+ */
+ public void notifyRcsAutoConfigurationReceived(@NonNull byte[] config, boolean isCompressed) {
+ }
+
+ /**
+ * Sets the configuration value for this ImsService.
+ *
+ * @param item an integer key.
+ * @param value an integer containing the configuration value.
+ * @return the result of setting the configuration value.
+ */
+ public @SetConfigResult int setConfig(int item, int value) {
+ // Base Implementation - To be overridden.
+ return CONFIG_RESULT_FAILED;
+ }
+
+ /**
+ * Sets the configuration value for this ImsService.
+ *
+ * @param item an integer key.
+ * @param value a String containing the new configuration value.
+ * @return Result of setting the configuration value.
+ */
+ public @SetConfigResult int setConfig(int item, String value) {
+ // Base Implementation - To be overridden.
+ return CONFIG_RESULT_FAILED;
+ }
+
+ /**
+ * Gets the currently stored value configuration value from the ImsService for {@code item}.
+ *
+ * @param item an integer key.
+ * @return configuration value, stored in integer format or {@link #CONFIG_RESULT_UNKNOWN} if
+ * unavailable.
+ */
+ public int getConfigInt(int item) {
+ // Base Implementation - To be overridden.
+ return CONFIG_RESULT_UNKNOWN;
+ }
+
+ /**
+ * Gets the currently stored value configuration value from the ImsService for {@code item}.
+ *
+ * @param item an integer key.
+ * @return configuration value, stored in String format or {@code null} if unavailable.
+ */
+ public String getConfigString(int item) {
+ // Base Implementation - To be overridden.
+ return null;
+ }
+
+ /**
+ * @hide
+ */
+ public void updateImsCarrierConfigs(PersistableBundle bundle) {
+ // Base Implementation - Should be overridden
+ }
+}
diff --git a/android/telephony/ims/stub/ImsEcbmImplBase.java b/android/telephony/ims/stub/ImsEcbmImplBase.java
new file mode 100644
index 0000000..4a3a2ea
--- /dev/null
+++ b/android/telephony/ims/stub/ImsEcbmImplBase.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2017 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.SystemApi;
+import android.annotation.TestApi;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.ims.internal.IImsEcbm;
+import com.android.ims.internal.IImsEcbmListener;
+
+/**
+ * Base implementation of ImsEcbm, which implements stub versions of the methods
+ * in the IImsEcbm AIDL. Override the methods that your implementation of ImsEcbm supports.
+ *
+ * DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
+ * will break other implementations of ImsEcbm maintained by other ImsServices.
+ *
+ * @hide
+ */
+@SystemApi
+@TestApi
+public class ImsEcbmImplBase {
+ private static final String TAG = "ImsEcbmImplBase";
+
+ private IImsEcbmListener mListener;
+ private IImsEcbm mImsEcbm = new IImsEcbm.Stub() {
+ @Override
+ public void setListener(IImsEcbmListener listener) {
+ mListener = listener;
+ }
+
+ @Override
+ public void exitEmergencyCallbackMode() {
+ ImsEcbmImplBase.this.exitEmergencyCallbackMode();
+ }
+ };
+
+ /** @hide */
+ public IImsEcbm getImsEcbm() {
+ return mImsEcbm;
+ }
+
+ /**
+ * This method should be implemented by the IMS provider. Framework will trigger this method to
+ * request to come out of ECBM mode
+ */
+ public void exitEmergencyCallbackMode() {
+ Log.d(TAG, "exitEmergencyCallbackMode() not implemented");
+ }
+
+ /**
+ * Notifies the framework when the device enters Emergency Callback Mode.
+ *
+ * @throws RuntimeException if the connection to the framework is not available.
+ */
+ public final void enteredEcbm() {
+ Log.d(TAG, "Entered ECBM.");
+ if (mListener != null) {
+ try {
+ mListener.enteredECBM();
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ /**
+ * Notifies the framework when the device exits Emergency Callback Mode.
+ *
+ * @throws RuntimeException if the connection to the framework is not available.
+ */
+ public final void exitedEcbm() {
+ Log.d(TAG, "Exited ECBM.");
+ if (mListener != null) {
+ try {
+ mListener.exitedECBM();
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+}
diff --git a/android/telephony/ims/stub/ImsFeatureConfiguration.java b/android/telephony/ims/stub/ImsFeatureConfiguration.java
new file mode 100644
index 0000000..4e7307e
--- /dev/null
+++ b/android/telephony/ims/stub/ImsFeatureConfiguration.java
@@ -0,0 +1,224 @@
+/*
+ * 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.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.ims.feature.ImsFeature;
+import android.util.ArraySet;
+
+import java.util.Set;
+
+/**
+ * Container class for IMS Feature configuration. This class contains the features that the
+ * ImsService supports, which are defined in {@link ImsFeature} as
+ * {@link ImsFeature#FEATURE_EMERGENCY_MMTEL}, {@link ImsFeature#FEATURE_MMTEL}, and
+ * {@link ImsFeature#FEATURE_RCS}.
+ *
+ * @hide
+ */
+@SystemApi
+@TestApi
+public final class ImsFeatureConfiguration implements Parcelable {
+
+ public static final class FeatureSlotPair {
+ /**
+ * SIM slot that this feature is associated with.
+ */
+ public final int slotId;
+ /**
+ * The feature that this slotId supports. Supported values are
+ * {@link ImsFeature#FEATURE_EMERGENCY_MMTEL}, {@link ImsFeature#FEATURE_MMTEL}, and
+ * {@link ImsFeature#FEATURE_RCS}.
+ */
+ public final @ImsFeature.FeatureType int featureType;
+
+ /**
+ * A mapping from slotId to IMS Feature type.
+ * @param slotId the SIM slot ID associated with this feature.
+ * @param featureType The feature that this slotId supports. Supported values are
+ * {@link ImsFeature#FEATURE_EMERGENCY_MMTEL}, {@link ImsFeature#FEATURE_MMTEL}, and
+ * {@link ImsFeature#FEATURE_RCS}.
+ */
+ public FeatureSlotPair(int slotId, @ImsFeature.FeatureType int featureType) {
+ this.slotId = slotId;
+ this.featureType = featureType;
+ }
+
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ FeatureSlotPair that = (FeatureSlotPair) o;
+
+ if (slotId != that.slotId) return false;
+ return featureType == that.featureType;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = slotId;
+ result = 31 * result + featureType;
+ return result;
+ }
+
+ @NonNull
+ @Override
+ public String toString() {
+ return "{s=" + slotId + ", f=" + ImsFeature.FEATURE_LOG_MAP.get(featureType) + "}";
+ }
+ }
+
+ /**
+ * Features that this ImsService supports.
+ */
+ private final Set<FeatureSlotPair> mFeatures;
+
+ /**
+ * Builder for {@link ImsFeatureConfiguration}.
+ */
+ public static class Builder {
+ ImsFeatureConfiguration mConfig;
+ public Builder() {
+ mConfig = new ImsFeatureConfiguration();
+ }
+
+ /**
+ * Adds an IMS feature associated with a SIM slot ID.
+ * @param slotId The slot ID associated with the IMS feature.
+ * @param featureType The feature that the slot ID supports. Supported values are
+ * {@link ImsFeature#FEATURE_EMERGENCY_MMTEL}, {@link ImsFeature#FEATURE_MMTEL}, and
+ * {@link ImsFeature#FEATURE_RCS}.
+ * @return a {@link Builder} to continue constructing the ImsFeatureConfiguration.
+ */
+ public Builder addFeature(int slotId, @ImsFeature.FeatureType int featureType) {
+ mConfig.addFeature(slotId, featureType);
+ return this;
+ }
+
+ public ImsFeatureConfiguration build() {
+ return mConfig;
+ }
+ }
+
+ /**
+ * Creates with all registration features empty.
+ * @hide
+ */
+ public ImsFeatureConfiguration() {
+ mFeatures = new ArraySet<>();
+ }
+
+ /**
+ * Configuration of the ImsService, which describes which features the ImsService supports
+ * (for registration).
+ * @param features a set of {@link FeatureSlotPair}s that describe which features this
+ * ImsService supports.
+ * @hide
+ */
+ public ImsFeatureConfiguration(Set<FeatureSlotPair> features) {
+ mFeatures = new ArraySet<>();
+
+ if (features != null) {
+ mFeatures.addAll(features);
+ }
+ }
+
+ /**
+ * @return a set of supported slot ID to feature type pairs contained within a
+ * {@link FeatureSlotPair}.
+ */
+ public Set<FeatureSlotPair> getServiceFeatures() {
+ return new ArraySet<>(mFeatures);
+ }
+
+ /**
+ * @hide
+ */
+ void addFeature(int slotId, int feature) {
+ mFeatures.add(new FeatureSlotPair(slotId, feature));
+ }
+
+ /** @hide */
+ protected ImsFeatureConfiguration(Parcel in) {
+ int featurePairLength = in.readInt();
+ // length
+ mFeatures = new ArraySet<>(featurePairLength);
+ for (int i = 0; i < featurePairLength; i++) {
+ // pair of reads for each entry (slotId->featureType)
+ mFeatures.add(new FeatureSlotPair(in.readInt(), in.readInt()));
+ }
+ }
+
+ public static final @android.annotation.NonNull Creator<ImsFeatureConfiguration> CREATOR
+ = new Creator<ImsFeatureConfiguration>() {
+ @Override
+ public ImsFeatureConfiguration createFromParcel(Parcel in) {
+ return new ImsFeatureConfiguration(in);
+ }
+
+ @Override
+ public ImsFeatureConfiguration[] newArray(int size) {
+ return new ImsFeatureConfiguration[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ FeatureSlotPair[] featureSlotPairs = new FeatureSlotPair[mFeatures.size()];
+ mFeatures.toArray(featureSlotPairs);
+ // length of list
+ dest.writeInt(featureSlotPairs.length);
+ // then pairs of integers for each entry (slotId->featureType).
+ for (FeatureSlotPair featureSlotPair : featureSlotPairs) {
+ dest.writeInt(featureSlotPair.slotId);
+ dest.writeInt(featureSlotPair.featureType);
+ }
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof ImsFeatureConfiguration)) return false;
+
+ ImsFeatureConfiguration
+ that = (ImsFeatureConfiguration) o;
+
+ return mFeatures.equals(that.mFeatures);
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public int hashCode() {
+ return mFeatures.hashCode();
+ }
+}
diff --git a/android/telephony/ims/stub/ImsMultiEndpointImplBase.java b/android/telephony/ims/stub/ImsMultiEndpointImplBase.java
new file mode 100644
index 0000000..0ae5bba
--- /dev/null
+++ b/android/telephony/ims/stub/ImsMultiEndpointImplBase.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2017 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.SystemApi;
+import android.annotation.TestApi;
+import android.os.RemoteException;
+import android.telephony.ims.ImsExternalCallState;
+import android.util.Log;
+
+import com.android.ims.internal.IImsExternalCallStateListener;
+import com.android.ims.internal.IImsMultiEndpoint;
+
+import java.util.List;
+
+/**
+ * Base implementation of ImsMultiEndpoint, which implements stub versions of the methods
+ * in the IImsMultiEndpoint AIDL. Override the methods that your implementation of
+ * ImsMultiEndpoint supports.
+ *
+ * DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
+ * will break other implementations of ImsMultiEndpoint maintained by other ImsServices.
+ *
+ * @hide
+ */
+@SystemApi
+@TestApi
+public class ImsMultiEndpointImplBase {
+ private static final String TAG = "MultiEndpointImplBase";
+
+ private IImsExternalCallStateListener mListener;
+ private IImsMultiEndpoint mImsMultiEndpoint = new IImsMultiEndpoint.Stub() {
+ @Override
+ public void setListener(IImsExternalCallStateListener listener) throws RemoteException {
+ mListener = listener;
+ }
+
+ @Override
+ public void requestImsExternalCallStateInfo() throws RemoteException {
+ ImsMultiEndpointImplBase.this.requestImsExternalCallStateInfo();
+ }
+ };
+
+ /** @hide */
+ public IImsMultiEndpoint getIImsMultiEndpoint() {
+ return mImsMultiEndpoint;
+ }
+
+ /**
+ * Notifies framework when Dialog Event Package update is received
+ *
+ * @throws RuntimeException if the connection to the framework is not available.
+ */
+ public final void onImsExternalCallStateUpdate(List<ImsExternalCallState> externalCallDialogs) {
+ Log.d(TAG, "ims external call state update triggered.");
+ if (mListener != null) {
+ try {
+ mListener.onImsExternalCallStateUpdate(externalCallDialogs);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ /**
+ * This method should be implemented by the IMS provider. Framework will trigger this to get the
+ * latest Dialog Event Package information. Should
+ */
+ public void requestImsExternalCallStateInfo() {
+ Log.d(TAG, "requestImsExternalCallStateInfo() not implemented");
+ }
+}
diff --git a/android/telephony/ims/stub/ImsRegistrationImplBase.java b/android/telephony/ims/stub/ImsRegistrationImplBase.java
new file mode 100644
index 0000000..7069e0a
--- /dev/null
+++ b/android/telephony/ims/stub/ImsRegistrationImplBase.java
@@ -0,0 +1,289 @@
+/*
+ * Copyright (C) 2017 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.SystemApi;
+import android.annotation.TestApi;
+import android.net.Uri;
+import android.os.RemoteException;
+import android.telephony.ims.ImsReasonInfo;
+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 java.lang.annotation.Retention;
+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.
+ * @hide
+ */
+@SystemApi
+@TestApi
+public class ImsRegistrationImplBase {
+
+ private static final String LOG_TAG = "ImsRegistrationImplBase";
+
+ /**
+ * @hide
+ */
+ // Defines the underlying radio technology type that we have registered for IMS over.
+ @IntDef(flag = true,
+ value = {
+ REGISTRATION_TECH_NONE,
+ REGISTRATION_TECH_LTE,
+ REGISTRATION_TECH_IWLAN
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ImsRegistrationTech {}
+ /**
+ * No registration technology specified, used when we are not registered.
+ */
+ public static final int REGISTRATION_TECH_NONE = -1;
+ /**
+ * IMS is registered to IMS via LTE.
+ */
+ public static final int REGISTRATION_TECH_LTE = 0;
+ /**
+ * IMS is registered to IMS via IWLAN.
+ */
+ public static final int REGISTRATION_TECH_IWLAN = 1;
+
+ // 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
+ // with NOT_REGISTERED in the case where the ImsService has not updated the registration state
+ // yet.
+ private static final int REGISTRATION_STATE_UNKNOWN = -1;
+
+ private final IImsRegistration mBinder = new IImsRegistration.Stub() {
+
+ @Override
+ public @ImsRegistrationTech int getRegistrationTechnology() throws RemoteException {
+ return getConnectionType();
+ }
+
+ @Override
+ public void addRegistrationCallback(IImsRegistrationCallback c) throws RemoteException {
+ ImsRegistrationImplBase.this.addRegistrationCallback(c);
+ }
+
+ @Override
+ public void removeRegistrationCallback(IImsRegistrationCallback c) throws RemoteException {
+ ImsRegistrationImplBase.this.removeRegistrationCallback(c);
+ }
+ };
+
+ private final RemoteCallbackListExt<IImsRegistrationCallback> mCallbacks =
+ new RemoteCallbackListExt<>();
+ private final Object mLock = new Object();
+ // Locked on mLock
+ private @ImsRegistrationTech
+ int mConnectionType = REGISTRATION_TECH_NONE;
+ // Locked on mLock
+ private int mRegistrationState = REGISTRATION_STATE_UNKNOWN;
+ // Locked on mLock, create unspecified disconnect cause.
+ private ImsReasonInfo mLastDisconnectCause = new ImsReasonInfo();
+
+ /**
+ * @hide
+ */
+ public final IImsRegistration getBinder() {
+ return mBinder;
+ }
+
+ private void addRegistrationCallback(IImsRegistrationCallback c) throws RemoteException {
+ mCallbacks.register(c);
+ updateNewCallbackWithState(c);
+ }
+
+ private void removeRegistrationCallback(IImsRegistrationCallback c) {
+ mCallbacks.unregister(c);
+ }
+
+ /**
+ * 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}.
+ */
+ public final void onRegistered(@ImsRegistrationTech int imsRadioTech) {
+ updateToState(imsRadioTech, RegistrationManager.REGISTRATION_STATE_REGISTERED);
+ mCallbacks.broadcastAction((c) -> {
+ try {
+ c.onRegistered(imsRadioTech);
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, e + " " + "onRegistrationConnected() - Skipping " +
+ "callback.");
+ }
+ });
+ }
+
+ /**
+ * 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}.
+ */
+ public final void onRegistering(@ImsRegistrationTech int imsRadioTech) {
+ updateToState(imsRadioTech, RegistrationManager.REGISTRATION_STATE_REGISTERING);
+ mCallbacks.broadcastAction((c) -> {
+ try {
+ c.onRegistering(imsRadioTech);
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, e + " " + "onRegistrationProcessing() - Skipping " +
+ "callback.");
+ }
+ });
+ }
+
+ /**
+ * Notify the framework that the device is disconnected from the IMS network.
+ * <p>
+ * Note: Prior to calling {@link #onDeregistered(ImsReasonInfo)}, you should ensure that any
+ * changes to {@link android.telephony.ims.feature.ImsFeature} capability availability is sent
+ * to the framework. For example,
+ * {@link android.telephony.ims.feature.MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VIDEO}
+ * and
+ * {@link android.telephony.ims.feature.MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VOICE}
+ * may be set to unavailable to ensure the framework knows these services are no longer
+ * available due to de-registration. If you do not report capability changes impacted by
+ * de-registration, the framework will not know which features are no longer available as a
+ * result.
+ *
+ * @param info the {@link ImsReasonInfo} associated with why registration was disconnected.
+ */
+ public final void onDeregistered(ImsReasonInfo info) {
+ updateToDisconnectedState(info);
+ // ImsReasonInfo should never be null.
+ final ImsReasonInfo reasonInfo = (info != null) ? info : new ImsReasonInfo();
+ mCallbacks.broadcastAction((c) -> {
+ try {
+ c.onDeregistered(reasonInfo);
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, e + " " + "onRegistrationDisconnected() - Skipping " +
+ "callback.");
+ }
+ });
+ }
+
+ /**
+ * 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}.
+ * @param info The {@link ImsReasonInfo} for the failure to change technology.
+ */
+ public final void onTechnologyChangeFailed(@ImsRegistrationTech int imsRadioTech,
+ ImsReasonInfo info) {
+ final ImsReasonInfo reasonInfo = (info != null) ? info : new ImsReasonInfo();
+ mCallbacks.broadcastAction((c) -> {
+ try {
+ c.onTechnologyChangeFailed(imsRadioTech, reasonInfo);
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, e + " " + "onRegistrationChangeFailed() - 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
+ */
+ public final void onSubscriberAssociatedUriChanged(Uri[] uris) {
+ mCallbacks.broadcastAction((c) -> {
+ try {
+ c.onSubscriberAssociatedUriChanged(uris);
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, e + " " + "onSubscriberAssociatedUriChanged() - Skipping " +
+ "callback.");
+ }
+ });
+ }
+
+ private void updateToState(@ImsRegistrationTech int connType, int newState) {
+ synchronized (mLock) {
+ mConnectionType = connType;
+ mRegistrationState = newState;
+ mLastDisconnectCause = null;
+ }
+ }
+
+ private void updateToDisconnectedState(ImsReasonInfo info) {
+ synchronized (mLock) {
+ updateToState(REGISTRATION_TECH_NONE,
+ RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED);
+ if (info != null) {
+ mLastDisconnectCause = info;
+ } else {
+ Log.w(LOG_TAG, "updateToDisconnectedState: no ImsReasonInfo provided.");
+ mLastDisconnectCause = new ImsReasonInfo();
+ }
+ }
+ }
+
+ /**
+ * @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 {
+ int state;
+ ImsReasonInfo disconnectInfo;
+ synchronized (mLock) {
+ state = mRegistrationState;
+ disconnectInfo = mLastDisconnectCause;
+ }
+ switch (state) {
+ case RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED: {
+ c.onDeregistered(disconnectInfo);
+ break;
+ }
+ case RegistrationManager.REGISTRATION_STATE_REGISTERING: {
+ c.onRegistering(getConnectionType());
+ break;
+ }
+ case RegistrationManager.REGISTRATION_STATE_REGISTERED: {
+ c.onRegistered(getConnectionType());
+ break;
+ }
+ case REGISTRATION_STATE_UNKNOWN: {
+ // Do not callback if the state has not been updated yet by the ImsService.
+ break;
+ }
+ }
+ }
+}
diff --git a/android/telephony/ims/stub/ImsSmsImplBase.java b/android/telephony/ims/stub/ImsSmsImplBase.java
new file mode 100644
index 0000000..ce9a73a
--- /dev/null
+++ b/android/telephony/ims/stub/ImsSmsImplBase.java
@@ -0,0 +1,431 @@
+/*
+ * 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.IntRange;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.os.RemoteException;
+import android.telephony.SmsManager;
+import android.telephony.SmsMessage;
+import android.telephony.ims.aidl.IImsSmsListener;
+import android.util.Log;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Base implementation for SMS over IMS.
+ *
+ * Any service wishing to provide SMS over IMS should extend this class and implement all methods
+ * that the service supports.
+ *
+ * @hide
+ */
+@SystemApi
+@TestApi
+public class ImsSmsImplBase {
+ private static final String LOG_TAG = "SmsImplBase";
+
+ /** @hide */
+ @IntDef({
+ SEND_STATUS_OK,
+ SEND_STATUS_ERROR,
+ SEND_STATUS_ERROR_RETRY,
+ SEND_STATUS_ERROR_FALLBACK
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface SendStatusResult {}
+ /**
+ * Message was sent successfully.
+ */
+ public static final int SEND_STATUS_OK = 1;
+
+ /**
+ * IMS provider failed to send the message and platform should not retry falling back to sending
+ * the message using the radio.
+ */
+ public static final int SEND_STATUS_ERROR = 2;
+
+ /**
+ * IMS provider failed to send the message and platform should retry again after setting TP-RD
+ * bit to high.
+ */
+ public static final int SEND_STATUS_ERROR_RETRY = 3;
+
+ /**
+ * IMS provider failed to send the message and platform should retry falling back to sending
+ * the message using the radio.
+ */
+ public static final int SEND_STATUS_ERROR_FALLBACK = 4;
+
+ /** @hide */
+ @IntDef({
+ DELIVER_STATUS_OK,
+ DELIVER_STATUS_ERROR_GENERIC,
+ DELIVER_STATUS_ERROR_NO_MEMORY,
+ DELIVER_STATUS_ERROR_REQUEST_NOT_SUPPORTED
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface DeliverStatusResult {}
+ /**
+ * Message was delivered successfully.
+ */
+ public static final int DELIVER_STATUS_OK = 1;
+
+ /**
+ * Message was not delivered.
+ */
+ public static final int DELIVER_STATUS_ERROR_GENERIC = 2;
+
+ /**
+ * Message was not delivered due to lack of memory.
+ */
+ public static final int DELIVER_STATUS_ERROR_NO_MEMORY = 3;
+
+ /**
+ * Message was not delivered as the request is not supported.
+ */
+ public static final int DELIVER_STATUS_ERROR_REQUEST_NOT_SUPPORTED = 4;
+
+ /** @hide */
+ @IntDef({
+ STATUS_REPORT_STATUS_OK,
+ STATUS_REPORT_STATUS_ERROR
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface StatusReportResult {}
+
+ /**
+ * Status Report was set successfully.
+ */
+ public static final int STATUS_REPORT_STATUS_OK = 1;
+
+ /**
+ * Error while setting status report.
+ */
+ public static final int STATUS_REPORT_STATUS_ERROR = 2;
+
+ /**
+ * No network error was generated while processing the SMS message.
+ */
+ // Should match SmsResponse.NO_ERROR_CODE
+ public static final int RESULT_NO_NETWORK_ERROR = -1;
+
+ // Lock for feature synchronization
+ private final Object mLock = new Object();
+ private IImsSmsListener mListener;
+
+ /**
+ * Registers a listener responsible for handling tasks like delivering messages.
+ *
+ * @param listener listener to register.
+ *
+ * @hide
+ */
+ public final void registerSmsListener(IImsSmsListener listener) {
+ synchronized (mLock) {
+ mListener = listener;
+ }
+ }
+
+ /**
+ * This method will be triggered by the platform when the user attempts to send an SMS. This
+ * method should be implemented by the IMS providers to provide implementation of sending an SMS
+ * over IMS.
+ *
+ * @param token unique token generated by the platform that should be used when triggering
+ * callbacks for this specific message.
+ * @param messageRef the message reference, which may be 1 byte if it is in
+ * {@link SmsMessage#FORMAT_3GPP} format (see TS.123.040) or 2 bytes if it is in
+ * {@link SmsMessage#FORMAT_3GPP2} format (see 3GPP2 C.S0015-B).
+ * @param format the format of the message.
+ * @param smsc the Short Message Service Center address.
+ * @param isRetry whether it is a retry of an already attempted message or not.
+ * @param pdu PDU representing the contents of the message.
+ */
+ public void sendSms(int token, @IntRange(from = 0, to = 65535) int messageRef,
+ @SmsMessage.Format String format, String smsc, boolean isRetry,
+ byte[] pdu) {
+ // Base implementation returns error. Should be overridden.
+ try {
+ onSendSmsResult(token, messageRef, SEND_STATUS_ERROR,
+ SmsManager.RESULT_ERROR_GENERIC_FAILURE);
+ } catch (RuntimeException e) {
+ Log.e(LOG_TAG, "Can not send sms: " + e.getMessage());
+ }
+ }
+
+ /**
+ * This method will be triggered by the platform after
+ * {@link #onSmsReceived(int, String, byte[])} has been called to deliver the result to the IMS
+ * provider.
+ *
+ * @param token token provided in {@link #onSmsReceived(int, String, byte[])}
+ * @param messageRef the message reference, which may be 1 byte if it is in
+ * {@link SmsMessage#FORMAT_3GPP} format (see TS.123.040) or 2 bytes if it is in
+ * {@link SmsMessage#FORMAT_3GPP2} format (see 3GPP2 C.S0015-B).
+ * @param result result of delivering the message.
+ */
+ public void acknowledgeSms(int token, @IntRange(from = 0, to = 65535) int messageRef,
+ @DeliverStatusResult int result) {
+ Log.e(LOG_TAG, "acknowledgeSms() not implemented.");
+ }
+
+ /**
+ * This method will be triggered by the platform after
+ * {@link #onSmsStatusReportReceived(int, int, String, byte[])} or
+ * {@link #onSmsStatusReportReceived(int, String, byte[])} has been called to provide the
+ * result to the IMS provider.
+ *
+ * @param token token provided in {@link #onSmsStatusReportReceived(int, int, String, byte[])}
+ * or {@link #onSmsStatusReportReceived(int, String, byte[])}
+ * @param messageRef the message reference, which may be 1 byte if it is in
+ * {@link SmsMessage#FORMAT_3GPP} format (see TS.123.040) or 2 bytes if it is in
+ * {@link SmsMessage#FORMAT_3GPP2} format (see 3GPP2 C.S0015-B).
+ * @param result result of delivering the message.
+ */
+ public void acknowledgeSmsReport(int token, @IntRange(from = 0, to = 65535) int messageRef,
+ @StatusReportResult int result) {
+ Log.e(LOG_TAG, "acknowledgeSmsReport() not implemented.");
+ }
+
+ /**
+ * This method should be triggered by the IMS providers when there is an incoming message. The
+ * platform will deliver the message to the messages database and notify the IMS provider of the
+ * result by calling {@link #acknowledgeSms(int, int, int)}.
+ *
+ * This method must not be called before {@link #onReady()} is called or the call will fail. If
+ * the platform is not available, {@link #acknowledgeSms(int, int, int)} will be called with the
+ * {@link #DELIVER_STATUS_ERROR_GENERIC} result code.
+ * @param token unique token generated by IMS providers that the platform will use to trigger
+ * callbacks for this message.
+ * @param format the format of the message.
+ * @param pdu PDU representing the contents of the message.
+ * @throws RuntimeException if called before {@link #onReady()} is triggered.
+ */
+ public final void onSmsReceived(int token, @SmsMessage.Format String format, byte[] pdu)
+ throws RuntimeException {
+ synchronized (mLock) {
+ if (mListener == null) {
+ throw new RuntimeException("Feature not ready.");
+ }
+ try {
+ mListener.onSmsReceived(token, format, pdu);
+ } catch (RemoteException e) {
+ Log.e(LOG_TAG, "Can not deliver sms: " + e.getMessage());
+ SmsMessage message = SmsMessage.createFromPdu(pdu, format);
+ if (message != null && message.mWrappedSmsMessage != null) {
+ acknowledgeSms(token, message.mWrappedSmsMessage.mMessageRef,
+ DELIVER_STATUS_ERROR_GENERIC);
+ } else {
+ Log.w(LOG_TAG, "onSmsReceived: Invalid pdu entered.");
+ acknowledgeSms(token, 0, DELIVER_STATUS_ERROR_GENERIC);
+ }
+ }
+ }
+ }
+
+ /**
+ * This method should be triggered by the IMS providers when an outgoing SMS message has been
+ * sent successfully.
+ *
+ * @param token token provided in {@link #sendSms(int, int, String, String, boolean, byte[])}
+ * @param messageRef the message reference, which may be 1 byte if it is in
+ * {@link SmsMessage#FORMAT_3GPP} format (see TS.123.040) or 2 bytes if it is in
+ * {@link SmsMessage#FORMAT_3GPP2} format (see 3GPP2 C.S0015-B).
+ *
+ * @throws RuntimeException if called before {@link #onReady()} is triggered or if the
+ * connection to the framework is not available. If this happens attempting to send the SMS
+ * should be aborted.
+ */
+ public final void onSendSmsResultSuccess(int token,
+ @IntRange(from = 0, to = 65535) int messageRef) throws RuntimeException {
+ synchronized (mLock) {
+ if (mListener == null) {
+ throw new RuntimeException("Feature not ready.");
+ }
+ try {
+ mListener.onSendSmsResult(token, messageRef, SEND_STATUS_OK,
+ SmsManager.RESULT_ERROR_NONE, RESULT_NO_NETWORK_ERROR);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
+ * This method should be triggered by the IMS providers to pass the result of the sent message
+ * to the platform.
+ *
+ * @param token token provided in {@link #sendSms(int, int, String, String, boolean, byte[])}
+ * @param messageRef the message reference, which may be 1 byte if it is in
+ * {@link SmsMessage#FORMAT_3GPP} format (see TS.123.040) or 2 bytes if it is in
+ * {@link SmsMessage#FORMAT_3GPP2} format (see 3GPP2 C.S0015-B).
+ * @param status result of sending the SMS.
+ * @param reason reason in case status is failure.
+ *
+ * @throws RuntimeException if called before {@link #onReady()} is triggered or if the
+ * connection to the framework is not available. If this happens attempting to send the SMS
+ * should be aborted.
+ * @deprecated Use {@link #onSendSmsResultSuccess(int, int)} or
+ * {@link #onSendSmsResultError(int, int, int, int, int)} to notify the framework of the SMS
+ * send result.
+ */
+ @Deprecated
+ public final void onSendSmsResult(int token, @IntRange(from = 0, to = 65535) int messageRef,
+ @SendStatusResult int status, @SmsManager.Result int reason) throws RuntimeException {
+ synchronized (mLock) {
+ if (mListener == null) {
+ throw new RuntimeException("Feature not ready.");
+ }
+ try {
+ mListener.onSendSmsResult(token, messageRef, status, reason,
+ RESULT_NO_NETWORK_ERROR);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
+ * This method should be triggered by the IMS providers when an outgoing message fails to be
+ * sent due to an error generated while processing the message or after being sent to the
+ * network.
+ *
+ * @param token token provided in {@link #sendSms(int, int, String, String, boolean, byte[])}
+ * @param messageRef the message reference, which may be 1 byte if it is in
+ * {@link SmsMessage#FORMAT_3GPP} format (see TS.123.040) or 2 bytes if it is in
+ * {@link SmsMessage#FORMAT_3GPP2} format (see 3GPP2 C.S0015-B).
+ * @param status result of sending the SMS.
+ * @param networkErrorCode the error code reported by the carrier network if sending this SMS
+ * has resulted in an error or {@link #RESULT_NO_NETWORK_ERROR} if no network error was
+ * generated. See 3GPP TS 24.011 Section 7.3.4 for valid error codes and more information.
+ *
+ * @throws RuntimeException if called before {@link #onReady()} is triggered or if the
+ * connection to the framework is not available. If this happens attempting to send the SMS
+ * should be aborted.
+ */
+ public final void onSendSmsResultError(int token,
+ @IntRange(from = 0, to = 65535) int messageRef, @SendStatusResult int status,
+ @SmsManager.Result int reason, int networkErrorCode) throws RuntimeException {
+ synchronized (mLock) {
+ if (mListener == null) {
+ throw new RuntimeException("Feature not ready.");
+ }
+ try {
+ mListener.onSendSmsResult(token, messageRef, status, reason, networkErrorCode);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
+ * This method should be triggered by the IMS providers when the status report of the sent
+ * message is received. The platform will handle the report and notify the IMS provider of the
+ * result by calling {@link #acknowledgeSmsReport(int, int, int)}.
+ *
+ * This method must not be called before {@link #onReady()} is called or the call will fail. If
+ * the platform is not available, {@link #acknowledgeSmsReport(int, int, int)} will be called
+ * with the {@link #STATUS_REPORT_STATUS_ERROR} result code.
+ * @param token token provided in {@link #sendSms(int, int, String, String, boolean, byte[])}
+ * @param messageRef the message reference, which may be 1 byte if it is in
+ * {@link SmsMessage#FORMAT_3GPP} format or 2 bytes if it is in
+ * {@link SmsMessage#FORMAT_3GPP2} format (see 3GPP2 C.S0015-B).
+ * @param format the format of the message.
+ * @param pdu PDU representing the content of the status report.
+ * @throws RuntimeException if called before {@link #onReady()} is triggered
+ *
+ * @deprecated Use {@link #onSmsStatusReportReceived(int, String, byte[])} instead without the
+ * message reference.
+ */
+ @Deprecated
+ public final void onSmsStatusReportReceived(int token,
+ @IntRange(from = 0, to = 65535) int messageRef, @SmsMessage.Format String format,
+ byte[] pdu) throws RuntimeException {
+ synchronized (mLock) {
+ if (mListener == null) {
+ throw new RuntimeException("Feature not ready.");
+ }
+ try {
+ mListener.onSmsStatusReportReceived(token, format, pdu);
+ } catch (RemoteException e) {
+ Log.e(LOG_TAG, "Can not process sms status report: " + e.getMessage());
+ acknowledgeSmsReport(token, messageRef, STATUS_REPORT_STATUS_ERROR);
+ }
+ }
+ }
+
+ /**
+ * This method should be triggered by the IMS providers when the status report of the sent
+ * message is received. The platform will handle the report and notify the IMS provider of the
+ * result by calling {@link #acknowledgeSmsReport(int, int, int)}.
+ *
+ * This method must not be called before {@link #onReady()} is called or the call will fail. If
+ * the platform is not available, {@link #acknowledgeSmsReport(int, int, int)} will be called
+ * with the {@link #STATUS_REPORT_STATUS_ERROR} result code.
+ * @param token unique token generated by IMS providers that the platform will use to trigger
+ * callbacks for this message.
+ * @param format the format of the message.
+ * @param pdu PDU representing the content of the status report.
+ * @throws RuntimeException if called before {@link #onReady()} is triggered
+ */
+ public final void onSmsStatusReportReceived(int token, @SmsMessage.Format String format,
+ byte[] pdu) throws RuntimeException {
+ synchronized (mLock) {
+ if (mListener == null) {
+ throw new RuntimeException("Feature not ready.");
+ }
+ try {
+ mListener.onSmsStatusReportReceived(token, format, pdu);
+ } catch (RemoteException e) {
+ Log.e(LOG_TAG, "Can not process sms status report: " + e.getMessage());
+ SmsMessage message = SmsMessage.createFromPdu(pdu, format);
+ if (message != null && message.mWrappedSmsMessage != null) {
+ acknowledgeSmsReport(
+ token,
+ message.mWrappedSmsMessage.mMessageRef,
+ STATUS_REPORT_STATUS_ERROR);
+ } else {
+ Log.w(LOG_TAG,
+ "onSmsStatusReportReceivedWithoutMessageRef: Invalid pdu entered.");
+ acknowledgeSmsReport(token, 0, STATUS_REPORT_STATUS_ERROR);
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns the SMS format that the ImsService expects.
+ *
+ * @return The expected format of the SMS messages.
+ */
+ public @SmsMessage.Format String getSmsFormat() {
+ return SmsMessage.FORMAT_3GPP;
+ }
+
+ /**
+ * Called when ImsSmsImpl has been initialized and communication with the framework is set up.
+ * Any attempt by this class to access the framework before this method is called will return
+ * with a {@link RuntimeException}.
+ */
+ public void onReady() {
+ // Base Implementation - Should be overridden
+ }
+}
diff --git a/android/telephony/ims/stub/ImsStreamMediaSessionImplBase.java b/android/telephony/ims/stub/ImsStreamMediaSessionImplBase.java
new file mode 100644
index 0000000..92f1a01
--- /dev/null
+++ b/android/telephony/ims/stub/ImsStreamMediaSessionImplBase.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2017 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.os.RemoteException;
+
+import com.android.ims.internal.IImsStreamMediaSession;
+
+/**
+ * Base implementation of ImsStreamMediaSession, which implements stub versions of the methods
+ * in the IImsStreamMediaSession AIDL. Override the methods that your implementation of
+ * ImsStreamMediaSession supports.
+ *
+ * DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
+ * will break other implementations of ImsStreamMediaSession maintained by other ImsServices.
+ *
+ * @hide
+ */
+
+public class ImsStreamMediaSessionImplBase extends IImsStreamMediaSession.Stub {
+
+ @Override
+ public void close() throws RemoteException {
+
+ }
+}
diff --git a/android/telephony/ims/stub/ImsUtImplBase.java b/android/telephony/ims/stub/ImsUtImplBase.java
new file mode 100644
index 0000000..8564f7a
--- /dev/null
+++ b/android/telephony/ims/stub/ImsUtImplBase.java
@@ -0,0 +1,446 @@
+/*
+ * Copyright (C) 2017 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.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.telephony.ims.ImsUtListener;
+
+import com.android.ims.internal.IImsUt;
+import com.android.ims.internal.IImsUtListener;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Base implementation of IMS UT interface, which implements stubs. Override these methods to
+ * implement functionality.
+ *
+ * @hide
+ */
+// DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
+// will break other implementations of ImsUt maintained by other ImsServices.
+@SystemApi
+@TestApi
+public class ImsUtImplBase {
+ /**
+ * Bar all incoming calls. (See 3GPP TS 24.611)
+ * @hide
+ */
+ public static final int CALL_BARRING_ALL_INCOMING = 1;
+
+ /**
+ * Bar all outgoing calls. (See 3GPP TS 24.611)
+ * @hide
+ */
+ public static final int CALL_BARRING_ALL_OUTGOING = 2;
+
+ /**
+ * Bar all outgoing international calls. (See 3GPP TS 24.611)
+ * @hide
+ */
+ public static final int CALL_BARRING_OUTGOING_INTL = 3;
+
+ /**
+ * Bar all outgoing international calls, excluding those to the home PLMN country
+ * (See 3GPP TS 24.611)
+ * @hide
+ */
+ public static final int CALL_BARRING_OUTGOING_INTL_EXCL_HOME = 4;
+
+ /**
+ * Bar all incoming calls when roaming (See 3GPP TS 24.611)
+ * @hide
+ */
+ public static final int CALL_BLOCKING_INCOMING_WHEN_ROAMING = 5;
+
+ /**
+ * Enable Anonymous Communication Rejection (See 3GPP TS 24.611)
+ * @hide
+ */
+ public static final int CALL_BARRING_ANONYMOUS_INCOMING = 6;
+
+ /**
+ * Bar all incoming and outgoing calls. (See 3GPP TS 24.611)
+ * @hide
+ */
+ public static final int CALL_BARRING_ALL = 7;
+
+ /**
+ * Bar all outgoing service requests, including calls. (See 3GPP TS 24.611)
+ * @hide
+ */
+ public static final int CALL_BARRING_OUTGOING_ALL_SERVICES = 8;
+
+ /**
+ * Bar all incoming service requests, including calls. (See 3GPP TS 24.611)
+ * @hide
+ */
+ public static final int CALL_BARRING_INCOMING_ALL_SERVICES = 9;
+
+ /**
+ * Bar specific incoming calls. (See 3GPP TS 24.611)
+ * @hide
+ */
+ public static final int CALL_BARRING_SPECIFIC_INCOMING_CALLS = 10;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = "CALL_BARRING_", value = {CALL_BARRING_ALL_INCOMING, CALL_BARRING_ALL_OUTGOING,
+ CALL_BARRING_OUTGOING_INTL, CALL_BARRING_OUTGOING_INTL_EXCL_HOME,
+ CALL_BLOCKING_INCOMING_WHEN_ROAMING, CALL_BARRING_ANONYMOUS_INCOMING,
+ CALL_BARRING_ALL, CALL_BARRING_OUTGOING_ALL_SERVICES,
+ CALL_BARRING_INCOMING_ALL_SERVICES, CALL_BARRING_SPECIFIC_INCOMING_CALLS})
+ public @interface CallBarringMode {}
+
+ /**
+ * Constant used to denote an invalid return value.
+ * @hide
+ */
+ public static final int INVALID_RESULT = -1;
+
+ private IImsUt.Stub mServiceImpl = new IImsUt.Stub() {
+ @Override
+ public void close() throws RemoteException {
+ ImsUtImplBase.this.close();
+ }
+
+ @Override
+ public int queryCallBarring(int cbType) throws RemoteException {
+ return ImsUtImplBase.this.queryCallBarring(cbType);
+ }
+
+ @Override
+ public int queryCallForward(int condition, String number) throws RemoteException {
+ return ImsUtImplBase.this.queryCallForward(condition, number);
+ }
+
+ @Override
+ public int queryCallWaiting() throws RemoteException {
+ return ImsUtImplBase.this.queryCallWaiting();
+ }
+
+ @Override
+ public int queryCLIR() throws RemoteException {
+ return ImsUtImplBase.this.queryCLIR();
+ }
+
+ @Override
+ public int queryCLIP() throws RemoteException {
+ return ImsUtImplBase.this.queryCLIP();
+ }
+
+ @Override
+ public int queryCOLR() throws RemoteException {
+ return ImsUtImplBase.this.queryCOLR();
+ }
+
+ @Override
+ public int queryCOLP() throws RemoteException {
+ return ImsUtImplBase.this.queryCOLP();
+ }
+
+ @Override
+ public int transact(Bundle ssInfo) throws RemoteException {
+ return ImsUtImplBase.this.transact(ssInfo);
+ }
+
+ @Override
+ public int updateCallBarring(int cbType, int action, String[] barrList) throws
+ RemoteException {
+ return ImsUtImplBase.this.updateCallBarring(cbType, action, barrList);
+ }
+
+ @Override
+ public int updateCallForward(int action, int condition, String number, int serviceClass,
+ int timeSeconds) throws RemoteException {
+ return ImsUtImplBase.this.updateCallForward(action, condition, number, serviceClass,
+ timeSeconds);
+ }
+
+ @Override
+ public int updateCallWaiting(boolean enable, int serviceClass) throws RemoteException {
+ return ImsUtImplBase.this.updateCallWaiting(enable, serviceClass);
+ }
+
+ @Override
+ public int updateCLIR(int clirMode) throws RemoteException {
+ return ImsUtImplBase.this.updateCLIR(clirMode);
+ }
+
+ @Override
+ public int updateCLIP(boolean enable) throws RemoteException {
+ return ImsUtImplBase.this.updateCLIP(enable);
+ }
+
+ @Override
+ public int updateCOLR(int presentation) throws RemoteException {
+ return ImsUtImplBase.this.updateCOLR(presentation);
+ }
+
+ @Override
+ public int updateCOLP(boolean enable) throws RemoteException {
+ return ImsUtImplBase.this.updateCOLP(enable);
+ }
+
+ @Override
+ public void setListener(IImsUtListener listener) throws RemoteException {
+ ImsUtImplBase.this.setListener(new ImsUtListener(listener));
+ }
+
+ @Override
+ public int queryCallBarringForServiceClass(int cbType, int serviceClass)
+ throws RemoteException {
+ return ImsUtImplBase.this.queryCallBarringForServiceClass(cbType, serviceClass);
+ }
+
+ @Override
+ public int updateCallBarringForServiceClass(int cbType, int action,
+ String[] barrList, int serviceClass) throws RemoteException {
+ return ImsUtImplBase.this.updateCallBarringForServiceClass(
+ cbType, action, barrList, serviceClass);
+ }
+
+ @Override
+ public int updateCallBarringWithPassword(int cbType, int action, String[] barrList,
+ int serviceClass, String password) throws RemoteException {
+ return ImsUtImplBase.this.updateCallBarringWithPassword(
+ cbType, action, barrList, serviceClass, password);
+ }
+ };
+
+ /**
+ * Called when the framework no longer needs to interact with the IMS UT implementation any
+ * longer.
+ */
+ public void close() {
+
+ }
+
+ /**
+ * Retrieves the call barring configuration.
+ * @param cbType
+ */
+ public int queryCallBarring(int cbType) {
+ return -1;
+ }
+
+ /**
+ * Retrieves the configuration of the call barring for specified service class.
+ */
+ public int queryCallBarringForServiceClass(int cbType, int serviceClass) {
+ return -1;
+ }
+
+ /**
+ * Retrieves the configuration of the call forward.
+ */
+ public int queryCallForward(int condition, String number) {
+ return -1;
+ }
+
+ /**
+ * Retrieves the configuration of the call waiting.
+ */
+ public int queryCallWaiting() {
+ return -1;
+ }
+
+ /**
+ * Retrieves the default CLIR setting.
+ * @hide
+ */
+ public int queryCLIR() {
+ return queryClir();
+ }
+
+ /**
+ * Retrieves the CLIP call setting.
+ * @hide
+ */
+ public int queryCLIP() {
+ return queryClip();
+ }
+
+ /**
+ * Retrieves the COLR call setting.
+ * @hide
+ */
+ public int queryCOLR() {
+ return queryColr();
+ }
+
+ /**
+ * Retrieves the COLP call setting.
+ * @hide
+ */
+ public int queryCOLP() {
+ return queryColp();
+ }
+
+ /**
+ * Retrieves the default CLIR setting.
+ */
+ public int queryClir() {
+ return -1;
+ }
+
+ /**
+ * Retrieves the CLIP call setting.
+ */
+ public int queryClip() {
+ return -1;
+ }
+
+ /**
+ * Retrieves the COLR call setting.
+ */
+ public int queryColr() {
+ return -1;
+ }
+
+ /**
+ * Retrieves the COLP call setting.
+ */
+ public int queryColp() {
+ return -1;
+ }
+
+ /**
+ * Updates or retrieves the supplementary service configuration.
+ */
+ public int transact(Bundle ssInfo) {
+ return -1;
+ }
+
+ /**
+ * Updates the configuration of the call barring.
+ */
+ public int updateCallBarring(@CallBarringMode int cbType, int action, String[] barrList) {
+ return -1;
+ }
+
+ /**
+ * Updates the configuration of the call barring for specified service class.
+ */
+ public int updateCallBarringForServiceClass(@CallBarringMode int cbType, int action,
+ String[] barrList, int serviceClass) {
+ return -1;
+ }
+
+ /**
+ * Updates the configuration of the call barring for specified service class with password.
+ * @hide
+ */
+ public int updateCallBarringWithPassword(int cbType, int action, @Nullable String[] barrList,
+ int serviceClass, @NonNull String password) {
+ return -1;
+ }
+
+ /**
+ * Updates the configuration of the call forward.
+ */
+ public int updateCallForward(int action, int condition, String number, int serviceClass,
+ int timeSeconds) {
+ return 0;
+ }
+
+ /**
+ * Updates the configuration of the call waiting.
+ */
+ public int updateCallWaiting(boolean enable, int serviceClass) {
+ return -1;
+ }
+
+ /**
+ * Updates the configuration of the CLIR supplementary service.
+ * @hide
+ */
+ public int updateCLIR(int clirMode) {
+ return updateClir(clirMode);
+ }
+
+ /**
+ * Updates the configuration of the CLIP supplementary service.
+ * @hide
+ */
+ public int updateCLIP(boolean enable) {
+ return updateClip(enable);
+ }
+
+ /**
+ * Updates the configuration of the COLR supplementary service.
+ * @hide
+ */
+ public int updateCOLR(int presentation) {
+ return updateColr(presentation);
+ }
+
+ /**
+ * Updates the configuration of the COLP supplementary service.
+ * @hide
+ */
+ public int updateCOLP(boolean enable) {
+ return updateColp(enable);
+ }
+
+ /**
+ * Updates the configuration of the CLIR supplementary service.
+ */
+ public int updateClir(int clirMode) {
+ return -1;
+ }
+
+ /**
+ * Updates the configuration of the CLIP supplementary service.
+ */
+ public int updateClip(boolean enable) {
+ return -1;
+ }
+
+ /**
+ * Updates the configuration of the COLR supplementary service.
+ */
+ public int updateColr(int presentation) {
+ return -1;
+ }
+
+ /**
+ * Updates the configuration of the COLP supplementary service.
+ */
+ public int updateColp(boolean enable) {
+ return -1;
+ }
+
+ /**
+ * Sets the listener.
+ */
+ public void setListener(ImsUtListener listener) {
+ }
+
+ /**
+ * @hide
+ */
+ public IImsUt getInterface() {
+ return mServiceImpl;
+ }
+}
diff --git a/android/telephony/ims/stub/RcsCapabilityExchange.java b/android/telephony/ims/stub/RcsCapabilityExchange.java
new file mode 100644
index 0000000..fda295a
--- /dev/null
+++ b/android/telephony/ims/stub/RcsCapabilityExchange.java
@@ -0,0 +1,122 @@
+/*
+ * 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/android/telephony/ims/stub/RcsPresenceExchangeImplBase.java b/android/telephony/ims/stub/RcsPresenceExchangeImplBase.java
new file mode 100644
index 0000000..bb03448
--- /dev/null
+++ b/android/telephony/ims/stub/RcsPresenceExchangeImplBase.java
@@ -0,0 +1,294 @@
+/*
+ * 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/android/telephony/ims/stub/RcsSipOptionsImplBase.java b/android/telephony/ims/stub/RcsSipOptionsImplBase.java
new file mode 100644
index 0000000..2035fac
--- /dev/null
+++ b/android/telephony/ims/stub/RcsSipOptionsImplBase.java
@@ -0,0 +1,208 @@
+/*
+ * 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.
+ }
+ }
+}