blob: 0d5a979e5894aee405badd6d952e85690691f47e [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.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.os.Bundle;
import android.os.Message;
import android.os.RemoteException;
import android.telecom.TelecomManager;
import android.telephony.ims.ImsCallProfile;
import android.telephony.ims.ImsCallSession;
import android.telephony.ims.ImsReasonInfo;
import android.telephony.ims.aidl.IImsCapabilityCallback;
import android.telephony.ims.aidl.IImsMmTelFeature;
import android.telephony.ims.aidl.IImsMmTelListener;
import android.telephony.ims.aidl.IImsSmsListener;
import android.telephony.ims.stub.ImsCallSessionImplBase;
import android.telephony.ims.stub.ImsEcbmImplBase;
import android.telephony.ims.stub.ImsMultiEndpointImplBase;
import android.telephony.ims.stub.ImsRegistrationImplBase;
import android.telephony.ims.stub.ImsSmsImplBase;
import android.telephony.ims.stub.ImsUtImplBase;
import com.android.ims.internal.IImsCallSession;
import com.android.ims.internal.IImsEcbm;
import com.android.ims.internal.IImsMultiEndpoint;
import com.android.ims.internal.IImsUt;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* Base implementation for Voice and SMS (IR-92) and Video (IR-94) IMS support.
*
* Any class wishing to use MmTelFeature should extend this class and implement all methods that the
* service supports.
*/
public class MmTelFeature extends ImsFeature {
private static final String LOG_TAG = "MmTelFeature";
/**
* @hide
*/
@SystemApi @TestApi
public MmTelFeature() {
}
private final IImsMmTelFeature mImsMMTelBinder = new IImsMmTelFeature.Stub() {
@Override
public void setListener(IImsMmTelListener l) {
MmTelFeature.this.setListener(l);
}
@Override
public int getFeatureState() throws RemoteException {
try {
return MmTelFeature.this.getFeatureState();
} catch (Exception e) {
throw new RemoteException(e.getMessage());
}
}
@Override
public ImsCallProfile createCallProfile(int callSessionType, int callType)
throws RemoteException {
synchronized (mLock) {
try {
return MmTelFeature.this.createCallProfile(callSessionType, callType);
} catch (Exception e) {
throw new RemoteException(e.getMessage());
}
}
}
@Override
public IImsCallSession createCallSession(ImsCallProfile profile) throws RemoteException {
synchronized (mLock) {
return createCallSessionInterface(profile);
}
}
@Override
public int shouldProcessCall(String[] numbers) {
synchronized (mLock) {
return MmTelFeature.this.shouldProcessCall(numbers);
}
}
@Override
public IImsUt getUtInterface() throws RemoteException {
synchronized (mLock) {
return MmTelFeature.this.getUtInterface();
}
}
@Override
public IImsEcbm getEcbmInterface() throws RemoteException {
synchronized (mLock) {
return MmTelFeature.this.getEcbmInterface();
}
}
@Override
public void setUiTtyMode(int uiTtyMode, Message onCompleteMessage) throws RemoteException {
synchronized (mLock) {
try {
MmTelFeature.this.setUiTtyMode(uiTtyMode, onCompleteMessage);
} catch (Exception e) {
throw new RemoteException(e.getMessage());
}
}
}
@Override
public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException {
synchronized (mLock) {
return MmTelFeature.this.getMultiEndpointInterface();
}
}
@Override
public int queryCapabilityStatus() {
return MmTelFeature.this.queryCapabilityStatus().mCapabilities;
}
@Override
public void addCapabilityCallback(IImsCapabilityCallback c) {
// no need to lock, structure already handles multithreading.
MmTelFeature.this.addCapabilityCallback(c);
}
@Override
public void removeCapabilityCallback(IImsCapabilityCallback c) {
// no need to lock, structure already handles multithreading.
MmTelFeature.this.removeCapabilityCallback(c);
}
@Override
public void changeCapabilitiesConfiguration(CapabilityChangeRequest request,
IImsCapabilityCallback c) {
MmTelFeature.this.requestChangeEnabledCapabilities(request, c);
}
@Override
public void queryCapabilityConfiguration(int capability, int radioTech,
IImsCapabilityCallback c) {
queryCapabilityConfigurationInternal(capability, radioTech, c);
}
@Override
public void setSmsListener(IImsSmsListener l) {
MmTelFeature.this.setSmsListener(l);
}
@Override
public void sendSms(int token, int messageRef, String format, String smsc, boolean retry,
byte[] pdu) {
synchronized (mLock) {
MmTelFeature.this.sendSms(token, messageRef, format, smsc, retry, pdu);
}
}
@Override
public void acknowledgeSms(int token, int messageRef, int result) {
synchronized (mLock) {
MmTelFeature.this.acknowledgeSms(token, messageRef, result);
}
}
@Override
public void acknowledgeSmsReport(int token, int messageRef, int result) {
synchronized (mLock) {
MmTelFeature.this.acknowledgeSmsReport(token, messageRef, result);
}
}
@Override
public String getSmsFormat() {
synchronized (mLock) {
return MmTelFeature.this.getSmsFormat();
}
}
@Override
public void onSmsReady() {
synchronized (mLock) {
MmTelFeature.this.onSmsReady();
}
}
};
/**
* Contains the capabilities defined and supported by a MmTelFeature in the form of a Bitmask.
* The capabilities that are used in MmTelFeature are defined as
* {@link MmTelCapabilities#CAPABILITY_TYPE_VOICE},
* {@link MmTelCapabilities#CAPABILITY_TYPE_VIDEO},
* {@link MmTelCapabilities#CAPABILITY_TYPE_UT}, and
* {@link MmTelCapabilities#CAPABILITY_TYPE_SMS}.
*
* The capabilities of this MmTelFeature will be set by the framework and can be queried with
* {@see #queryCapabilityStatus()}.
*
* This MmTelFeature can then return the status of each of these capabilities (enabled or not)
* by sending a {@see #notifyCapabilitiesStatusChanged} callback to the framework. The current
* status can also be queried using {@see #queryCapabilityStatus()}.
* @see #isCapable(int)
*/
public static class MmTelCapabilities extends Capabilities {
/**
* Create a new empty {@link MmTelCapabilities} instance.
* @see #addCapabilities(int)
* @see #removeCapabilities(int)
* @hide
*/
@SystemApi @TestApi
public MmTelCapabilities() {
super();
}
/**@deprecated Use {@link MmTelCapabilities} to construct a new instance instead.
* @hide
*/
@Deprecated
@SystemApi @TestApi
public MmTelCapabilities(Capabilities c) {
mCapabilities = c.mCapabilities;
}
/**
* Create a new {link @MmTelCapabilities} instance with the provided capabilities.
* @param capabilities The capabilities that are supported for MmTel in the form of a
* bitfield.
* @hide
*/
@SystemApi @TestApi
public MmTelCapabilities(@MmTelCapability int capabilities) {
super(capabilities);
}
/**
* @hide
*/
@SystemApi @TestApi
@IntDef(flag = true,
value = {
CAPABILITY_TYPE_VOICE,
CAPABILITY_TYPE_VIDEO,
CAPABILITY_TYPE_UT,
CAPABILITY_TYPE_SMS
})
@Retention(RetentionPolicy.SOURCE)
public @interface MmTelCapability {}
/**
* This MmTelFeature supports Voice calling (IR.92)
*/
public static final int CAPABILITY_TYPE_VOICE = 1 << 0;
/**
* This MmTelFeature supports Video (IR.94)
*/
public static final int CAPABILITY_TYPE_VIDEO = 1 << 1;
/**
* This MmTelFeature supports XCAP over Ut for supplementary services. (IR.92)
*/
public static final int CAPABILITY_TYPE_UT = 1 << 2;
/**
* This MmTelFeature supports SMS (IR.92)
*/
public static final int CAPABILITY_TYPE_SMS = 1 << 3;
/**
* @hide
*/
@Override
@SystemApi @TestApi
public final void addCapabilities(@MmTelCapability int capabilities) {
super.addCapabilities(capabilities);
}
/**
* @hide
*/
@Override
@SystemApi @TestApi
public final void removeCapabilities(@MmTelCapability int capability) {
super.removeCapabilities(capability);
}
/**
* @hide
*/
@Override
@SystemApi @TestApi
public final boolean isCapable(@MmTelCapability int capabilities) {
return super.isCapable(capabilities);
}
/**
* @hide
*/
@NonNull
@Override
@SystemApi @TestApi
public String toString() {
StringBuilder builder = new StringBuilder("MmTel Capabilities - [");
builder.append("Voice: ");
builder.append(isCapable(CAPABILITY_TYPE_VOICE));
builder.append(" Video: ");
builder.append(isCapable(CAPABILITY_TYPE_VIDEO));
builder.append(" UT: ");
builder.append(isCapable(CAPABILITY_TYPE_UT));
builder.append(" SMS: ");
builder.append(isCapable(CAPABILITY_TYPE_SMS));
builder.append("]");
return builder.toString();
}
}
/**
* Listener that the framework implements for communication from the MmTelFeature.
* @hide
*/
public static class Listener extends IImsMmTelListener.Stub {
/**
* Called when the IMS provider receives an incoming call.
* @param c The {@link ImsCallSession} associated with the new call.
* @hide
*/
@Override
@SystemApi @TestApi
public void onIncomingCall(IImsCallSession c, Bundle extras) {
}
/**
* Called when the IMS provider implicitly rejects an incoming call during setup.
* @param callProfile An {@link ImsCallProfile} with the call details.
* @param reason The {@link ImsReasonInfo} reason for call rejection.
* @hide
*/
@Override
@SystemApi @TestApi
public void onRejectedCall(ImsCallProfile callProfile, ImsReasonInfo reason) {
}
/**
* Updates the Listener when the voice message count for IMS has changed.
* @param count an integer representing the new message count.
* @hide
*/
@Override
@SystemApi @TestApi
public void onVoiceMessageCountUpdate(int count) {
}
}
/**
* To be returned by {@link #shouldProcessCall(String[])} when the ImsService should process the
* outgoing call as IMS.
* @hide
*/
@SystemApi @TestApi
public static final int PROCESS_CALL_IMS = 0;
/**
* To be returned by {@link #shouldProcessCall(String[])} when the telephony framework should
* not process the outgoing call as IMS and should instead use circuit switch.
* @hide
*/
@SystemApi @TestApi
public static final int PROCESS_CALL_CSFB = 1;
/**
* @hide
*/
@SystemApi @TestApi
@IntDef(flag = true,
value = {
PROCESS_CALL_IMS,
PROCESS_CALL_CSFB
})
@Retention(RetentionPolicy.SOURCE)
public @interface ProcessCallResult {}
/**
* If the flag is present and true, it indicates that the incoming call is for USSD.
* <p>
* This is an optional boolean flag.
* @hide
*/
@SystemApi @TestApi
public static final String EXTRA_IS_USSD = "android.telephony.ims.feature.extra.IS_USSD";
/**
* If this flag is present and true, this call is marked as an unknown dialing call instead
* of an incoming call. An example of such a call is a call that is originated by sending
* commands (like AT commands) directly to the modem without Android involvement or dialing
* calls appearing over IMS when the modem does a silent redial from circuit-switched to IMS in
* certain situations.
* <p>
* This is an optional boolean flag.
* @hide
*/
@SystemApi @TestApi
public static final String EXTRA_IS_UNKNOWN_CALL =
"android.telephony.ims.feature.extra.IS_UNKNOWN_CALL";
private IImsMmTelListener mListener;
/**
* @param listener A {@link Listener} used when the MmTelFeature receives an incoming call and
* notifies the framework.
* @hide
*/
private void setListener(IImsMmTelListener listener) {
synchronized (mLock) {
mListener = listener;
if (mListener != null) {
onFeatureReady();
}
}
}
/**
* The current capability status that this MmTelFeature has defined is available. This
* configuration will be used by the platform to figure out which capabilities are CURRENTLY
* available to be used.
*
* Should be a subset of the capabilities that are enabled by the framework in
* {@link #changeEnabledCapabilities}.
* @return A copy of the current MmTelFeature capability status.
* @hide
*/
@Override
@SystemApi @TestApi
public @NonNull final MmTelCapabilities queryCapabilityStatus() {
return new MmTelCapabilities(super.queryCapabilityStatus());
}
/**
* Notify the framework that the status of the Capabilities has changed. Even though the
* MmTelFeature capability may be enabled by the framework, the status may be disabled due to
* the feature being unavailable from the network.
* @param c The current capability status of the MmTelFeature. If a capability is disabled, then
* the status of that capability is disabled. This can happen if the network does not currently
* support the capability that is enabled. A capability that is disabled by the framework (via
* {@link #changeEnabledCapabilities}) should also show the status as disabled.
* @hide
*/
@SystemApi @TestApi
public final void notifyCapabilitiesStatusChanged(@NonNull MmTelCapabilities c) {
if (c == null) {
throw new IllegalArgumentException("MmTelCapabilities must be non-null!");
}
super.notifyCapabilitiesStatusChanged(c);
}
/**
* Notify the framework of an incoming call.
* @param c The {@link ImsCallSessionImplBase} of the new incoming call.
* @param extras A bundle containing extra parameters related to the call. See
* {@link #EXTRA_IS_UNKNOWN_CALL} and {@link #EXTRA_IS_USSD} above.
* @hide
*/
@SystemApi @TestApi
public final void notifyIncomingCall(@NonNull ImsCallSessionImplBase c,
@NonNull Bundle extras) {
if (c == null || extras == null) {
throw new IllegalArgumentException("ImsCallSessionImplBase and Bundle can not be "
+ "null.");
}
synchronized (mLock) {
if (mListener == null) {
throw new IllegalStateException("Session is not available.");
}
try {
mListener.onIncomingCall(c.getServiceImpl(), extras);
} catch (RemoteException e) {
throw new RuntimeException(e);
}
}
}
/**
* Notify the framework that a call has been implicitly rejected by this MmTelFeature
* during call setup.
* @param callProfile The {@link ImsCallProfile} IMS call profile with details.
* This can be null if no call information is available for the rejected call.
* @param reason The {@link ImsReasonInfo} call rejection reason.
* * @hide
*/
@SystemApi @TestApi
public final void notifyRejectedCall(@NonNull ImsCallProfile callProfile,
@NonNull ImsReasonInfo reason) {
if (callProfile == null || reason == null) {
throw new IllegalArgumentException("ImsCallProfile and ImsReasonInfo must not be "
+ "null.");
}
synchronized (mLock) {
if (mListener == null) {
throw new IllegalStateException("Session is not available.");
}
try {
mListener.onRejectedCall(callProfile, reason);
} catch (RemoteException e) {
throw new RuntimeException(e);
}
}
}
/**
*
* @hide
*/
public final void notifyIncomingCallSession(IImsCallSession c, Bundle extras) {
synchronized (mLock) {
if (mListener == null) {
throw new IllegalStateException("Session is not available.");
}
try {
mListener.onIncomingCall(c, extras);
} catch (RemoteException e) {
throw new RuntimeException(e);
}
}
}
/**
* Notify the framework of a change in the Voice Message count.
* @link count the new Voice Message count.
* @hide
*/
@SystemApi @TestApi
public final void notifyVoiceMessageCountUpdate(int count) {
synchronized (mLock) {
if (mListener == null) {
throw new IllegalStateException("Session is not available.");
}
try {
mListener.onVoiceMessageCountUpdate(count);
} catch (RemoteException e) {
throw new RuntimeException(e);
}
}
}
/**
* Provides the MmTelFeature with the ability to return the framework Capability Configuration
* for a provided Capability. If the framework calls {@link #changeEnabledCapabilities} and
* includes a capability A to enable or disable, this method should return the correct enabled
* status for capability A.
* @param capability The capability that we are querying the configuration for.
* @return true if the capability is enabled, false otherwise.
* @hide
*/
@Override
@SystemApi @TestApi
public boolean queryCapabilityConfiguration(@MmTelCapabilities.MmTelCapability int capability,
@ImsRegistrationImplBase.ImsRegistrationTech int radioTech) {
// Base implementation - Override to provide functionality
return false;
}
/**
* The MmTelFeature should override this method to handle the enabling/disabling of
* MmTel Features, defined in {@link MmTelCapabilities.MmTelCapability}. The framework assumes
* the {@link CapabilityChangeRequest} was processed successfully. If a subset of capabilities
* could not be set to their new values,
* {@link CapabilityCallbackProxy#onChangeCapabilityConfigurationError} must be called
* individually for each capability whose processing resulted in an error.
*
* Enabling/Disabling a capability here indicates that the capability should be registered or
* deregistered (depending on the capability change) and become available or unavailable to
* the framework.
* * @hide
*/
@Override
@SystemApi @TestApi
public void changeEnabledCapabilities(@NonNull CapabilityChangeRequest request,
@NonNull CapabilityCallbackProxy c) {
// Base implementation, no-op
}
/**
* Creates a {@link ImsCallProfile} from the service capabilities & IMS registration state.
*
* @param callSessionType a service type that is specified in {@link ImsCallProfile}
* {@link ImsCallProfile#SERVICE_TYPE_NONE}
* {@link ImsCallProfile#SERVICE_TYPE_NORMAL}
* {@link ImsCallProfile#SERVICE_TYPE_EMERGENCY}
* @param callType a call type that is specified in {@link ImsCallProfile}
* {@link ImsCallProfile#CALL_TYPE_VOICE}
* {@link ImsCallProfile#CALL_TYPE_VT}
* {@link ImsCallProfile#CALL_TYPE_VT_TX}
* {@link ImsCallProfile#CALL_TYPE_VT_RX}
* {@link ImsCallProfile#CALL_TYPE_VT_NODIR}
* {@link ImsCallProfile#CALL_TYPE_VS}
* {@link ImsCallProfile#CALL_TYPE_VS_TX}
* {@link ImsCallProfile#CALL_TYPE_VS_RX}
* @return a {@link ImsCallProfile} object
* @hide
*/
@SystemApi @TestApi
public @Nullable ImsCallProfile createCallProfile(int callSessionType, int callType) {
// Base Implementation - Should be overridden
return null;
}
/**
* @hide
*/
public IImsCallSession createCallSessionInterface(ImsCallProfile profile)
throws RemoteException {
ImsCallSessionImplBase s = MmTelFeature.this.createCallSession(profile);
return s != null ? s.getServiceImpl() : null;
}
/**
* Creates an {@link ImsCallSession} with the specified call profile.
* Use other methods, if applicable, instead of interacting with
* {@link ImsCallSession} directly.
*
* @param profile a call profile to make the call
* @hide
*/
@SystemApi @TestApi
public @Nullable ImsCallSessionImplBase createCallSession(@NonNull ImsCallProfile profile) {
// Base Implementation - Should be overridden
return null;
}
/**
* Called by the framework to determine if the outgoing call, designated by the outgoing
* {@link String}s, should be processed as an IMS call or CSFB call. If this method's
* functionality is not overridden, the platform will process every call as IMS as long as the
* MmTelFeature reports that the {@link MmTelCapabilities#CAPABILITY_TYPE_VOICE} capability is
* available.
* @param numbers An array of {@link String}s that will be used for placing the call. There can
* be multiple {@link String}s listed in the case when we want to place an outgoing
* call as a conference.
* @return a {@link ProcessCallResult} to the framework, which will be used to determine if the
* call will be placed over IMS or via CSFB.
* @hide
*/
@SystemApi @TestApi
public @ProcessCallResult int shouldProcessCall(@NonNull String[] numbers) {
return PROCESS_CALL_IMS;
}
/**
*
* @hide
*/
protected IImsUt getUtInterface() throws RemoteException {
ImsUtImplBase utImpl = getUt();
return utImpl != null ? utImpl.getInterface() : null;
}
/**
* @hide
*/
protected IImsEcbm getEcbmInterface() throws RemoteException {
ImsEcbmImplBase ecbmImpl = getEcbm();
return ecbmImpl != null ? ecbmImpl.getImsEcbm() : null;
}
/**
* @hide
*/
public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException {
ImsMultiEndpointImplBase multiendpointImpl = getMultiEndpoint();
return multiendpointImpl != null ? multiendpointImpl.getIImsMultiEndpoint() : null;
}
/**
* @return The {@link ImsUtImplBase} Ut interface implementation for the supplementary service
* configuration.
* @hide
*/
@SystemApi @TestApi
public @NonNull ImsUtImplBase getUt() {
// Base Implementation - Should be overridden
return new ImsUtImplBase();
}
/**
* @return The {@link ImsEcbmImplBase} Emergency call-back mode interface for emergency VoLTE
* calls that support it.
* @hide
*/
@SystemApi @TestApi
public @NonNull ImsEcbmImplBase getEcbm() {
// Base Implementation - Should be overridden
return new ImsEcbmImplBase();
}
/**
* @return The {@link ImsMultiEndpointImplBase} implementation for implementing Dialog event
* package processing for multi-endpoint.
* @hide
*/
@SystemApi @TestApi
public @NonNull ImsMultiEndpointImplBase getMultiEndpoint() {
// Base Implementation - Should be overridden
return new ImsMultiEndpointImplBase();
}
/**
* Sets the current UI TTY mode for the MmTelFeature.
* @param mode An integer containing the new UI TTY Mode, can consist of
* {@link TelecomManager#TTY_MODE_OFF},
* {@link TelecomManager#TTY_MODE_FULL},
* {@link TelecomManager#TTY_MODE_HCO},
* {@link TelecomManager#TTY_MODE_VCO}
* @param onCompleteMessage If non-null, this MmTelFeature should call this {@link Message} when
* the operation is complete by using the associated {@link android.os.Messenger} in
* {@link Message#replyTo}. For example:
* {@code
* // Set UI TTY Mode and other operations...
* try {
* // Notify framework that the mode was changed.
* Messenger uiMessenger = onCompleteMessage.replyTo;
* uiMessenger.send(onCompleteMessage);
* } catch (RemoteException e) {
* // Remote side is dead
* }
* }
* @hide
*/
@SystemApi @TestApi
public void setUiTtyMode(int mode, @Nullable Message onCompleteMessage) {
// Base Implementation - Should be overridden
}
private void setSmsListener(IImsSmsListener listener) {
getSmsImplementation().registerSmsListener(listener);
}
private void sendSms(int token, int messageRef, String format, String smsc, boolean isRetry,
byte[] pdu) {
getSmsImplementation().sendSms(token, messageRef, format, smsc, isRetry, pdu);
}
private void acknowledgeSms(int token, int messageRef,
@ImsSmsImplBase.DeliverStatusResult int result) {
getSmsImplementation().acknowledgeSms(token, messageRef, result);
}
private void acknowledgeSmsReport(int token, int messageRef,
@ImsSmsImplBase.StatusReportResult int result) {
getSmsImplementation().acknowledgeSmsReport(token, messageRef, result);
}
private void onSmsReady() {
getSmsImplementation().onReady();
}
/**
* Must be overridden by IMS Provider to be able to support SMS over IMS. Otherwise a default
* non-functional implementation is returned.
*
* @return an instance of {@link ImsSmsImplBase} which should be implemented by the IMS
* Provider.
* @hide
*/
@SystemApi @TestApi
public @NonNull ImsSmsImplBase getSmsImplementation() {
return new ImsSmsImplBase();
}
private String getSmsFormat() {
return getSmsImplementation().getSmsFormat();
}
/**
* {@inheritDoc}
* @hide
*/
@Override
@SystemApi @TestApi
public void onFeatureRemoved() {
// Base Implementation - Should be overridden
}
/**
* {@inheritDoc}
* @hide
*/
@Override
@SystemApi @TestApi
public void onFeatureReady() {
// Base Implementation - Should be overridden
}
/**
* @hide
*/
@Override
public final IImsMmTelFeature getBinder() {
return mImsMMTelBinder;
}
}