Adds @hide ImsService APIs

Adds @hide ImsService API implementations to be used for the new
dynamic ImsResolver.

1) ImsService - The main class that all vendor ImsServices will implement.
ImsServices that implement this method must return their implementations
of MMTelFeature when onCreateMMTelFeature is called. The base ImsService
class also relays all method calls through itself as a proxy. So, when
Telephony calls a method, the ImsService figures out which MMTelFeature
should be called (by slot) and then calls that feature's method
implementation.

2) MMTelFeature/RcsFeature - Implements the I*Feature interfaces, which
are used on both sides of the interface. The vendor implemented ImsService
must implement all methods provided in the I*Feature interface in their
implementation of *Feature that they return to the ImsService.

3) ImsServiceProxy[Compat] - The Proxy interface in telephony that will be
called in ImsManager. When a method in this class is called, it will call
the respective AIDL function: Telephony -> IImsServiceController AIDL ->
vendor ImsService -> vendor ImsFeature implementation.
ImsServiceProxyCompat is there to provide backwards compatibility with
older ImsServices that do not use the new ImsService implementations.
It implements all of the methods that are defined in the new I*Feature
interfaces and translates them to the old ImsService AIDL calls.

Test: Adds Unit Tests (see frameworks/opt/telephony)
Merged-In: Id3466c178384158c788ab1d708ab108bb95866fc
Change-Id: Id3466c178384158c788ab1d708ab108bb95866fc
This commit is contained in:
Brad Ebinger
2017-01-25 14:26:41 -08:00
parent eb82e3de87
commit 1639c21be6
13 changed files with 1471 additions and 4 deletions

View File

@@ -435,6 +435,7 @@ LOCAL_SRC_FILES += \
telephony/java/com/android/ims/internal/IImsEcbm.aidl \
telephony/java/com/android/ims/internal/IImsEcbmListener.aidl \
telephony/java/com/android/ims/internal/IImsExternalCallStateListener.aidl \
telephony/java/com/android/ims/internal/IImsFeatureStatusCallback.aidl \
telephony/java/com/android/ims/internal/IImsMultiEndpoint.aidl \
telephony/java/com/android/ims/internal/IImsService.aidl \
telephony/java/com/android/ims/internal/IImsServiceController.aidl \

View File

