Merge kwd to master
Change-Id: Idb607c0aa32f80fe4fe1539aedea7a221e9e7f04
diff --git a/Android.mk b/Android.mk
new file mode 100644
index 0000000..658b091
--- /dev/null
+++ b/Android.mk
@@ -0,0 +1,35 @@
+# Copyright 2013 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.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_AIDL_INCLUDES := $(LOCAL_PATH)/src/java
+LOCAL_SRC_FILES := \
+ src/java/com/android/ims/internal/IImsRegistrationListener.aidl \
+ src/java/com/android/ims/internal/IImsStreamMediaSession.aidl \
+ src/java/com/android/ims/internal/IImsCallSession.aidl \
+ src/java/com/android/ims/internal/IImsCallSessionListener.aidl \
+ src/java/com/android/ims/internal/IImsService.aidl \
+ src/java/com/android/ims/internal/IImsUt.aidl \
+ src/java/com/android/ims/internal/IImsUtListener.aidl \
+ $(call all-java-files-under, src/java)
+
+#LOCAL_JAVA_LIBRARIES := telephony-common
+
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE := ims-common
+
+include $(BUILD_JAVA_LIBRARY)
diff --git a/CleanSpec.mk b/CleanSpec.mk
new file mode 100644
index 0000000..ab62621
--- /dev/null
+++ b/CleanSpec.mk
@@ -0,0 +1,45 @@
+# Copyright 2013 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.
+#
+
+# If you don't need to do a full clean build but would like to touch
+# a file or delete some intermediate files, add a clean step to the end
+# of the list. These steps will only be run once, if they haven't been
+# run before.
+#
+# E.g.:
+# $(call add-clean-step, touch -c external/sqlite/sqlite3.h)
+# $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libz_intermediates)
+#
+# Always use "touch -c" and "rm -f" or "rm -rf" to gracefully deal with
+# files that are missing or have been moved.
+#
+# Use $(PRODUCT_OUT) to get to the "out/target/product/blah/" directory.
+# Use $(OUT_DIR) to refer to the "out" directory.
+#
+# If you need to re-do something that's already mentioned, just copy
+# the command and add it to the bottom of the list. E.g., if a change
+# that you made last week required touching a file and a change you
+# made today requires touching the same file, just copy the old
+# touch step and add it to the end of the list.
+#
+# ************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
+# ************************************************
+
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/ims_intermediates)
+
+# ************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
+# ************************************************
diff --git a/README.txt b/README.txt
new file mode 100644
index 0000000..f7271f6
--- /dev/null
+++ b/README.txt
@@ -0,0 +1,113 @@
+This package provides access to IP Multimedia Subsystem (IMS) functionality,
+especially making and taking VoLTE calls using IMS.
+At the moment, this only supports VoLTE calls which is compliant to GSMA IR.92 specification.
+
+For the additional information, you can refer the below standard specifications.
+ - GSMA IR.92 : features for voice and sms profile
+ - GSMA IR.94 : video calling feature
+ - 3GPP TS 24.229 : IMS call control (SIP and SDP)
+ - 3GPP TS 26.114 : IMS media handling and interaction
+ - 3GPP TS 26.111 : Codec for CS multimedia telephony service (H.324)
+ - 3GPP TS 24.623 : XCAP over the Ut interface for manipulating supplementary services
+ (XCAP : XML Configuration Access Protocol)
+
+To get started, you need to get an instance of the ImsManager by calling ImsManager#getInstance().
+
+With the ImsManager, you can initiate VoLTE calls with ImsManager#makeCall()
+and ImsManager#takeCall(). Both methods require a ImsCall#Listener that
+receives callbacks when the state of the call changes, such as
+when the call is ringing, established, or ended.
+
+ImsManager#makeCall() requires an ImsCallProfile objects, representing the call properties
+of the local device. ImsCallProfile can creates by ImsManager
+using the specified service and call type.
+ImsCallProfile is created by referring GSMA IR.92, GSMA IR.94, 3GPP TS 24.229,
+3GPP TS 26.114 and 3GPP TS 26.111.
+
+To receive calls, an IMS application MUST provide a BroadcastReceiver that
+has the ability to respond to an intent indicating that there is an incoming call.
+The default action for the incoming call intent is ImsManager#ACTION_IMS_INCOMING_CALL.
+And, the application frees to define the action for the incoming call intent.
+
+There are two packages for IMS APIs.
+
+ - com.android.ims
+ It provides the functionalities for the upper layer of IMS APIs.
+ In this moment, it is used by the VoLTE Phone application.
+
+ - com.android.ims.internal
+ It provides the functionalities for the internal usage or lower layer of IMS APIs.
+ It needs to be implemented by the IMS protocol solution vendor.
+
+
+
+== Classes for com.android.ims ==
+
+ImsManager
+ Provides APIs for IMS services, such as initiating IMS calls, and provides access to
+ the mobile operator's IMS network. This class is the starting point for any IMS actions.
+
+ImsCall
+ Provides IMS voice / video calls over LTE network.
+
+ImsCallGroup
+ Manages all IMS calls which are established hereafter the initial 1-to-1 call is established.
+ It's for providing the dummy calls which are disconnected with the IMS network after
+ merged or extended to the conference.
+
+ImsCallProfile
+ Parcelable object to handle IMS call profile
+ It provides the service and call type, the additional information related to the call.
+
+ImsConferenceState
+ It provides the conference information (defined in RFC 4575) for IMS conference call.
+
+ImsConnectionStateListener
+ It is a listener type for receiving notifications about changes to the IMS connection.
+ It provides a state of IMS registration between UE and network, the service availability of
+ the local device during IMS registered.
+
+ImsException
+ It provides a general IMS-related exception.
+
+ImsReasonInfo
+ It enables an application to get details on why a method call failed.
+
+ImsServiceClass
+ It defines an identifier for each IMS service category.
+
+ImsStreamMediaProfile
+ Parcelable object to handle IMS stream media profile.
+ It provides the media direction, quality of audio and/or video.
+
+ImsUtInterface
+ It provides APIs for the supplementary service settings using IMS (Ut interface, XCAP).
+
+
+
+== Classes for com.android.ims.internal ==
+
+CallGroup
+ Wrapper class which has an ICallGroup interface.
+
+CallGroupBase
+ Implements ICallGroup interface.
+ Manages all calls which are established hereafter the initial 1-to-1 call is established.
+ It's for providing the dummy calls which are disconnected with the IMS network after
+ merged or extended to the conference.
+
+CallGroupManager
+ Manages CallGroup objects.
+
+ICallGroup
+ Provides the interface to manage all calls which are established hereafter the initial
+ 1-to-1 call is established. It's for providing the dummy calls which are disconnected with
+ the IMS network after merged or extended to the conference.
+
+ImsCallSession
+ Provides the call initiation/termination, and media exchange between two IMS endpoints.
+ It directly communicates with IMS service which implements the IMS protocol behavior.
+
+ImsStreamMediaSession
+ Provides the APIs to control the media session, such as passing the surface object,
+ controlling the camera (front/rear selection, zoom, brightness, ...) for a video calling.
\ No newline at end of file
diff --git a/src/java/com/android/ims/ImsCall.java b/src/java/com/android/ims/ImsCall.java
new file mode 100644
index 0000000..b7adfb1
--- /dev/null
+++ b/src/java/com/android/ims/ImsCall.java
@@ -0,0 +1,2154 @@
+/*
+ * Copyright (c) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ims;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.os.Message;
+import android.telephony.Rlog;
+
+import com.android.ims.internal.CallGroup;
+import com.android.ims.internal.CallGroupManager;
+import com.android.ims.internal.ICall;
+import com.android.ims.internal.ImsCallSession;
+import com.android.ims.internal.ImsStreamMediaSession;
+
+/**
+ * Handles an IMS voice / video call over LTE. You can instantiate this class with
+ * {@link ImsManager}.
+ *
+ * @hide
+ */
+public class ImsCall implements ICall {
+ public static final int CALL_STATE_ACTIVE_TO_HOLD = 1;
+ public static final int CALL_STATE_HOLD_TO_ACTIVE = 2;
+
+ // Mode of USSD message
+ public static final int USSD_MODE_NOTIFY = 0;
+ public static final int USSD_MODE_REQUEST = 1;
+
+ private static final String TAG = "ImsCall";
+ private static final boolean DBG = true;
+
+ /**
+ * Listener for events relating to an IMS call, such as when a call is being
+ * recieved ("on ringing") or a call is outgoing ("on calling").
+ * <p>Many of these events are also received by {@link ImsCallSession.Listener}.</p>
+ */
+ public static class Listener {
+ /**
+ * Called when a request is sent out to initiate a new call
+ * and 1xx response is received from the network.
+ * The default implementation calls {@link #onCallStateChanged}.
+ *
+ * @param call the call object that carries out the IMS call
+ */
+ public void onCallProgressing(ImsCall call) {
+ onCallStateChanged(call);
+ }
+
+ /**
+ * Called when the call is established.
+ * The default implementation calls {@link #onCallStateChanged}.
+ *
+ * @param call the call object that carries out the IMS call
+ */
+ public void onCallStarted(ImsCall call) {
+ onCallStateChanged(call);
+ }
+
+ /**
+ * Called when the call setup is failed.
+ * The default implementation calls {@link #onCallError}.
+ *
+ * @param call the call object that carries out the IMS call
+ * @param reasonInfo detailed reason of the call setup failure
+ */
+ public void onCallStartFailed(ImsCall call, ImsReasonInfo reasonInfo) {
+ onCallError(call, reasonInfo);
+ }
+
+ /**
+ * Called when the call is terminated.
+ * The default implementation calls {@link #onCallStateChanged}.
+ *
+ * @param call the call object that carries out the IMS call
+ * @param reasonInfo detailed reason of the call termination
+ */
+ public void onCallTerminated(ImsCall call, ImsReasonInfo reasonInfo) {
+ // Store the call termination reason
+
+ onCallStateChanged(call);
+ }
+
+ /**
+ * Called when the call is in hold.
+ * The default implementation calls {@link #onCallStateChanged}.
+ *
+ * @param call the call object that carries out the IMS call
+ */
+ public void onCallHeld(ImsCall call) {
+ onCallStateChanged(call);
+ }
+
+ /**
+ * Called when the call hold is failed.
+ * The default implementation calls {@link #onCallError}.
+ *
+ * @param call the call object that carries out the IMS call
+ * @param reasonInfo detailed reason of the call hold failure
+ */
+ public void onCallHoldFailed(ImsCall call, ImsReasonInfo reasonInfo) {
+ onCallError(call, reasonInfo);
+ }
+
+ /**
+ * Called when the call hold is received from the remote user.
+ * The default implementation calls {@link #onCallStateChanged}.
+ *
+ * @param call the call object that carries out the IMS call
+ */
+ public void onCallHoldReceived(ImsCall call) {
+ onCallStateChanged(call);
+ }
+
+ /**
+ * Called when the call is in call.
+ * The default implementation calls {@link #onCallStateChanged}.
+ *
+ * @param call the call object that carries out the IMS call
+ */
+ public void onCallResumed(ImsCall call) {
+ onCallStateChanged(call);
+ }
+
+ /**
+ * Called when the call resume is failed.
+ * The default implementation calls {@link #onCallError}.
+ *
+ * @param call the call object that carries out the IMS call
+ * @param reasonInfo detailed reason of the call resume failure
+ */
+ public void onCallResumeFailed(ImsCall call, ImsReasonInfo reasonInfo) {
+ onCallError(call, reasonInfo);
+ }
+
+ /**
+ * Called when the call resume is received from the remote user.
+ * The default implementation calls {@link #onCallStateChanged}.
+ *
+ * @param call the call object that carries out the IMS call
+ */
+ public void onCallResumeReceived(ImsCall call) {
+ onCallStateChanged(call);
+ }
+
+ /**
+ * Called when the call is in call.
+ * The default implementation calls {@link #onCallStateChanged}.
+ *
+ * @param call the call object that carries out the IMS call
+ * @param newCall the call object that is merged with an active & hold call
+ */
+ public void onCallMerged(ImsCall call, ImsCall newCall) {
+ onCallStateChanged(call, newCall);
+ }
+
+ /**
+ * Called when the call merge is failed.
+ * The default implementation calls {@link #onCallError}.
+ *
+ * @param call the call object that carries out the IMS call
+ * @param reasonInfo detailed reason of the call merge failure
+ */
+ public void onCallMergeFailed(ImsCall call, ImsReasonInfo reasonInfo) {
+ onCallError(call, reasonInfo);
+ }
+
+ /**
+ * Called when the call is updated (except for hold/unhold).
+ * The default implementation calls {@link #onCallStateChanged}.
+ *
+ * @param call the call object that carries out the IMS call
+ */
+ public void onCallUpdated(ImsCall call) {
+ onCallStateChanged(call);
+ }
+
+ /**
+ * Called when the call update is failed.
+ * The default implementation calls {@link #onCallError}.
+ *
+ * @param call the call object that carries out the IMS call
+ * @param reasonInfo detailed reason of the call update failure
+ */
+ public void onCallUpdateFailed(ImsCall call, ImsReasonInfo reasonInfo) {
+ onCallError(call, reasonInfo);
+ }
+
+ /**
+ * Called when the call update is received from the remote user.
+ *
+ * @param call the call object that carries out the IMS call
+ */
+ public void onCallUpdateReceived(ImsCall call) {
+ // no-op
+ }
+
+ /**
+ * Called when the call is extended to the conference call.
+ * The default implementation calls {@link #onCallStateChanged}.
+ *
+ * @param call the call object that carries out the IMS call
+ * @param newCall the call object that is extended to the conference from the active call
+ */
+ public void onCallConferenceExtended(ImsCall call, ImsCall newCall) {
+ onCallStateChanged(call, newCall);
+ }
+
+ /**
+ * Called when the conference extension is failed.
+ * The default implementation calls {@link #onCallError}.
+ *
+ * @param call the call object that carries out the IMS call
+ * @param reasonInfo detailed reason of the conference extension failure
+ */
+ public void onCallConferenceExtendFailed(ImsCall call,
+ ImsReasonInfo reasonInfo) {
+ onCallError(call, reasonInfo);
+ }
+
+ /**
+ * Called when the conference extension is received from the remote user.
+ *
+ * @param call the call object that carries out the IMS call
+ * @param newCall the call object that is extended to the conference from the active call
+ */
+ public void onCallConferenceExtendReceived(ImsCall call, ImsCall newCall) {
+ onCallStateChanged(call, newCall);
+ }
+
+ /**
+ * Called when the invitation request of the participants is delivered to
+ * the conference server.
+ *
+ * @param call the call object that carries out the IMS call
+ */
+ public void onCallInviteParticipantsRequestDelivered(ImsCall call) {
+ // no-op
+ }
+
+ /**
+ * Called when the invitation request of the participants is failed.
+ *
+ * @param call the call object that carries out the IMS call
+ * @param reasonInfo detailed reason of the conference invitation failure
+ */
+ public void onCallInviteParticipantsRequestFailed(ImsCall call,
+ ImsReasonInfo reasonInfo) {
+ // no-op
+ }
+
+ /**
+ * Called when the removal request of the participants is delivered to
+ * the conference server.
+ *
+ * @param call the call object that carries out the IMS call
+ */
+ public void onCallRemoveParticipantsRequestDelivered(ImsCall call) {
+ // no-op
+ }
+
+ /**
+ * Called when the removal request of the participants is failed.
+ *
+ * @param call the call object that carries out the IMS call
+ * @param reasonInfo detailed reason of the conference removal failure
+ */
+ public void onCallRemoveParticipantsRequestFailed(ImsCall call,
+ ImsReasonInfo reasonInfo) {
+ // no-op
+ }
+
+ /**
+ * Called when the conference state is updated.
+ *
+ * @param call the call object that carries out the IMS call
+ * @param state state of the participant who is participated in the conference call
+ */
+ public void onCallConferenceStateUpdated(ImsCall call, 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 onCallUssdMessageReceived(ImsCall call,
+ int mode, String ussdMessage) {
+ // no-op
+ }
+
+ /**
+ * Called when an error occurs. The default implementation is no op.
+ * overridden. The default implementation is no op. Error events are
+ * not re-directed to this callback and are handled in {@link #onCallError}.
+ *
+ * @param call the call object that carries out the IMS call
+ * @param reasonInfo detailed reason of this error
+ * @see ImsReasonInfo
+ */
+ public void onCallError(ImsCall call, ImsReasonInfo reasonInfo) {
+ // no-op
+ }
+
+ /**
+ * Called when an event occurs and the corresponding callback is not
+ * overridden. The default implementation is no op. Error events are
+ * not re-directed to this callback and are handled in {@link #onCallError}.
+ *
+ * @param call the call object that carries out the IMS call
+ */
+ public void onCallStateChanged(ImsCall call) {
+ // no-op
+ }
+
+ /**
+ * Called when an event occurs and the corresponding callback is not
+ * overridden. The default implementation is no op. Error events are
+ * not re-directed to this callback and are handled in {@link #onCallError}.
+ *
+ * @param call the call object that carries out the IMS call
+ * @param newCall the call object that will be replaced by the previous call
+ */
+ public void onCallStateChanged(ImsCall call, ImsCall newCall) {
+ // no-op
+ }
+
+ /**
+ * Called when the call moves the hold state to the conversation state.
+ * For example, when merging the active & hold call, the state of all the hold call
+ * will be changed from hold state to conversation state.
+ * This callback method can be invoked even though the application does not trigger
+ * any operations.
+ *
+ * @param call the call object that carries out the IMS call
+ * @param state the detailed state of call state changes;
+ * Refer to CALL_STATE_* in {@link ImsCall}
+ */
+ public void onCallStateChanged(ImsCall call, int state) {
+ // no-op
+ }
+ }
+
+
+
+ // List of update operation for IMS call control
+ private static final int UPDATE_NONE = 0;
+ private static final int UPDATE_HOLD = 1;
+ private static final int UPDATE_HOLD_MERGE = 2;
+ private static final int UPDATE_RESUME = 3;
+ private static final int UPDATE_MERGE = 4;
+ private static final int UPDATE_EXTEND_TO_CONFERENCE = 5;
+ private static final int UPDATE_UNSPECIFIED = 6;
+
+ // For synchronization of private variables
+ private Object mLockObj = new Object();
+ private Context mContext;
+
+ // true if the call is established & in the conversation state
+ private boolean mInCall = false;
+ // true if the call is on hold
+ // If it is triggered by the local, mute the call. Otherwise, play local hold tone
+ // or network generated media.
+ private boolean mHold = false;
+ // true if the call is on mute
+ private boolean mMute = false;
+ // It contains the exclusive call update request. Refer to UPDATE_*.
+ private int mUpdateRequest = UPDATE_NONE;
+
+ private ImsCall.Listener mListener = null;
+ // It is for managing the multiple calls
+ // when the multiparty call is extended to the conference.
+ private CallGroup mCallGroup = null;
+
+ // Wrapper call session to interworking the IMS service (server).
+ private ImsCallSession mSession = null;
+ // Call profile of the current session.
+ // It can be changed at anytime when the call is updated.
+ private ImsCallProfile mCallProfile = null;
+ // Call profile to be updated after the application's action (accept/reject)
+ // to the call update. After the application's action (accept/reject) is done,
+ // it will be set to null.
+ private ImsCallProfile mProposedCallProfile = null;
+ private ImsReasonInfo mLastReasonInfo = null;
+
+ // Media session to control media (audio/video) operations for an IMS call
+ private ImsStreamMediaSession mMediaSession = null;
+
+ /**
+ * Create an IMS call object.
+ *
+ * @param context the context for accessing system services
+ * @param profile the call profile to make/take a call
+ */
+ public ImsCall(Context context, ImsCallProfile profile) {
+ mContext = context;
+ mCallProfile = profile;
+ }
+
+ /**
+ * Closes this object. This object is not usable after being closed.
+ */
+ @Override
+ public void close() {
+ synchronized(mLockObj) {
+ destroyCallGroup();
+
+ if (mSession != null) {
+ mSession.close();
+ mSession = null;
+ }
+
+ mCallProfile = null;
+ mProposedCallProfile = null;
+ mLastReasonInfo = null;
+ mMediaSession = null;
+ }
+ }
+
+ /**
+ * Checks if the call has a same remote user identity or not.
+ *
+ * @param userId the remote user identity
+ * @return true if the remote user identity is equal; otherwise, false
+ */
+ @Override
+ public boolean checkIfRemoteUserIsSame(String userId) {
+ if (userId == null) {
+ return false;
+ }
+
+ return userId.equals(mCallProfile.getCallExtra(ImsCallProfile.EXTRA_REMOTE_URI, ""));
+ }
+
+ /**
+ * Checks if the call is equal or not.
+ *
+ * @param call the call to be compared
+ * @return true if the call is equal; otherwise, false
+ */
+ @Override
+ public boolean equalsTo(ICall call) {
+ if (call == null) {
+ return false;
+ }
+
+ if (call instanceof ImsCall) {
+ return this.equals((ImsCall)call);
+ }
+
+ return false;
+ }
+
+ /**
+ * Gets the negotiated (local & remote) call profile.
+ *
+ * @return a {@link ImsCallProfile} object that has the negotiated call profile
+ */
+ public ImsCallProfile getCallProfile() {
+ synchronized(mLockObj) {
+ return mCallProfile;
+ }
+ }
+
+ /**
+ * Gets the local call profile (local capabilities).
+ *
+ * @return a {@link ImsCallProfile} object that has the local call profile
+ */
+ public ImsCallProfile getLocalCallProfile() throws ImsException {
+ synchronized(mLockObj) {
+ if (mSession == null) {
+ throw new ImsException("No call session",
+ ImsReasonInfo.CODE_LOCAL_CALL_TERMINATED);
+ }
+
+ try {
+ return mSession.getLocalCallProfile();
+ } catch (Throwable t) {
+ loge("getLocalCallProfile :: ", t);
+ throw new ImsException("getLocalCallProfile()", t, 0);
+ }
+ }
+ }
+
+ /**
+ * Gets the call profile proposed by the local/remote user.
+ *
+ * @return a {@link ImsCallProfile} object that has the proposed call profile
+ */
+ public ImsCallProfile getProposedCallProfile() {
+ synchronized(mLockObj) {
+ if (!isInCall()) {
+ return null;
+ }
+
+ return mProposedCallProfile;
+ }
+ }
+
+ /**
+ * Gets the state of the {@link ImsCallSession} that carries this call.
+ * The value returned must be one of the states in {@link ImsCallSession#State}.
+ *
+ * @return the session state
+ */
+ public int getState() {
+ synchronized(mLockObj) {
+ if (mSession == null) {
+ return ImsCallSession.State.IDLE;
+ }
+
+ return mSession.getState();
+ }
+ }
+
+ /**
+ * Gets the {@link ImsCallSession} that carries this call.
+ *
+ * @return the session object that carries this call
+ * @hide
+ */
+ public ImsCallSession getCallSession() {
+ synchronized(mLockObj) {
+ return mSession;
+ }
+ }
+
+ /**
+ * Gets the {@link ImsStreamMediaSession} that handles the media operation of this call.
+ * Almost interface APIs are for the VT (Video Telephony).
+ *
+ * @return the media session object that handles the media operation of this call
+ * @hide
+ */
+ public ImsStreamMediaSession getMediaSession() {
+ synchronized(mLockObj) {
+ return mMediaSession;
+ }
+ }
+
+ /**
+ * Gets the specified property of this call.
+ *
+ * @param name key to get the extra call information defined in {@link ImsCallProfile}
+ * @return the extra call information as string
+ */
+ public String getCallExtra(String name) throws ImsException {
+ // Lookup the cache
+
+ synchronized(mLockObj) {
+ // If not found, try to get the property from the remote
+ if (mSession == null) {
+ throw new ImsException("No call session",
+ ImsReasonInfo.CODE_LOCAL_CALL_TERMINATED);
+ }
+
+ try {
+ return mSession.getProperty(name);
+ } catch (Throwable t) {
+ loge("getCallExtra :: ", t);
+ throw new ImsException("getCallExtra()", t, 0);
+ }
+ }
+ }
+
+ /**
+ * Gets the last reason information when the call is not established, cancelled or terminated.
+ *
+ * @return the last reason information
+ */
+ public ImsReasonInfo getLastReasonInfo() {
+ synchronized(mLockObj) {
+ return mLastReasonInfo;
+ }
+ }
+
+ /**
+ * Checks if the call has a pending update operation.
+ *
+ * @return true if the call has a pending update operation
+ */
+ public boolean hasPendingUpdate() {
+ synchronized(mLockObj) {
+ return (mUpdateRequest != UPDATE_NONE);
+ }
+ }
+
+ /**
+ * Checks if the call is established.
+ *
+ * @return true if the call is established
+ */
+ public boolean isInCall() {
+ synchronized(mLockObj) {
+ return mInCall;
+ }
+ }
+
+ /**
+ * Checks if the call is muted.
+ *
+ * @return true if the call is muted
+ */
+ public boolean isMuted() {
+ synchronized(mLockObj) {
+ return mMute;
+ }
+ }
+
+ /**
+ * Checks if the call is on hold.
+ *
+ * @return true if the call is on hold
+ */
+ public boolean isOnHold() {
+ synchronized(mLockObj) {
+ return mHold;
+ }
+ }
+
+ /**
+ * Sets the listener to listen to the IMS call events.
+ * The method calls {@link #setListener setListener(listener, false)}.
+ *
+ * @param listener to listen to the IMS call events of this object; null to remove listener
+ * @see #setListener(Listener, boolean)
+ */
+ public void setListener(ImsCall.Listener listener) {
+ setListener(listener, false);
+ }
+
+ /**
+ * Sets the listener to listen to the IMS call events.
+ * A {@link ImsCall} can only hold one listener at a time. Subsequent calls
+ * to this method override the previous listener.
+ *
+ * @param listener to listen to the IMS call events of this object; null to remove listener
+ * @param callbackImmediately set to true if the caller wants to be called
+ * back immediately on the current state
+ */
+ public void setListener(ImsCall.Listener listener, boolean callbackImmediately) {
+ boolean inCall;
+ boolean onHold;
+ int state;
+ ImsReasonInfo lastReasonInfo;
+
+ synchronized(mLockObj) {
+ mListener = listener;
+
+ if ((listener == null) || !callbackImmediately) {
+ return;
+ }
+
+ inCall = mInCall;
+ onHold = mHold;
+ state = getState();
+ lastReasonInfo = mLastReasonInfo;
+ }
+
+ try {
+ if (lastReasonInfo != null) {
+ listener.onCallError(this, lastReasonInfo);
+ } else if (inCall) {
+ if (onHold) {
+ listener.onCallHeld(this);
+ } else {
+ listener.onCallStarted(this);
+ }
+ } else {
+ switch (state) {
+ case ImsCallSession.State.ESTABLISHING:
+ listener.onCallProgressing(this);
+ break;
+ case ImsCallSession.State.TERMINATED:
+ listener.onCallTerminated(this, lastReasonInfo);
+ break;
+ default:
+ // Ignore it. There is no action in the other state.
+ break;
+ }
+ }
+ } catch (Throwable t) {
+ loge("setListener()", t);
+ }
+ }
+
+ /**
+ * Mutes or unmutes the mic for the active call.
+ *
+ * @param muted true if the call is muted, false otherwise
+ */
+ public void setMute(boolean muted) throws ImsException {
+ synchronized(mLockObj) {
+ if (mMute != muted) {
+ mMute = muted;
+
+ try {
+ mSession.setMute(muted);
+ } catch (Throwable t) {
+ loge("setMute :: ", t);
+ throwImsException(t, 0);
+ }
+ }
+ }
+ }
+
+ /**
+ * Attaches an incoming call to this call object.
+ *
+ * @param session the session that receives the incoming call
+ * @throws ImsException if the IMS service fails to attach this object to the session
+ */
+ public void attachSession(ImsCallSession session) throws ImsException {
+ if (DBG) {
+ log("attachSession :: session=" + session);
+ }
+
+ synchronized(mLockObj) {
+ mSession = session;
+
+ try {
+ mSession.setListener(createCallSessionListener());
+ } catch (Throwable t) {
+ loge("attachSession :: ", t);
+ throwImsException(t, 0);
+ }
+ }
+ }
+
+ /**
+ * Initiates an IMS call with the call profile which is provided
+ * when creating a {@link ImsCall}.
+ *
+ * @param session the {@link ImsCallSession} for carrying out the call
+ * @param callee callee information to initiate an IMS call
+ * @throws ImsException if the IMS service fails to initiate the call
+ */
+ public void start(ImsCallSession session, String callee)
+ throws ImsException {
+ if (DBG) {
+ log("start(1) :: session=" + session + ", callee=" + callee);
+ }
+
+ synchronized(mLockObj) {
+ mSession = session;
+
+ try {
+ session.setListener(createCallSessionListener());
+ session.start(callee, mCallProfile);
+ } catch (Throwable t) {
+ loge("start(1) :: ", t);
+ throw new ImsException("start(1)", t, 0);
+ }
+ }
+ }
+
+ /**
+ * Initiates an IMS conferenca call with the call profile which is provided
+ * when creating a {@link ImsCall}.
+ *
+ * @param session the {@link ImsCallSession} for carrying out the call
+ * @param participants participant list to initiate an IMS conference call
+ * @throws ImsException if the IMS service fails to initiate the call
+ */
+ public void start(ImsCallSession session, String[] participants)
+ throws ImsException {
+ if (DBG) {
+ log("start(n) :: session=" + session + ", callee=" + participants);
+ }
+
+ synchronized(mLockObj) {
+ mSession = session;
+
+ try {
+ session.setListener(createCallSessionListener());
+ session.start(participants, mCallProfile);
+ } catch (Throwable t) {
+ loge("start(n) :: ", t);
+ throw new ImsException("start(n)", t, 0);
+ }
+ }
+ }
+
+ /**
+ * Accepts a call.
+ *
+ * @see Listener#onCallStarted
+ * @throws ImsException if the IMS service fails to accept the call
+ */
+ public void accept() throws ImsException {
+ if (DBG) {
+ log("accept :: session=" + mSession);
+ }
+
+ accept(ImsCallProfile.CALL_TYPE_VOICE, new ImsStreamMediaProfile());
+ }
+
+ /**
+ * Accepts a call.
+ *
+ * @param callType call type to be answered in {@link ImsCallProfile}
+ * @param profile a media profile to be answered (audio/audio & video, direction, ...)
+ * @see Listener#onCallStarted
+ * @throws ImsException if the IMS service fails to accept the call
+ */
+ public void accept(int callType, ImsStreamMediaProfile profile) throws ImsException {
+ if (DBG) {
+ log("accept :: session=" + mSession
+ + ", callType=" + callType + ", profile=" + profile);
+ }
+
+ synchronized(mLockObj) {
+ if (mSession == null) {
+ throw new ImsException("No call to answer",
+ ImsReasonInfo.CODE_LOCAL_CALL_TERMINATED);
+ }
+
+ try {
+ mSession.accept(callType, profile);
+ } catch (Throwable t) {
+ loge("accept :: ", t);
+ throw new ImsException("accept()", t, 0);
+ }
+
+ if (mInCall && (mProposedCallProfile != null)) {
+ if (DBG) {
+ log("accept :: call profile will be updated");
+ }
+
+ mCallProfile = mProposedCallProfile;
+ mProposedCallProfile = null;
+ }
+
+ // Other call update received
+ if (mInCall && (mUpdateRequest == UPDATE_UNSPECIFIED)) {
+ mUpdateRequest = UPDATE_NONE;
+ }
+ }
+ }
+
+ /**
+ * Rejects a call.
+ *
+ * @param reason reason code to reject an incoming call
+ * @see Listener#onCallStartFailed
+ * @throws ImsException if the IMS service fails to accept the call
+ */
+ public void reject(int reason) throws ImsException {
+ if (DBG) {
+ log("reject :: session=" + mSession + ", reason=" + reason);
+ }
+
+ synchronized(mLockObj) {
+ if (mSession != null) {
+ mSession.reject(reason);
+ }
+
+ if (mInCall && (mProposedCallProfile != null)) {
+ if (DBG) {
+ log("reject :: call profile is not updated; destroy it...");
+ }
+
+ mProposedCallProfile = null;
+ }
+
+ // Other call update received
+ if (mInCall && (mUpdateRequest == UPDATE_UNSPECIFIED)) {
+ mUpdateRequest = UPDATE_NONE;
+ }
+ }
+ }
+
+ /**
+ * Terminates an IMS call.
+ *
+ * @param reason reason code to terminate a call
+ * @throws ImsException if the IMS service fails to terminate the call
+ */
+ public void terminate(int reason) throws ImsException {
+ if (DBG) {
+ log("terminate :: session=" + mSession + ", reason=" + reason);
+ }
+
+ synchronized(mLockObj) {
+ mHold = false;
+ mInCall = false;
+
+ if (mSession != null) {
+ mSession.terminate(reason);
+ }
+ }
+ }
+
+ /**
+ * Puts a call on hold. When succeeds, {@link Listener#onCallHeld} is called.
+ *
+ * @see Listener#onCallHeld, Listener#onCallHoldFailed
+ * @throws ImsException if the IMS service fails to hold the call
+ */
+ public void hold() throws ImsException {
+ if (DBG) {
+ log("hold :: session=" + mSession);
+ }
+
+ if (isOnHold()) {
+ if (DBG) {
+ log("hold :: call is already on hold");
+ }
+ return;
+ }
+
+ synchronized(mLockObj) {
+ if (mUpdateRequest != UPDATE_NONE) {
+ loge("hold :: update is in progress; request=" + mUpdateRequest);
+ throw new ImsException("Call update is in progress",
+ ImsReasonInfo.CODE_LOCAL_ILLEGAL_STATE);
+ }
+
+ if (mSession == null) {
+ loge("hold :: ");
+ throw new ImsException("No call session",
+ ImsReasonInfo.CODE_LOCAL_CALL_TERMINATED);
+ }
+
+ mSession.hold(createHoldMediaProfile());
+ // FIXME: update the state on the callback?
+ mHold = true;
+ mUpdateRequest = UPDATE_HOLD;
+ }
+ }
+
+ /**
+ * Continues a call that's on hold. When succeeds, {@link Listener#onCallResumed} is called.
+ *
+ * @see Listener#onCallResumed, Listener#onCallResumeFailed
+ * @throws ImsException if the IMS service fails to resume the call
+ */
+ public void resume() throws ImsException {
+ if (DBG) {
+ log("resume :: session=" + mSession);
+ }
+
+ if (!isOnHold()) {
+ if (DBG) {
+ log("resume :: call is in conversation");
+ }
+ return;
+ }
+
+ synchronized(mLockObj) {
+ if (mUpdateRequest != UPDATE_NONE) {
+ loge("resume :: update is in progress; request=" + mUpdateRequest);
+ throw new ImsException("Call update is in progress",
+ ImsReasonInfo.CODE_LOCAL_ILLEGAL_STATE);
+ }
+
+ if (mSession == null) {
+ loge("resume :: ");
+ throw new ImsException("No call session",
+ ImsReasonInfo.CODE_LOCAL_CALL_TERMINATED);
+ }
+
+ mSession.resume(createResumeMediaProfile());
+ // FIXME: update the state on the callback?
+ mHold = false;
+ mUpdateRequest = UPDATE_RESUME;
+ }
+ }
+
+ /**
+ * Merges the active & hold call.
+ *
+ * @see Listener#onCallMerged, Listener#onCallMergeFailed
+ * @throws ImsException if the IMS service fails to merge the call
+ */
+ public void merge() throws ImsException {
+ if (DBG) {
+ log("merge :: session=" + mSession);
+ }
+
+ synchronized(mLockObj) {
+ if (mUpdateRequest != UPDATE_NONE) {
+ loge("merge :: update is in progress; request=" + mUpdateRequest);
+ throw new ImsException("Call update is in progress",
+ ImsReasonInfo.CODE_LOCAL_ILLEGAL_STATE);
+ }
+
+ if (mSession == null) {
+ loge("merge :: ");
+ throw new ImsException("No call session",
+ ImsReasonInfo.CODE_LOCAL_CALL_TERMINATED);
+ }
+
+ if (mHold) {
+ mSession.merge();
+ mUpdateRequest = UPDATE_MERGE;
+ } else {
+ mSession.hold(createHoldMediaProfile());
+ // FIXME: ?
+ mHold = true;
+ mUpdateRequest = UPDATE_HOLD_MERGE;
+ }
+ }
+ }
+
+ /**
+ * Merges the active & hold call.
+ *
+ * @param bgCall the background (holding) call
+ * @see Listener#onCallMerged, Listener#onCallMergeFailed
+ * @throws ImsException if the IMS service fails to merge the call
+ */
+ public void merge(ImsCall bgCall) throws ImsException {
+ if (DBG) {
+ log("merge(1) :: session=" + mSession);
+ }
+
+ if (bgCall == null) {
+ throw new ImsException("No background call",
+ ImsReasonInfo.CODE_LOCAL_ILLEGAL_ARGUMENT);
+ }
+
+ synchronized(mLockObj) {
+ createCallGroup(bgCall);
+ }
+
+ merge();
+ }
+
+ /**
+ * Updates the current call's properties (ex. call mode change: video upgrade / downgrade).
+ */
+ public void update(int callType, ImsStreamMediaProfile mediaProfile) throws ImsException {
+ if (DBG) {
+ log("update :: session=" + mSession);
+ }
+
+ if (isOnHold()) {
+ if (DBG) {
+ log("update :: call is on hold");
+ }
+ throw new ImsException("Not in a call to update call",
+ ImsReasonInfo.CODE_LOCAL_ILLEGAL_STATE);
+ }
+
+ synchronized(mLockObj) {
+ if (mUpdateRequest != UPDATE_NONE) {
+ if (DBG) {
+ log("update :: update is in progress; request=" + mUpdateRequest);
+ }
+ throw new ImsException("Call update is in progress",
+ ImsReasonInfo.CODE_LOCAL_ILLEGAL_STATE);
+ }
+
+ if (mSession == null) {
+ loge("update :: ");
+ throw new ImsException("No call session",
+ ImsReasonInfo.CODE_LOCAL_CALL_TERMINATED);
+ }
+
+ mSession.update(callType, mediaProfile);
+ mUpdateRequest = UPDATE_UNSPECIFIED;
+ }
+ }
+
+ /**
+ * Extends this call (1-to-1 call) to the conference call
+ * inviting the specified participants to.
+ *
+ */
+ public void extendToConference(String[] participants) throws ImsException {
+ if (DBG) {
+ log("extendToConference :: session=" + mSession);
+ }
+
+ if (isOnHold()) {
+ if (DBG) {
+ log("extendToConference :: call is on hold");
+ }
+ throw new ImsException("Not in a call to extend a call to conference",
+ ImsReasonInfo.CODE_LOCAL_ILLEGAL_STATE);
+ }
+
+ synchronized(mLockObj) {
+ if (mUpdateRequest != UPDATE_NONE) {
+ if (DBG) {
+ log("extendToConference :: update is in progress; request=" + mUpdateRequest);
+ }
+ throw new ImsException("Call update is in progress",
+ ImsReasonInfo.CODE_LOCAL_ILLEGAL_STATE);
+ }
+
+ if (mSession == null) {
+ loge("extendToConference :: ");
+ throw new ImsException("No call session",
+ ImsReasonInfo.CODE_LOCAL_CALL_TERMINATED);
+ }
+
+ mSession.extendToConference(participants);
+ mUpdateRequest = UPDATE_EXTEND_TO_CONFERENCE;
+ }
+ }
+
+ /**
+ * Requests the conference server to invite an additional participants to the conference.
+ *
+ */
+ public void inviteParticipants(String[] participants) throws ImsException {
+ if (DBG) {
+ log("inviteParticipants :: session=" + mSession);
+ }
+
+ synchronized(mLockObj) {
+ if (mSession == null) {
+ loge("inviteParticipants :: ");
+ throw new ImsException("No call session",
+ ImsReasonInfo.CODE_LOCAL_CALL_TERMINATED);
+ }
+
+ mSession.inviteParticipants(participants);
+ }
+ }
+
+ /**
+ * Requests the conference server to remove the specified participants from the conference.
+ *
+ */
+ public void removeParticipants(String[] participants) throws ImsException {
+ if (DBG) {
+ log("removeParticipants :: session=" + mSession);
+ }
+
+ synchronized(mLockObj) {
+ if (mSession == null) {
+ loge("removeParticipants :: ");
+ throw new ImsException("No call session",
+ ImsReasonInfo.CODE_LOCAL_CALL_TERMINATED);
+ }
+
+ mSession.removeParticipants(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 code the DTMF to send. Value 0 to 15 (inclusive) are valid inputs.
+ */
+ public void sendDtmf(int code) {
+ sendDtmf(code, null);
+ }
+
+ /**
+ * 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 code the DTMF to send. Value 0 to 15 (inclusive) are valid inputs.
+ * @param result the result message to send when done
+ */
+ public void sendDtmf(int code, Message result) {
+ if (DBG) {
+ log("sendDtmf :: session=" + mSession + ", code=" + code);
+ }
+
+ synchronized(mLockObj) {
+ if (mSession != null) {
+ mSession.sendDtmf(code);
+ }
+ }
+
+ if (result != null) {
+ result.sendToTarget();
+ }
+ }
+
+ /**
+ * Sends an USSD message.
+ *
+ * @param ussdMessage USSD message to send
+ */
+ public void sendUssd(String ussdMessage) throws ImsException {
+ if (DBG) {
+ log("sendUssd :: session=" + mSession + ", ussdMessage=" + ussdMessage);
+ }
+
+ synchronized(mLockObj) {
+ if (mSession == null) {
+ loge("sendUssd :: ");
+ throw new ImsException("No call session",
+ ImsReasonInfo.CODE_LOCAL_CALL_TERMINATED);
+ }
+
+ mSession.sendUssd(ussdMessage);
+ }
+ }
+
+ private void clear(ImsReasonInfo lastReasonInfo) {
+ mInCall = false;
+ mHold = false;
+ mUpdateRequest = UPDATE_NONE;
+ mLastReasonInfo = lastReasonInfo;
+ destroyCallGroup();
+ }
+
+ private void createCallGroup(ImsCall neutralReferrer) {
+ CallGroup referrerCallGroup = neutralReferrer.getCallGroup();
+
+ if (mCallGroup == null) {
+ if (referrerCallGroup == null) {
+ mCallGroup = CallGroupManager.getInstance().createCallGroup(new ImsCallGroup());
+ } else {
+ mCallGroup = referrerCallGroup;
+ }
+
+ if (mCallGroup != null) {
+ mCallGroup.setNeutralReferrer(neutralReferrer);
+ }
+ } else {
+ mCallGroup.setNeutralReferrer(neutralReferrer);
+
+ if ((referrerCallGroup != null)
+ && (mCallGroup != referrerCallGroup)) {
+ loge("fatal :: call group is mismatched; call is corrupted...");
+ }
+ }
+ }
+
+ private void updateCallGroup(ImsCall owner) {
+ if (mCallGroup == null) {
+ return;
+ }
+
+ ImsCall neutralReferrer = (ImsCall)mCallGroup.getNeutralReferrer();
+
+ mCallGroup.setNeutralReferrer(null);
+
+ if (owner == null) {
+ // Maintain the call group if the current call has been merged in the past.
+ if (!mCallGroup.hasReferrer()) {
+ CallGroupManager.getInstance().destroyCallGroup(mCallGroup);
+ mCallGroup = null;
+ }
+ } else {
+ mCallGroup.addReferrer(this);
+
+ if (neutralReferrer != null) {
+ if (neutralReferrer.isInCall() && (neutralReferrer.getCallGroup() == null)) {
+ neutralReferrer.setCallGroup(mCallGroup);
+ mCallGroup.addReferrer(neutralReferrer);
+ }
+
+ neutralReferrer.enforceConversationMode();
+ }
+
+ // Close the existing owner call if present
+ ImsCall exOwner = (ImsCall)mCallGroup.getOwner();
+
+ mCallGroup.setOwner(owner);
+
+ if (exOwner != null) {
+ exOwner.close();
+ }
+ }
+ }
+
+ private void destroyCallGroup() {
+ if (mCallGroup == null) {
+ return;
+ }
+
+ mCallGroup.removeReferrer(this);
+
+ if (!mCallGroup.hasReferrer()) {
+ CallGroupManager.getInstance().destroyCallGroup(mCallGroup);
+ }
+
+ mCallGroup = null;
+ }
+
+ private CallGroup getCallGroup() {
+ synchronized(mLockObj) {
+ return mCallGroup;
+ }
+ }
+
+ private void setCallGroup(CallGroup callGroup) {
+ synchronized(mLockObj) {
+ mCallGroup = callGroup;
+ }
+ }
+
+ /**
+ * Creates an IMS call session listener.
+ */
+ private ImsCallSession.Listener createCallSessionListener() {
+ return new ImsCallSessionListenerProxy();
+ }
+
+ private ImsCall createNewCall(ImsCallSession session, ImsCallProfile profile) {
+ ImsCall call = new ImsCall(mContext, profile);
+
+ try {
+ call.attachSession(session);
+ } catch (ImsException e) {
+ if (call != null) {
+ call.close();
+ call = null;
+ }
+ }
+
+ // Do additional operations...
+
+ return call;
+ }
+
+ private ImsStreamMediaProfile createHoldMediaProfile() {
+ ImsStreamMediaProfile mediaProfile = new ImsStreamMediaProfile();
+
+ if (mCallProfile == null) {
+ return mediaProfile;
+ }
+
+ mediaProfile.mAudioQuality = mCallProfile.mMediaProfile.mAudioQuality;
+ mediaProfile.mVideoQuality = mCallProfile.mMediaProfile.mVideoQuality;
+ mediaProfile.mAudioDirection = ImsStreamMediaProfile.DIRECTION_SEND;
+
+ if (mediaProfile.mVideoQuality != ImsStreamMediaProfile.VIDEO_QUALITY_NONE) {
+ mediaProfile.mVideoDirection = ImsStreamMediaProfile.DIRECTION_SEND;
+ }
+
+ return mediaProfile;
+ }
+
+ private ImsStreamMediaProfile createResumeMediaProfile() {
+ ImsStreamMediaProfile mediaProfile = new ImsStreamMediaProfile();
+
+ if (mCallProfile == null) {
+ return mediaProfile;
+ }
+
+ mediaProfile.mAudioQuality = mCallProfile.mMediaProfile.mAudioQuality;
+ mediaProfile.mVideoQuality = mCallProfile.mMediaProfile.mVideoQuality;
+ mediaProfile.mAudioDirection = ImsStreamMediaProfile.DIRECTION_SEND_RECEIVE;
+
+ if (mediaProfile.mVideoQuality != ImsStreamMediaProfile.VIDEO_QUALITY_NONE) {
+ mediaProfile.mVideoDirection = ImsStreamMediaProfile.DIRECTION_SEND_RECEIVE;
+ }
+
+ return mediaProfile;
+ }
+
+ private void enforceConversationMode() {
+ if (mInCall) {
+ mHold = false;
+ mUpdateRequest = UPDATE_NONE;
+ }
+ }
+
+ private void mergeInternal() {
+ if (DBG) {
+ log("mergeInternal :: session=" + mSession);
+ }
+
+ mSession.merge();
+ mUpdateRequest = UPDATE_MERGE;
+ }
+
+ private void notifyCallStateChanged() {
+ int state = 0;
+
+ if (mInCall && (mUpdateRequest == UPDATE_HOLD_MERGE)) {
+ state = CALL_STATE_ACTIVE_TO_HOLD;
+ mHold = true;
+ } else if (mInCall && ((mUpdateRequest == UPDATE_MERGE)
+ || (mUpdateRequest == UPDATE_EXTEND_TO_CONFERENCE))) {
+ state = CALL_STATE_HOLD_TO_ACTIVE;
+ mHold = false;
+ mMute = false;
+ }
+
+ if (state != 0) {
+ if (mListener != null) {
+ try {
+ mListener.onCallStateChanged(ImsCall.this, state);
+ } catch (Throwable t) {
+ loge("notifyCallStateChanged :: ", t);
+ }
+ }
+ }
+ }
+
+ private void notifyConferenceSessionTerminated(ImsReasonInfo reasonInfo) {
+ ImsCall.Listener listener;
+
+ if (mCallGroup.isOwner(ImsCall.this)) {
+ ArrayList<ICall> referrers = mCallGroup.getReferrers();
+
+ if (referrers != null) {
+ for (int i = 0; i < referrers.size(); ++i) {
+ ImsCall call = (ImsCall)referrers.get(i);
+
+ if (call == null) {
+ continue;
+ }
+
+ listener = call.mListener;
+ call.clear(reasonInfo);
+
+ if (listener != null) {
+ try {
+ listener.onCallTerminated(call, reasonInfo);
+ } catch (Throwable t) {
+ loge("notifyConferenceSessionTerminated :: ", t);
+ }
+ }
+ }
+ }
+ } else if (!mCallGroup.isReferrer(ImsCall.this)) {
+ return;
+ }
+
+ listener = mListener;
+ clear(reasonInfo);
+
+ if (listener != null) {
+ try {
+ listener.onCallTerminated(this, reasonInfo);
+ } catch (Throwable t) {
+ loge("notifyConferenceSessionTerminated :: ", t);
+ }
+ }
+ }
+
+ private void notifyConferenceStateUpdated(ImsConferenceState state) {
+ Set<Entry<String, Bundle>> paticipants = state.mParticipants.entrySet();
+
+ if (paticipants == null) {
+ return;
+ }
+
+ Iterator<Entry<String, Bundle>> iterator = paticipants.iterator();
+
+ while (iterator.hasNext()) {
+ Entry<String, Bundle> entry = iterator.next();
+
+ String key = entry.getKey();
+ Bundle confInfo = entry.getValue();
+ String status = confInfo.getString(ImsConferenceState.STATUS);
+ String user = confInfo.getString(ImsConferenceState.USER);
+ String endpoint = confInfo.getString(ImsConferenceState.ENDPOINT);
+
+ if (DBG) {
+ log("notifyConferenceStateUpdated :: key=" + key +
+ ", status=" + status +
+ ", user=" + user +
+ ", endpoint=" + endpoint);
+ }
+
+ if ((mCallGroup != null) && (!mCallGroup.isOwner(ImsCall.this))) {
+ continue;
+ }
+
+ ImsCall referrer = (ImsCall)mCallGroup.getReferrer(endpoint);
+
+ if (referrer == null) {
+ continue;
+ }
+
+ if (referrer.mListener == null) {
+ continue;
+ }
+
+ try {
+ if (status.equals(ImsConferenceState.STATUS_ALERTING)) {
+ referrer.mListener.onCallProgressing(referrer);
+ }
+ else if (status.equals(ImsConferenceState.STATUS_CONNECT_FAIL)) {
+ referrer.mListener.onCallStartFailed(referrer, new ImsReasonInfo());
+ }
+ else if (status.equals(ImsConferenceState.STATUS_ON_HOLD)) {
+ referrer.mListener.onCallHoldReceived(referrer);
+ }
+ else if (status.equals(ImsConferenceState.STATUS_CONNECTED)) {
+ referrer.mListener.onCallStarted(referrer);
+ }
+ else if (status.equals(ImsConferenceState.STATUS_DISCONNECTED)) {
+ referrer.clear(new ImsReasonInfo());
+ referrer.mListener.onCallTerminated(referrer, referrer.mLastReasonInfo);
+ }
+ } catch (Throwable t) {
+ loge("notifyConferenceStateUpdated :: ", t);
+ }
+ }
+ }
+
+ private void notifyError(int reason, int statusCode, String message) {
+ }
+
+ private void throwImsException(Throwable t, int code) throws ImsException {
+ if (t instanceof ImsException) {
+ throw (ImsException) t;
+ } else {
+ throw new ImsException(String.valueOf(code), t, code);
+ }
+ }
+
+ private void log(String s) {
+ Rlog.d(TAG, s);
+ }
+
+ private void loge(String s) {
+ Rlog.e(TAG, s);
+ }
+
+ private void loge(String s, Throwable t) {
+ Rlog.e(TAG, s, t);
+ }
+
+ private class ImsCallSessionListenerProxy extends ImsCallSession.Listener {
+ @Override
+ public void callSessionProgressing(ImsCallSession session,
+ ImsStreamMediaProfile profile) {
+ if (DBG) {
+ log("callSessionProgressing :: session=" + session + ", profile=" + profile);
+ }
+
+ ImsCall.Listener listener;
+
+ synchronized(ImsCall.this) {
+ listener = mListener;
+ mCallProfile.mMediaProfile.copyFrom(profile);
+ }
+
+ if (listener != null) {
+ try {
+ listener.onCallProgressing(ImsCall.this);
+ } catch (Throwable t) {
+ loge("callSessionProgressing :: ", t);
+ }
+ }
+ }
+
+ @Override
+ public void callSessionStarted(ImsCallSession session,
+ ImsCallProfile profile) {
+ if (DBG) {
+ log("callSessionStarted :: session=" + session + ", profile=" + profile);
+ }
+
+ ImsCall.Listener listener;
+
+ synchronized(ImsCall.this) {
+ listener = mListener;
+ mCallProfile = profile;
+ }
+
+ if (listener != null) {
+ try {
+ listener.onCallStarted(ImsCall.this);
+ } catch (Throwable t) {
+ loge("callSessionStarted :: ", t);
+ }
+ }
+ }
+
+ @Override
+ public void callSessionStartFailed(ImsCallSession session,
+ ImsReasonInfo reasonInfo) {
+ if (DBG) {
+ log("callSessionStartFailed :: session=" + session +
+ ", reasonInfo=" + reasonInfo);
+ }
+
+ ImsCall.Listener listener;
+
+ synchronized(ImsCall.this) {
+ listener = mListener;
+ mLastReasonInfo = reasonInfo;
+ }
+
+ if (listener != null) {
+ try {
+ listener.onCallStartFailed(ImsCall.this, reasonInfo);
+ } catch (Throwable t) {
+ loge("callSessionStarted :: ", t);
+ }
+ }
+ }
+
+ @Override
+ public void callSessionTerminated(ImsCallSession session,
+ ImsReasonInfo reasonInfo) {
+ if (DBG) {
+ log("callSessionTerminated :: session=" + session +
+ ", reasonInfo=" + reasonInfo);
+ }
+
+ ImsCall.Listener listener = null;
+
+ synchronized(ImsCall.this) {
+ if (mCallGroup != null) {
+ notifyConferenceSessionTerminated(reasonInfo);
+ } else {
+ listener = mListener;
+ clear(reasonInfo);
+ }
+ }
+
+ if (listener != null) {
+ try {
+ listener.onCallTerminated(ImsCall.this, reasonInfo);
+ } catch (Throwable t) {
+ loge("callSessionTerminated :: ", t);
+ }
+ }
+ }
+
+ @Override
+ public void callSessionHeld(ImsCallSession session,
+ ImsCallProfile profile) {
+ if (DBG) {
+ log("callSessionHeld :: session=" + session + ", profile=" + profile);
+ }
+
+ ImsCall.Listener listener;
+
+ synchronized(ImsCall.this) {
+ mCallProfile = profile;
+
+ if (mUpdateRequest == UPDATE_HOLD_MERGE) {
+ mergeInternal();
+ return;
+ }
+
+ mUpdateRequest = UPDATE_NONE;
+ listener = mListener;
+ }
+
+ if (listener != null) {
+ try {
+ listener.onCallHeld(ImsCall.this);
+ } catch (Throwable t) {
+ loge("callSessionHeld :: ", t);
+ }
+ }
+ }
+
+ @Override
+ public void callSessionHoldFailed(ImsCallSession session,
+ ImsReasonInfo reasonInfo) {
+ if (DBG) {
+ log("callSessionHoldFailed :: session=" + session +
+ ", reasonInfo=" + reasonInfo);
+ }
+
+ boolean isHoldForMerge = false;
+ ImsCall.Listener listener;
+
+ synchronized(ImsCall.this) {
+ if (mUpdateRequest == UPDATE_HOLD_MERGE) {
+ isHoldForMerge = true;
+ }
+
+ mUpdateRequest = UPDATE_NONE;
+ listener = mListener;
+ }
+
+ if (isHoldForMerge) {
+ callSessionMergeFailed(session, reasonInfo);
+ return;
+ }
+
+ if (listener != null) {
+ try {
+ listener.onCallHoldFailed(ImsCall.this, reasonInfo);
+ } catch (Throwable t) {
+ loge("callSessionHoldFailed :: ", t);
+ }
+ }
+ }
+
+ @Override
+ public void callSessionHoldReceived(ImsCallSession session,
+ ImsCallProfile profile) {
+ if (DBG) {
+ log("callSessionHoldReceived :: session=" + session + ", profile=" + profile);
+ }
+
+ ImsCall.Listener listener;
+
+ synchronized(ImsCall.this) {
+ listener = mListener;
+ mCallProfile = profile;
+ }
+
+ if (listener != null) {
+ try {
+ listener.onCallHoldReceived(ImsCall.this);
+ } catch (Throwable t) {
+ loge("callSessionHoldReceived :: ", t);
+ }
+ }
+ }
+
+ @Override
+ public void callSessionResumed(ImsCallSession session,
+ ImsCallProfile profile) {
+ if (DBG) {
+ log("callSessionResumed :: session=" + session + ", profile=" + profile);
+ }
+
+ ImsCall.Listener listener;
+
+ synchronized(ImsCall.this) {
+ listener = mListener;
+ mCallProfile = profile;
+ mUpdateRequest = UPDATE_NONE;
+ }
+
+ if (listener != null) {
+ try {
+ listener.onCallResumed(ImsCall.this);
+ } catch (Throwable t) {
+ loge("callSessionResumed :: ", t);
+ }
+ }
+ }
+
+ @Override
+ public void callSessionResumeFailed(ImsCallSession session,
+ ImsReasonInfo reasonInfo) {
+ if (DBG) {
+ log("callSessionResumeFailed :: session=" + session +
+ ", reasonInfo=" + reasonInfo);
+ }
+
+ ImsCall.Listener listener;
+
+ synchronized(ImsCall.this) {
+ listener = mListener;
+ mUpdateRequest = UPDATE_NONE;
+ }
+
+ if (listener != null) {
+ try {
+ listener.onCallResumeFailed(ImsCall.this, reasonInfo);
+ } catch (Throwable t) {
+ loge("callSessionResumeFailed :: ", t);
+ }
+ }
+ }
+
+ @Override
+ public void callSessionResumeReceived(ImsCallSession session,
+ ImsCallProfile profile) {
+ if (DBG) {
+ log("callSessionResumeReceived :: session=" + session +
+ ", profile=" + profile);
+ }
+
+ ImsCall.Listener listener;
+
+ synchronized(ImsCall.this) {
+ listener = mListener;
+ mCallProfile = profile;
+ }
+
+ if (listener != null) {
+ try {
+ listener.onCallResumeReceived(ImsCall.this);
+ } catch (Throwable t) {
+ loge("callSessionResumeReceived :: ", t);
+ }
+ }
+ }
+
+ @Override
+ public void callSessionMerged(ImsCallSession session,
+ ImsCallSession newSession, ImsCallProfile profile) {
+ if (DBG) {
+ log("callSessionMerged :: session=" + session
+ + ", newSession=" + newSession + ", profile=" + profile);
+ }
+
+ ImsCall newCall = createNewCall(newSession, profile);
+
+ if (newCall == null) {
+ callSessionMergeFailed(session, new ImsReasonInfo());
+ return;
+ }
+
+ ImsCall.Listener listener;
+
+ synchronized(ImsCall.this) {
+ listener = mListener;
+ updateCallGroup(newCall);
+ mUpdateRequest = UPDATE_NONE;
+ }
+
+ if (listener != null) {
+ try {
+ listener.onCallMerged(ImsCall.this, newCall);
+ } catch (Throwable t) {
+ loge("callSessionMerged :: ", t);
+ }
+ }
+ }
+
+ @Override
+ public void callSessionMergeFailed(ImsCallSession session,
+ ImsReasonInfo reasonInfo) {
+ if (DBG) {
+ log("callSessionMergeFailed :: session=" + session +
+ ", reasonInfo=" + reasonInfo);
+ }
+
+ ImsCall.Listener listener;
+
+ synchronized(ImsCall.this) {
+ listener = mListener;
+ updateCallGroup(null);
+ mUpdateRequest = UPDATE_NONE;
+ }
+
+ if (listener != null) {
+ try {
+ listener.onCallMergeFailed(ImsCall.this, reasonInfo);
+ } catch (Throwable t) {
+ loge("callSessionMergeFailed :: ", t);
+ }
+ }
+ }
+
+ @Override
+ public void callSessionUpdated(ImsCallSession session,
+ ImsCallProfile profile) {
+ if (DBG) {
+ log("callSessionUpdated :: session=" + session + ", profile=" + profile);
+ }
+
+ ImsCall.Listener listener;
+
+ synchronized(ImsCall.this) {
+ listener = mListener;
+ mCallProfile = profile;
+ mUpdateRequest = UPDATE_NONE;
+ }
+
+ if (listener != null) {
+ try {
+ listener.onCallUpdated(ImsCall.this);
+ } catch (Throwable t) {
+ loge("callSessionUpdated :: ", t);
+ }
+ }
+ }
+
+ @Override
+ public void callSessionUpdateFailed(ImsCallSession session,
+ ImsReasonInfo reasonInfo) {
+ if (DBG) {
+ log("callSessionUpdateFailed :: session=" + session +
+ ", reasonInfo=" + reasonInfo);
+ }
+
+ ImsCall.Listener listener;
+
+ synchronized(ImsCall.this) {
+ listener = mListener;
+ mUpdateRequest = UPDATE_NONE;
+ }
+
+ if (listener != null) {
+ try {
+ listener.onCallUpdateFailed(ImsCall.this, reasonInfo);
+ } catch (Throwable t) {
+ loge("callSessionUpdateFailed :: ", t);
+ }
+ }
+ }
+
+ @Override
+ public void callSessionUpdateReceived(ImsCallSession session,
+ ImsCallProfile profile) {
+ if (DBG) {
+ log("callSessionUpdateReceived :: session=" + session +
+ ", profile=" + profile);
+ }
+
+ ImsCall.Listener listener;
+
+ synchronized(ImsCall.this) {
+ listener = mListener;
+ mProposedCallProfile = profile;
+ mUpdateRequest = UPDATE_UNSPECIFIED;
+ }
+
+ if (listener != null) {
+ try {
+ listener.onCallUpdateReceived(ImsCall.this);
+ } catch (Throwable t) {
+ loge("callSessionUpdateReceived :: ", t);
+ }
+ }
+ }
+
+ @Override
+ public void callSessionConferenceExtended(ImsCallSession session,
+ ImsCallSession newSession, ImsCallProfile profile) {
+ if (DBG) {
+ log("callSessionConferenceExtended :: session=" + session
+ + ", newSession=" + newSession + ", profile=" + profile);
+ }
+
+ ImsCall newCall = createNewCall(newSession, profile);
+
+ if (newCall == null) {
+ callSessionConferenceExtendFailed(session, new ImsReasonInfo());
+ return;
+ }
+
+ ImsCall.Listener listener;
+
+ synchronized(ImsCall.this) {
+ listener = mListener;
+ mUpdateRequest = UPDATE_NONE;
+ }
+
+ if (listener != null) {
+ try {
+ listener.onCallConferenceExtended(ImsCall.this, newCall);
+ } catch (Throwable t) {
+ loge("callSessionConferenceExtended :: ", t);
+ }
+ }
+ }
+
+ @Override
+ public void callSessionConferenceExtendFailed(ImsCallSession session,
+ ImsReasonInfo reasonInfo) {
+ if (DBG) {
+ log("callSessionConferenceExtendFailed :: session=" + session +
+ ", reasonInfo=" + reasonInfo);
+ }
+
+ ImsCall.Listener listener;
+
+ synchronized(ImsCall.this) {
+ listener = mListener;
+ mUpdateRequest = UPDATE_NONE;
+ }
+
+ if (listener != null) {
+ try {
+ listener.onCallConferenceExtendFailed(ImsCall.this, reasonInfo);
+ } catch (Throwable t) {
+ loge("callSessionConferenceExtendFailed :: ", t);
+ }
+ }
+ }
+
+ @Override
+ public void callSessionConferenceExtendReceived(ImsCallSession session,
+ ImsCallSession newSession, ImsCallProfile profile) {
+ if (DBG) {
+ log("callSessionConferenceExtendReceived :: session=" + session
+ + ", newSession=" + newSession + ", profile=" + profile);
+ }
+
+ ImsCall newCall = createNewCall(newSession, profile);
+
+ if (newCall == null) {
+ // Should all the calls be terminated...???
+ return;
+ }
+
+ ImsCall.Listener listener;
+
+ synchronized(ImsCall.this) {
+ listener = mListener;
+ }
+
+ if (listener != null) {
+ try {
+ listener.onCallConferenceExtendReceived(ImsCall.this, newCall);
+ } catch (Throwable t) {
+ loge("callSessionConferenceExtendReceived :: ", t);
+ }
+ }
+ }
+
+ @Override
+ public void callSessionInviteParticipantsRequestDelivered(ImsCallSession session) {
+ if (DBG) {
+ log("callSessionInviteParticipantsRequestDelivered :: session=" + session);
+ }
+
+ ImsCall.Listener listener;
+
+ synchronized(ImsCall.this) {
+ listener = mListener;
+ }
+
+ if (listener != null) {
+ try {
+ listener.onCallInviteParticipantsRequestDelivered(ImsCall.this);
+ } catch (Throwable t) {
+ loge("callSessionInviteParticipantsRequestDelivered :: ", t);
+ }
+ }
+ }
+
+ @Override
+ public void callSessionInviteParticipantsRequestFailed(ImsCallSession session,
+ ImsReasonInfo reasonInfo) {
+ if (DBG) {
+ log("callSessionInviteParticipantsRequestFailed :: session=" + session
+ + ", reasonInfo=" + reasonInfo);
+ }
+
+ ImsCall.Listener listener;
+
+ synchronized(ImsCall.this) {
+ listener = mListener;
+ }
+
+ if (listener != null) {
+ try {
+ listener.onCallInviteParticipantsRequestFailed(ImsCall.this, reasonInfo);
+ } catch (Throwable t) {
+ loge("callSessionInviteParticipantsRequestFailed :: ", t);
+ }
+ }
+ }
+
+ @Override
+ public void callSessionRemoveParticipantsRequestDelivered(ImsCallSession session) {
+ if (DBG) {
+ log("callSessionRemoveParticipantsRequestDelivered :: session=" + session);
+ }
+
+ ImsCall.Listener listener;
+
+ synchronized(ImsCall.this) {
+ listener = mListener;
+ }
+
+ if (listener != null) {
+ try {
+ listener.onCallRemoveParticipantsRequestDelivered(ImsCall.this);
+ } catch (Throwable t) {
+ loge("callSessionRemoveParticipantsRequestDelivered :: ", t);
+ }
+ }
+ }
+
+ @Override
+ public void callSessionRemoveParticipantsRequestFailed(ImsCallSession session,
+ ImsReasonInfo reasonInfo) {
+ if (DBG) {
+ log("callSessionRemoveParticipantsRequestFailed :: session=" + session
+ + ", reasonInfo=" + reasonInfo);
+ }
+
+ ImsCall.Listener listener;
+
+ synchronized(ImsCall.this) {
+ listener = mListener;
+ }
+
+ if (listener != null) {
+ try {
+ listener.onCallRemoveParticipantsRequestFailed(ImsCall.this, reasonInfo);
+ } catch (Throwable t) {
+ loge("callSessionRemoveParticipantsRequestFailed :: ", t);
+ }
+ }
+ }
+
+ @Override
+ public void callSessionConferenceStateUpdated(ImsCallSession session,
+ ImsConferenceState state) {
+ if (DBG) {
+ log("callSessionConferenceStateUpdated :: session=" + session
+ + ", state=" + state);
+ }
+
+ ImsCall.Listener listener;
+
+ synchronized(ImsCall.this) {
+ notifyConferenceStateUpdated(state);
+ listener = mListener;
+ }
+
+ if (listener != null) {
+ try {
+ listener.onCallConferenceStateUpdated(ImsCall.this, state);
+ } catch (Throwable t) {
+ loge("callSessionConferenceStateUpdated :: ", t);
+ }
+ }
+ }
+
+ @Override
+ public void callSessionUssdMessageReceived(ImsCallSession session,
+ int mode, String ussdMessage) {
+ if (DBG) {
+ log("callSessionUssdMessageReceived :: session=" + session
+ + ", mode=" + mode + ", ussdMessage=" + ussdMessage);
+ }
+
+ ImsCall.Listener listener;
+
+ synchronized(ImsCall.this) {
+ listener = mListener;
+ }
+
+ if (listener != null) {
+ try {
+ listener.onCallUssdMessageReceived(ImsCall.this, mode, ussdMessage);
+ } catch (Throwable t) {
+ loge("callSessionUssdMessageReceived :: ", t);
+ }
+ }
+ }
+ }
+}
diff --git a/src/java/com/android/ims/ImsCallForwardInfo.aidl b/src/java/com/android/ims/ImsCallForwardInfo.aidl
new file mode 100644
index 0000000..a7c3f9a
--- /dev/null
+++ b/src/java/com/android/ims/ImsCallForwardInfo.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ims;
+
+parcelable ImsCallForwardInfo;
diff --git a/src/java/com/android/ims/ImsCallForwardInfo.java b/src/java/com/android/ims/ImsCallForwardInfo.java
new file mode 100644
index 0000000..3f8fd19
--- /dev/null
+++ b/src/java/com/android/ims/ImsCallForwardInfo.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ims;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Provides the call forward information for the supplementary service configuration.
+ *
+ * @hide
+ */
+public class ImsCallForwardInfo implements Parcelable {
+ // Refer to ImsUtInterface#CDIV_CF_XXX
+ public int mCondition;
+ // 0: disabled, 1: enabled
+ public int mStatus;
+ // 0x91: International, 0x81: Unknown
+ public int mToA;
+ // Number (it will not include the "sip" or "tel" URI scheme)
+ public String mNumber;
+ // No reply timer for CF
+ public int mTimeSeconds;
+
+ public ImsCallForwardInfo() {
+ }
+
+ 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);
+ }
+
+ @Override
+ public String toString() {
+ return super.toString() + ", Condition: " + mCondition
+ + ", Status: " + ((mStatus == 0) ? "disabled" : "enabled")
+ + ", ToA: " + mToA + ", 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();
+ }
+
+ public static final 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];
+ }
+ };
+}
diff --git a/src/java/com/android/ims/ImsCallGroup.java b/src/java/com/android/ims/ImsCallGroup.java
new file mode 100644
index 0000000..2e1a749
--- /dev/null
+++ b/src/java/com/android/ims/ImsCallGroup.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright (c) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ims;
+
+import java.util.ArrayList;
+
+import com.android.ims.internal.ICallGroup;
+import com.android.ims.internal.ICall;
+
+/**
+ * Manages all IMS calls which are established hereafter the initial 1-to-1 call is established.
+ * It's for providing the dummy calls which are disconnected with the IMS network after
+ * merged or extended to the conference.
+ *
+ * @hide
+ */
+public class ImsCallGroup implements ICallGroup {
+ private Object mLockObj = new Object();
+ private ImsCall mOwner;
+ private ImsCall mNeutralReferrer;
+ private ArrayList<ICall> mReferrers = new ArrayList<ICall>();
+
+ public ImsCallGroup() {
+ }
+
+ @Override
+ public ICall getNeutralReferrer() {
+ synchronized(mLockObj) {
+ return mNeutralReferrer;
+ }
+ }
+
+ @Override
+ public ICall getOwner() {
+ synchronized(mLockObj) {
+ return mOwner;
+ }
+ }
+
+ @Override
+ public ArrayList<ICall> getReferrers() {
+ synchronized(mLockObj) {
+ return mReferrers;
+ }
+ }
+
+ @Override
+ public boolean hasReferrer() {
+ synchronized(mLockObj) {
+ return !mReferrers.isEmpty();
+ }
+ }
+
+ @Override
+ public boolean isOwner(ICall call) {
+ ImsCall owner;
+
+ synchronized(mLockObj) {
+ owner = mOwner;
+ }
+
+ if ((call == null) || (owner == null)) {
+ return false;
+ }
+
+ if (!(call instanceof ImsCall)) {
+ return false;
+ }
+
+ return isSameCall(owner, (ImsCall)call);
+ }
+
+ @Override
+ public boolean isReferrer(ICall call) {
+ if (call == null) {
+ return false;
+ }
+
+ if (!(call instanceof ImsCall)) {
+ return false;
+ }
+
+ synchronized(mLockObj) {
+ for (ICall c : mReferrers) {
+ if ((c != null) && isSameCall((ImsCall)c, (ImsCall)call)) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ public void addReferrer(ICall call) {
+ if (call == null) {
+ return;
+ }
+
+ if (!(call instanceof ImsCall)) {
+ return;
+ }
+
+ // If the call is already present, ignore it
+ if (isReferrer(call)) {
+ return;
+ }
+
+ synchronized(mLockObj) {
+ mReferrers.add(call);
+ }
+ }
+
+ @Override
+ public void removeReferrer(ICall call) {
+ if (call == null) {
+ return;
+ }
+
+ if (!(call instanceof ImsCall)) {
+ return;
+ }
+
+ synchronized(mLockObj) {
+ mReferrers.remove(call);
+ }
+ }
+
+ @Override
+ public void setNeutralReferrer(ICall call) {
+ if ((call != null) && !(call instanceof ImsCall)) {
+ return;
+ }
+
+ synchronized(mLockObj) {
+ mNeutralReferrer = (ImsCall)call;
+ }
+ }
+
+ @Override
+ public void setOwner(ICall call) {
+ if ((call != null) && !(call instanceof ImsCall)) {
+ return;
+ }
+
+ synchronized(mLockObj) {
+ mOwner = (ImsCall)call;
+ }
+ }
+
+ @Override
+ public ICall getReferrer(String name) {
+ if ((name == null) || (name.isEmpty())) {
+ return null;
+ }
+
+ ArrayList<ICall> referrers = getReferrers();
+
+ if (referrers == null) {
+ return null;
+ }
+
+ for (ICall call : referrers) {
+ if ((call != null) && call.checkIfRemoteUserIsSame(name)) {
+ return call;
+ }
+ }
+
+ return null;
+ }
+
+ private boolean isSameCall(ImsCall call1, ImsCall call2) {
+ if ((call1 == null) || (call2 == null)) {
+ return false;
+ }
+
+ return call1.equalsTo(call2);
+ }
+}
diff --git a/src/java/com/android/ims/ImsCallProfile.aidl b/src/java/com/android/ims/ImsCallProfile.aidl
new file mode 100644
index 0000000..a356d13
--- /dev/null
+++ b/src/java/com/android/ims/ImsCallProfile.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ims;
+
+parcelable ImsCallProfile;
diff --git a/src/java/com/android/ims/ImsCallProfile.java b/src/java/com/android/ims/ImsCallProfile.java
new file mode 100644
index 0000000..208f467
--- /dev/null
+++ b/src/java/com/android/ims/ImsCallProfile.java
@@ -0,0 +1,289 @@
+/*
+ * Copyright (c) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ims;
+
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Parcelable object to handle IMS call profile.
+ * It is created from GSMA IR.92/IR.94, 3GPP TS 24.229/TS 26.114/TS26.111.
+ * It provides the service and call type, the additional information related to the call.
+ *
+ * @hide
+ */
+public 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.
+ */
+ public static final String EXTRA_CONFERENCE = "conference";
+ public static final String EXTRA_E_CALL = "e_call";
+ public static final String EXTRA_VMS = "vms";
+ public static final String EXTRA_CALL_MODE_CHANGEABLE = "call_mode_changeable";
+ public static final String EXTRA_CONFERENCE_AVAIL = "conference_avail";
+
+ /**
+ * Integer extra properties
+ * oir : Rule for originating identity (number) presentation, MO/MT.
+ * {@link ImsCallProfile#OIR_DEFAULT}
+ * {@link ImsCallProfile#OIR_PRESENTATION_RESTRICTED}
+ * {@link ImsCallProfile#OIR_PRESENTATION_NOT_RESTRICTED}
+ * cnap : Rule for calling name presentation
+ * {@link ImsCallProfile#OIR_DEFAULT}
+ * {@link ImsCallProfile#OIR_PRESENTATION_RESTRICTED}
+ * {@link ImsCallProfile#OIR_PRESENTATION_NOT_RESTRICTED}
+ * dialstring : To identify the Ims call type, MO
+ * {@link ImsCallProfile#DIALSTRING_NORMAL_CALL}
+ * {@link ImsCallProfile#DIALSTRING_SS_CONF}
+ * {@link ImsCallProfile#DIALSTRING_USSD}
+ */
+ public static final String EXTRA_OIR = "oir";
+ public static final String EXTRA_CNAP = "cnap";
+ public static final String EXTRA_DIALSTRING = "dialstring";
+
+ /**
+ * Values for EXTRA_OIR / EXTRA_CNAP
+ */
+ public static final int OIR_DEFAULT = 0; // "user subscription default value"
+ public static final int OIR_PRESENTATION_RESTRICTED = 1;
+ public static final int OIR_PRESENTATION_NOT_RESTRICTED = 2;
+
+ /**
+ * Values for EXTRA_DIALSTRING
+ */
+ // default (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;
+
+ /**
+ * 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)
+ */
+ 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 int mServiceType;
+ public int mCallType;
+ public Bundle mCallExtras;
+ public ImsStreamMediaProfile mMediaProfile;
+
+
+
+ public ImsCallProfile(Parcel in) {
+ readFromParcel(in);
+ }
+
+ public ImsCallProfile() {
+ mServiceType = SERVICE_TYPE_NORMAL;
+ mCallType = CALL_TYPE_VOICE_N_VIDEO;
+ mCallExtras = new Bundle();
+ mMediaProfile = new ImsStreamMediaProfile();
+ }
+
+ public ImsCallProfile(int serviceType, int callType) {
+ mServiceType = serviceType;
+ mCallType = callType;
+ mCallExtras = new Bundle();
+ mMediaProfile = new ImsStreamMediaProfile();
+ }
+
+ 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);
+ }
+ }
+
+ public void updateCallType(ImsCallProfile profile) {
+ mCallType = profile.mCallType;
+ }
+
+ public void updateCallExtras(ImsCallProfile profile) {
+ mCallExtras.clear();
+ mCallExtras = (Bundle) profile.mCallExtras.clone();
+ }
+
+ @Override
+ public String toString() {
+ return "{ serviceType=" + mServiceType +
+ ", callType=" + mCallType +
+ ", callExtras=" + mCallExtras.toString() +
+ ", mediaProfile=" + mMediaProfile.toString() + " }";
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(mServiceType);
+ out.writeInt(mCallType);
+ out.writeParcelable(mCallExtras, 0);
+ out.writeParcelable(mMediaProfile, 0);
+ }
+
+ private void readFromParcel(Parcel in) {
+ mServiceType = in.readInt();
+ mCallType = in.readInt();
+ mCallExtras = in.readParcelable(null);
+ mMediaProfile = in.readParcelable(null);
+ }
+
+ public static final 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];
+ }
+ };
+}
diff --git a/src/java/com/android/ims/ImsConferenceState.aidl b/src/java/com/android/ims/ImsConferenceState.aidl
new file mode 100644
index 0000000..2fc029f
--- /dev/null
+++ b/src/java/com/android/ims/ImsConferenceState.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ims;
+
+parcelable ImsConferenceState;
diff --git a/src/java/com/android/ims/ImsConferenceState.java b/src/java/com/android/ims/ImsConferenceState.java
new file mode 100644
index 0000000..f708d5b
--- /dev/null
+++ b/src/java/com/android/ims/ImsConferenceState.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ims;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Provides the conference information (defined in RFC 4575) for IMS conference call.
+ *
+ * @hide
+ */
+public class ImsConferenceState implements Parcelable {
+ /**
+ * 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";
+
+ /**
+ * conference-info : SIP status code (integer)
+ */
+ public static final String SIP_STATUS_CODE = "sipstatuscode";
+
+ public HashMap<String, Bundle> mParticipants = new HashMap<String, Bundle>();
+
+ public ImsConferenceState() {
+ }
+
+ public 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 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];
+ }
+ };
+}
diff --git a/src/java/com/android/ims/ImsConnectionStateListener.java b/src/java/com/android/ims/ImsConnectionStateListener.java
new file mode 100644
index 0000000..42ac717
--- /dev/null
+++ b/src/java/com/android/ims/ImsConnectionStateListener.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ims;
+
+/**
+ * Listener for receiving notifications about changes to the IMS connection.
+ * It provides a state of IMS registration between UE and IMS network, the service
+ * availability of the local device during IMS registered.
+ *
+ * @hide
+ */
+public class ImsConnectionStateListener {
+ /**
+ * Called when the device is connected to the IMS network.
+ */
+ public void onImsConnected() {
+ // no-op
+ }
+
+ /**
+ * Called when the device is disconnected from the IMS network.
+ */
+ public void onImsDisconnected() {
+ // no-op
+ }
+
+ /**
+ * Called when its suspended IMS connection is resumed, meaning the connection
+ * now allows throughput.
+ */
+ public void onImsResumed() {
+ // no-op
+ }
+
+ /**
+ * Called when its current IMS connection is suspended, meaning there is no data throughput.
+ */
+ public void onImsSuspended() {
+ // no-op
+ }
+}
diff --git a/src/java/com/android/ims/ImsException.java b/src/java/com/android/ims/ImsException.java
new file mode 100644
index 0000000..9ec3546
--- /dev/null
+++ b/src/java/com/android/ims/ImsException.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ims;
+
+/**
+ * This class defines a general IMS-related exception.
+ *
+ * @hide
+ */
+public class ImsException extends Exception {
+
+ /**
+ * Refer to CODE_LOCAL_* in {@link ImsReasonInfo}
+ */
+ private int mCode;
+
+ public ImsException() {
+ }
+
+ public ImsException(String message, int code) {
+ super(message);
+ mCode = code;
+ }
+
+ public ImsException(String message, Throwable cause, int code) {
+ super(message, cause);
+ mCode = code;
+ }
+
+ /**
+ * Gets the detailed exception code when ImsException is throwed
+ *
+ * @return the exception code in {@link ImsReasonInfo}
+ */
+ public int getCode() {
+ return mCode;
+ }
+}
diff --git a/src/java/com/android/ims/ImsManager.java b/src/java/com/android/ims/ImsManager.java
new file mode 100644
index 0000000..3e59f1a
--- /dev/null
+++ b/src/java/com/android/ims/ImsManager.java
@@ -0,0 +1,601 @@
+/*
+ * Copyright (c) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ims;
+
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.IBinder;
+import android.os.IBinder.DeathRecipient;
+import android.os.Message;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.telephony.Rlog;
+
+import com.android.ims.internal.IImsCallSession;
+import com.android.ims.internal.IImsRegistrationListener;
+import com.android.ims.internal.IImsService;
+import com.android.ims.internal.IImsUt;
+import com.android.ims.internal.ImsCallSession;
+
+/**
+ * Provides APIs for IMS services, such as initiating IMS calls, and provides access to
+ * the operator's IMS network. This class is the starting point for any IMS actions.
+ * You can acquire an instance of it with {@link #getInstance getInstance()}.</p>
+ * <p>The APIs in this class allows you to:</p>
+ *
+ * @hide
+ */
+public class ImsManager {
+ /**
+ * For accessing the IMS related service.
+ * Internal use only.
+ * @hide
+ */
+ public static final String IMS_SERVICE = "ims";
+
+ /**
+ * The result code to be sent back with the incoming call {@link PendingIntent}.
+ * @see #open(PendingIntent, ImsConnectionStateListener)
+ */
+ public static final int INCOMING_CALL_RESULT_CODE = 101;
+
+ /**
+ * Key to retrieve the call ID from an incoming call intent.
+ * @see #open(PendingIntent, ImsConnectionStateListener)
+ */
+ public static final String EXTRA_CALL_ID = "android:imsCallID";
+
+ /**
+ * Action to broadcast when ImsService is up.
+ * Internal use only.
+ * @hide
+ */
+ public static final String ACTION_IMS_SERVICE_UP =
+ "com.android.ims.IMS_SERVICE_UP";
+
+ /**
+ * Action to broadcast when ImsService is down.
+ * Internal use only.
+ * @hide
+ */
+ public static final String ACTION_IMS_SERVICE_DOWN =
+ "com.android.ims.IMS_SERVICE_DOWN";
+
+ /**
+ * Action for the incoming call intent for the Phone app.
+ * Internal use only.
+ * @hide
+ */
+ public static final String ACTION_IMS_INCOMING_CALL =
+ "com.android.ims.IMS_INCOMING_CALL";
+
+ /**
+ * Part of the ACTION_IMS_INCOMING_CALL intents.
+ * An integer value; service identifier obtained from {@link ImsManager#open}.
+ * Internal use only.
+ * @hide
+ */
+ public static final String EXTRA_SERVICE_ID = "android:imsServiceId";
+
+ /**
+ * Part of the ACTION_IMS_INCOMING_CALL intents.
+ * An boolean value; Flag to indicate that the incoming call is a normal call or call for USSD.
+ * The value "true" indicates that the incoming call is for USSD.
+ * Internal use only.
+ * @hide
+ */
+ public static final String EXTRA_USSD = "android:ussd";
+
+
+
+ private static final String TAG = "ImsManager";
+ private static final boolean DBG = true;
+
+ private static ImsManager mImsManager = null;
+ private Context mContext;
+ private IImsService mImsService = null;
+ private ImsServiceDeathRecipient mDeathRecipient = new ImsServiceDeathRecipient();
+ // Ut interface for the supplementary service configuration
+ private ImsUt mUt = null;
+
+ /**
+ * Gets a manager instance.
+ *
+ * @param context application context for creating the manager object
+ * @return the manager instance
+ */
+ public static ImsManager getInstance(Context context) {
+ if (mImsManager == null) {
+ mImsManager = new ImsManager(context);
+ }
+
+ return mImsManager;
+ }
+
+ private ImsManager(Context context) {
+ mContext = context;
+ createImsService(true);
+ }
+
+ /**
+ * Opens the IMS service for making calls and/or receiving generic IMS calls.
+ * The caller may make subsquent calls through {@link #makeCall}.
+ * 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 getCallId} and it can be used to take a call.
+ *
+ * @param serviceClass a service class specified in {@link ImsServiceClass}
+ * For VoLTE service, it MUST be a {@link ImsServiceClass#MMTEL}.
+ * @param incomingCallPendingIntent When an incoming call is received,
+ * the IMS service will call {@link PendingIntent#send(Context, int, Intent)} to
+ * send back the intent to the caller with {@link #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 identifier (greater than 0) for the specified service
+ * @throws NullPointerException if {@code incomingCallPendingIntent}
+ * or {@code listener} is null
+ * @throws ImsException if calling the IMS service results in an error
+ * @see #getCallId
+ * @see #getServiceId
+ */
+ public int open(int serviceClass, PendingIntent incomingCallPendingIntent,
+ ImsConnectionStateListener listener) throws ImsException {
+ checkAndThrowExceptionIfServiceUnavailable();
+
+ if (incomingCallPendingIntent == null) {
+ throw new NullPointerException("incomingCallPendingIntent can't be null");
+ }
+
+ if (listener == null) {
+ throw new NullPointerException("listener can't be null");
+ }
+
+ int result = 0;
+
+ try {
+ result = mImsService.open(serviceClass, incomingCallPendingIntent,
+ createRegistrationListenerProxy(serviceClass, listener));
+ } catch (RemoteException e) {
+ throw new ImsException("open()", e,
+ ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
+ }
+
+ if (result <= 0) {
+ // If the return value is a minus value,
+ // it means that an error occurred in the service.
+ // So, it needs to convert to the reason code specified in ImsReasonInfo.
+ throw new ImsException("open()", (result * (-1)));
+ }
+
+ return result;
+ }
+
+ /**
+ * Closes the specified service ({@link ImsServiceClass}) not to make/receive calls.
+ * All the resources that were allocated to the service are also released.
+ *
+ * @param serviceId a service id to be closed which is obtained from {@link ImsManager#open}
+ * @throws ImsException if calling the IMS service results in an error
+ */
+ public void close(int serviceId) throws ImsException {
+ checkAndThrowExceptionIfServiceUnavailable();
+
+ try {
+ mImsService.close(serviceId);
+ } catch (RemoteException e) {
+ throw new ImsException("close()", e,
+ ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
+ } finally {
+ mUt = null;
+ }
+ }
+
+ /**
+ * Gets the configuration interface to provision / withdraw the supplementary service settings.
+ *
+ * @param serviceId a service id which is obtained from {@link ImsManager#open}
+ * @return the Ut interface instance
+ * @throws ImsException if getting the Ut interface results in an error
+ */
+ public ImsUtInterface getSupplementaryServiceConfiguration(int serviceId)
+ throws ImsException {
+ // FIXME: manage the multiple Ut interfaces based on the service id
+ if (mUt == null) {
+ checkAndThrowExceptionIfServiceUnavailable();
+
+ try {
+ IImsUt iUt = mImsService.getUtInterface(serviceId);
+
+ if (iUt == null) {
+ throw new ImsException("getSupplementaryServiceConfiguration()",
+ ImsReasonInfo.CODE_UT_NOT_SUPPORTED);
+ }
+
+ mUt = new ImsUt(iUt);
+ } catch (RemoteException e) {
+ throw new ImsException("getSupplementaryServiceConfiguration()", e,
+ ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
+ }
+ }
+
+ return mUt;
+ }
+
+ /**
+ * Checks if the IMS service has successfully registered to the IMS network
+ * with the specified service & call type.
+ *
+ * @param serviceId a service id which is obtained from {@link ImsManager#open}
+ * @param serviceType 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
+ * @throws ImsException if calling the IMS service results in an error
+ */
+ public boolean isConnected(int serviceId, int serviceType, int callType)
+ throws ImsException {
+ checkAndThrowExceptionIfServiceUnavailable();
+
+ try {
+ return mImsService.isConnected(serviceId, serviceType, callType);
+ } catch (RemoteException e) {
+ throw new ImsException("isServiceConnected()", e,
+ ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
+ }
+ }
+
+ /**
+ * Checks if the specified IMS service is opend.
+ *
+ * @param serviceId a service id which is obtained from {@link ImsManager#open}
+ * @return true if the specified service id is opened; false otherwise
+ * @throws ImsException if calling the IMS service results in an error
+ */
+ public boolean isOpened(int serviceId) throws ImsException {
+ checkAndThrowExceptionIfServiceUnavailable();
+
+ try {
+ return mImsService.isOpened(serviceId);
+ } catch (RemoteException e) {
+ throw new ImsException("isOpened()", e,
+ ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
+ }
+ }
+
+ /**
+ * Creates a {@link ImsCallProfile} from the service capabilities & IMS registration state.
+ *
+ * @param serviceId a service id which is obtained from {@link ImsManager#open}
+ * @param serviceType 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
+ * @throws ImsException if calling the IMS service results in an error
+ */
+ public ImsCallProfile createCallProfile(int serviceId,
+ int serviceType, int callType) throws ImsException {
+ checkAndThrowExceptionIfServiceUnavailable();
+
+ try {
+ return mImsService.createCallProfile(serviceId, serviceType, callType);
+ } catch (RemoteException e) {
+ throw new ImsException("createCallProfile()", e,
+ ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
+ }
+ }
+
+ /**
+ * Creates a {@link ImsCall} to make a call.
+ *
+ * @param serviceId a service id which is obtained from {@link ImsManager#open}
+ * @param profile a call profile to make the call
+ * (it contains service type, call type, media information, etc.)
+ * @param participants participants to invite the conference call
+ * @param listener listen to the call events from {@link ImsCall}
+ * @return a {@link ImsCall} object
+ * @throws ImsException if calling the IMS service results in an error
+ */
+ public ImsCall makeCall(int serviceId, ImsCallProfile profile, String[] callees,
+ ImsCall.Listener listener) throws ImsException {
+ if (DBG) {
+ log("makeCall :: serviceId=" + serviceId
+ + ", profile=" + profile + ", callees=" + callees);
+ }
+
+ checkAndThrowExceptionIfServiceUnavailable();
+
+ ImsCall call = new ImsCall(mContext, profile);
+
+ call.setListener(listener);
+ ImsCallSession session = createCallSession(serviceId, profile);
+
+ if ((callees != null) && (callees.length == 1)) {
+ call.start(session, callees[0]);
+ } else {
+ call.start(session, callees);
+ }
+
+ return call;
+ }
+
+ /**
+ * Creates a {@link ImsCall} to take an incoming call.
+ *
+ * @param serviceId a service id which is obtained from {@link ImsManager#open}
+ * @param incomingCallIntent the incoming call broadcast intent
+ * @param listener to listen to the call events from {@link ImsCall}
+ * @return a {@link ImsCall} object
+ * @throws ImsException if calling the IMS service results in an error
+ */
+ public ImsCall takeCall(int serviceId, Intent incomingCallIntent,
+ ImsCall.Listener listener) throws ImsException {
+ if (DBG) {
+ log("takeCall :: serviceId=" + serviceId
+ + ", incomingCall=" + incomingCallIntent);
+ }
+
+ checkAndThrowExceptionIfServiceUnavailable();
+
+ if (incomingCallIntent == null) {
+ throw new ImsException("Can't retrieve session with null intent",
+ ImsReasonInfo.CODE_LOCAL_ILLEGAL_ARGUMENT);
+ }
+
+ int incomingServiceId = getServiceId(incomingCallIntent);
+
+ if (serviceId != incomingServiceId) {
+ throw new ImsException("Service id is mismatched in the incoming call intent",
+ ImsReasonInfo.CODE_LOCAL_ILLEGAL_ARGUMENT);
+ }
+
+ String callId = getCallId(incomingCallIntent);
+
+ if (callId == null) {
+ throw new ImsException("Call ID missing in the incoming call intent",
+ ImsReasonInfo.CODE_LOCAL_ILLEGAL_ARGUMENT);
+ }
+
+ try {
+ IImsCallSession session = mImsService.getPendingCallSession(serviceId, callId);
+
+ if (session == null) {
+ throw new ImsException("No pending session for the call",
+ ImsReasonInfo.CODE_LOCAL_NO_PENDING_CALL);
+ }
+
+ ImsCall call = new ImsCall(mContext, session.getCallProfile());
+
+ call.attachSession(new ImsCallSession(session));
+ call.setListener(listener);
+
+ return call;
+ } catch (Throwable t) {
+ throw new ImsException("takeCall()", t, ImsReasonInfo.CODE_UNSPECIFIED);
+ }
+ }
+
+ /**
+ * Gets the call ID from the specified incoming call broadcast intent.
+ *
+ * @param incomingCallIntent the incoming call broadcast intent
+ * @return the call ID or null if the intent does not contain it
+ */
+ private static String getCallId(Intent incomingCallIntent) {
+ if (incomingCallIntent == null) {
+ return null;
+ }
+
+ return incomingCallIntent.getStringExtra(EXTRA_CALL_ID);
+ }
+
+ /**
+ * Gets the service type from the specified incoming call broadcast intent.
+ *
+ * @param incomingCallIntent the incoming call broadcast intent
+ * @return the service identifier or -1 if the intent does not contain it
+ */
+ private static int getServiceId(Intent incomingCallIntent) {
+ if (incomingCallIntent == null) {
+ return (-1);
+ }
+
+ return incomingCallIntent.getIntExtra(EXTRA_SERVICE_ID, -1);
+ }
+
+ /**
+ * Binds the IMS service only if the service is not created.
+ */
+ private void checkAndThrowExceptionIfServiceUnavailable()
+ throws ImsException {
+ if (mImsService == null) {
+ createImsService(true);
+
+ if (mImsService == null) {
+ throw new ImsException("Service is unavailable",
+ ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
+ }
+ }
+ }
+
+ /**
+ * Binds the IMS service to make/receive the call.
+ */
+ private void createImsService(boolean checkService) {
+ if (checkService) {
+ IBinder binder = ServiceManager.checkService(IMS_SERVICE);
+
+ if (binder == null) {
+ return;
+ }
+ }
+
+ IBinder b = ServiceManager.getService(IMS_SERVICE);
+
+ if (b != null) {
+ try {
+ b.linkToDeath(mDeathRecipient, 0);
+ } catch (RemoteException e) {
+ }
+ }
+
+ mImsService = IImsService.Stub.asInterface(b);
+ }
+
+ /**
+ * Creates a {@link ImsCallSession} with the specified call profile.
+ * Use other methods, if applicable, instead of interacting with
+ * {@link ImsCallSession} directly.
+ *
+ * @param serviceId a service id which is obtained from {@link ImsManager#open}
+ * @param profile a call profile to make the call
+ */
+ private ImsCallSession createCallSession(int serviceId,
+ ImsCallProfile profile) throws ImsException {
+ try {
+ return new ImsCallSession(mImsService.createCallSession(serviceId, profile, null));
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+
+ private ImsRegistrationListenerProxy createRegistrationListenerProxy(int serviceClass,
+ ImsConnectionStateListener listener) {
+ ImsRegistrationListenerProxy proxy =
+ new ImsRegistrationListenerProxy(serviceClass, listener);
+ return proxy;
+ }
+
+ private void log(String s) {
+ Rlog.d(TAG, s);
+ }
+
+ private void loge(String s) {
+ Rlog.e(TAG, s);
+ }
+
+ private void loge(String s, Throwable t) {
+ Rlog.e(TAG, s, t);
+ }
+
+ /**
+ * Death recipient class for monitoring IMS service.
+ */
+ private class ImsServiceDeathRecipient implements IBinder.DeathRecipient {
+ @Override
+ public void binderDied() {
+ mImsService = null;
+ mUt = null;
+
+ if (mContext != null) {
+ mContext.sendBroadcast(new Intent(ACTION_IMS_SERVICE_DOWN));
+ }
+ }
+ }
+
+ /**
+ * Adapter class for {@link IImsRegistrationListener}.
+ */
+ private class ImsRegistrationListenerProxy extends IImsRegistrationListener.Stub {
+ private int mServiceClass;
+ private ImsConnectionStateListener mListener;
+
+ public ImsRegistrationListenerProxy(int serviceClass,
+ ImsConnectionStateListener listener) {
+ mServiceClass = serviceClass;
+ mListener = listener;
+ }
+
+ public boolean isSameProxy(int serviceClass) {
+ return (mServiceClass == serviceClass);
+ }
+
+ @Override
+ public void registrationConnected() {
+ if (DBG) {
+ log("registrationConnected ::");
+ }
+
+ if (mListener != null) {
+ mListener.onImsConnected();
+ }
+ }
+
+ @Override
+ public void registrationDisconnected() {
+ if (DBG) {
+ log("registrationDisconnected ::");
+ }
+
+ if (mListener != null) {
+ mListener.onImsDisconnected();
+ }
+ }
+
+ @Override
+ public void registrationResumed() {
+ if (DBG) {
+ log("registrationResumed ::");
+ }
+
+ if (mListener != null) {
+ mListener.onImsResumed();
+ }
+ }
+
+ @Override
+ public void registrationSuspended() {
+ if (DBG) {
+ log("registrationSuspended ::");
+ }
+
+ if (mListener != null) {
+ mListener.onImsSuspended();
+ }
+ }
+
+ @Override
+ public void registrationServiceCapabilityChanged(int serviceClass, int event) {
+ log("registrationServiceCapabilityChanged :: serviceClass=" +
+ serviceClass + ", event=" + event);
+
+ if (mListener != null) {
+ mListener.onImsConnected();
+ }
+ }
+ }
+}
diff --git a/src/java/com/android/ims/ImsReasonInfo.aidl b/src/java/com/android/ims/ImsReasonInfo.aidl
new file mode 100644
index 0000000..17e6d3a
--- /dev/null
+++ b/src/java/com/android/ims/ImsReasonInfo.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ims;
+
+parcelable ImsReasonInfo;
diff --git a/src/java/com/android/ims/ImsReasonInfo.java b/src/java/com/android/ims/ImsReasonInfo.java
new file mode 100644
index 0000000..99faba6
--- /dev/null
+++ b/src/java/com/android/ims/ImsReasonInfo.java
@@ -0,0 +1,326 @@
+/*
+ * Copyright (c) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ims;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * This class enables an application to get details on why a method call failed.
+ *
+ * @hide
+ */
+public class ImsReasonInfo implements Parcelable {
+
+ /**
+ * Reason types, defines the error category.
+ * UNSPECIFIED - unknown error reason
+ * LOCAL - indicates the local/device error reason
+ * LOCAL_TIMEOUT - indicates the local error reason when a specific timer is expired
+ * STATUSCODE - indicates the interworking error reason by SIP status code received
+ * from the network
+ * MEDIA - indicates the media error reason (local resource, SDP parameter, etc.)
+ * USER - indicates the error reason by the local or remote user
+ * UT - indicates the error reason for the supplementary service configuration
+ */
+ public static final int TYPE_UNSPECIFIED = 0;
+ public static final int TYPE_LOCAL = 1;
+ public static final int TYPE_TIMEOUT = 2;
+ public static final int TYPE_STATUSCODE = 3;
+ public static final int TYPE_MEDIA = 4;
+ public static final int TYPE_USER = 5;
+ public static final int TYPE_UT = 8;
+
+ /**
+ * Specific code of each types
+ */
+ public static final int CODE_UNSPECIFIED = 0;
+
+ /**
+ * LOCAL
+ */
+ // IMS -> Telephony
+ // The passed argument is an invalid
+ public static final int CODE_LOCAL_ILLEGAL_ARGUMENT = 101;
+ // The operation is invoked in 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;
+ // IMS service goes down (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 -> Telephony
+ // Service unavailable; by power off
+ public static final int CODE_LOCAL_POWER_OFF = 111;
+ // Service unavailable; by low battery
+ public static final int CODE_LOCAL_LOW_BATTERY = 112;
+ // Service unavailable; by out of service (data service state)
+ public static final int CODE_LOCAL_NETWORK_NO_SERVICE = 121;
+ // Service unavailable; by 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; by located in roaming area
+ public static final int CODE_LOCAL_NETWORK_ROAMING = 123;
+ // Service unavailable; by IP changed
+ public static final int CODE_LOCAL_NETWORK_IP_CHANGED = 124;
+ // Service unavailable; other
+ public static final int CODE_LOCAL_SERVICE_UNAVAILABLE = 131;
+ // Service unavailable; IMS connection is lost (IMS is not registered)
+ public static final int CODE_LOCAL_NOT_REGISTERED = 132;
+
+ // IMS <-> Telephony
+ // Max call exceeded
+ public static final int CODE_LOCAL_CALL_EXCEEDED = 141;
+ // IMS <- Telephony
+ // Call busy
+ public static final int CODE_LOCAL_CALL_BUSY = 142;
+ // Call decline
+ public static final int CODE_LOCAL_CALL_DECLINE = 143;
+ // IMS -> Telephony
+ // SRVCC is in progress
+ public static final int CODE_LOCAL_CALL_VCC_ON_PROGRESSING = 144;
+ // Resource reservation is failed (QoS precondition)
+ public static final int CODE_LOCAL_CALL_RESOURCE_RESERVATION_FAILED = 145;
+ // Retry CS call; VoLTE service can't be provided by the network or remote end
+ // Resolve the extra code(EXTRA_CODE_CALL_RETRY_*) if the below code is set
+ public static final int CODE_LOCAL_CALL_CS_RETRY_REQUIRED = 146;
+ // Retry VoLTE call; VoLTE service can't be provided by the network temporarily
+ 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;
+
+ /**
+ * TIMEOUT (IMS -> Telephony)
+ */
+ // 1xx waiting timer is expired after sending INVITE request (MO only)
+ public static final int CODE_TIMEOUT_1XX_WAITING = 201;
+ // User no 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;
+
+ /**
+ * STATUSCODE (SIP response code) (IMS -> Telephony)
+ */
+ // 3xx responses
+ // SIP request is redirected
+ public static final int CODE_SIP_REDIRECTED = 321;
+ // 4xx responses
+ // 400 : Bad Request
+ public static final int CODE_SIP_BAD_REQUEST = 331;
+ // 403 : Forbidden
+ public static final int CODE_SIP_FORBIDDEN = 332;
+ // 404 : Not Found
+ public static final int CODE_SIP_NOT_FOUND = 333;
+ // 415 : Unsupported Media Type
+ // 416 : Unsupported URI Scheme
+ // 420 : Bad Extension
+ public static final int CODE_SIP_NOT_SUPPORTED = 334;
+ // 408 : Request Timeout
+ public static final int CODE_SIP_REQUEST_TIMEOUT = 335;
+ // 480 : Temporarily Unavailable
+ public static final int CODE_SIP_TEMPRARILY_UNAVAILABLE = 336;
+ // 484 : Address Incomplete
+ public static final int CODE_SIP_BAD_ADDRESS = 337;
+ // 486 : Busy Here
+ // 600 : Busy Everywhere
+ public static final int CODE_SIP_BUSY = 338;
+ // 487 : Request Terminated
+ public static final int CODE_SIP_REQUEST_CANCELLED = 339;
+ // 406 : Not Acceptable
+ // 488 : Not Acceptable Here
+ // 606 : Not Acceptable
+ public static final int CODE_SIP_NOT_ACCEPTABLE = 340;
+ // 410 : Gone
+ // 604 : Does Not Exist Anywhere
+ public static final int CODE_SIP_NOT_REACHABLE = 341;
+ // Others
+ public static final int CODE_SIP_CLIENT_ERROR = 342;
+ // 5xx responses
+ // 501 : Server Internal Error
+ public static final int CODE_SIP_SERVER_INTERNAL_ERROR = 351;
+ // 503 : Service Unavailable
+ public static final int CODE_SIP_SERVICE_UNAVAILABLE = 352;
+ // 504 : Server Time-out
+ public static final int CODE_SIP_SERVER_TIMEOUT = 353;
+ // Others
+ public static final int CODE_SIP_SERVER_ERROR = 354;
+ // 6xx responses
+ // 603 : Decline
+ public static final int CODE_SIP_USER_REJECTED = 361;
+ // Others
+ public static final int CODE_SIP_GLOBAL_ERROR = 362;
+
+ /**
+ * 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;
+ // Unknown media related errors
+ public static final int CODE_MEDIA_UNSPECIFIED = 404;
+
+ /**
+ * USER
+ */
+ // Telephony -> IMS
+ // User triggers the call end
+ public static final int CODE_USER_TERMINATED = 501;
+ // No action while an incoming call is ringing
+ public static final int CODE_USER_NOANSWER = 502;
+ // User ignores an incoming call
+ public static final int CODE_USER_IGNORE = 503;
+ // User declines an incoming call
+ public static final int CODE_USER_DECLINE = 504;
+ // IMS -> Telephony
+ // The call is terminated by the network or remote user
+ public static final int CODE_USER_TERMINATED_BY_REMOTE = 510;
+
+ /**
+ * Extra codes for the specific code value
+ * This value can be referred when the code is CODE_LOCAL_CALL_CS_RETRY_REQUIRED.
+ */
+ // Try to connect CS call; normal
+ public static final int EXTRA_CODE_CALL_RETRY_NORMAL = 1;
+ // Try to connect CS call without the notification to user
+ public static final int EXTRA_CODE_CALL_RETRY_SILENT_REDIAL = 2;
+ // Try to connect CS call by the settings of the menu
+ public static final int EXTRA_CODE_CALL_RETRY_BY_SETTINGS = 3;
+
+ /**
+ * UT
+ */
+ public static final int CODE_UT_NOT_SUPPORTED = 801;
+ public static final int CODE_UT_SERVICE_UNAVAILABLE = 802;
+ public static final int CODE_UT_OPERATION_NOT_ALLOWED = 803;
+ public static final int CODE_UT_CB_PASSWORD_MISMATCH = 821;
+
+
+
+ // For reason type
+ public int mReasonType;
+ // For main reason code
+ public int mCode;
+ // For the extra code value; it depends on the code value.
+ public int mExtraCode;
+ // For the additional message of the reason info.
+ public String mExtraMessage;
+
+ public ImsReasonInfo() {
+ mReasonType = TYPE_UNSPECIFIED;
+ mCode = CODE_UNSPECIFIED;
+ mExtraCode = CODE_UNSPECIFIED;
+ mExtraMessage = null;
+ }
+
+ public ImsReasonInfo(Parcel in) {
+ readFromParcel(in);
+ }
+
+ public ImsReasonInfo(int code, int extraCode) {
+ mReasonType = (int) (code / 100);
+ mCode = code;
+ mExtraCode = extraCode;
+ mExtraMessage = null;
+ }
+
+ public ImsReasonInfo(int code, int extraCode, String extraMessage) {
+ mReasonType = (int) (code / 100);
+ mCode = code;
+ mExtraCode = extraCode;
+ mExtraMessage = extraMessage;
+ }
+
+ /**
+ *
+ */
+ public int getCode() {
+ return mCode;
+ }
+
+ /**
+ *
+ */
+ public int getExtraCode() {
+ return mExtraCode;
+ }
+
+ /**
+ *
+ */
+ public String getExtraMessage() {
+ return mExtraMessage;
+ }
+
+ /**
+ *
+ */
+ public int getReasonType() {
+ return mReasonType;
+ }
+
+ /**
+ * Returns the string format of {@link ImsReasonInfo}
+ *
+ * @return the string format of {@link ImsReasonInfo}
+ */
+ public String toString() {
+ return "ImsReasonInfo :: {" + mReasonType + ", "
+ + mCode + ", " + mExtraCode + ", " + mExtraMessage + "}";
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(mReasonType);
+ out.writeInt(mCode);
+ out.writeInt(mExtraCode);
+ out.writeString(mExtraMessage);
+ }
+
+ private void readFromParcel(Parcel in) {
+ mReasonType = in.readInt();
+ mCode = in.readInt();
+ mExtraCode = in.readInt();
+ mExtraMessage = in.readString();
+ }
+
+ public static final 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/src/java/com/android/ims/ImsServiceClass.java b/src/java/com/android/ims/ImsServiceClass.java
new file mode 100644
index 0000000..a4e43bc
--- /dev/null
+++ b/src/java/com/android/ims/ImsServiceClass.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ims;
+
+/**
+ * This class defines an identifier for each IMS service category.
+ *
+ * @hide
+ */
+public class ImsServiceClass {
+ /**
+ * Service classes
+ * It defines the service classes for IMS-based services.
+ */
+
+ /**
+ * MMTEL
+ * supports the IMS multimedia telephony communication service
+ * defined in 3GPP & GSMA IR.92, IR.94.
+ */
+ public static final int MMTEL = 1;
+
+ /**
+ * RCS
+ * supports the RCS service defined in GSMA RCS.
+ */
+ /** public static final int RCS = 2; */
+}
diff --git a/src/java/com/android/ims/ImsSsInfo.aidl b/src/java/com/android/ims/ImsSsInfo.aidl
new file mode 100644
index 0000000..0ac598b
--- /dev/null
+++ b/src/java/com/android/ims/ImsSsInfo.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ims;
+
+parcelable ImsSsInfo;
diff --git a/src/java/com/android/ims/ImsSsInfo.java b/src/java/com/android/ims/ImsSsInfo.java
new file mode 100644
index 0000000..dbde1c6
--- /dev/null
+++ b/src/java/com/android/ims/ImsSsInfo.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ims;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Provides the result to the update operation for the supplementary service configuration.
+ *
+ * @hide
+ */
+public class ImsSsInfo implements Parcelable {
+ /**
+ * 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;
+
+ // 0: disabled, 1: enabled
+ public int mStatus;
+
+ public ImsSsInfo() {
+ }
+
+ public ImsSsInfo(Parcel in) {
+ readFromParcel(in);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(mStatus);
+ }
+
+ @Override
+ public String toString() {
+ return super.toString() + ", Status: " + ((mStatus == 0) ? "disabled" : "enabled");
+ }
+
+ private void readFromParcel(Parcel in) {
+ mStatus = in.readInt();
+ }
+
+ public static final 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];
+ }
+ };
+}
diff --git a/src/java/com/android/ims/ImsStreamMediaProfile.aidl b/src/java/com/android/ims/ImsStreamMediaProfile.aidl
new file mode 100644
index 0000000..d648a35
--- /dev/null
+++ b/src/java/com/android/ims/ImsStreamMediaProfile.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ims;
+
+parcelable ImsStreamMediaProfile;
diff --git a/src/java/com/android/ims/ImsStreamMediaProfile.java b/src/java/com/android/ims/ImsStreamMediaProfile.java
new file mode 100644
index 0000000..003499c
--- /dev/null
+++ b/src/java/com/android/ims/ImsStreamMediaProfile.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ims;
+
+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
+ */
+public 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 << 0);
+ public static final int AUDIO_QUALITY_AMR_WB = (1 << 1);
+
+ /**
+ * 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);
+
+ // Audio related information
+ public int mAudioQuality;
+ public int mAudioDirection;
+ // Video related information
+ public int mVideoQuality;
+ public int mVideoDirection;
+
+
+
+ public ImsStreamMediaProfile(Parcel in) {
+ readFromParcel(in);
+ }
+
+ public ImsStreamMediaProfile() {
+ mAudioQuality = AUDIO_QUALITY_AMR_WB;
+ mAudioDirection = DIRECTION_SEND_RECEIVE;
+ mVideoQuality = VIDEO_QUALITY_NONE;
+ mVideoDirection = DIRECTION_INVALID;
+ }
+
+ public ImsStreamMediaProfile(int audioQuality, int audioDirection,
+ int videoQuality, int videoDirection) {
+ mAudioQuality = audioQuality;
+ mAudioDirection = audioDirection;
+ mVideoQuality = videoQuality;
+ mVideoDirection = videoDirection;
+ }
+
+ public void copyFrom(ImsStreamMediaProfile profile) {
+ mAudioQuality = profile.mAudioQuality;
+ mAudioDirection = profile.mAudioDirection;
+ mVideoQuality = profile.mVideoQuality;
+ mVideoDirection = profile.mVideoDirection;
+ }
+
+ @Override
+ public String toString() {
+ return "{ audioQuality=" + mAudioQuality +
+ ", audioDirection=" + mAudioDirection +
+ ", videoQuality=" + mVideoQuality +
+ ", videoDirection=" + mVideoDirection + " }";
+ }
+
+ @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);
+ }
+
+ private void readFromParcel(Parcel in) {
+ mAudioQuality = in.readInt();
+ mAudioDirection = in.readInt();
+ mVideoQuality = in.readInt();
+ mVideoDirection = in.readInt();
+ }
+
+ public static final 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];
+ }
+ };
+}
diff --git a/src/java/com/android/ims/ImsUt.java b/src/java/com/android/ims/ImsUt.java
new file mode 100644
index 0000000..c2f77d5
--- /dev/null
+++ b/src/java/com/android/ims/ImsUt.java
@@ -0,0 +1,433 @@
+/*
+ * Copyright (c) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ims;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import android.os.AsyncResult;
+import android.os.Bundle;
+import android.os.Message;
+import android.os.RemoteException;
+import android.telephony.Rlog;
+
+import com.android.ims.internal.IImsUt;
+import com.android.ims.internal.IImsUtListener;
+
+/**
+ * Provides APIs for the supplementary service settings using IMS (Ut interface).
+ * It is created from 3GPP TS 24.623 (XCAP(XML Configuration Access Protocol)
+ * over the Ut interface for manipulating supplementary services).
+ *
+ * @hide
+ */
+public class ImsUt implements ImsUtInterface {
+ /**
+ * Key string for an additional supplementary service configurations.
+ */
+ /**
+ * Actions : string format of ImsUtInterface#ACTION_xxx
+ * "0" (deactivation), "1" (activation), "2" (not_used),
+ * "3" (registration), "4" (erasure), "5" (Interrogation)
+ */
+ public static final String KEY_ACTION = "action";
+ /**
+ * Categories :
+ * "OIP", "OIR", "TIP", "TIR", "CDIV", "CB", "CW", "CONF",
+ * "ACR", "MCID", "ECT", "CCBS", "AOC", "MWI", "FA", "CAT"
+ *
+ * Detailed parameter name will be determined according to the properties
+ * of the supplementary service configuration.
+ */
+ public static final String KEY_CATEGORY = "category";
+ public static final String CATEGORY_OIP = "OIP";
+ public static final String CATEGORY_OIR = "OIR";
+ public static final String CATEGORY_TIP = "TIP";
+ public static final String CATEGORY_TIR = "TIR";
+ public static final String CATEGORY_CDIV = "CDIV";
+ public static final String CATEGORY_CB = "CB";
+ public static final String CATEGORY_CW = "CW";
+ public static final String CATEGORY_CONF = "CONF";
+
+ private static final String TAG = "ImsUt";
+ private static final boolean DBG = true;
+
+ // For synchronization of private variables
+ private Object mLockObj = new Object();
+ private final IImsUt miUt;
+ private HashMap<Integer, Message> mPendingCmds =
+ new HashMap<Integer, Message>();
+
+ public ImsUt(IImsUt iUt) {
+ miUt = iUt;
+
+ if (miUt != null) {
+ try {
+ miUt.setListener(new IImsUtListenerProxy());
+ } catch (RemoteException e) {
+ }
+ }
+ }
+
+ public void close() {
+ synchronized(mLockObj) {
+ if (miUt != null) {
+ try {
+ miUt.close();
+ } catch (RemoteException e) {
+ }
+ }
+
+ if (!mPendingCmds.isEmpty()) {
+ Map.Entry<Integer, Message>[] entries =
+ mPendingCmds.entrySet().toArray(new Map.Entry[mPendingCmds.size()]);
+
+ for (Map.Entry<Integer, Message> entry : entries) {
+ sendFailureReport(entry.getValue(),
+ ImsReasonInfo.CODE_UT_SERVICE_UNAVAILABLE);
+ }
+
+ mPendingCmds.clear();
+ }
+ }
+ }
+
+ /**
+ * Operations for the supplementary service configuration
+ */
+
+ /**
+ * Retrieves the configuration of the call barring.
+ *
+ * @param cbType type of call barring to be queried; ImsUtInterface#CB_XXX
+ * @param result message to pass the result of this operation
+ * The return value of ((AsyncResult)result.obj) is an array of {@link ImsSsInfo}.
+ */
+ @Override
+ public void queryCallBarring(int cbType, Message result) {
+ if (DBG) {
+ log("queryCallBarring :: Ut=" + miUt + ", cbType=" + cbType);
+ }
+
+ synchronized(mLockObj) {
+ try {
+ int id = miUt.queryCallBarring(cbType);
+
+ if (id < 0) {
+ id *= (-1);
+ sendFailureReport(result, id);
+ return;
+ }
+
+ mPendingCmds.put(Integer.valueOf(id), result);
+ } catch (RemoteException e) {
+ sendFailureReport(result, ImsReasonInfo.CODE_UT_SERVICE_UNAVAILABLE);
+ }
+ }
+ }
+
+ /**
+ * Retrieves the configuration of the call forward.
+ * The return value of ((AsyncResult)result.obj) is an array of {@link ImsCallForwardInfo}.
+ */
+ @Override
+ public void queryCallForward(int condition, String number, Message result) {
+ if (DBG) {
+ log("queryCallForward :: Ut=" + miUt + ", condition=" + condition
+ + ", number=" + number);
+ }
+
+ synchronized(mLockObj) {
+ try {
+ int id = miUt.queryCallForward(condition, number);
+
+ if (id < 0) {
+ id *= (-1);
+ sendFailureReport(result, id);
+ return;
+ }
+
+ mPendingCmds.put(Integer.valueOf(id), result);
+ } catch (RemoteException e) {
+ sendFailureReport(result, ImsReasonInfo.CODE_UT_SERVICE_UNAVAILABLE);
+ }
+ }
+ }
+
+ /**
+ * Retrieves the configuration of the call waiting.
+ * The return value of ((AsyncResult)result.obj) is an array of {@link ImsSsInfo}.
+ */
+ @Override
+ public void queryCallWaiting(Message result) {
+ if (DBG) {
+ log("queryCallWaiting :: Ut=" + miUt);
+ }
+
+ synchronized(mLockObj) {
+ try {
+ int id = miUt.queryCallWaiting();
+
+ if (id < 0) {
+ id *= (-1);
+ sendFailureReport(result, id);
+ return;
+ }
+
+ mPendingCmds.put(Integer.valueOf(id), result);
+ } catch (RemoteException e) {
+ sendFailureReport(result, ImsReasonInfo.CODE_UT_SERVICE_UNAVAILABLE);
+ }
+ }
+ }
+
+ /**
+ * Modifies the configuration of the call barring.
+ */
+ @Override
+ public void updateCallBarring(int cbType, boolean enable, Message result) {
+ if (DBG) {
+ log("updateCallBarring :: Ut=" + miUt + ", cbType=" + cbType
+ + ", enable=" + enable);
+ }
+
+ synchronized(mLockObj) {
+ try {
+ int id = miUt.updateCallBarring(cbType, enable);
+
+ if (id < 0) {
+ id *= (-1);
+ sendFailureReport(result, id);
+ return;
+ }
+
+ mPendingCmds.put(Integer.valueOf(id), result);
+ } catch (RemoteException e) {
+ sendFailureReport(result, ImsReasonInfo.CODE_UT_SERVICE_UNAVAILABLE);
+ }
+ }
+ }
+
+ /**
+ * Modifies the configuration of the call forward.
+ */
+ @Override
+ public void updateCallForward(int action, int condition, String number,
+ int timeSeconds, Message result) {
+ if (DBG) {
+ log("updateCallForward :: Ut=" + miUt + ", action=" + action
+ + ", condition=" + condition + ", number=" + number
+ + ", timeSeconds=" + timeSeconds);
+ }
+
+ synchronized(mLockObj) {
+ try {
+ int id = miUt.updateCallForward(action, condition, number, timeSeconds);
+
+ if (id < 0) {
+ id *= (-1);
+ sendFailureReport(result, id);
+ return;
+ }
+
+ mPendingCmds.put(Integer.valueOf(id), result);
+ } catch (RemoteException e) {
+ sendFailureReport(result, ImsReasonInfo.CODE_UT_SERVICE_UNAVAILABLE);
+ }
+ }
+ }
+
+ /**
+ * Modifies the configuration of the call waiting.
+ */
+ @Override
+ public void updateCallWaiting(boolean enable, Message result) {
+ if (DBG) {
+ log("updateCallWaiting :: Ut=" + miUt + ", enable=" + enable);
+ }
+
+ synchronized(mLockObj) {
+ try {
+ int id = miUt.updateCallWaiting(enable);
+
+ if (id < 0) {
+ id *= (-1);
+ sendFailureReport(result, id);
+ return;
+ }
+
+ mPendingCmds.put(Integer.valueOf(id), result);
+ } catch (RemoteException e) {
+ sendFailureReport(result, ImsReasonInfo.CODE_UT_SERVICE_UNAVAILABLE);
+ }
+ }
+ }
+
+ public void transact(Bundle ssInfo, Message result) {
+ if (DBG) {
+ log("transact :: Ut=" + miUt + ", ssInfo=" + ssInfo);
+ }
+
+ synchronized(mLockObj) {
+ try {
+ int id = miUt.transact(ssInfo);
+
+ if (id < 0) {
+ id *= (-1);
+ sendFailureReport(result, id);
+ return;
+ }
+
+ mPendingCmds.put(Integer.valueOf(id), result);
+ } catch (RemoteException e) {
+ sendFailureReport(result, ImsReasonInfo.CODE_UT_SERVICE_UNAVAILABLE);
+ }
+ }
+ }
+
+ private void sendFailureReport(Message result, int errorCode) {
+ if (result == null) {
+ return;
+ }
+
+ AsyncResult.forMessage(result, null, new ImsException("Ut Exception", errorCode));
+ result.sendToTarget();
+ }
+
+ private void sendSuccessReport(Message result) {
+ if (result == null) {
+ return;
+ }
+
+ AsyncResult.forMessage(result, null, null);
+ result.sendToTarget();
+ }
+
+ private void sendSuccessReport(Message result, Object ssInfo) {
+ if (result == null) {
+ return;
+ }
+
+ AsyncResult.forMessage(result, ssInfo, null);
+ result.sendToTarget();
+ }
+
+ private void log(String s) {
+ Rlog.d(TAG, s);
+ }
+
+ private void loge(String s) {
+ Rlog.e(TAG, s);
+ }
+
+ private void loge(String s, Throwable t) {
+ Rlog.e(TAG, s, t);
+ }
+
+ /**
+ * A listener type for the result of the supplementary service configuration.
+ */
+ private class IImsUtListenerProxy extends IImsUtListener.Stub {
+ /**
+ * Notifies the result of the supplementary service configuration udpate.
+ */
+ @Override
+ public void utConfigurationUpdated(IImsUt ut, int id) {
+ Integer key = Integer.valueOf(id);
+
+ synchronized(mLockObj) {
+ sendSuccessReport(mPendingCmds.get(key));
+ mPendingCmds.remove(key);
+ }
+ }
+
+ @Override
+ public void utConfigurationUpdateFailed(IImsUt ut, int id, int errorCode) {
+ Integer key = Integer.valueOf(id);
+
+ synchronized(mLockObj) {
+ sendFailureReport(mPendingCmds.get(key), errorCode);
+ mPendingCmds.remove(key);
+ }
+ }
+
+ /**
+ * Notifies the result of the supplementary service configuration query.
+ */
+ @Override
+ public void utConfigurationQueried(IImsUt ut, int id, Bundle ssInfo) {
+ Integer key = Integer.valueOf(id);
+
+ synchronized(mLockObj) {
+ sendSuccessReport(mPendingCmds.get(key), ssInfo);
+ mPendingCmds.remove(key);
+ }
+ }
+
+ @Override
+ public void utConfigurationQueryFailed(IImsUt ut, int id, int errorCode) {
+ Integer key = Integer.valueOf(id);
+
+ synchronized(mLockObj) {
+ sendFailureReport(mPendingCmds.get(key), errorCode);
+ mPendingCmds.remove(key);
+ }
+ }
+
+ /**
+ * Notifies the status of the call barring supplementary service.
+ */
+ @Override
+ public void utConfigurationCallBarringQueried(IImsUt ut,
+ int id, ImsSsInfo[] cbInfo) {
+ Integer key = Integer.valueOf(id);
+
+ synchronized(mLockObj) {
+ sendSuccessReport(mPendingCmds.get(key), cbInfo);
+ mPendingCmds.remove(key);
+ }
+ }
+
+ /**
+ * Notifies the status of the call forwarding supplementary service.
+ */
+ @Override
+ public void utConfigurationCallForwardQueried(IImsUt ut,
+ int id, ImsCallForwardInfo[] cfInfo) {
+ Integer key = Integer.valueOf(id);
+
+ synchronized(mLockObj) {
+ sendSuccessReport(mPendingCmds.get(key), cfInfo);
+ mPendingCmds.remove(key);
+ }
+ }
+
+ /**
+ * Notifies the status of the call waiting supplementary service.
+ */
+ @Override
+ public void utConfigurationCallWaitingQueried(IImsUt ut,
+ int id, ImsSsInfo[] cwInfo) {
+ Integer key = Integer.valueOf(id);
+
+ synchronized(mLockObj) {
+ sendSuccessReport(mPendingCmds.get(key), cwInfo);
+ mPendingCmds.remove(key);
+ }
+ }
+ }
+}
diff --git a/src/java/com/android/ims/ImsUtInterface.java b/src/java/com/android/ims/ImsUtInterface.java
new file mode 100644
index 0000000..d7695a0
--- /dev/null
+++ b/src/java/com/android/ims/ImsUtInterface.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ims;
+
+import android.os.Message;
+
+/**
+ * Provides APIs for the supplementary service settings using IMS (Ut interface).
+ * It is created from 3GPP TS 24.623 (XCAP(XML Configuration Access Protocol)
+ * over the Ut interface for manipulating supplementary services).
+ *
+ * @hide
+ */
+public interface ImsUtInterface {
+ /**
+ * Actions
+ * @hide
+ */
+ public static final int ACTION_DEACTIVATION = 0;
+ public static final int ACTION_ACTIVATION = 1;
+ public static final int ACTION_REGISTRATION = 3;
+ public static final int ACTION_ERASURE = 4;
+ public static final int ACTION_INTERROGATION = 5;
+
+ /**
+ * OIR (Originating Identification Restriction, 3GPP TS 24.607)
+ * OIP (Originating Identification Presentation, 3GPP TS 24.607)
+ * TIR (Terminating Identification Restriction, 3GPP TS 24.608)
+ * TIP (Terminating Identification Presentation, 3GPP TS 24.608)
+ */
+ public static final int OIR_DEFAULT = 0; // "user subscription default value"
+ public static final int OIR_PRESENTATION_RESTRICTED = 1;
+ public static final int OIR_PRESENTATION_NOT_RESTRICTED = 2;
+
+ /**
+ * CW (Communication Waiting, 3GPP TS 24.615)
+ */
+
+ /**
+ * CDIV (Communication Diversion, 3GPP TS 24.604)
+ * actions: target, no reply timer
+ */
+ public static final int CDIV_CF_UNCONDITIONAL = 0;
+ public static final int CDIV_CF_BUSY = 1;
+ public static final int CDIV_CF_NO_REPLY = 2;
+ public static final int CDIV_CF_NOT_REACHABLE = 3;
+ // For CS service code: 002
+ public static final int CDIV_CF_ALL = 4;
+ // For CS service code: 004
+ public static final int CDIV_CF_ALL_CONDITIONAL = 5;
+ // It's only supported in the IMS service (CS does not define it).
+ // IR.92 recommends that an UE activates both the CFNRc and the CFNL
+ // (CDIV using condition not-registered) to the same target.
+ public static final int CDIV_CF_NOT_LOGGED_IN = 6;
+
+ /**
+ * CB (Communication Barring, 3GPP TS 24.611)
+ */
+ // Barring of All Incoming Calls
+ public static final int CB_BAIC = 1;
+ // Barring of All Outgoing Calls
+ public static final int CB_BAOC = 2;
+ // Barring of Outgoing International Calls
+ public static final int CB_BOIC = 3;
+ // Barring of Outgoing International Calls - excluding Home Country
+ public static final int CB_BOIC_EXHC = 4;
+ // Barring of Incoming Calls - when roaming
+ public static final int CB_BIC_WR = 5;
+ // Barring of Anonymous Communication Rejection (ACR) - a particular case of ICB service
+ public static final int CB_BIC_ACR = 6;
+
+
+ /**
+ * Invalid result value.
+ */
+ public static final int INVALID = (-1);
+
+
+
+ /**
+ * Operations for the supplementary service configuration
+ */
+
+ /**
+ * Retrieves the configuration of the call barring.
+ * The return value of ((AsyncResult)result.obj) is an array of {@link ImsSsInfo}.
+ */
+ public void queryCallBarring(int cbType, Message result);
+
+ /**
+ * Retrieves the configuration of the call forward.
+ * The return value of ((AsyncResult)result.obj) is an array of {@link ImsCallForwardInfo}.
+ */
+ public void queryCallForward(int condition, String number, Message result);
+
+ /**
+ * Retrieves the configuration of the call waiting.
+ * The return value of ((AsyncResult)result.obj) is an array of {@link ImsSsInfo}.
+ */
+ public void queryCallWaiting(Message result);
+
+ /**
+ * Modifies the configuration of the call barring.
+ */
+ public void updateCallBarring(int cbType, boolean enable, Message result);
+
+ /**
+ * Modifies the configuration of the call forward.
+ */
+ public void updateCallForward(int action, int condition, String number,
+ int timeSeconds, Message result);
+
+ /**
+ * Modifies the configuration of the call waiting.
+ */
+ public void updateCallWaiting(boolean enable, Message result);
+}
diff --git a/src/java/com/android/ims/internal/CallGroup.java b/src/java/com/android/ims/internal/CallGroup.java
new file mode 100644
index 0000000..4cd15b5
--- /dev/null
+++ b/src/java/com/android/ims/internal/CallGroup.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ims.internal;
+
+import java.util.ArrayList;
+
+/**
+ * Wrapper class which has an ICallGroup interface.
+ *
+ * @hide
+ */
+public class CallGroup {
+ private final ICallGroup mCallGroup;
+
+ public CallGroup(ICallGroup callGroup) {
+ mCallGroup = callGroup;
+ }
+
+ public ICall getNeutralReferrer() {
+ if (mCallGroup == null) {
+ return null;
+ }
+
+ return mCallGroup.getNeutralReferrer();
+ }
+
+ public ICall getOwner() {
+ if (mCallGroup == null) {
+ return null;
+ }
+
+ return mCallGroup.getOwner();
+ }
+
+ public ICall getReferrer(String name) {
+ if (mCallGroup == null) {
+ return null;
+ }
+
+ return mCallGroup.getReferrer(name);
+ }
+
+ public ArrayList<ICall> getReferrers() {
+ if (mCallGroup == null) {
+ return null;
+ }
+
+ return mCallGroup.getReferrers();
+ }
+
+ public boolean hasReferrer() {
+ if (mCallGroup == null) {
+ return false;
+ }
+
+ return mCallGroup.hasReferrer();
+ }
+
+ public boolean isOwner(ICall call) {
+ if (mCallGroup == null) {
+ return false;
+ }
+
+ return mCallGroup.isOwner(call);
+ }
+
+ public boolean isReferrer(ICall call) {
+ if (mCallGroup == null) {
+ return false;
+ }
+
+ return mCallGroup.isReferrer(call);
+ }
+
+ public void addReferrer(ICall call) {
+ if (mCallGroup == null) {
+ return;
+ }
+
+ mCallGroup.addReferrer(call);
+ }
+
+ public void removeReferrer(ICall call) {
+ if (mCallGroup == null) {
+ return;
+ }
+
+ mCallGroup.removeReferrer(call);
+ }
+
+ public void setNeutralReferrer(ICall call) {
+ if (mCallGroup == null) {
+ return;
+ }
+
+ mCallGroup.setNeutralReferrer(call);
+ }
+
+ public void setOwner(ICall call) {
+ if (mCallGroup == null) {
+ return;
+ }
+
+ mCallGroup.setOwner(call);
+ }
+}
diff --git a/src/java/com/android/ims/internal/CallGroupManager.java b/src/java/com/android/ims/internal/CallGroupManager.java
new file mode 100644
index 0000000..36391de
--- /dev/null
+++ b/src/java/com/android/ims/internal/CallGroupManager.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ims.internal;
+
+import java.util.ArrayList;
+
+/**
+ * Manages CallGroup objects.
+ *
+ * @hide
+ */
+public class CallGroupManager {
+ private static CallGroupManager mCallGroupManager = new CallGroupManager();
+ private Object mLockObj = new Object();
+ private ArrayList<CallGroup> mCallGroups = new ArrayList<CallGroup>();
+
+ private CallGroupManager() {
+ }
+
+ public static CallGroupManager getInstance() {
+ return mCallGroupManager;
+ }
+
+ public CallGroup createCallGroup(ICallGroup callGroup) {
+ CallGroup cg = new CallGroup(callGroup);
+
+ synchronized(mLockObj) {
+ mCallGroups.add(cg);
+ }
+
+ return cg;
+ }
+
+ public void destroyCallGroup(CallGroup cg) {
+ if (cg == null) {
+ return;
+ }
+
+ synchronized(mLockObj) {
+ mCallGroups.remove(cg);
+ }
+ }
+
+ public CallGroup getCallGroupAsOwner(ICall call) {
+ synchronized(mLockObj) {
+ for (CallGroup cg : mCallGroups) {
+ if (cg.isOwner(call)) {
+ return cg;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ public CallGroup getCallGroupAsReferrer(ICall call) {
+ synchronized(mLockObj) {
+ for (CallGroup cg : mCallGroups) {
+ if (cg.isReferrer(call)) {
+ return cg;
+ }
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/src/java/com/android/ims/internal/ICall.java b/src/java/com/android/ims/internal/ICall.java
new file mode 100644
index 0000000..e38b6e6
--- /dev/null
+++ b/src/java/com/android/ims/internal/ICall.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ims.internal;
+
+/**
+ * IMS call interface
+ *
+ * @hide
+ */
+public interface ICall {
+ /**
+ * Closes this object. This object is not usable after being closed.
+ */
+ public void close();
+
+ /**
+ * Checks if the call has a same remote user identity or not.
+ *
+ * @param userId the remote user identity
+ * @return true if the remote user identity is equal; otherwise, false
+ */
+ public boolean checkIfRemoteUserIsSame(String userId);
+
+ /**
+ * Checks if the call is equal or not.
+ *
+ * @param call the call to be compared
+ * @return true if the call is equal; otherwise, false
+ */
+ public boolean equalsTo(ICall call);
+}
diff --git a/src/java/com/android/ims/internal/ICallGroup.java b/src/java/com/android/ims/internal/ICallGroup.java
new file mode 100644
index 0000000..9b65d61
--- /dev/null
+++ b/src/java/com/android/ims/internal/ICallGroup.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ims.internal;
+
+import java.util.ArrayList;
+
+/**
+ * Provides the interface to manage all calls which are established
+ * hereafter the initial 1-to-1 call is established.
+ * It's for providing the dummy calls which are disconnected with the IMS network after
+ * merged or extended to the conference.
+ *
+ * @hide
+ */
+public interface ICallGroup {
+ /**
+ * Gets the neutral referrer call object of this group.
+ *
+ * @return the neutral referrer call object
+ */
+ public ICall getNeutralReferrer();
+
+ /**
+ * Gets the owner call object of this group.
+ *
+ * @return the owner call object
+ */
+ public ICall getOwner();
+
+ /**
+ * Gets the referrer call object which is equal to the specified name.
+ *
+ * @return the referrer call object
+ */
+ public ICall getReferrer(String name);
+
+ /**
+ * Gets the referrer call objects of this group.
+ *
+ * @return the referrer call objects
+ */
+ public ArrayList<ICall> getReferrers();
+
+ /**
+ * Checks if the call group has a referrer.
+ *
+ * @return true if the call group has a referrer; false otherwise.
+ */
+ public boolean hasReferrer();
+
+ /**
+ * Checks if the specified call object is owner of this group.
+ *
+ * @param call the call object to be checked if it is an owner of this group
+ * @return true if the specified call object is an owner; false otherwise
+ */
+ public boolean isOwner(ICall call);
+
+ /**
+ * Checks if the specified call object is a referrer of this group.
+ *
+ * @param call the call object to be checked if it is a referrer of this group
+ * @return true if the specified call object is a referrer; false otherwise
+ */
+ public boolean isReferrer(ICall call);
+
+ /**
+ * Adds the call object to this call group.
+ *
+ * @param call the call object to be added to this group
+ */
+ public void addReferrer(ICall call);
+
+ /**
+ * Removes the call object from this call group.
+ *
+ * @param call the call object to be removed from this group
+ */
+ public void removeReferrer(ICall call);
+
+ /**
+ * Sets the referrer call object in the neutral state while the operation is in progress.
+ *
+ * @param call the call object to be added to this group if the operation is succeeded.
+ */
+ public void setNeutralReferrer(ICall call);
+
+ /**
+ * Sets the call object as the owner of this call group.
+ * If the owner call object is already present, this method overwrites the existing owner
+ * call object.
+ *
+ * @param call the call object to be added to this group as owner
+ */
+ public void setOwner(ICall call);
+}
diff --git a/src/java/com/android/ims/internal/IImsCallSession.aidl b/src/java/com/android/ims/internal/IImsCallSession.aidl
new file mode 100644
index 0000000..aa2204e
--- /dev/null
+++ b/src/java/com/android/ims/internal/IImsCallSession.aidl
@@ -0,0 +1,219 @@
+/*
+ * Copyright (c) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ims.internal;
+
+import com.android.ims.ImsCallProfile;
+import com.android.ims.ImsStreamMediaProfile;
+import com.android.ims.internal.IImsCallSessionListener;
+
+/**
+ * An IMS session that is associated with a SIP dialog which is established from/to
+ * INVITE request or a mid-call transaction to control the session.
+ * {@hide}
+ */
+interface IImsCallSession {
+ /**
+ * Closes the object. This object is not usable after being closed.
+ */
+ void close();
+
+ /**
+ * Gets the call ID of the session.
+ *
+ * @return the call ID
+ */
+ String getCallId();
+
+ /**
+ * Gets the call profile that this session is associated with
+ *
+ * @return the call profile that this session is associated with
+ */
+ ImsCallProfile getCallProfile();
+
+ /**
+ * Gets the local call profile that this session is associated with
+ *
+ * @return the local call profile that this session is associated with
+ */
+ ImsCallProfile getLocalCallProfile();
+
+ /**
+ * Gets the value associated with the specified property of this session.
+ *
+ * @return the string value associated with the specified property
+ */
+ String getProperty(String name);
+
+ /**
+ * Gets the session state. The value returned must be one of the states in
+ * {@link ImsCallSession#State}.
+ *
+ * @return the session state
+ */
+ int getState();
+
+ /**
+ * Checks if the session is in a call.
+ *
+ * @return true if the session is in a call
+ */
+ boolean isInCall();
+
+ /**
+ * Sets the listener to listen to the session events. A {@link IImsCallSession}
+ * 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
+ */
+ void setListener(in IImsCallSessionListener listener);
+
+ /**
+ * Mutes or unmutes the mic for the active call.
+ *
+ * @param muted true if the call is muted, false otherwise
+ */
+ void setMute(boolean muted);
+
+ /**
+ * 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
+ */
+ void start(String callee, in ImsCallProfile profile);
+
+ /**
+ * Initiates an IMS call with the specified participants 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
+ */
+ void startConference(in String[] participants, in 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 Listener#callSessionStarted
+ */
+ void accept(int callType, in ImsStreamMediaProfile profile);
+
+ /**
+ * Rejects an incoming call or session update.
+ *
+ * @param reason reason code to reject an incoming call
+ * @see Listener#callSessionStartFailed
+ */
+ void reject(int reason);
+
+ /**
+ * Terminates a call.
+ *
+ * @see Listener#callSessionTerminated
+ */
+ void terminate(int reason);
+
+ /**
+ * 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
+ */
+ void hold(in ImsStreamMediaProfile profile);
+
+ /**
+ * 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
+ */
+ void resume(in ImsStreamMediaProfile profile);
+
+ /**
+ * Merges the active & hold call. When it succeeds, {@link Listener#callSessionMerged}
+ * is called.
+ *
+ * @see Listener#callSessionMerged, Listener#callSessionMergeFailed
+ */
+ 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 Listener#callSessionUpdated, Listener#callSessionUpdateFailed
+ */
+ void update(int callType, in ImsStreamMediaProfile profile);
+
+ /**
+ * Extends this call to the conference call with the specified recipients.
+ *
+ * @participants participant list to be invited to the conference call after extending the call
+ * @see Listener#sessionConferenceExtened, Listener#sessionConferenceExtendFailed
+ */
+ void extendToConference(in String[] participants);
+
+ /**
+ * Requests the conference server to invite an additional participants to the conference.
+ *
+ * @participants participant list to be invited to the conference call
+ * @see Listener#sessionInviteParticipantsRequestDelivered,
+ * Listener#sessionInviteParticipantsRequestFailed
+ */
+ void inviteParticipants(in 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 Listener#sessionRemoveParticipantsRequestDelivered,
+ * Listener#sessionRemoveParticipantsRequestFailed
+ */
+ void removeParticipants(in 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 code the DTMF to send. Value 0 to 15 (inclusive) are valid inputs.
+ * @param duration the interval in milli-seconds between the DTMFs
+ */
+ void sendDtmf(int code, int duration);
+
+ /**
+ * Sends an USSD message.
+ *
+ * @param ussdMessage USSD message to send
+ */
+ void sendUssd(String ussdMessage);
+}
diff --git a/src/java/com/android/ims/internal/IImsCallSessionListener.aidl b/src/java/com/android/ims/internal/IImsCallSessionListener.aidl
new file mode 100644
index 0000000..f36cf39
--- /dev/null
+++ b/src/java/com/android/ims/internal/IImsCallSessionListener.aidl
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ims.internal;
+
+import com.android.ims.ImsStreamMediaProfile;
+import com.android.ims.ImsCallProfile;
+import com.android.ims.ImsReasonInfo;
+import com.android.ims.ImsConferenceState;
+import com.android.ims.internal.IImsCallSession;
+
+/**
+ * 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}.
+ * {@hide}
+ */
+interface IImsCallSessionListener {
+ /**
+ * Notifies the result of the basic session operation (setup / terminate).
+ */
+ void callSessionProgressing(in IImsCallSession session, in ImsStreamMediaProfile profile);
+ void callSessionStarted(in IImsCallSession session, in ImsCallProfile profile);
+ void callSessionStartFailed(in IImsCallSession session, in ImsReasonInfo reasonInfo);
+ void callSessionTerminated(in IImsCallSession session, in ImsReasonInfo reasonInfo);
+
+ /**
+ * Notifies the result of the call hold/resume operation.
+ */
+ void callSessionHeld(in IImsCallSession session, in ImsCallProfile profile);
+ void callSessionHoldFailed(in IImsCallSession session, in ImsReasonInfo reasonInfo);
+ void callSessionHoldReceived(in IImsCallSession session, in ImsCallProfile profile);
+ void callSessionResumed(in IImsCallSession session, in ImsCallProfile profile);
+ void callSessionResumeFailed(in IImsCallSession session, in ImsReasonInfo reasonInfo);
+ void callSessionResumeReceived(in IImsCallSession session, in ImsCallProfile profile);
+
+ /**
+ * Notifiies the result of call merge operation.
+ */
+ void callSessionMerged(in IImsCallSession session,
+ in IImsCallSession newSession, in ImsCallProfile profile);
+ void callSessionMergeFailed(in IImsCallSession session,
+ in ImsReasonInfo reasonInfo);
+
+ /**
+ * Notifies the result of call upgrade / downgrade or any other call updates.
+ */
+ void callSessionUpdated(in IImsCallSession session,
+ in ImsCallProfile profile);
+ void callSessionUpdateFailed(in IImsCallSession session,
+ in ImsReasonInfo reasonInfo);
+ void callSessionUpdateReceived(in IImsCallSession session,
+ in ImsCallProfile profile);
+
+ /**
+ * Notifies the result of conference extension.
+ */
+ void callSessionConferenceExtended(in IImsCallSession session,
+ in IImsCallSession newSession, in ImsCallProfile profile);
+ void callSessionConferenceExtendFailed(in IImsCallSession session,
+ in ImsReasonInfo reasonInfo);
+ void callSessionConferenceExtendReceived(in IImsCallSession session,
+ in IImsCallSession newSession, in ImsCallProfile profile);
+
+ /**
+ * Notifies the result of the participant invitation / removal to/from the conference session.
+ */
+ void callSessionInviteParticipantsRequestDelivered(in IImsCallSession session);
+ void callSessionInviteParticipantsRequestFailed(in IImsCallSession session,
+ in ImsReasonInfo reasonInfo);
+ void callSessionRemoveParticipantsRequestDelivered(in IImsCallSession session);
+ void callSessionRemoveParticipantsRequestFailed(in IImsCallSession session,
+ in ImsReasonInfo reasonInfo);
+
+ /**
+ * Notifies the changes of the conference info. in the conference session.
+ */
+ void callSessionConferenceStateUpdated(in IImsCallSession session,
+ in ImsConferenceState state);
+
+ /**
+ * Notifies the incoming USSD message.
+ */
+ void callSessionUssdMessageReceived(in IImsCallSession session,
+ int mode, String ussdMessage);
+}
diff --git a/src/java/com/android/ims/internal/IImsRegistrationListener.aidl b/src/java/com/android/ims/internal/IImsRegistrationListener.aidl
new file mode 100644
index 0000000..5f243a0
--- /dev/null
+++ b/src/java/com/android/ims/internal/IImsRegistrationListener.aidl
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ims.internal;
+
+/**
+ * A listener type for receiving notifications about the changes to
+ * the IMS connection(registration).
+ *
+ * {@hide}
+ */
+interface IImsRegistrationListener {
+ /**
+ * Notifies the application when the device is connected to the IMS network.
+ */
+ void registrationConnected();
+
+ /**
+ * Notifies the application when the device is disconnected from the IMS network.
+ */
+ void registrationDisconnected();
+
+ /**
+ * Notifies the application when its suspended IMS connection is resumed,
+ * meaning the connection now allows throughput.
+ */
+ void registrationResumed();
+
+ /**
+ * Notifies the application when its current IMS connection is suspended,
+ * meaning there is no data throughput.
+ */
+ void registrationSuspended();
+
+ /**
+ * Notifies the application when its current IMS connection is updated
+ * since the service setting is changed or the service is added/removed.
+ *
+ * @param serviceClass a service class specified in {@link ImsServiceClass}
+ * @param event an event type when this callback is called
+ * If {@code event} is 0, meaning the specified service is removed from the IMS connection.
+ * Else ({@code event} is 1), meaning the specified service is added to the IMS connection.
+ */
+ void registrationServiceCapabilityChanged(int serviceClass, int event);
+}
diff --git a/src/java/com/android/ims/internal/IImsService.aidl b/src/java/com/android/ims/internal/IImsService.aidl
new file mode 100644
index 0000000..bac5651
--- /dev/null
+++ b/src/java/com/android/ims/internal/IImsService.aidl
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ims.internal;
+
+import android.app.PendingIntent;
+
+import com.android.ims.ImsCallProfile;
+import com.android.ims.internal.IImsRegistrationListener;
+import com.android.ims.internal.IImsCallSession;
+import com.android.ims.internal.IImsCallSessionListener;
+import com.android.ims.internal.IImsUt;
+
+/**
+ * {@hide}
+ */
+interface IImsService {
+ int open(int serviceClass, in PendingIntent incomingCallIntent,
+ in IImsRegistrationListener listener);
+ void close(int serviceId);
+ boolean isConnected(int serviceId, int serviceType, int callType);
+ boolean isOpened(int serviceId);
+ void setRegistrationListener(int serviceId, in IImsRegistrationListener listener);
+
+ ImsCallProfile createCallProfile(int serviceId, int serviceType, int callType);
+
+ IImsCallSession createCallSession(int serviceId, in ImsCallProfile profile,
+ in IImsCallSessionListener listener);
+ IImsCallSession getPendingCallSession(int serviceId, String callId);
+
+ /**
+ * Ut interface for the supplementary service configuration.
+ */
+ IImsUt getUtInterface(int serviceId);
+}
diff --git a/src/java/com/android/ims/internal/IImsStreamMediaSession.aidl b/src/java/com/android/ims/internal/IImsStreamMediaSession.aidl
new file mode 100644
index 0000000..459c685
--- /dev/null
+++ b/src/java/com/android/ims/internal/IImsStreamMediaSession.aidl
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ims.internal;
+
+/**
+ *
+ * {@hide}
+ */
+interface IImsStreamMediaSession {
+ void close();
+}
diff --git a/src/java/com/android/ims/internal/IImsUt.aidl b/src/java/com/android/ims/internal/IImsUt.aidl
new file mode 100644
index 0000000..32929ed
--- /dev/null
+++ b/src/java/com/android/ims/internal/IImsUt.aidl
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ims.internal;
+
+import android.os.Bundle;
+
+import com.android.ims.internal.IImsUtListener;
+
+/**
+ * Provides the Ut interface interworking to get/set the supplementary service configuration.
+ *
+ * {@hide}
+ */
+interface IImsUt {
+ /**
+ * Closes the object. This object is not usable after being closed.
+ */
+ void close();
+
+ /**
+ * Retrieves the configuration of the call barring.
+ */
+ int queryCallBarring(int cbType);
+
+ /**
+ * Retrieves the configuration of the call forward.
+ */
+ int queryCallForward(int condition, String number);
+
+ /**
+ * Retrieves the configuration of the call waiting.
+ */
+ int queryCallWaiting();
+
+ /**
+ * Updates or retrieves the supplementary service configuration.
+ */
+ int transact(in Bundle ssInfo);
+
+ /**
+ * Updates the configuration of the call barring.
+ */
+ int updateCallBarring(int cbType, boolean enable);
+
+ /**
+ * Updates the configuration of the call forward.
+ */
+ int updateCallForward(int action, int condition, String number, int timeSeconds);
+
+ /**
+ * Updates the configuration of the call waiting.
+ */
+ int updateCallWaiting(boolean enable);
+
+ /**
+ * Sets the listener.
+ */
+ void setListener(in IImsUtListener listener);
+}
diff --git a/src/java/com/android/ims/internal/IImsUtListener.aidl b/src/java/com/android/ims/internal/IImsUtListener.aidl
new file mode 100644
index 0000000..3f1b5a7
--- /dev/null
+++ b/src/java/com/android/ims/internal/IImsUtListener.aidl
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ims.internal;
+
+import android.os.Bundle;
+
+import com.android.ims.ImsCallForwardInfo;
+import com.android.ims.ImsSsInfo;
+import com.android.ims.internal.IImsUt;
+
+/**
+ * {@hide}
+ */
+interface IImsUtListener {
+ /**
+ * Notifies the result of the supplementary service configuration udpate.
+ */
+ void utConfigurationUpdated(in IImsUt ut, int id);
+ void utConfigurationUpdateFailed(in IImsUt ut, int id, int errorCode);
+
+ /**
+ * Notifies the result of the supplementary service configuration query.
+ */
+ void utConfigurationQueried(in IImsUt ut, int id, in Bundle ssInfo);
+ void utConfigurationQueryFailed(in IImsUt ut, int id, int errorCode);
+
+ /**
+ * Notifies the status of the call barring supplementary service.
+ */
+ void utConfigurationCallBarringQueried(in IImsUt ut,
+ int id, in ImsSsInfo[] cbInfo);
+
+ /**
+ * Notifies the status of the call forwarding supplementary service.
+ */
+ void utConfigurationCallForwardQueried(in IImsUt ut,
+ int id, in ImsCallForwardInfo[] cfInfo);
+
+ /**
+ * Notifies the status of the call waiting supplementary service.
+ */
+ void utConfigurationCallWaitingQueried(in IImsUt ut,
+ int id, in ImsSsInfo[] cwInfo);
+}
diff --git a/src/java/com/android/ims/internal/ImsCallSession.java b/src/java/com/android/ims/internal/ImsCallSession.java
new file mode 100644
index 0000000..208875c
--- /dev/null
+++ b/src/java/com/android/ims/internal/ImsCallSession.java
@@ -0,0 +1,988 @@
+/*
+ * Copyright (c) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ims.internal;
+
+import android.os.RemoteException;
+import android.telephony.Rlog;
+
+import com.android.ims.ImsCallProfile;
+import com.android.ims.ImsConferenceState;
+import com.android.ims.ImsReasonInfo;
+import com.android.ims.ImsStreamMediaProfile;
+
+/**
+ * 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.
+ */
+ 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>
+ */
+ 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 is done.
+ *
+ * @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 callSessionMerged(ImsCallSession session,
+ ImsCallSession newSession, ImsCallProfile profile) {
+ }
+
+ /**
+ * Called when the session merge is 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 call the call object that carries out the IMS call
+ */
+ 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
+ }
+ }
+
+ private final IImsCallSession miSession;
+ private boolean mClosed = false;
+ private Listener mListener;
+
+ public ImsCallSession(IImsCallSession iSession) {
+ miSession = iSession;
+
+ if (iSession != null) {
+ try {
+ iSession.setListener(new IImsCallSessionListenerProxy());
+ } catch (RemoteException e) {
+ }
+ } else {
+ mClosed = true;
+ }
+ }
+
+ public ImsCallSession(IImsCallSession iSession, Listener listener) {
+ this(iSession);
+ setListener(listener);
+ }
+
+ /**
+ * Closes this object. This object is not usable after being closed.
+ */
+ public synchronized void close() {
+ 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 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;
+ }
+ }
+
+ /**
+ * 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
+ */
+ 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) {
+ }
+ }
+
+ /**
+ * 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) {
+ }
+ }
+
+ /**
+ * 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#callSessionMerged} is called.
+ *
+ * @see Listener#callSessionMerged, 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.
+ *
+ * @participants participant list to be invited to the conference call after extending the call
+ * @see Listener#sessionConferenceExtened, Listener#sessionConferenceExtendFailed
+ */
+ 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.
+ *
+ * @participants participant list to be invited to the conference call
+ * @see Listener#sessionInviteParticipantsRequestDelivered,
+ * Listener#sessionInviteParticipantsRequestFailed
+ */
+ 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#sessionRemoveParticipantsRequestDelivered,
+ * Listener#sessionRemoveParticipantsRequestFailed
+ */
+ 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 code the DTMF to send. Value 0 to 15 (inclusive) are valid inputs.
+ */
+ public void sendDtmf(int code) {
+ if (mClosed) {
+ return;
+ }
+
+ try {
+ miSession.sendDtmf(code, 200);
+ } 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) {
+ }
+ }
+
+ /**
+ * 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(IImsCallSession session,
+ ImsStreamMediaProfile profile) {
+ if (mListener != null) {
+ mListener.callSessionProgressing(ImsCallSession.this, profile);
+ }
+ }
+
+ @Override
+ public void callSessionStarted(IImsCallSession session,
+ ImsCallProfile profile) {
+ if (mListener != null) {
+ mListener.callSessionStarted(ImsCallSession.this, profile);
+ }
+ }
+
+ @Override
+ public void callSessionStartFailed(IImsCallSession session,
+ ImsReasonInfo reasonInfo) {
+ if (mListener != null) {
+ mListener.callSessionStartFailed(ImsCallSession.this, reasonInfo);
+ }
+ }
+
+ @Override
+ public void callSessionTerminated(IImsCallSession session,
+ ImsReasonInfo reasonInfo) {
+ if (mListener != null) {
+ mListener.callSessionTerminated(ImsCallSession.this, reasonInfo);
+ }
+ }
+
+ /**
+ * Notifies the result of the call hold/resume operation.
+ */
+ @Override
+ public void callSessionHeld(IImsCallSession session,
+ ImsCallProfile profile) {
+ if (mListener != null) {
+ mListener.callSessionHeld(ImsCallSession.this, profile);
+ }
+ }
+
+ @Override
+ public void callSessionHoldFailed(IImsCallSession session,
+ ImsReasonInfo reasonInfo) {
+ if (mListener != null) {
+ mListener.callSessionHoldFailed(ImsCallSession.this, reasonInfo);
+ }
+ }
+
+ @Override
+ public void callSessionHoldReceived(IImsCallSession session,
+ ImsCallProfile profile) {
+ if (mListener != null) {
+ mListener.callSessionHoldReceived(ImsCallSession.this, profile);
+ }
+ }
+
+ @Override
+ public void callSessionResumed(IImsCallSession session,
+ ImsCallProfile profile) {
+ if (mListener != null) {
+ mListener.callSessionResumed(ImsCallSession.this, profile);
+ }
+ }
+
+ @Override
+ public void callSessionResumeFailed(IImsCallSession session,
+ ImsReasonInfo reasonInfo) {
+ if (mListener != null) {
+ mListener.callSessionResumeFailed(ImsCallSession.this, reasonInfo);
+ }
+ }
+
+ @Override
+ public void callSessionResumeReceived(IImsCallSession session,
+ ImsCallProfile profile) {
+ if (mListener != null) {
+ mListener.callSessionResumeReceived(ImsCallSession.this, profile);
+ }
+ }
+
+ /**
+ * Notifiies the result of call merge operation.
+ */
+ @Override
+ public void callSessionMerged(IImsCallSession session,
+ IImsCallSession newSession, ImsCallProfile profile) {
+ if (mListener != null) {
+ mListener.callSessionMerged(ImsCallSession.this,
+ new ImsCallSession(newSession), profile);
+ }
+ }
+
+ @Override
+ public void callSessionMergeFailed(IImsCallSession session,
+ 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(IImsCallSession session,
+ ImsCallProfile profile) {
+ if (mListener != null) {
+ mListener.callSessionUpdated(ImsCallSession.this, profile);
+ }
+ }
+
+ @Override
+ public void callSessionUpdateFailed(IImsCallSession session,
+ ImsReasonInfo reasonInfo) {
+ if (mListener != null) {
+ mListener.callSessionUpdateFailed(ImsCallSession.this, reasonInfo);
+ }
+ }
+
+ @Override
+ public void callSessionUpdateReceived(IImsCallSession session,
+ ImsCallProfile profile) {
+ if (mListener != null) {
+ mListener.callSessionUpdateReceived(ImsCallSession.this, profile);
+ }
+ }
+
+ /**
+ * Notifies the result of conference extension.
+ */
+ @Override
+ public void callSessionConferenceExtended(IImsCallSession session,
+ IImsCallSession newSession, ImsCallProfile profile) {
+ if (mListener != null) {
+ mListener.callSessionConferenceExtended(ImsCallSession.this,
+ new ImsCallSession(newSession), profile);
+ }
+ }
+
+ @Override
+ public void callSessionConferenceExtendFailed(IImsCallSession session,
+ ImsReasonInfo reasonInfo) {
+ if (mListener != null) {
+ mListener.callSessionConferenceExtendFailed(ImsCallSession.this, reasonInfo);
+ }
+ }
+
+ @Override
+ public void callSessionConferenceExtendReceived(IImsCallSession session,
+ 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(IImsCallSession session) {
+ if (mListener != null) {
+ mListener.callSessionInviteParticipantsRequestDelivered(ImsCallSession.this);
+ }
+ }
+
+ @Override
+ public void callSessionInviteParticipantsRequestFailed(IImsCallSession session,
+ ImsReasonInfo reasonInfo) {
+ if (mListener != null) {
+ mListener.callSessionInviteParticipantsRequestFailed(ImsCallSession.this,
+ reasonInfo);
+ }
+ }
+
+ @Override
+ public void callSessionRemoveParticipantsRequestDelivered(IImsCallSession session) {
+ if (mListener != null) {
+ mListener.callSessionRemoveParticipantsRequestDelivered(ImsCallSession.this);
+ }
+ }
+
+ @Override
+ public void callSessionRemoveParticipantsRequestFailed(IImsCallSession session,
+ 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(IImsCallSession session,
+ ImsConferenceState state) {
+ if (mListener != null) {
+ mListener.callSessionConferenceStateUpdated(ImsCallSession.this, state);
+ }
+ }
+
+ /**
+ * Notifies the incoming USSD message.
+ */
+ @Override
+ public void callSessionUssdMessageReceived(IImsCallSession session,
+ int mode, String ussdMessage) {
+ if (mListener != null) {
+ mListener.callSessionUssdMessageReceived(ImsCallSession.this, mode, ussdMessage);
+ }
+ }
+ }
+}
diff --git a/src/java/com/android/ims/internal/ImsStreamMediaSession.java b/src/java/com/android/ims/internal/ImsStreamMediaSession.java
new file mode 100644
index 0000000..5814891
--- /dev/null
+++ b/src/java/com/android/ims/internal/ImsStreamMediaSession.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ims.internal;
+
+
+
+/**
+ * Provides the APIs to control the media session, such as passing the surface object,
+ * controlling the camera (front/rear selection, zoom, brightness, ...) for a video calling.
+ *
+ * @hide
+ */
+public class ImsStreamMediaSession {
+ private static final String TAG = "ImsStreamMediaSession";
+
+ /**
+ * Listener for events relating to an IMS media session.
+ * <p>Many of these events are also received by {@link ImsStreamMediaSession.Listener}.</p>
+ */
+ public static class Listener {
+ }
+
+ private Listener mListener;
+
+ ImsStreamMediaSession(IImsStreamMediaSession mediaSession) {
+ }
+
+ ImsStreamMediaSession(IImsStreamMediaSession mediaSession, Listener listener) {
+ this(mediaSession);
+ setListener(listener);
+ }
+
+ /**
+ * Sets the listener to listen to the media session events. A {@code ImsStreamMediaSession}
+ * can only hold one listener at a time. Subsequent calls to this method
+ * override the previous listener.
+ *
+ * @param listener to listen to the media session events of this object
+ */
+ public void setListener(Listener listener) {
+ mListener = listener;
+ }
+}