blob: 746246c64e8cc40de52657b213a51d7f92144f10 [file] [log] [blame]
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/
package android.telephony.ims.feature;
import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.os.Binder;
import android.os.Bundle;
import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
import android.telecom.TelecomManager;
import android.telephony.AccessNetworkConstants;
import android.telephony.ims.ImsCallProfile;
import android.telephony.ims.ImsCallSession;
import android.telephony.ims.ImsCallSessionListener;
import android.telephony.ims.ImsException;
import android.telephony.ims.ImsReasonInfo;
import android.telephony.ims.ImsService;
import android.telephony.ims.MediaQualityStatus;
import android.telephony.ims.MediaThreshold;
import android.telephony.ims.RtpHeaderExtensionType;
import android.telephony.ims.SrvccCall;
import android.telephony.ims.aidl.IImsCallSessionListener;
import android.telephony.ims.aidl.IImsCapabilityCallback;
import android.telephony.ims.aidl.IImsMmTelFeature;
import android.telephony.ims.aidl.IImsMmTelListener;
import android.telephony.ims.aidl.IImsSmsListener;
import android.telephony.ims.aidl.IImsTrafficSessionCallback;
import android.telephony.ims.aidl.ISrvccStartedCallback;
import android.telephony.ims.stub.ImsCallSessionImplBase;
import android.telephony.ims.stub.ImsEcbmImplBase;
import android.telephony.ims.stub.ImsMultiEndpointImplBase;
import android.telephony.ims.stub.ImsRegistrationImplBase;
import android.telephony.ims.stub.ImsSmsImplBase;
import android.telephony.ims.stub.ImsUtImplBase;
import android.util.ArraySet;
import android.util.Log;
import com.android.ims.internal.IImsCallSession;
import com.android.ims.internal.IImsEcbm;
import com.android.ims.internal.IImsMultiEndpoint;
import com.android.ims.internal.IImsUt;
import com.android.internal.telephony.util.TelephonyUtils;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CancellationException;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
import java.util.function.Supplier;
/**
* Base implementation for Voice and SMS (IR-92) and Video (IR-94) IMS support.
*
* Any class wishing to use MmTelFeature should extend this class and implement all methods that the
* service supports.
*/
public class MmTelFeature extends ImsFeature {
private static final String LOG_TAG = "MmTelFeature";
private Executor mExecutor;
private ImsSmsImplBase mSmsImpl;
private HashMap<ImsTrafficSessionCallback, ImsTrafficSessionCallbackWrapper> mTrafficCallbacks =
new HashMap<>();
/**
* Creates a new MmTelFeature using the Executor set in {@link ImsService#getExecutor}
* @hide
*/
@SystemApi
public MmTelFeature() {
}
/**
* Create a new MmTelFeature using the Executor specified for methods being called by the
* framework.
* @param executor The executor for the framework to use when executing the methods overridden
* by the implementation of MmTelFeature.
* @hide
*/
@SystemApi
public MmTelFeature(@NonNull Executor executor) {
super();
mExecutor = executor;
}
private final IImsMmTelFeature mImsMMTelBinder = new IImsMmTelFeature.Stub() {
@Override
public void setListener(IImsMmTelListener l) {
executeMethodAsyncNoException(() -> MmTelFeature.this.setListener(l), "setListener");
}
@Override
public int getFeatureState() throws RemoteException {
return executeMethodAsyncForResult(() -> MmTelFeature.this.getFeatureState(),
"getFeatureState");
}
@Override
public ImsCallProfile createCallProfile(int callSessionType, int callType)
throws RemoteException {
return executeMethodAsyncForResult(() -> MmTelFeature.this.createCallProfile(
callSessionType, callType), "createCallProfile");
}
@Override
public void changeOfferedRtpHeaderExtensionTypes(List<RtpHeaderExtensionType> types)
throws RemoteException {
executeMethodAsync(() -> MmTelFeature.this.changeOfferedRtpHeaderExtensionTypes(
new ArraySet<>(types)), "changeOfferedRtpHeaderExtensionTypes");
}
@Override
public IImsCallSession createCallSession(ImsCallProfile profile) throws RemoteException {
AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
IImsCallSession result = executeMethodAsyncForResult(() -> {
try {
return createCallSessionInterface(profile);
} catch (RemoteException e) {
exceptionRef.set(e);
return null;
}
}, "createCallSession");
if (exceptionRef.get() != null) {
throw exceptionRef.get();
}
return result;
}
@Override
public int shouldProcessCall(String[] numbers) {
Integer result = executeMethodAsyncForResultNoException(() ->
MmTelFeature.this.shouldProcessCall(numbers), "shouldProcessCall");
if (result != null) {
return result.intValue();
} else {
return PROCESS_CALL_CSFB;
}
}
@Override
public IImsUt getUtInterface() throws RemoteException {
AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
IImsUt result = executeMethodAsyncForResult(() -> {
try {
return MmTelFeature.this.getUtInterface();
} catch (RemoteException e) {
exceptionRef.set(e);
return null;
}
}, "getUtInterface");
if (exceptionRef.get() != null) {
throw exceptionRef.get();
}
return result;
}
@Override
public IImsEcbm getEcbmInterface() throws RemoteException {
AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
IImsEcbm result = executeMethodAsyncForResult(() -> {
try {
return MmTelFeature.this.getEcbmInterface();
} catch (RemoteException e) {
exceptionRef.set(e);
return null;
}
}, "getEcbmInterface");
if (exceptionRef.get() != null) {
throw exceptionRef.get();
}
return result;
}
@Override
public void setUiTtyMode(int uiTtyMode, Message onCompleteMessage) throws RemoteException {
executeMethodAsync(() -> MmTelFeature.this.setUiTtyMode(uiTtyMode, onCompleteMessage),
"setUiTtyMode");
}
@Override
public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException {
AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
IImsMultiEndpoint result = executeMethodAsyncForResult(() -> {
try {
return MmTelFeature.this.getMultiEndpointInterface();
} catch (RemoteException e) {
exceptionRef.set(e);
return null;
}
}, "getMultiEndpointInterface");
if (exceptionRef.get() != null) {
throw exceptionRef.get();
}
return result;
}
@Override
public int queryCapabilityStatus() {
Integer result = executeMethodAsyncForResultNoException(() -> MmTelFeature.this
.queryCapabilityStatus().mCapabilities, "queryCapabilityStatus");
if (result != null) {
return result.intValue();
} else {
return 0;
}
}
@Override
public void addCapabilityCallback(IImsCapabilityCallback c) {
executeMethodAsyncNoException(() -> MmTelFeature.this
.addCapabilityCallback(c), "addCapabilityCallback");
}
@Override
public void removeCapabilityCallback(IImsCapabilityCallback c) {
executeMethodAsyncNoException(() -> MmTelFeature.this
.removeCapabilityCallback(c), "removeCapabilityCallback");
}
@Override
public void changeCapabilitiesConfiguration(CapabilityChangeRequest request,
IImsCapabilityCallback c) {
executeMethodAsyncNoException(() -> MmTelFeature.this
.requestChangeEnabledCapabilities(request, c),
"changeCapabilitiesConfiguration");
}
@Override
public void queryCapabilityConfiguration(int capability, int radioTech,
IImsCapabilityCallback c) {
executeMethodAsyncNoException(() -> queryCapabilityConfigurationInternal(
capability, radioTech, c), "queryCapabilityConfiguration");
}
@Override
public void setMediaQualityThreshold(@MediaQualityStatus.MediaSessionType int sessionType,
MediaThreshold mediaThreshold) {
if (mediaThreshold != null) {
executeMethodAsyncNoException(() -> setMediaThreshold(sessionType, mediaThreshold),
"setMediaQualityThreshold");
} else {
executeMethodAsyncNoException(() -> clearMediaThreshold(sessionType),
"clearMediaQualityThreshold");
}
}
@Override
public MediaQualityStatus queryMediaQualityStatus(
@MediaQualityStatus.MediaSessionType int sessionType)
throws RemoteException {
return executeMethodAsyncForResult(() -> MmTelFeature.this.queryMediaQualityStatus(
sessionType), "queryMediaQualityStatus");
}
@Override
public void setSmsListener(IImsSmsListener l) {
executeMethodAsyncNoException(() -> MmTelFeature.this.setSmsListener(l),
"setSmsListener", getImsSmsImpl().getExecutor());
}
@Override
public void sendSms(int token, int messageRef, String format, String smsc, boolean retry,
byte[] pdu) {
executeMethodAsyncNoException(() -> MmTelFeature.this
.sendSms(token, messageRef, format, smsc, retry, pdu), "sendSms",
getImsSmsImpl().getExecutor());
}
@Override
public void onMemoryAvailable(int token) {
executeMethodAsyncNoException(() -> MmTelFeature.this
.onMemoryAvailable(token), "onMemoryAvailable", getImsSmsImpl().getExecutor());
}
@Override
public void acknowledgeSms(int token, int messageRef, int result) {
executeMethodAsyncNoException(() -> MmTelFeature.this
.acknowledgeSms(token, messageRef, result), "acknowledgeSms",
getImsSmsImpl().getExecutor());
}
@Override
public void acknowledgeSmsWithPdu(int token, int messageRef, int result, byte[] pdu) {
executeMethodAsyncNoException(() -> MmTelFeature.this
.acknowledgeSms(token, messageRef, result, pdu), "acknowledgeSms",
getImsSmsImpl().getExecutor());
}
@Override
public void acknowledgeSmsReport(int token, int messageRef, int result) {
executeMethodAsyncNoException(() -> MmTelFeature.this
.acknowledgeSmsReport(token, messageRef, result), "acknowledgeSmsReport",
getImsSmsImpl().getExecutor());
}
@Override
public String getSmsFormat() {
return executeMethodAsyncForResultNoException(() -> MmTelFeature.this
.getSmsFormat(), "getSmsFormat", getImsSmsImpl().getExecutor());
}
@Override
public void onSmsReady() {
executeMethodAsyncNoException(() -> MmTelFeature.this.onSmsReady(),
"onSmsReady", getImsSmsImpl().getExecutor());
}
@Override
public void notifySrvccStarted(final ISrvccStartedCallback cb) {
executeMethodAsyncNoException(
() -> MmTelFeature.this.notifySrvccStarted(
(profiles) -> {
try {
cb.onSrvccCallNotified(profiles);
} catch (Exception e) {
Log.e(LOG_TAG, "onSrvccCallNotified e=" + e);
}
}),
"notifySrvccStarted");
}
@Override
public void notifySrvccCompleted() {
executeMethodAsyncNoException(
() -> MmTelFeature.this.notifySrvccCompleted(), "notifySrvccCompleted");
}
@Override
public void notifySrvccFailed() {
executeMethodAsyncNoException(
() -> MmTelFeature.this.notifySrvccFailed(), "notifySrvccFailed");
}
@Override
public void notifySrvccCanceled() {
executeMethodAsyncNoException(
() -> MmTelFeature.this.notifySrvccCanceled(), "notifySrvccCanceled");
}
@Override
public void setTerminalBasedCallWaitingStatus(boolean enabled) throws RemoteException {
synchronized (mLock) {
try {
MmTelFeature.this.setTerminalBasedCallWaitingStatus(enabled);
} catch (ServiceSpecificException se) {
throw new ServiceSpecificException(se.errorCode, se.getMessage());
} catch (Exception e) {
throw new RemoteException(e.getMessage());
}
}
}
// Call the methods with a clean calling identity on the executor and wait indefinitely for
// the future to return.
private void executeMethodAsync(Runnable r, String errorLogName) throws RemoteException {
try {
CompletableFuture.runAsync(
() -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor).join();
} catch (CancellationException | CompletionException e) {
Log.w(LOG_TAG, "MmTelFeature Binder - " + errorLogName + " exception: "
+ e.getMessage());
throw new RemoteException(e.getMessage());
}
}
private void executeMethodAsyncNoException(Runnable r, String errorLogName) {
try {
CompletableFuture.runAsync(
() -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor).join();
} catch (CancellationException | CompletionException e) {
Log.w(LOG_TAG, "MmTelFeature Binder - " + errorLogName + " exception: "
+ e.getMessage());
}
}
private void executeMethodAsyncNoException(Runnable r, String errorLogName,
Executor executor) {
try {
CompletableFuture.runAsync(
() -> TelephonyUtils.runWithCleanCallingIdentity(r), executor).join();
} catch (CancellationException | CompletionException e) {
Log.w(LOG_TAG, "MmTelFeature Binder - " + errorLogName + " exception: "
+ e.getMessage());
}
}
private <T> T executeMethodAsyncForResult(Supplier<T> r,
String errorLogName) throws RemoteException {
CompletableFuture<T> future = CompletableFuture.supplyAsync(
() -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor);
try {
return future.get();
} catch (ExecutionException | InterruptedException e) {
Log.w(LOG_TAG, "MmTelFeature Binder - " + errorLogName + " exception: "
+ e.getMessage());
throw new RemoteException(e.getMessage());
}
}
private <T> T executeMethodAsyncForResultNoException(Supplier<T> r,
String errorLogName) {
CompletableFuture<T> future = CompletableFuture.supplyAsync(
() -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor);
try {
return future.get();
} catch (ExecutionException | InterruptedException e) {
Log.w(LOG_TAG, "MmTelFeature Binder - " + errorLogName + " exception: "
+ e.getMessage());
return null;
}
}
private <T> T executeMethodAsyncForResultNoException(Supplier<T> r,
String errorLogName, Executor executor) {
CompletableFuture<T> future = CompletableFuture.supplyAsync(
() -> TelephonyUtils.runWithCleanCallingIdentity(r), executor);
try {
return future.get();
} catch (ExecutionException | InterruptedException e) {
Log.w(LOG_TAG, "MmTelFeature Binder - " + errorLogName + " exception: "
+ e.getMessage());
return null;
}
}
};
/**
* Contains the capabilities defined and supported by a MmTelFeature in the form of a Bitmask.
* The capabilities that are used in MmTelFeature are defined as
* {@link MmTelCapabilities#CAPABILITY_TYPE_VOICE},
* {@link MmTelCapabilities#CAPABILITY_TYPE_VIDEO},
* {@link MmTelCapabilities#CAPABILITY_TYPE_UT},
* {@link MmTelCapabilities#CAPABILITY_TYPE_SMS}, and
* {@link MmTelCapabilities#CAPABILITY_TYPE_CALL_COMPOSER}.
*
* The capabilities of this MmTelFeature will be set by the framework.
*/
public static class MmTelCapabilities extends Capabilities {
/**
* Create a new empty {@link MmTelCapabilities} instance.
* @see #addCapabilities(int)
* @see #removeCapabilities(int)
* @hide
*/
@SystemApi
public MmTelCapabilities() {
super();
}
/**@deprecated Use {@link MmTelCapabilities} to construct a new instance instead.
* @hide
*/
@Deprecated
@SystemApi
public MmTelCapabilities(Capabilities c) {
mCapabilities = c.mCapabilities;
}
/**
* Create a new {link @MmTelCapabilities} instance with the provided capabilities.
* @param capabilities The capabilities that are supported for MmTel in the form of a
* bitfield.
* @hide
*/
@SystemApi
public MmTelCapabilities(@MmTelCapability int capabilities) {
super(capabilities);
}
/** @hide */
@IntDef(flag = true,
value = {
CAPABILITY_TYPE_VOICE,
CAPABILITY_TYPE_VIDEO,
CAPABILITY_TYPE_UT,
CAPABILITY_TYPE_SMS,
CAPABILITY_TYPE_CALL_COMPOSER
})
@Retention(RetentionPolicy.SOURCE)
public @interface MmTelCapability {}
/**
* Undefined capability type for initialization
* This is used to check the upper range of MmTel capability
* @hide
*/
public static final int CAPABILITY_TYPE_NONE = 0;
/**
* This MmTelFeature supports Voice calling (IR.92)
*/
public static final int CAPABILITY_TYPE_VOICE = 1 << 0;
/**
* This MmTelFeature supports Video (IR.94)
*/
public static final int CAPABILITY_TYPE_VIDEO = 1 << 1;
/**
* This MmTelFeature supports XCAP over Ut for supplementary services. (IR.92)
*/
public static final int CAPABILITY_TYPE_UT = 1 << 2;
/**
* This MmTelFeature supports SMS (IR.92)
*/
public static final int CAPABILITY_TYPE_SMS = 1 << 3;
/**
* This MmTelFeature supports Call Composer (section 2.4 of RC.20)
*/
public static final int CAPABILITY_TYPE_CALL_COMPOSER = 1 << 4;
/**
* This is used to check the upper range of MmTel capability
* @hide
*/
public static final int CAPABILITY_TYPE_MAX = CAPABILITY_TYPE_CALL_COMPOSER + 1;
/**
* @hide
*/
@Override
@SystemApi
public final void addCapabilities(@MmTelCapability int capabilities) {
super.addCapabilities(capabilities);
}
/**
* @hide
*/
@Override
@SystemApi
public final void removeCapabilities(@MmTelCapability int capability) {
super.removeCapabilities(capability);
}
/**
* @param capabilities a bitmask of one or more capabilities.
*
* @return true if all queried capabilities are true, otherwise false.
*/
@Override
public final boolean isCapable(@MmTelCapability int capabilities) {
return super.isCapable(capabilities);
}
/**
* @hide
*/
@NonNull
@Override
public String toString() {
StringBuilder builder = new StringBuilder("MmTel Capabilities - [");
builder.append("Voice: ");
builder.append(isCapable(CAPABILITY_TYPE_VOICE));
builder.append(" Video: ");
builder.append(isCapable(CAPABILITY_TYPE_VIDEO));
builder.append(" UT: ");
builder.append(isCapable(CAPABILITY_TYPE_UT));
builder.append(" SMS: ");
builder.append(isCapable(CAPABILITY_TYPE_SMS));
builder.append(" CALL_COMPOSER: ");
builder.append(isCapable(CAPABILITY_TYPE_CALL_COMPOSER));
builder.append("]");
return builder.toString();
}
}
/**
* Listener that the framework implements for communication from the MmTelFeature.
* @hide
*/
public static class Listener extends IImsMmTelListener.Stub {
/**
* Called when the IMS provider receives an incoming call.
* @param c The {@link ImsCallSession} associated with the new call.
* @param callId The call ID of the session of the new incoming call.
* @param extras A bundle containing extra parameters related to the call. See
* {@link #EXTRA_IS_UNKNOWN_CALL} and {@link #EXTRA_IS_USSD} above.
* @return the listener to listen to the session events. An {@link ImsCallSession} can only
* hold one listener at a time. see {@link ImsCallSessionListener}.
* If this method returns {@code null}, then the call could not be placed.
* @hide
*/
@Override
@Nullable
public IImsCallSessionListener onIncomingCall(IImsCallSession c,
String callId, Bundle extras) {
return null;
}
/**
* Called when the IMS provider implicitly rejects an incoming call during setup.
* @param callProfile An {@link ImsCallProfile} with the call details.
* @param reason The {@link ImsReasonInfo} reason for call rejection.
* @hide
*/
@Override
public void onRejectedCall(ImsCallProfile callProfile, ImsReasonInfo reason) {
}
/**
* Updates the Listener when the voice message count for IMS has changed.
* @param count an integer representing the new message count.
* @hide
*/
@Override
public void onVoiceMessageCountUpdate(int count) {
}
/**
* Called to set the audio handler for this connection.
* @param imsAudioHandler an {@link ImsAudioHandler} used to handle the audio
* for this IMS call.
* @hide
*/
@Override
public void onAudioModeIsVoipChanged(int imsAudioHandler) {
}
/**
* Called when the IMS triggers EPS fallback procedure.
*
* @param reason specifies the reason that causes EPS fallback.
* @hide
*/
@Override
public void onTriggerEpsFallback(@EpsFallbackReason int reason) {
}
/**
* Called when the IMS notifies the upcoming traffic type to the radio.
*
* @param token A nonce to identify the request
* @param trafficType The {@link ImsTrafficType} type for IMS traffic.
* @param accessNetworkType The {@link AccessNetworkConstants#RadioAccessNetworkType}
* type of the radio access network.
* @param trafficDirection Indicates whether traffic is originated by mobile originated or
* mobile terminated use case eg. MO/MT call/SMS etc.
* @param callback The callback to receive the result.
* @hide
*/
@Override
public void onStartImsTrafficSession(int token,
@ImsTrafficType int trafficType,
@AccessNetworkConstants.RadioAccessNetworkType int accessNetworkType,
@ImsTrafficDirection int trafficDirection,
IImsTrafficSessionCallback callback) {
}
/**
* Called when the IMS notifies the traffic type has been stopped.
*
* @param token A nonce registered with {@link #onStartImsTrafficSession}.
* @param accessNetworkType The {@link AccessNetworkConstants#RadioAccessNetworkType}
* type of the radio access network.
* @hide
*/
@Override
public void onModifyImsTrafficSession(int token,
@AccessNetworkConstants.RadioAccessNetworkType int accessNetworkType) {
}
/**
* Called when the IMS notifies the traffic type has been stopped.
*
* @param token A nonce registered with {@link #onStartImsTrafficSession}.
* @hide
*/
@Override
public void onStopImsTrafficSession(int token) {
}
/**
* Called when the IMS provider notifies {@link MediaQualityStatus}.
*
* @param status media quality status currently measured.
* @hide
*/
@Override
public void onMediaQualityStatusChanged(MediaQualityStatus status) {
}
}
/**
* A wrapper class of {@link ImsTrafficSessionCallback}.
* @hide
*/
public static class ImsTrafficSessionCallbackWrapper {
public static final int INVALID_TOKEN = -1;
private static final int MAX_TOKEN = 0x10000;
private static final AtomicInteger sTokenGenerator = new AtomicInteger();
/** Callback to receive the response */
private IImsTrafficSessionCallbackStub mCallback = null;
/** Identifier to distinguish each IMS traffic request */
private int mToken = INVALID_TOKEN;
private ImsTrafficSessionCallback mImsTrafficSessionCallback;
private ImsTrafficSessionCallbackWrapper(ImsTrafficSessionCallback callback) {
mImsTrafficSessionCallback = callback;
}
/**
* Updates the callback.
*
* The mToken should be kept since it is used to identify the traffic notified to the modem
* until calling {@link MmtelFEature#stopImsTrafficSession}.
*/
final void update(@NonNull @CallbackExecutor Executor executor) {
if (executor == null) {
throw new IllegalArgumentException(
"ImsTrafficSessionCallback Executor must be non-null");
}
if (mCallback == null) {
// initial start of Ims traffic.
mCallback = new IImsTrafficSessionCallbackStub(
mImsTrafficSessionCallback, executor);
mToken = generateToken();
} else {
// handover between cellular and Wi-Fi
mCallback.update(executor);
}
}
/**
* Using a static class and weak reference here to avoid memory leak caused by the
* {@link IImsTrafficSessionCallback.Stub} callback retaining references to the outside
* {@link ImsTrafficSessionCallback}.
*/
private static class IImsTrafficSessionCallbackStub
extends IImsTrafficSessionCallback.Stub {
private WeakReference<ImsTrafficSessionCallback> mImsTrafficSessionCallbackWeakRef;
private Executor mExecutor;
IImsTrafficSessionCallbackStub(ImsTrafficSessionCallback imsTrafficCallback,
Executor executor) {
mImsTrafficSessionCallbackWeakRef =
new WeakReference<ImsTrafficSessionCallback>(imsTrafficCallback);
mExecutor = executor;
}
void update(Executor executor) {
mExecutor = executor;
}
@Override
public void onReady() {
ImsTrafficSessionCallback callback = mImsTrafficSessionCallbackWeakRef.get();
if (callback == null) return;
Binder.withCleanCallingIdentity(
() -> mExecutor.execute(() -> callback.onReady()));
}
@Override
public void onError(ConnectionFailureInfo info) {
ImsTrafficSessionCallback callback = mImsTrafficSessionCallbackWeakRef.get();
if (callback == null) return;
Binder.withCleanCallingIdentity(
() -> mExecutor.execute(() -> callback.onError(info)));
}
}
/**
* Returns the callback binder.
*/
final IImsTrafficSessionCallbackStub getCallbackBinder() {
return mCallback;
}
/**
* Returns the token.
*/
final int getToken() {
return mToken;
}
/**
* Resets the members.
* It's called by {@link MmTelFeature#stopImsTrafficSession}.
*/
final void reset() {
mCallback = null;
mToken = INVALID_TOKEN;
}
private static int generateToken() {
int token = sTokenGenerator.incrementAndGet();
if (token == MAX_TOKEN) sTokenGenerator.set(0);
return token;
}
}
/**
* To be returned by {@link #shouldProcessCall(String[])} when the ImsService should process the
* outgoing call as IMS.
* @hide
*/
@SystemApi
public static final int PROCESS_CALL_IMS = 0;
/**
* To be returned by {@link #shouldProcessCall(String[])} when the telephony framework should
* not process the outgoing call as IMS and should instead use circuit switch.
* @hide
*/
@SystemApi
public static final int PROCESS_CALL_CSFB = 1;
/** @hide */
@IntDef(flag = true,
value = {
PROCESS_CALL_IMS,
PROCESS_CALL_CSFB
})
@Retention(RetentionPolicy.SOURCE)
public @interface ProcessCallResult {}
/**
* If the flag is present and true, it indicates that the incoming call is for USSD.
* <p>
* This is an optional boolean flag.
* @hide
*/
@SystemApi
public static final String EXTRA_IS_USSD = "android.telephony.ims.feature.extra.IS_USSD";
/**
* If this flag is present and true, this call is marked as an unknown dialing call instead
* of an incoming call. An example of such a call is a call that is originated by sending
* commands (like AT commands) directly to the modem without Android involvement or dialing
* calls appearing over IMS when the modem does a silent redial from circuit-switched to IMS in
* certain situations.
* <p>
* This is an optional boolean flag.
* @hide
*/
@SystemApi
public static final String EXTRA_IS_UNKNOWN_CALL =
"android.telephony.ims.feature.extra.IS_UNKNOWN_CALL";
/** @hide */
@IntDef(
prefix = "AUDIO_HANDLER_",
value = {
AUDIO_HANDLER_ANDROID,
AUDIO_HANDLER_BASEBAND
})
@Retention(RetentionPolicy.SOURCE)
public @interface ImsAudioHandler {}
/**
* Audio Handler - Android
* @hide
*/
@SystemApi
public static final int AUDIO_HANDLER_ANDROID = 0;
/**
* Audio Handler - Baseband
* @hide
*/
@SystemApi
public static final int AUDIO_HANDLER_BASEBAND = 1;
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(
prefix = "EPS_FALLBACK_REASON_",
value = {
EPS_FALLBACK_REASON_INVALID,
EPS_FALLBACK_REASON_NO_NETWORK_TRIGGER,
EPS_FALLBACK_REASON_NO_NETWORK_RESPONSE,
})
public @interface EpsFallbackReason {}
/**
* Default value. Internal use only.
* This value should not be used to trigger EPS fallback.
* @hide
*/
public static final int EPS_FALLBACK_REASON_INVALID = -1;
/**
* If the network only supports the EPS fallback in 5G NR SA for voice calling and the EPS
* Fallback procedure by the network during the call setup is not triggered, UE initiated
* fallback will be triggered with this reason. The modem shall locally release the 5G NR
* SA RRC connection and acquire the LTE network and perform a tracking area update
* procedure. After the EPS fallback procedure is completed, the call setup for voice will
* be established if there is no problem.
*
* @hide
*/
public static final int EPS_FALLBACK_REASON_NO_NETWORK_TRIGGER = 1;
/**
* If the UE doesn't receive any response for SIP INVITE within a certain timeout in 5G NR
* SA for MO voice calling, the device determines that voice call is not available in 5G and
* terminates all active SIP dialogs and SIP requests and enters IMS non-registered state.
* In that case, UE initiated fallback will be triggered with this reason. The modem shall
* reset modem's data buffer of IMS PDU to prevent the ghost call. After the EPS fallback
* procedure is completed, VoLTE call could be tried if there is no problem.
*
* @hide
*/
public static final int EPS_FALLBACK_REASON_NO_NETWORK_RESPONSE = 2;
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(
prefix = "IMS_TRAFFIC_TYPE_",
value = {
IMS_TRAFFIC_TYPE_NONE,
IMS_TRAFFIC_TYPE_EMERGENCY,
IMS_TRAFFIC_TYPE_EMERGENCY_SMS,
IMS_TRAFFIC_TYPE_VOICE,
IMS_TRAFFIC_TYPE_VIDEO,
IMS_TRAFFIC_TYPE_SMS,
IMS_TRAFFIC_TYPE_REGISTRATION,
IMS_TRAFFIC_TYPE_UT_XCAP
})
public @interface ImsTrafficType {}
/**
* Default value for initialization. Internal use only.
* @hide
*/
public static final int IMS_TRAFFIC_TYPE_NONE = -1;
/**
* Emergency call
* @hide
*/
public static final int IMS_TRAFFIC_TYPE_EMERGENCY = 0;
/**
* Emergency SMS
* @hide
*/
public static final int IMS_TRAFFIC_TYPE_EMERGENCY_SMS = 1;
/**
* Voice call
* @hide
*/
public static final int IMS_TRAFFIC_TYPE_VOICE = 2;
/**
* Video call
* @hide
*/
public static final int IMS_TRAFFIC_TYPE_VIDEO = 3;
/**
* SMS over IMS
* @hide
*/
public static final int IMS_TRAFFIC_TYPE_SMS = 4;
/**
* IMS registration and subscription for reg event package (signaling)
* @hide
*/
public static final int IMS_TRAFFIC_TYPE_REGISTRATION = 5;
/**
* Ut/XCAP (XML Configuration Access Protocol)
* @hide
*/
public static final int IMS_TRAFFIC_TYPE_UT_XCAP = 6;
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(
prefix = { "IMS_TRAFFIC_DIRECTION_" },
value = {IMS_TRAFFIC_DIRECTION_INCOMING, IMS_TRAFFIC_DIRECTION_OUTGOING})
public @interface ImsTrafficDirection {}
/**
* Indicates that the traffic is an incoming traffic.
* @hide
*/
public static final int IMS_TRAFFIC_DIRECTION_INCOMING = 0;
/**
* Indicates that the traffic is an outgoing traffic.
* @hide
*/
public static final int IMS_TRAFFIC_DIRECTION_OUTGOING = 1;
private IImsMmTelListener mListener;
/**
* @param listener A {@link Listener} used when the MmTelFeature receives an incoming call and
* notifies the framework.
*/
private void setListener(IImsMmTelListener listener) {
synchronized (mLock) {
mListener = listener;
if (mListener != null) {
onFeatureReady();
}
}
}
/**
* @return the listener associated with this MmTelFeature. May be null if it has not been set
* by the framework yet.
*/
private IImsMmTelListener getListener() {
synchronized (mLock) {
return mListener;
}
}
/**
* The current capability status that this MmTelFeature has defined is available. This
* configuration will be used by the platform to figure out which capabilities are CURRENTLY
* available to be used.
*
* Should be a subset of the capabilities that are enabled by the framework in
* {@link #changeEnabledCapabilities}.
* @return A copy of the current MmTelFeature capability status.
* @hide
*/
@Override
@SystemApi
public @NonNull final MmTelCapabilities queryCapabilityStatus() {
return new MmTelCapabilities(super.queryCapabilityStatus());
}
/**
* Notify the framework that the status of the Capabilities has changed. Even though the
* MmTelFeature capability may be enabled by the framework, the status may be disabled due to
* the feature being unavailable from the network.
* @param c The current capability status of the MmTelFeature. If a capability is disabled, then
* the status of that capability is disabled. This can happen if the network does not currently
* support the capability that is enabled. A capability that is disabled by the framework (via
* {@link #changeEnabledCapabilities}) should also show the status as disabled.
* @hide
*/
@SystemApi
public final void notifyCapabilitiesStatusChanged(@NonNull MmTelCapabilities c) {
if (c == null) {
throw new IllegalArgumentException("MmTelCapabilities must be non-null!");
}
super.notifyCapabilitiesStatusChanged(c);
}
/**
* Notify the framework that the measured media quality has crossed a threshold set by {@link
* MmTelFeature#setMediaThreshold}
*
* @param status current media quality status measured.
* @hide
*/
@SystemApi
public final void notifyMediaQualityStatusChanged(
@NonNull MediaQualityStatus status) {
if (status == null) {
throw new IllegalArgumentException(
"MediaQualityStatus must be non-null!");
}
Log.i(LOG_TAG, "notifyMediaQualityStatusChanged " + status);
IImsMmTelListener listener = getListener();
if (listener == null) {
throw new IllegalStateException("Session is not available.");
}
try {
listener.onMediaQualityStatusChanged(status);
} catch (RemoteException e) {
throw new RuntimeException(e);
}
}
/**
* Notify the framework of an incoming call.
* @param c The {@link ImsCallSessionImplBase} of the new incoming call.
* @param extras A bundle containing extra parameters related to the call. See
* {@link #EXTRA_IS_UNKNOWN_CALL} and {@link #EXTRA_IS_USSD} above.
* @hide
*
* @deprecated use {@link #notifyIncomingCall(ImsCallSessionImplBase, String, Bundle)} instead
*/
@Deprecated
@SystemApi
public final void notifyIncomingCall(@NonNull ImsCallSessionImplBase c,
@NonNull Bundle extras) {
if (c == null || extras == null) {
throw new IllegalArgumentException("ImsCallSessionImplBase and Bundle can not be "
+ "null.");
}
IImsMmTelListener listener = getListener();
if (listener == null) {
throw new IllegalStateException("Session is not available.");
}
try {
c.setDefaultExecutor(MmTelFeature.this.mExecutor);
listener.onIncomingCall(c.getServiceImpl(), null, extras);
} catch (RemoteException e) {
throw new RuntimeException(e);
}
}
/**
* Notify the framework of an incoming call.
* @param c The {@link ImsCallSessionImplBase} of the new incoming call.
* @param callId The call ID of the session of the new incoming call.
* @param extras A bundle containing extra parameters related to the call. See
* {@link #EXTRA_IS_UNKNOWN_CALL} and {@link #EXTRA_IS_USSD} above.
* @return The listener used by the framework to listen to call session events created
* from the ImsService.
* If this method returns {@code null}, then the call could not be placed.
* @hide
*/
@SystemApi
@Nullable
public final ImsCallSessionListener notifyIncomingCall(
@NonNull ImsCallSessionImplBase c, @NonNull String callId, @NonNull Bundle extras) {
if (c == null || callId == null || extras == null) {
throw new IllegalArgumentException("ImsCallSessionImplBase, callId, and Bundle can "
+ "not be null.");
}
IImsMmTelListener listener = getListener();
if (listener == null) {
throw new IllegalStateException("Session is not available.");
}
try {
c.setDefaultExecutor(MmTelFeature.this.mExecutor);
IImsCallSessionListener isl =
listener.onIncomingCall(c.getServiceImpl(), callId, extras);
if (isl != null) {
ImsCallSessionListener iCSL = new ImsCallSessionListener(isl);
iCSL.setDefaultExecutor(MmTelFeature.this.mExecutor);
return iCSL;
} else {
return null;
}
} catch (RemoteException e) {
throw new RuntimeException(e);
}
}
/**
* Notify the framework that a call has been implicitly rejected by this MmTelFeature
* during call setup.
* @param callProfile The {@link ImsCallProfile} IMS call profile with details.
* This can be null if no call information is available for the rejected call.
* @param reason The {@link ImsReasonInfo} call rejection reason.
* @hide
*/
@SystemApi
public final void notifyRejectedCall(@NonNull ImsCallProfile callProfile,
@NonNull ImsReasonInfo reason) {
if (callProfile == null || reason == null) {
throw new IllegalArgumentException("ImsCallProfile and ImsReasonInfo must not be "
+ "null.");
}
IImsMmTelListener listener = getListener();
if (listener == null) {
throw new IllegalStateException("Session is not available.");
}
try {
listener.onRejectedCall(callProfile, reason);
} catch (RemoteException e) {
throw new RuntimeException(e);
}
}
/**
*
* @hide
*/
public final void notifyIncomingCallSession(IImsCallSession c, Bundle extras) {
IImsMmTelListener listener = getListener();
if (listener == null) {
throw new IllegalStateException("Session is not available.");
}
try {
listener.onIncomingCall(c, null, extras);
} catch (RemoteException e) {
throw new RuntimeException(e);
}
}
/**
* Notify the framework of a change in the Voice Message count.
* @link count the new Voice Message count.
* @hide
*/
@SystemApi
public final void notifyVoiceMessageCountUpdate(int count) {
IImsMmTelListener listener = getListener();
if (listener == null) {
throw new IllegalStateException("Session is not available.");
}
try {
listener.onVoiceMessageCountUpdate(count);
} catch (RemoteException e) {
throw new RuntimeException(e);
}
}
/**
* Sets the audio handler for this connection. The vendor IMS stack will invoke this API
* to inform Telephony/Telecom layers about which audio handlers i.e. either Android or Modem
* shall be used for handling the IMS call audio.
*
* @param imsAudioHandler {@link MmTelFeature#ImsAudioHandler} used to handle the audio
* for this IMS call.
* @hide
*/
@SystemApi
public final void setCallAudioHandler(@ImsAudioHandler int imsAudioHandler) {
IImsMmTelListener listener = getListener();
if (listener == null) {
throw new IllegalStateException("Session is not available.");
}
try {
listener.onAudioModeIsVoipChanged(imsAudioHandler);
} catch (RemoteException e) {
throw new RuntimeException(e);
}
}
/**
* Triggers the EPS fallback procedure.
*
* @param reason specifies the reason that causes EPS fallback.
* @hide
*/
public final void triggerEpsFallback(@EpsFallbackReason int reason) {
IImsMmTelListener listener = getListener();
if (listener == null) {
throw new IllegalStateException("Session is not available.");
}
try {
listener.onTriggerEpsFallback(reason);
} catch (RemoteException e) {
throw new RuntimeException(e);
}
}
/**
* Starts a new IMS traffic session with the framework.
*
* This API notifies the NAS and RRC layers of the modem that IMS traffic of type
* {@link ImsTrafficType} is starting for the IMS session represented by a
* {@link ImsTrafficSessionCallback}. The {@link ImsTrafficSessionCallback}
* will notify the caller when IMS traffic is ready to start via the
* {@link ImsTrafficSessionCallback#onReady()} callback. If there was an error starting
* IMS traffic for the specified traffic type, {@link ImsTrafficSessionCallback#onError()} will
* be called, which will also notify the caller of the reason of the failure.
*
* If there is a handover that changes the {@link AccessNetworkConstants#RadioAccessNetworkType}
* of this IMS traffic session, then {@link #modifyImsTrafficSession} should be called. This is
* used, for example, when a WiFi <-> cellular handover occurs.
*
* Once the IMS traffic session is finished, {@link #stopImsTrafficSession} must be called.
*
* Note: This API will be used to prioritize RF resources in case of DSDS. The service priority
* is EMERGENCY > EMERGENCY SMS > VOICE > VIDEO > SMS > REGISTRATION > Ut/XCAP. RF
* shall be prioritized to the subscription which handles the higher priority service.
* When both subscriptions are handling the same type of service, then RF shall be
* prioritized to the voice preferred sub.
*
* @param trafficType The {@link ImsTrafficType} type for IMS traffic.
* @param accessNetworkType The {@link AccessNetworkConstants#RadioAccessNetworkType} type of
* the radio access network.
* @param trafficDirection Indicates whether traffic is originated by mobile originated or
* mobile terminated use case eg. MO/MT call/SMS etc.
* @param executor The Executor that will be used to call the {@link ImsTrafficSessionCallback}.
* @param callback The session representing the IMS Session associated with a specific
* trafficType. This callback instance should only be used for the specified traffic type
* until {@link #stopImsTrafficSession} is called.
*
* @see modifyImsTrafficSession
* @see stopImsTrafficSession
*
* @hide
*/
public final void startImsTrafficSession(@ImsTrafficType int trafficType,
@AccessNetworkConstants.RadioAccessNetworkType int accessNetworkType,
@ImsTrafficDirection int trafficDirection,
@NonNull Executor executor, @NonNull ImsTrafficSessionCallback callback) {
IImsMmTelListener listener = getListener();
if (listener == null) {
throw new IllegalStateException("Session is not available.");
}
// TODO: retrieve from the callback list
ImsTrafficSessionCallbackWrapper callbackWrapper = mTrafficCallbacks.get(callback);
if (callbackWrapper == null) {
callbackWrapper = new ImsTrafficSessionCallbackWrapper(callback);
mTrafficCallbacks.put(callback, callbackWrapper);
}
try {
callbackWrapper.update(executor);
listener.onStartImsTrafficSession(callbackWrapper.getToken(),
trafficType, accessNetworkType, trafficDirection,
callbackWrapper.getCallbackBinder());
} catch (RemoteException e) {
throw new RuntimeException(e);
}
}
/**
* Modifies an existing IMS traffic session represented by the associated
* {@link ImsTrafficSessionCallback}.
*
* The {@link ImsTrafficSessionCallback} will notify the caller when IMS traffic is ready to
* start after modification using the {@link ImsTrafficSessionCallback#onReady()} callback.
* If there was an error modifying IMS traffic for the new radio access network type type,
* {@link ImsTrafficSessionCallback#onError()} will be called, which will also notify the
* caller of the reason of the failure.
*
* @param accessNetworkType The {@link AccessNetworkConstants#RadioAccessNetworkType} type of
* the radio access network.
* @param callback The callback registered with {@link #startImsTrafficSession}.
*
* @see startImsTrafficSession
* @see stopImsTrafficSession
*
* @hide
*/
public final void modifyImsTrafficSession(
@AccessNetworkConstants.RadioAccessNetworkType int accessNetworkType,
@NonNull ImsTrafficSessionCallback callback) {
IImsMmTelListener listener = getListener();
if (listener == null) {
throw new IllegalStateException("Session is not available.");
}
ImsTrafficSessionCallbackWrapper callbackWrapper = mTrafficCallbacks.get(callback);
if (callbackWrapper == null) {
// should not reach here.
throw new IllegalStateException("Unknown ImsTrafficSessionCallback instance.");
}
try {
listener.onModifyImsTrafficSession(callbackWrapper.getToken(), accessNetworkType);
} catch (RemoteException e) {
throw new RuntimeException(e);
}
}
/**
* Notifies the framework that the IMS traffic session represented by the associated
* {@link ImsTrafficSessionCallback} has ended.
*
* @param callback The callback registered with {@link #startImsTrafficSession}.
*
* @see startImsTrafficSession
* @see modifyImsTrafficSession
*
* @hide
*/
public final void stopImsTrafficSession(@NonNull ImsTrafficSessionCallback callback) {
IImsMmTelListener listener = getListener();
if (listener == null) {
throw new IllegalStateException("Session is not available.");
}
ImsTrafficSessionCallbackWrapper callbackWrapper = mTrafficCallbacks.get(callback);
if (callbackWrapper == null) {
// should not reach here.
throw new IllegalStateException("Unknown ImsTrafficSessionCallback instance.");
}
try {
listener.onStopImsTrafficSession(callbackWrapper.getToken());
callbackWrapper.reset();
mTrafficCallbacks.remove(callback);
} catch (RemoteException e) {
throw new RuntimeException(e);
}
}
/**
* Provides the MmTelFeature with the ability to return the framework Capability Configuration
* for a provided Capability. If the framework calls {@link #changeEnabledCapabilities} and
* includes a capability A to enable or disable, this method should return the correct enabled
* status for capability A.
* @param capability The capability that we are querying the configuration for.
* @return true if the capability is enabled, false otherwise.
* @hide
*/
@Override
@SystemApi
public boolean queryCapabilityConfiguration(@MmTelCapabilities.MmTelCapability int capability,
@ImsRegistrationImplBase.ImsRegistrationTech int radioTech) {
// Base implementation - Override to provide functionality
return false;
}
/**
* The MmTelFeature should override this method to handle the enabling/disabling of
* MmTel Features, defined in {@link MmTelCapabilities.MmTelCapability}. The framework assumes
* the {@link CapabilityChangeRequest} was processed successfully. If a subset of capabilities
* could not be set to their new values,
* {@link CapabilityCallbackProxy#onChangeCapabilityConfigurationError} must be called
* individually for each capability whose processing resulted in an error.
*
* Enabling/Disabling a capability here indicates that the capability should be registered or
* deregistered (depending on the capability change) and become available or unavailable to
* the framework.
* @hide
*/
@Override
@SystemApi
public void changeEnabledCapabilities(@NonNull CapabilityChangeRequest request,
@NonNull CapabilityCallbackProxy c) {
// Base implementation, no-op
}
/**
* Called by the framework to pass {@link MediaThreshold}. The MmTelFeature should override this
* method to get Media quality threshold. This will pass the consolidated threshold values from
* Telephony framework. IMS provider needs to monitor media quality of active call and notify
* media quality {@link #notifyMediaQualityStatusChanged(MediaQualityStatus)} when the measured
* media quality crosses at least one of {@link MediaThreshold} set by this.
*
* @param mediaSessionType media session type for this Threshold info.
* @param mediaThreshold media threshold information
* @hide
*/
@SystemApi
public void setMediaThreshold(
@MediaQualityStatus.MediaSessionType int mediaSessionType,
@NonNull MediaThreshold mediaThreshold) {
// Base Implementation - Should be overridden.
Log.d(LOG_TAG, "setMediaThreshold is not supported." + mediaThreshold);
}
/**
* The MmTelFeature should override this method to clear Media quality thresholds that were
* registered and stop media quality status updates.
*
* @param mediaSessionType media session type
* @hide
*/
@SystemApi
public void clearMediaThreshold(@MediaQualityStatus.MediaSessionType int mediaSessionType) {
// Base Implementation - Should be overridden.
Log.d(LOG_TAG, "clearMediaThreshold is not supported." + mediaSessionType);
}
/**
* IMS provider should override this method to return currently measured media quality status.
*
* <p/>
* If media quality status is not yet measured after call is active, it needs to notify media
* quality status {@link #notifyMediaQualityStatusChanged(MediaQualityStatus)} when the first
* measurement is done.
*
* @param mediaSessionType media session type
* @return Current media quality status. It could be null if media quality status is not
* measured yet or {@link MediaThreshold} was not set corresponding to the media session
* type.
*
* @hide
*/
@SystemApi
@Nullable
public MediaQualityStatus queryMediaQualityStatus(
@MediaQualityStatus.MediaSessionType int mediaSessionType) {
// Base Implementation - Should be overridden.
Log.d(LOG_TAG, "queryMediaQualityStatus is not supported." + mediaSessionType);
return null;
}
/**
* Creates a {@link ImsCallProfile} from the service capabilities & IMS registration state.
*
* @param callSessionType a service type that is specified in {@link ImsCallProfile}
* {@link ImsCallProfile#SERVICE_TYPE_NONE}
* {@link ImsCallProfile#SERVICE_TYPE_NORMAL}
* {@link ImsCallProfile#SERVICE_TYPE_EMERGENCY}
* @param callType a call type that is specified in {@link ImsCallProfile}
* {@link ImsCallProfile#CALL_TYPE_VOICE}
* {@link ImsCallProfile#CALL_TYPE_VT}
* {@link ImsCallProfile#CALL_TYPE_VT_TX}
* {@link ImsCallProfile#CALL_TYPE_VT_RX}
* {@link ImsCallProfile#CALL_TYPE_VT_NODIR}
* {@link ImsCallProfile#CALL_TYPE_VS}
* {@link ImsCallProfile#CALL_TYPE_VS_TX}
* {@link ImsCallProfile#CALL_TYPE_VS_RX}
* @return a {@link ImsCallProfile} object
* @hide
*/
@SystemApi
public @Nullable ImsCallProfile createCallProfile(int callSessionType, int callType) {
// Base Implementation - Should be overridden
return null;
}
/**
* Called by the framework to report a change to the RTP header extension types which should be
* offered during SDP negotiation (see RFC8285 for more information).
* <p>
* The {@link ImsService} should report the RTP header extensions which were accepted during
* SDP negotiation using {@link ImsCallProfile#setAcceptedRtpHeaderExtensionTypes(Set)}.
*
* @param extensionTypes The RTP header extensions the framework wishes to offer during
* outgoing and incoming call setup. An empty list indicates that there
* are no framework defined RTP header extension types to offer.
* @hide
*/
@SystemApi
public void changeOfferedRtpHeaderExtensionTypes(
@NonNull Set<RtpHeaderExtensionType> extensionTypes) {
// Base implementation - should be overridden if RTP header extension handling is supported.
}
/**
* @hide
*/
public IImsCallSession createCallSessionInterface(ImsCallProfile profile)
throws RemoteException {
ImsCallSessionImplBase s = MmTelFeature.this.createCallSession(profile);
if (s != null) {
s.setDefaultExecutor(mExecutor);
return s.getServiceImpl();
} else {
return null;
}
}
/**
* Creates an {@link ImsCallSession} with the specified call profile.
* Use other methods, if applicable, instead of interacting with
* {@link ImsCallSession} directly.
*
* @param profile a call profile to make the call
* @hide
*/
@SystemApi
public @Nullable ImsCallSessionImplBase createCallSession(@NonNull ImsCallProfile profile) {
// Base Implementation - Should be overridden
return null;
}
/**
* Called by the framework to determine if the outgoing call, designated by the outgoing
* {@link String}s, should be processed as an IMS call or CSFB call. If this method's
* functionality is not overridden, the platform will process every call as IMS as long as the
* MmTelFeature reports that the {@link MmTelCapabilities#CAPABILITY_TYPE_VOICE} capability is
* available.
* @param numbers An array of {@link String}s that will be used for placing the call. There can
* be multiple {@link String}s listed in the case when we want to place an outgoing
* call as a conference.
* @return a {@link ProcessCallResult} to the framework, which will be used to determine if the
* call will be placed over IMS or via CSFB.
* @hide
*/
@SystemApi
public @ProcessCallResult int shouldProcessCall(@NonNull String[] numbers) {
return PROCESS_CALL_IMS;
}
/**
*
* @hide
*/
protected IImsUt getUtInterface() throws RemoteException {
ImsUtImplBase utImpl = getUt();
if (utImpl != null) {
utImpl.setDefaultExecutor(mExecutor);
return utImpl.getInterface();
} else {
return null;
}
}
/**
* @hide
*/
protected IImsEcbm getEcbmInterface() throws RemoteException {
ImsEcbmImplBase ecbmImpl = getEcbm();
if (ecbmImpl != null) {
ecbmImpl.setDefaultExecutor(mExecutor);
return ecbmImpl.getImsEcbm();
} else {
return null;
}
}
/**
* @hide
*/
public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException {
ImsMultiEndpointImplBase multiendpointImpl = getMultiEndpoint();
if (multiendpointImpl != null) {
multiendpointImpl.setDefaultExecutor(mExecutor);
return multiendpointImpl.getIImsMultiEndpoint();
} else {
return null;
}
}
/**
* @hide
*/
public @NonNull ImsSmsImplBase getImsSmsImpl() {
synchronized (mLock) {
if (mSmsImpl == null) {
mSmsImpl = getSmsImplementation();
mSmsImpl.setDefaultExecutor(mExecutor);
}
return mSmsImpl;
}
}
/**
* @return The {@link ImsUtImplBase} Ut interface implementation for the supplementary service
* configuration.
* @hide
*/
@SystemApi
public @NonNull ImsUtImplBase getUt() {
// Base Implementation - Should be overridden
return new ImsUtImplBase();
}
/**
* @return The {@link ImsEcbmImplBase} Emergency call-back mode interface for emergency VoLTE
* calls that support it.
* @hide
*/
@SystemApi
public @NonNull ImsEcbmImplBase getEcbm() {
// Base Implementation - Should be overridden
return new ImsEcbmImplBase();
}
/**
* @return The {@link ImsMultiEndpointImplBase} implementation for implementing Dialog event
* package processing for multi-endpoint.
* @hide
*/
@SystemApi
public @NonNull ImsMultiEndpointImplBase getMultiEndpoint() {
// Base Implementation - Should be overridden
return new ImsMultiEndpointImplBase();
}
/**
* Sets the current UI TTY mode for the MmTelFeature.
* @param mode An integer containing the new UI TTY Mode, can consist of
* {@link TelecomManager#TTY_MODE_OFF},
* {@link TelecomManager#TTY_MODE_FULL},
* {@link TelecomManager#TTY_MODE_HCO},
* {@link TelecomManager#TTY_MODE_VCO}
* @param onCompleteMessage If non-null, this MmTelFeature should call this {@link Message} when
* the operation is complete by using the associated {@link android.os.Messenger} in
* {@link Message#replyTo}. For example:
* {@code
* // Set UI TTY Mode and other operations...
* try {
* // Notify framework that the mode was changed.
* Messenger uiMessenger = onCompleteMessage.replyTo;
* uiMessenger.send(onCompleteMessage);
* } catch (RemoteException e) {
* // Remote side is dead
* }
* }
* @hide
*/
@SystemApi
public void setUiTtyMode(int mode, @Nullable Message onCompleteMessage) {
// Base Implementation - Should be overridden
}
/**
* Notifies the MmTelFeature of the enablement status of terminal based call waiting
*
* If the terminal based call waiting is provisioned,
* IMS controls the enablement of terminal based call waiting which is defined
* in 3GPP TS 24.615.
*
* @param enabled user setting controlling whether or not call waiting is enabled.
*
* @hide
*/
@SystemApi
public void setTerminalBasedCallWaitingStatus(boolean enabled) {
// Base Implementation - Should be overridden by IMS service
throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
"Not implemented on device.");
}
/**
* Notifies the MmTelFeature that the network has initiated an SRVCC (Single radio voice
* call continuity) for all IMS calls. When the network initiates an SRVCC, calls from
* the LTE domain are handed over to the legacy circuit switched domain. The modem requires
* knowledge of ongoing calls in the IMS domain in order to complete the SRVCC operation.
* <p>
* @param consumer The callback used to notify the framework of the list of IMS calls and their
* state at the time of the SRVCC.
*
* @hide
*/
@SystemApi
public void notifySrvccStarted(@NonNull Consumer<List<SrvccCall>> consumer) {
// Base Implementation - Should be overridden by IMS service
}
/**
* Notifies the MmTelFeature that the SRVCC is completed and the calls have been moved
* over to the circuit-switched domain.
* {@link android.telephony.CarrierConfigManager.ImsVoice#KEY_SRVCC_TYPE_INT_ARRAY}
* specifies the calls can be moved. Other calls will be disconnected.
* <p>
* The MmTelFeature may now release all resources related to the IMS calls.
*
* @hide
*/
@SystemApi
public void notifySrvccCompleted() {
// Base Implementation - Should be overridden by IMS service
}
/**
* Notifies the MmTelFeature that the SRVCC has failed.
*
* The handover can fail by encountering a failure at the radio level
* or temporary MSC server internal errors in handover procedure.
* Refer to 3GPP TS 23.216 section 8 Handover Failure.
* <p>
* IMS service will recover and continue calls over IMS.
* Per TS 24.237 12.2.4.2, UE shall send SIP UPDATE request containing the reason-text
* set to "failure to transition to CS domain".
*
* @hide
*/
@SystemApi
public void notifySrvccFailed() {
// Base Implementation - Should be overridden by IMS service
}
/**
* Notifies the MmTelFeature that the SRVCC has been canceled.
*
* Since the state of network can be changed, the network can decide to terminate
* the handover procedure before its completion and to return to its state before the handover
* procedure was triggered.
* Refer to 3GPP TS 23.216 section 8.1.3 Handover Cancellation.
*
* <p>
* IMS service will recover and continue calls over IMS.
* Per TS 24.237 12.2.4.2, UE shall send SIP UPDATE request containing the reason-text
* set to "handover canceled".
*
* @hide
*/
@SystemApi
public void notifySrvccCanceled() {
// Base Implementation - Should be overridden by IMS service
}
private void setSmsListener(IImsSmsListener listener) {
getImsSmsImpl().registerSmsListener(listener);
}
private void sendSms(int token, int messageRef, String format, String smsc, boolean isRetry,
byte[] pdu) {
getImsSmsImpl().sendSms(token, messageRef, format, smsc, isRetry, pdu);
}
private void onMemoryAvailable(int token) {
getImsSmsImpl().onMemoryAvailable(token);
}
private void acknowledgeSms(int token, int messageRef,
@ImsSmsImplBase.DeliverStatusResult int result) {
getImsSmsImpl().acknowledgeSms(token, messageRef, result);
}
private void acknowledgeSms(int token, int messageRef,
@ImsSmsImplBase.DeliverStatusResult int result, byte[] pdu) {
getImsSmsImpl().acknowledgeSms(token, messageRef, result, pdu);
}
private void acknowledgeSmsReport(int token, int messageRef,
@ImsSmsImplBase.StatusReportResult int result) {
getImsSmsImpl().acknowledgeSmsReport(token, messageRef, result);
}
private void onSmsReady() {
getImsSmsImpl().onReady();
}
/**
* Must be overridden by IMS Provider to be able to support SMS over IMS. Otherwise a default
* non-functional implementation is returned.
*
* @return an instance of {@link ImsSmsImplBase} which should be implemented by the IMS
* Provider.
* @hide
*/
@SystemApi
public @NonNull ImsSmsImplBase getSmsImplementation() {
return new ImsSmsImplBase();
}
private String getSmsFormat() {
return getImsSmsImpl().getSmsFormat();
}
/**
* {@inheritDoc}
* @hide
*/
@Override
@SystemApi
public void onFeatureRemoved() {
// Base Implementation - Should be overridden
}
/**
* {@inheritDoc}
* @hide
*/
@Override
@SystemApi
public void onFeatureReady() {
// Base Implementation - Should be overridden
}
/**
* @hide
*/
@Override
public final IImsMmTelFeature getBinder() {
return mImsMMTelBinder;
}
/**
* Set default Executor from ImsService.
* @param executor The default executor for the framework to use when executing the methods
* overridden by the implementation of MmTelFeature.
* @hide
*/
public final void setDefaultExecutor(@NonNull Executor executor) {
if (mExecutor == null) {
mExecutor = executor;
}
}
}