@@ -0,0 +1,430 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/
package android.telephony.ims;
import android.app.PendingIntent;
import android.content.Intent;
import android.os.IBinder;
import android.os.Message;
import android.os.RemoteException;
import android.telephony.CarrierConfigManager;
import android.telephony.ims.feature.ImsFeature;
import android.telephony.ims.feature.MMTelFeature;
import android.telephony.ims.feature.RcsFeature;
import android.util.Log;
import android.util.SparseArray;
import com.android.ims.ImsCallProfile;
import com.android.ims.internal.IImsCallSession;
import com.android.ims.internal.IImsCallSessionListener;
import com.android.ims.internal.IImsConfig;
import com.android.ims.internal.IImsEcbm;
import com.android.ims.internal.IImsFeatureStatusCallback;
import com.android.ims.internal.IImsMultiEndpoint;
import com.android.ims.internal.IImsRegistrationListener;
import com.android.ims.internal.IImsServiceController;
import com.android.ims.internal.IImsServiceFeatureListener;
import com.android.ims.internal.IImsUt;
import com.android.internal.annotations.VisibleForTesting;
/**
* Main ImsService implementation, which binds via the Telephony ImsResolver. Services that extend
* ImsService must register the service in their AndroidManifest to be detected by the framework.
* First, the application must declare that they use the "android.permission.BIND_IMS_SERVICE"
* permission. Then, the ImsService definition in the manifest must follow the following format:
*
* ...
* <service android:name=".EgImsService"
* android:permission="android.permission.BIND_IMS_SERVICE" >
* <!-- Apps must declare which features they support as metadata. The different categories are
* defined below. In this example, the RCS_FEATURE feature is supported. -->
* <meta-data android:name="android.telephony.ims.RCS_FEATURE" android:value="true" />
* <intent-filter>
* <action android:name="android.telephony.ims.ImsService" />
* </intent-filter>
* </service>
* ...
*
* The telephony framework will then bind to the ImsService you have defined in your manifest
* if you are either:
* 1) Defined as the default ImsService for the device in the device overlay using
* "config_ims_package".
* 2) Defined as a Carrier Provided ImsService in the Carrier Configuration using
* {@link CarrierConfigManager#KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING}.
*
* The features that are currently supported in an ImsService are:
* - RCS_FEATURE: This ImsService implements the {@link RcsFeature} class.
* - MMTEL_FEATURE: This ImsService implements the {@link MMTelFeature} class.
* - EMERGENCY_MMTEL_FEATURE: This ImsService implements the {@link MMTelFeature} class and will be
* available to place emergency calls at all times. This MUST be implemented by the default
* ImsService provided in the device overlay.
*
* @hide
*/
public abstract class ImsService extends ImsServiceBase {
private static final String LOG_TAG = "ImsService";
/**
* The intent that must be defined as an intent-filter in the AndroidManifest of the ImsService.
*/
public static final String SERVICE_INTERFACE = "android.telephony.ims.ImsService";
// A map of slot Id -> Set of features corresponding to that slot.
private final SparseArray<SparseArray<ImsFeature>> mFeatures = new SparseArray<>();
// Implements all supported features as a flat interface.
protected final IBinder mImsServiceController = new IImsServiceController.Stub() {
@Override
public void createImsFeature(int slotId, int feature, IImsFeatureStatusCallback c)
throws RemoteException {
synchronized (mFeatures) {
onCreateImsFeatureInternal(slotId, feature, c);
}
}
@Override
public void removeImsFeature(int slotId, int feature) throws RemoteException {
synchronized (mFeatures) {
onRemoveImsFeatureInternal(slotId, feature);
}
}
@Override
public int startSession(int slotId, int featureType, PendingIntent incomingCallIntent,
IImsRegistrationListener listener) throws RemoteException {
synchronized (mFeatures) {
MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
if (feature != null) {
return feature.startSession(incomingCallIntent, listener);
}
}
return 0;
}
@Override
public void endSession(int slotId, int featureType, int sessionId) throws RemoteException {
synchronized (mFeatures) {
MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
if (feature != null) {
feature.endSession(sessionId);
}
}
}
@Override
public boolean isConnected(int slotId, int featureType, int sessionId, int callSessionType,
int callType) throws RemoteException {
synchronized (mFeatures) {
MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
if (feature != null) {
return feature.isConnected(sessionId, callSessionType, callType);
}
}
return false;
}
@Override
public boolean isOpened(int slotId, int featureType, int sessionId) throws RemoteException {
synchronized (mFeatures) {
MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
if (feature != null) {
return feature.isOpened(sessionId);
}
}
return false;
}
@Override
public int getFeatureStatus(int slotId, int featureType) throws RemoteException {
int status = ImsFeature.STATE_NOT_AVAILABLE;
synchronized (mFeatures) {
SparseArray<ImsFeature> featureMap = mFeatures.get(slotId);
if (featureMap != null) {
ImsFeature feature = getImsFeatureFromType(featureMap, featureType);
if (feature != null) {
status = feature.getFeatureState();
}
}
}
return status;
}
@Override
public void addRegistrationListener(int slotId, int featureType, int sessionId,
IImsRegistrationListener listener) throws RemoteException {
synchronized (mFeatures) {
MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
if (feature != null) {
feature.addRegistrationListener(sessionId, listener);
}
}
}
@Override
public void removeRegistrationListener(int slotId, int featureType, int sessionId,
IImsRegistrationListener listener) throws RemoteException {
synchronized (mFeatures) {
MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
if (feature != null) {
feature.removeRegistrationListener(sessionId, listener);
}
}
}
@Override
public ImsCallProfile createCallProfile(int slotId, int featureType, int sessionId,
int callSessionType, int callType) throws RemoteException {
synchronized (mFeatures) {
MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
if (feature != null) {
return feature.createCallProfile(sessionId, callSessionType, callType);
}
}
return null;
}
@Override
public IImsCallSession createCallSession(int slotId, int featureType, int sessionId,
ImsCallProfile profile, IImsCallSessionListener listener) throws RemoteException {
synchronized (mFeatures) {
MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
if (feature != null) {
return feature.createCallSession(sessionId, profile, listener);
}
}
return null;
}
@Override
public IImsCallSession getPendingCallSession(int slotId, int featureType, int sessionId,
String callId) throws RemoteException {
synchronized (mFeatures) {
MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
if (feature != null) {
return feature.getPendingCallSession(sessionId, callId);
}
}
return null;
}
@Override
public IImsUt getUtInterface(int slotId, int featureType, int sessionId)
throws RemoteException {
synchronized (mFeatures) {
MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
if (feature != null) {
return feature.getUtInterface(sessionId);
}
}
return null;
}
@Override
public IImsConfig getConfigInterface(int slotId, int featureType, int sessionId)
throws RemoteException {
synchronized (mFeatures) {
MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
if (feature != null) {
return feature.getConfigInterface(sessionId);
}
}
return null;
}
@Override
public void turnOnIms(int slotId, int featureType, int sessionId) throws RemoteException {
synchronized (mFeatures) {
MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
if (feature != null) {
feature.turnOnIms(sessionId);
}
}
}
@Override
public void turnOffIms(int slotId, int featureType, int sessionId) throws RemoteException {
synchronized (mFeatures) {
MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
if (feature != null) {
feature.turnOffIms(sessionId);
}
}
}
@Override
public IImsEcbm getEcbmInterface(int slotId, int featureType, int sessionId)
throws RemoteException {
synchronized (mFeatures) {
MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
if (feature != null) {
return feature.getEcbmInterface(sessionId);
}
}
return null;
}
@Override
public void setUiTTYMode(int slotId, int featureType, int sessionId, int uiTtyMode,
Message onComplete) throws RemoteException {
synchronized (mFeatures) {
MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
if (feature != null) {
feature.setUiTTYMode(sessionId, uiTtyMode, onComplete);
}
}
}
@Override
public IImsMultiEndpoint getMultiEndpointInterface(int slotId, int featureType,
int sessionId) throws RemoteException {
synchronized (mFeatures) {
MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
if (feature != null) {
return feature.getMultiEndpointInterface(sessionId);
}
}
return null;
}
};
@Override
public IBinder onBind(Intent intent) {
if(SERVICE_INTERFACE.equals(intent.getAction())) {
return mImsServiceController;
}
return null;
}
/**
* Called from the ImsResolver to create the requested ImsFeature, as defined by the slot and
* featureType
* @param slotId An integer representing which SIM slot the ImsFeature is assigned to.
* @param featureType An integer representing the type of ImsFeature being created. This is
* defined in {@link ImsFeature}.
*/
// Be sure to lock on mFeatures before accessing this method
private void onCreateImsFeatureInternal(int slotId, int featureType,
IImsFeatureStatusCallback c) {
SparseArray<ImsFeature> featureMap = mFeatures.get(slotId);
if (featureMap == null) {
featureMap = new SparseArray<>();
mFeatures.put(slotId, featureMap);
}
ImsFeature f = makeImsFeature(slotId, featureType);
if (f != null) {
f.setImsFeatureStatusCallback(c);
featureMap.put(featureType, f);
}
}
/**
* Called from the ImsResolver to remove an existing ImsFeature, as defined by the slot and
* featureType.
* @param slotId An integer representing which SIM slot the ImsFeature is assigned to.
* @param featureType An integer representing the type of ImsFeature being removed. This is
* defined in {@link ImsFeature}.
*/
// Be sure to lock on mFeatures before accessing this method
private void onRemoveImsFeatureInternal(int slotId, int featureType) {
SparseArray<ImsFeature> featureMap = mFeatures.get(slotId);
if (featureMap == null) {
return;
}
ImsFeature featureToRemove = getImsFeatureFromType(featureMap, featureType);
if (featureToRemove != null) {
featureMap.remove(featureType);
featureToRemove.notifyFeatureRemoved(slotId);
// Remove reference to Binder
featureToRemove.setImsFeatureStatusCallback(null);
}
}
// Be sure to lock on mFeatures before accessing this method
private MMTelFeature resolveMMTelFeature(int slotId, int featureType) {
SparseArray<ImsFeature> features = getImsFeatureMap(slotId);
MMTelFeature feature = null;
if (features != null) {
feature = resolveImsFeature(features, featureType, MMTelFeature.class);
}
return feature;
}
// Be sure to lock on mFeatures before accessing this method
private <T extends ImsFeature> T resolveImsFeature(SparseArray<ImsFeature> set, int featureType,
Class<T> className) {
ImsFeature feature = getImsFeatureFromType(set, featureType);
if (feature == null) {
return null;
}
try {
return className.cast(feature);
} catch (ClassCastException e)
{
Log.e(LOG_TAG, "Can not cast ImsFeature! Exception: " + e.getMessage());
}
return null;
}
@VisibleForTesting
// Be sure to lock on mFeatures before accessing this method
public SparseArray<ImsFeature> getImsFeatureMap(int slotId) {
return mFeatures.get(slotId);
}
@VisibleForTesting
// Be sure to lock on mFeatures before accessing this method
public ImsFeature getImsFeatureFromType(SparseArray<ImsFeature> set, int featureType) {
return set.get(featureType);
}
private ImsFeature makeImsFeature(int slotId, int feature) {
switch (feature) {
case ImsFeature.EMERGENCY_MMTEL: {
return onCreateEmergencyMMTelImsFeature(slotId);
}
case ImsFeature.MMTEL: {
return onCreateMMTelImsFeature(slotId);
}
case ImsFeature.RCS: {
return onCreateRcsFeature(slotId);
}
}
// Tried to create feature that is not defined.
return null;
}
/**
* @return An implementation of MMTelFeature that will be used by the system for MMTel
* functionality. Must be able to handle emergency calls at any time as well.
*/
public abstract MMTelFeature onCreateEmergencyMMTelImsFeature(int slotId);
/**
* @return An implementation of MMTelFeature that will be used by the system for MMTel
* functionality.
*/
public abstract MMTelFeature onCreateMMTelImsFeature(int slotId);
/**
* @return An implementation of RcsFeature that will be used by the system for RCS.
*/
public abstract RcsFeature onCreateRcsFeature(int slotId);
}

View File

@@ -22,7 +22,9 @@ import android.content.Intent;
import android.os.IBinder;
/**
* Base ImsService Implementation, which is used by the ImsResolver to bind.
* Base ImsService Implementation, which is used by the ImsResolver to bind. ImsServices that do not
* need to provide an ImsService implementation but still wish to be managed by the ImsResolver
* lifecycle may implement this class directly.
* @hide
*/
@SystemApi

View File

@@ -0,0 +1,316 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/
package android.telephony.ims;
import android.app.PendingIntent;
import android.os.IBinder;
import android.os.Message;
import android.os.RemoteException;
import android.telephony.ims.feature.IRcsFeature;
import android.telephony.ims.feature.ImsFeature;
import android.util.Log;
import com.android.ims.ImsCallProfile;
import com.android.ims.internal.IImsCallSession;
import com.android.ims.internal.IImsCallSessionListener;
import com.android.ims.internal.IImsConfig;
import com.android.ims.internal.IImsEcbm;
import com.android.ims.internal.IImsMultiEndpoint;
import com.android.ims.internal.IImsRegistrationListener;
import com.android.ims.internal.IImsServiceController;
import com.android.ims.internal.IImsServiceFeatureListener;
import com.android.ims.internal.IImsUt;
/**
* A container of the IImsServiceController binder, which implements all of the ImsFeatures that
* the platform currently supports: MMTel and RCS.
* @hide
*/
public class ImsServiceProxy extends ImsServiceProxyCompat implements IRcsFeature {
protected String LOG_TAG = "ImsServiceProxy";
private final int mSupportedFeature;
// Start by assuming the proxy is available for usage.
private boolean mIsAvailable = true;
// ImsFeature Status from the ImsService. Cached.
private Integer mFeatureStatusCached = null;
private ImsServiceProxy.INotifyStatusChanged mStatusCallback;
private final Object mLock = new Object();
public interface INotifyStatusChanged {
void notifyStatusChanged();
}
private final IImsServiceFeatureListener mListenerBinder =
new IImsServiceFeatureListener.Stub() {
@Override
public void imsFeatureCreated(int slotId, int feature) throws RemoteException {
// The feature has been re-enabled. This may happen when the service crashes.
synchronized (mLock) {
if (!mIsAvailable && mSlotId == slotId && feature == mSupportedFeature) {
Log.i(LOG_TAG, "Feature enabled on slotId: " + slotId + " for feature: " +
feature);
mIsAvailable = true;
}
}
}
@Override
public void imsFeatureRemoved(int slotId, int feature) throws RemoteException {
synchronized (mLock) {
if (mIsAvailable && mSlotId == slotId && feature == mSupportedFeature) {
Log.i(LOG_TAG, "Feature disabled on slotId: " + slotId + " for feature: " +
feature);
mIsAvailable = false;
}
}
}
@Override
public void imsStatusChanged(int slotId, int feature, int status) throws RemoteException {
synchronized (mLock) {
Log.i(LOG_TAG, "imsStatusChanged: slot: " + slotId + " feature: " + feature +
" status: " + status);
if (mSlotId == slotId && feature == mSupportedFeature) {
mFeatureStatusCached = status;
}
}
if (mStatusCallback != null) {
mStatusCallback.notifyStatusChanged();
}
}
};
public ImsServiceProxy(int slotId, IBinder binder, int featureType) {
super(slotId, binder);
mSupportedFeature = featureType;
}
public ImsServiceProxy(int slotId, int featureType) {
super(slotId, null /*IBinder*/);
mSupportedFeature = featureType;
}
public IImsServiceFeatureListener getListener() {
return mListenerBinder;
}
public void setBinder(IBinder binder) {
mBinder = binder;
}
@Override
public int startSession(PendingIntent incomingCallIntent, IImsRegistrationListener listener)
throws RemoteException {
synchronized (mLock) {
checkBinderConnection();
return getServiceInterface(mBinder).startSession(mSlotId, mSupportedFeature,
incomingCallIntent, listener);
}
}
@Override
public void endSession(int sessionId) throws RemoteException {
synchronized (mLock) {
checkBinderConnection();
getServiceInterface(mBinder).endSession(mSlotId, mSupportedFeature, sessionId);
}
}
@Override
public boolean isConnected(int sessionId, int callServiceType, int callType)
throws RemoteException {
synchronized (mLock) {
checkBinderConnection();
return getServiceInterface(mBinder).isConnected(mSlotId, mSupportedFeature, sessionId,
callServiceType, callType);
}
}
@Override
public boolean isOpened(int sessionId) throws RemoteException {
synchronized (mLock) {
checkBinderConnection();
return getServiceInterface(mBinder).isOpened(mSlotId, mSupportedFeature, sessionId);
}
}
@Override
public void addRegistrationListener(int sessionId, IImsRegistrationListener listener)
throws RemoteException {
synchronized (mLock) {
checkBinderConnection();
getServiceInterface(mBinder).addRegistrationListener(mSlotId, mSupportedFeature,
sessionId, listener);
}
}
@Override
public void removeRegistrationListener(int sessionId, IImsRegistrationListener listener)
throws RemoteException {
synchronized (mLock) {
checkBinderConnection();
getServiceInterface(mBinder).removeRegistrationListener(mSlotId, mSupportedFeature,
sessionId, listener);
}
}
@Override
public ImsCallProfile createCallProfile(int sessionId, int callServiceType, int callType)
throws RemoteException {
synchronized (mLock) {
checkBinderConnection();
return getServiceInterface(mBinder).createCallProfile(mSlotId, mSupportedFeature,
sessionId, callServiceType, callType);
}
}
@Override
public IImsCallSession createCallSession(int sessionId, ImsCallProfile profile,
IImsCallSessionListener listener) throws RemoteException {
synchronized (mLock) {
checkBinderConnection();
return getServiceInterface(mBinder).createCallSession(mSlotId, mSupportedFeature,
sessionId, profile, listener);
}
}
@Override
public IImsCallSession getPendingCallSession(int sessionId, String callId)
throws RemoteException {
synchronized (mLock) {
checkBinderConnection();
return getServiceInterface(mBinder).getPendingCallSession(mSlotId, mSupportedFeature,
sessionId, callId);
}
}
@Override
public IImsUt getUtInterface(int sessionId) throws RemoteException {
synchronized (mLock) {
checkBinderConnection();
return getServiceInterface(mBinder).getUtInterface(mSlotId, mSupportedFeature,
sessionId);
}
}
@Override
public IImsConfig getConfigInterface(int sessionId) throws RemoteException {
synchronized (mLock) {
checkBinderConnection();
return getServiceInterface(mBinder).getConfigInterface(mSlotId, mSupportedFeature,
sessionId);
}
}
@Override
public void turnOnIms(int sessionId) throws RemoteException {
synchronized (mLock) {
checkBinderConnection();
getServiceInterface(mBinder).turnOnIms(mSlotId, mSupportedFeature, sessionId);
}
}
@Override
public void turnOffIms(int sessionId) throws RemoteException {
synchronized (mLock) {
checkBinderConnection();
getServiceInterface(mBinder).turnOffIms(mSlotId, mSupportedFeature, sessionId);
}
}
@Override
public IImsEcbm getEcbmInterface(int sessionId) throws RemoteException {
synchronized (mLock) {
checkBinderConnection();
return getServiceInterface(mBinder).getEcbmInterface(mSlotId, mSupportedFeature,
sessionId);
}
}
@Override
public void setUiTTYMode(int sessionId, int uiTtyMode, Message onComplete)
throws RemoteException {
synchronized (mLock) {
checkBinderConnection();
getServiceInterface(mBinder).setUiTTYMode(mSlotId, mSupportedFeature, sessionId,
uiTtyMode, onComplete);
}
}
@Override
public IImsMultiEndpoint getMultiEndpointInterface(int sessionId) throws RemoteException {
synchronized (mLock) {
checkBinderConnection();
return getServiceInterface(mBinder).getMultiEndpointInterface(mSlotId,
mSupportedFeature, sessionId);
}
}
@Override
public int getFeatureStatus() {
synchronized (mLock) {
if (mFeatureStatusCached != null) {
return mFeatureStatusCached;
}
}
// Don't synchronize on Binder call.
Integer status = retrieveFeatureStatus();
synchronized (mLock) {
if (status == null) {
return ImsFeature.STATE_NOT_AVAILABLE;
}
// Cache only non-null value for feature status.
mFeatureStatusCached = status;
}
return status;
}
/**
* Internal method used to retrieve the feature status from the corresponding ImsService.
*/
private Integer retrieveFeatureStatus() {
if (mBinder != null) {
try {
return getServiceInterface(mBinder).getFeatureStatus(mSlotId, mSupportedFeature);
} catch (RemoteException e) {
// Status check failed, don't update cache
}
}
return null;
}
/**
* @param c Callback that will fire when the feature status has changed.
*/
public void setStatusCallback(INotifyStatusChanged c) {
mStatusCallback = c;
}
@Override
public boolean isBinderAlive() {
return mIsAvailable && getFeatureStatus() == ImsFeature.STATE_READY && mBinder != null &&
mBinder.isBinderAlive();
}
private IImsServiceController getServiceInterface(IBinder b) {
return IImsServiceController.Stub.asInterface(b);
}
}

View File

@@ -0,0 +1,182 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/
package android.telephony.ims;
import android.app.PendingIntent;
import android.os.IBinder;
import android.os.Message;
import android.os.RemoteException;
import android.telephony.ims.feature.IMMTelFeature;
import android.telephony.ims.feature.ImsFeature;
import com.android.ims.ImsCallProfile;
import com.android.ims.internal.IImsCallSession;
import com.android.ims.internal.IImsCallSessionListener;
import com.android.ims.internal.IImsConfig;
import com.android.ims.internal.IImsEcbm;
import com.android.ims.internal.IImsMultiEndpoint;
import com.android.ims.internal.IImsRegistrationListener;
import com.android.ims.internal.IImsService;
import com.android.ims.internal.IImsUt;
/**
* Compatibility class that implements the new ImsService IMMTelFeature interface, but
* uses the old IImsService interface to support older devices that implement the deprecated
* opt/net/ims interface.
* @hide
*/
public class ImsServiceProxyCompat implements IMMTelFeature {
protected final int mSlotId;
protected IBinder mBinder;
public ImsServiceProxyCompat(int slotId, IBinder binder) {
mSlotId = slotId;
mBinder = binder;
}
@Override
public int startSession(PendingIntent incomingCallIntent, IImsRegistrationListener listener)
throws RemoteException {
checkBinderConnection();
return getServiceInterface(mBinder).open(mSlotId, ImsFeature.MMTEL, incomingCallIntent,
listener);
}
@Override
public void endSession(int sessionId) throws RemoteException {
checkBinderConnection();
getServiceInterface(mBinder).close(sessionId);
}
@Override
public boolean isConnected(int sessionId, int callServiceType, int callType)
throws RemoteException {
checkBinderConnection();
return getServiceInterface(mBinder).isConnected(sessionId, callServiceType, callType);
}
@Override
public boolean isOpened(int sessionId) throws RemoteException {
checkBinderConnection();
return getServiceInterface(mBinder).isOpened(sessionId);
}
@Override
public void addRegistrationListener(int sessionId, IImsRegistrationListener listener)
throws RemoteException {
checkBinderConnection();
getServiceInterface(mBinder).addRegistrationListener(mSlotId, ImsFeature.MMTEL, listener);
}
@Override
public void removeRegistrationListener(int sessionId, IImsRegistrationListener listener)
throws RemoteException {
checkBinderConnection();
// Not Implemented in old ImsService. If the registration listener becomes invalid, the
// ImsService will remove.
}
@Override
public ImsCallProfile createCallProfile(int sessionId, int callServiceType, int callType)
throws RemoteException {
checkBinderConnection();
return getServiceInterface(mBinder).createCallProfile(sessionId, callServiceType, callType);
}
@Override
public IImsCallSession createCallSession(int sessionId, ImsCallProfile profile,
IImsCallSessionListener listener) throws RemoteException {
checkBinderConnection();
return getServiceInterface(mBinder).createCallSession(sessionId, profile, listener);
}
@Override
public IImsCallSession getPendingCallSession(int sessionId, String callId)
throws RemoteException {
checkBinderConnection();
return getServiceInterface(mBinder).getPendingCallSession(sessionId, callId);
}
@Override
public IImsUt getUtInterface(int sessionId) throws RemoteException {
checkBinderConnection();
return getServiceInterface(mBinder).getUtInterface(sessionId);
}
@Override
public IImsConfig getConfigInterface(int sessionId) throws RemoteException {
checkBinderConnection();
return getServiceInterface(mBinder).getConfigInterface(mSlotId);
}
@Override
public void turnOnIms(int sessionId) throws RemoteException {
checkBinderConnection();
getServiceInterface(mBinder).turnOnIms(mSlotId);
}
@Override
public void turnOffIms(int sessionId) throws RemoteException {
checkBinderConnection();
getServiceInterface(mBinder).turnOffIms(mSlotId);
}
@Override
public IImsEcbm getEcbmInterface(int sessionId) throws RemoteException {
checkBinderConnection();
return getServiceInterface(mBinder).getEcbmInterface(sessionId);
}
@Override
public void setUiTTYMode(int sessionId, int uiTtyMode, Message onComplete)
throws RemoteException {
checkBinderConnection();
getServiceInterface(mBinder).setUiTTYMode(sessionId, uiTtyMode, onComplete);
}
@Override
public IImsMultiEndpoint getMultiEndpointInterface(int sessionId) throws RemoteException {
checkBinderConnection();
return getServiceInterface(mBinder).getMultiEndpointInterface(sessionId);
}
/**
* Base implementation, always returns READY for compatibility with old ImsService.
*/
public int getFeatureStatus() {
return ImsFeature.STATE_READY;
}
/**
* @return false if the binder connection is no longer alive.
*/
public boolean isBinderAlive() {
return mBinder != null && mBinder.isBinderAlive();
}
private IImsService getServiceInterface(IBinder b) {
return IImsService.Stub.asInterface(b);
}
protected void checkBinderConnection() throws RemoteException {
if (!isBinderAlive()) {
throw new RemoteException("ImsServiceProxy is not available for that feature.");
}
}
}

View File

@@ -0,0 +1,192 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/
package android.telephony.ims.feature;
import android.app.PendingIntent;
import android.os.Message;
import android.os.RemoteException;
import com.android.ims.ImsCallProfile;
import com.android.ims.internal.IImsCallSession;
import com.android.ims.internal.IImsCallSessionListener;
import com.android.ims.internal.IImsConfig;
import com.android.ims.internal.IImsEcbm;
import com.android.ims.internal.IImsMultiEndpoint;
import com.android.ims.internal.IImsRegistrationListener;
import com.android.ims.internal.IImsUt;
/**
* MMTel interface for an ImsService. When updating this interface, ensure that base implementations
* of your changes are also present in MMTelFeature for compatibility with older versions of the
* MMTel feature.
* @hide
*/
public interface IMMTelFeature {
/**
* Notifies the MMTel feature that you would like to start a session. This should always be
* done before making/receiving IMS calls. The IMS service will register the device to the
* operator's network with the credentials (from ISIM) periodically in order to receive calls
* from the operator's network. When the IMS service receives a new call, it will send out an
* intent with the provided action string. The intent contains a call ID extra
* {@link IImsCallSession#getCallId} and it can be used to take a call.
*
* @param incomingCallIntent When an incoming call is received, the IMS service will call
* {@link PendingIntent#send} to send back the intent to the caller with
* {@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 an integer (greater than 0) representing the session id associated with the session
* that has been started.
*/
int startSession(PendingIntent incomingCallIntent, IImsRegistrationListener listener)
throws RemoteException;
/**
* End a previously started session using the associated sessionId.
* @param sessionId an integer (greater than 0) representing the ongoing session. See
* {@link #startSession}.
*/
void endSession(int sessionId) throws RemoteException;
/**
* Checks if the IMS service has successfully registered to the IMS network with the specified
* service & call type.
*
* @param sessionId a session id which is obtained from {@link #startSession}
* @param callServiceType 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 RemoteException
*/
boolean isConnected(int sessionId, int callServiceType, int callType) throws RemoteException;
/**
* Checks if the specified IMS service is opened.
*
* @param sessionId a service id which is obtained from {@link #startSession}
* @return true if the specified service id is opened; false otherwise
*/
boolean isOpened(int sessionId) throws RemoteException;
/**
* Add a new registration listener for the client associated with the session Id.
* @param sessionId a session id which is obtained from {@link #startSession}
* @param listener An implementation of IImsRegistrationListener.
*/
void addRegistrationListener(int sessionId, IImsRegistrationListener listener)
throws RemoteException;
/**
* Remove a previously registered listener using {@link #addRegistrationListener} for the client
* associated with the session Id.
* @param sessionId a session id which is obtained from {@link #startSession}
* @param listener A previously registered IImsRegistrationListener
*/
void removeRegistrationListener(int sessionId, IImsRegistrationListener listener)
throws RemoteException;
/**
* Creates a {@link ImsCallProfile} from the service capabilities & IMS registration state.
*
* @param sessionId a session id which is obtained from {@link #startSession}
* @param callServiceType 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
*/
ImsCallProfile createCallProfile(int sessionId, int callServiceType, int callType)
throws RemoteException;
/**
* Creates a {@link ImsCallSession} with the specified call profile.
* Use other methods, if applicable, instead of interacting with
* {@link ImsCallSession} directly.
*
* @param sessionId a session id which is obtained from {@link #startSession}
* @param profile a call profile to make the call
* @param listener An implementation of IImsCallSessionListener.
*/
IImsCallSession createCallSession(int sessionId, ImsCallProfile profile,
IImsCallSessionListener listener) throws RemoteException;
/**
* Retrieves the call session associated with a pending call.
*
* @param sessionId a session id which is obtained from {@link #startSession}
* @param callId a call id to make the call
*/
IImsCallSession getPendingCallSession(int sessionId, String callId) throws RemoteException;
/**
* @return The Ut interface for the supplementary service configuration.
*/
IImsUt getUtInterface(int sessionId) throws RemoteException;
/**
* @return The config interface for IMS Configuration
*/
IImsConfig getConfigInterface(int sessionId) throws RemoteException;
/**
* Signal the MMTelFeature to turn on IMS when it has been turned off using {@link #turnOffIms}
* @param sessionId a session id which is obtained from {@link #startSession}
*/
void turnOnIms(int sessionId) throws RemoteException;
/**
* Signal the MMTelFeature to turn off IMS when it has been turned on using {@link #turnOnIms}
* @param sessionId a session id which is obtained from {@link #startSession}
*/
void turnOffIms(int sessionId) throws RemoteException;
/**
* @return The Emergency call-back mode interface for emergency VoLTE calls that support it.
*/
IImsEcbm getEcbmInterface(int sessionId) throws RemoteException;
/**
* Sets the current UI TTY mode for the MMTelFeature.
* @param sessionId a session id which is obtained from {@link #startSession}
* @param uiTtyMode An integer containing the new UI TTY Mode.
* @param onComplete A {@link Message} to be used when the mode has been set.
* @throws RemoteException
*/
void setUiTTYMode(int sessionId, int uiTtyMode, Message onComplete) throws RemoteException;
/**
* @return MultiEndpoint interface for DEP notifications
*/
IImsMultiEndpoint getMultiEndpointInterface(int sessionId) throws RemoteException;
}

View File

@@ -0,0 +1,26 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/
package android.telephony.ims.feature;
/**
* Feature interface that provides access to RCS APIs. Currently empty until RCS support is added
* in the framework.
* @hide
*/
public interface IRcsFeature {
}

View File

@@ -16,18 +16,112 @@
package android.telephony.ims.feature;
import android.annotation.IntDef;
import android.os.RemoteException;
import android.util.Log;
import com.android.ims.internal.IImsFeatureStatusCallback;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
/**
* Base class for all IMS features that are supported by the framework.
* @hide
*/
public class ImsFeature {
public abstract class ImsFeature {
private static final String LOG_TAG = "ImsFeature";
// Invalid feature value
public static final int INVALID = -1;
// ImsFeatures that are defined in the Manifests
// ImsFeatures that are defined in the Manifests. Ensure that these values match the previously
// defined values in ImsServiceClass for compatibility purposes.
public static final int EMERGENCY_MMTEL = 0;
public static final int MMTEL = 1;
public static final int RCS = 2;
// Total number of features defined
public static final int MAX = 3;
// Integer values defining the state of the ImsFeature at any time.
@IntDef(flag = true,
value = {
STATE_NOT_AVAILABLE,
STATE_INITIALIZING,
STATE_READY,
})
@Retention(RetentionPolicy.SOURCE)
public @interface ImsState {}
public static final int STATE_NOT_AVAILABLE = 0;
public static final int STATE_INITIALIZING = 1;
public static final int STATE_READY = 2;
private List<INotifyFeatureRemoved> mRemovedListeners = new ArrayList<>();
private IImsFeatureStatusCallback mStatusCallback;
private @ImsState int mState = STATE_NOT_AVAILABLE;
public interface INotifyFeatureRemoved {
void onFeatureRemoved(int slotId);
}
public void addFeatureRemovedListener(INotifyFeatureRemoved listener) {
synchronized (mRemovedListeners) {
mRemovedListeners.add(listener);
}
}
public void removeFeatureRemovedListener(INotifyFeatureRemoved listener) {
synchronized (mRemovedListeners) {
mRemovedListeners.remove(listener);
}
}
// Not final for testing.
public void notifyFeatureRemoved(int slotId) {
synchronized (mRemovedListeners) {
mRemovedListeners.forEach(l -> l.onFeatureRemoved(slotId));
onFeatureRemoved();
}
}
public int getFeatureState() {
return mState;
}
protected final void setFeatureState(@ImsState int state) {
if (mState != state) {
mState = state;
notifyFeatureState(state);
}
}
// Not final for testing.
public void setImsFeatureStatusCallback(IImsFeatureStatusCallback c) {
mStatusCallback = c;
// If we have just connected, send queued status.
notifyFeatureState(mState);
}
/**
* Internal method called by ImsFeature when setFeatureState has changed.
* @param state
*/
private void notifyFeatureState(@ImsState int state) {
if (mStatusCallback != null) {
try {
Log.i(LOG_TAG, "notifying ImsFeatureState");
mStatusCallback.notifyImsFeatureStatus(state);
} catch (RemoteException e) {
mStatusCallback = null;
Log.w(LOG_TAG, "Couldn't notify feature state: " + e.getMessage());
}
}
}
/**
* Called when the feature is being removed and must be cleaned up.
*/
public abstract void onFeatureRemoved();
}

View File

@@ -0,0 +1,122 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/
package android.telephony.ims.feature;
import android.app.PendingIntent;
import android.os.Message;
import com.android.ims.ImsCallProfile;
import com.android.ims.internal.IImsCallSession;
import com.android.ims.internal.IImsCallSessionListener;
import com.android.ims.internal.IImsConfig;
import com.android.ims.internal.IImsEcbm;
import com.android.ims.internal.IImsMultiEndpoint;
import com.android.ims.internal.IImsRegistrationListener;
import com.android.ims.internal.IImsUt;
import java.util.ArrayList;
import java.util.List;
/**
* Base implementation, which implements all methods in IMMTelFeature. Any class wishing to use
* MMTelFeature should extend this class and implement all methods that the service supports.
*
* @hide
*/
public class MMTelFeature extends ImsFeature implements IMMTelFeature {
@Override
public int startSession(PendingIntent incomingCallIntent, IImsRegistrationListener listener) {
return 0;
}
@Override
public void endSession(int sessionId) {
}
@Override
public boolean isConnected(int sessionId, int callSessionType, int callType) {
return false;
}
@Override
public boolean isOpened(int sessionId) {
return false;
}
@Override
public void addRegistrationListener(int sessionId, IImsRegistrationListener listener) {
}
@Override
public void removeRegistrationListener(int sessionId, IImsRegistrationListener listener) {
}
@Override
public ImsCallProfile createCallProfile(int sessionId, int callSessionType, int callType) {
return null;
}
@Override
public IImsCallSession createCallSession(int sessionId, ImsCallProfile profile,
IImsCallSessionListener listener) {
return null;
}
@Override
public IImsCallSession getPendingCallSession(int sessionId, String callId) {
return null;
}
@Override
public IImsUt getUtInterface(int sessionId) {
return null;
}
@Override
public IImsConfig getConfigInterface(int sessionId) {
return null;
}
@Override
public void turnOnIms(int sessionId) {
}
@Override
public void turnOffIms(int sessionId) {
}
@Override
public IImsEcbm getEcbmInterface(int sessionId) {
return null;
}
@Override
public void setUiTTYMode(int sessionId, int uiTtyMode, Message onComplete) {
}
@Override
public IImsMultiEndpoint getMultiEndpointInterface(int sessionId) {
return null;
}
@Override
public void onFeatureRemoved() {
}
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/
package android.telephony.ims.feature;
/**
* Base implementation of the RcsFeature APIs. Any ImsService wishing to support RCS should extend
* this class and provide implementations of the IRcsFeature methods that they support.
* @hide
*/
public class RcsFeature extends ImsFeature implements IRcsFeature {
public RcsFeature() {
super();
}
@Override
public void onFeatureRemoved() {
}
}

View File

@@ -0,0 +1,25 @@
/*
* Copyright (c) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.ims.internal;
/**
* Interface from ImsFeature in the ImsService to ImsServiceController.
* {@hide}
*/
oneway interface IImsFeatureStatusCallback {
void notifyImsFeatureStatus(int featureStatus);
}

View File

@@ -16,10 +16,50 @@
package com.android.ims.internal;
import android.app.PendingIntent;
import com.android.ims.ImsCallProfile;
import com.android.ims.internal.IImsCallSession;
import com.android.ims.internal.IImsCallSessionListener;
import com.android.ims.internal.IImsConfig;
import com.android.ims.internal.IImsEcbm;
import com.android.ims.internal.IImsFeatureStatusCallback;
import com.android.ims.internal.IImsMultiEndpoint;
import com.android.ims.internal.IImsRegistrationListener;
import com.android.ims.internal.IImsUt;
import android.os.Message;
/**
* See ImsService and IMMTelFeature for more information.
* {@hide}
*/
interface IImsServiceController {
void createImsFeature(int slotId, int feature);
// ImsService Control
void createImsFeature(int slotId, int feature, IImsFeatureStatusCallback c);
void removeImsFeature(int slotId, int feature);
// MMTel Feature
int startSession(int slotId, int featureType, in PendingIntent incomingCallIntent,
in IImsRegistrationListener listener);
void endSession(int slotId, int featureType, int sessionId);
boolean isConnected(int slotId, int featureType, int sessionId, int callSessionType, int callType);
boolean isOpened(int slotId, int featureType, int sessionId);
int getFeatureStatus(int slotId, int featureType);
void addRegistrationListener(int slotId, int featureType, int sessionId,
in IImsRegistrationListener listener);
void removeRegistrationListener(int slotId, int featureType, int sessionId,
in IImsRegistrationListener listener);
ImsCallProfile createCallProfile(int slotId, int featureType, int sessionId, int callSessionType, int callType);
IImsCallSession createCallSession(int slotId, int featureType, int sessionId,
in ImsCallProfile profile, IImsCallSessionListener listener);
IImsCallSession getPendingCallSession(int slotId, int featureType, int sessionId,
String callId);
IImsUt getUtInterface(int slotId, int featureType, int sessionId);
IImsConfig getConfigInterface(int slotId, int featureType, int sessionId);
void turnOnIms(int slotId, int featureType, int sessionId);
void turnOffIms(int slotId, int featureType, int sessionId);
IImsEcbm getEcbmInterface(int slotId, int featureType, int sessionId);
void setUiTTYMode(int slotId, int featureType, int sessionId, int uiTtyMode,
in Message onComplete);
IImsMultiEndpoint getMultiEndpointInterface(int slotId, int featureType, int sessionId);
}

View File

@@ -17,9 +17,11 @@
package com.android.ims.internal;
/**
* Interface from ImsResolver to ImsServiceProxy in ImsManager.
* {@hide}
*/
oneway interface IImsServiceFeatureListener {
void imsFeatureCreated(int slotId, int feature);
void imsFeatureRemoved(int slotId, int feature);
void imsStatusChanged(int slotId, int feature, int status);
}